fuzzInstrumented = false // whether fuzzing uses instrumentation
)
+var (
+ goHostOS, goHostArch string
+ cgoEnabled string // raw value from 'go env CGO_ENABLED'
+)
+
var exeSuffix string = func() string {
if runtime.GOOS == "windows" {
return ".exe"
// run the main func exported via export_test.go, and exit.
// We set CMDGO_TEST_RUN_MAIN via os.Setenv and testScript.setup.
if os.Getenv("CMDGO_TEST_RUN_MAIN") != "" {
+ cfg.SetGOROOT(cfg.GOROOT, true)
+
if v := os.Getenv("TESTGO_VERSION"); v != "" {
work.RuntimeVersion = v
}
// which will cause many tests to do unnecessary rebuilds and some
// tests to attempt to overwrite the installed standard library.
// Bail out entirely in this case.
- hostGOOS := goEnv("GOHOSTOS")
- hostGOARCH := goEnv("GOHOSTARCH")
- if hostGOOS != runtime.GOOS || hostGOARCH != runtime.GOARCH {
- fmt.Fprintf(os.Stderr, "testing: warning: no tests to run\n") // magic string for cmd/go
- fmt.Printf("cmd/go test is not compatible with GOOS/GOARCH != GOHOSTOS/GOHOSTARCH (%s/%s != %s/%s)\n", runtime.GOOS, runtime.GOARCH, hostGOOS, hostGOARCH)
- fmt.Printf("SKIP\n")
- return
+ goHostOS = goEnv("GOHOSTOS")
+ os.Setenv("TESTGO_GOHOSTOS", goHostOS)
+ goHostArch = goEnv("GOHOSTARCH")
+ os.Setenv("TESTGO_GOHOSTARCH", goHostArch)
+
+ cgoEnabled = goEnv("CGO_ENABLED")
+ canCgo, err = strconv.ParseBool(cgoEnabled)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "can't parse go env CGO_ENABLED output: %q\n", strings.TrimSpace(cgoEnabled))
+ os.Exit(2)
}
// Duplicate the test executable into the path at testGo, for $PATH.
}
}
- cmd := exec.Command(testGo, "env", "CGO_ENABLED")
- cmd.Stderr = new(strings.Builder)
- if out, err := cmd.Output(); err != nil {
- fmt.Fprintf(os.Stderr, "running testgo failed: %v\n%s", err, cmd.Stderr)
- os.Exit(2)
- } else {
- canCgo, err = strconv.ParseBool(strings.TrimSpace(string(out)))
- if err != nil {
- fmt.Fprintf(os.Stderr, "can't parse go env CGO_ENABLED output: %v\n", strings.TrimSpace(string(out)))
- }
- }
-
out, err := exec.Command(gotool, "env", "GOCACHE").CombinedOutput()
if err != nil {
fmt.Fprintf(os.Stderr, "could not find testing GOCACHE: %v\n%s", err, out)
canFuzz = sys.FuzzSupported(runtime.GOOS, runtime.GOARCH)
fuzzInstrumented = sys.FuzzInstrumented(runtime.GOOS, runtime.GOARCH)
}
+
// Don't let these environment variables confuse the test.
os.Setenv("GOENV", "off")
os.Unsetenv("GOFLAGS")
"src/math/bits",
"src/unsafe",
filepath.Join("pkg", runtime.GOOS+"_"+runtime.GOARCH),
- filepath.Join("pkg/tool", runtime.GOOS+"_"+runtime.GOARCH),
+ filepath.Join("pkg/tool", goHostOS+"_"+goHostArch),
"pkg/include",
} {
srcdir := filepath.Join(testGOROOT, copydir)
defer tg.cleanup()
tg.parallel()
+ tg.wantNotStale("runtime", "", "must be non-stale to compare staleness under -toolexec")
+
if _, err := os.Stat("/usr/bin/time"); err != nil {
t.Skip(err)
}
"go/build"
"os"
"path/filepath"
- "runtime"
"cmd/go/internal/cfg"
)
-// Configuration for finding tool binaries.
-var (
- ToolGOOS = runtime.GOOS
- ToolGOARCH = runtime.GOARCH
- ToolIsWindows = ToolGOOS == "windows"
- ToolDir = build.ToolDir
-)
-
-const ToolWindowsExtension = ".exe"
-
// Tool returns the path to the named tool (for example, "vet").
// If the tool cannot be found, Tool exits the process.
func Tool(toolName string) string {
- toolPath := filepath.Join(ToolDir, toolName)
- if ToolIsWindows {
- toolPath += ToolWindowsExtension
- }
+ toolPath := filepath.Join(build.ToolDir, toolName) + cfg.ToolExeSuffix()
if len(cfg.BuildToolexec) > 0 {
return toolPath
}
return ""
}
+// Configuration for tools installed to GOROOT/bin.
+// Normally these match runtime.GOOS and runtime.GOARCH,
+// but when testing a cross-compiled cmd/go they will
+// indicate the GOOS and GOARCH of the installed cmd/go
+// rather than the test binary.
+var (
+ installedGOOS string
+ installedGOARCH string
+)
+
+// ToolExeSuffix returns the suffix for executables installed
+// in build.ToolDir.
+func ToolExeSuffix() string {
+ if installedGOOS == "windows" {
+ return ".exe"
+ }
+ return ""
+}
+
// These are general "build flags" used by build and other commands.
var (
BuildA bool // -a flag
}
func init() {
- SetGOROOT(findGOROOT())
+ SetGOROOT(findGOROOT(), false)
BuildToolchainCompiler = func() string { return "missing-compiler" }
BuildToolchainLinker = func() string { return "missing-linker" }
}
-func SetGOROOT(goroot string) {
+// SetGOROOT sets GOROOT and associated variables to the given values.
+//
+// If isTestGo is true, build.ToolDir is set based on the TESTGO_GOHOSTOS and
+// TESTGO_GOHOSTARCH environment variables instead of runtime.GOOS and
+// runtime.GOARCH.
+func SetGOROOT(goroot string, isTestGo bool) {
BuildContext.GOROOT = goroot
GOROOT = goroot
}
GOROOT_FINAL = findGOROOT_FINAL(goroot)
- if runtime.Compiler != "gccgo" && goroot != "" {
- // Note that we must use runtime.GOOS and runtime.GOARCH here,
- // as the tool directory does not move based on environment
- // variables. This matches the initialization of ToolDir in
- // go/build, except for using BuildContext.GOROOT rather than
- // runtime.GOROOT.
- build.ToolDir = filepath.Join(goroot, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
+ installedGOOS = runtime.GOOS
+ installedGOARCH = runtime.GOARCH
+ if isTestGo {
+ if testOS := os.Getenv("TESTGO_GOHOSTOS"); testOS != "" {
+ installedGOOS = testOS
+ }
+ if testArch := os.Getenv("TESTGO_GOHOSTARCH"); testArch != "" {
+ installedGOARCH = testArch
+ }
+ }
+
+ if runtime.Compiler != "gccgo" {
+ if goroot == "" {
+ build.ToolDir = ""
+ } else {
+ // Note that we must use the installed OS and arch here: the tool
+ // directory does not move based on environment variables, and even if we
+ // are testing a cross-compiled cmd/go all of the installed packages and
+ // tools would have been built using the native compiler and linker (and
+ // would spuriously appear stale if we used a cross-compiled compiler and
+ // linker).
+ //
+ // This matches the initialization of ToolDir in go/build, except for
+ // using ctxt.GOROOT and the installed GOOS and GOARCH rather than the
+ // GOROOT, GOOS, and GOARCH reported by the runtime package.
+ build.ToolDir = filepath.Join(GOROOTpkg, "tool", installedGOOS+"_"+installedGOARCH)
+ }
}
}
"io"
"os"
"path/filepath"
+ "runtime"
"strconv"
"strings"
"time"
return
}
// Windows does not allow deletion of a binary file while it is executing.
- if base.ToolIsWindows {
+ if runtime.GOOS == "windows" {
// Remove lingering ~ file from last attempt.
if _, err2 := os.Stat(f + "~"); err2 == nil {
os.Remove(f + "~")
{Name: "GOROOT", Value: cfg.GOROOT},
{Name: "GOSUMDB", Value: cfg.GOSUMDB},
{Name: "GOTMPDIR", Value: cfg.Getenv("GOTMPDIR")},
- {Name: "GOTOOLDIR", Value: base.ToolDir},
+ {Name: "GOTOOLDIR", Value: build.ToolDir},
{Name: "GOVCS", Value: cfg.GOVCS},
{Name: "GOVERSION", Value: runtime.Version()},
}
}
func gofmtPath() string {
- gofmt := "gofmt"
- if base.ToolIsWindows {
- gofmt += base.ToolWindowsExtension
- }
+ gofmt := "gofmt" + cfg.ToolExeSuffix()
gofmtPath := filepath.Join(cfg.GOBIN, gofmt)
if _, err := os.Stat(gofmtPath); err == nil {
"io/fs"
"os"
"os/exec"
- "path"
pathpkg "path"
"path/filepath"
"runtime"
setError(e)
return
}
- elem := p.DefaultExecName()
- full := cfg.BuildContext.GOOS + "_" + cfg.BuildContext.GOARCH + "/" + elem
- if cfg.BuildContext.GOOS != base.ToolGOOS || cfg.BuildContext.GOARCH != base.ToolGOARCH {
+ elem := p.DefaultExecName() + cfg.ExeSuffix
+ full := cfg.BuildContext.GOOS + "_" + cfg.BuildContext.GOARCH + string(filepath.Separator) + elem
+ if cfg.BuildContext.GOOS != runtime.GOOS || cfg.BuildContext.GOARCH != runtime.GOARCH {
// Install cross-compiled binaries to subdirectories of bin.
elem = full
}
if p.Internal.Build.BinDir != "" {
// Install to GOBIN or bin of GOPATH entry.
p.Target = filepath.Join(p.Internal.Build.BinDir, elem)
- if !p.Goroot && strings.Contains(elem, "/") && cfg.GOBIN != "" {
+ if !p.Goroot && strings.Contains(elem, string(filepath.Separator)) && cfg.GOBIN != "" {
// Do not create $GOBIN/goos_goarch/elem.
p.Target = ""
p.Internal.GobinSubdir = true
// This is for 'go tool'.
// Override all the usual logic and force it into the tool directory.
if cfg.BuildToolchainName == "gccgo" {
- p.Target = filepath.Join(base.ToolDir, elem)
+ p.Target = filepath.Join(build.ToolDir, elem)
} else {
p.Target = filepath.Join(cfg.GOROOTpkg, "tool", full)
}
}
- if p.Target != "" && cfg.BuildContext.GOOS == "windows" {
- p.Target += ".exe"
- }
} else if p.Internal.Local {
// Local import turned into absolute path.
// No permanent install target.
glob = pattern[len("all:"):]
}
// Check pattern is valid for //go:embed.
- if _, err := path.Match(glob, ""); err != nil || !validEmbedPattern(glob) {
+ if _, err := pathpkg.Match(glob, ""); err != nil || !validEmbedPattern(glob) {
return nil, nil, fmt.Errorf("invalid pattern syntax")
}
return nil, fmt.Errorf("%s: argument must be a package path, not an absolute path", arg)
case search.IsMetaPackage(p):
return nil, fmt.Errorf("%s: argument must be a package path, not a meta-package", arg)
- case path.Clean(p) != p:
+ case pathpkg.Clean(p) != p:
return nil, fmt.Errorf("%s: argument must be a clean package path", arg)
case !strings.Contains(p, "...") && search.IsStandardImportPath(p) && modindex.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, p):
return nil, fmt.Errorf("%s: argument must not be a package in the standard library", arg)
)
func TestMain(m *testing.M) {
- cfg.SetGOROOT(testenv.GOROOT(nil))
+ cfg.SetGOROOT(testenv.GOROOT(nil), false)
}
func TestPassFlagToTestIncludesAllTestFlags(t *testing.T) {
import (
"context"
"fmt"
+ "go/build"
"os"
"os/exec"
"os/signal"
// listTools prints a list of the available tools in the tools directory.
func listTools() {
- f, err := os.Open(base.ToolDir)
+ f, err := os.Open(build.ToolDir)
if err != nil {
fmt.Fprintf(os.Stderr, "go: no tool directory: %s\n", err)
base.SetExitStatus(2)
sort.Strings(names)
for _, name := range names {
// Unify presentation by going to lower case.
- name = strings.ToLower(name)
// If it's windows, don't show the .exe suffix.
- if base.ToolIsWindows && strings.HasSuffix(name, base.ToolWindowsExtension) {
- name = name[:len(name)-len(base.ToolWindowsExtension)]
- }
+ name = strings.TrimSuffix(strings.ToLower(name), cfg.ToolExeSuffix())
+
// The tool directory used by gccgo will have other binaries
// in addition to go tools. Only display go tools here.
if cfg.BuildToolchainName == "gccgo" && !isGccgoTool(name) {
}
// On Windows, remove lingering ~ file from last attempt.
- if base.ToolIsWindows {
+ if runtime.GOOS == "windows" {
if _, err := os.Stat(dst + "~"); err == nil {
os.Remove(dst + "~")
}
mayberemovefile(dst)
df, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
- if err != nil && base.ToolIsWindows {
+ if err != nil && runtime.GOOS == "windows" {
// Windows does not allow deletion of a binary file
// while it is executing. Try to move it out of the way.
// If the move fails, which is likely, we'll try again the
homeEnvName() + "=/no-home",
"CCACHE_DISABLE=1", // ccache breaks with non-existent HOME
"GOARCH=" + runtime.GOARCH,
+ "TESTGO_GOHOSTARCH=" + goHostArch,
"GOCACHE=" + testGOCACHE,
"GODEBUG=" + os.Getenv("GODEBUG"),
"GOEXE=" + cfg.ExeSuffix,
"GOEXPERIMENT=" + os.Getenv("GOEXPERIMENT"),
"GOOS=" + runtime.GOOS,
+ "TESTGO_GOHOSTOS=" + goHostOS,
"GOPATH=" + filepath.Join(ts.workdir, "gopath"),
"GOPROXY=" + proxyURL,
"GOPRIVATE=",
if !testenv.HasExternalNetwork() {
ts.env = append(ts.env, "TESTGONETWORK=panic", "TESTGOVCS=panic")
}
+ if os.Getenv("CGO_ENABLED") != "" || runtime.GOOS != goHostOS || runtime.GOARCH != goHostArch {
+ // If the actual CGO_ENABLED might not match the cmd/go default, set it
+ // explicitly in the environment. Otherwise, leave it unset so that we also
+ // cover the default behaviors.
+ ts.env = append(ts.env, "CGO_ENABLED="+cgoEnabled)
+ }
for _, key := range extraEnvKeys {
if val := os.Getenv(key); val != "" {
switch cond.tag {
case runtime.GOOS, runtime.GOARCH, runtime.Compiler:
ok = true
+ case "cross":
+ ok = goHostOS != runtime.GOOS || goHostArch != runtime.GOARCH
case "short":
ok = testing.Short()
case "cgo":
tmpl := "{{if .Error}}{{.ImportPath}}: {{.Error.Err}}{{else}}"
switch want {
case failure:
- tmpl += "{{if .Stale}}{{.ImportPath}} is unexpectedly stale: {{.StaleReason}}{{end}}"
+ tmpl += `{{if .Stale}}{{.ImportPath}} ({{.Target}}) is unexpectedly stale:{{"\n\t"}}{{.StaleReason}}{{end}}`
case success:
- tmpl += "{{if not .Stale}}{{.ImportPath}} is unexpectedly NOT stale{{end}}"
+ tmpl += "{{if not .Stale}}{{.ImportPath}} ({{.Target}}) is unexpectedly NOT stale{{end}}"
default:
ts.fatalf("unsupported: %v stale", want)
}
goArgs := append([]string{"list", "-e", "-f=" + tmpl}, args...)
stdout, stderr, err := ts.exec(testGo, goArgs...)
if err != nil {
+ // Print stdout before stderr, because stderr may explain the error
+ // independent of whatever we may have printed to stdout.
ts.fatalf("go list: %v\n%s%s", err, stdout, stderr)
}
if stdout != "" {
- ts.fatalf("%s", stdout)
+ // Print stderr before stdout, because stderr may contain verbose
+ // debugging info (for example, if GODEBUG=gocachehash=1 is set)
+ // and we know that stdout contains a useful summary.
+ ts.fatalf("%s%s", stderr, stdout)
}
}
# Run same tests but with gccgo.
env GO111MODULE=off
[!exec:gccgo] stop
+[cross] stop # gccgo can't necessarily cross-compile
! go build -compiler=gccgo .
go build -compiler=gccgo -overlay overlay.json -o main_gccgo$GOEXE .
# Same sequence of tests but with gccgo.
# gccgo does not support builds in module mode.
[!exec:gccgo] stop
+[cross] stop # gccgo can't necessarily cross-compile
env GOPATH=$WORK/a
# A binary built with gccgo without -trimpath should contain the current
env GO111MODULE=off
[short] skip
+[cross] skip # gccgo can't necessarily cross-compile
cd m
go build
env GOBIN=gobin
mkdir gobin
go list -f '{{.Target}}' cmd/go
-stdout $GOROOT[/\\]bin[/\\]go$GOEXE
+stdout $GOROOT${/}bin${/}go$GOEXE
# Check that tools are installed to $GOTOOLDIR, not $GOBIN.
go list -f '{{.Target}}' cmd/compile
-stdout $GOROOT[/\\]pkg[/\\]tool[/\\]${GOOS}_${GOARCH}[/\\]compile$GOEXE
+stdout $GOROOT${/}pkg${/}tool${/}${GOOS}_${GOARCH}${/}compile$GOEXE
! go install ./needmod/needmod.go
stderr 'needmod[/\\]needmod.go:10:2: no required module provides package example.com/version: go.mod file not found in current directory or any parent directory; see ''go help modules''$'
-# 'go install' should succeed with a package in GOROOT.
+# 'go install' for a package in GOROOT should succeed.
+# (It must be non-stale here so that the test does not write to GOROOT).
+! stale cmd/addr2line
go install cmd/addr2line
! stderr .
# smoke test for complex build configuration
go build -o complex.exe complex
-[exec:gccgo] go build -compiler=gccgo -o complex.exe complex
+[!cross] [exec:gccgo] go build -compiler=gccgo -o complex.exe complex
-- complex/main.go --
package main