blob: 4d65de62346229f7faff4bd5c5e8cd01c2773e13 [file] [log] [blame]
Peter Collingbourne244ecf52014-10-23 02:33:23 +00001//===-- 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
Andrew Wilkins92113962015-09-01 03:14:31 +000027const (
28 linkmodeComponentLibs = "component-libs"
29 linkmodeDylib = "dylib"
30)
31
Peter Collingbourne244ecf52014-10-23 02:33:23 +000032type pkg struct {
33 llvmpath, pkgpath string
34}
35
36var packages = []pkg{
37 {"bindings/go/llvm", "llvm.org/llvm/bindings/go/llvm"},
38}
39
40type compilerFlags struct {
41 cpp, cxx, ld string
42}
43
44var components = []string{
45 "all-targets",
46 "analysis",
47 "asmparser",
48 "asmprinter",
49 "bitreader",
50 "bitwriter",
51 "codegen",
52 "core",
Zachary Turner82af9432015-01-30 18:07:45 +000053 "debuginfodwarf",
Peter Collingbourne244ecf52014-10-23 02:33:23 +000054 "executionengine",
55 "instrumentation",
56 "interpreter",
57 "ipo",
58 "irreader",
59 "linker",
60 "mc",
61 "mcjit",
62 "objcarcopts",
63 "option",
64 "profiledata",
65 "scalaropts",
66 "support",
67 "target",
68}
69
70func llvmConfig(args ...string) string {
71 configpath := os.Getenv("LLVM_CONFIG")
72 if configpath == "" {
Andrew Wilkins92113962015-09-01 03:14:31 +000073 bin, _ := filepath.Split(os.Args[0])
74 configpath = filepath.Join(bin, "llvm-config")
Peter Collingbourne244ecf52014-10-23 02:33:23 +000075 }
76
77 cmd := exec.Command(configpath, args...)
Andrew Wilkins92113962015-09-01 03:14:31 +000078 cmd.Stderr = os.Stderr
Peter Collingbourne244ecf52014-10-23 02:33:23 +000079 out, err := cmd.Output()
80 if err != nil {
81 panic(err.Error())
82 }
83
84 outstr := string(out)
85 outstr = strings.TrimSuffix(outstr, "\n")
Andrew Wilkins92113962015-09-01 03:14:31 +000086 outstr = strings.Replace(outstr, "\n", " ", -1)
87 return outstr
Peter Collingbourne244ecf52014-10-23 02:33:23 +000088}
89
Andrew Wilkinsdfd60882016-01-20 04:03:09 +000090func llvmFlags() compilerFlags {
Andrew Wilkins7ab4dc72016-01-21 02:33:39 +000091 args := append([]string{"--ldflags", "--libs", "--system-libs"}, components...)
92 ldflags := llvmConfig(args...)
Peter Collingbourne244ecf52014-10-23 02:33:23 +000093 if runtime.GOOS != "darwin" {
94 // OS X doesn't like -rpath with cgo. See:
Hans Wennborg08b34a02017-11-13 23:47:58 +000095 // https://github.com/golang/go/issues/7293
Peter Collingbourne244ecf52014-10-23 02:33:23 +000096 ldflags = "-Wl,-rpath," + llvmConfig("--libdir") + " " + ldflags
97 }
98 return compilerFlags{
99 cpp: llvmConfig("--cppflags"),
100 cxx: "-std=c++11",
101 ld: ldflags,
102 }
103}
104
105func addTag(args []string, tag string) []string {
106 args = append([]string{}, args...)
107 addedTag := false
108 for i, a := range args {
109 if strings.HasPrefix(a, "-tags=") {
110 args[i] = a + " " + tag
111 addedTag = true
112 } else if a == "-tags" && i+1 < len(args) {
113 args[i+1] = args[i+1] + " " + tag
114 addedTag = true
115 }
116 }
117 if !addedTag {
118 args = append([]string{args[0], "-tags", tag}, args[1:]...)
119 }
120 return args
121}
122
123func printComponents() {
124 fmt.Println(strings.Join(components, " "))
125}
126
Andrew Wilkinsdfd60882016-01-20 04:03:09 +0000127func printConfig() {
128 flags := llvmFlags()
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000129
130 fmt.Printf(`// +build !byollvm
131
132// This file is generated by llvm-go, do not edit.
133
134package llvm
135
136/*
137#cgo CPPFLAGS: %s
138#cgo CXXFLAGS: %s
139#cgo LDFLAGS: %s
140*/
141import "C"
142
143type (run_build_sh int)
144`, flags.cpp, flags.cxx, flags.ld)
145}
146
Andrew Wilkins5bf7d812016-07-27 03:21:51 +0000147func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags string, packages []pkg) {
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000148 args = addTag(args, "byollvm")
149
150 srcdir := llvmConfig("--src-root")
151
152 tmpgopath, err := ioutil.TempDir("", "gopath")
153 if err != nil {
154 panic(err.Error())
155 }
156
157 for _, p := range packages {
158 path := filepath.Join(tmpgopath, "src", p.pkgpath)
159 err := os.MkdirAll(filepath.Dir(path), os.ModePerm)
160 if err != nil {
161 panic(err.Error())
162 }
163
Andrew Wilkins5bf7d812016-07-27 03:21:51 +0000164 abspath := p.llvmpath
165 if !filepath.IsAbs(abspath) {
166 abspath = filepath.Join(srcdir, abspath)
167 }
168
169 err = os.Symlink(abspath, path)
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000170 if err != nil {
171 panic(err.Error())
172 }
173 }
174
Peter Collingbournea4f0bd72014-11-27 00:15:21 +0000175 newpath := os.Getenv("PATH")
176
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000177 newgopathlist := []string{tmpgopath}
178 newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...)
179 newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator))
180
Andrew Wilkinsdfd60882016-01-20 04:03:09 +0000181 flags := llvmFlags()
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000182
183 newenv := []string{
184 "CC=" + cc,
185 "CXX=" + cxx,
186 "CGO_CPPFLAGS=" + flags.cpp + " " + cppflags,
187 "CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags,
188 "CGO_LDFLAGS=" + flags.ld + " " + ldflags,
189 "GOPATH=" + newgopath,
Peter Collingbournea4f0bd72014-11-27 00:15:21 +0000190 "PATH=" + newpath,
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000191 }
Peter Collingbourne46f4b482015-02-14 01:45:57 +0000192 if llgo != "" {
Andrew Wilkins92113962015-09-01 03:14:31 +0000193 newenv = append(newenv, "GCCGO="+llgo)
Peter Collingbourne46f4b482015-02-14 01:45:57 +0000194 }
195
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000196 for _, v := range os.Environ() {
197 if !strings.HasPrefix(v, "CC=") &&
198 !strings.HasPrefix(v, "CXX=") &&
199 !strings.HasPrefix(v, "CGO_CPPFLAGS=") &&
200 !strings.HasPrefix(v, "CGO_CXXFLAGS=") &&
201 !strings.HasPrefix(v, "CGO_LDFLAGS=") &&
Peter Collingbourne46f4b482015-02-14 01:45:57 +0000202 !strings.HasPrefix(v, "GCCGO=") &&
Peter Collingbournea4f0bd72014-11-27 00:15:21 +0000203 !strings.HasPrefix(v, "GOPATH=") &&
204 !strings.HasPrefix(v, "PATH=") {
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000205 newenv = append(newenv, v)
206 }
207 }
208
Peter Collingbourne55707082015-02-14 01:45:56 +0000209 gocmdpath, err := exec.LookPath(gocmd)
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000210 if err != nil {
211 panic(err.Error())
212 }
213
Peter Collingbourne55707082015-02-14 01:45:56 +0000214 proc, err := os.StartProcess(gocmdpath, append([]string{gocmd}, args...),
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000215 &os.ProcAttr{
216 Env: newenv,
217 Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
218 })
219 if err != nil {
220 panic(err.Error())
221 }
222 ps, err := proc.Wait()
223 if err != nil {
224 panic(err.Error())
225 }
226
227 os.RemoveAll(tmpgopath)
228
229 if !ps.Success() {
230 os.Exit(1)
231 }
232}
233
234func usage() {
235 fmt.Println(`Usage: llvm-go subcommand [flags]
236
237Available subcommands: build get install run test print-components print-config`)
238 os.Exit(0)
239}
240
241func main() {
242 cc := os.Getenv("CC")
243 cxx := os.Getenv("CXX")
244 cppflags := os.Getenv("CGO_CPPFLAGS")
245 cxxflags := os.Getenv("CGO_CXXFLAGS")
246 ldflags := os.Getenv("CGO_LDFLAGS")
Peter Collingbourne55707082015-02-14 01:45:56 +0000247 gocmd := "go"
Peter Collingbournea4f0bd72014-11-27 00:15:21 +0000248 llgo := ""
Andrew Wilkins5bf7d812016-07-27 03:21:51 +0000249 packagesString := ""
Andrew Wilkins92113962015-09-01 03:14:31 +0000250
251 flags := []struct {
252 name string
253 dest *string
254 }{
255 {"cc", &cc},
256 {"cxx", &cxx},
257 {"go", &gocmd},
258 {"llgo", &llgo},
259 {"cppflags", &cppflags},
260 {"ldflags", &ldflags},
Andrew Wilkins5bf7d812016-07-27 03:21:51 +0000261 {"packages", &packagesString},
Andrew Wilkins92113962015-09-01 03:14:31 +0000262 }
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000263
264 args := os.Args[1:]
Andrew Wilkins92113962015-09-01 03:14:31 +0000265LOOP:
266 for {
267 if len(args) == 0 {
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000268 usage()
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000269 }
Andrew Wilkins92113962015-09-01 03:14:31 +0000270 for _, flag := range flags {
271 if strings.HasPrefix(args[0], flag.name+"=") {
272 *flag.dest = args[0][len(flag.name)+1:]
273 args = args[1:]
274 continue LOOP
275 }
276 }
277 break
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000278 }
279
Andrew Wilkins5bf7d812016-07-27 03:21:51 +0000280 packages := packages
281 if packagesString != "" {
282 for _, field := range strings.Fields(packagesString) {
283 pos := strings.IndexRune(field, '=')
284 if pos == -1 {
285 fmt.Fprintf(os.Stderr, "invalid packages value %q, expected 'pkgpath=llvmpath [pkgpath=llvmpath ...]'\n", packagesString)
286 os.Exit(1)
287 }
288 packages = append(packages, pkg{
289 pkgpath: field[:pos],
290 llvmpath: field[pos+1:],
291 })
292 }
293 }
294
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000295 switch args[0] {
296 case "build", "get", "install", "run", "test":
Andrew Wilkins5bf7d812016-07-27 03:21:51 +0000297 runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, packages)
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000298 case "print-components":
299 printComponents()
300 case "print-config":
Andrew Wilkinsdfd60882016-01-20 04:03:09 +0000301 printConfig()
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000302 default:
303 usage()
304 }
305}