From d540da105c799a8fa010ee83419d6cb24d6627b4 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 4 May 2018 15:15:47 -0700 Subject: [PATCH] go/build, cmd/go: don't expect gccgo to have GOROOT packages When using gccgo the standard library sources are not available in GOROOT. Don't expect them to be there. In the gccgo build, use a set of standard library packages generated at build time. Change-Id: Id133022604d9b7e778e73e8512f9080c61462fba Reviewed-on: https://go-review.googlesource.com/111595 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh --- src/cmd/go/internal/test/test.go | 5 ++ src/go/build/build.go | 35 ++++++--- src/go/build/gc.go | 120 +++++++++++++++++++++++++++++++ src/go/build/gccgo.go | 6 ++ 4 files changed, 155 insertions(+), 11 deletions(-) diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go index 31cb517943..bcff5ff3b1 100644 --- a/src/cmd/go/internal/test/test.go +++ b/src/cmd/go/internal/test/test.go @@ -626,6 +626,11 @@ func runTest(cmd *base.Command, args []string) { a := &work.Action{Mode: "go test -i"} for _, p := range load.PackagesForBuild(all) { + if cfg.BuildToolchainName == "gccgo" && p.Standard { + // gccgo's standard library packages + // can not be reinstalled. + continue + } a.Deps = append(a.Deps, b.CompileAction(work.ModeInstall, work.ModeInstall, p)) } b.Do(a) diff --git a/src/go/build/build.go b/src/go/build/build.go index ef43888fc5..38380d323d 100644 --- a/src/go/build/build.go +++ b/src/go/build/build.go @@ -238,7 +238,7 @@ func (ctxt *Context) gopath() []string { // that do not exist. func (ctxt *Context) SrcDirs() []string { var all []string - if ctxt.GOROOT != "" { + if ctxt.GOROOT != "" && ctxt.Compiler != "gccgo" { dir := ctxt.joinPath(ctxt.GOROOT, "src") if ctxt.isDir(dir) { all = append(all, dir) @@ -538,7 +538,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa inTestdata := func(sub string) bool { return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || strings.HasPrefix(sub, "testdata/") || sub == "testdata" } - if ctxt.GOROOT != "" { + if ctxt.GOROOT != "" && ctxt.Compiler != "gccgo" { root := ctxt.joinPath(ctxt.GOROOT, "src") if sub, ok := ctxt.hasSubdir(root, p.Dir); ok && !inTestdata(sub) { p.Goroot = true @@ -555,7 +555,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa // We found a potential import path for dir, // but check that using it wouldn't find something // else first. - if ctxt.GOROOT != "" { + if ctxt.GOROOT != "" && ctxt.Compiler != "gccgo" { if dir := ctxt.joinPath(ctxt.GOROOT, "src", sub); ctxt.isDir(dir) { p.ConflictDir = dir goto Found @@ -620,7 +620,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa } return false } - if searchVendor(ctxt.GOROOT, true) { + if ctxt.Compiler != "gccgo" && searchVendor(ctxt.GOROOT, true) { goto Found } for _, root := range gopath { @@ -633,16 +633,24 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa // Determine directory from import path. if ctxt.GOROOT != "" { dir := ctxt.joinPath(ctxt.GOROOT, "src", path) - isDir := ctxt.isDir(dir) - binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga)) - if isDir || binaryOnly { - p.Dir = dir - p.Goroot = true - p.Root = ctxt.GOROOT - goto Found + if ctxt.Compiler != "gccgo" { + isDir := ctxt.isDir(dir) + binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga)) + if isDir || binaryOnly { + p.Dir = dir + p.Goroot = true + p.Root = ctxt.GOROOT + goto Found + } } tried.goroot = dir } + if ctxt.Compiler == "gccgo" && isStandardPackage(path) { + p.Dir = ctxt.joinPath(ctxt.GOROOT, "src", path) + p.Goroot = true + p.Root = ctxt.GOROOT + goto Found + } for _, root := range gopath { dir := ctxt.joinPath(root, "src", path) isDir := ctxt.isDir(dir) @@ -706,6 +714,11 @@ Found: return p, pkgerr } + if ctxt.Compiler == "gccgo" && p.Goroot { + // gccgo has no sources for GOROOT packages. + return p, nil + } + dirs, err := ctxt.readDir(p.Dir) if err != nil { return p, err diff --git a/src/go/build/gc.go b/src/go/build/gc.go index 3025cd5681..e2be2cbb1d 100644 --- a/src/go/build/gc.go +++ b/src/go/build/gc.go @@ -7,11 +7,131 @@ package build import ( + "os" + "os/exec" "path/filepath" "runtime" + "strings" + "sync" ) // getToolDir returns the default value of ToolDir. func getToolDir() string { return filepath.Join(runtime.GOROOT(), "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH) } + +// isStandardPackage is not used for the gc toolchain. +// However, this function may be called when using `go build -compiler=gccgo`. +func isStandardPackage(path string) bool { + return gccgoSearch.isStandard(path) +} + +// gccgoSearch holds the gccgo search directories. +type gccgoDirs struct { + once sync.Once + dirs []string +} + +// gccgoSearch is used to check whether a gccgo package exists in the +// standard library. +var gccgoSearch gccgoDirs + +// init finds the gccgo search directories. If this fails it leaves dirs == nil. +func (gd *gccgoDirs) init() { + gccgo := os.Getenv("GCCGO") + if gccgo == "" { + gccgo = "gccgo" + } + bin, err := exec.LookPath(gccgo) + if err != nil { + return + } + + allDirs, err := exec.Command(bin, "-print-search-dirs").Output() + if err != nil { + return + } + versionB, err := exec.Command(bin, "-dumpversion").Output() + if err != nil { + return + } + version := strings.TrimSpace(string(versionB)) + machineB, err := exec.Command(bin, "-dumpmachine").Output() + if err != nil { + return + } + machine := strings.TrimSpace(string(machineB)) + + dirsEntries := strings.Split(string(allDirs), "\n") + const prefix = "libraries: =" + var dirs []string + for _, dirEntry := range dirsEntries { + if strings.HasPrefix(dirEntry, prefix) { + dirs = filepath.SplitList(strings.TrimPrefix(dirEntry, prefix)) + break + } + } + if len(dirs) == 0 { + return + } + + var lastDirs []string + for _, dir := range dirs { + goDir := filepath.Join(dir, "go", version) + if fi, err := os.Stat(goDir); err == nil && fi.IsDir() { + gd.dirs = append(gd.dirs, goDir) + goDir = filepath.Join(goDir, machine) + if fi, err = os.Stat(goDir); err == nil && fi.IsDir() { + gd.dirs = append(gd.dirs, goDir) + } + } + if fi, err := os.Stat(dir); err == nil && fi.IsDir() { + lastDirs = append(lastDirs, dir) + } + } + gd.dirs = append(gd.dirs, lastDirs...) +} + +// isStandard returns whether path is a standard library for gccgo. +func (gd *gccgoDirs) isStandard(path string) bool { + // Quick check: if the first path component has a '.', it's not + // in the standard library. This skips most GOPATH directories. + i := strings.Index(path, "/") + if i < 0 { + i = len(path) + } + if strings.Contains(path[:i], ".") { + return false + } + + if path == "unsafe" { + // Special case. + return true + } + + gd.once.Do(gd.init) + if gd.dirs == nil { + // We couldn't find the gccgo search directories. + // Best guess, since the first component did not contain + // '.', is that this is a standard library package. + return true + } + + for _, dir := range gd.dirs { + full := filepath.Join(dir, path) + pkgdir, pkg := filepath.Split(full) + for _, p := range [...]string{ + full, + full + ".gox", + pkgdir + "lib" + pkg + ".so", + pkgdir + "lib" + pkg + ".a", + full + ".o", + } { + if fi, err := os.Stat(p); err == nil && !fi.IsDir() { + return true + } + } + } + + return false +} diff --git a/src/go/build/gccgo.go b/src/go/build/gccgo.go index c6aac9aa1b..59e089d69d 100644 --- a/src/go/build/gccgo.go +++ b/src/go/build/gccgo.go @@ -12,3 +12,9 @@ import "runtime" func getToolDir() string { return envOr("GCCGOTOOLDIR", runtime.GCCGOTOOLDIR) } + +// isStandardPackage returns whether path names a standard library package. +// This uses a list generated at build time. +func isStandardPackage(path string) bool { + return stdpkg[path] +} -- 2.50.0