aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/go/internal/cfg/cfg.go
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 /src/cmd/go/internal/cfg/cfg.go
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>
Diffstat (limited to 'src/cmd/go/internal/cfg/cfg.go')
-rw-r--r--src/cmd/go/internal/cfg/cfg.go254
1 files changed, 230 insertions, 24 deletions
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)