aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/internal
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2021-04-15 23:05:49 -0400
committerRuss Cox <rsc@golang.org>2021-04-16 19:20:53 +0000
commit95ed5c3800a87ddf9b0ec3958eaaa1a969306293 (patch)
treecb0c555f10ab706a5c491cbe48dd36da16658a1e /src/cmd/internal
parent2fc0ebb623e2859094ad3f41e61325df0c2163f8 (diff)
downloadgo-95ed5c3800a87ddf9b0ec3958eaaa1a969306293.tar.gz
go-95ed5c3800a87ddf9b0ec3958eaaa1a969306293.zip
internal/buildcfg: move build configuration out of cmd/internal/objabi
The go/build package needs access to this configuration, so move it into a new package available to the standard library. Change-Id: I868a94148b52350c76116451f4ad9191246adcff Reviewed-on: https://go-review.googlesource.com/c/go/+/310731 Trust: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: Austin Clements <austin@google.com> Reviewed-by: Jay Conrod <jayconrod@google.com>
Diffstat (limited to 'src/cmd/internal')
-rw-r--r--src/cmd/internal/dwarf/dwarf.go6
-rw-r--r--src/cmd/internal/goobj/objfile_test.go5
-rw-r--r--src/cmd/internal/obj/arm/asm5.go7
-rw-r--r--src/cmd/internal/obj/arm/obj5.go3
-rw-r--r--src/cmd/internal/obj/arm64/obj7.go3
-rw-r--r--src/cmd/internal/obj/sym.go5
-rw-r--r--src/cmd/internal/obj/util.go7
-rw-r--r--src/cmd/internal/obj/x86/asm6.go3
-rw-r--r--src/cmd/internal/obj/x86/obj6.go5
-rw-r--r--src/cmd/internal/objabi/exp.go151
-rw-r--r--src/cmd/internal/objabi/flag.go9
-rw-r--r--src/cmd/internal/objabi/line.go5
-rw-r--r--src/cmd/internal/objabi/stack.go4
-rw-r--r--src/cmd/internal/objabi/util.go111
14 files changed, 40 insertions, 284 deletions
diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go
index c48e1723c8..ec441c2bcb 100644
--- a/src/cmd/internal/dwarf/dwarf.go
+++ b/src/cmd/internal/dwarf/dwarf.go
@@ -9,13 +9,15 @@ package dwarf
import (
"bytes"
- "cmd/internal/objabi"
"errors"
"fmt"
+ "internal/buildcfg"
exec "internal/execabs"
"sort"
"strconv"
"strings"
+
+ "cmd/internal/objabi"
)
// InfoPrefix is the prefix for all the symbols containing DWARF info entries.
@@ -381,7 +383,7 @@ func expandPseudoForm(form uint8) uint8 {
return form
}
expandedForm := DW_FORM_udata
- if objabi.GOOS == "darwin" || objabi.GOOS == "ios" {
+ if buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" {
expandedForm = DW_FORM_data4
}
return uint8(expandedForm)
diff --git a/src/cmd/internal/goobj/objfile_test.go b/src/cmd/internal/goobj/objfile_test.go
index ad80ede0f3..ed942aa934 100644
--- a/src/cmd/internal/goobj/objfile_test.go
+++ b/src/cmd/internal/goobj/objfile_test.go
@@ -10,6 +10,7 @@ import (
"cmd/internal/bio"
"cmd/internal/objabi"
"fmt"
+ "internal/buildcfg"
"internal/testenv"
"io/ioutil"
"os"
@@ -91,8 +92,8 @@ func main() {
`
func TestIssue41621LargeNumberOfRelocations(t *testing.T) {
- if testing.Short() || (objabi.GOARCH != "amd64") {
- t.Skipf("Skipping large number of relocations test in short mode or on %s", objabi.GOARCH)
+ if testing.Short() || (buildcfg.GOARCH != "amd64") {
+ t.Skipf("Skipping large number of relocations test in short mode or on %s", buildcfg.GOARCH)
}
testenv.MustHaveGoBuild(t)
diff --git a/src/cmd/internal/obj/arm/asm5.go b/src/cmd/internal/obj/arm/asm5.go
index ebb98b4859..ccf5f9e7f8 100644
--- a/src/cmd/internal/obj/arm/asm5.go
+++ b/src/cmd/internal/obj/arm/asm5.go
@@ -34,6 +34,7 @@ import (
"cmd/internal/obj"
"cmd/internal/objabi"
"fmt"
+ "internal/buildcfg"
"log"
"math"
"sort"
@@ -976,7 +977,7 @@ func (c *ctxt5) aclass(a *obj.Addr) int {
if immrot(^uint32(c.instoffset)) != 0 {
return C_NCON
}
- if uint32(c.instoffset) <= 0xffff && objabi.GOARM == 7 {
+ if uint32(c.instoffset) <= 0xffff && buildcfg.GOARM == 7 {
return C_SCON
}
if x, y := immrot2a(uint32(c.instoffset)); x != 0 && y != 0 {
@@ -3044,7 +3045,7 @@ func (c *ctxt5) omvl(p *obj.Prog, a *obj.Addr, dr int) uint32 {
func (c *ctxt5) chipzero5(e float64) int {
// We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions.
- if objabi.GOARM < 7 || math.Float64bits(e) != 0 {
+ if buildcfg.GOARM < 7 || math.Float64bits(e) != 0 {
return -1
}
return 0
@@ -3052,7 +3053,7 @@ func (c *ctxt5) chipzero5(e float64) int {
func (c *ctxt5) chipfloat5(e float64) int {
// We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions.
- if objabi.GOARM < 7 {
+ if buildcfg.GOARM < 7 {
return -1
}
diff --git a/src/cmd/internal/obj/arm/obj5.go b/src/cmd/internal/obj/arm/obj5.go
index edb384806b..1454d8a7c9 100644
--- a/src/cmd/internal/obj/arm/obj5.go
+++ b/src/cmd/internal/obj/arm/obj5.go
@@ -34,6 +34,7 @@ import (
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/sys"
+ "internal/buildcfg"
"log"
)
@@ -64,7 +65,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line())
}
- if objabi.GOARM < 7 {
+ if buildcfg.GOARM < 7 {
// Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension.
if progedit_tlsfallback == nil {
progedit_tlsfallback = ctxt.Lookup("runtime.read_tls_fallback")
diff --git a/src/cmd/internal/obj/arm64/obj7.go b/src/cmd/internal/obj/arm64/obj7.go
index 514991e340..e41fb3bb75 100644
--- a/src/cmd/internal/obj/arm64/obj7.go
+++ b/src/cmd/internal/obj/arm64/obj7.go
@@ -35,6 +35,7 @@ import (
"cmd/internal/objabi"
"cmd/internal/src"
"cmd/internal/sys"
+ "internal/buildcfg"
"log"
"math"
)
@@ -576,7 +577,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
q1.To.Reg = REGSP
q1.Spadj = c.autosize
- if objabi.GOOS == "ios" {
+ if buildcfg.GOOS == "ios" {
// iOS does not support SA_ONSTACK. We will run the signal handler
// on the G stack. If we write below SP, it may be clobbered by
// the signal handler. So we save LR after decrementing SP.
diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go
index 98c7364e2a..9e8b4dd790 100644
--- a/src/cmd/internal/obj/sym.go
+++ b/src/cmd/internal/obj/sym.go
@@ -35,6 +35,7 @@ import (
"cmd/internal/goobj"
"cmd/internal/objabi"
"fmt"
+ "internal/buildcfg"
"log"
"math"
"sort"
@@ -49,8 +50,8 @@ func Linknew(arch *LinkArch) *Link {
ctxt.Arch = arch
ctxt.Pathname = objabi.WorkingDir()
- if err := ctxt.Headtype.Set(objabi.GOOS); err != nil {
- log.Fatalf("unknown goos %s", objabi.GOOS)
+ if err := ctxt.Headtype.Set(buildcfg.GOOS); err != nil {
+ log.Fatalf("unknown goos %s", buildcfg.GOOS)
}
ctxt.Flag_optimize = true
diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go
index 1c34b4e833..e8441a6969 100644
--- a/src/cmd/internal/obj/util.go
+++ b/src/cmd/internal/obj/util.go
@@ -8,6 +8,7 @@ import (
"bytes"
"cmd/internal/objabi"
"fmt"
+ "internal/buildcfg"
"io"
"strings"
)
@@ -83,7 +84,7 @@ func CConv(s uint8) string {
}
for i := range opSuffixSpace {
sset := &opSuffixSpace[i]
- if sset.arch == objabi.GOARCH {
+ if sset.arch == buildcfg.GOARCH {
return sset.cconv(s)
}
}
@@ -330,7 +331,7 @@ func writeDconv(w io.Writer, p *Prog, a *Addr, abiDetail bool) {
case TYPE_SHIFT:
v := int(a.Offset)
ops := "<<>>->@>"
- switch objabi.GOARCH {
+ switch buildcfg.GOARCH {
case "arm":
op := ops[((v>>5)&3)<<1:]
if v&(1<<4) != 0 {
@@ -346,7 +347,7 @@ func writeDconv(w io.Writer, p *Prog, a *Addr, abiDetail bool) {
r := (v >> 16) & 31
fmt.Fprintf(w, "%s%c%c%d", Rconv(r+RBaseARM64), op[0], op[1], (v>>10)&63)
default:
- panic("TYPE_SHIFT is not supported on " + objabi.GOARCH)
+ panic("TYPE_SHIFT is not supported on " + buildcfg.GOARCH)
}
case TYPE_REGREG:
diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go
index 52ac567a36..17fa76727e 100644
--- a/src/cmd/internal/obj/x86/asm6.go
+++ b/src/cmd/internal/obj/x86/asm6.go
@@ -36,6 +36,7 @@ import (
"cmd/internal/sys"
"encoding/binary"
"fmt"
+ "internal/buildcfg"
"log"
"strings"
)
@@ -2460,7 +2461,7 @@ func instinit(ctxt *obj.Link) {
}
}
-var isAndroid = objabi.GOOS == "android"
+var isAndroid = buildcfg.GOOS == "android"
func prefixof(ctxt *obj.Link, a *obj.Addr) int {
if a.Reg < REG_CS && a.Index < REG_CS { // fast path
diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go
index 06736d43bd..e2732d53e3 100644
--- a/src/cmd/internal/obj/x86/obj6.go
+++ b/src/cmd/internal/obj/x86/obj6.go
@@ -35,6 +35,7 @@ import (
"cmd/internal/objabi"
"cmd/internal/src"
"cmd/internal/sys"
+ "internal/buildcfg"
"log"
"math"
"path"
@@ -646,13 +647,13 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
var regg int16
if !p.From.Sym.NoSplit() || p.From.Sym.Wrapper() {
- if ctxt.Arch.Family == sys.AMD64 && objabi.Experiment.RegabiG && cursym.ABI() == obj.ABIInternal {
+ if ctxt.Arch.Family == sys.AMD64 && buildcfg.Experiment.RegabiG && cursym.ABI() == obj.ABIInternal {
regg = REGG // use the g register directly in ABIInternal
} else {
p = obj.Appendp(p, newprog)
regg = REG_CX
if ctxt.Arch.Family == sys.AMD64 {
- // Using this register means that stacksplit works w/ //go:registerparams even when !objabi.Experiment.RegabiG
+ // Using this register means that stacksplit works w/ //go:registerparams even when !buildcfg.Experiment.RegabiG
regg = REGG // == REG_R14
}
p = load_g(ctxt, p, newprog, regg) // load g into regg
diff --git a/src/cmd/internal/objabi/exp.go b/src/cmd/internal/objabi/exp.go
deleted file mode 100644
index 3371c6c8f8..0000000000
--- a/src/cmd/internal/objabi/exp.go
+++ /dev/null
@@ -1,151 +0,0 @@
-// 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 objabi
-
-import (
- "fmt"
- "os"
- "reflect"
- "strings"
-
- "internal/goexperiment"
-)
-
-// Experiment contains the toolchain experiments enabled for the
-// current build.
-//
-// (This is not necessarily the set of experiments the compiler itself
-// was built with.)
-var Experiment goexperiment.Flags = parseExperiments()
-
-// 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 experimentBaseline goexperiment.Flags
-
-// 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"
-
-func parseExperiments() goexperiment.Flags {
- // Start with the statically enabled set of experiments.
- flags := experimentBaseline
-
- // 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.
- env := envOr("GOEXPERIMENT", defaultGOEXPERIMENT)
-
- if env != "" {
- // Create a map of known experiment names.
- names := make(map[string]reflect.Value)
- rv := reflect.ValueOf(&flags).Elem()
- rt := rv.Type()
- for i := 0; i < rt.NumField(); i++ {
- field := rv.Field(i)
- names[strings.ToLower(rt.Field(i).Name)] = field
- }
-
- // Parse names.
- for _, f := range strings.Split(env, ",") {
- 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 = goexperiment.Flags{}
- continue
- }
- val := true
- if strings.HasPrefix(f, "no") {
- f, val = f[2:], false
- }
- field, ok := names[f]
- if !ok {
- fmt.Printf("unknown experiment %s\n", f)
- os.Exit(2)
- }
- field.SetBool(val)
- }
- }
-
- // regabi is only supported on amd64.
- if GOARCH != "amd64" {
- flags.Regabi = false
- flags.RegabiWrappers = false
- flags.RegabiG = false
- flags.RegabiReflect = false
- flags.RegabiDefer = false
- flags.RegabiArgs = false
- }
- // Setting regabi sets working sub-experiments.
- if flags.Regabi {
- flags.RegabiWrappers = true
- flags.RegabiG = true
- flags.RegabiReflect = true
- flags.RegabiDefer = true
- // Not ready yet:
- //flags.RegabiArgs = true
- }
- // Check regabi dependencies.
- if flags.RegabiG && !flags.RegabiWrappers {
- panic("GOEXPERIMENT regabig requires regabiwrappers")
- }
- if flags.RegabiArgs && !(flags.RegabiWrappers && flags.RegabiG && flags.RegabiReflect && flags.RegabiDefer) {
- panic("GOEXPERIMENT regabiargs requires regabiwrappers,regabig,regabireflect,regabidefer")
- }
- return flags
-}
-
-// 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
-}
-
-// 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 {
- return strings.Join(expList(&Experiment, &experimentBaseline, false), ",")
-}
-
-// EnabledExperiments returns a list of enabled experiments, as
-// lower-cased experiment names.
-func EnabledExperiments() []string {
- return expList(&Experiment, nil, false)
-}
diff --git a/src/cmd/internal/objabi/flag.go b/src/cmd/internal/objabi/flag.go
index 9fcab4cc85..e41fc570b0 100644
--- a/src/cmd/internal/objabi/flag.go
+++ b/src/cmd/internal/objabi/flag.go
@@ -8,6 +8,7 @@ import (
"bytes"
"flag"
"fmt"
+ "internal/buildcfg"
"io"
"io/ioutil"
"log"
@@ -96,11 +97,11 @@ func (versionFlag) Set(s string) error {
if s == "goexperiment" {
// test/run.go uses this to discover the full set of
// experiment tags. Report everything.
- p = " X:" + strings.Join(expList(&Experiment, nil, true), ",")
+ p = " X:" + strings.Join(buildcfg.AllExperiments(), ",")
} else {
// If the enabled experiments differ from the defaults,
// include that difference.
- if goexperiment := GOEXPERIMENT(); goexperiment != "" {
+ if goexperiment := buildcfg.GOEXPERIMENT(); goexperiment != "" {
p = " X:" + goexperiment
}
}
@@ -111,12 +112,12 @@ func (versionFlag) Set(s string) error {
// build ID of the binary, so that if the compiler is changed and
// rebuilt, we notice and rebuild all packages.
if s == "full" {
- if strings.HasPrefix(Version, "devel") {
+ if strings.HasPrefix(buildcfg.Version, "devel") {
p += " buildID=" + buildID
}
}
- fmt.Printf("%s version %s%s\n", name, Version, p)
+ fmt.Printf("%s version %s%s\n", name, buildcfg.Version, p)
os.Exit(0)
return nil
}
diff --git a/src/cmd/internal/objabi/line.go b/src/cmd/internal/objabi/line.go
index 0733b65138..0b1e0bb181 100644
--- a/src/cmd/internal/objabi/line.go
+++ b/src/cmd/internal/objabi/line.go
@@ -5,6 +5,7 @@
package objabi
import (
+ "internal/buildcfg"
"os"
"path/filepath"
"strings"
@@ -38,8 +39,8 @@ func AbsFile(dir, file, rewrites string) string {
}
abs, rewritten := ApplyRewrites(abs, rewrites)
- if !rewritten && hasPathPrefix(abs, GOROOT) {
- abs = "$GOROOT" + abs[len(GOROOT):]
+ if !rewritten && hasPathPrefix(abs, buildcfg.GOROOT) {
+ abs = "$GOROOT" + abs[len(buildcfg.GOROOT):]
}
if abs == "" {
diff --git a/src/cmd/internal/objabi/stack.go b/src/cmd/internal/objabi/stack.go
index 1f531176cc..0c82a7c6dd 100644
--- a/src/cmd/internal/objabi/stack.go
+++ b/src/cmd/internal/objabi/stack.go
@@ -4,6 +4,8 @@
package objabi
+import "internal/buildcfg"
+
// For the linkers. Must match Go definitions.
const (
@@ -22,7 +24,7 @@ var StackLimit = StackGuard - StackSystem - StackSmall
// builds that have larger stack frames or for specific targets.
func stackGuardMultiplier() int {
// On AIX, a larger stack is needed for syscalls.
- if GOOS == "aix" {
+ if buildcfg.GOOS == "aix" {
return 2
}
return stackGuardMultiplierDefault
diff --git a/src/cmd/internal/objabi/util.go b/src/cmd/internal/objabi/util.go
index 5a7a74cfde..63640950d9 100644
--- a/src/cmd/internal/objabi/util.go
+++ b/src/cmd/internal/objabi/util.go
@@ -6,32 +6,9 @@ package objabi
import (
"fmt"
- "log"
- "os"
"strings"
-)
-
-func envOr(key, value string) string {
- if x := os.Getenv(key); x != "" {
- return x
- }
- return value
-}
-var (
- defaultGOROOT string // set by linker
-
- GOROOT = envOr("GOROOT", defaultGOROOT)
- GOARCH = envOr("GOARCH", defaultGOARCH)
- GOOS = envOr("GOOS", defaultGOOS)
- GO386 = envOr("GO386", defaultGO386)
- GOARM = goarm()
- GOMIPS = gomips()
- GOMIPS64 = gomips64()
- GOPPC64 = goppc64()
- GOWASM = gowasm()
- GO_LDSO = defaultGO_LDSO
- Version = version
+ "internal/buildcfg"
)
const (
@@ -39,94 +16,10 @@ const (
MachoRelocOffset = 2048 // reserve enough space for ELF relocations
)
-func goarm() int {
- def := defaultGOARM
- if GOOS == "android" && GOARCH == "arm" {
- // Android arm devices always support GOARM=7.
- def = "7"
- }
- switch v := envOr("GOARM", def); v {
- case "5":
- return 5
- case "6":
- return 6
- case "7":
- return 7
- }
- // Fail here, rather than validate at multiple call sites.
- log.Fatalf("Invalid GOARM value. Must be 5, 6, or 7.")
- panic("unreachable")
-}
-
-func gomips() string {
- switch v := envOr("GOMIPS", defaultGOMIPS); v {
- case "hardfloat", "softfloat":
- return v
- }
- log.Fatalf("Invalid GOMIPS value. Must be hardfloat or softfloat.")
- panic("unreachable")
-}
-
-func gomips64() string {
- switch v := envOr("GOMIPS64", defaultGOMIPS64); v {
- case "hardfloat", "softfloat":
- return v
- }
- log.Fatalf("Invalid GOMIPS64 value. Must be hardfloat or softfloat.")
- panic("unreachable")
-}
-
-func goppc64() int {
- switch v := envOr("GOPPC64", defaultGOPPC64); v {
- case "power8":
- return 8
- case "power9":
- return 9
- }
- log.Fatalf("Invalid GOPPC64 value. Must be power8 or power9.")
- panic("unreachable")
-}
-
-type gowasmFeatures struct {
- SignExt bool
- SatConv 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:
- log.Fatalf("Invalid GOWASM value. No such feature: " + opt)
- }
- }
- return
-}
-
-func Getgoextlinkenabled() string {
- return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED)
-}
-
// HeaderString returns the toolchain configuration string written in
// Go object headers. This string ensures we don't attempt to import
// 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", GOOS, GOARCH, Version, strings.Join(EnabledExperiments(), ","))
+ return fmt.Sprintf("go object %s %s %s X:%s\n", buildcfg.GOOS, buildcfg.GOARCH, buildcfg.Version, strings.Join(buildcfg.EnabledExperiments(), ","))
}