aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2019-04-08 11:23:42 -0400
committerRuss Cox <rsc@golang.org>2019-04-23 00:58:08 +0000
commitf0e97546962736fe4aa73b7c7ed590f0134515e1 (patch)
treed6903cc240c0cdef29bcd0d7fa3b059eab080a17
parente40dffe55ac0ec40fc325bf9ef03dde297fcc2c0 (diff)
downloadgo-f0e97546962736fe4aa73b7c7ed590f0134515e1.tar.gz
go-f0e97546962736fe4aa73b7c7ed590f0134515e1.zip
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 <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com>
-rw-r--r--src/cmd/go/alldocs.go57
-rw-r--r--src/cmd/go/go_test.go13
-rw-r--r--src/cmd/go/internal/base/goflags.go3
-rw-r--r--src/cmd/go/internal/cache/default.go3
-rw-r--r--src/cmd/go/internal/cfg/cfg.go254
-rw-r--r--src/cmd/go/internal/envcmd/env.go240
-rw-r--r--src/cmd/go/internal/help/helpdoc.go47
-rw-r--r--src/cmd/go/internal/modfetch/notary.go4
-rw-r--r--src/cmd/go/internal/modfetch/proxy.go4
-rw-r--r--src/cmd/go/internal/modload/init.go27
-rw-r--r--src/cmd/go/internal/work/action.go2
-rw-r--r--src/cmd/go/internal/work/exec.go35
-rw-r--r--src/cmd/go/internal/work/gccgo.go6
-rw-r--r--src/cmd/go/internal/work/security.go9
-rw-r--r--src/cmd/go/main.go10
-rw-r--r--src/cmd/go/testdata/script/env_write.txt87
-rw-r--r--src/cmd/internal/objabi/util.go2
-rwxr-xr-xsrc/make.bash1
-rw-r--r--src/make.bat3
-rwxr-xr-xsrc/make.rc1
-rw-r--r--src/runtime/extern.go3
21 files changed, 671 insertions, 140 deletions
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 <NAME>, run 'go env <NAME>'. To change the default setting,
+// run 'go env -w <NAME>=<VALUE>'. 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 <NAME>, run 'go env <NAME>'. To change the default setting,
+run 'go env -w <NAME>=<VALUE>'. 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,