]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go: add -changed to query for non-defaults in the env
authorqiulaidongfeng <2645477756@qq.com>
Thu, 9 May 2024 22:11:00 +0000 (22:11 +0000)
committerMichael Matloob <matloob@golang.org>
Thu, 16 May 2024 14:34:32 +0000 (14:34 +0000)
Fixes #34208

Change-Id: I8ec2d96262dcd7cbf870f6173690143c54190722
GitHub-Last-Rev: 6543df4784cff1ba5751dc9885ef502e69679118
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>
16 files changed:
doc/next/3-tools.md
src/cmd/go/alldocs.go
src/cmd/go/go_test.go
src/cmd/go/internal/bug/bug.go
src/cmd/go/internal/cache/default.go
src/cmd/go/internal/cfg/cfg.go
src/cmd/go/internal/clean/clean.go
src/cmd/go/internal/envcmd/env.go
src/cmd/go/internal/envcmd/env_test.go
src/cmd/go/internal/load/pkg.go
src/cmd/go/internal/modindex/read.go
src/cmd/go/internal/test/test.go
src/cmd/go/internal/work/exec.go
src/cmd/go/internal/work/shell.go
src/cmd/go/testdata/script/env_changed.txt [new file with mode: 0644]
src/go/build/build.go

index c052f3b084009a98efb1101426c625da55ac9c30..4112fb61ac831683d133f818f2fd77cdbb65249a 100644 (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
 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}
 
 The `go vet` subcommand now includes the
index ad34b8dfcc3ecea99d956f3f3b612b5575d4d8a3..9263be5a6bcc401ef497e091c98261f3574fd274 100644 (file)
 //
 // Usage:
 //
-//     go env [-json] [-u] [-w] [var ...]
+//     go env [-json] [-changed] [-u] [-w] [var ...]
 //
 // Env prints Go environment information.
 //
 // form NAME=VALUE and changes the default settings
 // 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'.
 //
 // # Update packages to use new APIs
index a5ce22c0c363aebe11cd508f4079bb197f16e051..3a3383b271ce8a369b89d1d9ffb53522cf627866 100644 (file)
@@ -196,7 +196,7 @@ func TestMain(m *testing.M) {
                defer removeAll(testTmpDir)
        }
 
