Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/go/internal/cfg/cfg.go

Documentation: cmd/go/internal/cfg

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package cfg holds configuration shared by multiple parts
     6  // of the go command.
     7  package cfg
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"go/build"
    13  	"internal/buildcfg"
    14  	"internal/cfg"
    15  	"io"
    16  	"os"
    17  	"path/filepath"
    18  	"runtime"
    19  	"strings"
    20  	"sync"
    21  
    22  	"cmd/go/internal/fsys"
    23  )
    24  
    25  // These are general "build flags" used by build and other commands.
    26  var (
    27  	BuildA                 bool   // -a flag
    28  	BuildBuildmode         string // -buildmode flag
    29  	BuildContext           = defaultContext()
    30  	BuildMod               string                  // -mod flag
    31  	BuildModExplicit       bool                    // whether -mod was set explicitly
    32  	BuildModReason         string                  // reason -mod was set, if set by default
    33  	BuildI                 bool                    // -i flag
    34  	BuildLinkshared        bool                    // -linkshared flag
    35  	BuildMSan              bool                    // -msan flag
    36  	BuildN                 bool                    // -n flag
    37  	BuildO                 string                  // -o flag
    38  	BuildP                 = runtime.GOMAXPROCS(0) // -p flag
    39  	BuildPkgdir            string                  // -pkgdir flag
    40  	BuildRace              bool                    // -race flag
    41  	BuildToolexec          []string                // -toolexec flag
    42  	BuildToolchainName     string
    43  	BuildToolchainCompiler func() string
    44  	BuildToolchainLinker   func() string
    45  	BuildTrimpath          bool // -trimpath flag
    46  	BuildV                 bool // -v flag
    47  	BuildWork              bool // -work flag
    48  	BuildX                 bool // -x flag
    49  
    50  	ModCacheRW bool   // -modcacherw flag
    51  	ModFile    string // -modfile flag
    52  
    53  	CmdName string // "build", "install", "list", "mod tidy", etc.
    54  
    55  	DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)
    56  	DebugTrace       string // -debug-trace flag
    57  )
    58  
    59  func defaultContext() build.Context {
    60  	ctxt := build.Default
    61  	ctxt.JoinPath = filepath.Join // back door to say "do not use go command"
    62  
    63  	ctxt.GOROOT = findGOROOT()
    64  	if runtime.Compiler != "gccgo" {
    65  		// Note that we must use runtime.GOOS and runtime.GOARCH here,
    66  		// as the tool directory does not move based on environment
    67  		// variables. This matches the initialization of ToolDir in
    68  		// go/build, except for using ctxt.GOROOT rather than
    69  		// runtime.GOROOT.
    70  		build.ToolDir = filepath.Join(ctxt.GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
    71  	}
    72  
    73  	ctxt.GOPATH = envOr("GOPATH", ctxt.GOPATH)
    74  
    75  	// Override defaults computed in go/build with defaults
    76  	// from go environment configuration file, if known.
    77  	ctxt.GOOS = envOr("GOOS", ctxt.GOOS)
    78  	ctxt.GOARCH = envOr("GOARCH", ctxt.GOARCH)
    79  
    80  	// The experiments flags are based on GOARCH, so they may
    81  	// need to change.  TODO: This should be cleaned up.
    82  	buildcfg.UpdateExperiments(ctxt.GOOS, ctxt.GOARCH, envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT))
    83  	ctxt.ToolTags = nil
    84  	for _, exp := range buildcfg.EnabledExperiments() {
    85  		ctxt.ToolTags = append(ctxt.ToolTags, "goexperiment."+exp)
    86  	}
    87  
    88  	// The go/build rule for whether cgo is enabled is:
    89  	//	1. If $CGO_ENABLED is set, respect it.
    90  	//	2. Otherwise, if this is a cross-compile, disable cgo.
    91  	//	3. Otherwise, use built-in default for GOOS/GOARCH.
    92  	// Recreate that logic here with the new GOOS/GOARCH setting.
    93  	if v := Getenv("CGO_ENABLED"); v == "0" || v == "1" {
    94  		ctxt.CgoEnabled = v[0] == '1'
    95  	} else if ctxt.GOOS != runtime.GOOS || ctxt.GOARCH != runtime.GOARCH {
    96  		ctxt.CgoEnabled = false
    97  	} else {
    98  		// Use built-in default cgo setting for GOOS/GOARCH.
    99  		// Note that ctxt.GOOS/GOARCH are derived from the preference list
   100  		// (1) environment, (2) go/env file, (3) runtime constants,
   101  		// while go/build.Default.GOOS/GOARCH are derived from the preference list
   102  		// (1) environment, (2) runtime constants.
   103  		// We know ctxt.GOOS/GOARCH == runtime.GOOS/GOARCH;
   104  		// no matter how that happened, go/build.Default will make the
   105  		// same decision (either the environment variables are set explicitly
   106  		// to match the runtime constants, or else they are unset, in which
   107  		// case go/build falls back to the runtime constants), so
   108  		// go/build.Default.GOOS/GOARCH == runtime.GOOS/GOARCH.
   109  		// So ctxt.CgoEnabled (== go/build.Default.CgoEnabled) is correct
   110  		// as is and can be left unmodified.
   111  		// Nothing to do here.
   112  	}
   113  
   114  	ctxt.OpenFile = func(path string) (io.ReadCloser, error) {
   115  		return fsys.Open(path)
   116  	}
   117  	ctxt.ReadDir = fsys.ReadDir
   118  	ctxt.IsDir = func(path string) bool {
   119  		isDir, err := fsys.IsDir(path)
   120  		return err == nil && isDir
   121  	}
   122  
   123  	return ctxt
   124  }
   125  
   126  func init() {
   127  	BuildToolchainCompiler = func() string { return "missing-compiler" }
   128  	BuildToolchainLinker = func() string { return "missing-linker" }
   129  }
   130  
   131  // An EnvVar is an environment variable Name=Value.
   132  type EnvVar struct {
   133  	Name  string
   134  	Value string
   135  }
   136  
   137  // OrigEnv is the original environment of the program at startup.
   138  var OrigEnv []string
   139  
   140  // CmdEnv is the new environment for running go tool commands.
   141  // User binaries (during go test or go run) are run with OrigEnv,
   142  // not CmdEnv.
   143  var CmdEnv []EnvVar
   144  
   145  // Global build parameters (used during package load)
   146  var (
   147  	Goarch = BuildContext.GOARCH
   148  	Goos   = BuildContext.GOOS
   149  
   150  	ExeSuffix = exeSuffix()
   151  
   152  	// ModulesEnabled specifies whether the go command is running
   153  	// in module-aware mode (as opposed to GOPATH mode).
   154  	// It is equal to modload.Enabled, but not all packages can import modload.
   155  	ModulesEnabled bool
   156  )
   157  
   158  func exeSuffix() string {
   159  	if Goos == "windows" {
   160  		return ".exe"
   161  	}
   162  	return ""
   163  }
   164  
   165  var envCache struct {
   166  	once sync.Once
   167  	m    map[string]string
   168  }
   169  
   170  // EnvFile returns the name of the Go environment configuration file.
   171  func EnvFile() (string, error) {
   172  	if file := os.Getenv("GOENV"); file != "" {
   173  		if file == "off" {
   174  			return "", fmt.Errorf("GOENV=off")
   175  		}
   176  		return file, nil
   177  	}
   178  	dir, err := os.UserConfigDir()
   179  	if err != nil {
   180  		return "", err
   181  	}
   182  	if dir == "" {
   183  		return "", fmt.Errorf("missing user-config dir")
   184  	}
   185  	return filepath.Join(dir, "go/env"), nil
   186  }
   187  
   188  func initEnvCache() {
   189  	envCache.m = make(map[string]string)
   190  	file, _ := EnvFile()
   191  	if file == "" {
   192  		return
   193  	}
   194  	data, err := os.ReadFile(file)
   195  	if err != nil {
   196  		return
   197  	}
   198  
   199  	for len(data) > 0 {
   200  		// Get next line.
   201  		line := data
   202  		i := bytes.IndexByte(data, '\n')
   203  		if i >= 0 {
   204  			line, data = line[:i], data[i+1:]
   205  		} else {
   206  			data = nil
   207  		}
   208  
   209  		i = bytes.IndexByte(line, '=')
   210  		if i < 0 || line[0] < 'A' || 'Z' < line[0] {
   211  			// Line is missing = (or empty) or a comment or not a valid env name. Ignore.
   212  			// (This should not happen, since the file should be maintained almost
   213  			// exclusively by "go env -w", but better to silently ignore than to make
   214  			// the go command unusable just because somehow the env file has
   215  			// gotten corrupted.)
   216  			continue
   217  		}
   218  		key, val := line[:i], line[i+1:]
   219  		envCache.m[string(key)] = string(val)
   220  	}
   221  }
   222  
   223  // Getenv gets the value for the configuration key.
   224  // It consults the operating system environment
   225  // and then the go/env file.
   226  // If Getenv is called for a key that cannot be set
   227  // in the go/env file (for example GODEBUG), it panics.
   228  // This ensures that CanGetenv is accurate, so that
   229  // 'go env -w' stays in sync with what Getenv can retrieve.
   230  func Getenv(key string) string {
   231  	if !CanGetenv(key) {
   232  		switch key {
   233  		case "CGO_TEST_ALLOW", "CGO_TEST_DISALLOW", "CGO_test_ALLOW", "CGO_test_DISALLOW":
   234  			// used by internal/work/security_test.go; allow
   235  		default:
   236  			panic("internal error: invalid Getenv " + key)
   237  		}
   238  	}
   239  	val := os.Getenv(key)
   240  	if val != "" {
   241  		return val
   242  	}
   243  	envCache.once.Do(initEnvCache)
   244  	return envCache.m[key]
   245  }
   246  
   247  // CanGetenv reports whether key is a valid go/env configuration key.
   248  func CanGetenv(key string) bool {
   249  	return strings.Contains(cfg.KnownEnv, "\t"+key+"\n")
   250  }
   251  
   252  var (
   253  	GOROOT       = BuildContext.GOROOT
   254  	GOBIN        = Getenv("GOBIN")
   255  	GOROOTbin    = filepath.Join(GOROOT, "bin")
   256  	GOROOTpkg    = filepath.Join(GOROOT, "pkg")
   257  	GOROOTsrc    = filepath.Join(GOROOT, "src")
   258  	GOROOT_FINAL = findGOROOT_FINAL()
   259  	GOMODCACHE   = envOr("GOMODCACHE", gopathDir("pkg/mod"))
   260  
   261  	// Used in envcmd.MkEnv and build ID computations.
   262  	GOARM    = envOr("GOARM", fmt.Sprint(buildcfg.GOARM))
   263  	GO386    = envOr("GO386", buildcfg.GO386)
   264  	GOMIPS   = envOr("GOMIPS", buildcfg.GOMIPS)
   265  	GOMIPS64 = envOr("GOMIPS64", buildcfg.GOMIPS64)
   266  	GOPPC64  = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", buildcfg.GOPPC64))
   267  	GOWASM   = envOr("GOWASM", fmt.Sprint(buildcfg.GOWASM))
   268  
   269  	GOPROXY    = envOr("GOPROXY", "https://proxy.golang.org,direct")
   270  	GOSUMDB    = envOr("GOSUMDB", "sum.golang.org")
   271  	GOPRIVATE  = Getenv("GOPRIVATE")
   272  	GONOPROXY  = envOr("GONOPROXY", GOPRIVATE)
   273  	GONOSUMDB  = envOr("GONOSUMDB", GOPRIVATE)
   274  	GOINSECURE = Getenv("GOINSECURE")
   275  	GOVCS      = Getenv("GOVCS")
   276  )
   277  
   278  var SumdbDir = gopathDir("pkg/sumdb")
   279  
   280  // GetArchEnv returns the name and setting of the
   281  // GOARCH-specific architecture environment variable.
   282  // If the current architecture has no GOARCH-specific variable,
   283  // GetArchEnv returns empty key and value.
   284  func GetArchEnv() (key, val string) {
   285  	switch Goarch {
   286  	case "arm":
   287  		return "GOARM", GOARM
   288  	case "386":
   289  		return "GO386", GO386
   290  	case "mips", "mipsle":
   291  		return "GOMIPS", GOMIPS
   292  	case "mips64", "mips64le":
   293  		return "GOMIPS64", GOMIPS64
   294  	case "ppc64", "ppc64le":
   295  		return "GOPPC64", GOPPC64
   296  	case "wasm":
   297  		return "GOWASM", GOWASM
   298  	}
   299  	return "", ""
   300  }
   301  
   302  // envOr returns Getenv(key) if set, or else def.
   303  func envOr(key, def string) string {
   304  	val := Getenv(key)
   305  	if val == "" {
   306  		val = def
   307  	}
   308  	return val
   309  }
   310  
   311  // There is a copy of findGOROOT, isSameDir, and isGOROOT in
   312  // x/tools/cmd/godoc/goroot.go.
   313  // Try to keep them in sync for now.
   314  
   315  // findGOROOT returns the GOROOT value, using either an explicitly
   316  // provided environment variable, a GOROOT that contains the current
   317  // os.Executable value, or else the GOROOT that the binary was built
   318  // with from runtime.GOROOT().
   319  //
   320  // There is a copy of this code in x/tools/cmd/godoc/goroot.go.
   321  func findGOROOT() string {
   322  	if env := Getenv("GOROOT"); env != "" {
   323  		return filepath.Clean(env)
   324  	}
   325  	def := filepath.Clean(runtime.GOROOT())
   326  	if runtime.Compiler == "gccgo" {
   327  		// gccgo has no real GOROOT, and it certainly doesn't
   328  		// depend on the executable's location.
   329  		return def
   330  	}
   331  	exe, err := os.Executable()
   332  	if err == nil {
   333  		exe, err = filepath.Abs(exe)
   334  		if err == nil {
   335  			if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
   336  				// If def (runtime.GOROOT()) and dir are the same
   337  				// directory, prefer the spelling used in def.
   338  				if isSameDir(def, dir) {
   339  					return def
   340  				}
   341  				return dir
   342  			}
   343  			exe, err = filepath.EvalSymlinks(exe)
   344  			if err == nil {
   345  				if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
   346  					if isSameDir(def, dir) {
   347  						return def
   348  					}
   349  					return dir
   350  				}
   351  			}
   352  		}
   353  	}
   354  	return def
   355  }
   356  
   357  func findGOROOT_FINAL() string {
   358  	// $GOROOT_FINAL is only for use during make.bash
   359  	// so it is not settable using go/env, so we use os.Getenv here.
   360  	def := GOROOT
   361  	if env := os.Getenv("GOROOT_FINAL"); env != "" {
   362  		def = filepath.Clean(env)
   363  	}
   364  	return def
   365  }
   366  
   367  // isSameDir reports whether dir1 and dir2 are the same directory.
   368  func isSameDir(dir1, dir2 string) bool {
   369  	if dir1 == dir2 {
   370  		return true
   371  	}
   372  	info1, err1 := os.Stat(dir1)
   373  	info2, err2 := os.Stat(dir2)
   374  	return err1 == nil && err2 == nil && os.SameFile(info1, info2)
   375  }
   376  
   377  // isGOROOT reports whether path looks like a GOROOT.
   378  //
   379  // It does this by looking for the path/pkg/tool directory,
   380  // which is necessary for useful operation of the cmd/go tool,
   381  // and is not typically present in a GOPATH.
   382  //
   383  // There is a copy of this code in x/tools/cmd/godoc/goroot.go.
   384  func isGOROOT(path string) bool {
   385  	stat, err := os.Stat(filepath.Join(path, "pkg", "tool"))
   386  	if err != nil {
   387  		return false
   388  	}
   389  	return stat.IsDir()
   390  }
   391  
   392  func gopathDir(rel string) string {
   393  	list := filepath.SplitList(BuildContext.GOPATH)
   394  	if len(list) == 0 || list[0] == "" {
   395  		return ""
   396  	}
   397  	return filepath.Join(list[0], rel)
   398  }
   399  

View as plain text