d0f794177bbf8aad75e5a2378e10bf33874aa3d2
[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 const (
28         linkmodeComponentLibs = "component-libs"
29         linkmodeDylib         = "dylib"
30 )
31
32 type pkg struct {
33         llvmpath, pkgpath string
34 }
35
36 var packages = []pkg{
37         {"bindings/go/llvm", "llvm.org/llvm/bindings/go/llvm"},
38         {"tools/llgo", "llvm.org/llgo"},
39 }
40
41 type compilerFlags struct {
42         cpp, cxx, ld string
43 }
44
45 var 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
71 func 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
91 func llvmFlags() compilerFlags {
92         ldflags := llvmConfig("--ldflags", "--libs", "--system-libs")
93         if runtime.GOOS != "darwin" {
94                 // OS X doesn't like -rpath with cgo. See:
95                 // https://code.google.com/p/go/issues/detail?id=7293
96                 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
105 func 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
123 func printComponents() {
124         fmt.Println(strings.Join(components, " "))
125 }
126
127 func printConfig() {
128         flags := llvmFlags()
129
130         fmt.Printf(`// +build !byollvm
131
132 // This file is generated by llvm-go, do not edit.
133
134 package llvm
135
136 /*
137 #cgo CPPFLAGS: %s
138 #cgo CXXFLAGS: %s
139 #cgo LDFLAGS: %s
140 */
141 import "C"
142
143 type (run_build_sh int)
144 `, flags.cpp, flags.cxx, flags.ld)
145 }
146
147 func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags string) {
148         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
164                 err = os.Symlink(filepath.Join(srcdir, p.llvmpath), path)
165                 if err != nil {
166                         panic(err.Error())
167                 }
168         }
169
170         newpath := os.Getenv("PATH")
171
172         newgopathlist := []string{tmpgopath}
173         newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...)
174         newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator))
175
176         flags := llvmFlags()
177
178         newenv := []string{
179                 "CC=" + cc,
180                 "CXX=" + cxx,
181                 "CGO_CPPFLAGS=" + flags.cpp + " " + cppflags,
182                 "CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags,
183                 "CGO_LDFLAGS=" + flags.ld + " " + ldflags,
184                 "GOPATH=" + newgopath,
185                 "PATH=" + newpath,
186         }
187         if llgo != "" {
188                 newenv = append(newenv, "GCCGO="+llgo)
189         }
190
191         for _, v := range os.Environ() {
192                 if !strings.HasPrefix(v, "CC=") &&
193                         !strings.HasPrefix(v, "CXX=") &&
194                         !strings.HasPrefix(v, "CGO_CPPFLAGS=") &&
195                         !strings.HasPrefix(v, "CGO_CXXFLAGS=") &&
196                         !strings.HasPrefix(v, "CGO_LDFLAGS=") &&
197                         !strings.HasPrefix(v, "GCCGO=") &&
198                         !strings.HasPrefix(v, "GOPATH=") &&
199                         !strings.HasPrefix(v, "PATH=") {
200                         newenv = append(newenv, v)
201                 }
202         }
203
204         gocmdpath, err := exec.LookPath(gocmd)
205         if err != nil {
206                 panic(err.Error())
207         }
208
209         proc, err := os.StartProcess(gocmdpath, append([]string{gocmd}, args...),
210                 &os.ProcAttr{
211                         Env:   newenv,
212                         Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
213                 })
214         if err != nil {
215                 panic(err.Error())
216         }
217         ps, err := proc.Wait()
218         if err != nil {
219                 panic(err.Error())
220         }
221
222         os.RemoveAll(tmpgopath)
223
224         if !ps.Success() {
225                 os.Exit(1)
226         }
227 }
228
229 func usage() {
230         fmt.Println(`Usage: llvm-go subcommand [flags]
231
232 Available subcommands: build get install run test print-components print-config`)
233         os.Exit(0)
234 }
235
236 func main() {
237         cc := os.Getenv("CC")
238         cxx := os.Getenv("CXX")
239         cppflags := os.Getenv("CGO_CPPFLAGS")
240         cxxflags := os.Getenv("CGO_CXXFLAGS")
241         ldflags := os.Getenv("CGO_LDFLAGS")
242         gocmd := "go"
243         llgo := ""
244
245         flags := []struct {
246                 name string
247                 dest *string
248         }{
249                 {"cc", &cc},
250                 {"cxx", &cxx},
251                 {"go", &gocmd},
252                 {"llgo", &llgo},
253                 {"cppflags", &cppflags},
254                 {"ldflags", &ldflags},
255         }
256
257         args := os.Args[1:]
258 LOOP:
259         for {
260                 if len(args) == 0 {
261                         usage()
262                 }
263                 for _, flag := range flags {
264                         if strings.HasPrefix(args[0], flag.name+"=") {
265                                 *flag.dest = args[0][len(flag.name)+1:]
266                                 args = args[1:]
267                                 continue LOOP
268                         }
269                 }
270                 break
271         }
272
273         switch args[0] {
274         case "build", "get", "install", "run", "test":
275                 runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags)
276         case "print-components":
277                 printComponents()
278         case "print-config":
279                 printConfig()
280         default:
281                 usage()
282         }
283 }