-       testGOCACHE = cache.DefaultDir()
+       testGOCACHE, _ = cache.DefaultDir()
        if testenv.HasGoBuild() {
                testBin = filepath.Join(testTmpDir, "testbin")
                if err := os.Mkdir(testBin, 0777); err != nil {
index ed1813605e5de4f2c55f9789cadb18862be94ec3..d3f9065d3da4e5ce15574b14b48ac6a5d17f076f 100644 (file)
@@ -106,7 +106,7 @@ func printGoEnv(w io.Writer) {
        env := envcmd.MkEnv()
        env = append(env, envcmd.ExtraEnvVars()...)
        env = append(env, envcmd.ExtraEnvVarsCostly()...)
-       envcmd.PrintEnv(w, env)
+       envcmd.PrintEnv(w, env, false)
 }
 
 func printGoDetails(w io.Writer) {
index b5650eac669b46926769c9519dec7f7476fb6e2d..5430d9651ec1d7d117d5330e7f11e95d52810348 100644 (file)
@@ -39,7 +39,7 @@ See golang.org to learn more about Go.
 // initDefaultCache does the work of finding the default cache
 // the first time Default is called.
 func initDefaultCache() {
-       dir := DefaultDir()
+       dir, _ := DefaultDir()
        if dir == "off" {
                if defaultDirErr != nil {
                        base.Fatalf("build cache is required, but could not be located: %v", defaultDirErr)
@@ -67,14 +67,16 @@ func initDefaultCache() {
 }
 
 var (
-       defaultDirOnce sync.Once
-       defaultDir     string
-       defaultDirErr  error
+       defaultDirOnce    sync.Once
+       defaultDir        string
+       defaultDirChanged bool // effective value differs from $GOCACHE
+       defaultDirErr     error
 )
 
 // DefaultDir returns the effective GOCACHE setting.
-// It returns "off" if the cache is disabled.
-func DefaultDir() string {
+// It returns "off" if the cache is disabled,
+// 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
        // initDefaultCache. cmd/go/main.go explicitly sets GOCACHE so that
        // subprocesses will inherit it, but that means initDefaultCache can't
@@ -82,10 +84,11 @@ func DefaultDir() string {
 
        defaultDirOnce.Do(func() {
                defaultDir = cfg.Getenv("GOCACHE")
-               if filepath.IsAbs(defaultDir) || defaultDir == "off" {
-                       return
-               }
                if defaultDir != "" {
+                       defaultDirChanged = true
+                       if filepath.IsAbs(defaultDir) || defaultDir == "off" {
+                               return
+                       }
                        defaultDir = "off"
                        defaultDirErr = fmt.Errorf("GOCACHE is not an absolute path")
                        return
@@ -95,11 +98,12 @@ func DefaultDir() string {
                dir, err := os.UserCacheDir()
                if err != nil {
                        defaultDir = "off"
+                       defaultDirChanged = true
                        defaultDirErr = fmt.Errorf("GOCACHE is not defined and %v", err)
                        return
                }
                defaultDir = filepath.Join(dir, "go-build")
        })
 
-       return defaultDir
+       return defaultDir, defaultDirChanged
 }
index afb595a0c6aa4cb48a8172840e976b5597d5b6da..002d0006ed41fc6d1164aa7e529b885f330a586a 100644 (file)
@@ -101,7 +101,9 @@ var (
 
        // GoPathError is set when GOPATH is not set. it contains an
        // explanation why GOPATH is unset.
-       GoPathError string
+       GoPathError   string
+       GOPATHChanged bool
+       CGOChanged    bool
 )
 
 func defaultContext() build.Context {
@@ -111,7 +113,7 @@ func defaultContext() build.Context {
 
        // Override defaults computed in go/build with defaults
        // from go environment configuration file, if known.
-       ctxt.GOPATH = envOr("GOPATH", gopath(ctxt))
+       ctxt.GOPATH, GOPATHChanged = EnvOrAndChanged("GOPATH", gopath(ctxt))
        ctxt.GOOS = Goos
        ctxt.GOARCH = Goarch
 
@@ -125,14 +127,16 @@ func defaultContext() build.Context {
        ctxt.ToolTags = save
 
        // 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.
+       //  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
+       // We need to run steps 2 and 3 to determine what the default value
+       // of CgoEnabled would be for computing CGOChanged.
+       defaultCgoEnabled := ctxt.CgoEnabled
+       if ctxt.GOOS != runtime.GOOS || ctxt.GOARCH != runtime.GOARCH {
+               defaultCgoEnabled = false
        } else {
                // Use built-in default cgo setting for GOOS/GOARCH.
                // Note that ctxt.GOOS/GOARCH are derived from the preference list
@@ -159,11 +163,16 @@ func defaultContext() build.Context {
                        if os.Getenv("CC") == "" {
                                cc := DefaultCC(ctxt.GOOS, ctxt.GOARCH)
                                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) {
                return fsys.Open(path)
@@ -262,8 +271,9 @@ func init() {
 
 // An EnvVar is an environment variable Name=Value.
 type EnvVar struct {
-       Name  string
-       Value string
+       Name    string
+       Value   string
+       Changed bool // effective Value differs from default
 }
 
 // OrigEnv is the original environment of the program at startup.
@@ -279,27 +289,28 @@ var envCache struct {
        m    map[string]string
 }
 
-// EnvFile returns the name of the Go environment configuration file.
-func EnvFile() (string, error) {
+// EnvFile returns the name of the Go environment configuration file,
+// and reports whether the effective value differs from the default.
+func EnvFile() (string, bool, error) {
        if file := os.Getenv("GOENV"); file != "" {
                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()
        if err != nil {
-               return "", err
+               return "", false, err
        }
        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() {
        envCache.m = make(map[string]string)
-       if file, _ := EnvFile(); file != "" {
+       if file, _, _ := EnvFile(); file != "" {
                readEnvFile(file, "user")
        }
        goroot := findGOROOT(envCache.m["GOROOT"])
@@ -397,57 +408,67 @@ var (
        GOROOTpkg string
        GOROOTsrc string
 
-       GOBIN      = Getenv("GOBIN")
-       GOMODCACHE = envOr("GOMODCACHE", gopathDir("pkg/mod"))
+       GOBIN                         = Getenv("GOBIN")
+       GOMODCACHE, GOMODCACHEChanged = EnvOrAndChanged("GOMODCACHE", gopathDir("pkg/mod"))
 
        // Used in envcmd.MkEnv and build ID computations.
-       GOARM     = envOr("GOARM", fmt.Sprint(buildcfg.GOARM))
-       GOARM64   = envOr("GOARM64", fmt.Sprint(buildcfg.GOARM64))
-       GO386     = envOr("GO386", buildcfg.GO386)
-       GOAMD64   = envOr("GOAMD64", fmt.Sprintf("%s%d", "v", buildcfg.GOAMD64))
-       GOMIPS    = envOr("GOMIPS", buildcfg.GOMIPS)
-       GOMIPS64  = envOr("GOMIPS64", buildcfg.GOMIPS64)
-       GOPPC64   = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", buildcfg.GOPPC64))
-       GORISCV64 = envOr("GORISCV64", fmt.Sprintf("rva%du64", buildcfg.GORISCV64))
-       GOWASM    = envOr("GOWASM", fmt.Sprint(buildcfg.GOWASM))
-
-       GOPROXY    = envOr("GOPROXY", "")
-       GOSUMDB    = envOr("GOSUMDB", "")
-       GOPRIVATE  = Getenv("GOPRIVATE")
-       GONOPROXY  = envOr("GONOPROXY", GOPRIVATE)
-       GONOSUMDB  = envOr("GONOSUMDB", GOPRIVATE)
-       GOINSECURE = Getenv("GOINSECURE")
-       GOVCS      = Getenv("GOVCS")
+       GOARM64, goARM64Changed     = EnvOrAndChanged("GOARM64", fmt.Sprint(buildcfg.GOARM64))
+       GOARM, goARMChanged         = EnvOrAndChanged("GOARM", fmt.Sprint(buildcfg.GOARM))
+       GO386, go386Changed         = EnvOrAndChanged("GO386", buildcfg.GO386)
+       GOAMD64, goAMD64Changed     = EnvOrAndChanged("GOAMD64", fmt.Sprintf("%s%d", "v", buildcfg.GOAMD64))
+       GOMIPS, goMIPSChanged       = EnvOrAndChanged("GOMIPS", buildcfg.GOMIPS)
+       GOMIPS64, goMIPS64Changed   = EnvOrAndChanged("GOMIPS64", buildcfg.GOMIPS64)
+       GOPPC64, goPPC64Changed     = EnvOrAndChanged("GOPPC64", fmt.Sprintf("%s%d", "power", buildcfg.GOPPC64))
+       GORISCV64, goRISCV64Changed = EnvOrAndChanged("GORISCV64", fmt.Sprintf("rva%du64", buildcfg.GORISCV64))
+       GOWASM, goWASMChanged       = EnvOrAndChanged("GOWASM", fmt.Sprint(buildcfg.GOWASM))
+
+       GOPROXY, GOPROXYChanged     = EnvOrAndChanged("GOPROXY", "")
+       GOSUMDB, GOSUMDBChanged     = EnvOrAndChanged("GOSUMDB", "")
+       GOPRIVATE                   = Getenv("GOPRIVATE")
+       GONOPROXY, GONOPROXYChanged = EnvOrAndChanged("GONOPROXY", GOPRIVATE)
+       GONOSUMDB, GONOSUMDBChanged = EnvOrAndChanged("GONOSUMDB", GOPRIVATE)
+       GOINSECURE                  = Getenv("GOINSECURE")
+       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")
 
 // 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) {
+func GetArchEnv() (key, val string, changed bool) {
        switch Goarch {
        case "arm":
-               return "GOARM", GOARM
+               return "GOARM", GOARM, goARMChanged
        case "arm64":
-               return "GOARM64", GOARM64
+               return "GOARM64", GOARM64, goARM64Changed
        case "386":
-               return "GO386", GO386
+               return "GO386", GO386, go386Changed
        case "amd64":
-               return "GOAMD64", GOAMD64
+               return "GOAMD64", GOAMD64, goAMD64Changed
        case "mips", "mipsle":
-               return "GOMIPS", GOMIPS
+               return "GOMIPS", GOMIPS, goMIPSChanged
        case "mips64", "mips64le":
-               return "GOMIPS64", GOMIPS64
+               return "GOMIPS64", GOMIPS64, goMIPS64Changed
        case "ppc64", "ppc64le":
-               return "GOPPC64", GOPPC64
+               return "GOPPC64", GOPPC64, goPPC64Changed
        case "riscv64":
-               return "GORISCV64", GORISCV64
+               return "GORISCV64", GORISCV64, goRISCV64Changed
        case "wasm":
-               return "GOWASM", GOWASM
+               return "GOWASM", GOWASM, goWASMChanged
        }
-       return "", ""
+       return "", "", false
 }
 
 // envOr returns Getenv(key) if set, or else def.
@@ -565,6 +586,7 @@ func gopathDir(rel string) string {
        return filepath.Join(list[0], rel)
 }
 
+// Keep consistent with go/build.defaultGOPATH.
 func gopath(ctxt build.Context) string {
        if len(ctxt.GOPATH) > 0 {
                return ctxt.GOPATH
index b021b784dada5cd0ea76b9d2d80170aa46116c92..de2ef9dcb957bb8bf55cde1269c908c373d57abd 100644 (file)
@@ -153,7 +153,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) {
        sh := work.NewShell("", fmt.Print)
 
        if cleanCache {
-               dir := cache.DefaultDir()
+               dir, _ := cache.DefaultDir()
                if dir != "off" {
                        // Remove the cache subdirectories but not the top cache directory.
                        // 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,
                // we write a file to the cache indicating that all test results from before
                // right now are to be ignored.
-               dir := cache.DefaultDir()
+               dir, _ := cache.DefaultDir()
                if dir != "off" {
                        f, err := lockedfile.Edit(filepath.Join(dir, "testexpire.txt"))
                        if err == nil {
index bff3fe5d55be0856b8cdf2fa03a6a62333f19eec..b3838a75e253b1f2534842bd7de84a2cc3dce8fd 100644 (file)
@@ -32,7 +32,7 @@ import (
 )
 
 var CmdEnv = &base.Command{
-       UsageLine: "go env [-json] [-u] [-w] [var ...]",
+       UsageLine: "go env [-json] [-changed] [-u] [-w] [var ...]",
        Short:     "print Go environment information",
        Long: `
 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
 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'.
        `,
 }
@@ -64,19 +68,20 @@ func init() {
 }
 
 var (
-       envJson = CmdEnv.Flag.Bool("json", false, "")
-       envU    = CmdEnv.Flag.Bool("u", false, "")
-       envW    = CmdEnv.Flag.Bool("w", false, "")
+       envJson    = CmdEnv.Flag.Bool("json", false, "")
+       envU       = CmdEnv.Flag.Bool("u", false, "")
+       envW       = CmdEnv.Flag.Bool("w", false, "")
+       envChanged = CmdEnv.Flag.Bool("changed", false, "")
 )
 
 func MkEnv() []cfg.EnvVar {
-       envFile, _ := cfg.EnvFile()
+       envFile, envFileChanged, _ := cfg.EnvFile()
        env := []cfg.EnvVar{
                {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: "GOCACHE", Value: cache.DefaultDir()},
-               {Name: "GOENV", Value: envFile},
+               {Name: "GOCACHE"},
+               {Name: "GOENV", Value: envFile, Changed: envFileChanged},
                {Name: "GOEXE", Value: cfg.ExeSuffix},
 
                // List the raw value of GOEXPERIMENT, not the cleaned one.
@@ -90,63 +95,82 @@ func MkEnv() []cfg.EnvVar {
                {Name: "GOHOSTARCH", Value: runtime.GOARCH},
                {Name: "GOHOSTOS", Value: runtime.GOOS},
                {Name: "GOINSECURE", Value: cfg.GOINSECURE},
-               {Name: "GOMODCACHE", Value: cfg.GOMODCACHE},
-               {Name: "GONOPROXY", Value: cfg.GONOPROXY},
-               {Name: "GONOSUMDB", Value: cfg.GONOSUMDB},
-               {Name: "GOOS", Value: cfg.Goos},
-               {Name: "GOPATH", Value: cfg.BuildContext.GOPATH},
+               {Name: "GOMODCACHE", Value: cfg.GOMODCACHE, Changed: cfg.GOMODCACHEChanged},
+               {Name: "GONOPROXY", Value: cfg.GONOPROXY, Changed: cfg.GONOPROXYChanged},
+               {Name: "GONOSUMDB", Value: cfg.GONOSUMDB, Changed: cfg.GONOSUMDBChanged},
+               {Name: "GOOS", Value: cfg.Goos, Changed: cfg.Goos != runtime.GOOS},
+               {Name: "GOPATH", Value: cfg.BuildContext.GOPATH, Changed: cfg.GOPATHChanged},
                {Name: "GOPRIVATE", Value: cfg.GOPRIVATE},
-               {Name: "GOPROXY", Value: cfg.GOPROXY},
+               {Name: "GOPROXY", Value: cfg.GOPROXY, Changed: cfg.GOPROXYChanged},
                {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: "GOTOOLCHAIN", Value: cfg.Getenv("GOTOOLCHAIN")},
                {Name: "GOTOOLDIR", Value: build.ToolDir},
                {Name: "GOVCS", Value: cfg.GOVCS},
                {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 != "" {
-               env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoBin})
+               env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoBin, Changed: true})
        } else {
                env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoName})
        }
 
-       key, val := cfg.GetArchEnv()
-       if key != "" {
-               env = append(env, cfg.EnvVar{Name: key, Value: val})
+       goarch, val, changed := cfg.GetArchEnv()
+       if goarch != "" {
+               env = append(env, cfg.EnvVar{Name: goarch, Value: val, Changed: changed})
        }
 
        cc := cfg.Getenv("CC")
+       ccChanged := true
        if cc == "" {
+               ccChanged = false
                cc = cfg.DefaultCC(cfg.Goos, cfg.Goarch)
        }
        cxx := cfg.Getenv("CXX")
+       cxxChanged := true
        if cxx == "" {
+               cxxChanged = false
                cxx = cfg.DefaultCXX(cfg.Goos, cfg.Goarch)
        }
-       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})
+       ar, arChanged := cfg.EnvOrAndChanged("AR", "ar")
+       env = append(env, cfg.EnvVar{Name: "AR", Value: ar, Changed: arChanged})
+       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 {
-               env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "1"})
+               env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "1", Changed: cfg.CGOChanged})
        } 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
 }
 
-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 {
@@ -206,7 +230,7 @@ func ExtraEnvVarsCostly() []cfg.EnvVar {
                return q
        }
 
-       return []cfg.EnvVar{
+       ret := []cfg.EnvVar{
                // Note: Update the switch in runEnv below when adding to this list.
                {Name: "CGO_CFLAGS", Value: join(cflags)},
                {Name: "CGO_CPPFLAGS", Value: join(cppflags)},
@@ -216,6 +240,21 @@ func ExtraEnvVarsCostly() []cfg.EnvVar {
                {Name: "PKG_CONFIG", Value: b.PkgconfigCmd()},
                {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.
@@ -297,27 +336,43 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
        }
 
        if len(args) > 0 {
-               if *envJson {
-                       var es []cfg.EnvVar
-                       for _, name := range args {
-                               e := cfg.EnvVar{Name: name, Value: findEnv(env, name)}
-                               es = append(es, e)
+               // 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
                        }
-                       printEnvAsJSON(es)
                } else {
+                       // Show only the changed, named vars.
+                       var es []cfg.EnvVar
                        for _, name := range args {
-                               fmt.Printf("%s\n", findEnv(env, name))
+                               for _, e := range env {
+                                       if e.Name == name {
+                                               es = append(es, e)
+                                               break
+                                       }
+                               }
                        }
+                       env = es
                }
-               return
        }
 
+       // print
        if *envJson {
-               printEnvAsJSON(env)
-               return
+               printEnvAsJSON(env, *envChanged)
+       } else {
+               PrintEnv(os.Stdout, env, *envChanged)
        }
-
-       PrintEnv(os.Stdout, env)
 }
 
 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.
-func PrintEnv(w io.Writer, env []cfg.EnvVar) {
+func PrintEnv(w io.Writer, env []cfg.EnvVar, onlyChanged bool) {
        for _, e := range env {
                if e.Name != "TERM" {
                        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)
                        }
+                       if onlyChanged && !e.Changed {
+                               continue
+                       }
                        switch runtime.GOOS {
                        default:
                                fmt.Fprintf(w, "%s=%s\n", e.Name, shellQuote(e.Value))
@@ -503,12 +561,15 @@ func batchEscape(s string) string {
        return b.String()
 }
 
-func printEnvAsJSON(env []cfg.EnvVar) {
+func printEnvAsJSON(env []cfg.EnvVar, onlyChanged bool) {
        m := make(map[string]string)
        for _, e := range env {
                if e.Name == "TERM" {
                        continue
                }
+               if onlyChanged && !e.Changed {
+                       continue
+               }
                m[e.Name] = e.Value
        }
        enc := json.NewEncoder(os.Stdout)
@@ -591,7 +652,7 @@ func checkEnvWrite(key, val string) error {
 }
 
 func readEnvFileLines(mustExist bool) []string {
-       file, err := cfg.EnvFile()
+       file, _, err := cfg.EnvFile()
        if file == "" {
                if mustExist {
                        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 == "" {
                base.Fatalf("go: cannot find go env config: %v", err)
        }
index 7419cf3fc20e73db8b565991566e4452edf9d3d4..2f6470b87165db9c9aba9626dcbe765dd76837a9 100644 (file)
@@ -56,7 +56,7 @@ func FuzzPrintEnvEscape(f *testing.F) {
                if runtime.GOOS == "windows" {
                        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
                if runtime.GOOS == "windows" {
                        fmt.Fprintf(&b, "echo \"%%var%%\"\n")
index 58acd4dc3401a9f10e013faa6f658066d88de099..4bf2f381dd04ea57bb81ef78fb3c6aa25276d6f4 100644 (file)
@@ -2392,7 +2392,7 @@ func (p *Package) setBuildInfo(ctx context.Context, autoVCS bool) {
                appendSetting("GOEXPERIMENT", cfg.RawGOEXPERIMENT)
        }
        appendSetting("GOOS", cfg.BuildContext.GOOS)
-       if key, val := cfg.GetArchEnv(); key != "" && val != "" {
+       if key, val, _ := cfg.GetArchEnv(); key != "" && val != "" {
                appendSetting(key, val)
        }
 
index bda3fb4338c49ee73d89976d54ca0ce1ec54dcb1..9d8c48f2b046237715c3833c9ea55710f2d55174 100644 (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
 // is not in a module.
 func GetModule(modroot string) (*Module, error) {
-       if !enabled || cache.DefaultDir() == "off" {
+       dir, _ := cache.DefaultDir()
+       if !enabled || dir == "off" {
                return nil, errDisabled
        }
        if modroot == "" {
index ac9d2721f5e78155ab8a941b013d79796d38d5e8..a13070a91e7b0458948d6e0f46c7bcd81885fc60 100644 (file)
@@ -837,7 +837,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
        // Read testcache expiration time, if present.
        // (We implement go clean -testcache by writing an expiration date
        // 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 t, err := strconv.ParseInt(string(data[:len(data)-1]), 10, 64); err == nil {
                                testCacheExpire = time.Unix(0, t)
index 04459d0990a6dd1b2e77b787bfdf9ae3746bdaf3..90c61a9c30754588d38a656f55b1f2597b496bc6 100644 (file)
@@ -46,7 +46,7 @@ import (
        "cmd/internal/sys"
 )
 
-const defaultCFlags = "-O2 -g"
+const DefaultCFlags = "-O2 -g"
 
 // actionList returns the list of actions in the dag rooted at root
 // as visited in a depth-first post-order traversal.
@@ -337,7 +337,7 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
                }
 
                // GOARM, GOMIPS, etc.
-               key, val := cfg.GetArchEnv()
+               key, val, _ := cfg.GetArchEnv()
                fmt.Fprintf(h, "%s=%s\n", key, val)
 
                if cfg.CleanGOEXPERIMENT != "" {
@@ -1419,7 +1419,7 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) {
                }
 
                // GOARM, GOMIPS, etc.
-               key, val := cfg.GetArchEnv()
+               key, val, _ := cfg.GetArchEnv()
                fmt.Fprintf(h, "%s=%s\n", key, val)
 
                if cfg.CleanGOEXPERIMENT != "" {
@@ -2460,13 +2460,13 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool {
 
        cmdArgs := str.StringList(compiler, 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 {
                        return false
                }
                cmdArgs = append(cmdArgs, ldflags...)
        } else { /* compiler flag, add "-c" */
-               cflags, err := buildFlags("CFLAGS", defaultCFlags, nil, checkCompilerFlags)
+               cflags, err := buildFlags("CFLAGS", DefaultCFlags, nil, checkCompilerFlags)
                if err != nil {
                        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 {
                return
        }
-       if cflags, err = buildFlags("CFLAGS", defaultCFlags, p.CgoCFLAGS, checkCompilerFlags); err != nil {
+       if cflags, err = buildFlags("CFLAGS", DefaultCFlags, p.CgoCFLAGS, checkCompilerFlags); err != nil {
                return
        }
-       if cxxflags, err = buildFlags("CXXFLAGS", defaultCFlags, p.CgoCXXFLAGS, checkCompilerFlags); err != nil {
+       if cxxflags, err = buildFlags("CXXFLAGS", DefaultCFlags, p.CgoCXXFLAGS, checkCompilerFlags); err != nil {
                return
        }
-       if fflags, err = buildFlags("FFLAGS", defaultCFlags, p.CgoFFLAGS, checkCompilerFlags); err != nil {
+       if fflags, err = buildFlags("FFLAGS", DefaultCFlags, p.CgoFFLAGS, checkCompilerFlags); err != nil {
                return
        }
-       if ldflags, err = buildFlags("LDFLAGS", defaultCFlags, p.CgoLDFLAGS, checkLinkerFlags); err != nil {
+       if ldflags, err = buildFlags("LDFLAGS", DefaultCFlags, p.CgoLDFLAGS, checkLinkerFlags); err != nil {
                return
        }
 
index 60817d9c3b3e9b8c79878e586485b0c76f6f9c21..1fac8e3a45e8e23d1a3abab2d92c4724708a2bd8 100644 (file)
@@ -114,7 +114,8 @@ func (sh *Shell) moveOrCopyFile(dst, src string, perm fs.FileMode, force bool) e
        // Otherwise fall back to standard copy.
 
        // 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)
        }
 
diff --git a/src/cmd/go/testdata/script/env_changed.txt b/src/cmd/go/testdata/script/env_changed.txt
new file mode 100644 (file)
index 0000000..7b7b154
--- /dev/null
@@ -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"'
index 9ce3700dc4bcbe310320b64d7badc3c20b1cb505..43c74cb99adf8745108ea3efd3e1dc4e2f93ed06 100644 (file)
@@ -286,6 +286,7 @@ func (ctxt *Context) SrcDirs() []string {
 // if set, or else the compiled code's GOARCH, GOOS, and GOROOT.
 var Default Context = defaultContext()
 
+// Keep consistent with cmd/go/internal/cfg.defaultGOPATH.
 func defaultGOPATH() string {
        env := "HOME"
        if runtime.GOOS == "windows" {