1//===-- llvm-go.go - go tool wrapper for LLVM -----------------------------===// 2// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6// 7//===----------------------------------------------------------------------===// 8// 9// This tool lets us build LLVM components within the tree by setting up a 10// $GOPATH that resembles a tree fetched in the normal way with "go get". 11// 12//===----------------------------------------------------------------------===// 13 14package main 15 16import ( 17 "fmt" 18 "io/ioutil" 19 "os" 20 "os/exec" 21 "path/filepath" 22 "runtime" 23 "strings" 24) 25 26const ( 27 linkmodeComponentLibs = "component-libs" 28 linkmodeDylib = "dylib" 29) 30 31type pkg struct { 32 llvmpath, pkgpath string 33} 34 35var packages = []pkg{ 36 {"bindings/go/llvm", "llvm.org/llvm/bindings/go/llvm"}, 37} 38 39type compilerFlags struct { 40 cpp, cxx, ld string 41} 42 43var components = []string{ 44 "all-targets", 45 "analysis", 46 "asmparser", 47 "asmprinter", 48 "bitreader", 49 "bitwriter", 50 "codegen", 51 "core", 52 "coroutines", 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 70func 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 90func llvmFlags() compilerFlags { 91 args := append([]string{"--ldflags", "--libs", "--system-libs"}, components...) 92 ldflags := llvmConfig(args...) 93 stdLibOption := "" 94 if strings.Contains(llvmConfig("--cxxflags"), "-stdlib=libc++") { 95 // If libc++ is used to build LLVM libraries, -stdlib=libc++ is 96 // needed to resolve dependent symbols 97 stdLibOption = "-stdlib=libc++" 98 } 99 if runtime.GOOS != "darwin" { 100 // OS X doesn't like -rpath with cgo. See: 101 // https://github.com/golang/go/issues/7293 102 ldflags = "-Wl,-rpath," + llvmConfig("--libdir") + " " + ldflags 103 } 104 return compilerFlags{ 105 cpp: llvmConfig("--cppflags"), 106 cxx: "-std=c++14" + " " + stdLibOption, 107 ld: ldflags, 108 } 109} 110 111func addTag(args []string, tag string) []string { 112 args = append([]string{}, args...) 113 addedTag := false 114 for i, a := range args { 115 if strings.HasPrefix(a, "-tags=") { 116 args[i] = a + " " + tag 117 addedTag = true 118 } else if a == "-tags" && i+1 < len(args) { 119 args[i+1] = args[i+1] + " " + tag 120 addedTag = true 121 } 122 } 123 if !addedTag { 124 args = append([]string{args[0], "-tags", tag}, args[1:]...) 125 } 126 return args 127} 128 129func printComponents() { 130 fmt.Println(strings.Join(components, " ")) 131} 132 133func printConfig() { 134 flags := llvmFlags() 135 136 fmt.Printf(`// +build !byollvm 137 138// This file is generated by llvm-go, do not edit. 139 140package llvm 141 142/* 143#cgo CPPFLAGS: %s 144#cgo CXXFLAGS: %s 145#cgo LDFLAGS: %s 146*/ 147import "C" 148 149type (run_build_sh int) 150`, flags.cpp, flags.cxx, flags.ld) 151} 152 153func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags string, packages []pkg) { 154 args = addTag(args, "byollvm") 155 156 srcdir := llvmConfig("--src-root") 157 158 tmpgopath, err := ioutil.TempDir("", "gopath") 159 if err != nil { 160 panic(err.Error()) 161 } 162 163 for _, p := range packages { 164 path := filepath.Join(tmpgopath, "src", p.pkgpath) 165 err := os.MkdirAll(filepath.Dir(path), os.ModePerm) 166 if err != nil { 167 panic(err.Error()) 168 } 169 170 abspath := p.llvmpath 171 if !filepath.IsAbs(abspath) { 172 abspath = filepath.Join(srcdir, abspath) 173 } 174 175 err = os.Symlink(abspath, path) 176 if err != nil { 177 panic(err.Error()) 178 } 179 } 180 181 newpath := os.Getenv("PATH") 182 183 newgopathlist := []string{tmpgopath} 184 newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...) 185 newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator)) 186 187 flags := llvmFlags() 188 189 newenv := []string{ 190 "CC=" + cc, 191 "CXX=" + cxx, 192 "CGO_CPPFLAGS=" + flags.cpp + " " + cppflags, 193 "CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags, 194 "CGO_LDFLAGS=" + flags.ld + " " + ldflags, 195 "GOPATH=" + newgopath, 196 "PATH=" + newpath, 197 } 198 if llgo != "" { 199 newenv = append(newenv, "GCCGO="+llgo) 200 } 201 202 for _, v := range os.Environ() { 203 if !strings.HasPrefix(v, "CC=") && 204 !strings.HasPrefix(v, "CXX=") && 205 !strings.HasPrefix(v, "CGO_CPPFLAGS=") && 206 !strings.HasPrefix(v, "CGO_CXXFLAGS=") && 207 !strings.HasPrefix(v, "CGO_LDFLAGS=") && 208 !strings.HasPrefix(v, "GCCGO=") && 209 !strings.HasPrefix(v, "GOPATH=") && 210 !strings.HasPrefix(v, "PATH=") { 211 newenv = append(newenv, v) 212 } 213 } 214 215 gocmdpath, err := exec.LookPath(gocmd) 216 if err != nil { 217 panic(err.Error()) 218 } 219 220 proc, err := os.StartProcess(gocmdpath, append([]string{gocmd}, args...), 221 &os.ProcAttr{ 222 Env: newenv, 223 Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}, 224 }) 225 if err != nil { 226 panic(err.Error()) 227 } 228 ps, err := proc.Wait() 229 if err != nil { 230 panic(err.Error()) 231 } 232 233 os.RemoveAll(tmpgopath) 234 235 if !ps.Success() { 236 os.Exit(1) 237 } 238} 239 240func usage() { 241 fmt.Println(`Usage: llvm-go subcommand [flags] 242 243Available subcommands: build get install run test print-components print-config`) 244 os.Exit(0) 245} 246 247func main() { 248 cc := os.Getenv("CC") 249 cxx := os.Getenv("CXX") 250 cppflags := os.Getenv("CGO_CPPFLAGS") 251 cxxflags := os.Getenv("CGO_CXXFLAGS") 252 ldflags := os.Getenv("CGO_LDFLAGS") 253 gocmd := "go" 254 llgo := "" 255 packagesString := "" 256 257 flags := []struct { 258 name string 259 dest *string 260 }{ 261 {"cc", &cc}, 262 {"cxx", &cxx}, 263 {"go", &gocmd}, 264 {"llgo", &llgo}, 265 {"cppflags", &cppflags}, 266 {"ldflags", &ldflags}, 267 {"packages", &packagesString}, 268 } 269 270 args := os.Args[1:] 271LOOP: 272 for { 273 if len(args) == 0 { 274 usage() 275 } 276 for _, flag := range flags { 277 if strings.HasPrefix(args[0], flag.name+"=") { 278 *flag.dest = args[0][len(flag.name)+1:] 279 args = args[1:] 280 continue LOOP 281 } 282 } 283 break 284 } 285 286 packages := packages 287 if packagesString != "" { 288 for _, field := range strings.Fields(packagesString) { 289 pos := strings.IndexRune(field, '=') 290 if pos == -1 { 291 fmt.Fprintf(os.Stderr, "invalid packages value %q, expected 'pkgpath=llvmpath [pkgpath=llvmpath ...]'\n", packagesString) 292 os.Exit(1) 293 } 294 packages = append(packages, pkg{ 295 pkgpath: field[:pos], 296 llvmpath: field[pos+1:], 297 }) 298 } 299 } 300 301 switch args[0] { 302 case "build", "get", "install", "run", "test": 303 runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, packages) 304 case "print-components": 305 printComponents() 306 case "print-config": 307 printConfig() 308 default: 309 usage() 310 } 311} 312