1
0
mirror of https://github.com/golang/go synced 2024-09-29 05:14:29 -06:00

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

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

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

Fixes #30411.

Change-Id: I36e83f55b666459f8f7f482432a4a6ee015da71d
Reviewed-on: https://go-review.googlesource.com/c/go/+/171137
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
This commit is contained in:
Russ Cox 2019-04-08 11:23:42 -04:00
parent e40dffe55a
commit f0e9754696
21 changed files with 671 additions and 140 deletions

View File

@ -368,7 +368,7 @@
//
// Usage:
//
// go env [-json] [var ...]
// go env [-json] [-u] [-w] [var ...]
//
// Env prints Go environment information.
//
@ -380,6 +380,14 @@
// The -json flag prints the environment in JSON format
// instead of as a shell script.
//
// The -u flag requires one or more arguments and unsets
// the default setting for the named environment variables,
// if one has been set with 'go env -w'.
//
// The -w flag requires one or more arguments of the
// form NAME=VALUE and changes the default settings
// of the named environment variables to the given values.
//
// For more about environment variables, see 'go help environment'.
//
//
@ -1485,10 +1493,17 @@
//
// Environment variables
//
// The go command, and the tools it invokes, examine a few different
// environment variables. For many of these, you can see the default
// value of on your system by running 'go env NAME', where NAME is the
// name of the variable.
// The go command and the tools it invokes consult environment variables
// for configuration. If an environment variable is unset, the go command
// uses a sensible default setting. To see the effective setting of the
// variable <NAME>, run 'go env <NAME>'. To change the default setting,
// run 'go env -w <NAME>=<VALUE>'. Defaults changed using 'go env -w'
// are recorded in a Go environment configuration file stored in the
// per-user configuration directory, as reported by os.UserConfigDir.
// The location of the configuration file can be changed by setting
// the environment variable GOENV, and 'go env GOENV' prints the
// effective location, but 'go env -w' cannot change the default location.
// See 'go help env' for details.
//
// General-purpose environment variables:
//
@ -1502,10 +1517,15 @@
// GOCACHE
// The directory where the go command will store cached
// information for reuse in future builds.
// GOENV
// The location of the Go environment configuration file.
// Cannot be set using 'go env -w'.
// GOFLAGS
// A space-separated list of -flag=value settings to apply
// to go commands by default, when the given flag is known by
// the current command. Flags listed on the command line
// the current command. Each entry must be a standalone flag.
// Because the entries are space-separated, flag values must
// not contain spaces. Flags listed on the command line
// are applied after this list and therefore override it.
// GOOS
// The operating system for which to compile code.
@ -1514,21 +1534,18 @@
// For more details see: 'go help gopath'.
// GOPROXY
// URL of Go module proxy. See 'go help goproxy'.
// GORACE
// Options for the race detector.
// See https://golang.org/doc/articles/race_detector.html.
// GOROOT
// The root of the go tree.
// GOTMPDIR
// The directory where the go command will write
// temporary source files, packages, and binaries.
//
// Each entry in the GOFLAGS list must be a standalone flag.
// Because the entries are space-separated, flag values must
// not contain spaces.
//
// Environment variables for use with cgo:
//
// AR
// The command to use to manipulate library archives when
// building with the gccgo compiler.
// The default is 'ar'.
// CC
// The command to use to compile C code.
// CGO_ENABLED
@ -1558,12 +1575,10 @@
// but for the linker.
// CXX
// The command to use to compile C++ code.
// FC
// The command to use to compile Fortran code.
// PKG_CONFIG
// Path to pkg-config tool.
// AR
// The command to use to manipulate library archives when
// building with the gccgo compiler.
// The default is 'ar'.
//
// Architecture-specific environment variables:
//
@ -1598,9 +1613,11 @@
// when using -linkmode=auto with code that uses cgo.
// Set to 0 to disable external linking mode, 1 to enable it.
// GIT_ALLOW_PROTOCOL
// Defined by Git. A colon-separated list of schemes that are allowed to be used
// with git fetch/clone. If set, any scheme not explicitly mentioned will be
// considered insecure by 'go get'.
// Defined by Git. A colon-separated list of schemes that are allowed
// to be used with git fetch/clone. If set, any scheme not explicitly
// mentioned will be considered insecure by 'go get'.
// Because the variable is defined by Git, the default value cannot
// be set using 'go env -w'.
//
// Additional information available from 'go env' but not read from the environment:
//

View File

