Add llvm-go tool.
[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 }
34
35 type compilerFlags struct {
36         cpp, cxx, ld string
37 }
38
39 var components = []string{
40         "all-targets",
41         "analysis",
42         "asmparser",
43         "asmprinter",
44         "bitreader",
45         "bitwriter",
46         "codegen",
47         "core",
48         "debuginfo",
49         "executionengine",
50         "instrumentation",
51         "interpreter",
52         "ipo",
53         "irreader",
54         "linker",
55         "mc",
56         "mcjit",
57         "objcarcopts",
58         "option",
59         "profiledata",
60         "scalaropts",
61         "support",
62         "target",
63 }
64
65 func llvmConfig(args ...string) string {
66         configpath := os.Getenv("LLVM_CONFIG")
67         if configpath == "" {
68                 // strip llvm-go, add llvm-config
69                 configpath = os.Args[0][:len(os.Args[0])-7] + "llvm-config"
70         }
71
72         cmd := exec.Command(configpath, args...)
73         out, err := cmd.Output()
74         if err != nil {
75                 panic(err.Error())
76         }
77
78         outstr := string(out)
79         outstr = strings.TrimSuffix(outstr, "\n")
80         return strings.Replace(outstr, "\n", " ", -1)
81 }
82
83 func llvmFlags() compilerFlags {
84         ldflags := llvmConfig(append([]string{"--ldflags", "--libs", "--system-libs"}, components...)...)
85         if runtime.GOOS != "darwin" {
86                 // OS X doesn't like -rpath with cgo. See:
87                 // https://code.google.com/p/go/issues/detail?id=7293
88                 ldflags = "-Wl,-rpath," + llvmConfig("--libdir") + " " + ldflags
89         }
90         return compilerFlags{
91                 cpp: llvmConfig("--cppflags"),
92                 cxx: "-std=c++11",
93                 ld:  ldflags,
94         }
95 }
96
97 func addTag(args []string, tag string) []string {
98         args = append([]string{}, args...)
99         addedTag := false
100         for i, a := range args {
101                 if strings.HasPrefix(a, "-tags=") {
102                         args[i] = a + " " + tag
103                         addedTag = true
104                 } else if a == "-tags" && i+1 < len(args) {
105                         args[i+1] = args[i+1] + " " + tag
106                         addedTag = true
107                 }
108         }
109         if !addedTag {
110                 args = append([]string{args[0], "-tags", tag}, args[1:]...)
111         }
112         return args
113 }
114
115 func printComponents() {
116         fmt.Println(strings.Join(components, " "))
117 }
118
119 func printConfig() {
120         flags := llvmFlags()
121
122         fmt.Printf(`// +build !byollvm
123
124 // This file is generated by llvm-go, do not edit.
125
126 package llvm
127
128 /*
129 #cgo CPPFLAGS: %s
130 #cgo CXXFLAGS: %s
131 #cgo LDFLAGS: %s
132 */
133 import "C"
134
135 type (run_build_sh int)
136 `, flags.cpp, flags.cxx, flags.ld)
137 }
138
139 func runGoWithLLVMEnv(args []string, cc, cxx, cppflags, cxxflags, ldflags string) {
140         args = addTag(args, "byollvm")
141
142         srcdir := llvmConfig("--src-root")
143
144         tmpgopath, err := ioutil.TempDir("", "gopath")
145         if err != nil {
146                 panic(err.Error())
147         }
148
149         for _, p := range packages {
150                 path := filepath.Join(tmpgopath, "src", p.pkgpath)
151                 err := os.MkdirAll(filepath.Dir(path), os.ModePerm)
152                 if err != nil {
153                         panic(err.Error())
154                 }
155
156                 err = os.Symlink(filepath.Join(srcdir, p.llvmpath), path)
157                 if err != nil {
158                         panic(err.Error())
159                 }
160         }
161
162         newgopathlist := []string{tmpgopath}
163         newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...)
164         newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator))
165
166         flags := llvmFlags()
167
168         newenv := []string{
169                 "CC=" + cc,
170                 "CXX=" + cxx,
171                 "CGO_CPPFLAGS=" + flags.cpp + " " + cppflags,
172                 "CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags,
173                 "CGO_LDFLAGS=" + flags.ld + " " + ldflags,
174                 "GOPATH=" + newgopath,
175         }
176         for _, v := range os.Environ() {
177                 if !strings.HasPrefix(v, "CC=") &&
178                         !strings.HasPrefix(v, "CXX=") &&
179                         !strings.HasPrefix(v, "CGO_CPPFLAGS=") &&
180                         !strings.HasPrefix(v, "CGO_CXXFLAGS=") &&
181                         !strings.HasPrefix(v, "CGO_LDFLAGS=") &&
182                         !strings.HasPrefix(v, "GOPATH=") {
183                         newenv = append(newenv, v)
184                 }
185         }
186
187         gocmdpath, err := exec.LookPath("go")
188         if err != nil {
189                 panic(err.Error())
190         }
191
192         proc, err := os.StartProcess(gocmdpath, append([]string{"go"}, args...),
193                 &os.ProcAttr{
194                         Env:   newenv,
195                         Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
196                 })
197         if err != nil {
198                 panic(err.Error())
199         }
200         ps, err := proc.Wait()
201         if err != nil {
202                 panic(err.Error())
203         }
204
205         os.RemoveAll(tmpgopath)
206
207         if !ps.Success() {
208                 os.Exit(1)
209         }
210 }
211
212 func usage() {
213         fmt.Println(`Usage: llvm-go subcommand [flags]
214
215 Available subcommands: build get install run test print-components print-config`)
216         os.Exit(0)
217 }
218
219 func main() {
220         cc := os.Getenv("CC")
221         cxx := os.Getenv("CXX")
222         cppflags := os.Getenv("CGO_CPPFLAGS")
223         cxxflags := os.Getenv("CGO_CXXFLAGS")
224         ldflags := os.Getenv("CGO_LDFLAGS")
225
226         args := os.Args[1:]
227         DONE: for {
228                 switch {
229                 case len(args) == 0:
230                         usage()
231                 case strings.HasPrefix(args[0], "cc="):
232                         cc = args[0][3:]
233                         args = args[1:]
234                 case strings.HasPrefix(args[0], "cxx="):
235                         cxx = args[0][4:]
236                         args = args[1:]
237                 case strings.HasPrefix(args[0], "cppflags="):
238                         cppflags = args[0][9:]
239                         args = args[1:]
240                 case strings.HasPrefix(args[0], "cxxflags="):
241                         cxxflags = args[0][9:]
242                         args = args[1:]
243                 case strings.HasPrefix(args[0], "ldflags="):
244                         ldflags = args[0][8:]
245                         args = args[1:]
246                 default:
247                         break DONE
248                 }
249         }
250
251         switch args[0] {
252         case "build", "get", "install", "run", "test":
253                 runGoWithLLVMEnv(args, cc, cxx, cppflags, cxxflags, ldflags)
254         case "print-components":
255                 printComponents()
256         case "print-config":
257                 printConfig()
258         default:
259                 usage()
260         }
261 }