Skip to content

Commit b60f88d

Browse files
committed
cmd/internal/script: new hook for adding in toolchain script conditions
Introduce a new function AddToolChainScriptConditions that augments a default "script.Cond" set with a collection of useful conditions, including godebug/goexperiment, cgo, race support, buildmode, asan, msan, and so on. Having these conditions available makes it easier to write script tests that deal with specific build-flavor corner cases. The functions backing the new conditions are helper functions migrated over from the Go command's script test setup. Updates #68606. Change-Id: I14def1115b54dc47529c983abcd2c5ea9326b9de Reviewed-on: https://go-review.googlesource.com/c/go/+/601715 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Cherry Mui <[email protected]>
1 parent 86bec1e commit b60f88d

File tree

5 files changed

+126
-93
lines changed

5 files changed

+126
-93
lines changed

src/cmd/compile/testdata/script/script_test_basics.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
go build
55
[!cgo] skip
66
cc -c testdata/mumble.c
7+
[GOEXPERIMENT:fieldtrack] help exec
78

89
-- go.mod --
910
module main

src/cmd/go/script_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func TestScript(t *testing.T) {
8989
t.Fatal(err)
9090
}
9191
engine := &script.Engine{
92-
Conds: scriptConditions(),
92+
Conds: scriptConditions(t),
9393
Cmds: scriptCommands(quitSignal(), gracePeriod),
9494
Quiet: !testing.Verbose(),
9595
}

src/cmd/go/scriptconds_test.go

Lines changed: 4 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,21 @@ import (
1010
"cmd/internal/script/scripttest"
1111
"errors"
1212
"fmt"
13-
"internal/buildcfg"
14-
"internal/platform"
1513
"internal/testenv"
1614
"os"
1715
"os/exec"
1816
"path/filepath"
1917
"runtime"
2018
"runtime/debug"
21-
"strings"
2219
"sync"
20+
"testing"
2321
)
2422

25-
func scriptConditions() map[string]script.Cond {
23+
func scriptConditions(t *testing.T) map[string]script.Cond {
2624
conds := scripttest.DefaultConds()
2725

26+
scripttest.AddToolChainScriptConditions(t, conds, goHostOS, goHostArch)
27+
2828
add := func(name string, cond script.Cond) {
2929
if _, ok := conds[name]; ok {
3030
panic(fmt.Sprintf("condition %q is already registered", name))
@@ -37,26 +37,10 @@ func scriptConditions() map[string]script.Cond {
3737
}
3838

3939
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))
4240
add("case-sensitive", script.OnceCondition("$WORK filesystem is case-sensitive", isCaseSensitive))
4341
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))
4942
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))
5643
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))
6044
add("trimpath", script.OnceCondition("test binary was built with -trimpath", isTrimpath))
6145

6246
return conds
@@ -84,23 +68,6 @@ func ccIs(s *script.State, want string) (bool, error) {
8468
return cfg.DefaultCC(GOOS, GOARCH) == want, nil
8569
}
8670

87-
func 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-
98-
func 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-
10471
var scriptNetEnabled sync.Map // testing.TB → already enabled
10572

