Index: bindings/go/build.sh =================================================================== --- bindings/go/build.sh +++ bindings/go/build.sh @@ -1,36 +1,5 @@ #!/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 workdir=$gollvmdir/workdir @@ -41,12 +10,14 @@ cmake_flags="../../../../.. $@" llvm_config="$llvm_builddir/bin/llvm-config" +llvm_go="$llvm_builddir/bin/llvm-go" if test -n "`which ninja`" ; then # If Ninja is available, we can speed up the build by building only the # required subset of LLVM. (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')" ninja -C $llvm_builddir $llvm_buildtargets FileCheck else @@ -54,14 +25,7 @@ make -C $llvm_builddir -j4 fi +$llvm_go print-config > $gollvmdir/llvm_config.go + 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 Index: test/Bindings/Go/go.test =================================================================== --- test/Bindings/Go/go.test +++ test/Bindings/Go/go.test @@ -1,8 +1,3 @@ -; RUN: cd %S/../../../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 . +; RUN: llvm-go test llvm.org/llvm/bindings/go/llvm ; REQUIRES: shell Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -30,8 +30,9 @@ llvm-cov llvm-diff llvm-dis - llvm-extract llvm-dwarfdump + llvm-extract + llvm-go llvm-link llvm-lto llvm-mc Index: test/lit.cfg =================================================================== --- test/lit.cfg +++ test/lit.cfg @@ -200,6 +200,7 @@ r"\bllvm-dis\b", r"\bllvm-dwarfdump\b", r"\bllvm-extract\b", + r"\bllvm-go\b", r"\bllvm-link\b", r"\bllvm-lto\b", r"\bllvm-mc\b", Index: tools/CMakeLists.txt =================================================================== --- tools/CMakeLists.txt +++ tools/CMakeLists.txt @@ -52,6 +52,8 @@ add_llvm_tool_subdirectory(obj2yaml) add_llvm_tool_subdirectory(yaml2obj) +add_llvm_tool_subdirectory(llvm-go) + if(NOT CYGWIN AND LLVM_ENABLE_PIC) add_llvm_tool_subdirectory(lto) add_llvm_tool_subdirectory(llvm-lto) Index: tools/llvm-go/CMakeLists.txt =================================================================== --- /dev/null +++ tools/llvm-go/CMakeLists.txt @@ -0,0 +1,9 @@ +if(NOT GO_EXECUTABLE STREQUAL "GO_EXECUTABLE-NOTFOUND") + 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() Index: tools/llvm-go/llvm-go.go =================================================================== --- /dev/null +++ tools/llvm-go/llvm-go.go @@ -0,0 +1,257 @@ +//===-- 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 { + cppflags, cxxflags, ldflags 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{ + cppflags: llvmConfig("--cppflags"), + cxxflags: "-std=c++11", + ldflags: 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 + +package llvm + +/* +#cgo CPPFLAGS: %s +#cgo CXXFLAGS: %s +#cgo LDFLAGS: %s +*/ +import "C" + +type (run_build_sh int) +`, flags.cppflags, flags.cxxflags, flags.ldflags) +} + +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()) + } + + os.Symlink(filepath.Join(srcdir, p.llvmpath), path) + } + + 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.cppflags + " " + cppflags, + "CGO_CXXFLAGS=" + flags.cxxflags + " " + cxxflags, + "CGO_LDFLAGS=" + flags.ldflags + " " + 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 := false + for !done { + 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: + done = true + } + } + + switch os.Args[1] { + case "build", "get", "install", "run", "test": + runGoWithLLVMEnv(args, cc, cxx, cppflags, cxxflags, ldflags) + case "print-components": + printComponents() + case "print-config": + printConfig() + default: + usage() + } +}