cmd/go: add -changed to query for non-defaults in the env

Fixes #34208

Change-Id: I8ec2d96262dcd7cbf870f6173690143c54190722
GitHub-Last-Rev: 6543df4784
GitHub-Pull-Request: golang/go#65655
Reviewed-on: https://go-review.googlesource.com/c/go/+/563137
Reviewed-by: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
qiulaidongfeng 2024-05-09 22:11:00 +00:00 committed by Michael Matloob
parent d367b2a475
commit 6cd066f8b2
16 changed files with 272 additions and 129 deletions

View File

@ -8,6 +8,10 @@ Distributions that install the `go` command to a location other than
`$GOROOT/bin/go` should install a symlink instead of relocating `$GOROOT/bin/go` should install a symlink instead of relocating
or copying the `go` binary. or copying the `go` binary.
The new go env `-changed` flag causes the command to print only
those settings whose effective value differs from the default value
that would be obtained in an empty environment with no prior uses of the `-w` flag.
### Vet {#vet} ### Vet {#vet}
The `go vet` subcommand now includes the The `go vet` subcommand now includes the

View File

@ -456,7 +456,7 @@
// //
// Usage: // Usage:
// //
// go env [-json] [-u] [-w] [var ...] // go env [-json] [-changed] [-u] [-w] [var ...]
// //
// Env prints Go environment information. // Env prints Go environment information.
// //
@ -476,6 +476,10 @@
// form NAME=VALUE and changes the default settings // form NAME=VALUE and changes the default settings
// of the named environment variables to the given values. // of the named environment variables to the given values.
// //
// The -changed flag prints only those settings whose effective
// value differs from the default value that would be obtained in
// an empty environment with no prior uses of the -w flag.
//
// For more about environment variables, see 'go help environment'. // For more about environment variables, see 'go help environment'.
// //
// # Update packages to use new APIs // # Update packages to use new APIs

View File

