diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go index e970272954..a574924c5b 100644 --- a/src/cmd/go/internal/work/init.go +++ b/src/cmd/go/internal/work/init.go @@ -10,6 +10,7 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/load" + "cmd/internal/objabi" "cmd/internal/sys" "flag" "fmt" @@ -34,6 +35,20 @@ func BuildInit() { } cfg.BuildPkgdir = p } + + // For each experiment that has been enabled in the toolchain, define a + // build tag with the same name but prefixed by "goexperiment." which can be + // used for compiling alternative files for the experiment. This allows + // changes for the experiment, like extra struct fields in the runtime, + // without affecting the base non-experiment code at all. [2:] strips the + // leading "X:" from objabi.Expstring(). + exp := objabi.Expstring()[2:] + if exp != "none" { + experiments := strings.Split(exp, ",") + for _, expt := range experiments { + cfg.BuildContext.BuildTags = append(cfg.BuildContext.BuildTags, "goexperiment."+expt) + } + } } func instrumentInit() { diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go index 3eb66f9166..87afb6aec8 100644 --- a/src/cmd/go/script_test.go +++ b/src/cmd/go/script_test.go @@ -30,6 +30,7 @@ import ( "cmd/go/internal/robustio" "cmd/go/internal/txtar" "cmd/go/internal/work" + "cmd/internal/objabi" "cmd/internal/sys" ) @@ -119,6 +120,7 @@ func (ts *testScript) setup() { "GOCACHE=" + testGOCACHE, "GODEBUG=" + os.Getenv("GODEBUG"), "GOEXE=" + cfg.ExeSuffix, + "GOEXPSTRING=" + objabi.Expstring()[2:], "GOOS=" + runtime.GOOS, "GOPATH=" + filepath.Join(ts.workdir, "gopath"), "GOPROXY=" + proxyURL, diff --git a/src/cmd/go/testdata/script/README b/src/cmd/go/testdata/script/README index 81b6d9d814..f4c92e65ab 100644 --- a/src/cmd/go/testdata/script/README +++ b/src/cmd/go/testdata/script/README @@ -29,6 +29,7 @@ Scripts also have access to these other environment variables: GOARCH= GOCACHE= GOEXE= + GOEXPSTRING= GOOS= GOPATH=$WORK/gopath GOPROXY= diff --git a/src/cmd/go/testdata/script/build_tag_goexperiment.txt b/src/cmd/go/testdata/script/build_tag_goexperiment.txt new file mode 100644 index 0000000000..26ad029845 --- /dev/null +++ b/src/cmd/go/testdata/script/build_tag_goexperiment.txt @@ -0,0 +1,104 @@ +# compile_ext will fail if the buildtags that are enabled (or not enabled) for the +# framepointer and fieldtrack experiments are not consistent with the value of +# GOEXPSTRING (which comes from objabi.Expstring()). + +[short] skip +go run m + +-- expt_main.go -- +package main + +import ( + "os" + "strings" +) + +func main() { + fp() + ft() +} + +func hasExpEntry(s string) bool { + // script_test.go defines GOEXPSTRING to be the value of + // objabi.Expstring(), which gives the enabled experiments baked into the + // toolchain. + g := os.Getenv("GOEXPSTRING") + for _, f := range strings.Split(g, ",") { + if f == s { + return true + } + } + return false +} + +-- fp_off.go -- +// +build !goexperiment.framepointer + +package main + +import ( + "fmt" + "os" +) + +func fp() { + if hasExpEntry("framepointer") { + fmt.Println("in !framepointer build, but objabi.Expstring() has 'framepointer'") + os.Exit(1) + } +} + +-- fp_on.go -- +// +build goexperiment.framepointer + +package main + +import ( + "fmt" + "os" +) + +func fp() { + if !hasExpEntry("framepointer") { + fmt.Println("in framepointer build, but objabi.Expstring() does not have 'framepointer', is", os.Getenv("GOEXPSTRING")) + os.Exit(1) + } +} + +-- ft_off.go -- +// +build !goexperiment.fieldtrack + +package main + +import ( + "fmt" + "os" +) + +func ft() { + if hasExpEntry("fieldtrack") { + fmt.Println("in !fieldtrack build, but objabi.Expstring() has 'fieldtrack'") + os.Exit(1) + } +} + +-- ft_on.go -- +// +build goexperiment.fieldtrack + +package main + +import ( + "fmt" + "os" +) + +func ft() { + if !hasExpEntry("fieldtrack") { + fmt.Println("in fieldtrack build, but objabi.Expstring() does not have 'fieldtrack', is", os.Getenv("GOEXPSTRING")) + os.Exit(1) + } +} + +-- go.mod -- +module m +go 1.14