Teach LLVM about llgo subproject.
[oota-llvm.git] / tools / llvm-go / llvm-go.go
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
15 package main
16
17 import (
18         "fmt"
19         "io/ioutil"
20         "os"
21         "os/exec"
22         "path/filepath"
23         "runtime"
24         "strings"
25 )
26
27 type pkg struct {
28         llvmpath, pkgpath string
29 }
30
31 var packages = []pkg{
32         {"bindings/go/llvm", "llvm.org/llvm/bindings/go/llvm"},
33         {"tools/llgo", "llvm.org/llgo"},
34 }
35
36 type compilerFlags struct {
37         cpp, cxx, ld string
38 }
39
40 var 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
66 func 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
84 func 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
98 func 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
116 func printComponents() {
117         fmt.Println(strings.Join(components, " "))
118 }
119
120 func printConfig() {
121         flags := llvmFlags()
122
123         fmt.Printf(`// +build !byollvm
124
125 // This file is generated by llvm-go, do not edit.
126
127 package llvm
128
129 /*
130 #cgo CPPFLAGS: %s
131 #cgo CXXFLAGS: %s
132 #cgo LDFLAGS: %s
133 */
134 import "C"
135
136 type (run_build_sh int)
137 `, flags.cpp, flags.cxx, flags.ld)
138 }
139
140 func 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
237 func usage() {
238         fmt.Println(`Usage: llvm-go subcommand [flags]
239
240 Available subcommands: build get install run test print-components print-config`)
241         os.Exit(0)
242 }
243
244 func 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 }