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

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

View as plain text