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