main: make $GOROOT more robust and configurable

Check various locations that $GOROOT may live, including the location of
the go binary. But make it possible to override this autodetection by
setting GOROOT manually as an environment variable.
This commit is contained in:
Ayke van Laethem
2019-05-05 17:24:36 +02:00
committed by Ron Evans
parent a79edf416c
commit 141a70f401
4 changed files with 75 additions and 12 deletions
+7 -7
View File
@@ -9,7 +9,6 @@ import (
"go/types" "go/types"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"strconv" "strconv"
"strings" "strings"
@@ -39,7 +38,8 @@ type Config struct {
LDFlags []string // ldflags to pass to cgo LDFlags []string // ldflags to pass to cgo
DumpSSA bool // dump Go SSA, for compiler debugging DumpSSA bool // dump Go SSA, for compiler debugging
Debug bool // add debug symbols for gdb 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` GOPATH string // GOPATH, like `go env GOPATH`
BuildTags []string // build tags for TinyGo (empty means {Config.GOOS/Config.GOARCH}) 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. // the TinyGo root.
overlayGopath := c.GOPATH overlayGopath := c.GOPATH
if overlayGopath == "" { if overlayGopath == "" {
overlayGopath = runtime.GOROOT() overlayGopath = c.GOROOT
} else { } else {
overlayGopath = runtime.GOROOT() + string(filepath.ListSeparator) + overlayGopath overlayGopath = c.GOROOT + string(filepath.ListSeparator) + overlayGopath
} }
wd, err := os.Getwd() wd, err := os.Getwd()
@@ -178,7 +178,7 @@ func (c *Compiler) Compile(mainPath string) []error {
Build: &build.Context{ Build: &build.Context{
GOARCH: c.GOARCH, GOARCH: c.GOARCH,
GOOS: c.GOOS, GOOS: c.GOOS,
GOROOT: runtime.GOROOT(), GOROOT: c.GOROOT,
GOPATH: c.GOPATH, GOPATH: c.GOPATH,
CgoEnabled: true, CgoEnabled: true,
UseAllFiles: false, UseAllFiles: false,
@@ -188,7 +188,7 @@ func (c *Compiler) Compile(mainPath string) []error {
OverlayBuild: &build.Context{ OverlayBuild: &build.Context{
GOARCH: c.GOARCH, GOARCH: c.GOARCH,
GOOS: c.GOOS, GOOS: c.GOOS,
GOROOT: c.RootDir, GOROOT: c.TINYGOROOT,
GOPATH: overlayGopath, GOPATH: overlayGopath,
CgoEnabled: true, CgoEnabled: true,
UseAllFiles: false, UseAllFiles: false,
@@ -220,7 +220,7 @@ func (c *Compiler) Compile(mainPath string) []error {
}, },
}, },
Dir: wd, Dir: wd,
TinyGoRoot: c.RootDir, TINYGOROOT: c.TINYGOROOT,
CFlags: c.CFlags, CFlags: c.CFlags,
} }
if strings.HasSuffix(mainPath, ".go") { if strings.HasSuffix(mainPath, ".go") {
+4 -4
View File
@@ -22,7 +22,7 @@ type Program struct {
fset *token.FileSet fset *token.FileSet
TypeChecker types.Config TypeChecker types.Config
Dir string // current working directory (for error reporting) 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 CFlags []string
} }
@@ -297,12 +297,12 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
} }
clangIncludes := "" clangIncludes := ""
if len(p.CgoFiles) != 0 { 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. // 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 { } else {
// Running from the installation directory. // 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 { for _, file := range p.CgoFiles {
+6 -1
View File
@@ -76,6 +76,10 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
ldflags = append(ldflags, strings.Replace(flag, "{root}", root, -1)) 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{ compilerConfig := compiler.Config{
Triple: spec.Triple, Triple: spec.Triple,
CPU: spec.CPU, CPU: spec.CPU,
@@ -87,7 +91,8 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
LDFlags: ldflags, LDFlags: ldflags,
Debug: config.debug, Debug: config.debug,
DumpSSA: config.dumpSSA, DumpSSA: config.dumpSSA,
RootDir: root, TINYGOROOT: root,
GOROOT: goroot,
GOPATH: getGopath(), GOPATH: getGopath(),
BuildTags: spec.BuildTags, BuildTags: spec.BuildTags,
} }
+58
View File
@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"os/exec"
"os/user" "os/user"
"path/filepath" "path/filepath"
"runtime" "runtime"
@@ -310,3 +311,60 @@ func getHomeDir() string {
} }
return u.HomeDir 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
}