From f0e97546962736fe4aa73b7c7ed590f0134515e1 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 8 Apr 2019 11:23:42 -0400 Subject: [PATCH] cmd/go: add env -w and env -u to set and unset default env vars Setting environment variables for go command configuration is too difficult and system-specific. This CL adds go env -w, to change the default settings more easily, in a portable way. It also adds go env -u, to unset those changes. See https://golang.org/design/30411-env for details. Fixes #30411. Change-Id: I36e83f55b666459f8f7f482432a4a6ee015da71d Reviewed-on: https://go-review.googlesource.com/c/go/+/171137 Run-TryBot: Russ Cox TryBot-Result: Gobot Gobot Reviewed-by: Bryan C. Mills --- src/cmd/go/alldocs.go | 57 +++-- src/cmd/go/go_test.go | 13 +- src/cmd/go/internal/base/goflags.go | 3 +- src/cmd/go/internal/cache/default.go | 3 +- src/cmd/go/internal/cfg/cfg.go | 254 ++++++++++++++++++++--- src/cmd/go/internal/envcmd/env.go | 240 +++++++++++++++++++-- src/cmd/go/internal/help/helpdoc.go | 47 +++-- src/cmd/go/internal/modfetch/notary.go | 4 +- src/cmd/go/internal/modfetch/proxy.go | 4 +- src/cmd/go/internal/modload/init.go | 27 +-- src/cmd/go/internal/work/action.go | 2 +- src/cmd/go/internal/work/exec.go | 35 ++-- src/cmd/go/internal/work/gccgo.go | 6 +- src/cmd/go/internal/work/security.go | 9 +- src/cmd/go/main.go | 10 +- src/cmd/go/testdata/script/env_write.txt | 87 ++++++++ src/cmd/internal/objabi/util.go | 2 +- src/make.bash | 1 + src/make.bat | 3 +- src/make.rc | 1 + src/runtime/extern.go | 3 + 21 files changed, 671 insertions(+), 140 deletions(-) create mode 100644 src/cmd/go/testdata/script/env_write.txt diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 2cc00f29b1..d012235b81 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -368,7 +368,7 @@ // // Usage: // -// go env [-json] [var ...] +// go env [-json] [-u] [-w] [var ...] // // Env prints Go environment information. // @@ -380,6 +380,14 @@ // The -json flag prints the environment in JSON format // instead of as a shell script. // +// The -u flag requires one or more arguments and unsets +// the default setting for the named environment variables, +// if one has been set with 'go env -w'. +// +// The -w flag requires one or more arguments of the +// form NAME=VALUE and changes the default settings +// of the named environment variables to the given values. +// // For more about environment variables, see 'go help environment'. // // @@ -1485,10 +1493,17 @@ // // Environment variables // -// The go command, and the tools it invokes, examine a few different -// environment variables. For many of these, you can see the default -// value of on your system by running 'go env NAME', where NAME is the -// name of the variable. +// The go command and the tools it invokes consult environment variables +// for configuration. If an environment variable is unset, the go command +// uses a sensible default setting. To see the effective setting of the +// variable , run 'go env '. To change the default setting, +// run 'go env -w ='. Defaults changed using 'go env -w' +// are recorded in a Go environment configuration file stored in the +// per-user configuration directory, as reported by os.UserConfigDir. +// The location of the configuration file can be changed by setting +// the environment variable GOENV, and 'go env GOENV' prints the +// effective location, but 'go env -w' cannot change the default location. +// See 'go help env' for details. // // General-purpose environment variables: // @@ -1502,10 +1517,15 @@ // GOCACHE // The directory where the go command will store cached // information for reuse in future builds. +// GOENV +// The location of the Go environment configuration file. +// Cannot be set using 'go env -w'. // GOFLAGS // A space-separated list of -flag=value settings to apply // to go commands by default, when the given flag is known by -// the current command. Flags listed on the command line +// the current command. Each entry must be a standalone flag. +// Because the entries are space-separated, flag values must +// not contain spaces. Flags listed on the command line // are applied after this list and therefore override it. // GOOS // The operating system for which to compile code. @@ -1514,21 +1534,18 @@ // For more details see: 'go help gopath'. // GOPROXY // URL of Go module proxy. See 'go help goproxy'. -// GORACE -// Options for the race detector. -// See https://golang.org/doc/articles/race_detector.html. // GOROOT // The root of the go tree. // GOTMPDIR // The directory where the go command will write // temporary source files, packages, and binaries. // -// Each entry in the GOFLAGS list must be a standalone flag. -// Because the entries are space-separated, flag values must -// not contain spaces. -// // Environment variables for use with cgo: // +// AR +// The command to use to manipulate library archives when +// building with the gccgo compiler. +// The default is 'ar'. // CC // The command to use to compile C code. // CGO_ENABLED @@ -1558,12 +1575,10 @@ // but for the linker. // CXX // The command to use to compile C++ code. +// FC +// The command to use to compile Fortran code. // PKG_CONFIG // Path to pkg-config tool. -// AR -// The command to use to manipulate library archives when -// building with the gccgo compiler. -// The default is 'ar'. // // Architecture-specific environment variables: // @@ -1598,9 +1613,11 @@ // when using -linkmode=auto with code that uses cgo. // Set to 0 to disable external linking mode, 1 to enable it. // GIT_ALLOW_PROTOCOL -// Defined by Git. A colon-separated list of schemes that are allowed to be used -// with git fetch/clone. If set, any scheme not explicitly mentioned will be -// considered insecure by 'go get'. +// Defined by Git. A colon-separated list of schemes that are allowed +// to be used with git fetch/clone. If set, any scheme not explicitly +// mentioned will be considered insecure by 'go get'. +// Because the variable is defined by Git, the default value cannot +// be set using 'go env -w'. // // Additional information available from 'go env' but not read from the environment: // diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index 473f62ca5b..5ec02d8e49 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -6,8 +6,6 @@ package main_test import ( "bytes" - "cmd/go/internal/cache" - "cmd/internal/sys" "context" "debug/elf" "debug/macho" @@ -28,6 +26,10 @@ import ( "strings" "testing" "time" + + "cmd/go/internal/cache" + "cmd/go/internal/cfg" + "cmd/internal/sys" ) var ( @@ -119,6 +121,8 @@ var testCtx = context.Background() // The TestMain function creates a go command for testing purposes and // deletes it after the tests have been run. func TestMain(m *testing.M) { + // $GO_GCFLAGS a compiler debug flag known to cmd/dist, make.bash, etc. + // It is not a standard go command flag; use os.Getenv, not cfg.Getenv. if os.Getenv("GO_GCFLAGS") != "" { fmt.Fprintf(os.Stderr, "testing: warning: no tests to run\n") // magic string for cmd/go fmt.Printf("cmd/go test is not compatible with $GO_GCFLAGS being set\n") @@ -256,6 +260,7 @@ func TestMain(m *testing.M) { } } // Don't let these environment variables confuse the test. + os.Setenv("GOENV", "off") os.Unsetenv("GOBIN") os.Unsetenv("GOPATH") os.Unsetenv("GIT_ALLOW_PROTOCOL") @@ -264,7 +269,7 @@ func TestMain(m *testing.M) { // Setting HOME to a non-existent directory will break // those systems. Disable ccache and use real compiler. Issue 17668. os.Setenv("CCACHE_DISABLE", "1") - if os.Getenv("GOCACHE") == "" { + if cfg.Getenv("GOCACHE") == "" { os.Setenv("GOCACHE", testGOCACHE) // because $HOME is gone } @@ -5258,7 +5263,7 @@ func TestCacheVet(t *testing.T) { if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { t.Skip("GODEBUG gocacheverify") } - if os.Getenv("GOCACHE") == "off" { + if cfg.Getenv("GOCACHE") == "off" { tooSlow(t) tg.makeTempdir() tg.setenv("GOCACHE", tg.path("cache")) diff --git a/src/cmd/go/internal/base/goflags.go b/src/cmd/go/internal/base/goflags.go index 2f50b50bfc..187c2a1472 100644 --- a/src/cmd/go/internal/base/goflags.go +++ b/src/cmd/go/internal/base/goflags.go @@ -7,7 +7,6 @@ package base import ( "flag" "fmt" - "os" "runtime" "strings" @@ -62,7 +61,7 @@ func InitGOFLAGS() { // (Both will show the GOFLAGS setting if let succeed.) hideErrors := cfg.CmdName == "env" || cfg.CmdName == "bug" - goflags = strings.Fields(os.Getenv("GOFLAGS")) + goflags = strings.Fields(cfg.Getenv("GOFLAGS")) if goflags == nil { goflags = []string{} // avoid work on later InitGOFLAGS call } diff --git a/src/cmd/go/internal/cache/default.go b/src/cmd/go/internal/cache/default.go index 7d389c3c1a..9f8dd8af4b 100644 --- a/src/cmd/go/internal/cache/default.go +++ b/src/cmd/go/internal/cache/default.go @@ -12,6 +12,7 @@ import ( "sync" "cmd/go/internal/base" + "cmd/go/internal/cfg" ) // Default returns the default cache to use, or nil if no cache should be used. @@ -73,7 +74,7 @@ func DefaultDir() string { // otherwise distinguish between an explicit "off" and a UserCacheDir error. defaultDirOnce.Do(func() { - defaultDir = os.Getenv("GOCACHE") + defaultDir = cfg.Getenv("GOCACHE") if filepath.IsAbs(defaultDir) || defaultDir == "off" { return } diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go index 35f7f1a173..1060c8f6df 100644 --- a/src/cmd/go/internal/cfg/cfg.go +++ b/src/cmd/go/internal/cfg/cfg.go @@ -7,11 +7,15 @@ package cfg import ( + "bytes" "fmt" "go/build" + "io/ioutil" "os" "path/filepath" "runtime" + "strings" + "sync" "cmd/internal/objabi" ) @@ -46,6 +50,50 @@ var ( func defaultContext() build.Context { ctxt := build.Default ctxt.JoinPath = filepath.Join // back door to say "do not use go command" + + ctxt.GOROOT = findGOROOT() + if runtime.Compiler != "gccgo" { + // Note that we must use runtime.GOOS and runtime.GOARCH here, + // as the tool directory does not move based on environment + // variables. This matches the initialization of ToolDir in + // go/build, except for using ctxt.GOROOT rather than + // runtime.GOROOT. + build.ToolDir = filepath.Join(ctxt.GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH) + } + + ctxt.GOPATH = envOr("GOPATH", ctxt.GOPATH) + + // 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) + + // The go/build rule for whether cgo is enabled is: + // 1. If $CGO_ENABLED is set, respect it. + // 2. Otherwise, if this is a cross-compile, disable cgo. + // 3. Otherwise, use built-in default for GOOS/GOARCH. + // Recreate that logic here with the new GOOS/GOARCH setting. + if v := Getenv("CGO_ENABLED"); v == "0" || v == "1" { + ctxt.CgoEnabled = v[0] == '1' + } else if ctxt.GOOS != runtime.GOOS || ctxt.GOARCH != runtime.GOARCH { + ctxt.CgoEnabled = false + } else { + // Use built-in default cgo setting for GOOS/GOARCH. + // Note that ctxt.GOOS/GOARCH are derived from the preference list + // (1) environment, (2) go/env file, (3) runtime constants, + // while go/build.Default.GOOS/GOARCH are derived from the preference list + // (1) environment, (2) runtime constants. + // We know ctxt.GOOS/GOARCH == runtime.GOOS/GOARCH; + // no matter how that happened, go/build.Default will make the + // same decision (either the environment variables are set explicitly + // to match the runtime constants, or else they are unset, in which + // case go/build falls back to the runtime constants), so + // go/build.Default.GOOS/GOARCH == runtime.GOOS/GOARCH. + // So ctxt.CgoEnabled (== go/build.Default.CgoEnabled) is correct + // as is and can be left unmodified. + // Nothing to do here. + } + return ctxt } @@ -70,9 +118,10 @@ var CmdEnv []EnvVar // Global build parameters (used during package load) var ( - Goarch = BuildContext.GOARCH - Goos = BuildContext.GOOS - ExeSuffix string + 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). @@ -85,40 +134,195 @@ var ( GoModInGOPATH string ) -func init() { +func exeSuffix() string { if Goos == "windows" { - ExeSuffix = ".exe" + return ".exe" + } + return "" +} + +var envCache struct { + once sync.Once + m map[string]string +} + +// EnvFile returns the name of the Go environment configuration file. +func EnvFile() (string, error) { + if file := os.Getenv("GOENV"); file != "" { + if file == "off" { + return "", fmt.Errorf("GOENV=off") + } + return file, nil + } + dir, err := os.UserConfigDir() + if err != nil { + return "", err + } + if dir == "" { + return "", fmt.Errorf("missing user-config dir") + } + return filepath.Join(dir, "go/env"), nil +} + +func initEnvCache() { + envCache.m = make(map[string]string) + file, _ := EnvFile() + if file == "" { + return + } + data, err := ioutil.ReadFile(file) + if err != nil { + return + } + + for len(data) > 0 { + // Get next line. + line := data + i := bytes.IndexByte(data, '\n') + if i >= 0 { + line, data = line[:i], data[i+1:] + } else { + data = nil + } + + i = bytes.IndexByte(line, '=') + if i < 0 || line[0] < 'A' || 'Z' < line[0] { + // Line is missing = (or empty) or a comment or not a valid env name. Ignore. + // (This should not happen, since the file should be maintained almost + // exclusively by "go env -w", but better to silently ignore than to make + // the go command unusable just because somehow the env file has + // gotten corrupted.) + continue + } + key, val := line[:i], line[i+1:] + envCache.m[string(key)] = string(val) } } +// Getenv gets the value for the configuration key. +// It consults the operating system environment +// and then the go/env file. +// If Getenv is called for a key that cannot be set +// in the go/env file (for example GODEBUG), it panics. +// This ensures that CanGetenv is accurate, so that +// 'go env -w' stays in sync with what Getenv can retrieve. +func Getenv(key string) string { + if !CanGetenv(key) { + switch key { + case "CGO_TEST_ALLOW", "CGO_TEST_DISALLOW", "CGO_test_ALLOW", "CGO_test_DISALLOW": + // used by internal/work/security_test.go; allow + default: + panic("internal error: invalid Getenv " + key) + } + } + val := os.Getenv(key) + if val != "" { + return val + } + envCache.once.Do(initEnvCache) + return envCache.m[key] +} + +// CanGetenv reports whether key is a valid go/env configuration key. +func CanGetenv(key string) bool { + return strings.Contains(knownEnv, "\t"+key+"\n") +} + +var knownEnv = ` + AR + CC + CGO_CFLAGS + CGO_CFLAGS_ALLOW + CGO_CFLAGS_DISALLOW + CGO_CPPFLAGS + CGO_CPPFLAGS_ALLOW + CGO_CPPFLAGS_DISALLOW + CGO_CXXFLAGS + CGO_CXXFLAGS_ALLOW + CGO_CXXFLAGS_DISALLOW + CGO_ENABLED + CGO_FFLAGS + CGO_FFLAGS_ALLOW + CGO_FFLAGS_DISALLOW + CGO_LDFLAGS + CGO_LDFLAGS_ALLOW + CGO_LDFLAGS_DISALLOW + CXX + FC + GCCGO + GO111MODULE + GO386 + GOARCH + GOARM + GOBIN + GOCACHE + GOENV + GOEXE + GOFLAGS + GOGCCFLAGS + GOHOSTARCH + GOHOSTOS + GOMIPS + GOMIPS64 + GONOVERIFY + GOOS + GOPATH + GOPPC64 + GOPROXY + GOROOT + GOTMPDIR + GOTOOLDIR + GOWASM + GO_EXTLINK_ENABLED + PKG_CONFIG +` + var ( - GOROOT = findGOROOT() - GOBIN = os.Getenv("GOBIN") + GOROOT = BuildContext.GOROOT + GOBIN = Getenv("GOBIN") GOROOTbin = filepath.Join(GOROOT, "bin") GOROOTpkg = filepath.Join(GOROOT, "pkg") GOROOTsrc = filepath.Join(GOROOT, "src") GOROOT_FINAL = findGOROOT_FINAL() // Used in envcmd.MkEnv and build ID computations. - GOARM = fmt.Sprint(objabi.GOARM) - GO386 = objabi.GO386 - GOMIPS = objabi.GOMIPS - GOMIPS64 = objabi.GOMIPS64 - GOPPC64 = fmt.Sprintf("%s%d", "power", objabi.GOPPC64) - GOWASM = objabi.GOWASM + GOARM = envOr("GOARM", fmt.Sprint(objabi.GOARM)) + GO386 = envOr("GO386", objabi.GO386) + GOMIPS = envOr("GOMIPS", objabi.GOMIPS) + GOMIPS64 = envOr("GOMIPS64", objabi.GOMIPS64) + GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64)) + GOWASM = envOr("GOWASM", fmt.Sprint(objabi.GOWASM)) ) -// Update build context to use our computed GOROOT. -func init() { - BuildContext.GOROOT = GOROOT - if runtime.Compiler != "gccgo" { - // Note that we must use runtime.GOOS and runtime.GOARCH here, - // as the tool directory does not move based on environment - // variables. This matches the initialization of ToolDir in - // go/build, except for using GOROOT rather than - // runtime.GOROOT. - build.ToolDir = filepath.Join(GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH) +// GetArchEnv returns the name and setting of the +// GOARCH-specific architecture environment variable. +// If the current architecture has no GOARCH-specific variable, +// GetArchEnv returns empty key and value. +func GetArchEnv() (key, val string) { + switch Goarch { + case "arm": + return "GOARM", GOARM + case "386": + return "GO386", GO386 + case "mips", "mipsle": + return "GOMIPS", GOMIPS + case "mips64", "mips64le": + return "GOMIPS64", GOMIPS64 + case "ppc64", "ppc64le": + return "GOPPC64", GOPPC64 + case "wasm": + return "GOWASM", GOWASM } + return "", "" +} + +// envOr returns Getenv(key) if set, or else def. +func envOr(key, def string) string { + val := Getenv(key) + if val == "" { + val = def + } + return val } // There is a copy of findGOROOT, isSameDir, and isGOROOT in @@ -132,7 +336,7 @@ func init() { // // There is a copy of this code in x/tools/cmd/godoc/goroot.go. func findGOROOT() string { - if env := os.Getenv("GOROOT"); env != "" { + if env := Getenv("GOROOT"); env != "" { return filepath.Clean(env) } def := filepath.Clean(runtime.GOROOT()) @@ -168,6 +372,8 @@ func findGOROOT() string { } func findGOROOT_FINAL() string { + // $GOROOT_FINAL is only for use during make.bash + // so it is not settable using go/env, so we use os.Getenv here. def := GOROOT if env := os.Getenv("GOROOT_FINAL"); env != "" { def = filepath.Clean(env) diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go index 645f83246a..f03eeca9ff 100644 --- a/src/cmd/go/internal/envcmd/env.go +++ b/src/cmd/go/internal/envcmd/env.go @@ -10,6 +10,9 @@ import ( "fmt" "os" "path/filepath" + "unicode/utf8" + "io/ioutil" + "sort" "runtime" "strings" @@ -22,7 +25,7 @@ import ( ) var CmdEnv = &base.Command{ - UsageLine: "go env [-json] [var ...]", + UsageLine: "go env [-json] [-u] [-w] [var ...]", Short: "print Go environment information", Long: ` Env prints Go environment information. @@ -35,6 +38,14 @@ each named variable on its own line. The -json flag prints the environment in JSON format instead of as a shell script. +The -u flag requires one or more arguments and unsets +the default setting for the named environment variables, +if one has been set with 'go env -w'. + +The -w flag requires one or more arguments of the +form NAME=VALUE and changes the default settings +of the named environment variables to the given values. + For more about environment variables, see 'go help environment'. `, } @@ -43,26 +54,31 @@ func init() { CmdEnv.Run = runEnv // break init cycle } -var envJson = CmdEnv.Flag.Bool("json", false, "") +var ( + envJson = CmdEnv.Flag.Bool("json", false, "") + envU = CmdEnv.Flag.Bool("u", false, "") + envW = CmdEnv.Flag.Bool("w", false, "") +) func MkEnv() []cfg.EnvVar { var b work.Builder b.Init() + envFile, _ := cfg.EnvFile() env := []cfg.EnvVar{ {Name: "GOARCH", Value: cfg.Goarch}, {Name: "GOBIN", Value: cfg.GOBIN}, {Name: "GOCACHE", Value: cache.DefaultDir()}, + {Name: "GOENV", Value: envFile}, {Name: "GOEXE", Value: cfg.ExeSuffix}, - {Name: "GOFLAGS", Value: os.Getenv("GOFLAGS")}, + {Name: "GOFLAGS", Value: cfg.Getenv("GOFLAGS")}, {Name: "GOHOSTARCH", Value: runtime.GOARCH}, {Name: "GOHOSTOS", Value: runtime.GOOS}, {Name: "GOOS", Value: cfg.Goos}, {Name: "GOPATH", Value: cfg.BuildContext.GOPATH}, - {Name: "GOPROXY", Value: os.Getenv("GOPROXY")}, - {Name: "GORACE", Value: os.Getenv("GORACE")}, + {Name: "GOPROXY", Value: cfg.Getenv("GOPROXY")}, {Name: "GOROOT", Value: cfg.GOROOT}, - {Name: "GOTMPDIR", Value: os.Getenv("GOTMPDIR")}, + {Name: "GOTMPDIR", Value: cfg.Getenv("GOTMPDIR")}, {Name: "GOTOOLDIR", Value: base.ToolDir}, } @@ -72,29 +88,20 @@ func MkEnv() []cfg.EnvVar { env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoName}) } - switch cfg.Goarch { - case "arm": - env = append(env, cfg.EnvVar{Name: "GOARM", Value: cfg.GOARM}) - case "386": - env = append(env, cfg.EnvVar{Name: "GO386", Value: cfg.GO386}) - case "mips", "mipsle": - env = append(env, cfg.EnvVar{Name: "GOMIPS", Value: cfg.GOMIPS}) - case "mips64", "mips64le": - env = append(env, cfg.EnvVar{Name: "GOMIPS64", Value: cfg.GOMIPS64}) - case "ppc64", "ppc64le": - env = append(env, cfg.EnvVar{Name: "GOPPC64", Value: cfg.GOPPC64}) - case "wasm": - env = append(env, cfg.EnvVar{Name: "GOWASM", Value: cfg.GOWASM.String()}) + key, val := cfg.GetArchEnv() + if key != "" { + env = append(env, cfg.EnvVar{Name: key, Value: val}) } cc := cfg.DefaultCC(cfg.Goos, cfg.Goarch) - if env := strings.Fields(os.Getenv("CC")); len(env) > 0 { + if env := strings.Fields(cfg.Getenv("CC")); len(env) > 0 { cc = env[0] } cxx := cfg.DefaultCXX(cfg.Goos, cfg.Goarch) - if env := strings.Fields(os.Getenv("CXX")); len(env) > 0 { + if env := strings.Fields(cfg.Getenv("CXX")); len(env) > 0 { cxx = env[0] } + env = append(env, cfg.EnvVar{Name: "AR", Value: envOr("AR", "ar")}) env = append(env, cfg.EnvVar{Name: "CC", Value: cc}) env = append(env, cfg.EnvVar{Name: "CXX", Value: cxx}) @@ -107,6 +114,14 @@ func MkEnv() []cfg.EnvVar { return env } +func envOr(name, def string) string { + val := cfg.Getenv(name) + if val != "" { + return val + } + return def +} + func findEnv(env []cfg.EnvVar, name string) string { for _, e := range env { if e.Name == name { @@ -154,7 +169,25 @@ func ExtraEnvVarsCostly() []cfg.EnvVar { } } +// argKey returns the KEY part of the arg KEY=VAL, or else arg itself. +func argKey(arg string) string { + i := strings.Index(arg, "=") + if i < 0 { + return arg + } + return arg[:i] +} + func runEnv(cmd *base.Command, args []string) { + if *envJson && *envU { + base.Fatalf("go env: cannot use -json with -u") + } + if *envJson && *envW { + base.Fatalf("go env: cannot use -json with -w") + } + if *envU && *envW { + base.Fatalf("go env: cannot use -u with -w") + } env := cfg.CmdEnv env = append(env, ExtraEnvVars()...) @@ -165,7 +198,7 @@ func runEnv(cmd *base.Command, args []string) { if len(args) > 0 { needCostly = false for _, arg := range args { - switch arg { + switch argKey(arg) { case "CGO_CFLAGS", "CGO_CPPFLAGS", "CGO_CXXFLAGS", @@ -181,6 +214,55 @@ func runEnv(cmd *base.Command, args []string) { env = append(env, ExtraEnvVarsCostly()...) } + if *envW { + // Process and sanity-check command line. + if len(args) == 0 { + base.Fatalf("go env -w: no KEY=VALUE arguments given") + } + osEnv := make(map[string]string) + for _, e := range cfg.OrigEnv { + if i := strings.Index(e, "="); i >= 0 { + osEnv[e[:i]] = e[i+1:] + } + } + add := make(map[string]string) + for _, arg := range args { + i := strings.Index(arg, "=") + if i < 0 { + base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg) + } + key, val := arg[:i], arg[i+1:] + if err := checkEnvWrite(key, val, env); err != nil { + base.Fatalf("go env -w: %v", err) + } + if _, ok := add[key]; ok { + base.Fatalf("go env -w: multiple values for key: %s", key) + } + add[key] = val + if osVal := osEnv[key]; osVal != "" && osVal != val { + fmt.Fprintf(os.Stderr, "warning: go env -w %s=... does not override conflicting OS environment variable\n", key) + } + } + updateEnvFile(add, nil) + return + } + + if *envU { + // Process and sanity-check command line. + if len(args) == 0 { + base.Fatalf("go env -u: no arguments given") + } + del := make(map[string]bool) + for _, arg := range args { + if err := checkEnvWrite(arg, "", env); err != nil { + base.Fatalf("go env -u: %v", err) + } + del[arg] = true + } + updateEnvFile(nil, del) + return + } + if len(args) > 0 { if *envJson { var es []cfg.EnvVar @@ -239,6 +321,118 @@ func printEnvAsJSON(env []cfg.EnvVar) { enc := json.NewEncoder(os.Stdout) enc.SetIndent("", "\t") if err := enc.Encode(m); err != nil { - base.Fatalf("%s", err) + base.Fatalf("go env -json: %s", err) } } + +func checkEnvWrite(key, val string, env []cfg.EnvVar) error { + switch key { + case "GOEXE", "GOGCCFLAGS", "GOHOSTARCH", "GOHOSTOS", "GOMOD", "GOTOOLDIR": + return fmt.Errorf("%s cannot be modified", key) + case "GOENV": + return fmt.Errorf("%s can only be set using the OS environment", key) + } + + // To catch typos and the like, check that we know the variable. + if !cfg.CanGetenv(key) { + return fmt.Errorf("unknown go command variable %s", key) + } + + if !utf8.ValidString(val) { + return fmt.Errorf("invalid UTF-8 in %s=... value", key) + } + if strings.Contains(val, "\x00") { + return fmt.Errorf("invalid NUL in %s=... value", key) + } + if strings.ContainsAny(val, "\v\r\n") { + return fmt.Errorf("invalid newline in %s=... value", key) + } + return nil +} + +func updateEnvFile(add map[string]string, del map[string]bool) { + file, err := cfg.EnvFile() + if file == "" { + base.Fatalf("go env: cannot find go env config: %v", err) + } + data, err := ioutil.ReadFile(file) + if err != nil && (!os.IsNotExist(err) || len(add) == 0) { + base.Fatalf("go env: reading go env config: %v", err) + } + + lines := strings.SplitAfter(string(data), "\n") + if lines[len(lines)-1] == "" { + lines = lines[:len(lines)-1] + } else { + lines[len(lines)-1] += "\n" + } + + // Delete all but last copy of any duplicated variables, + // since the last copy is the one that takes effect. + prev := make(map[string]int) + for l, line := range lines { + if key := lineToKey(line); key != "" { + if p, ok := prev[key]; ok { + lines[p] = "" + } + prev[key] = l + } + } + + // Add variables (go env -w). Update existing lines in file if present, add to end otherwise. + for key, val := range add { + if p, ok := prev[key]; ok { + lines[p] = key + "=" + val + "\n" + delete(add, key) + } + } + for key, val := range add { + lines = append(lines, key + "=" + val + "\n") + } + + // Delete requested variables (go env -u). + for key := range del { + if p, ok := prev[key]; ok { + lines[p] = "" + } + } + + // Sort runs of KEY=VALUE lines + // (that is, blocks of lines where blocks are separated + // by comments, blank lines, or invalid lines). + start := 0 + for i := 0; i <= len(lines); i++ { + if i == len(lines) || lineToKey(lines[i]) == "" { + sortKeyValues(lines[start:i]) + start = i+1 + } + } + + data = []byte(strings.Join(lines, "")) + err = ioutil.WriteFile(file, data, 0666) + if err != nil { + // Try creating directory. + os.MkdirAll(filepath.Dir(file), 0777) + err = ioutil.WriteFile(file, data, 0666) + if err != nil { + base.Fatalf("go env: writing go env config: %v", err) + } + } +} + +// lineToKey returns the KEY part of the line KEY=VALUE or else an empty string. +func lineToKey(line string) string { + i := strings.Index(line, "=") + if i < 0 || strings.Contains(line[:i], "#") { + return "" + } + return line[:i] +} + +// sortKeyValues sorts a sequence of lines by key. +// It differs from sort.Strings in that GO386= sorts after GO=. +func sortKeyValues(lines []string) { + sort.Slice(lines, func(i, j int) bool { + return lineToKey(lines[i]) < lineToKey(lines[j]) + }) +} diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go index 98d4bd0382..43ad57f2c0 100644 --- a/src/cmd/go/internal/help/helpdoc.go +++ b/src/cmd/go/internal/help/helpdoc.go @@ -469,10 +469,17 @@ var HelpEnvironment = &base.Command{ Short: "environment variables", Long: ` -The go command, and the tools it invokes, examine a few different -environment variables. For many of these, you can see the default -value of on your system by running 'go env NAME', where NAME is the -name of the variable. +The go command and the tools it invokes consult environment variables +for configuration. If an environment variable is unset, the go command +uses a sensible default setting. To see the effective setting of the +variable , run 'go env '. To change the default setting, +run 'go env -w ='. Defaults changed using 'go env -w' +are recorded in a Go environment configuration file stored in the +per-user configuration directory, as reported by os.UserConfigDir. +The location of the configuration file can be changed by setting +the environment variable GOENV, and 'go env GOENV' prints the +effective location, but 'go env -w' cannot change the default location. +See 'go help env' for details. General-purpose environment variables: @@ -486,10 +493,15 @@ General-purpose environment variables: GOCACHE The directory where the go command will store cached information for reuse in future builds. + GOENV + The location of the Go environment configuration file. + Cannot be set using 'go env -w'. GOFLAGS A space-separated list of -flag=value settings to apply to go commands by default, when the given flag is known by - the current command. Flags listed on the command line + the current command. Each entry must be a standalone flag. + Because the entries are space-separated, flag values must + not contain spaces. Flags listed on the command line are applied after this list and therefore override it. GOOS The operating system for which to compile code. @@ -498,21 +510,18 @@ General-purpose environment variables: For more details see: 'go help gopath'. GOPROXY URL of Go module proxy. See 'go help goproxy'. - GORACE - Options for the race detector. - See https://golang.org/doc/articles/race_detector.html. GOROOT The root of the go tree. GOTMPDIR The directory where the go command will write temporary source files, packages, and binaries. -Each entry in the GOFLAGS list must be a standalone flag. -Because the entries are space-separated, flag values must -not contain spaces. - Environment variables for use with cgo: + AR + The command to use to manipulate library archives when + building with the gccgo compiler. + The default is 'ar'. CC The command to use to compile C code. CGO_ENABLED @@ -542,12 +551,10 @@ Environment variables for use with cgo: but for the linker. CXX The command to use to compile C++ code. + FC + The command to use to compile Fortran code. PKG_CONFIG Path to pkg-config tool. - AR - The command to use to manipulate library archives when - building with the gccgo compiler. - The default is 'ar'. Architecture-specific environment variables: @@ -582,9 +589,11 @@ Special-purpose environment variables: when using -linkmode=auto with code that uses cgo. Set to 0 to disable external linking mode, 1 to enable it. GIT_ALLOW_PROTOCOL - Defined by Git. A colon-separated list of schemes that are allowed to be used - with git fetch/clone. If set, any scheme not explicitly mentioned will be - considered insecure by 'go get'. + Defined by Git. A colon-separated list of schemes that are allowed + to be used with git fetch/clone. If set, any scheme not explicitly + mentioned will be considered insecure by 'go get'. + Because the variable is defined by Git, the default value cannot + be set using 'go env -w'. Additional information available from 'go env' but not read from the environment: diff --git a/src/cmd/go/internal/modfetch/notary.go b/src/cmd/go/internal/modfetch/notary.go index 9514958817..dea37ff450 100644 --- a/src/cmd/go/internal/modfetch/notary.go +++ b/src/cmd/go/internal/modfetch/notary.go @@ -6,11 +6,11 @@ package modfetch import ( "fmt" - "os" pathpkg "path" "strings" "cmd/go/internal/base" + "cmd/go/internal/cfg" "cmd/go/internal/get" "cmd/go/internal/module" ) @@ -67,7 +67,7 @@ func useNotary(mod module.Version) bool { if get.Insecure { return false } - wantNotary, err := notaryShouldVerify(mod.Path, os.Getenv("GONOVERIFY")) + wantNotary, err := notaryShouldVerify(mod.Path, cfg.Getenv("GONOVERIFY")) if err != nil { base.Fatalf("%v", err) } diff --git a/src/cmd/go/internal/modfetch/proxy.go b/src/cmd/go/internal/modfetch/proxy.go index 3d4d2becf4..cbf476d1e4 100644 --- a/src/cmd/go/internal/modfetch/proxy.go +++ b/src/cmd/go/internal/modfetch/proxy.go @@ -9,11 +9,11 @@ import ( "fmt" "io" "net/url" - "os" "strings" "time" "cmd/go/internal/base" + "cmd/go/internal/cfg" "cmd/go/internal/modfetch/codehost" "cmd/go/internal/module" "cmd/go/internal/semver" @@ -85,7 +85,7 @@ cached module versions with GOPROXY=https://example.com/proxy. `, } -var proxyURL = os.Getenv("GOPROXY") +var proxyURL = cfg.Getenv("GOPROXY") // SetProxy sets the proxy to use when fetching modules. // It accepts the same syntax as the GOPROXY environment variable, diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index bc0541705f..eaf4407529 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -6,6 +6,18 @@ package modload import ( "bytes" + "encoding/json" + "fmt" + "go/build" + "internal/lazyregexp" + "io/ioutil" + "os" + "path" + "path/filepath" + "runtime/debug" + "strconv" + "strings" + "cmd/go/internal/base" "cmd/go/internal/cache" "cmd/go/internal/cfg" @@ -18,17 +30,6 @@ import ( "cmd/go/internal/mvs" "cmd/go/internal/renameio" "cmd/go/internal/search" - "encoding/json" - "fmt" - "go/build" - "internal/lazyregexp" - "io/ioutil" - "os" - "path" - "path/filepath" - "runtime/debug" - "strconv" - "strings" ) var ( @@ -90,7 +91,7 @@ func Init() { } initialized = true - env := os.Getenv("GO111MODULE") + env := cfg.Getenv("GO111MODULE") switch env { default: base.Fatalf("go: unknown environment setting GO111MODULE=%s", env) @@ -294,7 +295,7 @@ func die() { if printStackInDie { debug.PrintStack() } - if os.Getenv("GO111MODULE") == "off" { + if cfg.Getenv("GO111MODULE") == "off" { base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'") } if inGOPATH && !mustUseModules { diff --git a/src/cmd/go/internal/work/action.go b/src/cmd/go/internal/work/action.go index 1134b1f35b..7a74b1bb0d 100644 --- a/src/cmd/go/internal/work/action.go +++ b/src/cmd/go/internal/work/action.go @@ -226,7 +226,7 @@ func (b *Builder) Init() { if cfg.BuildN { b.WorkDir = "$WORK" } else { - tmp, err := ioutil.TempDir(os.Getenv("GOTMPDIR"), "go-build") + tmp, err := ioutil.TempDir(cfg.Getenv("GOTMPDIR"), "go-build") if err != nil { base.Fatalf("go: creating work dir: %v", err) } diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index 14d13f83d3..5d2659cef5 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -224,12 +224,16 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID { if len(p.SFiles) > 0 { fmt.Fprintf(h, "asm %q %q %q\n", b.toolID("asm"), forcedAsmflags, p.Internal.Asmflags) } + // GO386, GOARM, GOMIPS, etc. - baseArch := strings.TrimSuffix(cfg.BuildContext.GOARCH, "le") - fmt.Fprintf(h, "GO$GOARCH=%s\n", os.Getenv("GO"+strings.ToUpper(baseArch))) + key, val := cfg.GetArchEnv() + fmt.Fprintf(h, "%s=%s\n", key, val) // TODO(rsc): Convince compiler team not to add more magic environment variables, // or perhaps restrict the environment variables passed to subprocesses. + // Because these are clumsy, undocumented special-case hacks + // for debugging the compiler, they are not settable using 'go env -w', + // and so here we use os.Getenv, not cfg.Getenv. magic := []string{ "GOCLOBBERDEADHASH", "GOSSAFUNC", @@ -1115,21 +1119,16 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) { if p != nil { fmt.Fprintf(h, "linkflags %q\n", p.Internal.Ldflags) } - fmt.Fprintf(h, "GO$GOARCH=%s\n", os.Getenv("GO"+strings.ToUpper(cfg.BuildContext.GOARCH))) // GO386, GOARM, etc + + // GO386, GOARM, GOMIPS, etc. + key, val := cfg.GetArchEnv() + fmt.Fprintf(h, "%s=%s\n", key, val) // The linker writes source file paths that say GOROOT_FINAL. fmt.Fprintf(h, "GOROOT=%s\n", cfg.GOROOT_FINAL) - // TODO(rsc): Convince linker team not to add more magic environment variables, - // or perhaps restrict the environment variables passed to subprocesses. - magic := []string{ - "GO_EXTLINK_ENABLED", - } - for _, env := range magic { - if x := os.Getenv(env); x != "" { - fmt.Fprintf(h, "magic %s=%s\n", env, x) - } - } + // GO_EXTLINK_ENABLED controls whether the external linker is used. + fmt.Fprintf(h, "GO_EXTLINK_ENABLED=%s\n", cfg.Getenv("GO_EXTLINK_ENABLED")) // TODO(rsc): Do cgo settings and flags need to be included? // Or external linker settings and flags? @@ -2192,8 +2191,8 @@ func (b *Builder) gccld(p *load.Package, objdir, outfile string, flags []string, // Grab these before main helpfully overwrites them. var ( - origCC = os.Getenv("CC") - origCXX = os.Getenv("CXX") + origCC = cfg.Getenv("CC") + origCXX = cfg.Getenv("CXX") ) // gccCmd returns a gcc command line prefix @@ -2225,7 +2224,7 @@ func (b *Builder) cxxExe() []string { // fcExe returns the FC compiler setting without all the extra flags we add implicitly. func (b *Builder) fcExe() []string { - return b.compilerExe(os.Getenv("FC"), "gfortran") + return b.compilerExe(cfg.Getenv("FC"), "gfortran") } // compilerExe returns the compiler to use given an @@ -2391,7 +2390,7 @@ func (b *Builder) gccArchArgs() []string { // envList returns the value of the given environment variable broken // into fields, using the default value when the variable is empty. func envList(key, def string) []string { - v := os.Getenv(key) + v := cfg.Getenv(key) if v == "" { v = def } @@ -2448,7 +2447,7 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo // Support gfortran out of the box and let others pass the correct link options // via CGO_LDFLAGS if len(ffiles) > 0 { - fc := os.Getenv("FC") + fc := cfg.Getenv("FC") if fc == "" { fc = "gfortran" } diff --git a/src/cmd/go/internal/work/gccgo.go b/src/cmd/go/internal/work/gccgo.go index 0ba690fd62..ff2621e1c4 100644 --- a/src/cmd/go/internal/work/gccgo.go +++ b/src/cmd/go/internal/work/gccgo.go @@ -26,7 +26,7 @@ var GccgoName, GccgoBin string var gccgoErr error func init() { - GccgoName = os.Getenv("GCCGO") + GccgoName = cfg.Getenv("GCCGO") if GccgoName == "" { GccgoName = "gccgo" } @@ -44,7 +44,7 @@ func (gccgoToolchain) linker() string { } func (gccgoToolchain) ar() string { - ar := os.Getenv("AR") + ar := cfg.Getenv("AR") if ar == "" { ar = "ar" } @@ -479,7 +479,7 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string ldflags = append(ldflags, "-lobjc") } if fortran { - fc := os.Getenv("FC") + fc := cfg.Getenv("FC") if fc == "" { fc = "gfortran" } diff --git a/src/cmd/go/internal/work/security.go b/src/cmd/go/internal/work/security.go index 8351e4c731..ecfb9df1b2 100644 --- a/src/cmd/go/internal/work/security.go +++ b/src/cmd/go/internal/work/security.go @@ -30,12 +30,13 @@ package work import ( - "cmd/go/internal/load" "fmt" "internal/lazyregexp" - "os" "regexp" "strings" + + "cmd/go/internal/cfg" + "cmd/go/internal/load" ) var re = lazyregexp.New @@ -229,14 +230,14 @@ func checkFlags(name, source string, list []string, valid []*lazyregexp.Regexp, allow *regexp.Regexp disallow *regexp.Regexp ) - if env := os.Getenv("CGO_" + name + "_ALLOW"); env != "" { + if env := cfg.Getenv("CGO_" + name + "_ALLOW"); env != "" { r, err := regexp.Compile(env) if err != nil { return fmt.Errorf("parsing $CGO_%s_ALLOW: %v", name, err) } allow = r } - if env := os.Getenv("CGO_" + name + "_DISALLOW"); env != "" { + if env := cfg.Getenv("CGO_" + name + "_DISALLOW"); env != "" { r, err := regexp.Compile(env) if err != nil { return fmt.Errorf("parsing $CGO_%s_DISALLOW: %v", name, err) diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index 35a507680f..0207862d0b 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -122,8 +122,14 @@ func main() { os.Exit(2) } if !filepath.IsAbs(p) { - fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nFor more details see: 'go help gopath'\n", p) - os.Exit(2) + if cfg.Getenv("GOPATH") == "" { + // We inferred $GOPATH from $HOME and did a bad job at it. + // Instead of dying, uninfer it. + cfg.BuildContext.GOPATH = "" + } else { + fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nFor more details see: 'go help gopath'\n", p) + os.Exit(2) + } } } } diff --git a/src/cmd/go/testdata/script/env_write.txt b/src/cmd/go/testdata/script/env_write.txt new file mode 100644 index 0000000000..bdc348c953 --- /dev/null +++ b/src/cmd/go/testdata/script/env_write.txt @@ -0,0 +1,87 @@ +env GO111MODULE=off + +# go env should default to the right places +env AppData=$HOME/windowsappdata +env home=$HOME/plan9home +go env GOENV +[aix] stdout $HOME/.config/go/env +[darwin] stdout $HOME/Library/Preferences/go/env +[freebsd] stdout $HOME/.config/go/env +[linux] stdout $HOME/.config/go/env +[netbsd] stdout $HOME/.config/go/env +[openbsd] stdout $HOME/.config/go/env +[plan9] stdout $HOME/plan9home/lib/go/env +[windows] stdout $HOME\\windowsappdata\\go\\env + +# Now override it to something writable. +env GOENV=$WORK/envdir/go/env +go env GOENV +stdout envdir[\\/]go[\\/]env + +# go env shows all variables +go env +stdout GOARCH= +stdout GOOS= +stdout GOROOT= + +# go env -w changes default setting +env root= +[windows] env root=c: +env GOPATH= +go env -w GOPATH=$root/non-exist/gopath +! stderr .+ +grep GOPATH=$root/non-exist/gopath $WORK/envdir/go/env +go env GOPATH +stdout /non-exist/gopath + +# go env -w does not override OS environment, and warns about that +env GOPATH=$root/other +go env -w GOPATH=$root/non-exist/gopath2 +stderr 'warning: go env -w GOPATH=... does not override conflicting OS environment variable' +go env GOPATH +stdout $root/other + +# but go env -w does do the update, and unsetting the env var exposes the change +env GOPATH= +go env GOPATH +stdout $root/non-exist/gopath2 + +# unsetting with go env -u does not warn about OS environment overrides, +# nor does it warn about variables that haven't been set by go env -w. +env GOPATH=$root/other +go env -u GOPATH +! stderr .+ +go env -u GOPATH +! stderr .+ + +# go env -w rejects unknown or bad variables +! go env -w GODEBUG=gctrace=1 +stderr 'unknown go command variable GODEBUG' +! go env -w GOEXE=.bat +stderr 'GOEXE cannot be modified' +! go env -w GOENV=/env +stderr 'GOENV can only be set using the OS environment' + +# go env -w can set multiple variables +env CC= +go env CC +! stdout ^xyc$ +go env -w GOOS=$GOOS CC=xyc +grep CC=xyc $GOENV +# file is maintained in sorted order +grep 'CC=xyc\nGOOS=' $GOENV +go env CC +stdout ^xyc$ + +# go env -u unsets effect of go env -w. +go env -u CC +go env CC +! stdout ^xyc$ + +# go env -w rejects double-set variables +! go env -w GOOS=$GOOS GOOS=$GOOS +stderr 'multiple values for key: GOOS' + +# go env -w rejects missing variables +! go env -w GOOS +stderr 'arguments must be KEY=VALUE: invalid argument: GOOS' diff --git a/src/cmd/internal/objabi/util.go b/src/cmd/internal/objabi/util.go index 57f19f2e3c..9e41b87aa4 100644 --- a/src/cmd/internal/objabi/util.go +++ b/src/cmd/internal/objabi/util.go @@ -87,7 +87,7 @@ type gowasmFeatures struct { SatConv bool } -func (f *gowasmFeatures) String() string { +func (f gowasmFeatures) String() string { var flags []string if f.SatConv { flags = append(flags, "satconv") diff --git a/src/make.bash b/src/make.bash index 2883f47c12..92d148110a 100755 --- a/src/make.bash +++ b/src/make.bash @@ -65,6 +65,7 @@ set -e +export GOENV=off unset GOBIN # Issue 14340 unset GOFLAGS unset GO111MODULE diff --git a/src/make.bat b/src/make.bat index d22cb30ab2..d18cd87d48 100644 --- a/src/make.bat +++ b/src/make.bat @@ -46,13 +46,14 @@ if x%4==x--no-local goto nolocal setlocal :nolocal +set GOENV=off set GOBUILDFAIL=0 set GOFLAGS= set GO111MODULE= if exist make.bat goto ok echo Must run make.bat from Go src directory. -goto fail +goto fail :ok :: Clean old generated file that will cause problems in the build. diff --git a/src/make.rc b/src/make.rc index f055ff8e14..f5e57e9755 100755 --- a/src/make.rc +++ b/src/make.rc @@ -47,6 +47,7 @@ if(~ $1 -v) { shift } +GOENV=off GOFLAGS=() GO111MODULE=() GOROOT = `{cd .. && pwd} diff --git a/src/runtime/extern.go b/src/runtime/extern.go index e308dd38b1..2917efefa6 100644 --- a/src/runtime/extern.go +++ b/src/runtime/extern.go @@ -136,6 +136,9 @@ that can be blocked in system calls on behalf of Go code; those do not count aga the GOMAXPROCS limit. This package's GOMAXPROCS function queries and changes the limit. +The GORACE variable configures the race detector, for programs built using -race. +See https://golang.org/doc/articles/race_detector.html for details. + The GOTRACEBACK variable controls the amount of output generated when a Go program fails due to an unrecovered panic or an unexpected runtime condition. By default, a failure prints a stack trace for the current goroutine,