// Set macros for GOEXPERIMENTs so we can easily switch
// runtime assembly code based on them.
if *flags.CompilingRuntime {
- for _, exp := range buildcfg.EnabledExperiments() {
+ for _, exp := range buildcfg.Experiment.Enabled() {
// Define macro.
name := "GOEXPERIMENT_" + exp
macros[name] = &Macro{
"cmd/go/internal/fsys"
)
+// Global build parameters (used during package load)
+var (
+ Goos = envOr("GOOS", build.Default.GOOS)
+ Goarch = envOr("GOARCH", build.Default.GOARCH)
+
+ ExeSuffix = exeSuffix()
+
+ // ModulesEnabled specifies whether the go command is running
+ // in module-aware mode (as opposed to GOPATH mode).
+ // It is equal to modload.Enabled, but not all packages can import modload.
+ ModulesEnabled bool
+)
+
+func exeSuffix() string {
+ if Goos == "windows" {
+ return ".exe"
+ }
+ return ""
+}
+
// These are general "build flags" used by build and other commands.
var (
BuildA bool // -a flag
// GoPathError is set when GOPATH is not set. it contains an
// explanation why GOPATH is unset.
GoPathError string
-
- GOEXPERIMENT = envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT)
)
func defaultContext() build.Context {
build.ToolDir = filepath.Join(ctxt.GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
}
- ctxt.GOPATH = envOr("GOPATH", gopath(ctxt))
-
// Override defaults computed in go/build with defaults
// from go environment configuration file, if known.
- ctxt.GOOS = envOr("GOOS", ctxt.GOOS)
- ctxt.GOARCH = envOr("GOARCH", ctxt.GOARCH)
+ ctxt.GOPATH = envOr("GOPATH", gopath(ctxt))
+ ctxt.GOOS = Goos
+ ctxt.GOARCH = Goarch
- // The experiments flags are based on GOARCH, so they may
- // need to change. TODO: This should be cleaned up.
- buildcfg.UpdateExperiments(ctxt.GOOS, ctxt.GOARCH, GOEXPERIMENT)
+ // ToolTags are based on GOEXPERIMENT, which we will parse and
+ // initialize later.
ctxt.ToolTags = nil
- for _, exp := range buildcfg.EnabledExperiments() {
- ctxt.ToolTags = append(ctxt.ToolTags, "goexperiment."+exp)
- }
// The go/build rule for whether cgo is enabled is:
// 1. If $CGO_ENABLED is set, respect it.
BuildToolchainLinker = func() string { return "missing-linker" }
}
+// Experiment configuration.
+var (
+ // RawGOEXPERIMENT is the GOEXPERIMENT value set by the user.
+ RawGOEXPERIMENT = envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT)
+ // CleanGOEXPERIMENT is the minimal GOEXPERIMENT value needed to reproduce the
+ // experiments enabled by RawGOEXPERIMENT.
+ CleanGOEXPERIMENT = RawGOEXPERIMENT
+
+ Experiment *buildcfg.ExperimentFlags
+ ExperimentErr error
+)
+
+func init() {
+ Experiment, ExperimentErr = buildcfg.ParseGOEXPERIMENT(Goos, Goarch, RawGOEXPERIMENT)
+ if ExperimentErr != nil {
+ return
+ }
+
+ // GOEXPERIMENT is valid, so convert it to canonical form.
+ CleanGOEXPERIMENT = Experiment.String()
+
+ // Add build tags based on the experiments in effect.
+ for _, exp := range Experiment.Enabled() {
+ BuildContext.ToolTags = append(BuildContext.ToolTags, "goexperiment."+exp)
+ }
+}
+
// An EnvVar is an environment variable Name=Value.
type EnvVar struct {
Name string
// not CmdEnv.
var CmdEnv []EnvVar
-// Global build parameters (used during package load)
-var (
- Goarch = BuildContext.GOARCH
- Goos = BuildContext.GOOS
-
- ExeSuffix = exeSuffix()
-
- // ModulesEnabled specifies whether the go command is running
- // in module-aware mode (as opposed to GOPATH mode).
- // It is equal to modload.Enabled, but not all packages can import modload.
- ModulesEnabled bool
-)
-
-func exeSuffix() string {
- if Goos == "windows" {
- return ".exe"
- }
- return ""
-}
-
var envCache struct {
once sync.Once
m map[string]string
{Name: "GOCACHE", Value: cache.DefaultDir()},
{Name: "GOENV", Value: envFile},
{Name: "GOEXE", Value: cfg.ExeSuffix},
- {Name: "GOEXPERIMENT", Value: buildcfg.GOEXPERIMENT()},
+
+ // List the raw value of GOEXPERIMENT, not the cleaned one.
+ // The set of default experiments may change from one release
+ // to the next, so a GOEXPERIMENT setting that is redundant
+ // with the current toolchain might actually be relevant with
+ // a different version (for example, when bisecting a regression).
+ {Name: "GOEXPERIMENT", Value: cfg.RawGOEXPERIMENT},
+
{Name: "GOFLAGS", Value: cfg.Getenv("GOFLAGS")},
{Name: "GOHOSTARCH", Value: runtime.GOARCH},
{Name: "GOHOSTOS", Value: runtime.GOOS},
}
buildcfg.Check()
+ if cfg.ExperimentErr != nil {
+ base.Fatalf("go: %v", cfg.ExperimentErr)
+ }
env := cfg.CmdEnv
env = append(env, ExtraEnvVars()...)
}
}
- goexperiment, okGOEXPERIMENT := get("GOEXPERIMENT", buildcfg.GOEXPERIMENT(), "")
+ goexperiment, okGOEXPERIMENT := get("GOEXPERIMENT", cfg.RawGOEXPERIMENT, buildcfg.DefaultGOEXPERIMENT)
if okGOEXPERIMENT {
- if _, _, err := buildcfg.ParseGOEXPERIMENT(goos, goarch, goexperiment); err != nil {
+ if _, err := buildcfg.ParseGOEXPERIMENT(goos, goarch, goexperiment); err != nil {
return err
}
}
}
}
appendSetting("GOARCH", cfg.BuildContext.GOARCH)
- if cfg.GOEXPERIMENT != "" {
- appendSetting("GOEXPERIMENT", cfg.GOEXPERIMENT)
+ if cfg.RawGOEXPERIMENT != "" {
+ appendSetting("GOEXPERIMENT", cfg.RawGOEXPERIMENT)
}
appendSetting("GOOS", cfg.BuildContext.GOOS)
if key, val := cfg.GetArchEnv(); key != "" && val != "" {
"encoding/json"
"errors"
"fmt"
- "internal/buildcfg"
exec "internal/execabs"
"internal/lazyregexp"
"io"
key, val := cfg.GetArchEnv()
fmt.Fprintf(h, "%s=%s\n", key, val)
- if goexperiment := buildcfg.GOEXPERIMENT(); goexperiment != "" {
- fmt.Fprintf(h, "GOEXPERIMENT=%q\n", goexperiment)
+ if cfg.CleanGOEXPERIMENT != "" {
+ fmt.Fprintf(h, "GOEXPERIMENT=%q\n", cfg.CleanGOEXPERIMENT)
}
// TODO(rsc): Convince compiler team not to add more magic environment variables,
key, val := cfg.GetArchEnv()
fmt.Fprintf(h, "%s=%s\n", key, val)
- if goexperiment := buildcfg.GOEXPERIMENT(); goexperiment != "" {
- fmt.Fprintf(h, "GOEXPERIMENT=%q\n", goexperiment)
+ if cfg.CleanGOEXPERIMENT != "" {
+ fmt.Fprintf(h, "GOEXPERIMENT=%q\n", cfg.CleanGOEXPERIMENT)
}
// The linker writes source file paths that say GOROOT_FINAL, but
"bufio"
"bytes"
"fmt"
- "internal/buildcfg"
"io"
"log"
"os"
}
// TODO: Test and delete these conditions.
- if buildcfg.Experiment.FieldTrack || buildcfg.Experiment.PreemptibleLoops {
+ if cfg.ExperimentErr != nil || cfg.Experiment.FieldTrack || cfg.Experiment.PreemptibleLoops {
canDashC = false
}
// 'go env' handles checking the build config
if cmd != envcmd.CmdEnv {
buildcfg.Check()
+ if cfg.ExperimentErr != nil {
+ base.Fatalf("go: %v", cfg.ExperimentErr)
+ }
}
// Set environment (GOOS, GOARCH, etc) explicitly.
if s == "goexperiment" {
// test/run.go uses this to discover the full set of
// experiment tags. Report everything.
- p = " X:" + strings.Join(buildcfg.AllExperiments(), ",")
+ p = " X:" + strings.Join(buildcfg.Experiment.All(), ",")
} else {
- // If the enabled experiments differ from the defaults,
+ // If the enabled experiments differ from the baseline,
// include that difference.
- if goexperiment := buildcfg.GOEXPERIMENT(); goexperiment != "" {
+ if goexperiment := buildcfg.Experiment.String(); goexperiment != "" {
p = " X:" + goexperiment
}
}
// or link object files that are incompatible with each other. This
// string always starts with "go object ".
func HeaderString() string {
- return fmt.Sprintf("go object %s %s %s X:%s\n", buildcfg.GOOS, buildcfg.GOARCH, buildcfg.Version, strings.Join(buildcfg.EnabledExperiments(), ","))
+ return fmt.Sprintf("go object %s %s %s X:%s\n", buildcfg.GOOS, buildcfg.GOARCH, buildcfg.Version, strings.Join(buildcfg.Experiment.Enabled(), ","))
}
addstrdata1(ctxt, "internal/buildcfg.defaultGOROOT="+final)
buildVersion := buildcfg.Version
- if goexperiment := buildcfg.GOEXPERIMENT(); goexperiment != "" {
+ if goexperiment := buildcfg.Experiment.String(); goexperiment != "" {
buildVersion += " X:" + goexperiment
}
addstrdata1(ctxt, "runtime.buildVersion="+buildVersion)
// 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.
- for _, exp := range buildcfg.EnabledExperiments() {
+ for _, exp := range buildcfg.Experiment.Enabled() {
c.ToolTags = append(c.ToolTags, "goexperiment."+exp)
}
defaultToolTags = append([]string{}, c.ToolTags...) // our own private copy
"internal/goexperiment"
)
+// ExperimentFlags represents a set of GOEXPERIMENT flags relative to a baseline
+// (platform-default) experiment configuration.
+type ExperimentFlags struct {
+ goexperiment.Flags
+ baseline goexperiment.Flags
+}
+
// Experiment contains the toolchain experiments enabled for the
// current build.
//
// experimentBaseline specifies the experiment flags that are enabled by
// default in the current toolchain. This is, in effect, the "control"
// configuration and any variation from this is an experiment.
-var Experiment, experimentBaseline = func() (goexperiment.Flags, goexperiment.Flags) {
- flags, baseline, err := ParseGOEXPERIMENT(GOOS, GOARCH, envOr("GOEXPERIMENT", defaultGOEXPERIMENT))
+var Experiment ExperimentFlags = func() ExperimentFlags {
+ flags, err := ParseGOEXPERIMENT(GOOS, GOARCH, envOr("GOEXPERIMENT", defaultGOEXPERIMENT))
if err != nil {
Error = err
+ return ExperimentFlags{}
}
- return flags, baseline
+ return *flags
}()
+// DefaultGOEXPERIMENT is the embedded default GOEXPERIMENT string.
+// It is not guaranteed to be canonical.
const DefaultGOEXPERIMENT = defaultGOEXPERIMENT
// FramePointerEnabled enables the use of platform conventions for
// flag sets.
//
// TODO(mdempsky): Move to internal/goexperiment.
-func ParseGOEXPERIMENT(goos, goarch, goexp string) (flags, baseline goexperiment.Flags, err error) {
+func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) {
regabiSupported := false
switch goarch {
case "amd64", "arm64", "ppc64le", "ppc64":
regabiSupported = true
}
- baseline = goexperiment.Flags{
+ baseline := goexperiment.Flags{
RegabiWrappers: regabiSupported,
RegabiArgs: regabiSupported,
PacerRedesign: true,
}
// Start with the statically enabled set of experiments.
- flags = baseline
+ flags := &ExperimentFlags{
+ Flags: baseline,
+ baseline: baseline,
+ }
// Pick up any changes to the baseline configuration from the
// GOEXPERIMENT environment. This can be set at make.bash time
if goexp != "" {
// Create a map of known experiment names.
names := make(map[string]func(bool))
- rv := reflect.ValueOf(&flags).Elem()
+ rv := reflect.ValueOf(&flags.Flags).Elem()
rt := rv.Type()
for i := 0; i < rt.NumField(); i++ {
field := rv.Field(i)
// GOEXPERIMENT=none disables all experiment flags.
// This is used by cmd/dist, which doesn't know how
// to build with any experiment flags.
- flags = goexperiment.Flags{}
+ flags.Flags = goexperiment.Flags{}
continue
}
val := true
}
set, ok := names[f]
if !ok {
- err = fmt.Errorf("unknown GOEXPERIMENT %s", f)
- return
+ return nil, fmt.Errorf("unknown GOEXPERIMENT %s", f)
}
set(val)
}
}
// Check regabi dependencies.
if flags.RegabiArgs && !flags.RegabiWrappers {
- err = fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers")
+ return nil, fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers")
}
- return
+ return flags, nil
+}
+
+// String returns the canonical GOEXPERIMENT string to enable this experiment
+// configuration. (Experiments in the same state as in the baseline are elided.)
+func (exp *ExperimentFlags) String() string {
+ return strings.Join(expList(&exp.Flags, &exp.baseline, false), ",")
}
// expList returns the list of lower-cased experiment names for
return list
}
-// GOEXPERIMENT is a comma-separated list of enabled or disabled
-// experiments that differ from the baseline experiment configuration.
-// GOEXPERIMENT is exactly what a user would set on the command line
-// to get the set of enabled experiments.
-func GOEXPERIMENT() string {
- goexp := strings.Join(expList(&Experiment, &experimentBaseline, false), ",")
- if goexp == "" && DefaultGOEXPERIMENT != "" {
- goexp = "," // non-empty to override DefaultGOEXPERIMENT
- }
- return goexp
-}
-
-// EnabledExperiments returns a list of enabled experiments, as
+// Enabled returns a list of enabled experiments, as
// lower-cased experiment names.
-func EnabledExperiments() []string {
- return expList(&Experiment, nil, false)
+func (exp *ExperimentFlags) Enabled() []string {
+ return expList(&exp.Flags, nil, false)
}
-// AllExperiments returns a list of all experiment settings.
+// All returns a list of all experiment settings.
// Disabled experiments appear in the list prefixed by "no".
-func AllExperiments() []string {
- return expList(&Experiment, nil, true)
-}
-
-// UpdateExperiments updates the Experiment global based on a new GOARCH value.
-// This is only required for cmd/go, which can change GOARCH after
-// program startup due to use of "go env -w".
-func UpdateExperiments(goos, goarch, goexperiment string) {
- var err error
- Experiment, experimentBaseline, err = ParseGOEXPERIMENT(goos, goarch, goexperiment)
- if err != nil {
- Error = err
- }
+func (exp *ExperimentFlags) All() []string {
+ return expList(&exp.Flags, nil, true)
}