blob: dec64b607bd8985fc21afac6770db1b15d105b42 [file] [log] [blame]
Peter Collingbournebdc3a5b2014-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 Wilkins10add602015-09-01 03:14:31 +000027const (
28 linkmodeComponentLibs = "component-libs"
29 linkmodeDylib = "dylib"
30)
31
Peter Collingbournebdc3a5b2014-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",
whitequarkb52e8dc2018-08-19 23:40:05 +000053 "coroutines",
Zachary Turner50418a02015-01-30 18:07:45 +000054 "debuginfodwarf",
Peter Collingbournebdc3a5b2014-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 Wilkins10add602015-09-01 03:14:31 +000074 bin, _ := filepath.Split(os.Args[0])
Mauro Rossi6f676672019-02-23 13:23:31 +010075 configpath = filepath.Join(bin, "llvm80-config")
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +000076 }
77
78 cmd := exec.Command(configpath, args...)
Andrew Wilkins10add602015-09-01 03:14:31 +000079 cmd.Stderr = os.Stderr
Peter Collingbournebdc3a5b2014-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 Wilkins10add602015-09-01 03:14:31 +000087 outstr = strings.Replace(outstr, "\n", " ", -1)
88 return outstr
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +000089}
90
Andrew Wilkins93083d42016-01-20 04:03:09 +000091func llvmFlags() compilerFlags {
Andrew Wilkins9c6305e2016-01-21 02:33:39 +000092 args := append([]string{"--ldflags", "--libs", "--system-libs"}, components...)
93 ldflags := llvmConfig(args...)
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +000094 if runtime.GOOS != "darwin" {
95 // OS X doesn't like -rpath with cgo. See:
Hans Wennborg138434e2017-11-13 23:47:58 +000096 // https://github.com/golang/go/issues/7293
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +000097 ldflags = "-Wl,-rpath," + llvmConfig("--libdir") + " " + ldflags
98 }
99 return compilerFlags{
100 cpp: llvmConfig("--cppflags"),
101 cxx: "-std=c++11",
102 ld: ldflags,
103 }
104}
105
106func addTag(args []string, tag string) []string {
107 args = append([]string{}, args...)
108 addedTag := false
109 for i, a := range args {
110 if strings.HasPrefix(a, "-tags=") {
111 args[i] = a + " " + tag
112 addedTag = true
113 } else if a == "-tags" && i+1 < len(args) {
114 args[i+1] = args[i+1] + " " + tag
115 addedTag = true
116 }
117 }
118 if !addedTag {
119 args = append([]string{args[0], "-tags", tag}, args[1:]...)
120 }
121 return args
122}
123
124func printComponents() {
125 fmt.Println(strings.Join(components, " "))
126}
127
Andrew Wilkins93083d42016-01-20 04:03:09 +0000128func printConfig() {
129 flags := llvmFlags()
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000130
131 fmt.Printf(`// +build !byollvm
132
133// This file is generated by llvm-go, do not edit.
134
Mauro Rossi6f676672019-02-23 13:23:31 +0100135package llvm80
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000136
137/*
138#cgo CPPFLAGS: %s
139#cgo CXXFLAGS: %s
140#cgo LDFLAGS: %s
141*/
142import "C"
143
144type (run_build_sh int)
145`, flags.cpp, flags.cxx, flags.ld)
146}
147
Andrew Wilkins064517f2016-07-27 03:21:51 +0000148func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags string, packages []pkg) {
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000149 args = addTag(args, "byollvm")
150
151 srcdir := llvmConfig("--src-root")
152
153 tmpgopath, err := ioutil.TempDir("", "gopath")
154 if err != nil {
155 panic(err.Error())
156 }
157
158 for _, p := range packages {
159 path := filepath.Join(tmpgopath, "src", p.pkgpath)
160 err := os.MkdirAll(filepath.Dir(path), os.ModePerm)
161 if err != nil {
162 panic(err.Error())
163 }
164
Andrew Wilkins064517f2016-07-27 03:21:51 +0000165 abspath := p.llvmpath
166 if !filepath.IsAbs(abspath) {
167 abspath = filepath.Join(srcdir, abspath)
168 }
169
170 err = os.Symlink(abspath, path)
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000171 if err != nil {
172 panic(err.Error())
173 }
174 }
175
Peter Collingbournea9ce0382014-11-27 00:15:21 +0000176 newpath := os.Getenv("PATH")
177
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000178 newgopathlist := []string{tmpgopath}
179 newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...)
180 newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator))
181
Andrew Wilkins93083d42016-01-20 04:03:09 +0000182 flags := llvmFlags()
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000183
184 newenv := []string{
185 "CC=" + cc,
186 "CXX=" + cxx,
187 "CGO_CPPFLAGS=" + flags.cpp + " " + cppflags,
188 "CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags,
189 "CGO_LDFLAGS=" + flags.ld + " " + ldflags,
190 "GOPATH=" + newgopath,
Peter Collingbournea9ce0382014-11-27 00:15:21 +0000191 "PATH=" + newpath,
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000192 }
Peter Collingbourne0f708432015-02-14 01:45:57 +0000193 if llgo != "" {
Andrew Wilkins10add602015-09-01 03:14:31 +0000194 newenv = append(newenv, "GCCGO="+llgo)
Peter Collingbourne0f708432015-02-14 01:45:57 +0000195 }
196
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000197 for _, v := range os.Environ() {
198 if !strings.HasPrefix(v, "CC=") &&
199 !strings.HasPrefix(v, "CXX=") &&
200 !strings.HasPrefix(v, "CGO_CPPFLAGS=") &&
201 !strings.HasPrefix(v, "CGO_CXXFLAGS=") &&
202 !strings.HasPrefix(v, "CGO_LDFLAGS=") &&
Peter Collingbourne0f708432015-02-14 01:45:57 +0000203 !strings.HasPrefix(v, "GCCGO=") &&
Peter Collingbournea9ce0382014-11-27 00:15:21 +0000204 !strings.HasPrefix(v, "GOPATH=") &&
205 !strings.HasPrefix(v, "PATH=") {
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000206 newenv = append(newenv, v)
207 }
208 }
209
Peter Collingbournee887b2e2015-02-14 01:45:56 +0000210 gocmdpath, err := exec.LookPath(gocmd)
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000211 if err != nil {
212 panic(err.Error())
213 }
214
Peter Collingbournee887b2e2015-02-14 01:45:56 +0000215 proc, err := os.StartProcess(gocmdpath, append([]string{gocmd}, args...),
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000216 &os.ProcAttr{
217 Env: newenv,
218 Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
219 })
220 if err != nil {
221 panic(err.Error())
222 }
223 ps, err := proc.Wait()
224 if err != nil {
225 panic(err.Error())
226 }
227
228 os.RemoveAll(tmpgopath)
229
230 if !ps.Success() {
231 os.Exit(1)
232 }
233}
234
235func usage() {
236 fmt.Println(`Usage: llvm-go subcommand [flags]
237
238Available subcommands: build get install run test print-components print-config`)
239 os.Exit(0)
240}
241
242func main() {
243 cc := os.Getenv("CC")
244 cxx := os.Getenv("CXX")
245 cppflags := os.Getenv("CGO_CPPFLAGS")
246 cxxflags := os.Getenv("CGO_CXXFLAGS")
247 ldflags := os.Getenv("CGO_LDFLAGS")
Peter Collingbournee887b2e2015-02-14 01:45:56 +0000248 gocmd := "go"
Peter Collingbournea9ce0382014-11-27 00:15:21 +0000249 llgo := ""
Andrew Wilkins064517f2016-07-27 03:21:51 +0000250 packagesString := ""
Andrew Wilkins10add602015-09-01 03:14:31 +0000251
252 flags := []struct {
253 name string
254 dest *string
255 }{
256 {"cc", &cc},
257 {"cxx", &cxx},
258 {"go", &gocmd},
259 {"llgo", &llgo},
260 {"cppflags", &cppflags},
261 {"ldflags", &ldflags},
Andrew Wilkins064517f2016-07-27 03:21:51 +0000262 {"packages", &packagesString},
Andrew Wilkins10add602015-09-01 03:14:31 +0000263 }
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000264
265 args := os.Args[1:]
Andrew Wilkins10add602015-09-01 03:14:31 +0000266LOOP:
267 for {
268 if len(args) == 0 {
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000269 usage()
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000270 }
Andrew Wilkins10add602015-09-01 03:14:31 +0000271 for _, flag := range flags {
272 if strings.HasPrefix(args[0], flag.name+"=") {
273 *flag.dest = args[0][len(flag.name)+1:]
274 args = args[1:]
275 continue LOOP
276 }
277 }
278 break
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000279 }
280
Andrew Wilkins064517f2016-07-27 03:21:51 +0000281 packages := packages
282 if packagesString != "" {
283 for _, field := range strings.Fields(packagesString) {
284 pos := strings.IndexRune(field, '=')
285 if pos == -1 {
286 fmt.Fprintf(os.Stderr, "invalid packages value %q, expected 'pkgpath=llvmpath [pkgpath=llvmpath ...]'\n", packagesString)
287 os.Exit(1)
288 }
289 packages = append(packages, pkg{
290 pkgpath: field[:pos],
291 llvmpath: field[pos+1:],
292 })
293 }
294 }
295
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000296 switch args[0] {
297 case "build", "get", "install", "run", "test":
Andrew Wilkins064517f2016-07-27 03:21:51 +0000298 runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, packages)
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000299 case "print-components":
300 printComponents()
301 case "print-config":
Andrew Wilkins93083d42016-01-20 04:03:09 +0000302 printConfig()
Peter Collingbournebdc3a5b2014-10-23 02:33:23 +0000303 default:
304 usage()
305 }
306}