@ -6,8 +6,6 @@ package main_test
import (
"bytes"
"cmd/go/internal/cache"
"cmd/internal/sys"
"context"
"debug/elf"
"debug/macho"
@ -28,6 +26,10 @@ import (
"strings"
"testing"
"time"
"cmd/go/internal/cache"
"cmd/go/internal/cfg"
"cmd/internal/sys"
)
var (
@ -119,6 +121,8 @@ var testCtx = context.Background()
// The TestMain function creates a go command for testing purposes and
// deletes it after the tests have been run.
func TestMain(m *testing.M) {
// $GO_GCFLAGS a compiler debug flag known to cmd/dist, make.bash, etc.
// It is not a standard go command flag; use os.Getenv, not cfg.Getenv.
if os.Getenv("GO_GCFLAGS") != "" {
fmt.Fprintf(os.Stderr, "testing: warning: no tests to run\n") // magic string for cmd/go
fmt.Printf("cmd/go test is not compatible with $GO_GCFLAGS being set\n")
@ -256,6 +260,7 @@ func TestMain(m *testing.M) {
}
}
// Don't let these environment variables confuse the test.
os.Setenv("GOENV", "off")
os.Unsetenv("GOBIN")
os.Unsetenv("GOPATH")
os.Unsetenv("GIT_ALLOW_PROTOCOL")
@ -264,7 +269,7 @@ func TestMain(m *testing.M) {
// Setting HOME to a non-existent directory will break
// those systems. Disable ccache and use real compiler. Issue 17668.
os.Setenv("CCACHE_DISABLE", "1")
if os.Getenv("GOCACHE") == "" {
if cfg.Getenv("GOCACHE") == "" {
os.Setenv("GOCACHE", testGOCACHE) // because $HOME is gone
}
@ -5258,7 +5263,7 @@ func TestCacheVet(t *testing.T) {
if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
t.Skip("GODEBUG gocacheverify")
}
if os.Getenv("GOCACHE") == "off" {
if cfg.Getenv("GOCACHE") == "off" {
tooSlow(t)
tg.makeTempdir()
tg.setenv("GOCACHE", tg.path("cache"))

View File

@ -7,7 +7,6 @@ package base
import (
"flag"
"fmt"
"os"
"runtime"
"strings"
@ -62,7 +61,7 @@ func InitGOFLAGS() {
// (Both will show the GOFLAGS setting if let succeed.)
hideErrors := cfg.CmdName == "env" || cfg.CmdName == "bug"
goflags = strings.Fields(os.Getenv("GOFLAGS"))
goflags = strings.Fields(cfg.Getenv("GOFLAGS"))
if goflags == nil {
goflags = []string{} // avoid work on later InitGOFLAGS call
}

View File

@ -12,6 +12,7 @@ import (
"sync"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
)
// Default returns the default cache to use, or nil if no cache should be used.
@ -73,7 +74,7 @@ func DefaultDir() string {
// otherwise distinguish between an explicit "off" and a UserCacheDir error.
defaultDirOnce.Do(func() {
defaultDir = os.Getenv("GOCACHE")
defaultDir = cfg.Getenv("GOCACHE")
if filepath.IsAbs(defaultDir) || defaultDir == "off" {
return
}

View File

@ -7,11 +7,15 @@
package cfg
import (
"bytes"
"fmt"
"go/build"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"cmd/internal/objabi"
)
@ -46,6 +50,50 @@ var (
func defaultContext() build.Context {
ctxt := build.Default
ctxt.JoinPath = filepath.Join // back door to say "do not use go command"
ctxt.GOROOT = findGOROOT()
if runtime.Compiler != "gccgo" {
// Note that we must use runtime.GOOS and runtime.GOARCH here,
// as the tool directory does not move based on environment
// variables. This matches the initialization of ToolDir in
// go/build, except for using ctxt.GOROOT rather than
// runtime.GOROOT.
build.ToolDir = filepath.Join(ctxt.GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
}
ctxt.GOPATH = envOr("GOPATH", ctxt.GOPATH)
// Override defaults computed in go/build with defaults
// from go environment configuration file, if known.
ctxt.GOOS = envOr("GOOS", ctxt.GOOS)
ctxt.GOARCH = envOr("GOARCH", ctxt.GOARCH)
// The go/build rule for whether cgo is enabled is:
// 1. If $CGO_ENABLED is set, respect it.
// 2. Otherwise, if this is a cross-compile, disable cgo.
// 3. Otherwise, use built-in default for GOOS/GOARCH.
// Recreate that logic here with the new GOOS/GOARCH setting.
if v := Getenv("CGO_ENABLED"); v == "0" || v == "1" {
ctxt.CgoEnabled = v[0] == '1'
} else if ctxt.GOOS != runtime.GOOS || ctxt.GOARCH != runtime.GOARCH {
ctxt.CgoEnabled = false
} else {
// Use built-in default cgo setting for GOOS/GOARCH.
// Note that ctxt.GOOS/GOARCH are derived from the preference list
// (1) environment, (2) go/env file, (3) runtime constants,
// while go/build.Default.GOOS/GOARCH are derived from the preference list
// (1) environment, (2) runtime constants.
// We know ctxt.GOOS/GOARCH == runtime.GOOS/GOARCH;
// no matter how that happened, go/build.Default will make the
// same decision (either the environment variables are set explicitly
// to match the runtime constants, or else they are unset, in which
// case go/build falls back to the runtime constants), so
// go/build.Default.GOOS/GOARCH == runtime.GOOS/GOARCH.
// So ctxt.CgoEnabled (== go/build.Default.CgoEnabled) is correct
// as is and can be left unmodified.
// Nothing to do here.
}
return ctxt
}
@ -70,9 +118,10 @@ var CmdEnv []EnvVar
// Global build parameters (used during package load)
var (
Goarch = BuildContext.GOARCH
Goos = BuildContext.GOOS
ExeSuffix string
Goarch = BuildContext.GOARCH
Goos = BuildContext.GOOS
ExeSuffix = exeSuffix()
// ModulesEnabled specifies whether the go command is running
// in module-aware mode (as opposed to GOPATH mode).
@ -85,40 +134,195 @@ var (
GoModInGOPATH string
)
func init() {
func exeSuffix() string {
if Goos == "windows" {
ExeSuffix = ".exe"
return ".exe"
}
return ""
}
var envCache struct {
once sync.Once
m map[string]string
}
// EnvFile returns the name of the Go environment configuration file.
func EnvFile() (string, error) {
if file := os.Getenv("GOENV"); file != "" {
if file == "off" {
return "", fmt.Errorf("GOENV=off")
}
return file, nil
}
dir, err := os.UserConfigDir()
if err != nil {
return "", err
}
if dir == "" {
return "", fmt.Errorf("missing user-config dir")
}
return filepath.Join(dir, "go/env"), nil
}
func initEnvCache() {
envCache.m = make(map[string]string)
file, _ := EnvFile()
if file == "" {
return
}
data, err := ioutil.ReadFile(file)
if err != nil {
return
}
for len(data) > 0 {
// Get next line.
line := data
i := bytes.IndexByte(data, '\n')
if i >= 0 {
line, data = line[:i], data[i+1:]
} else {
data = nil
}
i = bytes.IndexByte(line, '=')
if i < 0 || line[0] < 'A' || 'Z' < line[0] {
// Line is missing = (or empty) or a comment or not a valid env name. Ignore.
// (This should not happen, since the file should be maintained almost
// exclusively by "go env -w", but better to silently ignore than to make
// the go command unusable just because somehow the env file has
// gotten corrupted.)
continue
}
key, val := line[:i], line[i+1:]
envCache.m[string(key)] = string(val)
}
}
// Getenv gets the value for the configuration key.
// It consults the operating system environment
// and then the go/env file.
// If Getenv is called for a key that cannot be set
// in the go/env file (for example GODEBUG), it panics.
// This ensures that CanGetenv is accurate, so that
// 'go env -w' stays in sync with what Getenv can retrieve.
func Getenv(key string) string {
if !CanGetenv(key) {
switch key {
case "CGO_TEST_ALLOW", "CGO_TEST_DISALLOW", "CGO_test_ALLOW", "CGO_test_DISALLOW":
// used by internal/work/security_test.go; allow
default:
panic("internal error: invalid Getenv " + key)
}
}
val := os.Getenv(key)
if val != "" {
return val
}
envCache.once.Do(initEnvCache)
return envCache.m[key]
}
// CanGetenv reports whether key is a valid go/env configuration key.
func CanGetenv(key string) bool {
return strings.Contains(knownEnv, "\t"+key+"\n")
}
var knownEnv = `
AR
CC
CGO_CFLAGS
CGO_CFLAGS_ALLOW
CGO_CFLAGS_DISALLOW
CGO_CPPFLAGS
CGO_CPPFLAGS_ALLOW
CGO_CPPFLAGS_DISALLOW
CGO_CXXFLAGS
CGO_CXXFLAGS_ALLOW
CGO_CXXFLAGS_DISALLOW
CGO_ENABLED
CGO_FFLAGS
CGO_FFLAGS_ALLOW
CGO_FFLAGS_DISALLOW
CGO_LDFLAGS
CGO_LDFLAGS_ALLOW
CGO_LDFLAGS_DISALLOW
CXX
FC
GCCGO
GO111MODULE
GO386
GOARCH
GOARM
GOBIN
GOCACHE
GOENV
GOEXE
GOFLAGS
GOGCCFLAGS
GOHOSTARCH
GOHOSTOS
GOMIPS
GOMIPS64
GONOVERIFY
GOOS
GOPATH
GOPPC64
GOPROXY
GOROOT
GOTMPDIR
GOTOOLDIR
GOWASM
GO_EXTLINK_ENABLED
PKG_CONFIG
`
var (
GOROOT = findGOROOT()
GOBIN = os.Getenv("GOBIN")
GOROOT = BuildContext.GOROOT
GOBIN = Getenv("GOBIN")
GOROOTbin = filepath.Join(GOROOT, "bin")
GOROOTpkg = filepath.Join(GOROOT, "pkg")
GOROOTsrc = filepath.Join(GOROOT, "src")
GOROOT_FINAL = findGOROOT_FINAL()
// Used in envcmd.MkEnv and build ID computations.
GOARM = fmt.Sprint(objabi.GOARM)
GO386 = objabi.GO386
GOMIPS = objabi.GOMIPS
GOMIPS64 = objabi.GOMIPS64
GOPPC64 = fmt.Sprintf("%s%d", "power", objabi.GOPPC64)
GOWASM = objabi.GOWASM
GOARM = envOr("GOARM", fmt.Sprint(objabi.GOARM))
GO386 = envOr("GO386", objabi.GO386)
GOMIPS = envOr("GOMIPS", objabi.GOMIPS)
GOMIPS64 = envOr("GOMIPS64", objabi.GOMIPS64)
GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64))
GOWASM = envOr("GOWASM", fmt.Sprint(objabi.GOWASM))
)
// Update build context to use our computed GOROOT.
func init() {
BuildContext.GOROOT = GOROOT
if runtime.Compiler != "gccgo" {
// Note that we must use runtime.GOOS and runtime.GOARCH here,
// as the tool directory does not move based on environment
// variables. This matches the initialization of ToolDir in
// go/build, except for using GOROOT rather than
// runtime.GOROOT.
build.ToolDir = filepath.Join(GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
// GetArchEnv returns the name and setting of the
// GOARCH-specific architecture environment variable.
// If the current architecture has no GOARCH-specific variable,
// GetArchEnv returns empty key and value.
func GetArchEnv() (key, val string) {
switch Goarch {
case "arm":
return "GOARM", GOARM
case "386":
return "GO386", GO386
case "mips", "mipsle":
return "GOMIPS", GOMIPS
case "mips64", "mips64le":
return "GOMIPS64", GOMIPS64
case "ppc64", "ppc64le":
return "GOPPC64", GOPPC64
case "wasm":
return "GOWASM", GOWASM
}
return "", ""
}
// envOr returns Getenv(key) if set, or else def.
func envOr(key, def string) string {
val := Getenv(key)
if val == "" {
val = def
}
return val
}
// There is a copy of findGOROOT, isSameDir, and isGOROOT in
@ -132,7 +336,7 @@ func init() {
//
// There is a copy of this code in x/tools/cmd/godoc/goroot.go.
func findGOROOT() string {
if env := os.Getenv("GOROOT"); env != "" {
if env := Getenv("GOROOT"); env != "" {
return filepath.Clean(env)
}
def := filepath.Clean(runtime.GOROOT())
@ -168,6 +372,8 @@ func findGOROOT() string {
}
func findGOROOT_FINAL() string {
// $GOROOT_FINAL is only for use during make.bash
// so it is not settable using go/env, so we use os.Getenv here.
def := GOROOT
if env := os.Getenv("GOROOT_FINAL"); env != "" {
def = filepath.Clean(env)

View File

@ -10,6 +10,9 @@ import (
"fmt"
"os"
"path/filepath"
"unicode/utf8"
"io/ioutil"
"sort"
"runtime"
"strings"
@ -22,7 +25,7 @@ import (
)
var CmdEnv = &base.Command{
UsageLine: "go env [-json] [var ...]",
UsageLine: "go env [-json] [-u] [-w] [var ...]",
Short: "print Go environment information",
Long: `
Env prints Go environment information.
@ -35,6 +38,14 @@ each named variable on its own line.
The -json flag prints the environment in JSON format
instead of as a shell script.
The -u flag requires one or more arguments and unsets
the default setting for the named environment variables,
if one has been set with 'go env -w'.
The -w flag requires one or more arguments of the
form NAME=VALUE and changes the default settings
of the named environment variables to the given values.
For more about environment variables, see 'go help environment'.
`,
}
@ -43,26 +54,31 @@ func init() {
CmdEnv.Run = runEnv // break init cycle
}
var envJson = CmdEnv.Flag.Bool("json", false, "")
var (
envJson = CmdEnv.Flag.Bool("json", false, "")
envU = CmdEnv.Flag.Bool("u", false, "")
envW = CmdEnv.Flag.Bool("w", false, "")
)
func MkEnv() []cfg.EnvVar {
var b work.Builder
b.Init()
envFile, _ := cfg.EnvFile()
env := []cfg.EnvVar{
{Name: "GOARCH", Value: cfg.Goarch},
{Name: "GOBIN", Value: cfg.GOBIN},
{Name: "GOCACHE", Value: cache.DefaultDir()},
{Name: "GOENV", Value: envFile},
{Name: "GOEXE", Value: cfg.ExeSuffix},
{Name: "GOFLAGS", Value: os.Getenv("GOFLAGS")},
{Name: "GOFLAGS", Value: cfg.Getenv("GOFLAGS")},
{Name: "GOHOSTARCH", Value: runtime.GOARCH},
{Name: "GOHOSTOS", Value: runtime.GOOS},
{Name: "GOOS", Value: cfg.Goos},
{Name: "GOPATH", Value: cfg.BuildContext.GOPATH},
{Name: "GOPROXY", Value: os.Getenv("GOPROXY")},
{Name: "GORACE", Value: os.Getenv("GORACE")},
{Name: "GOPROXY", Value: cfg.Getenv("GOPROXY")},
{Name: "GOROOT", Value: cfg.GOROOT},
{Name: "GOTMPDIR", Value: os.Getenv("GOTMPDIR")},
{Name: "GOTMPDIR", Value: cfg.Getenv("GOTMPDIR")},
{Name: "GOTOOLDIR", Value: base.ToolDir},
}
@ -72,29 +88,20 @@ func MkEnv() []cfg.EnvVar {
env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoName})
}
switch cfg.Goarch {
case "arm":
env = append(env, cfg.EnvVar{Name: "GOARM", Value: cfg.GOARM})
case "386":
env = append(env, cfg.EnvVar{Name: "GO386", Value: cfg.GO386})
case "mips", "mipsle":
env = append(env, cfg.EnvVar{Name: "GOMIPS", Value: cfg.GOMIPS})
case "mips64", "mips64le":
env = append(env, cfg.EnvVar{Name: "GOMIPS64", Value: cfg.GOMIPS64})
case "ppc64", "ppc64le":
env = append(env, cfg.EnvVar{Name: "GOPPC64", Value: cfg.GOPPC64})
case "wasm":
env = append(env, cfg.EnvVar{Name: "GOWASM", Value: cfg.GOWASM.String()})
key, val := cfg.GetArchEnv()
if key != "" {
env = append(env, cfg.EnvVar{Name: key, Value: val})
}
cc := cfg.DefaultCC(cfg.Goos, cfg.Goarch)
if env := strings.Fields(os.Getenv("CC")); len(env) > 0 {
if env := strings.Fields(cfg.Getenv("CC")); len(env) > 0 {
cc = env[0]
}
cxx := cfg.DefaultCXX(cfg.Goos, cfg.Goarch)
if env := strings.Fields(os.Getenv("CXX")); len(env) > 0 {
if env := strings.Fields(cfg.Getenv("CXX")); len(env) > 0 {
cxx = env[0]
}
env = append(env, cfg.EnvVar{Name: "AR", Value: envOr("AR", "ar")})
env = append(env, cfg.EnvVar{Name: "CC", Value: cc})
env = append(env, cfg.EnvVar{Name: "CXX", Value: cxx})
@ -107,6 +114,14 @@ func MkEnv() []cfg.EnvVar {
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 {
@ -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) {
if *envJson && *envU {
base.Fatalf("go env: cannot use -json with -u")
}
if *envJson && *envW {
base.Fatalf("go env: cannot use -json with -w")
}
if *envU && *envW {
base.Fatalf("go env: cannot use -u with -w")
}
env := cfg.CmdEnv
env = append(env, ExtraEnvVars()...)
@ -165,7 +198,7 @@ func runEnv(cmd *base.Command, args []string) {
if len(args) > 0 {
needCostly = false
for _, arg := range args {
switch arg {
switch argKey(arg) {
case "CGO_CFLAGS",
"CGO_CPPFLAGS",
"CGO_CXXFLAGS",
@ -181,6 +214,55 @@ func runEnv(cmd *base.Command, args []string) {
env = append(env, ExtraEnvVarsCostly()...)
}
if *envW {
// Process and sanity-check command line.
if len(args) == 0 {
base.Fatalf("go env -w: no KEY=VALUE arguments given")
}
osEnv := make(map[string]string)
for _, e := range cfg.OrigEnv {
if i := strings.Index(e, "="); i >= 0 {
osEnv[e[:i]] = e[i+1:]
}
}
add := make(map[string]string)
for _, arg := range args {
i := strings.Index(arg, "=")
if i < 0 {
base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg)
}
key, val := arg[:i], arg[i+1:]
if err := checkEnvWrite(key, val, env); err != nil {
base.Fatalf("go env -w: %v", err)
}
if _, ok := add[key]; ok {
base.Fatalf("go env -w: multiple values for key: %s", key)
}
add[key] = val
if osVal := osEnv[key]; osVal != "" && osVal != val {
fmt.Fprintf(os.Stderr, "warning: go env -w %s=... does not override conflicting OS environment variable\n", key)
}
}
updateEnvFile(add, nil)
return
}
if *envU {
// Process and sanity-check command line.
if len(args) == 0 {
base.Fatalf("go env -u: no arguments given")
}
del := make(map[string]bool)
for _, arg := range args {
if err := checkEnvWrite(arg, "", env); err != nil {
base.Fatalf("go env -u: %v", err)
}
del[arg] = true
}
updateEnvFile(nil, del)
return
}
if len(args) > 0 {
if *envJson {
var es []cfg.EnvVar
@ -239,6 +321,118 @@ func printEnvAsJSON(env []cfg.EnvVar) {
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", "\t")
if err := enc.Encode(m); err != nil {
base.Fatalf("%s", err)
base.Fatalf("go env -json: %s", err)
}
}
func checkEnvWrite(key, val string, env []cfg.EnvVar) error {
switch key {
case "GOEXE", "GOGCCFLAGS", "GOHOSTARCH", "GOHOSTOS", "GOMOD", "GOTOOLDIR":
return fmt.Errorf("%s cannot be modified", key)
case "GOENV":
return fmt.Errorf("%s can only be set using the OS environment", key)
}
// To catch typos and the like, check that we know the variable.
if !cfg.CanGetenv(key) {
return fmt.Errorf("unknown go command variable %s", key)
}
if !utf8.ValidString(val) {
return fmt.Errorf("invalid UTF-8 in %s=... value", key)
}
if strings.Contains(val, "\x00") {
return fmt.Errorf("invalid NUL in %s=... value", key)
}
if strings.ContainsAny(val, "\v\r\n") {
return fmt.Errorf("invalid newline in %s=... value", key)
}
return nil
}
func updateEnvFile(add map[string]string, del map[string]bool) {
file, err := cfg.EnvFile()
if file == "" {
base.Fatalf("go env: cannot find go env config: %v", err)
}
data, err := ioutil.ReadFile(file)
if err != nil && (!os.IsNotExist(err) || len(add) == 0) {
base.Fatalf("go env: reading go env config: %v", err)
}
lines := strings.SplitAfter(string(data), "\n")
if lines[len(lines)-1] == "" {
lines = lines[:len(lines)-1]
} else {
lines[len(lines)-1] += "\n"
}
// Delete all but last copy of any duplicated variables,
// since the last copy is the one that takes effect.
prev := make(map[string]int)
for l, line := range lines {
if key := lineToKey(line); key != "" {
if p, ok := prev[key]; ok {
lines[p] = ""
}
prev[key] = l
}
}
// Add variables (go env -w). Update existing lines in file if present, add to end otherwise.
for key, val := range add {
if p, ok := prev[key]; ok {
lines[p] = key + "=" + val + "\n"
delete(add, key)
}
}
for key, val := range add {
lines = append(lines, key + "=" + val + "\n")
}
// Delete requested variables (go env -u).
for key := range del {
if p, ok := prev[key]; ok {
lines[p] = ""
}
}
// Sort runs of KEY=VALUE lines
// (that is, blocks of lines where blocks are separated
// by comments, blank lines, or invalid lines).
start := 0
for i := 0; i <= len(lines); i++ {
if i == len(lines) || lineToKey(lines[i]) == "" {
sortKeyValues(lines[start:i])
start = i+1
}
}
data = []byte(strings.Join(lines, ""))
err = ioutil.WriteFile(file, data, 0666)
if err != nil {
// Try creating directory.
os.MkdirAll(filepath.Dir(file), 0777)
err = ioutil.WriteFile(file, data, 0666)
if err != nil {
base.Fatalf("go env: writing go env config: %v", err)
}
}
}
// lineToKey returns the KEY part of the line KEY=VALUE or else an empty string.
func lineToKey(line string) string {
i := strings.Index(line, "=")
if i < 0 || strings.Contains(line[:i], "#") {
return ""
}
return line[:i]
}
// sortKeyValues sorts a sequence of lines by key.
// It differs from sort.Strings in that GO386= sorts after GO=.
func sortKeyValues(lines []string) {
sort.Slice(lines, func(i, j int) bool {
return lineToKey(lines[i]) < lineToKey(lines[j])
})
}

View File

@ -469,10 +469,17 @@ var HelpEnvironment = &base.Command{
Short: "environment variables",
Long: `
The go command, and the tools it invokes, examine a few different
environment variables. For many of these, you can see the default
value of on your system by running 'go env NAME', where NAME is the
name of the variable.
The go command and the tools it invokes consult environment variables
for configuration. If an environment variable is unset, the go command
uses a sensible default setting. To see the effective setting of the
variable <NAME>, run 'go env <NAME>'. To change the default setting,
run 'go env -w <NAME>=<VALUE>'. Defaults changed using 'go env -w'
are recorded in a Go environment configuration file stored in the
per-user configuration directory, as reported by os.UserConfigDir.
The location of the configuration file can be changed by setting
the environment variable GOENV, and 'go env GOENV' prints the
effective location, but 'go env -w' cannot change the default location.
See 'go help env' for details.
General-purpose environment variables:
@ -486,10 +493,15 @@ General-purpose environment variables:
GOCACHE
The directory where the go command will store cached
information for reuse in future builds.
GOENV
The location of the Go environment configuration file.
Cannot be set using 'go env -w'.
GOFLAGS
A space-separated list of -flag=value settings to apply
to go commands by default, when the given flag is known by
the current command. Flags listed on the command line
the current command. Each entry must be a standalone flag.
Because the entries are space-separated, flag values must
not contain spaces. Flags listed on the command line
are applied after this list and therefore override it.
GOOS
The operating system for which to compile code.
@ -498,21 +510,18 @@ General-purpose environment variables:
For more details see: 'go help gopath'.
GOPROXY
URL of Go module proxy. See 'go help goproxy'.
GORACE
Options for the race detector.
See https://golang.org/doc/articles/race_detector.html.
GOROOT
The root of the go tree.
GOTMPDIR
The directory where the go command will write
temporary source files, packages, and binaries.
Each entry in the GOFLAGS list must be a standalone flag.
Because the entries are space-separated, flag values must
not contain spaces.
Environment variables for use with cgo:
AR
The command to use to manipulate library archives when
building with the gccgo compiler.
The default is 'ar'.
CC
The command to use to compile C code.
CGO_ENABLED
@ -542,12 +551,10 @@ Environment variables for use with cgo:
but for the linker.
CXX
The command to use to compile C++ code.
FC
The command to use to compile Fortran code.
PKG_CONFIG
Path to pkg-config tool.
AR
The command to use to manipulate library archives when
building with the gccgo compiler.
The default is 'ar'.
Architecture-specific environment variables:
@ -582,9 +589,11 @@ Special-purpose environment variables:
when using -linkmode=auto with code that uses cgo.
Set to 0 to disable external linking mode, 1 to enable it.
GIT_ALLOW_PROTOCOL
Defined by Git. A colon-separated list of schemes that are allowed to be used
with git fetch/clone. If set, any scheme not explicitly mentioned will be
considered insecure by 'go get'.
Defined by Git. A colon-separated list of schemes that are allowed
to be used with git fetch/clone. If set, any scheme not explicitly
mentioned will be considered insecure by 'go get'.
Because the variable is defined by Git, the default value cannot
be set using 'go env -w'.
Additional information available from 'go env' but not read from the environment:

View File

@ -6,11 +6,11 @@ package modfetch
import (
"fmt"
"os"
pathpkg "path"
"strings"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/get"
"cmd/go/internal/module"
)
@ -67,7 +67,7 @@ func useNotary(mod module.Version) bool {
if get.Insecure {
return false
}
wantNotary, err := notaryShouldVerify(mod.Path, os.Getenv("GONOVERIFY"))
wantNotary, err := notaryShouldVerify(mod.Path, cfg.Getenv("GONOVERIFY"))
if err != nil {
base.Fatalf("%v", err)
}

View File

@ -9,11 +9,11 @@ import (
"fmt"
"io"
"net/url"
"os"
"strings"
"time"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/modfetch/codehost"
"cmd/go/internal/module"
"cmd/go/internal/semver"
@ -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.
// It accepts the same syntax as the GOPROXY environment variable,

View File

@ -6,6 +6,18 @@ package modload
import (
"bytes"
"encoding/json"
"fmt"
"go/build"
"internal/lazyregexp"
"io/ioutil"
"os"
"path"
"path/filepath"
"runtime/debug"
"strconv"
"strings"
"cmd/go/internal/base"
"cmd/go/internal/cache"
"cmd/go/internal/cfg"
@ -18,17 +30,6 @@ import (
"cmd/go/internal/mvs"
"cmd/go/internal/renameio"
"cmd/go/internal/search"
"encoding/json"
"fmt"
"go/build"
"internal/lazyregexp"
"io/ioutil"
"os"
"path"
"path/filepath"
"runtime/debug"
"strconv"
"strings"
)
var (
@ -90,7 +91,7 @@ func Init() {
}
initialized = true
env := os.Getenv("GO111MODULE")
env := cfg.Getenv("GO111MODULE")
switch env {
default:
base.Fatalf("go: unknown environment setting GO111MODULE=%s", env)
@ -294,7 +295,7 @@ func die() {
if printStackInDie {
debug.PrintStack()
}
if os.Getenv("GO111MODULE") == "off" {
if cfg.Getenv("GO111MODULE") == "off" {
base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
}
if inGOPATH && !mustUseModules {

View File

@ -226,7 +226,7 @@ func (b *Builder) Init() {
if cfg.BuildN {
b.WorkDir = "$WORK"
} else {
tmp, err := ioutil.TempDir(os.Getenv("GOTMPDIR"), "go-build")
tmp, err := ioutil.TempDir(cfg.Getenv("GOTMPDIR"), "go-build")
if err != nil {
base.Fatalf("go: creating work dir: %v", err)
}

View File

@ -224,12 +224,16 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
if len(p.SFiles) > 0 {
fmt.Fprintf(h, "asm %q %q %q\n", b.toolID("asm"), forcedAsmflags, p.Internal.Asmflags)
}
// GO386, GOARM, GOMIPS, etc.
baseArch := strings.TrimSuffix(cfg.BuildContext.GOARCH, "le")
fmt.Fprintf(h, "GO$GOARCH=%s\n", os.Getenv("GO"+strings.ToUpper(baseArch)))
key, val := cfg.GetArchEnv()
fmt.Fprintf(h, "%s=%s\n", key, val)
// TODO(rsc): Convince compiler team not to add more magic environment variables,
// or perhaps restrict the environment variables passed to subprocesses.
// Because these are clumsy, undocumented special-case hacks
// for debugging the compiler, they are not settable using 'go env -w',
// and so here we use os.Getenv, not cfg.Getenv.
magic := []string{
"GOCLOBBERDEADHASH",
"GOSSAFUNC",
@ -1115,21 +1119,16 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) {
if p != nil {
fmt.Fprintf(h, "linkflags %q\n", p.Internal.Ldflags)
}
fmt.Fprintf(h, "GO$GOARCH=%s\n", os.Getenv("GO"+strings.ToUpper(cfg.BuildContext.GOARCH))) // GO386, GOARM, etc
// GO386, GOARM, GOMIPS, etc.
key, val := cfg.GetArchEnv()
fmt.Fprintf(h, "%s=%s\n", key, val)
// The linker writes source file paths that say GOROOT_FINAL.
fmt.Fprintf(h, "GOROOT=%s\n", cfg.GOROOT_FINAL)
// TODO(rsc): Convince linker team not to add more magic environment variables,
// or perhaps restrict the environment variables passed to subprocesses.
magic := []string{
"GO_EXTLINK_ENABLED",
}
for _, env := range magic {
if x := os.Getenv(env); x != "" {
fmt.Fprintf(h, "magic %s=%s\n", env, x)
}
}
// GO_EXTLINK_ENABLED controls whether the external linker is used.
fmt.Fprintf(h, "GO_EXTLINK_ENABLED=%s\n", cfg.Getenv("GO_EXTLINK_ENABLED"))
// TODO(rsc): Do cgo settings and flags need to be included?
// Or external linker settings and flags?
@ -2192,8 +2191,8 @@ func (b *Builder) gccld(p *load.Package, objdir, outfile string, flags []string,
// Grab these before main helpfully overwrites them.
var (
origCC = os.Getenv("CC")
origCXX = os.Getenv("CXX")
origCC = cfg.Getenv("CC")
origCXX = cfg.Getenv("CXX")
)
// gccCmd returns a gcc command line prefix
@ -2225,7 +2224,7 @@ func (b *Builder) cxxExe() []string {
// fcExe returns the FC compiler setting without all the extra flags we add implicitly.
func (b *Builder) fcExe() []string {
return b.compilerExe(os.Getenv("FC"), "gfortran")
return b.compilerExe(cfg.Getenv("FC"), "gfortran")
}
// compilerExe returns the compiler to use given an
@ -2391,7 +2390,7 @@ func (b *Builder) gccArchArgs() []string {
// envList returns the value of the given environment variable broken
// into fields, using the default value when the variable is empty.
func envList(key, def string) []string {
v := os.Getenv(key)
v := cfg.Getenv(key)
if v == "" {
v = def
}
@ -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
// via CGO_LDFLAGS
if len(ffiles) > 0 {
fc := os.Getenv("FC")
fc := cfg.Getenv("FC")
if fc == "" {
fc = "gfortran"
}

View File

@ -26,7 +26,7 @@ var GccgoName, GccgoBin string
var gccgoErr error
func init() {
GccgoName = os.Getenv("GCCGO")
GccgoName = cfg.Getenv("GCCGO")
if GccgoName == "" {
GccgoName = "gccgo"
}
@ -44,7 +44,7 @@ func (gccgoToolchain) linker() string {
}
func (gccgoToolchain) ar() string {
ar := os.Getenv("AR")
ar := cfg.Getenv("AR")
if ar == "" {
ar = "ar"
}
@ -479,7 +479,7 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string
ldflags = append(ldflags, "-lobjc")
}
if fortran {
fc := os.Getenv("FC")
fc := cfg.Getenv("FC")
if fc == "" {
fc = "gfortran"
}

View File

@ -30,12 +30,13 @@
package work
import (
"cmd/go/internal/load"
"fmt"
"internal/lazyregexp"
"os"
"regexp"
"strings"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
)
var re = lazyregexp.New
@ -229,14 +230,14 @@ func checkFlags(name, source string, list []string, valid []*lazyregexp.Regexp,
allow *regexp.Regexp
disallow *regexp.Regexp
)
if env := os.Getenv("CGO_" + name + "_ALLOW"); env != "" {
if env := cfg.Getenv("CGO_" + name + "_ALLOW"); env != "" {
r, err := regexp.Compile(env)
if err != nil {
return fmt.Errorf("parsing $CGO_%s_ALLOW: %v", name, err)
}
allow = r
}
if env := os.Getenv("CGO_" + name + "_DISALLOW"); env != "" {
if env := cfg.Getenv("CGO_" + name + "_DISALLOW"); env != "" {
r, err := regexp.Compile(env)
if err != nil {
return fmt.Errorf("parsing $CGO_%s_DISALLOW: %v", name, err)

View File

@ -122,8 +122,14 @@ func main() {
os.Exit(2)
}
if !filepath.IsAbs(p) {
fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nFor more details see: 'go help gopath'\n", p)
os.Exit(2)
if cfg.Getenv("GOPATH") == "" {
// We inferred $GOPATH from $HOME and did a bad job at it.
// Instead of dying, uninfer it.
cfg.BuildContext.GOPATH = ""
} else {
fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nFor more details see: 'go help gopath'\n", p)
os.Exit(2)
}
}
}
}

View File

@ -0,0 +1,87 @@
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'

View File

@ -87,7 +87,7 @@ type gowasmFeatures struct {
SatConv bool
}
func (f *gowasmFeatures) String() string {
func (f gowasmFeatures) String() string {
var flags []string
if f.SatConv {
flags = append(flags, "satconv")

View File

@ -65,6 +65,7 @@
set -e
export GOENV=off
unset GOBIN # Issue 14340
unset GOFLAGS
unset GO111MODULE

View File

@ -46,13 +46,14 @@ if x%4==x--no-local goto nolocal
setlocal
:nolocal
set GOENV=off
set GOBUILDFAIL=0
set GOFLAGS=
set GO111MODULE=
if exist make.bat goto ok
echo Must run make.bat from Go src directory.
goto fail
goto fail
:ok
:: Clean old generated file that will cause problems in the build.

View File

@ -47,6 +47,7 @@ if(~ $1 -v) {
shift
}
GOENV=off
GOFLAGS=()
GO111MODULE=()
GOROOT = `{cd .. && pwd}

View File

@ -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 limit.
The GORACE variable configures the race detector, for programs built using -race.
See https://golang.org/doc/articles/race_detector.html for details.
The GOTRACEBACK variable controls the amount of output generated when a Go
program fails due to an unrecovered panic or an unexpected runtime condition.
By default, a failure prints a stack trace for the current goroutine,