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(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 114func 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 132func printComponents() { 133 fmt.Println(strings.Join(components, " ")) 134} 135 136func 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 143package llvm 144 145/* 146#cgo CPPFLAGS: %s 147#cgo CXXFLAGS: %s 148#cgo LDFLAGS: %s 149*/ 150import "C" 151 152type (run_build_sh int) 153`, flags.cpp, flags.cxx, flags.ld) 154} 155 156func 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 238func usage() { 239 fmt.Println(`Usage: llvm-go subcommand [flags] 240 241Available subcommands: build get install run test print-components print-config`) 242 os.Exit(0) 243} 244 245func 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:] 269LOOP: 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} 295