Add llvm-go tool.

This tool lets us build LLVM components within the tree by setting up a
$GOPATH that resembles a tree fetched in the normal way with "go get".

It is intended that components such as the Go frontend will be built in-tree
using this tool.

Differential Revision: http://reviews.llvm.org/D5902

llvm-svn: 220462
This commit is contained in:
Peter Collingbourne 2014-10-23 02:33:23 +00:00
parent 30428bc844
commit 244ecf55bd
10 changed files with 313 additions and 56 deletions

View File

@ -1,36 +1,5 @@
#!/bin/sh -xe #!/bin/sh -xe
llvm_components="\
all-targets \
analysis \
asmparser \
asmprinter \
bitreader \
bitwriter \
codegen \
core \
debuginfo \
executionengine \
instrumentation \
interpreter \
ipo \
irreader \
linker \
mc \
mcjit \
objcarcopts \
option \
profiledata \
scalaropts \
support \
target \
"
if [ "$1" = "--print-components" ] ; then
echo $llvm_components
exit 0
fi
gollvmdir=$(dirname "$0")/llvm gollvmdir=$(dirname "$0")/llvm
workdir=$gollvmdir/workdir workdir=$gollvmdir/workdir
@ -41,12 +10,14 @@ mkdir -p $llvm_builddir
cmake_flags="../../../../.. $@" cmake_flags="../../../../.. $@"
llvm_config="$llvm_builddir/bin/llvm-config" llvm_config="$llvm_builddir/bin/llvm-config"
llvm_go="$llvm_builddir/bin/llvm-go"
if test -n "`which ninja`" ; then if test -n "`which ninja`" ; then
# If Ninja is available, we can speed up the build by building only the # If Ninja is available, we can speed up the build by building only the
# required subset of LLVM. # required subset of LLVM.
(cd $llvm_builddir && cmake -G Ninja $cmake_flags) (cd $llvm_builddir && cmake -G Ninja $cmake_flags)
ninja -C $llvm_builddir llvm-config ninja -C $llvm_builddir llvm-config llvm-go
llvm_components="$($llvm_go print-components)"
llvm_buildtargets="$($llvm_config --libs $llvm_components | sed -e 's/-l//g')" llvm_buildtargets="$($llvm_config --libs $llvm_components | sed -e 's/-l//g')"
ninja -C $llvm_builddir $llvm_buildtargets FileCheck ninja -C $llvm_builddir $llvm_buildtargets FileCheck
else else
@ -54,14 +25,7 @@ else
make -C $llvm_builddir -j4 make -C $llvm_builddir -j4
fi fi
$llvm_go print-config > $gollvmdir/llvm_config.go
llvm_version="$($llvm_config --version)" llvm_version="$($llvm_config --version)"
llvm_cflags="$($llvm_config --cppflags)"
llvm_ldflags="$($llvm_config --ldflags) $($llvm_config --libs $llvm_components) $($llvm_config --system-libs)"
if [ $(uname) != "Darwin" ]; then
# OS X doesn't like -rpath with cgo. See:
# https://code.google.com/p/go/issues/detail?id=7293
llvm_ldflags="-Wl,-rpath,$($llvm_config --libdir) $llvm_ldflags"
fi
sed -e "s#@LLVM_CFLAGS@#$llvm_cflags#g; s#@LLVM_LDFLAGS@#$llvm_ldflags#g" $gollvmdir/llvm_config.go.in > \
$gollvmdir/llvm_config.go
printf "package llvm\n\nconst Version = \"%s\"\n" "$llvm_version" > $gollvmdir/version.go printf "package llvm\n\nconst Version = \"%s\"\n" "$llvm_version" > $gollvmdir/version.go

View File

@ -493,16 +493,20 @@ else()
endif() endif()
set(LLVM_BINDINGS "") set(LLVM_BINDINGS "")
find_program(GO_EXECUTABLE NAMES go DOC "go executable") if(WIN32)
if(GO_EXECUTABLE STREQUAL "GO_EXECUTABLE-NOTFOUND")
message(STATUS "Go bindings disabled.") message(STATUS "Go bindings disabled.")
else() else()
execute_process(COMMAND ${GO_EXECUTABLE} run ${CMAKE_SOURCE_DIR}/bindings/go/conftest.go find_program(GO_EXECUTABLE NAMES go DOC "go executable")
RESULT_VARIABLE GO_CONFTEST) if(GO_EXECUTABLE STREQUAL "GO_EXECUTABLE-NOTFOUND")
if(GO_CONFTEST STREQUAL "0") message(STATUS "Go bindings disabled.")
set(LLVM_BINDINGS "${LLVM_BINDINGS} go")
message(STATUS "Go bindings enabled.")
else() else()
message(STATUS "Go bindings disabled, need at least Go 1.2.") execute_process(COMMAND ${GO_EXECUTABLE} run ${CMAKE_SOURCE_DIR}/bindings/go/conftest.go
RESULT_VARIABLE GO_CONFTEST)
if(GO_CONFTEST STREQUAL "0")
set(LLVM_BINDINGS "${LLVM_BINDINGS} go")
message(STATUS "Go bindings enabled.")
else()
message(STATUS "Go bindings disabled, need at least Go 1.2.")
endif()
endif() endif()
endif() endif()

