blob: ed79ca67b89d3743e1fbc4a18986b7f2c3722071 [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"},
Peter Collingbournea4f0bd72014-11-27 00:15:21 +000038 {"tools/llgo", "llvm.org/llgo"},
Peter Collingbourne244ecf52014-10-23 02:33:23 +000039}
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",
Zachary Turner82af9432015-01-30 18:07:45 +000054 "debuginfodwarf",
Peter Collingbourne244ecf52014-10-23 02:33:23 +000055 "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 == "" {
Andrew Wilkins92113962015-09-01 03:14:31 +000074 bin, _ := filepath.Split(os.Args[0])
75 configpath = filepath.Join(bin, "llvm-config")
Peter Collingbourne244ecf52014-10-23 02:33:23 +000076 }
77
78 cmd := exec.Command(configpath, args...)
Andrew Wilkins92113962015-09-01 03:14:31 +000079 cmd.Stderr = os.Stderr
Peter Collingbourne244ecf52014-10-23 02:33:23 +000080 out, err := cmd.Output()
81 if err != nil {
82 panic(err.Error())
83 }
84
85 outstr := string(out)
86 outstr = strings.TrimSuffix(outstr, "\n")
Andrew Wilkins92113962015-09-01 03:14:31 +000087 outstr = strings.Replace(outstr, "\n", " ", -1)
88 return outstr
Peter Collingbourne244ecf52014-10-23 02:33:23 +000089}
90
Andrew Wilkins92113962015-09-01 03:14:31 +000091func 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")
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000102 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
Andrew Wilkins92113962015-09-01 03:14:31 +0000136func printConfig(linkmode string) {
137 flags := llvmFlags(linkmode)
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000138
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
Andrew Wilkins92113962015-09-01 03:14:31 +0000156func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, linkmode string) {
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000157 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
Peter Collingbournea4f0bd72014-11-27 00:15:21 +0000179 newpath := os.Getenv("PATH")
180
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000181 newgopathlist := []string{tmpgopath}
182 newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...)
183 newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator))
184
Andrew Wilkins92113962015-09-01 03:14:31 +0000185 flags := llvmFlags(linkmode)
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000186
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,
Peter Collingbournea4f0bd72014-11-27 00:15:21 +0000194 "PATH=" + newpath,
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000195 }
Peter Collingbourne46f4b482015-02-14 01:45:57 +0000196 if llgo != "" {
Andrew Wilkins92113962015-09-01 03:14:31 +0000197 newenv = append(newenv, "GCCGO="+llgo)
Peter Collingbourne46f4b482015-02-14 01:45:57 +0000198 }
199
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000200 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=") &&
Peter Collingbourne46f4b482015-02-14 01:45:57 +0000206 !strings.HasPrefix(v, "GCCGO=") &&
Peter Collingbournea4f0bd72014-11-27 00:15:21 +0000207 !strings.HasPrefix(v, "GOPATH=") &&
208 !strings.HasPrefix(v, "PATH=") {
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000209 newenv = append(newenv, v)
210 }
211 }
212
Peter Collingbourne55707082015-02-14 01:45:56 +0000213 gocmdpath, err := exec.LookPath(gocmd)
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000214 if err != nil {
215 panic(err.Error())
216 }
217
Peter Collingbourne55707082015-02-14 01:45:56 +0000218 proc, err := os.StartProcess(gocmdpath, append([]string{gocmd}, args...),
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000219 &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")
Peter Collingbourne55707082015-02-14 01:45:56 +0000251 gocmd := "go"
Peter Collingbournea4f0bd72014-11-27 00:15:21 +0000252 llgo := ""
Andrew Wilkins92113962015-09-01 03:14:31 +0000253 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 }
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000267
268 args := os.Args[1:]
Andrew Wilkins92113962015-09-01 03:14:31 +0000269LOOP:
270 for {
271 if len(args) == 0 {
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000272 usage()
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000273 }
Andrew Wilkins92113962015-09-01 03:14:31 +0000274 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
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000282 }
283
284 switch args[0] {
285 case "build", "get", "install", "run", "test":
Andrew Wilkins92113962015-09-01 03:14:31 +0000286 runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, linkmode)
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000287 case "print-components":
288 printComponents()
289 case "print-config":
Andrew Wilkins92113962015-09-01 03:14:31 +0000290 printConfig(linkmode)
Peter Collingbourne244ecf52014-10-23 02:33:23 +0000291 default:
292 usage()
293 }
294}