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