Revert r257003
[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(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")
102         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
114 func 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
132 func printComponents() {
133         fmt.Println(strings.Join(components, " "))
134 }
135
136 func printConfig(linkmode string) {
137         flags := llvmFlags(linkmode)
138
139         fmt.Printf(`// +build !byollvm
140
141 // This file is generated by llvm-go, do not edit.
142
143 package llvm
144
145 /*
146 #cgo CPPFLAGS: %s
147 #cgo CXXFLAGS: %s
148 #cgo LDFLAGS: %s
149 */
150 import "C"
151
152 type (run_build_sh int)
153 `, flags.cpp, flags.cxx, flags.ld)
154 }
155
156 func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, linkmode string) {
157         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
179         newpath := os.Getenv("PATH")
180
181         newgopathlist := []string{tmpgopath}
182         newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...)
183         newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator))
184
185         flags := llvmFlags(linkmode)
186
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,
194                 "PATH=" + newpath,
195         }
196         if llgo != "" {
197                 newenv = append(newenv, "GCCGO="+llgo)
198         }
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, "GCCGO=") &&
207                         !strings.HasPrefix(v, "GOPATH=") &&
208                         !strings.HasPrefix(v, "PATH=") {
209                         newenv = append(newenv, v)
210                 }
211         }
212
213         gocmdpath, err := exec.LookPath(gocmd)
214         if err != nil {
215                 panic(err.Error())
216         }
217
218         proc, err := os.StartProcess(gocmdpath, append([]string{gocmd}, args...),
219                 &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
238 func usage() {
239         fmt.Println(`Usage: llvm-go subcommand [flags]
240
241 Available subcommands: build get install run test print-components print-config`)
242         os.Exit(0)
243 }
244
245 func 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")
251         gocmd := "go"
252         llgo := ""
253         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         }
267
268         args := os.Args[1:]
269 LOOP:
270         for {
271                 if len(args) == 0 {
272                         usage()
273                 }
274                 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
282         }
283
284         switch args[0] {
285         case "build", "get", "install", "run", "test":
286                 runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, linkmode)
287         case "print-components":
288                 printComponents()
289         case "print-config":
290                 printConfig(linkmode)
291         default:
292                 usage()
293         }
294 }