//
// Usage:
//
-// go env [-json] [var ...]
+// go env [-json] [-u] [-w] [var ...]
//
// Env prints Go environment information.
//
// 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'.
//
//
//
// 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:
//
// 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.
// 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
// 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:
//
// 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:
//
import (
"bytes"
- "cmd/go/internal/cache"
- "cmd/internal/sys"
"context"
"debug/elf"
"debug/macho"
"strings"
"testing"
"time"
+
+ "cmd/go/internal/cache"
+ "cmd/go/internal/cfg"
+ "cmd/internal/sys"
)
var (
// 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")
}
}
// Don't let these environment variables confuse the test.
+ os.Setenv("GOENV", "off")
os.Unsetenv("GOBIN")
os.Unsetenv("GOPATH")
os.Unsetenv("GIT_ALLOW_PROTOCOL")
// 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
}
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"))
import (
"flag"
"fmt"
- "os"
"runtime"
"strings"
// (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
}
"sync"
"cmd/go/internal/base"
+ "cmd/go/internal/cfg"
)
// Default returns the default cache to use, or nil if no cache should be used.
// 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
}
package cfg
import (
+ "bytes"
"fmt"
"go/build"
+ "io/ioutil"
"os"
"path/filepath"
"runtime"
+ "strings"
+ "sync"
"cmd/internal/objabi"
)
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
}
// 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).
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
//
// 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())
}
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)
"fmt"
"os"
"path/filepath"
+ "unicode/utf8"
+ "io/ioutil"
+ "sort"
"runtime"
"strings"
)
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.
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'.
`,
}
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},
}
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})
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 {
}
}
+// 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()...)
if len(args) > 0 {
needCostly = false
for _, arg := range args {
- switch arg {
+ switch argKey(arg) {
case "CGO_CFLAGS",
"CGO_CPPFLAGS",
"CGO_CXXFLAGS",
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
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])
+ })
}
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:
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.
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
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:
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:
import (
"fmt"
- "os"
pathpkg "path"
"strings"
"cmd/go/internal/base"
+ "cmd/go/internal/cfg"
"cmd/go/internal/get"
"cmd/go/internal/module"
)
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)
}
"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"
`,
}
-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,
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"
"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 (
}
initialized = true
- env := os.Getenv("GO111MODULE")
+ env := cfg.Getenv("GO111MODULE")
switch env {
default:
base.Fatalf("go: unknown environment setting GO111MODULE=%s", env)
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 {
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)
}
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",
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?
// 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
// 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
// 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
}
// 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"
}
var gccgoErr error
func init() {
- GccgoName = os.Getenv("GCCGO")
+ GccgoName = cfg.Getenv("GCCGO")
if GccgoName == "" {
GccgoName = "gccgo"
}
}
func (gccgoToolchain) ar() string {
- ar := os.Getenv("AR")
+ ar := cfg.Getenv("AR")
if ar == "" {
ar = "ar"
}
ldflags = append(ldflags, "-lobjc")
}
if fortran {
- fc := os.Getenv("FC")
+ fc := cfg.Getenv("FC")
if fc == "" {
fc = "gfortran"
}
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
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)
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)
+ }
}
}
}
--- /dev/null
+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'
SatConv bool
}
-func (f *gowasmFeatures) String() string {
+func (f gowasmFeatures) String() string {
var flags []string
if f.SatConv {
flags = append(flags, "satconv")
set -e
+export GOENV=off
unset GOBIN # Issue 14340
unset GOFLAGS
unset GO111MODULE
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.
shift
}
+GOENV=off
GOFLAGS=()
GO111MODULE=()
GOROOT = `{cd .. && pwd}
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,