10673
func hasNet(s *script.State, host string) (bool, error) {
@@ -137,35 +104,6 @@ func hasNet(s *script.State, host string) (bool, error) {
137104
return true, nil
138105
}
139106

140-
func 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-
150-
func 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-
169107
func isCaseSensitive() (bool, error) {
170108
tmpdir, err := os.MkdirTemp(testTmpDir, "case-sensitive")
171109
if err != nil {
@@ -213,21 +151,3 @@ func hasWorkingGit() bool {
213151
_, err := exec.LookPath("git")
214152
return err == nil
215153
}
216-
217-
func 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-
223-
func 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-
229-
func pieLinkExt(s *script.State) (bool, error) {
230-
GOOS, _ := s.LookupEnv("GOOS")
231-
GOARCH, _ := s.LookupEnv("GOARCH")
232-
return !platform.InternalLinkPIESupported(GOOS, GOARCH), nil
233-
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Copyright 2024 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+
5+
package scripttest
6+
7+
import (
8+
"cmd/internal/script"
9+
"fmt"
10+
"internal/buildcfg"
11+
"internal/platform"
12+
"internal/testenv"
13+
"runtime"
14+
"strings"
15+
"testing"
16+
)
17+
18+
// AddToolChainConditions accepts a script.Cond map and adds into it a
19+
// set of commonly used conditions for doing toolchains testing,
20+
// including whether the platform supports cgo, a buildmode condition,
21+
// support for GOEXPERIMENT testing, etc. Callers must also pass in
22+
// current GOHOSTOOS/GOHOSTARCH settings, since some of the conditions
23+
// introduced can be influenced by them.
24+
func AddToolChainScriptConditions(t *testing.T, conds map[string]script.Cond, goHostOS, goHostArch string) {
25+
add := func(name string, cond script.Cond) {
26+
if _, ok := conds[name]; ok {
27+
t.Fatalf("condition %q is already registered", name)
28+
}
29+
conds[name] = cond
30+
}
31+
32+
lazyBool := func(summary string, f func() bool) script.Cond {
33+
return script.OnceCondition(summary, func() (bool, error) { return f(), nil })
34+
}
35+
36+
add("asan", sysCondition("-asan", platform.ASanSupported, true, goHostOS, goHostArch))
37+
add("buildmode", script.PrefixCondition("go supports -buildmode=<suffix>", hasBuildmode))
38+
add("cgo", script.BoolCondition("host CGO_ENABLED", testenv.HasCGO()))
39+
add("cgolinkext", script.Condition("platform requires external linking for cgo", cgoLinkExt))
40+
add("cross", script.BoolCondition("cmd/go GOOS/GOARCH != GOHOSTOS/GOHOSTARCH", goHostOS != runtime.GOOS || goHostArch != runtime.GOARCH))
41+
add("fuzz", sysCondition("-fuzz", platform.FuzzSupported, false, goHostOS, goHostArch))
42+
add("fuzz-instrumented", sysCondition("-fuzz with instrumentation", platform.FuzzInstrumented, false, goHostOS, goHostArch))
43+
add("GODEBUG", script.PrefixCondition("GODEBUG contains <suffix>", hasGodebug))
44+
add("GOEXPERIMENT", script.PrefixCondition("GOEXPERIMENT <suffix> is enabled", hasGoexperiment))
45+
add("go-builder", script.BoolCondition("GO_BUILDER_NAME is non-empty", testenv.Builder() != ""))
46+
add("link", lazyBool("testenv.HasLink()", testenv.HasLink))
47+
add("msan", sysCondition("-msan", platform.MSanSupported, true, goHostOS, goHostArch))
48+
add("mustlinkext", script.Condition("platform always requires external linking", mustLinkExt))
49+
add("pielinkext", script.Condition("platform requires external linking for PIE", pieLinkExt))
50+
add("race", sysCondition("-race", platform.RaceDetectorSupported, true, goHostOS, goHostArch))
51+
add("symlink", lazyBool("testenv.HasSymlink()", testenv.HasSymlink))
52+
}
53+
54+
func sysCondition(flag string, f func(goos, goarch string) bool, needsCgo bool, goHostOS, goHostArch string) script.Cond {
55+
return script.Condition(
56+
"GOOS/GOARCH supports "+flag,
57+
func(s *script.State) (bool, error) {
58+
GOOS, _ := s.LookupEnv("GOOS")
59+
GOARCH, _ := s.LookupEnv("GOARCH")
60+
cross := goHostOS != GOOS || goHostArch != GOARCH
61+
return (!needsCgo || (testenv.HasCGO() && !cross)) && f(GOOS, GOARCH), nil
62+
})
63+
}
64+
65+
func hasBuildmode(s *script.State, mode string) (bool, error) {
66+
GOOS, _ := s.LookupEnv("GOOS")
67+
GOARCH, _ := s.LookupEnv("GOARCH")
68+
return platform.BuildModeSupported(runtime.Compiler, mode, GOOS, GOARCH), nil
69+
}
70+
71+
func cgoLinkExt(s *script.State) (bool, error) {
72+
GOOS, _ := s.LookupEnv("GOOS")
73+
GOARCH, _ := s.LookupEnv("GOARCH")
74+
return platform.MustLinkExternal(GOOS, GOARCH, true), nil
75+
}
76+
77+
func mustLinkExt(s *script.State) (bool, error) {
78+
GOOS, _ := s.LookupEnv("GOOS")
79+
GOARCH, _ := s.LookupEnv("GOARCH")
80+
return platform.MustLinkExternal(GOOS, GOARCH, false), nil
81+
}
82+
83+
func pieLinkExt(s *script.State) (bool, error) {
84+
GOOS, _ := s.LookupEnv("GOOS")
85+
GOARCH, _ := s.LookupEnv("GOARCH")
86+
return !platform.InternalLinkPIESupported(GOOS, GOARCH), nil
87+
}
88+
89+
func hasGodebug(s *script.State, value string) (bool, error) {
90+
godebug, _ := s.LookupEnv("GODEBUG")
91+
for _, p := range strings.Split(godebug, ",") {
92+
if strings.TrimSpace(p) == value {
93+
return true, nil
94+
}
95+
}
96+
return false, nil
97+
}
98+
99+
func hasGoexperiment(s *script.State, value string) (bool, error) {
100+
GOOS, _ := s.LookupEnv("GOOS")
101+
GOARCH, _ := s.LookupEnv("GOARCH")
102+
goexp, _ := s.LookupEnv("GOEXPERIMENT")
103+
flags, err := buildcfg.ParseGOEXPERIMENT(GOOS, GOARCH, goexp)
104+
if err != nil {
105+
return false, err
106+
}
107+
for _, exp := range flags.All() {
108+
if value == exp {
109+
return true, nil
110+
}
111+
if strings.TrimPrefix(value, "no") == strings.TrimPrefix(exp, "no") {
112+
return false, nil
113+
}
114+
}
115+
return false, fmt.Errorf("unrecognized GOEXPERIMENT %q", value)
116+
}

src/cmd/internal/script/scripttest/run.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,6 @@ func RunToolScriptTest(t *testing.T, repls []ToolReplacement, pattern string) {
7171
cmds[name] = cmd
7272
}
7373

74-
addcond := func(name string, cond script.Cond) {
75-
if _, ok := conds[name]; ok {
76-
panic(fmt.Sprintf("condition %q is already registered", name))
77-
}
78-
conds[name] = cond
79-
}
80-
8174
prependToPath := func(env []string, dir string) {
8275
found := false
8376
for k := range env {
@@ -135,7 +128,10 @@ func RunToolScriptTest(t *testing.T, repls []ToolReplacement, pattern string) {
135128
cccmd := script.Program(goEnv("CC"), interrupt, gracePeriod)
136129
addcmd("go", gocmd)
137130
addcmd("cc", cccmd)
138-
addcond("cgo", script.BoolCondition("host CGO_ENABLED", testenv.HasCGO()))
131+
132+
// Add various helpful conditions related to builds and toolchain use.
133+
goHostOS, goHostArch := goEnv("GOHOSTOS"), goEnv("GOHOSTARCH")
134+
AddToolChainScriptConditions(t, conds, goHostOS, goHostArch)
139135

140136
// Environment setup.
141137
env := os.Environ()

0 commit comments

Comments
 (0)