Source file
src/cmd/go/scriptconds_test.go
Documentation: cmd/go
1
2
3
4
5 package main_test
6
7 import (
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
25 func 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("mismatched-goroot", script.Condition("test's GOROOT_FINAL does not match the real GOROOT", isMismatchedGoroot))
55 add("msan", sysCondition("-msan", platform.MSanSupported, true))
56 add("mustlinkext", script.Condition("platform always requires external linking", mustLinkExt))
57 add("net", script.PrefixCondition("can connect to external network host <suffix>", hasNet))
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
65 func 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
77 func 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
87 func isMismatchedGoroot(s *script.State) (bool, error) {
88 gorootFinal, _ := s.LookupEnv("GOROOT_FINAL")
89 if gorootFinal == "" {
90 gorootFinal, _ = s.LookupEnv("GOROOT")
91 }
92 return gorootFinal != testGOROOT, nil
93 }
94
95 func sysCondition(flag string, f func(goos, goarch string) bool, needsCgo bool) script.Cond {
96 return script.Condition(
97 "GOOS/GOARCH supports "+flag,
98 func(s *script.State) (bool, error) {
99 GOOS, _ := s.LookupEnv("GOOS")
100 GOARCH, _ := s.LookupEnv("GOARCH")
101 cross := goHostOS != GOOS || goHostArch != GOARCH
102 return (!needsCgo || (testenv.HasCGO() && !cross)) && f(GOOS, GOARCH), nil
103 })
104 }
105
106 func hasBuildmode(s *script.State, mode string) (bool, error) {
107 GOOS, _ := s.LookupEnv("GOOS")
108 GOARCH, _ := s.LookupEnv("GOARCH")
109 return platform.BuildModeSupported(runtime.Compiler, mode, GOOS, GOARCH), nil
110 }
111
112 var scriptNetEnabled sync.Map
113
114 func hasNet(s *script.State, host string) (bool, error) {
115 if !testenv.HasExternalNetwork() {
116 return false, nil
117 }
118
119
120
121
122 t, ok := tbFromContext(s.Context())
123 if !ok {
124 return false, errors.New("script Context unexpectedly missing testing.TB key")
125 }
126
127 if netTestSem != nil {
128
129
130
131 _, dup := scriptNetEnabled.LoadOrStore(t, true)
132 if !dup {
133
134 netTestSem <- struct{}{}
135 t.Cleanup(func() {
136 <-netTestSem
137 scriptNetEnabled.Delete(t)
138 })
139 }
140 }
141
142
143
144 s.Setenv("TESTGONETWORK", "")
145 return true, nil
146 }
147
148 func hasGodebug(s *script.State, value string) (bool, error) {
149 godebug, _ := s.LookupEnv("GODEBUG")
150 for _, p := range strings.Split(godebug, ",") {
151 if strings.TrimSpace(p) == value {
152 return true, nil
153 }
154 }
155 return false, nil
156 }
157
158 func hasGoexperiment(s *script.State, value string) (bool, error) {
159 GOOS, _ := s.LookupEnv("GOOS")
160 GOARCH, _ := s.LookupEnv("GOARCH")
161 goexp, _ := s.LookupEnv("GOEXPERIMENT")
162 flags, err := buildcfg.ParseGOEXPERIMENT(GOOS, GOARCH, goexp)
163 if err != nil {
164 return false, err
165 }
166 for _, exp := range flags.All() {
167 if value == exp {
168 return true, nil
169 }
170 if strings.TrimPrefix(value, "no") == strings.TrimPrefix(exp, "no") {
171 return false, nil
172 }
173 }
174 return false, fmt.Errorf("unrecognized GOEXPERIMENT %q", value)
175 }
176
177 func isCaseSensitive() (bool, error) {
178 tmpdir, err := os.MkdirTemp(testTmpDir, "case-sensitive")
179 if err != nil {
180 return false, fmt.Errorf("failed to create directory to determine case-sensitivity: %w", err)
181 }
182 defer os.RemoveAll(tmpdir)
183
184 fcap := filepath.Join(tmpdir, "FILE")
185 if err := os.WriteFile(fcap, []byte{}, 0644); err != nil {
186 return false, fmt.Errorf("error writing file to determine case-sensitivity: %w", err)
187 }
188
189 flow := filepath.Join(tmpdir, "file")
190 _, err = os.ReadFile(flow)
191 switch {
192 case err == nil:
193 return false, nil
194 case os.IsNotExist(err):
195 return true, nil
196 default:
197 return false, fmt.Errorf("unexpected error reading file when determining case-sensitivity: %w", err)
198 }
199 }
200
201 func isTrimpath() (bool, error) {
202 info, _ := debug.ReadBuildInfo()
203 if info == nil {
204 return false, errors.New("missing build info")
205 }
206
207 for _, s := range info.Settings {
208 if s.Key == "-trimpath" && s.Value == "true" {
209 return true, nil
210 }
211 }
212 return false, nil
213 }
214
215 func hasWorkingGit() bool {
216 if runtime.GOOS == "plan9" {
217
218
219 return false
220 }
221 _, err := exec.LookPath("git")
222 return err == nil
223 }
224
225 func cgoLinkExt(s *script.State) (bool, error) {
226 GOOS, _ := s.LookupEnv("GOOS")
227 GOARCH, _ := s.LookupEnv("GOARCH")
228 return platform.MustLinkExternal(GOOS, GOARCH, true), nil
229 }
230
231 func mustLinkExt(s *script.State) (bool, error) {
232 GOOS, _ := s.LookupEnv("GOOS")
233 GOARCH, _ := s.LookupEnv("GOARCH")
234 return platform.MustLinkExternal(GOOS, GOARCH, false), nil
235 }
236
View as plain text