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 } 39 40 type compilerFlags struct { 41 cpp, cxx, ld string 42 } 43 44 var components = []string{ 45 "all-targets", 46 "analysis", 47 "asmparser", 48 "asmprinter", 49 "bitreader", 50 "bitwriter", 51 "codegen", 52 "core", 53 "debuginfodwarf", 54 "executionengine", 55 "instrumentation", 56 "interpreter", 57 "ipo", 58 "irreader", 59 "linker", 60 "mc", 61 "mcjit", 62 "objcarcopts", 63 "option", 64 "profiledata", 65 "scalaropts", 66 "support", 67 "target", 68 } 69 70 func llvmConfig(args ...string) string { 71 configpath := os.Getenv("LLVM_CONFIG") 72 if configpath == "" { 73 bin, _ := filepath.Split(os.Args[0]) 74 configpath = filepath.Join(bin, "llvm-config") 75 } 76 77 cmd := exec.Command(configpath, args...) 78 cmd.Stderr = os.Stderr 79 out, err := cmd.Output() 80 if err != nil { 81 panic(err.Error()) 82 } 83 84 outstr := string(out) 85 outstr = strings.TrimSuffix(outstr, "\n") 86 outstr = strings.Replace(outstr, "\n", " ", -1) 87 return outstr 88 } 89 90 func llvmFlags() compilerFlags { 91 args := append([]string{"--ldflags", "--libs", "--system-libs"}, components...) 92 ldflags := llvmConfig(args...) 93 if runtime.GOOS != "darwin" { 94 // OS X doesn't like -rpath with cgo. See: 95 // https://github.com/golang/go/issues/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, packages []pkg) { 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 abspath := p.llvmpath 165 if !filepath.IsAbs(abspath) { 166 abspath = filepath.Join(srcdir, abspath) 167 } 168 169 err = os.Symlink(abspath, path) 170 if err != nil { 171 panic(err.Error()) 172 } 173 } 174 175 newpath := os.Getenv("PATH") 176 177 newgopathlist := []string{tmpgopath} 178 newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...) 179 newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator)) 180 181 flags := llvmFlags() 182 183 newenv := []string{ 184 "CC=" + cc, 185 "CXX=" + cxx, 186 "CGO_CPPFLAGS=" + flags.cpp + " " + cppflags, 187 "CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags, 188 "CGO_LDFLAGS=" + flags.ld + " " + ldflags, 189 "GOPATH=" + newgopath, 190 "PATH=" + newpath, 191 } 192 if llgo != "" { 193 newenv = append(newenv, "GCCGO="+llgo) 194 } 195 196 for _, v := range os.Environ() { 197 if !strings.HasPrefix(v, "CC=") && 198 !strings.HasPrefix(v, "CXX=") && 199 !strings.HasPrefix(v, "CGO_CPPFLAGS=") && 200 !strings.HasPrefix(v, "CGO_CXXFLAGS=") && 201 !strings.HasPrefix(v, "CGO_LDFLAGS=") && 202 !strings.HasPrefix(v, "GCCGO=") && 203 !strings.HasPrefix(v, "GOPATH=") && 204 !strings.HasPrefix(v, "PATH=") { 205 newenv = append(newenv, v) 206 } 207 } 208 209 gocmdpath, err := exec.LookPath(gocmd) 210 if err != nil { 211 panic(err.Error()) 212 } 213 214 proc, err := os.StartProcess(gocmdpath, append([]string{gocmd}, args...), 215 &os.ProcAttr{ 216 Env: newenv, 217 Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}, 218 }) 219 if err != nil { 220 panic(err.Error()) 221 } 222 ps, err := proc.Wait() 223 if err != nil { 224 panic(err.Error()) 225 } 226 227 os.RemoveAll(tmpgopath) 228 229 if !ps.Success() { 230 os.Exit(1) 231 } 232 } 233 234 func usage() { 235 fmt.Println(`Usage: llvm-go subcommand [flags] 236 237 Available subcommands: build get install run test print-components print-config`) 238 os.Exit(0) 239 } 240 241 func main() { 242 cc := os.Getenv("CC") 243 cxx := os.Getenv("CXX") 244 cppflags := os.Getenv("CGO_CPPFLAGS") 245 cxxflags := os.Getenv("CGO_CXXFLAGS") 246 ldflags := os.Getenv("CGO_LDFLAGS") 247 gocmd := "go" 248 llgo := "" 249 packagesString := "" 250 251 flags := []struct { 252 name string 253 dest *string 254 }{ 255 {"cc", &cc}, 256 {"cxx", &cxx}, 257 {"go", &gocmd}, 258 {"llgo", &llgo}, 259 {"cppflags", &cppflags}, 260 {"ldflags", &ldflags}, 261 {"packages", &packagesString}, 262 } 263 264 args := os.Args[1:] 265 LOOP: 266 for { 267 if len(args) == 0 { 268 usage() 269 } 270 for _, flag := range flags { 271 if strings.HasPrefix(args[0], flag.name+"=") { 272 *flag.dest = args[0][len(flag.name)+1:] 273 args = args[1:] 274 continue LOOP 275 } 276 } 277 break 278 } 279 280 packages := packages 281 if packagesString != "" { 282 for _, field := range strings.Fields(packagesString) { 283 pos := strings.IndexRune(field, '=') 284 if pos == -1 { 285 fmt.Fprintf(os.Stderr, "invalid packages value %q, expected 'pkgpath=llvmpath [pkgpath=llvmpath ...]'\n", packagesString) 286 os.Exit(1) 287 } 288 packages = append(packages, pkg{ 289 pkgpath: field[:pos], 290 llvmpath: field[pos+1:], 291 }) 292 } 293 } 294 295 switch args[0] { 296 case "build", "get", "install", "run", "test": 297 runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, packages) 298 case "print-components": 299 printComponents() 300 case "print-config": 301 printConfig() 302 default: 303 usage() 304 } 305 } 306