1 //===-- llvm-go.go - go tool wrapper for LLVM -----------------------------===//
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
7 //===----------------------------------------------------------------------===//
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".
12 //===----------------------------------------------------------------------===//
27 linkmodeComponentLibs = "component-libs"
28 linkmodeDylib = "dylib"
32 llvmpath, pkgpath string
36 {"bindings/go/llvm", "llvm.org/llvm/bindings/go/llvm"},
39 type compilerFlags struct {
43 var components = []string{
70 func llvmConfig(args ...string) string {
71 configpath := os.Getenv("LLVM_CONFIG")
73 bin, _ := filepath.Split(os.Args[0])
74 configpath = filepath.Join(bin, "llvm90-config")
77 cmd := exec.Command(configpath, args...)
78 cmd.Stderr = os.Stderr
79 out, err := cmd.Output()
85 outstr = strings.TrimSuffix(outstr, "\n")
86 outstr = strings.Replace(outstr, "\n", " ", -1)
90 func llvmFlags() compilerFlags {
91 args := append([]string{"--ldflags", "--libs", "--system-libs"}, components...)
92 ldflags := llvmConfig(args...)
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++"
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
104 return compilerFlags{
105 cpp: llvmConfig("--cppflags"),
106 cxx: "-std=c++11" + " " + stdLibOption,
111 func addTag(args []string, tag string) []string {
112 args = append([]string{}, args...)
114 for i, a := range args {
115 if strings.HasPrefix(a, "-tags=") {
116 args[i] = a + " " + tag
118 } else if a == "-tags" && i+1 < len(args) {
119 args[i+1] = args[i+1] + " " + tag
124 args = append([]string{args[0], "-tags", tag}, args[1:]...)
129 func printComponents() {
130 fmt.Println(strings.Join(components, " "))
136 fmt.Printf(`// +build !byollvm
138 // This file is generated by llvm-go, do not edit.
149 type (run_build_sh int)
150 `, flags.cpp, flags.cxx, flags.ld)
153 func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags string, packages []pkg) {
154 args = addTag(args, "byollvm")
156 srcdir := llvmConfig("--src-root")
158 tmpgopath, err := ioutil.TempDir("", "gopath")
163 for _, p := range packages {
164 path := filepath.Join(tmpgopath, "src", p.pkgpath)
165 err := os.MkdirAll(filepath.Dir(path), os.ModePerm)
170 abspath := p.llvmpath
171 if !filepath.IsAbs(abspath) {
172 abspath = filepath.Join(srcdir, abspath)
175 err = os.Symlink(abspath, path)
181 newpath := os.Getenv("PATH")
183 newgopathlist := []string{tmpgopath}
184 newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...)
185 newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator))
192 "CGO_CPPFLAGS=" + flags.cpp + " " + cppflags,
193 "CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags,
194 "CGO_LDFLAGS=" + flags.ld + " " + ldflags,
195 "GOPATH=" + newgopath,
199 newenv = append(newenv, "GCCGO="+llgo)
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)
215 gocmdpath, err := exec.LookPath(gocmd)
220 proc, err := os.StartProcess(gocmdpath, append([]string{gocmd}, args...),
223 Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
228 ps, err := proc.Wait()
233 os.RemoveAll(tmpgopath)
241 fmt.Println(`Usage: llvm-go subcommand [flags]
243 Available subcommands: build get install run test print-components print-config`)
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")
265 {"cppflags", &cppflags},
266 {"ldflags", &ldflags},
267 {"packages", &packagesString},
276 for _, flag := range flags {
277 if strings.HasPrefix(args[0], flag.name+"=") {
278 *flag.dest = args[0][len(flag.name)+1:]
287 if packagesString != "" {
288 for _, field := range strings.Fields(packagesString) {
289 pos := strings.IndexRune(field, '=')
291 fmt.Fprintf(os.Stderr, "invalid packages value %q, expected 'pkgpath=llvmpath [pkgpath=llvmpath ...]'\n", packagesString)
294 packages = append(packages, pkg{
295 pkgpath: field[:pos],
296 llvmpath: field[pos+1:],
302 case "build", "get", "install", "run", "test":
303 runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, packages)
304 case "print-components":