summaryrefslogtreecommitdiffstats
path: root/src/internal/buildcfg
diff options
context:
space:
mode:
Diffstat (limited to 'src/internal/buildcfg')
-rw-r--r--src/internal/buildcfg/cfg.go274
-rw-r--r--src/internal/buildcfg/cfg_test.go26
-rw-r--r--src/internal/buildcfg/exp.go194
3 files changed, 494 insertions, 0 deletions
diff --git a/src/internal/buildcfg/cfg.go b/src/internal/buildcfg/cfg.go
new file mode 100644
index 0000000..8b97a65
--- /dev/null
+++ b/src/internal/buildcfg/cfg.go
@@ -0,0 +1,274 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package buildcfg provides access to the build configuration
+// described by the current environment. It is for use by build tools
+// such as cmd/go or cmd/compile and for setting up go/build's Default context.
+//
+// Note that it does NOT provide access to the build configuration used to
+// build the currently-running binary. For that, use runtime.GOOS etc
+// as well as internal/goexperiment.
+package buildcfg
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+)
+
+var (
+ GOROOT = runtime.GOROOT() // cached for efficiency
+ GOARCH = envOr("GOARCH", defaultGOARCH)
+ GOOS = envOr("GOOS", defaultGOOS)
+ GO386 = envOr("GO386", defaultGO386)
+ GOAMD64 = goamd64()
+ GOARM = goarm()
+ GOMIPS = gomips()
+ GOMIPS64 = gomips64()
+ GOPPC64 = goppc64()
+ GOWASM = gowasm()
+ ToolTags = toolTags()
+ GO_LDSO = defaultGO_LDSO
+ Version = version
+)
+
+// Error is one of the errors found (if any) in the build configuration.
+var Error error
+
+// Check exits the program with a fatal error if Error is non-nil.
+func Check() {
+ if Error != nil {
+ fmt.Fprintf(os.Stderr, "%s: %v\n", filepath.Base(os.Args[0]), Error)
+ os.Exit(2)
+ }
+}
+
+func envOr(key, value string) string {
+ if x := os.Getenv(key); x != "" {
+ return x
+ }
+ return value
+}
+
+func goamd64() int {
+ switch v := envOr("GOAMD64", defaultGOAMD64); v {
+ case "v1":
+ return 1
+ case "v2":
+ return 2
+ case "v3":
+ return 3
+ case "v4":
+ return 4
+ }
+ Error = fmt.Errorf("invalid GOAMD64: must be v1, v2, v3, v4")
+ return int(defaultGOAMD64[len("v")] - '0')
+}
+
+type goarmFeatures struct {
+ Version int
+ SoftFloat bool
+}
+
+func (g goarmFeatures) String() string {
+ armStr := strconv.Itoa(g.Version)
+ if g.SoftFloat {
+ armStr += ",softfloat"
+ } else {
+ armStr += ",hardfloat"
+ }
+ return armStr
+}
+
+func goarm() (g goarmFeatures) {
+ const (
+ softFloatOpt = ",softfloat"
+ hardFloatOpt = ",hardfloat"
+ )
+ def := defaultGOARM
+ if GOOS == "android" && GOARCH == "arm" {
+ // Android arm devices always support GOARM=7.
+ def = "7"
+ }
+ v := envOr("GOARM", def)
+
+ floatSpecified := false
+ if strings.HasSuffix(v, softFloatOpt) {
+ g.SoftFloat = true
+ floatSpecified = true
+ v = v[:len(v)-len(softFloatOpt)]
+ }
+ if strings.HasSuffix(v, hardFloatOpt) {
+ floatSpecified = true
+ v = v[:len(v)-len(hardFloatOpt)]
+ }
+
+ switch v {
+ case "5":
+ g.Version = 5
+ case "6":
+ g.Version = 6
+ case "7":
+ g.Version = 7
+ default:
+ Error = fmt.Errorf("invalid GOARM: must start with 5, 6, or 7, and may optionally end in either %q or %q", hardFloatOpt, softFloatOpt)
+ g.Version = int(def[0] - '0')
+ }
+
+ // 5 defaults to softfloat. 6 and 7 default to hardfloat.
+ if !floatSpecified && g.Version == 5 {
+ g.SoftFloat = true
+ }
+ return
+}
+
+func gomips() string {
+ switch v := envOr("GOMIPS", defaultGOMIPS); v {
+ case "hardfloat", "softfloat":
+ return v
+ }
+ Error = fmt.Errorf("invalid GOMIPS: must be hardfloat, softfloat")
+ return defaultGOMIPS
+}
+
+func gomips64() string {
+ switch v := envOr("GOMIPS64", defaultGOMIPS64); v {
+ case "hardfloat", "softfloat":
+ return v
+ }
+ Error = fmt.Errorf("invalid GOMIPS64: must be hardfloat, softfloat")
+ return defaultGOMIPS64
+}
+
+func goppc64() int {
+ switch v := envOr("GOPPC64", defaultGOPPC64); v {
+ case "power8":
+ return 8
+ case "power9":
+ return 9
+ case "power10":
+ return 10
+ }
+ Error = fmt.Errorf("invalid GOPPC64: must be power8, power9, power10")
+ return int(defaultGOPPC64[len("power")] - '0')
+}
+
+type gowasmFeatures struct {
+ SatConv bool
+ SignExt bool
+}
+
+func (f gowasmFeatures) String() string {
+ var flags []string
+ if f.SatConv {
+ flags = append(flags, "satconv")
+ }
+ if f.SignExt {
+ flags = append(flags, "signext")
+ }
+ return strings.Join(flags, ",")
+}
+
+func gowasm() (f gowasmFeatures) {
+ for _, opt := range strings.Split(envOr("GOWASM", ""), ",") {
+ switch opt {
+ case "satconv":
+ f.SatConv = true
+ case "signext":
+ f.SignExt = true
+ case "":
+ // ignore
+ default:
+ Error = fmt.Errorf("invalid GOWASM: no such feature %q", opt)
+ }
+ }
+ return
+}
+
+func Getgoextlinkenabled() string {
+ return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED)
+}
+
+func toolTags() []string {
+ tags := experimentTags()
+ tags = append(tags, gogoarchTags()...)
+ return tags
+}
+
+func experimentTags() []string {
+ var list []string
+ // 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.
+ for _, exp := range Experiment.Enabled() {
+ list = append(list, "goexperiment."+exp)
+ }
+ return list
+}
+
+// GOGOARCH returns the name and value of the GO$GOARCH setting.
+// For example, if GOARCH is "amd64" it might return "GOAMD64", "v2".
+func GOGOARCH() (name, value string) {
+ switch GOARCH {
+ case "386":
+ return "GO386", GO386
+ case "amd64":
+ return "GOAMD64", fmt.Sprintf("v%d", GOAMD64)
+ case "arm":
+ return "GOARM", GOARM.String()
+ case "mips", "mipsle":
+ return "GOMIPS", GOMIPS
+ case "mips64", "mips64le":
+ return "GOMIPS64", GOMIPS64
+ case "ppc64", "ppc64le":
+ return "GOPPC64", fmt.Sprintf("power%d", GOPPC64)
+ case "wasm":
+ return "GOWASM", GOWASM.String()
+ }
+ return "", ""
+}
+
+func gogoarchTags() []string {
+ switch GOARCH {
+ case "386":
+ return []string{GOARCH + "." + GO386}
+ case "amd64":
+ var list []string
+ for i := 1; i <= GOAMD64; i++ {
+ list = append(list, fmt.Sprintf("%s.v%d", GOARCH, i))
+ }
+ return list
+ case "arm":
+ var list []string
+ for i := 5; i <= GOARM.Version; i++ {
+ list = append(list, fmt.Sprintf("%s.%d", GOARCH, i))
+ }
+ return list
+ case "mips", "mipsle":
+ return []string{GOARCH + "." + GOMIPS}
+ case "mips64", "mips64le":
+ return []string{GOARCH + "." + GOMIPS64}
+ case "ppc64", "ppc64le":
+ var list []string
+ for i := 8; i <= GOPPC64; i++ {
+ list = append(list, fmt.Sprintf("%s.power%d", GOARCH, i))
+ }
+ return list
+ case "wasm":
+ var list []string
+ if GOWASM.SatConv {
+ list = append(list, GOARCH+".satconv")
+ }
+ if GOWASM.SignExt {
+ list = append(list, GOARCH+".signext")
+ }
+ return list
+ }
+ return nil
+}
diff --git a/src/internal/buildcfg/cfg_test.go b/src/internal/buildcfg/cfg_test.go
new file mode 100644
index 0000000..0123593
--- /dev/null
+++ b/src/internal/buildcfg/cfg_test.go
@@ -0,0 +1,26 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buildcfg
+
+import (
+ "os"
+ "testing"
+)
+
+func TestConfigFlags(t *testing.T) {
+ os.Setenv("GOAMD64", "v1")
+ if goamd64() != 1 {
+ t.Errorf("Wrong parsing of GOAMD64=v1")
+ }
+ os.Setenv("GOAMD64", "v4")
+ if goamd64() != 4 {
+ t.Errorf("Wrong parsing of GOAMD64=v4")
+ }
+ Error = nil
+ os.Setenv("GOAMD64", "1")
+ if goamd64(); Error == nil {
+ t.Errorf("Wrong parsing of GOAMD64=1")
+ }
+}
diff --git a/src/internal/buildcfg/exp.go b/src/internal/buildcfg/exp.go
new file mode 100644
index 0000000..a45cfaf
--- /dev/null
+++ b/src/internal/buildcfg/exp.go
@@ -0,0 +1,194 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buildcfg
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+
+ "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.
+//
+// (This is not necessarily the set of experiments the compiler itself
+// was built with.)
+//
+// 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 ExperimentFlags = func() ExperimentFlags {
+ flags, err := ParseGOEXPERIMENT(GOOS, GOARCH, envOr("GOEXPERIMENT", defaultGOEXPERIMENT))
+ if err != nil {
+ Error = err
+ return ExperimentFlags{}
+ }
+ 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
+// saving frame pointers.
+//
+// This used to be an experiment, but now it's always enabled on
+// platforms that support it.
+//
+// Note: must agree with runtime.framepointer_enabled.
+var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"
+
+// ParseGOEXPERIMENT parses a (GOOS, GOARCH, GOEXPERIMENT)
+// configuration tuple and returns the enabled and baseline experiment
+// flag sets.
+//
+// TODO(mdempsky): Move to internal/goexperiment.
+func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) {
+ // regabiSupported is set to true on platforms where register ABI is
+ // supported and enabled by default.
+ // regabiAlwaysOn is set to true on platforms where register ABI is
+ // always on.
+ var regabiSupported, regabiAlwaysOn bool
+ switch goarch {
+ case "amd64", "arm64", "ppc64le", "ppc64", "riscv64":
+ regabiAlwaysOn = true
+ regabiSupported = true
+ case "loong64":
+ regabiSupported = true
+ }
+
+ baseline := goexperiment.Flags{
+ RegabiWrappers: regabiSupported,
+ RegabiArgs: regabiSupported,
+ CoverageRedesign: true,
+ AllocHeaders: true,
+ ExecTracer2: true,
+ }
+
+ // Start with the statically enabled set of experiments.
+ 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
+ // and overridden at build time.
+ if goexp != "" {
+ // Create a map of known experiment names.
+ names := make(map[string]func(bool))
+ rv := reflect.ValueOf(&flags.Flags).Elem()
+ rt := rv.Type()
+ for i := 0; i < rt.NumField(); i++ {
+ field := rv.Field(i)
+ names[strings.ToLower(rt.Field(i).Name)] = field.SetBool
+ }
+
+ // "regabi" is an alias for all working regabi
+ // subexperiments, and not an experiment itself. Doing
+ // this as an alias make both "regabi" and "noregabi"
+ // do the right thing.
+ names["regabi"] = func(v bool) {
+ flags.RegabiWrappers = v
+ flags.RegabiArgs = v
+ }
+
+ // Parse names.
+ for _, f := range strings.Split(goexp, ",") {
+ if f == "" {
+ continue
+ }
+ if f == "none" {
+ // GOEXPERIMENT=none disables all experiment flags.
+ // This is used by cmd/dist, which doesn't know how
+ // to build with any experiment flags.
+ flags.Flags = goexperiment.Flags{}
+ continue
+ }
+ val := true
+ if strings.HasPrefix(f, "no") {
+ f, val = f[2:], false
+ }
+ set, ok := names[f]
+ if !ok {
+ return nil, fmt.Errorf("unknown GOEXPERIMENT %s", f)
+ }
+ set(val)
+ }
+ }
+
+ if regabiAlwaysOn {
+ flags.RegabiWrappers = true
+ flags.RegabiArgs = true
+ }
+ // regabi is only supported on amd64, arm64, loong64, riscv64, ppc64 and ppc64le.
+ if !regabiSupported {
+ flags.RegabiWrappers = false
+ flags.RegabiArgs = false
+ }
+ // Check regabi dependencies.
+ if flags.RegabiArgs && !flags.RegabiWrappers {
+ return nil, fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers")
+ }
+ 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
+// experiments that differ from base. base may be nil to indicate no
+// experiments. If all is true, then include all experiment flags,
+// regardless of base.
+func expList(exp, base *goexperiment.Flags, all bool) []string {
+ var list []string
+ rv := reflect.ValueOf(exp).Elem()
+ var rBase reflect.Value
+ if base != nil {
+ rBase = reflect.ValueOf(base).Elem()
+ }
+ rt := rv.Type()
+ for i := 0; i < rt.NumField(); i++ {
+ name := strings.ToLower(rt.Field(i).Name)
+ val := rv.Field(i).Bool()
+ baseVal := false
+ if base != nil {
+ baseVal = rBase.Field(i).Bool()
+ }
+ if all || val != baseVal {
+ if val {
+ list = append(list, name)
+ } else {
+ list = append(list, "no"+name)
+ }
+ }
+ }
+ return list
+}
+
+// Enabled returns a list of enabled experiments, as
+// lower-cased experiment names.
+func (exp *ExperimentFlags) Enabled() []string {
+ return expList(&exp.Flags, nil, false)
+}
+
+// All returns a list of all experiment settings.
+// Disabled experiments appear in the list prefixed by "no".
+func (exp *ExperimentFlags) All() []string {
+ return expList(&exp.Flags, nil, true)
+}