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 "debuginfo", 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, 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 if llgo != "" { 166 bindir := filepath.Join(tmpgopath, "bin") 167 168 err = os.MkdirAll(bindir, os.ModePerm) 169 if err != nil { 170 panic(err.Error()) 171 } 172 173 err = os.Symlink(llgo, filepath.Join(bindir, "gccgo")) 174 if err != nil { 175 panic(err.Error()) 176 } 177 178 newpathlist := []string{bindir} 179 newpathlist = append(newpathlist, filepath.SplitList(newpath)...) 180 newpath = strings.Join(newpathlist, string(filepath.ListSeparator)) 181 182 args = append([]string{args[0], "-compiler", "gccgo"}, args[1:]...) 183 } 184 185 newgopathlist := []string{tmpgopath} 186 newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...) 187 newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator)) 188 189 flags := llvmFlags() 190 191 newenv := []string{ 192 "CC=" + cc, 193 "CXX=" + cxx, 194 "CGO_CPPFLAGS=" + flags.cpp + " " + cppflags, 195 "CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags, 196 "CGO_LDFLAGS=" + flags.ld + " " + ldflags, 197 "GOPATH=" + newgopath, 198 "PATH=" + newpath, 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, "GOPATH=") && 207 !strings.HasPrefix(v, "PATH=") { 208 newenv = append(newenv, v) 209 } 210 } 211 212 gocmdpath, err := exec.LookPath("go") 213 if err != nil { 214 panic(err.Error()) 215 } 216 217 proc, err := os.StartProcess(gocmdpath, append([]string{"go"}, args...), 218 &os.ProcAttr{ 219 Env: newenv, 220 Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}, 221 }) 222 if err != nil { 223 panic(err.Error()) 224 } 225 ps, err := proc.Wait() 226 if err != nil { 227 panic(err.Error()) 228 } 229 230 os.RemoveAll(tmpgopath) 231 232 if !ps.Success() { 233 os.Exit(1) 234 } 235} 236 237func usage() { 238 fmt.Println(`Usage: llvm-go subcommand [flags] 239 240Available subcommands: build get install run test print-components print-config`) 241 os.Exit(0) 242} 243 244func main() { 245 cc := os.Getenv("CC") 246 cxx := os.Getenv("CXX") 247 cppflags := os.Getenv("CGO_CPPFLAGS") 248 cxxflags := os.Getenv("CGO_CXXFLAGS") 249 ldflags := os.Getenv("CGO_LDFLAGS") 250 llgo := "" 251 252 args := os.Args[1:] 253 DONE: for { 254 switch { 255 case len(args) == 0: 256 usage() 257 case strings.HasPrefix(args[0], "cc="): 258 cc = args[0][3:] 259 args = args[1:] 260 case strings.HasPrefix(args[0], "cxx="): 261 cxx = args[0][4:] 262 args = args[1:] 263 case strings.HasPrefix(args[0], "llgo="): 264 llgo = args[0][5:] 265 args = args[1:] 266 case strings.HasPrefix(args[0], "cppflags="): 267 cppflags = args[0][9:] 268 args = args[1:] 269 case strings.HasPrefix(args[0], "cxxflags="): 270 cxxflags = args[0][9:] 271 args = args[1:] 272 case strings.HasPrefix(args[0], "ldflags="): 273 ldflags = args[0][8:] 274 args = args[1:] 275 default: 276 break DONE 277 } 278 } 279 280 switch args[0] { 281 case "build", "get", "install", "run", "test": 282 runGoWithLLVMEnv(args, cc, cxx, llgo, cppflags, cxxflags, ldflags) 283 case "print-components": 284 printComponents() 285 case "print-config": 286 printConfig() 287 default: 288 usage() 289 } 290} 291