1//===-- llvm-go.go - go tool wrapper for LLVM -----------------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This tool lets us build LLVM components within the tree by setting up a
11// $GOPATH that resembles a tree fetched in the normal way with "go get".
12//
13//===----------------------------------------------------------------------===//
14
15package main
16
17import (
18	"fmt"
19	"io/ioutil"
20	"os"
21	"os/exec"
22	"path/filepath"
23	"runtime"
24	"strings"
25)
26
27type pkg struct {
28	llvmpath, pkgpath string
29}
30
31var packages = []pkg{
32	{"bindings/go/llvm", "llvm.org/llvm/bindings/go/llvm"},
33	{"tools/llgo", "llvm.org/llgo"},
34}
35
36type compilerFlags struct {
37	cpp, cxx, ld string
38}
39
40var components = []string{
41	"all-targets",
42	"analysis",
43	"asmparser",
44	"asmprinter",
45	"bitreader",
46	"bitwriter",
47	"codegen",
48	"core",
49	"debuginfodwarf",
50	"executionengine",
51	"instrumentation",
52	"interpreter",
53	"ipo",
54	"irreader",
55	"linker",
56	"mc",
57	"mcjit",
58	"objcarcopts",
59	"option",
60	"profiledata",
61	"scalaropts",
62	"support",
63	"target",
64}
65
66func llvmConfig(args ...string) string {
67	configpath := os.Getenv("LLVM_CONFIG")
68	if configpath == "" {
69		// strip llvm-go, add llvm-config
70		configpath = os.Args[0][:len(os.Args[0])-7] + "llvm-config"
71	}
72
73	cmd := exec.Command(configpath, args...)
74	out, err := cmd.Output()
75	if err != nil {
76		panic(err.Error())
77	}
78
79	outstr := string(out)
80	outstr = strings.TrimSuffix(outstr, "\n")
81	return strings.Replace(outstr, "\n", " ", -1)
82}
83
84func llvmFlags() compilerFlags {
85	ldflags := llvmConfig(append([]string{"--ldflags", "--libs", "--system-libs"}, components...)...)
86	if runtime.GOOS != "darwin" {
87		// OS X doesn't like -rpath with cgo. See:
88		// https://code.google.com/p/go/issues/detail?id=7293
89		ldflags = "-Wl,-rpath," + llvmConfig("--libdir") + " " + ldflags
90	}
91	return compilerFlags{
92		cpp: llvmConfig("--cppflags"),
93		cxx: "-std=c++11",
94		ld:  ldflags,
95	}
96}
97
98func addTag(args []string, tag string) []string {
99	args = append([]string{}, args...)
100	addedTag := false
101	for i, a := range args {
102		if strings.HasPrefix(a, "-tags=") {
103			args[i] = a + " " + tag
104			addedTag = true
105		} else if a == "-tags" && i+1 < len(args) {
106			args[i+1] = args[i+1] + " " + tag
107			addedTag = true
108		}
109	}
110	if !addedTag {
111		args = append([]string{args[0], "-tags", tag}, args[1:]...)
112	}
113	return args
114}
115
116func printComponents() {
117	fmt.Println(strings.Join(components, " "))
118}
119
120func printConfig() {
121	flags := llvmFlags()
122
123	fmt.Printf(`// +build !byollvm
124
125// This file is generated by llvm-go, do not edit.
126
127package llvm
128
129/*
130#cgo CPPFLAGS: %s
131#cgo CXXFLAGS: %s
132#cgo LDFLAGS: %s
133*/
134import "C"
135
136type (run_build_sh int)
137`, flags.cpp, flags.cxx, flags.ld)
138}
139
140func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags string) {
141	args = addTag(args, "byollvm")
142
143	srcdir := llvmConfig("--src-root")
144
145	tmpgopath, err := ioutil.TempDir("", "gopath")
146	if err != nil {
147		panic(err.Error())
148	}
149
150	for _, p := range packages {
151		path := filepath.Join(tmpgopath, "src", p.pkgpath)
152		err := os.MkdirAll(filepath.Dir(path), os.ModePerm)
153		if err != nil {
154			panic(err.Error())
155		}
156
157		err = os.Symlink(filepath.Join(srcdir, p.llvmpath), path)
158		if err != nil {
159			panic(err.Error())
160		}
161	}
162
163	newpath := os.Getenv("PATH")
164
165	newgopathlist := []string{tmpgopath}
166	newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...)
167	newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator))
168
169	flags := llvmFlags()
170
171	newenv := []string{
172		"CC=" + cc,
173		"CXX=" + cxx,
174		"CGO_CPPFLAGS=" + flags.cpp + " " + cppflags,
175		"CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags,
176		"CGO_LDFLAGS=" + flags.ld + " " + ldflags,
177		"GOPATH=" + newgopath,
178		"PATH=" + newpath,
179	}
180	if llgo != "" {
181		newenv = append(newenv, "GCCGO=" + llgo)
182	}
183
184	for _, v := range os.Environ() {
185		if !strings.HasPrefix(v, "CC=") &&
186			!strings.HasPrefix(v, "CXX=") &&
187			!strings.HasPrefix(v, "CGO_CPPFLAGS=") &&
188			!strings.HasPrefix(v, "CGO_CXXFLAGS=") &&
189			!strings.HasPrefix(v, "CGO_LDFLAGS=") &&
190			!strings.HasPrefix(v, "GCCGO=") &&
191			!strings.HasPrefix(v, "GOPATH=") &&
192			!strings.HasPrefix(v, "PATH=") {
193			newenv = append(newenv, v)
194		}
195	}
196
197	gocmdpath, err := exec.LookPath(gocmd)
198	if err != nil {
199		panic(err.Error())
200	}
201
202	proc, err := os.StartProcess(gocmdpath, append([]string{gocmd}, args...),
203		&os.ProcAttr{
204			Env:   newenv,
205			Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
206		})
207	if err != nil {
208		panic(err.Error())
209	}
210	ps, err := proc.Wait()
211	if err != nil {
212		panic(err.Error())
213	}
214
215	os.RemoveAll(tmpgopath)
216
217	if !ps.Success() {
218		os.Exit(1)
219	}
220}
221
222func usage() {
223	fmt.Println(`Usage: llvm-go subcommand [flags]
224
225Available subcommands: build get install run test print-components print-config`)
226	os.Exit(0)
227}
228
229func main() {
230	cc := os.Getenv("CC")
231	cxx := os.Getenv("CXX")
232	cppflags := os.Getenv("CGO_CPPFLAGS")
233	cxxflags := os.Getenv("CGO_CXXFLAGS")
234	ldflags := os.Getenv("CGO_LDFLAGS")
235	gocmd := "go"
236	llgo := ""
237
238	args := os.Args[1:]
239	DONE: for {
240		switch {
241		case len(args) == 0:
242			usage()
243		case strings.HasPrefix(args[0], "cc="):
244			cc = args[0][3:]
245			args = args[1:]
246		case strings.HasPrefix(args[0], "cxx="):
247			cxx = args[0][4:]
248			args = args[1:]
249		case strings.HasPrefix(args[0], "go="):
250			gocmd = args[0][3:]
251			args = args[1:]
252		case strings.HasPrefix(args[0], "llgo="):
253			llgo = args[0][5:]
254			args = args[1:]
255		case strings.HasPrefix(args[0], "cppflags="):
256			cppflags = args[0][9:]
257			args = args[1:]
258		case strings.HasPrefix(args[0], "cxxflags="):
259			cxxflags = args[0][9:]
260			args = args[1:]
261		case strings.HasPrefix(args[0], "ldflags="):
262			ldflags = args[0][8:]
263			args = args[1:]
264		default:
265			break DONE
266		}
267	}
268
269	switch args[0] {
270	case "build", "get", "install", "run", "test":
271		runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags)
272	case "print-components":
273		printComponents()
274	case "print-config":
275		printConfig()
276	default:
277		usage()
278	}
279}
280