• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package main_test
6
7import (
8	"cmd/go/internal/cfg"
9	"cmd/go/internal/script"
10	"cmd/go/internal/script/scripttest"
11	"errors"
12	"fmt"
13	"internal/buildcfg"
14	"internal/platform"
15	"internal/testenv"
16	"os"
17	"os/exec"
18	"path/filepath"
19	"runtime"
20	"runtime/debug"
21	"strings"
22	"sync"
23)
24
25func scriptConditions() map[string]script.Cond {
26	conds := scripttest.DefaultConds()
27
28	add := func(name string, cond script.Cond) {
29		if _, ok := conds[name]; ok {
30			panic(fmt.Sprintf("condition %q is already registered", name))
31		}
32		conds[name] = cond
33	}
34
35	lazyBool := func(summary string, f func() bool) script.Cond {
36		return script.OnceCondition(summary, func() (bool, error) { return f(), nil })
37	}
38
39	add("abscc", script.Condition("default $CC path is absolute and exists", defaultCCIsAbsolute))
40	add("asan", sysCondition("-asan", platform.ASanSupported, true))
41	add("buildmode", script.PrefixCondition("go supports -buildmode=<suffix>", hasBuildmode))
42	add("case-sensitive", script.OnceCondition("$WORK filesystem is case-sensitive", isCaseSensitive))
43	add("cc", script.PrefixCondition("go env CC = <suffix> (ignoring the go/env file)", ccIs))
44	add("cgo", script.BoolCondition("host CGO_ENABLED", testenv.HasCGO()))
45	add("cgolinkext", script.Condition("platform requires external linking for cgo", cgoLinkExt))
46	add("cross", script.BoolCondition("cmd/go GOOS/GOARCH != GOHOSTOS/GOHOSTARCH", goHostOS != runtime.GOOS || goHostArch != runtime.GOARCH))
47	add("fuzz", sysCondition("-fuzz", platform.FuzzSupported, false))
48	add("fuzz-instrumented", sysCondition("-fuzz with instrumentation", platform.FuzzInstrumented, false))
49	add("git", lazyBool("the 'git' executable exists and provides the standard CLI", hasWorkingGit))
50	add("GODEBUG", script.PrefixCondition("GODEBUG contains <suffix>", hasGodebug))
51	add("GOEXPERIMENT", script.PrefixCondition("GOEXPERIMENT <suffix> is enabled", hasGoexperiment))
52	add("go-builder", script.BoolCondition("GO_BUILDER_NAME is non-empty", testenv.Builder() != ""))
53	add("link", lazyBool("testenv.HasLink()", testenv.HasLink))
54	add("msan", sysCondition("-msan", platform.MSanSupported, true))
55	add("mustlinkext", script.Condition("platform always requires external linking", mustLinkExt))
56	add("net", script.PrefixCondition("can connect to external network host <suffix>", hasNet))
57	add("pielinkext", script.Condition("platform requires external linking for PIE", pieLinkExt))
58	add("race", sysCondition("-race", platform.RaceDetectorSupported, true))
59	add("symlink", lazyBool("testenv.HasSymlink()", testenv.HasSymlink))
60	add("trimpath", script.OnceCondition("test binary was built with -trimpath", isTrimpath))
61
62	return conds
63}
64
65func defaultCCIsAbsolute(s *script.State) (bool, error) {
66	GOOS, _ := s.LookupEnv("GOOS")
67	GOARCH, _ := s.LookupEnv("GOARCH")
68	defaultCC := cfg.DefaultCC(GOOS, GOARCH)
69	if filepath.IsAbs(defaultCC) {
70		if _, err := exec.LookPath(defaultCC); err == nil {
71			return true, nil
72		}
73	}
74	return false, nil
75}
76
77func ccIs(s *script.State, want string) (bool, error) {
78	CC, _ := s.LookupEnv("CC")
79	if CC != "" {
80		return CC == want, nil
81	}
82	GOOS, _ := s.LookupEnv("GOOS")
83	GOARCH, _ := s.LookupEnv("GOARCH")
84	return cfg.DefaultCC(GOOS, GOARCH) == want, nil
85}
86
87func sysCondition(flag string, f func(goos, goarch string) bool, needsCgo bool) script.Cond {
88	return script.Condition(
89		"GOOS/GOARCH supports "+flag,
90		func(s *script.State) (bool, error) {
91			GOOS, _ := s.LookupEnv("GOOS")
92			GOARCH, _ := s.LookupEnv("GOARCH")
93			cross := goHostOS != GOOS || goHostArch != GOARCH
94			return (!needsCgo || (testenv.HasCGO() && !cross)) && f(GOOS, GOARCH), nil
95		})
96}
97
98func hasBuildmode(s *script.State, mode string) (bool, error) {
99	GOOS, _ := s.LookupEnv("GOOS")
100	GOARCH, _ := s.LookupEnv("GOARCH")
101	return platform.BuildModeSupported(runtime.Compiler, mode, GOOS, GOARCH), nil
102}
103
104var scriptNetEnabled sync.Map // testing.TB → already enabled
105
106func hasNet(s *script.State, host string) (bool, error) {
107	if !testenv.HasExternalNetwork() {
108		return false, nil
109	}
110
111	// TODO(bcmills): Add a flag or environment variable to allow skipping tests
112	// for specific hosts and/or skipping all net tests except for specific hosts.
113
114	t, ok := tbFromContext(s.Context())
115	if !ok {
116		return false, errors.New("script Context unexpectedly missing testing.TB key")
117	}
118
119	if netTestSem != nil {
120		// When the number of external network connections is limited, we limit the
121		// number of net tests that can run concurrently so that the overall number
122		// of network connections won't exceed the limit.
123		_, dup := scriptNetEnabled.LoadOrStore(t, true)
124		if !dup {
125			// Acquire a net token for this test until the test completes.
126			netTestSem <- struct{}{}
127			t.Cleanup(func() {
128				<-netTestSem
129				scriptNetEnabled.Delete(t)
130			})
131		}
132	}
133
134	// Since we have confirmed that the network is available,
135	// allow cmd/go to use it.
136	s.Setenv("TESTGONETWORK", "")
137	return true, nil
138}
139
140func hasGodebug(s *script.State, value string) (bool, error) {
141	godebug, _ := s.LookupEnv("GODEBUG")
142	for _, p := range strings.Split(godebug, ",") {
143		if strings.TrimSpace(p) == value {
144			return true, nil
145		}
146	}
147	return false, nil
148}
149
150func hasGoexperiment(s *script.State, value string) (bool, error) {
151	GOOS, _ := s.LookupEnv("GOOS")
152	GOARCH, _ := s.LookupEnv("GOARCH")
153	goexp, _ := s.LookupEnv("GOEXPERIMENT")
154	flags, err := buildcfg.ParseGOEXPERIMENT(GOOS, GOARCH, goexp)
155	if err != nil {
156		return false, err
157	}
158	for _, exp := range flags.All() {
159		if value == exp {
160			return true, nil
161		}
162		if strings.TrimPrefix(value, "no") == strings.TrimPrefix(exp, "no") {
163			return false, nil
164		}
165	}
166	return false, fmt.Errorf("unrecognized GOEXPERIMENT %q", value)
167}
168
169func isCaseSensitive() (bool, error) {
170	tmpdir, err := os.MkdirTemp(testTmpDir, "case-sensitive")
171	if err != nil {
172		return false, fmt.Errorf("failed to create directory to determine case-sensitivity: %w", err)
173	}
174	defer os.RemoveAll(tmpdir)
175
176	fcap := filepath.Join(tmpdir, "FILE")
177	if err := os.WriteFile(fcap, []byte{}, 0644); err != nil {
178		return false, fmt.Errorf("error writing file to determine case-sensitivity: %w", err)
179	}
180
181	flow := filepath.Join(tmpdir, "file")
182	_, err = os.ReadFile(flow)
183	switch {
184	case err == nil:
185		return false, nil
186	case os.IsNotExist(err):
187		return true, nil
188	default:
189		return false, fmt.Errorf("unexpected error reading file when determining case-sensitivity: %w", err)
190	}
191}
192
193func isTrimpath() (bool, error) {
194	info, _ := debug.ReadBuildInfo()
195	if info == nil {
196		return false, errors.New("missing build info")
197	}
198
199	for _, s := range info.Settings {
200		if s.Key == "-trimpath" && s.Value == "true" {
201			return true, nil
202		}
203	}
204	return false, nil
205}
206
207func hasWorkingGit() bool {
208	if runtime.GOOS == "plan9" {
209		// The Git command is usually not the real Git on Plan 9.
210		// See https://golang.org/issues/29640.
211		return false
212	}
213	_, err := exec.LookPath("git")
214	return err == nil
215}
216
217func cgoLinkExt(s *script.State) (bool, error) {
218	GOOS, _ := s.LookupEnv("GOOS")
219	GOARCH, _ := s.LookupEnv("GOARCH")
220	return platform.MustLinkExternal(GOOS, GOARCH, true), nil
221}
222
223func mustLinkExt(s *script.State) (bool, error) {
224	GOOS, _ := s.LookupEnv("GOOS")
225	GOARCH, _ := s.LookupEnv("GOARCH")
226	return platform.MustLinkExternal(GOOS, GOARCH, false), nil
227}
228
229func pieLinkExt(s *script.State) (bool, error) {
230	GOOS, _ := s.LookupEnv("GOOS")
231	GOARCH, _ := s.LookupEnv("GOARCH")
232	return !platform.InternalLinkPIESupported(GOOS, GOARCH), nil
233}
234