Home | History | Annotate | Download | only in llvm-go
      1 //===-- llvm-go.go - go tool wrapper for LLVM -----------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This tool lets us build LLVM components within the tree by setting up a
     11 // $GOPATH that resembles a tree fetched in the normal way with "go get".
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 package main
     16 
     17 import (
     18 	"fmt"
     19 	"io/ioutil"
     20 	"os"
     21 	"os/exec"
     22 	"path/filepath"
     23 	"runtime"
     24 	"strings"
     25 )
     26 
     27 type pkg struct {
     28 	llvmpath, pkgpath string
     29 }
     30 
     31 var packages = []pkg{
     32 	{"bindings/go/llvm", "llvm.org/llvm/bindings/go/llvm"},
     33 	{"tools/llgo", "llvm.org/llgo"},
     34 }
     35 
     36 type compilerFlags struct {
     37 	cpp, cxx, ld string
     38 }
     39 
     40 var components = []string{
     41 	"all-targets",
     42 	"analysis",
     43 	"asmparser",
     44 	"asmprinter",
     45 	"bitreader",
     46 	"bitwriter",
     47 	"codegen",
     48 	"core",
     49 	"debuginfodwarf",
     50 	"executionengine",
     51 	"instrumentation",
     52 	"interpreter",
     53 	"ipo",
     54 	"irreader",
     55 	"linker",
     56 	"mc",
     57 	"mcjit",
     58 	"objcarcopts",
     59 	"option",
     60 	"profiledata",
     61 	"scalaropts",
     62 	"support",
     63 	"target",
     64 }
     65 
     66 func llvmConfig(args ...string) string {
     67 	configpath := os.Getenv("LLVM_CONFIG")
     68 	if configpath == "" {
     69 		// strip llvm-go, add llvm-config
     70 		configpath = os.Args[0][:len(os.Args[0])-7] + "llvm-config"
     71 	}
     72 
     73 	cmd := exec.Command(configpath, args...)
     74 	out, err := cmd.Output()
     75 	if err != nil {
     76 		panic(err.Error())
     77 	}
     78 
     79 	outstr := string(out)
     80 	outstr = strings.TrimSuffix(outstr, "\n")
     81 	return strings.Replace(outstr, "\n", " ", -1)
     82 }
     83 
     84 func llvmFlags() compilerFlags {
     85 	ldflags := llvmConfig(append([]string{"--ldflags", "--libs", "--system-libs"}, components...)...)
     86 	if runtime.GOOS != "darwin" {
     87 		// OS X doesn't like -rpath with cgo. See:
     88 		// https://code.google.com/p/go/issues/detail?id=7293
     89 		ldflags = "-Wl,-rpath," + llvmConfig("--libdir") + " " + ldflags
     90 	}
     91 	return compilerFlags{
     92 		cpp: llvmConfig("--cppflags"),
     93 		cxx: "-std=c++11",
     94 		ld:  ldflags,
     95 	}
     96 }
     97 
     98 func addTag(args []string, tag string) []string {
     99 	args = append([]string{}, args...)
    100 	addedTag := false
    101 	for i, a := range args {
    102 		if strings.HasPrefix(a, "-tags=") {
    103 			args[i] = a + " " + tag
    104 			addedTag = true
    105 		} else if a == "-tags" && i+1 < len(args) {
    106 			args[i+1] = args[i+1] + " " + tag
    107 			addedTag = true
    108 		}
    109 	}
    110 	if !addedTag {
    111 		args = append([]string{args[0], "-tags", tag}, args[1:]...)
    112 	}
    113 	return args
    114 }
    115 
    116 func printComponents() {
    117 	fmt.Println(strings.Join(components, " "))
    118 }
    119 
    120 func printConfig() {
    121 	flags := llvmFlags()
    122 
    123 	fmt.Printf(`// +build !byollvm
    124 
    125 // This file is generated by llvm-go, do not edit.
    126 
    127 package llvm
    128 
    129 /*
    130 #cgo CPPFLAGS: %s
    131 #cgo CXXFLAGS: %s
    132 #cgo LDFLAGS: %s
    133 */
    134 import "C"
    135 
    136 type (run_build_sh int)
    137 `, flags.cpp, flags.cxx, flags.ld)
    138 }
    139 
    140 func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags string) {
    141 	args = addTag(args, "byollvm")
    142 
    143 	srcdir := llvmConfig("--src-root")
    144 
    145 	tmpgopath, err := ioutil.TempDir("", "gopath")
    146 	if err != nil {
    147 		panic(err.Error())
    148 	}
    149 
    150 	for _, p := range packages {
    151 		path := filepath.Join(tmpgopath, "src", p.pkgpath)
    152 		err := os.MkdirAll(filepath.Dir(path), os.ModePerm)
    153 		if err != nil {
    154 			panic(err.Error())
    155 		}
    156 
    157 		err = os.Symlink(filepath.Join(srcdir, p.llvmpath), path)
    158 		if err != nil {
    159 			panic(err.Error())
    160 		}
    161 	}
    162 
    163 	newpath := os.Getenv("PATH")
    164 
    165 	newgopathlist := []string{tmpgopath}
    166 	newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...)
    167 	newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator))
    168 
    169 	flags := llvmFlags()
    170 
    171 	newenv := []string{
    172 		"CC=" + cc,
    173 		"CXX=" + cxx,
    174 		"CGO_CPPFLAGS=" + flags.cpp + " " + cppflags,
    175 		"CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags,
    176 		"CGO_LDFLAGS=" + flags.ld + " " + ldflags,
    177 		"GOPATH=" + newgopath,
    178 		"PATH=" + newpath,
    179 	}
    180 	if llgo != "" {
    181 		newenv = append(newenv, "GCCGO=" + llgo)
    182 	}
    183 
    184 	for _, v := range os.Environ() {
    185 		if !strings.HasPrefix(v, "CC=") &&
    186 			!strings.HasPrefix(v, "CXX=") &&
    187 			!strings.HasPrefix(v, "CGO_CPPFLAGS=") &&
    188 			!strings.HasPrefix(v, "CGO_CXXFLAGS=") &&
    189 			!strings.HasPrefix(v, "CGO_LDFLAGS=") &&
    190 			!strings.HasPrefix(v, "GCCGO=") &&
    191 			!strings.HasPrefix(v, "GOPATH=") &&
    192 			!strings.HasPrefix(v, "PATH=") {
    193 			newenv = append(newenv, v)
    194 		}
    195 	}
    196 
    197 	gocmdpath, err := exec.LookPath(gocmd)
    198 	if err != nil {
    199 		panic(err.Error())
    200 	}
    201 
    202 	proc, err := os.StartProcess(gocmdpath, append([]string{gocmd}, args...),
    203 		&os.ProcAttr{
    204 			Env:   newenv,
    205 			Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
    206 		})
    207 	if err != nil {
    208 		panic(err.Error())
    209 	}
    210 	ps, err := proc.Wait()
    211 	if err != nil {
    212 		panic(err.Error())
    213 	}
    214 
    215 	os.RemoveAll(tmpgopath)
    216 
    217 	if !ps.Success() {
    218 		os.Exit(1)
    219 	}
    220 }
    221 
    222 func usage() {
    223 	fmt.Println(`Usage: llvm-go subcommand [flags]
    224 
    225 Available subcommands: build get install run test print-components print-config`)
    226 	os.Exit(0)
    227 }
    228 
    229 func main() {
    230 	cc := os.Getenv("CC")
    231 	cxx := os.Getenv("CXX")
    232 	cppflags := os.Getenv("CGO_CPPFLAGS")
    233 	cxxflags := os.Getenv("CGO_CXXFLAGS")
    234 	ldflags := os.Getenv("CGO_LDFLAGS")
    235 	gocmd := "go"
    236 	llgo := ""
    237 
    238 	args := os.Args[1:]
    239 	DONE: for {
    240 		switch {
    241 		case len(args) == 0:
    242 			usage()
    243 		case strings.HasPrefix(args[0], "cc="):
    244 			cc = args[0][3:]
    245 			args = args[1:]
    246 		case strings.HasPrefix(args[0], "cxx="):
    247 			cxx = args[0][4:]
    248 			args = args[1:]
    249 		case strings.HasPrefix(args[0], "go="):
    250 			gocmd = args[0][3:]
    251 			args = args[1:]
    252 		case strings.HasPrefix(args[0], "llgo="):
    253 			llgo = args[0][5:]
    254 			args = args[1:]
    255 		case strings.HasPrefix(args[0], "cppflags="):
    256 			cppflags = args[0][9:]
    257 			args = args[1:]
    258 		case strings.HasPrefix(args[0], "cxxflags="):
    259 			cxxflags = args[0][9:]
    260 			args = args[1:]
    261 		case strings.HasPrefix(args[0], "ldflags="):
    262 			ldflags = args[0][8:]
    263 			args = args[1:]
    264 		default:
    265 			break DONE
    266 		}
    267 	}
    268 
    269 	switch args[0] {
    270 	case "build", "get", "install", "run", "test":
    271 		runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags)
    272 	case "print-components":
    273 		printComponents()
    274 	case "print-config":
    275 		printConfig()
    276 	default:
    277 		usage()
    278 	}
    279 }
    280