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