Add llvm-go tool.
authorPeter Collingbourne <peter@pcc.me.uk>
Thu, 23 Oct 2014 02:33:23 +0000 (02:33 +0000)
committerPeter Collingbourne <peter@pcc.me.uk>
Thu, 23 Oct 2014 02:33:23 +0000 (02:33 +0000)
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

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@220462 91177308-0d34-0410-b5e6-96231b3b80d8

bindings/go/build.sh
cmake/config-ix.cmake
test/Bindings/Go/go.test
test/CMakeLists.txt
test/lit.cfg
tools/CMakeLists.txt
tools/Makefile
tools/llvm-go/CMakeLists.txt [new file with mode: 0644]
tools/llvm-go/Makefile [new file with mode: 0644]
tools/llvm-go/llvm-go.go [new file with mode: 0644]

index 4a6b442eedbd5c5275fda2a4b8f0097e902206fd..3f0be00f808e8b870d35eedbe0bbfbd61f67183c 100755 (executable)
@@ -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
 
 workdir=$gollvmdir/workdir
 gollvmdir=$(dirname "$0")/llvm
 
 workdir=$gollvmdir/workdir
@@ -41,12 +10,14 @@ mkdir -p $llvm_builddir
 
 cmake_flags="../../../../.. $@"
 llvm_config="$llvm_builddir/bin/llvm-config"
 
 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)
 
 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
   llvm_buildtargets="$($llvm_config --libs $llvm_components | sed -e 's/-l//g')"
   ninja -C $llvm_builddir $llvm_buildtargets FileCheck
 else
@@ -54,14 +25,7 @@ else
   make -C $llvm_builddir -j4
 fi
 
   make -C $llvm_builddir -j4
 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
index 358a32cd10736b71ee88d3060a106a586a582048..8139d57ff83fb40b7cc6b8ed45f42c8869d1135e 100755 (executable)
@@ -493,16 +493,20 @@ else()
 endif()
 
 set(LLVM_BINDINGS "")
 endif()
 
 set(LLVM_BINDINGS "")
-find_program(GO_EXECUTABLE NAMES go DOC "go executable")
-if(GO_EXECUTABLE STREQUAL "GO_EXECUTABLE-NOTFOUND")
+if(WIN32)
   message(STATUS "Go bindings disabled.")
 else()
   message(STATUS "Go bindings disabled.")
 else()
-  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.")
+  find_program(GO_EXECUTABLE NAMES go DOC "go executable")
+  if(GO_EXECUTABLE STREQUAL "GO_EXECUTABLE-NOTFOUND")
+    message(STATUS "Go bindings disabled.")
   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()
index ff0cf90e1d8ff3797ab845172c30c944dce99d88..3951483b6ffb486f9c72ce491a01e508f5ede7cd 100644 (file)
@@ -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
 
 ; REQUIRES: shell
index f20bc950385800dd669e79ab2834970833846328..14e54b7ffff5f051257a07e1c0c284bd8126e0fc 100644 (file)
@@ -30,8 +30,9 @@ set(LLVM_TEST_DEPENDS
           llvm-cov
           llvm-diff
           llvm-dis
           llvm-cov
           llvm-diff
           llvm-dis
-          llvm-extract
           llvm-dwarfdump
           llvm-dwarfdump
+          llvm-extract
+          llvm-go
           llvm-link
           llvm-lto
           llvm-mc
           llvm-link
           llvm-lto
           llvm-mc
index 1b4df11192114905b8e00ff0a77d3d4782c8f61e..55f559e68dc7f2b379705238e09a12fa7270219d 100644 (file)
@@ -200,6 +200,7 @@ for pattern in [r"\bbugpoint\b(?!-)",
                 r"\bllvm-dis\b",
                 r"\bllvm-dwarfdump\b",
                 r"\bllvm-extract\b",
                 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",
                 r"\bllvm-link\b",
                 r"\bllvm-lto\b",
                 r"\bllvm-mc\b",
index 940f656b5980a756fb93eda1e12cc0a9b5ee8c02..2b0f853cbb4263d59cc643d2c7740c209d96d762 100644 (file)
@@ -52,6 +52,8 @@ add_llvm_tool_subdirectory(llvm-c-test)
 add_llvm_tool_subdirectory(obj2yaml)
 add_llvm_tool_subdirectory(yaml2obj)
 
 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)
 if(NOT CYGWIN AND LLVM_ENABLE_PIC)
   add_llvm_tool_subdirectory(lto)
   add_llvm_tool_subdirectory(llvm-lto)
index ff6e55d7299130b87203c299e0f2175544d80620..4b8923a58d33a94abe9f46026cd3a86e22c1ff74 100644 (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
diff --git a/tools/llvm-go/CMakeLists.txt b/tools/llvm-go/CMakeLists.txt
new file mode 100644 (file)
index 0000000..20393f7
--- /dev/null
@@ -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()
diff --git a/tools/llvm-go/Makefile b/tools/llvm-go/Makefile
new file mode 100644 (file)
index 0000000..4465b2a
--- /dev/null
@@ -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 $@ $<
diff --git a/tools/llvm-go/llvm-go.go b/tools/llvm-go/llvm-go.go
new file mode 100644 (file)
index 0000000..47f9481
--- /dev/null
@@ -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()
+       }
+}