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	"debuginfo",
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, 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	if llgo != "" {
166		bindir := filepath.Join(tmpgopath, "bin")
167
168		err = os.MkdirAll(bindir, os.ModePerm)
169		if err != nil {
170			panic(err.Error())
171		}
172
173		err = os.Symlink(llgo, filepath.Join(bindir, "gccgo"))
174		if err != nil {
175			panic(err.Error())
176		}
177
178		newpathlist := []string{bindir}
179		newpathlist = append(newpathlist, filepath.SplitList(newpath)...)
180		newpath = strings.Join(newpathlist, string(filepath.ListSeparator))
181
182		args = append([]string{args[0], "-compiler", "gccgo"}, args[1:]...)
183	}
184
185	newgopathlist := []string{tmpgopath}
186	newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...)
187	newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator))
188
189	flags := llvmFlags()
190
191	newenv := []string{
192		"CC=" + cc,
193		"CXX=" + cxx,
194		"CGO_CPPFLAGS=" + flags.cpp + " " + cppflags,
195		"CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags,
196		"CGO_LDFLAGS=" + flags.ld + " " + ldflags,
197		"GOPATH=" + newgopath,
198		"PATH=" + newpath,
199	}
200	for _, v := range os.Environ() {
201		if !strings.HasPrefix(v, "CC=") &&
202			!strings.HasPrefix(v, "CXX=") &&
203			!strings.HasPrefix(v, "CGO_CPPFLAGS=") &&
204			!strings.HasPrefix(v, "CGO_CXXFLAGS=") &&
205			!strings.HasPrefix(v, "CGO_LDFLAGS=") &&
206			!strings.HasPrefix(v, "GOPATH=") &&
207			!strings.HasPrefix(v, "PATH=") {
208			newenv = append(newenv, v)
209		}
210	}
211
212	gocmdpath, err := exec.LookPath("go")
213	if err != nil {
214		panic(err.Error())
215	}
216
217	proc, err := os.StartProcess(gocmdpath, append([]string{"go"}, args...),
218		&os.ProcAttr{
219			Env:   newenv,
220			Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
221		})
222	if err != nil {
223		panic(err.Error())
224	}
225	ps, err := proc.Wait()
226	if err != nil {
227		panic(err.Error())
228	}
229
230	os.RemoveAll(tmpgopath)
231
232	if !ps.Success() {
233		os.Exit(1)
234	}
235}
236
237func usage() {
238	fmt.Println(`Usage: llvm-go subcommand [flags]
239
240Available subcommands: build get install run test print-components print-config`)
241	os.Exit(0)
242}
243
244func main() {
245	cc := os.Getenv("CC")
246	cxx := os.Getenv("CXX")
247	cppflags := os.Getenv("CGO_CPPFLAGS")
248	cxxflags := os.Getenv("CGO_CXXFLAGS")
249	ldflags := os.Getenv("CGO_LDFLAGS")
250	llgo := ""
251
252	args := os.Args[1:]
253	DONE: for {
254		switch {
255		case len(args) == 0:
256			usage()
257		case strings.HasPrefix(args[0], "cc="):
258			cc = args[0][3:]
259			args = args[1:]
260		case strings.HasPrefix(args[0], "cxx="):
261			cxx = args[0][4:]
262			args = args[1:]
263		case strings.HasPrefix(args[0], "llgo="):
264			llgo = args[0][5:]
265			args = args[1:]
266		case strings.HasPrefix(args[0], "cppflags="):
267			cppflags = args[0][9:]
268			args = args[1:]
269		case strings.HasPrefix(args[0], "cxxflags="):
270			cxxflags = args[0][9:]
271			args = args[1:]
272		case strings.HasPrefix(args[0], "ldflags="):
273			ldflags = args[0][8:]
274			args = args[1:]
275		default:
276			break DONE
277		}
278	}
279
280	switch args[0] {
281	case "build", "get", "install", "run", "test":
282		runGoWithLLVMEnv(args, cc, cxx, llgo, cppflags, cxxflags, ldflags)
283	case "print-components":
284		printComponents()
285	case "print-config":
286		printConfig()
287	default:
288		usage()
289	}
290}
291