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
, "llvm-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++14" + " " + 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":