View File

@ -1,8 +1,3 @@
; RUN: cd %S/../../../bindings/go/llvm && \ ; RUN: llvm-go test llvm.org/llvm/bindings/go/llvm
; RUN: env CGO_CPPFLAGS="$(llvm-config --cppflags)" \
; RUN: CGO_CXXFLAGS=-std=c++11 \
; RUN: CGO_LDFLAGS="$(llvm-config --ldflags --libs --system-libs \
; RUN: $(../build.sh --print-components)) $CGO_LDFLAGS" \
; RUN: %go test -tags byollvm .
; REQUIRES: shell ; REQUIRES: shell

View File

@ -30,8 +30,9 @@ set(LLVM_TEST_DEPENDS
llvm-cov llvm-cov
llvm-diff llvm-diff
llvm-dis llvm-dis
llvm-extract
llvm-dwarfdump llvm-dwarfdump
llvm-extract
llvm-go
llvm-link llvm-link
llvm-lto llvm-lto
llvm-mc llvm-mc

View File

@ -200,6 +200,7 @@ for pattern in [r"\bbugpoint\b(?!-)",
r"\bllvm-dis\b", r"\bllvm-dis\b",
r"\bllvm-dwarfdump\b", r"\bllvm-dwarfdump\b",
r"\bllvm-extract\b", r"\bllvm-extract\b",
r"\bllvm-go\b",
r"\bllvm-link\b", r"\bllvm-link\b",
r"\bllvm-lto\b", r"\bllvm-lto\b",
r"\bllvm-mc\b", r"\bllvm-mc\b",

View File

@ -52,6 +52,8 @@ add_llvm_tool_subdirectory(llvm-c-test)
add_llvm_tool_subdirectory(obj2yaml) add_llvm_tool_subdirectory(obj2yaml)
add_llvm_tool_subdirectory(yaml2obj) add_llvm_tool_subdirectory(yaml2obj)
add_llvm_tool_subdirectory(llvm-go)
if(NOT CYGWIN AND LLVM_ENABLE_PIC) if(NOT CYGWIN AND LLVM_ENABLE_PIC)
add_llvm_tool_subdirectory(lto) add_llvm_tool_subdirectory(lto)
add_llvm_tool_subdirectory(llvm-lto) add_llvm_tool_subdirectory(llvm-lto)

View File

@ -74,4 +74,8 @@ ifneq ($(ENABLE_SHARED),1)
endif endif
endif endif
ifneq (,$(filter go,$(BINDINGS_TO_BUILD)))
PARALLEL_DIRS += llvm-go
endif
include $(LEVEL)/Makefile.common include $(LEVEL)/Makefile.common

View File

@ -0,0 +1,9 @@
if(LLVM_BINDINGS MATCHES "go")
set(binpath ${CMAKE_BINARY_DIR}/bin/llvm-go${CMAKE_EXECUTABLE_SUFFIX})
add_custom_command(OUTPUT ${binpath}
COMMAND ${GO_EXECUTABLE} build -o ${binpath} llvm-go.go
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/llvm-go.go
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Building Go executable llvm-go")
add_custom_target(llvm-go ALL DEPENDS ${binpath})
endif()

View File

@ -0,0 +1,16 @@
##===- tools/llvm-go/Makefile ------------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
LEVEL := ../..
include $(LEVEL)/Makefile.common
all:: $(ToolDir)/llvm-go$(EXEEXT)
$(ToolDir)/llvm-go$(EXEEXT): $(PROJ_SRC_DIR)/llvm-go.go
$(GO) build -o $@ $<

View File

@ -0,0 +1,261 @@
//===-- llvm-go.go - go tool wrapper for LLVM -----------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This tool lets us build LLVM components within the tree by setting up a
// $GOPATH that resembles a tree fetched in the normal way with "go get".
//
//===----------------------------------------------------------------------===//
package main
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
)
type pkg struct {
llvmpath, pkgpath string
}
var packages = []pkg{
{"bindings/go/llvm", "llvm.org/llvm/bindings/go/llvm"},
}
type compilerFlags struct {
cpp, cxx, ld string
}
var components = []string{
"all-targets",
"analysis",
"asmparser",
"asmprinter",
"bitreader",
"bitwriter",
"codegen",
"core",
"debuginfo",
"executionengine",
"instrumentation",
"interpreter",
"ipo",
"irreader",
"linker",
"mc",
"mcjit",
"objcarcopts",
"option",
"profiledata",
"scalaropts",
"support",
"target",
}
func llvmConfig(args ...string) string {
configpath := os.Getenv("LLVM_CONFIG")
if configpath == "" {
// strip llvm-go, add llvm-config
configpath = os.Args[0][:len(os.Args[0])-7] + "llvm-config"
}
cmd := exec.Command(configpath, args...)
out, err := cmd.Output()
if err != nil {
panic(err.Error())
}
outstr := string(out)
outstr = strings.TrimSuffix(outstr, "\n")
return strings.Replace(outstr, "\n", " ", -1)
}
func llvmFlags() compilerFlags {
ldflags := llvmConfig(append([]string{"--ldflags", "--libs", "--system-libs"}, components...)...)
if runtime.GOOS != "darwin" {
// OS X doesn't like -rpath with cgo. See:
// https://code.google.com/p/go/issues/detail?id=7293
ldflags = "-Wl,-rpath," + llvmConfig("--libdir") + " " + ldflags
}
return compilerFlags{
cpp: llvmConfig("--cppflags"),
cxx: "-std=c++11",
ld: ldflags,
}
}
func addTag(args []string, tag string) []string {
args = append([]string{}, args...)
addedTag := false
for i, a := range args {
if strings.HasPrefix(a, "-tags=") {
args[i] = a + " " + tag
addedTag = true
} else if a == "-tags" && i+1 < len(args) {
args[i+1] = args[i+1] + " " + tag
addedTag = true
}
}
if !addedTag {
args = append([]string{args[0], "-tags", tag}, args[1:]...)
}
return args
}
func printComponents() {
fmt.Println(strings.Join(components, " "))
}
func printConfig() {
flags := llvmFlags()
fmt.Printf(`// +build !byollvm
// This file is generated by llvm-go, do not edit.
package llvm
/*
#cgo CPPFLAGS: %s
#cgo CXXFLAGS: %s
#cgo LDFLAGS: %s
*/
import "C"
type (run_build_sh int)
`, flags.cpp, flags.cxx, flags.ld)
}
func runGoWithLLVMEnv(args []string, cc, cxx, cppflags, cxxflags, ldflags string) {
args = addTag(args, "byollvm")
srcdir := llvmConfig("--src-root")
tmpgopath, err := ioutil.TempDir("", "gopath")
if err != nil {
panic(err.Error())
}
for _, p := range packages {
path := filepath.Join(tmpgopath, "src", p.pkgpath)
err := os.MkdirAll(filepath.Dir(path), os.ModePerm)
if err != nil {
panic(err.Error())
}
err = os.Symlink(filepath.Join(srcdir, p.llvmpath), path)
if err != nil {
panic(err.Error())
}
}
newgopathlist := []string{tmpgopath}
newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...)
newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator))
flags := llvmFlags()
newenv := []string{
"CC=" + cc,
"CXX=" + cxx,
"CGO_CPPFLAGS=" + flags.cpp + " " + cppflags,
"CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags,
"CGO_LDFLAGS=" + flags.ld + " " + ldflags,
"GOPATH=" + newgopath,
}
for _, v := range os.Environ() {
if !strings.HasPrefix(v, "CC=") &&
!strings.HasPrefix(v, "CXX=") &&
!strings.HasPrefix(v, "CGO_CPPFLAGS=") &&
!strings.HasPrefix(v, "CGO_CXXFLAGS=") &&
!strings.HasPrefix(v, "CGO_LDFLAGS=") &&
!strings.HasPrefix(v, "GOPATH=") {
newenv = append(newenv, v)
}
}
gocmdpath, err := exec.LookPath("go")
if err != nil {
panic(err.Error())
}
proc, err := os.StartProcess(gocmdpath, append([]string{"go"}, args...),
&os.ProcAttr{
Env: newenv,
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
})
if err != nil {
panic(err.Error())
}
ps, err := proc.Wait()
if err != nil {
panic(err.Error())
}
os.RemoveAll(tmpgopath)
if !ps.Success() {
os.Exit(1)
}
}
func usage() {
fmt.Println(`Usage: llvm-go subcommand [flags]
Available subcommands: build get install run test print-components print-config`)
os.Exit(0)
}
func main() {
cc := os.Getenv("CC")
cxx := os.Getenv("CXX")
cppflags := os.Getenv("CGO_CPPFLAGS")
cxxflags := os.Getenv("CGO_CXXFLAGS")
ldflags := os.Getenv("CGO_LDFLAGS")
args := os.Args[1:]
DONE: for {
switch {
case len(args) == 0:
usage()
case strings.HasPrefix(args[0], "cc="):
cc = args[0][3:]
args = args[1:]
case strings.HasPrefix(args[0], "cxx="):
cxx = args[0][4:]
args = args[1:]
case strings.HasPrefix(args[0], "cppflags="):
cppflags = args[0][9:]
args = args[1:]
case strings.HasPrefix(args[0], "cxxflags="):
cxxflags = args[0][9:]
args = args[1:]
case strings.HasPrefix(args[0], "ldflags="):
ldflags = args[0][8:]
args = args[1:]
default:
break DONE
}
}
switch args[0] {
case "build", "get", "install", "run", "test":
runGoWithLLVMEnv(args, cc, cxx, cppflags, cxxflags, ldflags)
case "print-components":
printComponents()
case "print-config":
printConfig()
default:
usage()
}
}