diff --git a/compiler/compiler.go b/compiler/compiler.go index b4cb5dc8..185b6ea0 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -9,7 +9,6 @@ import ( "go/types" "os" "path/filepath" - "runtime" "strconv" "strings" @@ -39,7 +38,8 @@ type Config struct { LDFlags []string // ldflags to pass to cgo DumpSSA bool // dump Go SSA, for compiler debugging Debug bool // add debug symbols for gdb - RootDir string // GOROOT for TinyGo + GOROOT string // GOROOT + TINYGOROOT string // GOROOT for TinyGo GOPATH string // GOPATH, like `go env GOPATH` BuildTags []string // build tags for TinyGo (empty means {Config.GOOS/Config.GOARCH}) } @@ -165,9 +165,9 @@ func (c *Compiler) Compile(mainPath string) []error { // the TinyGo root. overlayGopath := c.GOPATH if overlayGopath == "" { - overlayGopath = runtime.GOROOT() + overlayGopath = c.GOROOT } else { - overlayGopath = runtime.GOROOT() + string(filepath.ListSeparator) + overlayGopath + overlayGopath = c.GOROOT + string(filepath.ListSeparator) + overlayGopath } wd, err := os.Getwd() @@ -178,7 +178,7 @@ func (c *Compiler) Compile(mainPath string) []error { Build: &build.Context{ GOARCH: c.GOARCH, GOOS: c.GOOS, - GOROOT: runtime.GOROOT(), + GOROOT: c.GOROOT, GOPATH: c.GOPATH, CgoEnabled: true, UseAllFiles: false, @@ -188,7 +188,7 @@ func (c *Compiler) Compile(mainPath string) []error { OverlayBuild: &build.Context{ GOARCH: c.GOARCH, GOOS: c.GOOS, - GOROOT: c.RootDir, + GOROOT: c.TINYGOROOT, GOPATH: overlayGopath, CgoEnabled: true, UseAllFiles: false, @@ -220,7 +220,7 @@ func (c *Compiler) Compile(mainPath string) []error { }, }, Dir: wd, - TinyGoRoot: c.RootDir, + TINYGOROOT: c.TINYGOROOT, CFlags: c.CFlags, } if strings.HasSuffix(mainPath, ".go") { diff --git a/loader/loader.go b/loader/loader.go index 45e10efe..cebf7228 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -22,7 +22,7 @@ type Program struct { fset *token.FileSet TypeChecker types.Config Dir string // current working directory (for error reporting) - TinyGoRoot string // root of the TinyGo installation or root of the source code + TINYGOROOT string // root of the TinyGo installation or root of the source code CFlags []string } @@ -297,12 +297,12 @@ func (p *Package) parseFiles() ([]*ast.File, error) { } clangIncludes := "" if len(p.CgoFiles) != 0 { - if _, err := os.Stat(filepath.Join(p.TinyGoRoot, "llvm", "tools", "clang", "lib", "Headers")); !os.IsNotExist(err) { + if _, err := os.Stat(filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")); !os.IsNotExist(err) { // Running from the source directory. - clangIncludes = filepath.Join(p.TinyGoRoot, "llvm", "tools", "clang", "lib", "Headers") + clangIncludes = filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers") } else { // Running from the installation directory. - clangIncludes = filepath.Join(p.TinyGoRoot, "lib", "clang", "include") + clangIncludes = filepath.Join(p.TINYGOROOT, "lib", "clang", "include") } } for _, file := range p.CgoFiles { diff --git a/main.go b/main.go index b662d65c..759746b8 100644 --- a/main.go +++ b/main.go @@ -76,6 +76,10 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act ldflags = append(ldflags, strings.Replace(flag, "{root}", root, -1)) } + goroot := getGoroot() + if goroot == "" { + return errors.New("cannot locate $GOROOT, please set it manually") + } compilerConfig := compiler.Config{ Triple: spec.Triple, CPU: spec.CPU, @@ -87,7 +91,8 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act LDFlags: ldflags, Debug: config.debug, DumpSSA: config.dumpSSA, - RootDir: root, + TINYGOROOT: root, + GOROOT: goroot, GOPATH: getGopath(), BuildTags: spec.BuildTags, } diff --git a/target.go b/target.go index 546883f3..61baf3b0 100644 --- a/target.go +++ b/target.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "os" + "os/exec" "os/user" "path/filepath" "runtime" @@ -310,3 +311,60 @@ func getHomeDir() string { } return u.HomeDir } + +// getGoroot returns an appropriate GOROOT from various sources. If it can't be +// found, it returns an empty string. +func getGoroot() string { + goroot := os.Getenv("GOROOT") + if goroot != "" { + // An explicitly set GOROOT always has preference. + return goroot + } + + // Check for the location of the 'go' binary and base GOROOT on that. + binpath, err := exec.LookPath("go") + if err == nil { + binpath, err = filepath.EvalSymlinks(binpath) + if err == nil { + goroot := filepath.Dir(filepath.Dir(binpath)) + if isGoroot(goroot) { + return goroot + } + } + } + + // Check what GOROOT was at compile time. + if isGoroot(runtime.GOROOT()) { + return runtime.GOROOT() + } + + // Check for some standard locations, as a last resort. + var candidates []string + switch runtime.GOOS { + case "linux": + candidates = []string{ + "/usr/local/go", // manually installed + "/usr/lib/go", // from the distribution + } + case "darwin": + candidates = []string{ + "/usr/local/go", // manually installed + "/usr/local/opt/go/libexec", // from Homebrew + } + } + + for _, candidate := range candidates { + if isGoroot(candidate) { + return candidate + } + } + + // Can't find GOROOT... + return "" +} + +// isGoroot checks whether the given path looks like a GOROOT. +func isGoroot(goroot string) bool { + _, err := os.Stat(filepath.Join(goroot, "src", "runtime", "internal", "sys", "zversion.go")) + return err == nil +}