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