@ -196,7 +196,7 @@ func TestMain(m *testing.M) {
defer removeAll(testTmpDir) defer removeAll(testTmpDir)
} }
testGOCACHE = cache.DefaultDir() testGOCACHE, _ = cache.DefaultDir()
if testenv.HasGoBuild() { if testenv.HasGoBuild() {
testBin = filepath.Join(testTmpDir, "testbin") testBin = filepath.Join(testTmpDir, "testbin")
if err := os.Mkdir(testBin, 0777); err != nil { if err := os.Mkdir(testBin, 0777); err != nil {

View File

@ -106,7 +106,7 @@ func printGoEnv(w io.Writer) {
env := envcmd.MkEnv() env := envcmd.MkEnv()
env = append(env, envcmd.ExtraEnvVars()...) env = append(env, envcmd.ExtraEnvVars()...)
env = append(env, envcmd.ExtraEnvVarsCostly()...) env = append(env, envcmd.ExtraEnvVarsCostly()...)
envcmd.PrintEnv(w, env) envcmd.PrintEnv(w, env, false)
} }
func printGoDetails(w io.Writer) { func printGoDetails(w io.Writer) {

View File

@ -39,7 +39,7 @@ See golang.org to learn more about Go.
// initDefaultCache does the work of finding the default cache // initDefaultCache does the work of finding the default cache
// the first time Default is called. // the first time Default is called.
func initDefaultCache() { func initDefaultCache() {
dir := DefaultDir() dir, _ := DefaultDir()
if dir == "off" { if dir == "off" {
if defaultDirErr != nil { if defaultDirErr != nil {
base.Fatalf("build cache is required, but could not be located: %v", defaultDirErr) base.Fatalf("build cache is required, but could not be located: %v", defaultDirErr)
@ -67,14 +67,16 @@ func initDefaultCache() {
} }
var ( var (
defaultDirOnce sync.Once defaultDirOnce sync.Once
defaultDir string defaultDir string
defaultDirErr error defaultDirChanged bool // effective value differs from $GOCACHE
defaultDirErr error
) )
// DefaultDir returns the effective GOCACHE setting. // DefaultDir returns the effective GOCACHE setting.
// It returns "off" if the cache is disabled. // It returns "off" if the cache is disabled,
func DefaultDir() string { // and reports whether the effective value differs from GOCACHE.
func DefaultDir() (string, bool) {
// Save the result of the first call to DefaultDir for later use in // Save the result of the first call to DefaultDir for later use in
// initDefaultCache. cmd/go/main.go explicitly sets GOCACHE so that // initDefaultCache. cmd/go/main.go explicitly sets GOCACHE so that
// subprocesses will inherit it, but that means initDefaultCache can't // subprocesses will inherit it, but that means initDefaultCache can't
@ -82,10 +84,11 @@ func DefaultDir() string {
defaultDirOnce.Do(func() { defaultDirOnce.Do(func() {
defaultDir = cfg.Getenv("GOCACHE") defaultDir = cfg.Getenv("GOCACHE")
if filepath.IsAbs(defaultDir) || defaultDir == "off" {
return
}
if defaultDir != "" { if defaultDir != "" {
defaultDirChanged = true
if filepath.IsAbs(defaultDir) || defaultDir == "off" {
return
}
defaultDir = "off" defaultDir = "off"
defaultDirErr = fmt.Errorf("GOCACHE is not an absolute path") defaultDirErr = fmt.Errorf("GOCACHE is not an absolute path")
return return
@ -95,11 +98,12 @@ func DefaultDir() string {
dir, err := os.UserCacheDir() dir, err := os.UserCacheDir()
if err != nil { if err != nil {
defaultDir = "off" defaultDir = "off"
defaultDirChanged = true
defaultDirErr = fmt.Errorf("GOCACHE is not defined and %v", err) defaultDirErr = fmt.Errorf("GOCACHE is not defined and %v", err)
return return
} }
defaultDir = filepath.Join(dir, "go-build") defaultDir = filepath.Join(dir, "go-build")
}) })
return defaultDir return defaultDir, defaultDirChanged
} }

View File

@ -101,7 +101,9 @@ var (
// GoPathError is set when GOPATH is not set. it contains an // GoPathError is set when GOPATH is not set. it contains an
// explanation why GOPATH is unset. // explanation why GOPATH is unset.
GoPathError string GoPathError string
GOPATHChanged bool
CGOChanged bool
) )
func defaultContext() build.Context { func defaultContext() build.Context {
@ -111,7 +113,7 @@ func defaultContext() build.Context {
// Override defaults computed in go/build with defaults // Override defaults computed in go/build with defaults
// from go environment configuration file, if known. // from go environment configuration file, if known.
ctxt.GOPATH = envOr("GOPATH", gopath(ctxt)) ctxt.GOPATH, GOPATHChanged = EnvOrAndChanged("GOPATH", gopath(ctxt))
ctxt.GOOS = Goos ctxt.GOOS = Goos
ctxt.GOARCH = Goarch ctxt.GOARCH = Goarch
@ -125,14 +127,16 @@ func defaultContext() build.Context {
ctxt.ToolTags = save ctxt.ToolTags = save
// The go/build rule for whether cgo is enabled is: // The go/build rule for whether cgo is enabled is:
// 1. If $CGO_ENABLED is set, respect it. // 1. If $CGO_ENABLED is set, respect it.
// 2. Otherwise, if this is a cross-compile, disable cgo. // 2. Otherwise, if this is a cross-compile, disable cgo.
// 3. Otherwise, use built-in default for GOOS/GOARCH. // 3. Otherwise, use built-in default for GOOS/GOARCH.
//
// Recreate that logic here with the new GOOS/GOARCH setting. // Recreate that logic here with the new GOOS/GOARCH setting.
if v := Getenv("CGO_ENABLED"); v == "0" || v == "1" { // We need to run steps 2 and 3 to determine what the default value
ctxt.CgoEnabled = v[0] == '1' // of CgoEnabled would be for computing CGOChanged.
} else if ctxt.GOOS != runtime.GOOS || ctxt.GOARCH != runtime.GOARCH { defaultCgoEnabled := ctxt.CgoEnabled
ctxt.CgoEnabled = false if ctxt.GOOS != runtime.GOOS || ctxt.GOARCH != runtime.GOARCH {
defaultCgoEnabled = false
} else { } else {
// Use built-in default cgo setting for GOOS/GOARCH. // Use built-in default cgo setting for GOOS/GOARCH.
// Note that ctxt.GOOS/GOARCH are derived from the preference list // Note that ctxt.GOOS/GOARCH are derived from the preference list
@ -159,11 +163,16 @@ func defaultContext() build.Context {
if os.Getenv("CC") == "" { if os.Getenv("CC") == "" {
cc := DefaultCC(ctxt.GOOS, ctxt.GOARCH) cc := DefaultCC(ctxt.GOOS, ctxt.GOARCH)
if _, err := LookPath(cc); err != nil { if _, err := LookPath(cc); err != nil {
ctxt.CgoEnabled = false defaultCgoEnabled = false
} }
} }
} }
} }
ctxt.CgoEnabled = defaultCgoEnabled
if v := Getenv("CGO_ENABLED"); v == "0" || v == "1" {
ctxt.CgoEnabled = v[0] == '1'
}
CGOChanged = ctxt.CgoEnabled != defaultCgoEnabled
ctxt.OpenFile = func(path string) (io.ReadCloser, error) { ctxt.OpenFile = func(path string) (io.ReadCloser, error) {
return fsys.Open(path) return fsys.Open(path)
@ -262,8 +271,9 @@ func init() {
// An EnvVar is an environment variable Name=Value. // An EnvVar is an environment variable Name=Value.
type EnvVar struct { type EnvVar struct {
Name string Name string
Value string Value string
Changed bool // effective Value differs from default
} }
// OrigEnv is the original environment of the program at startup. // OrigEnv is the original environment of the program at startup.
@ -279,27 +289,28 @@ var envCache struct {
m map[string]string m map[string]string
} }
// EnvFile returns the name of the Go environment configuration file. // EnvFile returns the name of the Go environment configuration file,
func EnvFile() (string, error) { // and reports whether the effective value differs from the default.
func EnvFile() (string, bool, error) {
if file := os.Getenv("GOENV"); file != "" { if file := os.Getenv("GOENV"); file != "" {
if file == "off" { if file == "off" {
return "", fmt.Errorf("GOENV=off") return "", false, fmt.Errorf("GOENV=off")
} }
return file, nil return file, true, nil
} }
dir, err := os.UserConfigDir() dir, err := os.UserConfigDir()
if err != nil { if err != nil {
return "", err return "", false, err
} }
if dir == "" { if dir == "" {
return "", fmt.Errorf("missing user-config dir") return "", false, fmt.Errorf("missing user-config dir")
} }
return filepath.Join(dir, "go/env"), nil return filepath.Join(dir, "go/env"), false, nil
} }
func initEnvCache() { func initEnvCache() {
envCache.m = make(map[string]string) envCache.m = make(map[string]string)
if file, _ := EnvFile(); file != "" { if file, _, _ := EnvFile(); file != "" {
readEnvFile(file, "user") readEnvFile(file, "user")
} }
goroot := findGOROOT(envCache.m["GOROOT"]) goroot := findGOROOT(envCache.m["GOROOT"])
@ -397,57 +408,67 @@ var (
GOROOTpkg string GOROOTpkg string
GOROOTsrc string GOROOTsrc string
GOBIN = Getenv("GOBIN") GOBIN = Getenv("GOBIN")
GOMODCACHE = envOr("GOMODCACHE", gopathDir("pkg/mod")) GOMODCACHE, GOMODCACHEChanged = EnvOrAndChanged("GOMODCACHE", gopathDir("pkg/mod"))
// Used in envcmd.MkEnv and build ID computations. // Used in envcmd.MkEnv and build ID computations.
GOARM = envOr("GOARM", fmt.Sprint(buildcfg.GOARM)) GOARM64, goARM64Changed = EnvOrAndChanged("GOARM64", fmt.Sprint(buildcfg.GOARM64))
GOARM64 = envOr("GOARM64", fmt.Sprint(buildcfg.GOARM64)) GOARM, goARMChanged = EnvOrAndChanged("GOARM", fmt.Sprint(buildcfg.GOARM))
GO386 = envOr("GO386", buildcfg.GO386) GO386, go386Changed = EnvOrAndChanged("GO386", buildcfg.GO386)
GOAMD64 = envOr("GOAMD64", fmt.Sprintf("%s%d", "v", buildcfg.GOAMD64)) GOAMD64, goAMD64Changed = EnvOrAndChanged("GOAMD64", fmt.Sprintf("%s%d", "v", buildcfg.GOAMD64))
GOMIPS = envOr("GOMIPS", buildcfg.GOMIPS) GOMIPS, goMIPSChanged = EnvOrAndChanged("GOMIPS", buildcfg.GOMIPS)
GOMIPS64 = envOr("GOMIPS64", buildcfg.GOMIPS64) GOMIPS64, goMIPS64Changed = EnvOrAndChanged("GOMIPS64", buildcfg.GOMIPS64)
GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", buildcfg.GOPPC64)) GOPPC64, goPPC64Changed = EnvOrAndChanged("GOPPC64", fmt.Sprintf("%s%d", "power", buildcfg.GOPPC64))
GORISCV64 = envOr("GORISCV64", fmt.Sprintf("rva%du64", buildcfg.GORISCV64)) GORISCV64, goRISCV64Changed = EnvOrAndChanged("GORISCV64", fmt.Sprintf("rva%du64", buildcfg.GORISCV64))
GOWASM = envOr("GOWASM", fmt.Sprint(buildcfg.GOWASM)) GOWASM, goWASMChanged = EnvOrAndChanged("GOWASM", fmt.Sprint(buildcfg.GOWASM))
GOPROXY = envOr("GOPROXY", "") GOPROXY, GOPROXYChanged = EnvOrAndChanged("GOPROXY", "")
GOSUMDB = envOr("GOSUMDB", "") GOSUMDB, GOSUMDBChanged = EnvOrAndChanged("GOSUMDB", "")
GOPRIVATE = Getenv("GOPRIVATE") GOPRIVATE = Getenv("GOPRIVATE")
GONOPROXY = envOr("GONOPROXY", GOPRIVATE) GONOPROXY, GONOPROXYChanged = EnvOrAndChanged("GONOPROXY", GOPRIVATE)
GONOSUMDB = envOr("GONOSUMDB", GOPRIVATE) GONOSUMDB, GONOSUMDBChanged = EnvOrAndChanged("GONOSUMDB", GOPRIVATE)
GOINSECURE = Getenv("GOINSECURE") GOINSECURE = Getenv("GOINSECURE")
GOVCS = Getenv("GOVCS") GOVCS = Getenv("GOVCS")
) )
// EnvOrAndChanged returns the environment variable value
// and reports whether it differs from the default value.
func EnvOrAndChanged(name, def string) (string, bool) {
val := Getenv(name)
if val != "" {
return val, val != def
}
return def, false
}
var SumdbDir = gopathDir("pkg/sumdb") var SumdbDir = gopathDir("pkg/sumdb")
// GetArchEnv returns the name and setting of the // GetArchEnv returns the name and setting of the
// GOARCH-specific architecture environment variable. // GOARCH-specific architecture environment variable.
// If the current architecture has no GOARCH-specific variable, // If the current architecture has no GOARCH-specific variable,
// GetArchEnv returns empty key and value. // GetArchEnv returns empty key and value.
func GetArchEnv() (key, val string) { func GetArchEnv() (key, val string, changed bool) {
switch Goarch { switch Goarch {
case "arm": case "arm":
return "GOARM", GOARM return "GOARM", GOARM, goARMChanged
case "arm64": case "arm64":
return "GOARM64", GOARM64 return "GOARM64", GOARM64, goARM64Changed
case "386": case "386":
return "GO386", GO386 return "GO386", GO386, go386Changed
case "amd64": case "amd64":
return "GOAMD64", GOAMD64 return "GOAMD64", GOAMD64, goAMD64Changed
case "mips", "mipsle": case "mips", "mipsle":
return "GOMIPS", GOMIPS return "GOMIPS", GOMIPS, goMIPSChanged
case "mips64", "mips64le": case "mips64", "mips64le":
return "GOMIPS64", GOMIPS64 return "GOMIPS64", GOMIPS64, goMIPS64Changed
case "ppc64", "ppc64le": case "ppc64", "ppc64le":
return "GOPPC64", GOPPC64 return "GOPPC64", GOPPC64, goPPC64Changed
case "riscv64": case "riscv64":
return "GORISCV64", GORISCV64 return "GORISCV64", GORISCV64, goRISCV64Changed
case "wasm": case "wasm":
return "GOWASM", GOWASM return "GOWASM", GOWASM, goWASMChanged
} }
return "", "" return "", "", false
} }
// envOr returns Getenv(key) if set, or else def. // envOr returns Getenv(key) if set, or else def.
@ -565,6 +586,7 @@ func gopathDir(rel string) string {
return filepath.Join(list[0], rel) return filepath.Join(list[0], rel)
} }
// Keep consistent with go/build.defaultGOPATH.
func gopath(ctxt build.Context) string { func gopath(ctxt build.Context) string {
if len(ctxt.GOPATH) > 0 { if len(ctxt.GOPATH) > 0 {
return ctxt.GOPATH return ctxt.GOPATH

View File

@ -153,7 +153,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) {
sh := work.NewShell("", fmt.Print) sh := work.NewShell("", fmt.Print)
if cleanCache { if cleanCache {
dir := cache.DefaultDir() dir, _ := cache.DefaultDir()
if dir != "off" { if dir != "off" {
// Remove the cache subdirectories but not the top cache directory. // Remove the cache subdirectories but not the top cache directory.
// The top cache directory may have been created with special permissions // The top cache directory may have been created with special permissions
@ -180,7 +180,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) {
// Instead of walking through the entire cache looking for test results, // Instead of walking through the entire cache looking for test results,
// we write a file to the cache indicating that all test results from before // we write a file to the cache indicating that all test results from before
// right now are to be ignored. // right now are to be ignored.
dir := cache.DefaultDir() dir, _ := cache.DefaultDir()
if dir != "off" { if dir != "off" {
f, err := lockedfile.Edit(filepath.Join(dir, "testexpire.txt")) f, err := lockedfile.Edit(filepath.Join(dir, "testexpire.txt"))
if err == nil { if err == nil {

View File

@ -32,7 +32,7 @@ import (
) )
var CmdEnv = &base.Command{ var CmdEnv = &base.Command{
UsageLine: "go env [-json] [-u] [-w] [var ...]", UsageLine: "go env [-json] [-changed] [-u] [-w] [var ...]",
Short: "print Go environment information", Short: "print Go environment information",
Long: ` Long: `
Env prints Go environment information. Env prints Go environment information.
@ -53,6 +53,10 @@ The -w flag requires one or more arguments of the
form NAME=VALUE and changes the default settings form NAME=VALUE and changes the default settings
of the named environment variables to the given values. of the named environment variables to the given values.
The -changed flag prints only those settings whose effective
value differs from the default value that would be obtained in
an empty environment with no prior uses of the -w flag.
For more about environment variables, see 'go help environment'. For more about environment variables, see 'go help environment'.
`, `,
} }
@ -64,19 +68,20 @@ func init() {
} }
var ( var (
envJson = CmdEnv.Flag.Bool("json", false, "") envJson = CmdEnv.Flag.Bool("json", false, "")
envU = CmdEnv.Flag.Bool("u", false, "") envU = CmdEnv.Flag.Bool("u", false, "")
envW = CmdEnv.Flag.Bool("w", false, "") envW = CmdEnv.Flag.Bool("w", false, "")
envChanged = CmdEnv.Flag.Bool("changed", false, "")
) )
func MkEnv() []cfg.EnvVar { func MkEnv() []cfg.EnvVar {
envFile, _ := cfg.EnvFile() envFile, envFileChanged, _ := cfg.EnvFile()
env := []cfg.EnvVar{ env := []cfg.EnvVar{
{Name: "GO111MODULE", Value: cfg.Getenv("GO111MODULE")}, {Name: "GO111MODULE", Value: cfg.Getenv("GO111MODULE")},
{Name: "GOARCH", Value: cfg.Goarch}, {Name: "GOARCH", Value: cfg.Goarch, Changed: cfg.Goarch != runtime.GOARCH},
{Name: "GOBIN", Value: cfg.GOBIN}, {Name: "GOBIN", Value: cfg.GOBIN},
{Name: "GOCACHE", Value: cache.DefaultDir()}, {Name: "GOCACHE"},
{Name: "GOENV", Value: envFile}, {Name: "GOENV", Value: envFile, Changed: envFileChanged},
{Name: "GOEXE", Value: cfg.ExeSuffix}, {Name: "GOEXE", Value: cfg.ExeSuffix},
// List the raw value of GOEXPERIMENT, not the cleaned one. // List the raw value of GOEXPERIMENT, not the cleaned one.
@ -90,63 +95,82 @@ func MkEnv() []cfg.EnvVar {
{Name: "GOHOSTARCH", Value: runtime.GOARCH}, {Name: "GOHOSTARCH", Value: runtime.GOARCH},
{Name: "GOHOSTOS", Value: runtime.GOOS}, {Name: "GOHOSTOS", Value: runtime.GOOS},
{Name: "GOINSECURE", Value: cfg.GOINSECURE}, {Name: "GOINSECURE", Value: cfg.GOINSECURE},
{Name: "GOMODCACHE", Value: cfg.GOMODCACHE}, {Name: "GOMODCACHE", Value: cfg.GOMODCACHE, Changed: cfg.GOMODCACHEChanged},
{Name: "GONOPROXY", Value: cfg.GONOPROXY}, {Name: "GONOPROXY", Value: cfg.GONOPROXY, Changed: cfg.GONOPROXYChanged},
{Name: "GONOSUMDB", Value: cfg.GONOSUMDB}, {Name: "GONOSUMDB", Value: cfg.GONOSUMDB, Changed: cfg.GONOSUMDBChanged},
{Name: "GOOS", Value: cfg.Goos}, {Name: "GOOS", Value: cfg.Goos, Changed: cfg.Goos != runtime.GOOS},
{Name: "GOPATH", Value: cfg.BuildContext.GOPATH}, {Name: "GOPATH", Value: cfg.BuildContext.GOPATH, Changed: cfg.GOPATHChanged},
{Name: "GOPRIVATE", Value: cfg.GOPRIVATE}, {Name: "GOPRIVATE", Value: cfg.GOPRIVATE},
{Name: "GOPROXY", Value: cfg.GOPROXY}, {Name: "GOPROXY", Value: cfg.GOPROXY, Changed: cfg.GOPROXYChanged},
{Name: "GOROOT", Value: cfg.GOROOT}, {Name: "GOROOT", Value: cfg.GOROOT},
{Name: "GOSUMDB", Value: cfg.GOSUMDB}, {Name: "GOSUMDB", Value: cfg.GOSUMDB, Changed: cfg.GOSUMDBChanged},
{Name: "GOTMPDIR", Value: cfg.Getenv("GOTMPDIR")}, {Name: "GOTMPDIR", Value: cfg.Getenv("GOTMPDIR")},
{Name: "GOTOOLCHAIN", Value: cfg.Getenv("GOTOOLCHAIN")}, {Name: "GOTOOLCHAIN", Value: cfg.Getenv("GOTOOLCHAIN")},
{Name: "GOTOOLDIR", Value: build.ToolDir}, {Name: "GOTOOLDIR", Value: build.ToolDir},
{Name: "GOVCS", Value: cfg.GOVCS}, {Name: "GOVCS", Value: cfg.GOVCS},
{Name: "GOVERSION", Value: runtime.Version()}, {Name: "GOVERSION", Value: runtime.Version()},
{Name: "GODEBUG", Value: os.Getenv("GODEBUG")}, {Name: "GODEBUG"},
}
for i := range env {
switch env[i].Name {
case "GO111MODULE":
if env[i].Value != "on" && env[i].Value != "" {
env[i].Changed = true
}
case "GOBIN", "GOEXPERIMENT", "GOFLAGS", "GOINSECURE", "GOPRIVATE", "GOTMPDIR", "GOVCS":
if env[i].Value != "" {
env[i].Changed = true
}
case "GOCACHE":
env[i].Value, env[i].Changed = cache.DefaultDir()
case "GOTOOLCHAIN":
if env[i].Value != "auto" {
env[i].Changed = true
}
case "GODEBUG":
env[i].Value = os.Getenv("GODEBUG")
env[i].Changed = env[i].Value != ""
}
} }
if work.GccgoBin != "" { if work.GccgoBin != "" {
env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoBin}) env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoBin, Changed: true})
} else { } else {
env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoName}) env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoName})
} }
key, val := cfg.GetArchEnv() goarch, val, changed := cfg.GetArchEnv()
if key != "" { if goarch != "" {
env = append(env, cfg.EnvVar{Name: key, Value: val}) env = append(env, cfg.EnvVar{Name: goarch, Value: val, Changed: changed})
} }
cc := cfg.Getenv("CC") cc := cfg.Getenv("CC")
ccChanged := true
if cc == "" { if cc == "" {
ccChanged = false
cc = cfg.DefaultCC(cfg.Goos, cfg.Goarch) cc = cfg.DefaultCC(cfg.Goos, cfg.Goarch)
} }
cxx := cfg.Getenv("CXX") cxx := cfg.Getenv("CXX")
cxxChanged := true
if cxx == "" { if cxx == "" {
cxxChanged = false
cxx = cfg.DefaultCXX(cfg.Goos, cfg.Goarch) cxx = cfg.DefaultCXX(cfg.Goos, cfg.Goarch)
} }
env = append(env, cfg.EnvVar{Name: "AR", Value: envOr("AR", "ar")}) ar, arChanged := cfg.EnvOrAndChanged("AR", "ar")
env = append(env, cfg.EnvVar{Name: "CC", Value: cc}) env = append(env, cfg.EnvVar{Name: "AR", Value: ar, Changed: arChanged})
env = append(env, cfg.EnvVar{Name: "CXX", Value: cxx}) env = append(env, cfg.EnvVar{Name: "CC", Value: cc, Changed: ccChanged})
env = append(env, cfg.EnvVar{Name: "CXX", Value: cxx, Changed: cxxChanged})
if cfg.BuildContext.CgoEnabled { if cfg.BuildContext.CgoEnabled {
env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "1"}) env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "1", Changed: cfg.CGOChanged})
} else { } else {
env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "0"}) env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "0", Changed: cfg.CGOChanged})
} }
return env 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 { func findEnv(env []cfg.EnvVar, name string) string {
for _, e := range env { for _, e := range env {
if e.Name == name { if e.Name == name {
@ -206,7 +230,7 @@ func ExtraEnvVarsCostly() []cfg.EnvVar {
return q return q
} }
return []cfg.EnvVar{ ret := []cfg.EnvVar{
// Note: Update the switch in runEnv below when adding to this list. // Note: Update the switch in runEnv below when adding to this list.
{Name: "CGO_CFLAGS", Value: join(cflags)}, {Name: "CGO_CFLAGS", Value: join(cflags)},
{Name: "CGO_CPPFLAGS", Value: join(cppflags)}, {Name: "CGO_CPPFLAGS", Value: join(cppflags)},
@ -216,6 +240,21 @@ func ExtraEnvVarsCostly() []cfg.EnvVar {
{Name: "PKG_CONFIG", Value: b.PkgconfigCmd()}, {Name: "PKG_CONFIG", Value: b.PkgconfigCmd()},
{Name: "GOGCCFLAGS", Value: join(cmd[3:])}, {Name: "GOGCCFLAGS", Value: join(cmd[3:])},
} }
for i := range ret {
ev := &ret[i]
switch ev.Name {
case "GOGCCFLAGS": // GOGCCFLAGS cannot be modified
case "CGO_CPPFLAGS":
ev.Changed = ev.Value != ""
case "PKG_CONFIG":
ev.Changed = ev.Value != cfg.DefaultPkgConfig
case "CGO_CXXFLAGS", "CGO_CFLAGS", "CGO_FFLAGS", "GGO_LDFLAGS":
ev.Changed = ev.Value != work.DefaultCFlags
}
}
return ret
} }
// argKey returns the KEY part of the arg KEY=VAL, or else arg itself. // argKey returns the KEY part of the arg KEY=VAL, or else arg itself.
@ -297,27 +336,43 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
} }
if len(args) > 0 { if len(args) > 0 {
if *envJson { // Show only the named vars.
if !*envChanged {
if *envJson {
var es []cfg.EnvVar
for _, name := range args {
e := cfg.EnvVar{Name: name, Value: findEnv(env, name)}
es = append(es, e)
}
env = es
} else {
// Print just the values, without names.
for _, name := range args {
fmt.Printf("%s\n", findEnv(env, name))
}
return
}
} else {
// Show only the changed, named vars.
var es []cfg.EnvVar var es []cfg.EnvVar
for _, name := range args { for _, name := range args {
e := cfg.EnvVar{Name: name, Value: findEnv(env, name)} for _, e := range env {
es = append(es, e) if e.Name == name {
} es = append(es, e)
printEnvAsJSON(es) break
} else { }
for _, name := range args { }
fmt.Printf("%s\n", findEnv(env, name))
} }
env = es
} }
return
} }
// print
if *envJson { if *envJson {
printEnvAsJSON(env) printEnvAsJSON(env, *envChanged)
return } else {
PrintEnv(os.Stdout, env, *envChanged)
} }
PrintEnv(os.Stdout, env)
} }
func runEnvW(args []string) { func runEnvW(args []string) {
@ -423,12 +478,15 @@ func checkBuildConfig(add map[string]string, del map[string]bool) error {
} }
// PrintEnv prints the environment variables to w. // PrintEnv prints the environment variables to w.
func PrintEnv(w io.Writer, env []cfg.EnvVar) { func PrintEnv(w io.Writer, env []cfg.EnvVar, onlyChanged bool) {
for _, e := range env { for _, e := range env {
if e.Name != "TERM" { if e.Name != "TERM" {
if runtime.GOOS != "plan9" && bytes.Contains([]byte(e.Value), []byte{0}) { if runtime.GOOS != "plan9" && bytes.Contains([]byte(e.Value), []byte{0}) {
base.Fatalf("go: internal error: encountered null byte in environment variable %s on non-plan9 platform", e.Name) base.Fatalf("go: internal error: encountered null byte in environment variable %s on non-plan9 platform", e.Name)
} }
if onlyChanged && !e.Changed {
continue
}
switch runtime.GOOS { switch runtime.GOOS {
default: default:
fmt.Fprintf(w, "%s=%s\n", e.Name, shellQuote(e.Value)) fmt.Fprintf(w, "%s=%s\n", e.Name, shellQuote(e.Value))
@ -503,12 +561,15 @@ func batchEscape(s string) string {
return b.String() return b.String()
} }
func printEnvAsJSON(env []cfg.EnvVar) { func printEnvAsJSON(env []cfg.EnvVar, onlyChanged bool) {
m := make(map[string]string) m := make(map[string]string)
for _, e := range env { for _, e := range env {
if e.Name == "TERM" { if e.Name == "TERM" {
continue continue
} }
if onlyChanged && !e.Changed {
continue
}
m[e.Name] = e.Value m[e.Name] = e.Value
} }
enc := json.NewEncoder(os.Stdout) enc := json.NewEncoder(os.Stdout)
@ -591,7 +652,7 @@ func checkEnvWrite(key, val string) error {
} }
func readEnvFileLines(mustExist bool) []string { func readEnvFileLines(mustExist bool) []string {
file, err := cfg.EnvFile() file, _, err := cfg.EnvFile()
if file == "" { if file == "" {
if mustExist { if mustExist {
base.Fatalf("go: cannot find go env config: %v", err) base.Fatalf("go: cannot find go env config: %v", err)
@ -655,7 +716,7 @@ func updateEnvFile(add map[string]string, del map[string]bool) {
} }
} }
file, err := cfg.EnvFile() file, _, err := cfg.EnvFile()
if file == "" { if file == "" {
base.Fatalf("go: cannot find go env config: %v", err) base.Fatalf("go: cannot find go env config: %v", err)
} }

View File

@ -56,7 +56,7 @@ func FuzzPrintEnvEscape(f *testing.F) {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
b.WriteString("@echo off\n") b.WriteString("@echo off\n")
} }
PrintEnv(&b, []cfg.EnvVar{{Name: "var", Value: s}}) PrintEnv(&b, []cfg.EnvVar{{Name: "var", Value: s}}, false)
var want string var want string
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
fmt.Fprintf(&b, "echo \"%%var%%\"\n") fmt.Fprintf(&b, "echo \"%%var%%\"\n")

View File

@ -2392,7 +2392,7 @@ func (p *Package) setBuildInfo(ctx context.Context, autoVCS bool) {
appendSetting("GOEXPERIMENT", cfg.RawGOEXPERIMENT) appendSetting("GOEXPERIMENT", cfg.RawGOEXPERIMENT)
} }
appendSetting("GOOS", cfg.BuildContext.GOOS) appendSetting("GOOS", cfg.BuildContext.GOOS)
if key, val := cfg.GetArchEnv(); key != "" && val != "" { if key, val, _ := cfg.GetArchEnv(); key != "" && val != "" {
appendSetting(key, val) appendSetting(key, val)
} }

View File

@ -147,7 +147,8 @@ func GetPackage(modroot, pkgdir string) (*IndexPackage, error) {
// using the index, for instance because the index is disabled, or the package // using the index, for instance because the index is disabled, or the package
// is not in a module. // is not in a module.
func GetModule(modroot string) (*Module, error) { func GetModule(modroot string) (*Module, error) {
if !enabled || cache.DefaultDir() == "off" { dir, _ := cache.DefaultDir()
if !enabled || dir == "off" {
return nil, errDisabled return nil, errDisabled
} }
if modroot == "" { if modroot == "" {

View File

@ -837,7 +837,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
// Read testcache expiration time, if present. // Read testcache expiration time, if present.
// (We implement go clean -testcache by writing an expiration date // (We implement go clean -testcache by writing an expiration date
// instead of searching out and deleting test result cache entries.) // instead of searching out and deleting test result cache entries.)
if dir := cache.DefaultDir(); dir != "off" { if dir, _ := cache.DefaultDir(); dir != "off" {
if data, _ := lockedfile.Read(filepath.Join(dir, "testexpire.txt")); len(data) > 0 && data[len(data)-1] == '\n' { if data, _ := lockedfile.Read(filepath.Join(dir, "testexpire.txt")); len(data) > 0 && data[len(data)-1] == '\n' {
if t, err := strconv.ParseInt(string(data[:len(data)-1]), 10, 64); err == nil { if t, err := strconv.ParseInt(string(data[:len(data)-1]), 10, 64); err == nil {
testCacheExpire = time.Unix(0, t) testCacheExpire = time.Unix(0, t)

View File

@ -46,7 +46,7 @@ import (
"cmd/internal/sys" "cmd/internal/sys"
) )
const defaultCFlags = "-O2 -g" const DefaultCFlags = "-O2 -g"
// actionList returns the list of actions in the dag rooted at root // actionList returns the list of actions in the dag rooted at root
// as visited in a depth-first post-order traversal. // as visited in a depth-first post-order traversal.
@ -337,7 +337,7 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
} }
// GOARM, GOMIPS, etc. // GOARM, GOMIPS, etc.
key, val := cfg.GetArchEnv() key, val, _ := cfg.GetArchEnv()
fmt.Fprintf(h, "%s=%s\n", key, val) fmt.Fprintf(h, "%s=%s\n", key, val)
if cfg.CleanGOEXPERIMENT != "" { if cfg.CleanGOEXPERIMENT != "" {
@ -1419,7 +1419,7 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) {
} }
// GOARM, GOMIPS, etc. // GOARM, GOMIPS, etc.
key, val := cfg.GetArchEnv() key, val, _ := cfg.GetArchEnv()
fmt.Fprintf(h, "%s=%s\n", key, val) fmt.Fprintf(h, "%s=%s\n", key, val)
if cfg.CleanGOEXPERIMENT != "" { if cfg.CleanGOEXPERIMENT != "" {
@ -2460,13 +2460,13 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool {
cmdArgs := str.StringList(compiler, flag) cmdArgs := str.StringList(compiler, flag)
if strings.HasPrefix(flag, "-Wl,") /* linker flag */ { if strings.HasPrefix(flag, "-Wl,") /* linker flag */ {
ldflags, err := buildFlags("LDFLAGS", defaultCFlags, nil, checkLinkerFlags) ldflags, err := buildFlags("LDFLAGS", DefaultCFlags, nil, checkLinkerFlags)
if err != nil { if err != nil {
return false return false
} }
cmdArgs = append(cmdArgs, ldflags...) cmdArgs = append(cmdArgs, ldflags...)
} else { /* compiler flag, add "-c" */ } else { /* compiler flag, add "-c" */
cflags, err := buildFlags("CFLAGS", defaultCFlags, nil, checkCompilerFlags) cflags, err := buildFlags("CFLAGS", DefaultCFlags, nil, checkCompilerFlags)
if err != nil { if err != nil {
return false return false
} }
@ -2707,16 +2707,16 @@ func (b *Builder) CFlags(p *load.Package) (cppflags, cflags, cxxflags, fflags, l
if cppflags, err = buildFlags("CPPFLAGS", "", p.CgoCPPFLAGS, checkCompilerFlags); err != nil { if cppflags, err = buildFlags("CPPFLAGS", "", p.CgoCPPFLAGS, checkCompilerFlags); err != nil {
return return
} }
if cflags, err = buildFlags("CFLAGS", defaultCFlags, p.CgoCFLAGS, checkCompilerFlags); err != nil { if cflags, err = buildFlags("CFLAGS", DefaultCFlags, p.CgoCFLAGS, checkCompilerFlags); err != nil {
return return
} }
if cxxflags, err = buildFlags("CXXFLAGS", defaultCFlags, p.CgoCXXFLAGS, checkCompilerFlags); err != nil { if cxxflags, err = buildFlags("CXXFLAGS", DefaultCFlags, p.CgoCXXFLAGS, checkCompilerFlags); err != nil {
return return
} }
if fflags, err = buildFlags("FFLAGS", defaultCFlags, p.CgoFFLAGS, checkCompilerFlags); err != nil { if fflags, err = buildFlags("FFLAGS", DefaultCFlags, p.CgoFFLAGS, checkCompilerFlags); err != nil {
return return
} }
if ldflags, err = buildFlags("LDFLAGS", defaultCFlags, p.CgoLDFLAGS, checkLinkerFlags); err != nil { if ldflags, err = buildFlags("LDFLAGS", DefaultCFlags, p.CgoLDFLAGS, checkLinkerFlags); err != nil {
return return
} }

View File

@ -114,7 +114,8 @@ func (sh *Shell) moveOrCopyFile(dst, src string, perm fs.FileMode, force bool) e
// Otherwise fall back to standard copy. // Otherwise fall back to standard copy.
// If the source is in the build cache, we need to copy it. // If the source is in the build cache, we need to copy it.
if strings.HasPrefix(src, cache.DefaultDir()) { dir, _ := cache.DefaultDir()
if strings.HasPrefix(src, dir) {
return sh.CopyFile(dst, src, perm, force) return sh.CopyFile(dst, src, perm, force)
} }

View File

@ -0,0 +1,45 @@
# Test query for non-defaults in the env
env GOTOOLCHAIN=local
env GOSUMDB=nodefault
env GOPROXY=nodefault
env GO111MODULE=auto
env CGO_CFLAGS=nodefault
env CGO_CPPFLAGS=nodefault
go env -changed
# linux output like GOTOOLCHAIN='local'
# windows output like GOTOOLCHAIN=local
stdout 'GOTOOLCHAIN=''?local''?'
stdout 'GOSUMDB=''?nodefault''?'
stdout 'GOPROXY=''?nodefault''?'
stdout 'GO111MODULE=''?auto''?'
stdout 'CGO_CFLAGS=''?nodefault''?'
stdout 'CGO_CPPFLAGS=''?nodefault''?'
go env -changed -json
stdout '"GOTOOLCHAIN": "local"'
stdout '"GOSUMDB": "nodefault"'
stdout '"GOPROXY": "nodefault"'
stdout '"GO111MODULE": "auto"'
stdout '"CGO_CFLAGS": "nodefault"'
stdout '"CGO_CPPFLAGS": "nodefault"'
[GOOS:windows] env GOOS=linux
[!GOOS:windows] env GOOS=windows
[GOARCH:amd64] env GOARCH=arm64
[!GOARCH:amd64] env GOARCH=amd64
go env -changed GOOS
[GOOS:windows] stdout 'set GOOS=linux'
[!GOOS:windows] stdout 'GOOS=''windows'''
go env -changed GOARCH
[GOARCH:amd64] stdout 'set GOARCH=arm64|GOARCH=''arm64'''
[!GOARCH:amd64] stdout 'set GOARCH=amd64|GOARCH=''amd64'''
go env -changed -json GOOS
[GOOS:windows] stdout '"GOOS": "linux"'
[!GOOS:windows] stdout '"GOOS": "windows"'
go env -changed -json GOARCH
[GOARCH:amd64] stdout '"GOARCH": "arm64"'
[!GOARCH:amd64] stdout '"GOARCH": "amd64"'

View File

@ -286,6 +286,7 @@ func (ctxt *Context) SrcDirs() []string {
// if set, or else the compiled code's GOARCH, GOOS, and GOROOT. // if set, or else the compiled code's GOARCH, GOOS, and GOROOT.
var Default Context = defaultContext() var Default Context = defaultContext()
// Keep consistent with cmd/go/internal/cfg.defaultGOPATH.
func defaultGOPATH() string { func defaultGOPATH() string {
env := "HOME" env := "HOME"
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {