Commit f0e97546 authored by Russ Cox's avatar Russ Cox

cmd/go: add env -w and env -u to set and unset default env vars

Setting environment variables for go command configuration
is too difficult and system-specific. This CL adds go env -w,
to change the default settings more easily, in a portable way.
It also adds go env -u, to unset those changes.

See https://golang.org/design/30411-env for details.

Fixes #30411.

Change-Id: I36e83f55b666459f8f7f482432a4a6ee015da71d
Reviewed-on: https://go-review.googlesource.com/c/go/+/171137
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBryan C. Mills <bcmills@google.com>
parent e40dffe5
...@@ -368,7 +368,7 @@ ...@@ -368,7 +368,7 @@
// //
// Usage: // Usage:
// //
// go env [-json] [var ...] // go env [-json] [-u] [-w] [var ...]
// //
// Env prints Go environment information. // Env prints Go environment information.
// //
...@@ -380,6 +380,14 @@ ...@@ -380,6 +380,14 @@
// The -json flag prints the environment in JSON format // The -json flag prints the environment in JSON format
// instead of as a shell script. // 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'. // For more about environment variables, see 'go help environment'.
// //
// //
...@@ -1485,10 +1493,17 @@ ...@@ -1485,10 +1493,17 @@
// //
// Environment variables // Environment variables
// //
// The go command, and the tools it invokes, examine a few different // The go command and the tools it invokes consult environment variables
// environment variables. For many of these, you can see the default // for configuration. If an environment variable is unset, the go command
// value of on your system by running 'go env NAME', where NAME is the // uses a sensible default setting. To see the effective setting of the
// name of the variable. // 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: // General-purpose environment variables:
// //
...@@ -1502,10 +1517,15 @@ ...@@ -1502,10 +1517,15 @@
// GOCACHE // GOCACHE
// The directory where the go command will store cached // The directory where the go command will store cached
// information for reuse in future builds. // information for reuse in future builds.
// GOENV
// The location of the Go environment configuration file.
// Cannot be set using 'go env -w'.
// GOFLAGS // GOFLAGS
// A space-separated list of -flag=value settings to apply // A space-separated list of -flag=value settings to apply
// to go commands by default, when the given flag is known by // 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. // are applied after this list and therefore override it.
// GOOS // GOOS
// The operating system for which to compile code. // The operating system for which to compile code.
...@@ -1514,21 +1534,18 @@ ...@@ -1514,21 +1534,18 @@
// For more details see: 'go help gopath'. // For more details see: 'go help gopath'.
// GOPROXY // GOPROXY
// URL of Go module proxy. See 'go help 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 // GOROOT
// The root of the go tree. // The root of the go tree.
// GOTMPDIR // GOTMPDIR
// The directory where the go command will write // The directory where the go command will write
// temporary source files, packages, and binaries. // 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: // 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 // CC
// The command to use to compile C code. // The command to use to compile C code.
// CGO_ENABLED // CGO_ENABLED
...@@ -1558,12 +1575,10 @@ ...@@ -1558,12 +1575,10 @@
// but for the linker. // but for the linker.
// CXX // CXX
// The command to use to compile C++ code. // The command to use to compile C++ code.
// FC
// The command to use to compile Fortran code.
// PKG_CONFIG // PKG_CONFIG
// Path to pkg-config tool. // 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: // Architecture-specific environment variables:
// //
...@@ -1598,9 +1613,11 @@ ...@@ -1598,9 +1613,11 @@
// when using -linkmode=auto with code that uses cgo. // when using -linkmode=auto with code that uses cgo.
// Set to 0 to disable external linking mode, 1 to enable it. // Set to 0 to disable external linking mode, 1 to enable it.
// GIT_ALLOW_PROTOCOL // GIT_ALLOW_PROTOCOL
// Defined by Git. A colon-separated list of schemes that are allowed to be used // Defined by Git. A colon-separated list of schemes that are allowed
// with git fetch/clone. If set, any scheme not explicitly mentioned will be // to be used with git fetch/clone. If set, any scheme not explicitly
// considered insecure by 'go get'. // 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: // Additional information available from 'go env' but not read from the environment:
// //
......
...@@ -6,8 +6,6 @@ package main_test ...@@ -6,8 +6,6 @@ package main_test
import ( import (
"bytes" "bytes"
"cmd/go/internal/cache"
"cmd/internal/sys"
"context" "context"
"debug/elf" "debug/elf"
"debug/macho" "debug/macho"
...@@ -28,6 +26,10 @@ import ( ...@@ -28,6 +26,10 @@ import (
"strings" "strings"
"testing" "testing"
"time" "time"
"cmd/go/internal/cache"
"cmd/go/internal/cfg"
"cmd/internal/sys"
) )
var ( var (
...@@ -119,6 +121,8 @@ var testCtx = context.Background() ...@@ -119,6 +121,8 @@ var testCtx = context.Background()
// The TestMain function creates a go command for testing purposes and // The TestMain function creates a go command for testing purposes and
// deletes it after the tests have been run. // deletes it after the tests have been run.
func TestMain(m *testing.M) { 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") != "" { if os.Getenv("GO_GCFLAGS") != "" {
fmt.Fprintf(os.Stderr, "testing: warning: no tests to run\n") // magic string for cmd/go 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") fmt.Printf("cmd/go test is not compatible with $GO_GCFLAGS being set\n")
...@@ -256,6 +260,7 @@ func TestMain(m *testing.M) { ...@@ -256,6 +260,7 @@ func TestMain(m *testing.M) {
} }
} }
// Don't let these environment variables confuse the test. // Don't let these environment variables confuse the test.
os.Setenv("GOENV", "off")
os.Unsetenv("GOBIN") os.Unsetenv("GOBIN")
os.Unsetenv("GOPATH") os.Unsetenv("GOPATH")
os.Unsetenv("GIT_ALLOW_PROTOCOL") os.Unsetenv("GIT_ALLOW_PROTOCOL")
...@@ -264,7 +269,7 @@ func TestMain(m *testing.M) { ...@@ -264,7 +269,7 @@ func TestMain(m *testing.M) {
// Setting HOME to a non-existent directory will break // Setting HOME to a non-existent directory will break
// those systems. Disable ccache and use real compiler. Issue 17668. // those systems. Disable ccache and use real compiler. Issue 17668.
os.Setenv("CCACHE_DISABLE", "1") os.Setenv("CCACHE_DISABLE", "1")
if os.Getenv("GOCACHE") == "" { if cfg.Getenv("GOCACHE") == "" {
os.Setenv("GOCACHE", testGOCACHE) // because $HOME is gone os.Setenv("GOCACHE", testGOCACHE) // because $HOME is gone
} }
...@@ -5258,7 +5263,7 @@ func TestCacheVet(t *testing.T) { ...@@ -5258,7 +5263,7 @@ func TestCacheVet(t *testing.T) {
if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
t.Skip("GODEBUG gocacheverify") t.Skip("GODEBUG gocacheverify")
} }
if os.Getenv("GOCACHE") == "off" { if cfg.Getenv("GOCACHE") == "off" {
tooSlow(t) tooSlow(t)
tg.makeTempdir() tg.makeTempdir()
tg.setenv("GOCACHE", tg.path("cache")) tg.setenv("GOCACHE", tg.path("cache"))
......
...@@ -7,7 +7,6 @@ package base ...@@ -7,7 +7,6 @@ package base
import ( import (
"flag" "flag"
"fmt" "fmt"
"os"
"runtime" "runtime"
"strings" "strings"
...@@ -62,7 +61,7 @@ func InitGOFLAGS() { ...@@ -62,7 +61,7 @@ func InitGOFLAGS() {
// (Both will show the GOFLAGS setting if let succeed.) // (Both will show the GOFLAGS setting if let succeed.)
hideErrors := cfg.CmdName == "env" || cfg.CmdName == "bug" hideErrors := cfg.CmdName == "env" || cfg.CmdName == "bug"
goflags = strings.Fields(os.Getenv("GOFLAGS")) goflags = strings.Fields(cfg.Getenv("GOFLAGS"))
if goflags == nil { if goflags == nil {
goflags = []string{} // avoid work on later InitGOFLAGS call goflags = []string{} // avoid work on later InitGOFLAGS call
} }
......
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"sync" "sync"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
) )
// Default returns the default cache to use, or nil if no cache should be used. // Default returns the default cache to use, or nil if no cache should be used.
...@@ -73,7 +74,7 @@ func DefaultDir() string { ...@@ -73,7 +74,7 @@ func DefaultDir() string {
// otherwise distinguish between an explicit "off" and a UserCacheDir error. // otherwise distinguish between an explicit "off" and a UserCacheDir error.
defaultDirOnce.Do(func() { defaultDirOnce.Do(func() {
defaultDir = os.Getenv("GOCACHE") defaultDir = cfg.Getenv("GOCACHE")
if filepath.IsAbs(defaultDir) || defaultDir == "off" { if filepath.IsAbs(defaultDir) || defaultDir == "off" {
return return
} }
......
...@@ -7,11 +7,15 @@ ...@@ -7,11 +7,15 @@
package cfg package cfg
import ( import (
"bytes"
"fmt" "fmt"
"go/build" "go/build"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings"
"sync"
"cmd/internal/objabi" "cmd/internal/objabi"
) )
...@@ -46,6 +50,50 @@ var ( ...@@ -46,6 +50,50 @@ var (
func defaultContext() build.Context { func defaultContext() build.Context {
ctxt := build.Default ctxt := build.Default
ctxt.JoinPath = filepath.Join // back door to say "do not use go command" 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 return ctxt
} }
...@@ -70,9 +118,10 @@ var CmdEnv []EnvVar ...@@ -70,9 +118,10 @@ var CmdEnv []EnvVar
// Global build parameters (used during package load) // Global build parameters (used during package load)
var ( var (
Goarch = BuildContext.GOARCH Goarch = BuildContext.GOARCH
Goos = BuildContext.GOOS Goos = BuildContext.GOOS
ExeSuffix string
ExeSuffix = exeSuffix()
// ModulesEnabled specifies whether the go command is running // ModulesEnabled specifies whether the go command is running
// in module-aware mode (as opposed to GOPATH mode). // in module-aware mode (as opposed to GOPATH mode).
...@@ -85,40 +134,195 @@ var ( ...@@ -85,40 +134,195 @@ var (
GoModInGOPATH string GoModInGOPATH string
) )
func init() { func exeSuffix() string {
if Goos == "windows" { 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 ( var (
GOROOT = findGOROOT() GOROOT = BuildContext.GOROOT
GOBIN = os.Getenv("GOBIN") GOBIN = Getenv("GOBIN")
GOROOTbin = filepath.Join(GOROOT, "bin") GOROOTbin = filepath.Join(GOROOT, "bin")
GOROOTpkg = filepath.Join(GOROOT, "pkg") GOROOTpkg = filepath.Join(GOROOT, "pkg")
GOROOTsrc = filepath.Join(GOROOT, "src") GOROOTsrc = filepath.Join(GOROOT, "src")
GOROOT_FINAL = findGOROOT_FINAL() GOROOT_FINAL = findGOROOT_FINAL()
// Used in envcmd.MkEnv and build ID computations. // Used in envcmd.MkEnv and build ID computations.
GOARM = fmt.Sprint(objabi.GOARM) GOARM = envOr("GOARM", fmt.Sprint(objabi.GOARM))
GO386 = objabi.GO386 GO386 = envOr("GO386", objabi.GO386)
GOMIPS = objabi.GOMIPS GOMIPS = envOr("GOMIPS", objabi.GOMIPS)
GOMIPS64 = objabi.GOMIPS64 GOMIPS64 = envOr("GOMIPS64", objabi.GOMIPS64)
GOPPC64 = fmt.Sprintf("%s%d", "power", objabi.GOPPC64) GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64))
GOWASM = objabi.GOWASM GOWASM = envOr("GOWASM", fmt.Sprint(objabi.GOWASM))
) )
// Update build context to use our computed GOROOT. // GetArchEnv returns the name and setting of the
func init() { // GOARCH-specific architecture environment variable.
BuildContext.GOROOT = GOROOT // If the current architecture has no GOARCH-specific variable,
if runtime.Compiler != "gccgo" { // GetArchEnv returns empty key and value.
// Note that we must use runtime.GOOS and runtime.GOARCH here, func GetArchEnv() (key, val string) {
// as the tool directory does not move based on environment switch Goarch {
// variables. This matches the initialization of ToolDir in case "arm":
// go/build, except for using GOROOT rather than return "GOARM", GOARM
// runtime.GOROOT. case "386":
build.ToolDir = filepath.Join(GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH) 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 findGOROOT, isSameDir, and isGOROOT in
...@@ -132,7 +336,7 @@ func init() { ...@@ -132,7 +336,7 @@ func init() {
// //
// There is a copy of this code in x/tools/cmd/godoc/goroot.go. // There is a copy of this code in x/tools/cmd/godoc/goroot.go.
func findGOROOT() string { func findGOROOT() string {
if env := os.Getenv("GOROOT"); env != "" { if env := Getenv("GOROOT"); env != "" {
return filepath.Clean(env) return filepath.Clean(env)
} }
def := filepath.Clean(runtime.GOROOT()) def := filepath.Clean(runtime.GOROOT())
...@@ -168,6 +372,8 @@ func findGOROOT() string { ...@@ -168,6 +372,8 @@ func findGOROOT() string {
} }
func findGOROOT_FINAL() string { func findGOROOT_FINAL() string {
// $GOROOT_FINAL is only for use during make.bash
// so it is not settable using go/env, so we use os.Getenv here.
def := GOROOT def := GOROOT
if env := os.Getenv("GOROOT_FINAL"); env != "" { if env := os.Getenv("GOROOT_FINAL"); env != "" {
def = filepath.Clean(env) def = filepath.Clean(env)
......
...@@ -10,6 +10,9 @@ import ( ...@@ -10,6 +10,9 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"unicode/utf8"
"io/ioutil"
"sort"
"runtime" "runtime"
"strings" "strings"
...@@ -22,7 +25,7 @@ import ( ...@@ -22,7 +25,7 @@ import (
) )
var CmdEnv = &base.Command{ var CmdEnv = &base.Command{
UsageLine: "go env [-json] [var ...]", UsageLine: "go env [-json] [-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.
...@@ -35,6 +38,14 @@ each named variable on its own line. ...@@ -35,6 +38,14 @@ each named variable on its own line.
The -json flag prints the environment in JSON format The -json flag prints the environment in JSON format
instead of as a shell script. 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'. For more about environment variables, see 'go help environment'.
`, `,
} }
...@@ -43,26 +54,31 @@ func init() { ...@@ -43,26 +54,31 @@ func init() {
CmdEnv.Run = runEnv // break init cycle 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 { func MkEnv() []cfg.EnvVar {
var b work.Builder var b work.Builder
b.Init() b.Init()
envFile, _ := cfg.EnvFile()
env := []cfg.EnvVar{ env := []cfg.EnvVar{
{Name: "GOARCH", Value: cfg.Goarch}, {Name: "GOARCH", Value: cfg.Goarch},
{Name: "GOBIN", Value: cfg.GOBIN}, {Name: "GOBIN", Value: cfg.GOBIN},
{Name: "GOCACHE", Value: cache.DefaultDir()}, {Name: "GOCACHE", Value: cache.DefaultDir()},
{Name: "GOENV", Value: envFile},
{Name: "GOEXE", Value: cfg.ExeSuffix}, {Name: "GOEXE", Value: cfg.ExeSuffix},
{Name: "GOFLAGS", Value: os.Getenv("GOFLAGS")}, {Name: "GOFLAGS", Value: cfg.Getenv("GOFLAGS")},
{Name: "GOHOSTARCH", Value: runtime.GOARCH}, {Name: "GOHOSTARCH", Value: runtime.GOARCH},
{Name: "GOHOSTOS", Value: runtime.GOOS}, {Name: "GOHOSTOS", Value: runtime.GOOS},
{Name: "GOOS", Value: cfg.Goos}, {Name: "GOOS", Value: cfg.Goos},
{Name: "GOPATH", Value: cfg.BuildContext.GOPATH}, {Name: "GOPATH", Value: cfg.BuildContext.GOPATH},
{Name: "GOPROXY", Value: os.Getenv("GOPROXY")}, {Name: "GOPROXY", Value: cfg.Getenv("GOPROXY")},
{Name: "GORACE", Value: os.Getenv("GORACE")},
{Name: "GOROOT", Value: cfg.GOROOT}, {Name: "GOROOT", Value: cfg.GOROOT},
{Name: "GOTMPDIR", Value: os.Getenv("GOTMPDIR")}, {Name: "GOTMPDIR", Value: cfg.Getenv("GOTMPDIR")},
{Name: "GOTOOLDIR", Value: base.ToolDir}, {Name: "GOTOOLDIR", Value: base.ToolDir},
} }
...@@ -72,29 +88,20 @@ func MkEnv() []cfg.EnvVar { ...@@ -72,29 +88,20 @@ func MkEnv() []cfg.EnvVar {
env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoName}) env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoName})
} }
switch cfg.Goarch { key, val := cfg.GetArchEnv()
case "arm": if key != "" {
env = append(env, cfg.EnvVar{Name: "GOARM", Value: cfg.GOARM}) env = append(env, cfg.EnvVar{Name: key, Value: val})
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()})
} }
cc := cfg.DefaultCC(cfg.Goos, cfg.Goarch) 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] cc = env[0]
} }
cxx := cfg.DefaultCXX(cfg.Goos, cfg.Goarch) 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] 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: "CC", Value: cc})
env = append(env, cfg.EnvVar{Name: "CXX", Value: cxx}) env = append(env, cfg.EnvVar{Name: "CXX", Value: cxx})
...@@ -107,6 +114,14 @@ func MkEnv() []cfg.EnvVar { ...@@ -107,6 +114,14 @@ func MkEnv() []cfg.EnvVar {
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 {
...@@ -154,7 +169,25 @@ func ExtraEnvVarsCostly() []cfg.EnvVar { ...@@ -154,7 +169,25 @@ func ExtraEnvVarsCostly() []cfg.EnvVar {
} }
} }
// argKey returns the KEY part of the arg KEY=VAL, or else arg itself.
func argKey(arg string) string {
i := strings.Index(arg, "=")
if i < 0 {
return arg
}
return arg[:i]
}
func runEnv(cmd *base.Command, args []string) { 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 := cfg.CmdEnv
env = append(env, ExtraEnvVars()...) env = append(env, ExtraEnvVars()...)
...@@ -165,7 +198,7 @@ func runEnv(cmd *base.Command, args []string) { ...@@ -165,7 +198,7 @@ func runEnv(cmd *base.Command, args []string) {
if len(args) > 0 { if len(args) > 0 {
needCostly = false needCostly = false
for _, arg := range args { for _, arg := range args {
switch arg { switch argKey(arg) {
case "CGO_CFLAGS", case "CGO_CFLAGS",
"CGO_CPPFLAGS", "CGO_CPPFLAGS",
"CGO_CXXFLAGS", "CGO_CXXFLAGS",
...@@ -181,6 +214,55 @@ func runEnv(cmd *base.Command, args []string) { ...@@ -181,6 +214,55 @@ func runEnv(cmd *base.Command, args []string) {
env = append(env, ExtraEnvVarsCostly()...) 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 len(args) > 0 {
if *envJson { if *envJson {
var es []cfg.EnvVar var es []cfg.EnvVar
...@@ -239,6 +321,118 @@ func printEnvAsJSON(env []cfg.EnvVar) { ...@@ -239,6 +321,118 @@ func printEnvAsJSON(env []cfg.EnvVar) {
enc := json.NewEncoder(os.Stdout) enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", "\t") enc.SetIndent("", "\t")
if err := enc.Encode(m); err != nil { 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])
})
} }
...@@ -469,10 +469,17 @@ var HelpEnvironment = &base.Command{ ...@@ -469,10 +469,17 @@ var HelpEnvironment = &base.Command{
Short: "environment variables", Short: "environment variables",
Long: ` Long: `
The go command, and the tools it invokes, examine a few different The go command and the tools it invokes consult environment variables
environment variables. For many of these, you can see the default for configuration. If an environment variable is unset, the go command
value of on your system by running 'go env NAME', where NAME is the uses a sensible default setting. To see the effective setting of the
name of the variable. 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: General-purpose environment variables:
...@@ -486,10 +493,15 @@ General-purpose environment variables: ...@@ -486,10 +493,15 @@ General-purpose environment variables:
GOCACHE GOCACHE
The directory where the go command will store cached The directory where the go command will store cached
information for reuse in future builds. information for reuse in future builds.
GOENV
The location of the Go environment configuration file.
Cannot be set using 'go env -w'.
GOFLAGS GOFLAGS
A space-separated list of -flag=value settings to apply A space-separated list of -flag=value settings to apply
to go commands by default, when the given flag is known by 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. are applied after this list and therefore override it.
GOOS GOOS
The operating system for which to compile code. The operating system for which to compile code.
...@@ -498,21 +510,18 @@ General-purpose environment variables: ...@@ -498,21 +510,18 @@ General-purpose environment variables:
For more details see: 'go help gopath'. For more details see: 'go help gopath'.
GOPROXY GOPROXY
URL of Go module proxy. See 'go help 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 GOROOT
The root of the go tree. The root of the go tree.
GOTMPDIR GOTMPDIR
The directory where the go command will write The directory where the go command will write
temporary source files, packages, and binaries. 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: 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 CC
The command to use to compile C code. The command to use to compile C code.
CGO_ENABLED CGO_ENABLED
...@@ -542,12 +551,10 @@ Environment variables for use with cgo: ...@@ -542,12 +551,10 @@ Environment variables for use with cgo:
but for the linker. but for the linker.
CXX CXX
The command to use to compile C++ code. The command to use to compile C++ code.
FC
The command to use to compile Fortran code.
PKG_CONFIG PKG_CONFIG
Path to pkg-config tool. 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: Architecture-specific environment variables:
...@@ -582,9 +589,11 @@ Special-purpose environment variables: ...@@ -582,9 +589,11 @@ Special-purpose environment variables:
when using -linkmode=auto with code that uses cgo. when using -linkmode=auto with code that uses cgo.
Set to 0 to disable external linking mode, 1 to enable it. Set to 0 to disable external linking mode, 1 to enable it.
GIT_ALLOW_PROTOCOL GIT_ALLOW_PROTOCOL
Defined by Git. A colon-separated list of schemes that are allowed to be used Defined by Git. A colon-separated list of schemes that are allowed
with git fetch/clone. If set, any scheme not explicitly mentioned will be to be used with git fetch/clone. If set, any scheme not explicitly
considered insecure by 'go get'. 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: Additional information available from 'go env' but not read from the environment:
......
...@@ -6,11 +6,11 @@ package modfetch ...@@ -6,11 +6,11 @@ package modfetch
import ( import (
"fmt" "fmt"
"os"
pathpkg "path" pathpkg "path"
"strings" "strings"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/get" "cmd/go/internal/get"
"cmd/go/internal/module" "cmd/go/internal/module"
) )
...@@ -67,7 +67,7 @@ func useNotary(mod module.Version) bool { ...@@ -67,7 +67,7 @@ func useNotary(mod module.Version) bool {
if get.Insecure { if get.Insecure {
return false return false
} }
wantNotary, err := notaryShouldVerify(mod.Path, os.Getenv("GONOVERIFY")) wantNotary, err := notaryShouldVerify(mod.Path, cfg.Getenv("GONOVERIFY"))
if err != nil { if err != nil {
base.Fatalf("%v", err) base.Fatalf("%v", err)
} }
......
...@@ -9,11 +9,11 @@ import ( ...@@ -9,11 +9,11 @@ import (
"fmt" "fmt"
"io" "io"
"net/url" "net/url"
"os"
"strings" "strings"
"time" "time"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/modfetch/codehost" "cmd/go/internal/modfetch/codehost"
"cmd/go/internal/module" "cmd/go/internal/module"
"cmd/go/internal/semver" "cmd/go/internal/semver"
...@@ -85,7 +85,7 @@ cached module versions with GOPROXY=https://example.com/proxy. ...@@ -85,7 +85,7 @@ cached module versions with GOPROXY=https://example.com/proxy.
`, `,
} }
var proxyURL = os.Getenv("GOPROXY") var proxyURL = cfg.Getenv("GOPROXY")
// SetProxy sets the proxy to use when fetching modules. // SetProxy sets the proxy to use when fetching modules.
// It accepts the same syntax as the GOPROXY environment variable, // It accepts the same syntax as the GOPROXY environment variable,
......
...@@ -6,6 +6,18 @@ package modload ...@@ -6,6 +6,18 @@ package modload
import ( import (
"bytes" "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/base"
"cmd/go/internal/cache" "cmd/go/internal/cache"
"cmd/go/internal/cfg" "cmd/go/internal/cfg"
...@@ -18,17 +30,6 @@ import ( ...@@ -18,17 +30,6 @@ import (
"cmd/go/internal/mvs" "cmd/go/internal/mvs"
"cmd/go/internal/renameio" "cmd/go/internal/renameio"
"cmd/go/internal/search" "cmd/go/internal/search"
"encoding/json"
"fmt"
"go/build"
"internal/lazyregexp"
"io/ioutil"
"os"
"path"
"path/filepath"
"runtime/debug"
"strconv"
"strings"
) )
var ( var (
...@@ -90,7 +91,7 @@ func Init() { ...@@ -90,7 +91,7 @@ func Init() {
} }
initialized = true initialized = true
env := os.Getenv("GO111MODULE") env := cfg.Getenv("GO111MODULE")
switch env { switch env {
default: default:
base.Fatalf("go: unknown environment setting GO111MODULE=%s", env) base.Fatalf("go: unknown environment setting GO111MODULE=%s", env)
...@@ -294,7 +295,7 @@ func die() { ...@@ -294,7 +295,7 @@ func die() {
if printStackInDie { if printStackInDie {
debug.PrintStack() debug.PrintStack()
} }
if os.Getenv("GO111MODULE") == "off" { if cfg.Getenv("GO111MODULE") == "off" {
base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'") base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
} }
if inGOPATH && !mustUseModules { if inGOPATH && !mustUseModules {
......
...@@ -226,7 +226,7 @@ func (b *Builder) Init() { ...@@ -226,7 +226,7 @@ func (b *Builder) Init() {
if cfg.BuildN { if cfg.BuildN {
b.WorkDir = "$WORK" b.WorkDir = "$WORK"
} else { } else {
tmp, err := ioutil.TempDir(os.Getenv("GOTMPDIR"), "go-build") tmp, err := ioutil.TempDir(cfg.Getenv("GOTMPDIR"), "go-build")
if err != nil { if err != nil {
base.Fatalf("go: creating work dir: %v", err) base.Fatalf("go: creating work dir: %v", err)
} }
......
...@@ -224,12 +224,16 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID { ...@@ -224,12 +224,16 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
if len(p.SFiles) > 0 { if len(p.SFiles) > 0 {
fmt.Fprintf(h, "asm %q %q %q\n", b.toolID("asm"), forcedAsmflags, p.Internal.Asmflags) fmt.Fprintf(h, "asm %q %q %q\n", b.toolID("asm"), forcedAsmflags, p.Internal.Asmflags)
} }
// GO386, GOARM, GOMIPS, etc. // GO386, GOARM, GOMIPS, etc.
baseArch := strings.TrimSuffix(cfg.BuildContext.GOARCH, "le") key, val := cfg.GetArchEnv()
fmt.Fprintf(h, "GO$GOARCH=%s\n", os.Getenv("GO"+strings.ToUpper(baseArch))) fmt.Fprintf(h, "%s=%s\n", key, val)
// TODO(rsc): Convince compiler team not to add more magic environment variables, // TODO(rsc): Convince compiler team not to add more magic environment variables,
// or perhaps restrict the environment variables passed to subprocesses. // 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{ magic := []string{
"GOCLOBBERDEADHASH", "GOCLOBBERDEADHASH",
"GOSSAFUNC", "GOSSAFUNC",
...@@ -1115,21 +1119,16 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) { ...@@ -1115,21 +1119,16 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) {
if p != nil { if p != nil {
fmt.Fprintf(h, "linkflags %q\n", p.Internal.Ldflags) 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. // The linker writes source file paths that say GOROOT_FINAL.
fmt.Fprintf(h, "GOROOT=%s\n", cfg.GOROOT_FINAL) fmt.Fprintf(h, "GOROOT=%s\n", cfg.GOROOT_FINAL)
// TODO(rsc): Convince linker team not to add more magic environment variables, // GO_EXTLINK_ENABLED controls whether the external linker is used.
// or perhaps restrict the environment variables passed to subprocesses. fmt.Fprintf(h, "GO_EXTLINK_ENABLED=%s\n", cfg.Getenv("GO_EXTLINK_ENABLED"))
magic := []string{
"GO_EXTLINK_ENABLED",
}
for _, env := range magic {
if x := os.Getenv(env); x != "" {
fmt.Fprintf(h, "magic %s=%s\n", env, x)
}
}
// TODO(rsc): Do cgo settings and flags need to be included? // TODO(rsc): Do cgo settings and flags need to be included?
// Or external linker settings and flags? // Or external linker settings and flags?
...@@ -2192,8 +2191,8 @@ func (b *Builder) gccld(p *load.Package, objdir, outfile string, flags []string, ...@@ -2192,8 +2191,8 @@ func (b *Builder) gccld(p *load.Package, objdir, outfile string, flags []string,
// Grab these before main helpfully overwrites them. // Grab these before main helpfully overwrites them.
var ( var (
origCC = os.Getenv("CC") origCC = cfg.Getenv("CC")
origCXX = os.Getenv("CXX") origCXX = cfg.Getenv("CXX")
) )
// gccCmd returns a gcc command line prefix // gccCmd returns a gcc command line prefix
...@@ -2225,7 +2224,7 @@ func (b *Builder) cxxExe() []string { ...@@ -2225,7 +2224,7 @@ func (b *Builder) cxxExe() []string {
// fcExe returns the FC compiler setting without all the extra flags we add implicitly. // fcExe returns the FC compiler setting without all the extra flags we add implicitly.
func (b *Builder) fcExe() []string { 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 // compilerExe returns the compiler to use given an
...@@ -2391,7 +2390,7 @@ func (b *Builder) gccArchArgs() []string { ...@@ -2391,7 +2390,7 @@ func (b *Builder) gccArchArgs() []string {
// envList returns the value of the given environment variable broken // envList returns the value of the given environment variable broken
// into fields, using the default value when the variable is empty. // into fields, using the default value when the variable is empty.
func envList(key, def string) []string { func envList(key, def string) []string {
v := os.Getenv(key) v := cfg.Getenv(key)
if v == "" { if v == "" {
v = def v = def
} }
...@@ -2448,7 +2447,7 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo ...@@ -2448,7 +2447,7 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
// Support gfortran out of the box and let others pass the correct link options // Support gfortran out of the box and let others pass the correct link options
// via CGO_LDFLAGS // via CGO_LDFLAGS
if len(ffiles) > 0 { if len(ffiles) > 0 {
fc := os.Getenv("FC") fc := cfg.Getenv("FC")
if fc == "" { if fc == "" {
fc = "gfortran" fc = "gfortran"
} }
......
...@@ -26,7 +26,7 @@ var GccgoName, GccgoBin string ...@@ -26,7 +26,7 @@ var GccgoName, GccgoBin string
var gccgoErr error var gccgoErr error
func init() { func init() {
GccgoName = os.Getenv("GCCGO") GccgoName = cfg.Getenv("GCCGO")
if GccgoName == "" { if GccgoName == "" {
GccgoName = "gccgo" GccgoName = "gccgo"
} }
...@@ -44,7 +44,7 @@ func (gccgoToolchain) linker() string { ...@@ -44,7 +44,7 @@ func (gccgoToolchain) linker() string {
} }
func (gccgoToolchain) ar() string { func (gccgoToolchain) ar() string {
ar := os.Getenv("AR") ar := cfg.Getenv("AR")
if ar == "" { if ar == "" {
ar = "ar" ar = "ar"
} }
...@@ -479,7 +479,7 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string ...@@ -479,7 +479,7 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string
ldflags = append(ldflags, "-lobjc") ldflags = append(ldflags, "-lobjc")
} }
if fortran { if fortran {
fc := os.Getenv("FC") fc := cfg.Getenv("FC")
if fc == "" { if fc == "" {
fc = "gfortran" fc = "gfortran"
} }
......
...@@ -30,12 +30,13 @@ ...@@ -30,12 +30,13 @@
package work package work
import ( import (
"cmd/go/internal/load"
"fmt" "fmt"
"internal/lazyregexp" "internal/lazyregexp"
"os"
"regexp" "regexp"
"strings" "strings"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
) )
var re = lazyregexp.New var re = lazyregexp.New
...@@ -229,14 +230,14 @@ func checkFlags(name, source string, list []string, valid []*lazyregexp.Regexp, ...@@ -229,14 +230,14 @@ func checkFlags(name, source string, list []string, valid []*lazyregexp.Regexp,
allow *regexp.Regexp allow *regexp.Regexp
disallow *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) r, err := regexp.Compile(env)
if err != nil { if err != nil {
return fmt.Errorf("parsing $CGO_%s_ALLOW: %v", name, err) return fmt.Errorf("parsing $CGO_%s_ALLOW: %v", name, err)
} }
allow = r allow = r
} }
if env := os.Getenv("CGO_" + name + "_DISALLOW"); env != "" { if env := cfg.Getenv("CGO_" + name + "_DISALLOW"); env != "" {
r, err := regexp.Compile(env) r, err := regexp.Compile(env)
if err != nil { if err != nil {
return fmt.Errorf("parsing $CGO_%s_DISALLOW: %v", name, err) return fmt.Errorf("parsing $CGO_%s_DISALLOW: %v", name, err)
......
...@@ -122,8 +122,14 @@ func main() { ...@@ -122,8 +122,14 @@ func main() {
os.Exit(2) os.Exit(2)
} }
if !filepath.IsAbs(p) { 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) if cfg.Getenv("GOPATH") == "" {
os.Exit(2) // 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)
}
} }
} }
} }
......
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'
...@@ -87,7 +87,7 @@ type gowasmFeatures struct { ...@@ -87,7 +87,7 @@ type gowasmFeatures struct {
SatConv bool SatConv bool
} }
func (f *gowasmFeatures) String() string { func (f gowasmFeatures) String() string {
var flags []string var flags []string
if f.SatConv { if f.SatConv {
flags = append(flags, "satconv") flags = append(flags, "satconv")
......
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
set -e set -e
export GOENV=off
unset GOBIN # Issue 14340 unset GOBIN # Issue 14340
unset GOFLAGS unset GOFLAGS
unset GO111MODULE unset GO111MODULE
......
...@@ -46,13 +46,14 @@ if x%4==x--no-local goto nolocal ...@@ -46,13 +46,14 @@ if x%4==x--no-local goto nolocal
setlocal setlocal
:nolocal :nolocal
set GOENV=off
set GOBUILDFAIL=0 set GOBUILDFAIL=0
set GOFLAGS= set GOFLAGS=
set GO111MODULE= set GO111MODULE=
if exist make.bat goto ok if exist make.bat goto ok
echo Must run make.bat from Go src directory. echo Must run make.bat from Go src directory.
goto fail goto fail
:ok :ok
:: Clean old generated file that will cause problems in the build. :: Clean old generated file that will cause problems in the build.
......
...@@ -47,6 +47,7 @@ if(~ $1 -v) { ...@@ -47,6 +47,7 @@ if(~ $1 -v) {
shift shift
} }
GOENV=off
GOFLAGS=() GOFLAGS=()
GO111MODULE=() GO111MODULE=()
GOROOT = `{cd .. && pwd} GOROOT = `{cd .. && pwd}
......
...@@ -136,6 +136,9 @@ that can be blocked in system calls on behalf of Go code; those do not count aga ...@@ -136,6 +136,9 @@ that can be blocked in system calls on behalf of Go code; those do not count aga
the GOMAXPROCS limit. This package's GOMAXPROCS function queries and changes the GOMAXPROCS limit. This package's GOMAXPROCS function queries and changes
the limit. 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 The GOTRACEBACK variable controls the amount of output generated when a Go
program fails due to an unrecovered panic or an unexpected runtime condition. program fails due to an unrecovered panic or an unexpected runtime condition.
By default, a failure prints a stack trace for the current goroutine, By default, a failure prints a stack trace for the current goroutine,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment