mirror of https://go.googlesource.com/go
234 lines
7.4 KiB
Go
234 lines
7.4 KiB
Go
// Copyright 2018 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package main_test
|
|
|
|
import (
|
|
"cmd/go/internal/cfg"
|
|
"cmd/go/internal/script"
|
|
"cmd/go/internal/script/scripttest"
|
|
"errors"
|
|
"fmt"
|
|
"internal/buildcfg"
|
|
"internal/platform"
|
|
"internal/testenv"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"runtime/debug"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
func scriptConditions() map[string]script.Cond {
|
|
conds := scripttest.DefaultConds()
|
|
|
|
add := func(name string, cond script.Cond) {
|
|
if _, ok := conds[name]; ok {
|
|
panic(fmt.Sprintf("condition %q is already registered", name))
|
|
}
|
|
conds[name] = cond
|
|
}
|
|
|
|
lazyBool := func(summary string, f func() bool) script.Cond {
|
|
return script.OnceCondition(summary, func() (bool, error) { return f(), nil })
|
|
}
|
|
|
|
add("abscc", script.Condition("default $CC path is absolute and exists", defaultCCIsAbsolute))
|
|
add("asan", sysCondition("-asan", platform.ASanSupported, true))
|
|
add("buildmode", script.PrefixCondition("go supports -buildmode=<suffix>", hasBuildmode))
|
|
add("case-sensitive", script.OnceCondition("$WORK filesystem is case-sensitive", isCaseSensitive))
|
|
add("cc", script.PrefixCondition("go env CC = <suffix> (ignoring the go/env file)", ccIs))
|
|
add("cgo", script.BoolCondition("host CGO_ENABLED", testenv.HasCGO()))
|
|
add("cgolinkext", script.Condition("platform requires external linking for cgo", cgoLinkExt))
|
|
add("cross", script.BoolCondition("cmd/go GOOS/GOARCH != GOHOSTOS/GOHOSTARCH", goHostOS != runtime.GOOS || goHostArch != runtime.GOARCH))
|
|
add("fuzz", sysCondition("-fuzz", platform.FuzzSupported, false))
|
|
add("fuzz-instrumented", sysCondition("-fuzz with instrumentation", platform.FuzzInstrumented, false))
|
|
add("git", lazyBool("the 'git' executable exists and provides the standard CLI", hasWorkingGit))
|
|
add("GODEBUG", script.PrefixCondition("GODEBUG contains <suffix>", hasGodebug))
|
|
add("GOEXPERIMENT", script.PrefixCondition("GOEXPERIMENT <suffix> is enabled", hasGoexperiment))
|
|
add("go-builder", script.BoolCondition("GO_BUILDER_NAME is non-empty", testenv.Builder() != ""))
|
|
add("link", lazyBool("testenv.HasLink()", testenv.HasLink))
|
|
add("msan", sysCondition("-msan", platform.MSanSupported, true))
|
|
add("mustlinkext", script.Condition("platform always requires external linking", mustLinkExt))
|
|
add("net", script.PrefixCondition("can connect to external network host <suffix>", hasNet))
|
|
add("pielinkext", script.Condition("platform requires external linking for PIE", pieLinkExt))
|
|
add("race", sysCondition("-race", platform.RaceDetectorSupported, true))
|
|
add("symlink", lazyBool("testenv.HasSymlink()", testenv.HasSymlink))
|
|
add("trimpath", script.OnceCondition("test binary was built with -trimpath", isTrimpath))
|
|
|
|
return conds
|
|
}
|
|
|
|
func defaultCCIsAbsolute(s *script.State) (bool, error) {
|
|
GOOS, _ := s.LookupEnv("GOOS")
|
|
GOARCH, _ := s.LookupEnv("GOARCH")
|
|
defaultCC := cfg.DefaultCC(GOOS, GOARCH)
|
|
if filepath.IsAbs(defaultCC) {
|
|
if _, err := exec.LookPath(defaultCC); err == nil {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func ccIs(s *script.State, want string) (bool, error) {
|
|
CC, _ := s.LookupEnv("CC")
|
|
if CC != "" {
|
|
return CC == want, nil
|
|
}
|
|
GOOS, _ := s.LookupEnv("GOOS")
|
|
GOARCH, _ := s.LookupEnv("GOARCH")
|
|
return cfg.DefaultCC(GOOS, GOARCH) == want, nil
|
|
}
|
|
|
|
func sysCondition(flag string, f func(goos, goarch string) bool, needsCgo bool) script.Cond {
|
|
return script.Condition(
|
|
"GOOS/GOARCH supports "+flag,
|
|
func(s *script.State) (bool, error) {
|
|
GOOS, _ := s.LookupEnv("GOOS")
|
|
GOARCH, _ := s.LookupEnv("GOARCH")
|
|
cross := goHostOS != GOOS || goHostArch != GOARCH
|
|
return (!needsCgo || (testenv.HasCGO() && !cross)) && f(GOOS, GOARCH), nil
|
|
})
|
|
}
|
|
|
|
func hasBuildmode(s *script.State, mode string) (bool, error) {
|
|
GOOS, _ := s.LookupEnv("GOOS")
|
|
GOARCH, _ := s.LookupEnv("GOARCH")
|
|
return platform.BuildModeSupported(runtime.Compiler, mode, GOOS, GOARCH), nil
|
|
}
|
|
|
|
var scriptNetEnabled sync.Map // testing.TB → already enabled
|
|
|
|
func hasNet(s *script.State, host string) (bool, error) {
|
|
if !testenv.HasExternalNetwork() {
|
|
return false, nil
|
|
}
|
|
|
|
// TODO(bcmills): Add a flag or environment variable to allow skipping tests
|
|
// for specific hosts and/or skipping all net tests except for specific hosts.
|
|
|
|
t, ok := tbFromContext(s.Context())
|
|
if !ok {
|
|
return false, errors.New("script Context unexpectedly missing testing.TB key")
|
|
}
|
|
|
|
if netTestSem != nil {
|
|
// When the number of external network connections is limited, we limit the
|
|
// number of net tests that can run concurrently so that the overall number
|
|
// of network connections won't exceed the limit.
|
|
_, dup := scriptNetEnabled.LoadOrStore(t, true)
|
|
if !dup {
|
|
// Acquire a net token for this test until the test completes.
|
|
netTestSem <- struct{}{}
|
|
t.Cleanup(func() {
|
|
<-netTestSem
|
|
scriptNetEnabled.Delete(t)
|
|
})
|
|
}
|
|
}
|
|
|
|
// Since we have confirmed that the network is available,
|
|
// allow cmd/go to use it.
|
|
s.Setenv("TESTGONETWORK", "")
|
|
return true, nil
|
|
}
|
|
|
|
func hasGodebug(s *script.State, value string) (bool, error) {
|
|
godebug, _ := s.LookupEnv("GODEBUG")
|
|
for _, p := range strings.Split(godebug, ",") {
|
|
if strings.TrimSpace(p) == value {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func hasGoexperiment(s *script.State, value string) (bool, error) {
|
|
GOOS, _ := s.LookupEnv("GOOS")
|
|
GOARCH, _ := s.LookupEnv("GOARCH")
|
|
goexp, _ := s.LookupEnv("GOEXPERIMENT")
|
|
flags, err := buildcfg.ParseGOEXPERIMENT(GOOS, GOARCH, goexp)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
for _, exp := range flags.All() {
|
|
if value == exp {
|
|
return true, nil
|
|
}
|
|
if strings.TrimPrefix(value, "no") == strings.TrimPrefix(exp, "no") {
|
|
return false, nil
|
|
}
|
|
}
|
|
return false, fmt.Errorf("unrecognized GOEXPERIMENT %q", value)
|
|
}
|
|
|
|
func isCaseSensitive() (bool, error) {
|
|
tmpdir, err := os.MkdirTemp(testTmpDir, "case-sensitive")
|
|
if err != nil {
|
|
return false, fmt.Errorf("failed to create directory to determine case-sensitivity: %w", err)
|
|
}
|
|
defer os.RemoveAll(tmpdir)
|
|
|
|
fcap := filepath.Join(tmpdir, "FILE")
|
|
if err := os.WriteFile(fcap, []byte{}, 0644); err != nil {
|
|
return false, fmt.Errorf("error writing file to determine case-sensitivity: %w", err)
|
|
}
|
|
|
|
flow := filepath.Join(tmpdir, "file")
|
|
_, err = os.ReadFile(flow)
|
|
switch {
|
|
case err == nil:
|
|
return false, nil
|
|
case os.IsNotExist(err):
|
|
return true, nil
|
|
default:
|
|
return false, fmt.Errorf("unexpected error reading file when determining case-sensitivity: %w", err)
|
|
}
|
|
}
|
|
|
|
func isTrimpath() (bool, error) {
|
|
info, _ := debug.ReadBuildInfo()
|
|
if info == nil {
|
|
return false, errors.New("missing build info")
|
|
}
|
|
|
|
for _, s := range info.Settings {
|
|
if s.Key == "-trimpath" && s.Value == "true" {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func hasWorkingGit() bool {
|
|
if runtime.GOOS == "plan9" {
|
|
// The Git command is usually not the real Git on Plan 9.
|
|
// See https://golang.org/issues/29640.
|
|
return false
|
|
}
|
|
_, err := exec.LookPath("git")
|
|
return err == nil
|
|
}
|
|
|
|
func cgoLinkExt(s *script.State) (bool, error) {
|
|
GOOS, _ := s.LookupEnv("GOOS")
|
|
GOARCH, _ := s.LookupEnv("GOARCH")
|
|
return platform.MustLinkExternal(GOOS, GOARCH, true), nil
|
|
}
|
|
|
|
func mustLinkExt(s *script.State) (bool, error) {
|
|
GOOS, _ := s.LookupEnv("GOOS")
|
|
GOARCH, _ := s.LookupEnv("GOARCH")
|
|
return platform.MustLinkExternal(GOOS, GOARCH, false), nil
|
|
}
|
|
|
|
func pieLinkExt(s *script.State) (bool, error) {
|
|
GOOS, _ := s.LookupEnv("GOOS")
|
|
GOARCH, _ := s.LookupEnv("GOARCH")
|
|
return !platform.InternalLinkPIESupported(GOOS, GOARCH), nil
|
|
}
|