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