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  	"context"
    12  	"fmt"
    13  	"go/build"
    14  	"internal/buildcfg"
    15  	"internal/cfg"
    16  	"internal/platform"
    17  	"io"
    18  	"io/fs"
    19  	"os"
    20  	"path/filepath"
    21  	"runtime"
    22  	"strings"
    23  	"sync"
    24  	"time"
    25  
    26  	"cmd/go/internal/fsys"
    27  	"cmd/internal/pathcache"
    28  )
    29  
    30  // Global build parameters (used during package load)
    31  var (
    32  	Goos   = envOr("GOOS", build.Default.GOOS)
    33  	Goarch = envOr("GOARCH", build.Default.GOARCH)
    34  
    35  	ExeSuffix = exeSuffix()
    36  
    37  	// ModulesEnabled specifies whether the go command is running
    38  	// in module-aware mode (as opposed to GOPATH mode).
    39  	// It is equal to modload.Enabled, but not all packages can import modload.
    40  	ModulesEnabled bool
    41  )
    42  
    43  func exeSuffix() string {
    44  	if Goos == "windows" {
    45  		return ".exe"
    46  	}
    47  	return ""
    48  }
    49  
    50  // Configuration for tools installed to GOROOT/bin.
    51  // Normally these match runtime.GOOS and runtime.GOARCH,
    52  // but when testing a cross-compiled cmd/go they will
    53  // indicate the GOOS and GOARCH of the installed cmd/go
    54  // rather than the test binary.
    55  var (
    56  	installedGOOS   string
    57  	installedGOARCH string
    58  )
    59  
    60  // ToolExeSuffix returns the suffix for executables installed
    61  // in build.ToolDir.
    62  func ToolExeSuffix() string {
    63  	if installedGOOS == "windows" {
    64  		return ".exe"
    65  	}
    66  	return ""
    67  }
    68  
    69  // These are general "build flags" used by build and other commands.
    70  var (
    71  	BuildA                 bool     // -a flag
    72  	BuildBuildmode         string   // -buildmode flag
    73  	BuildBuildvcs          = "auto" // -buildvcs flag: "true", "false", or "auto"
    74  	BuildContext           = defaultContext()
    75  	BuildMod               string                  // -mod flag
    76  	BuildModExplicit       bool                    // whether -mod was set explicitly
    77  	BuildModReason         string                  // reason -mod was set, if set by default
    78  	BuildLinkshared        bool                    // -linkshared flag
    79  	BuildMSan              bool                    // -msan flag
    80  	BuildASan              bool                    // -asan flag
    81  	BuildCover             bool                    // -cover flag
    82  	BuildCoverMode         string                  // -covermode flag
    83  	BuildCoverPkg          []string                // -coverpkg flag
    84  	BuildJSON              bool                    // -json flag
    85  	BuildN                 bool                    // -n flag
    86  	BuildO                 string                  // -o flag
    87  	BuildP                 = runtime.GOMAXPROCS(0) // -p flag
    88  	BuildPGO               string                  // -pgo flag
    89  	BuildPkgdir            string                  // -pkgdir flag
    90  	BuildRace              bool                    // -race flag
    91  	BuildToolexec          []string                // -toolexec flag
    92  	BuildToolchainName     string
    93  	BuildToolchainCompiler func() string
    94  	BuildToolchainLinker   func() string
    95  	BuildTrimpath          bool // -trimpath flag
    96  	BuildV                 bool // -v flag
    97  	BuildWork              bool // -work flag
    98  	BuildX                 bool // -x flag
    99  
   100  	ModCacheRW bool   // -modcacherw flag
   101  	ModFile    string // -modfile flag
   102  
   103  	CmdName string // "build", "install", "list", "mod tidy", etc.
   104  
   105  	DebugActiongraph  string // -debug-actiongraph flag (undocumented, unstable)
   106  	DebugTrace        string // -debug-trace flag
   107  	DebugRuntimeTrace string // -debug-runtime-trace flag (undocumented, unstable)
   108  
   109  	// GoPathError is set when GOPATH is not set. it contains an
   110  	// explanation why GOPATH is unset.
   111  	GoPathError   string
   112  	GOPATHChanged bool
   113  	CGOChanged    bool
   114  )
   115  
   116  func defaultContext() build.Context {
   117  	ctxt := build.Default
   118  
   119  	ctxt.JoinPath = filepath.Join // back door to say "do not use go command"
   120  
   121  	// Override defaults computed in go/build with defaults
   122  	// from go environment configuration file, if known.
   123  	ctxt.GOPATH, GOPATHChanged = EnvOrAndChanged("GOPATH", gopath(ctxt))
   124  	ctxt.GOOS = Goos
   125  	ctxt.GOARCH = Goarch
   126  
   127  	// Clear the GOEXPERIMENT-based tool tags, which we will recompute later.
   128  	var save []string
   129  	for _, tag := range ctxt.ToolTags {
   130  		if !strings.HasPrefix(tag, "goexperiment.") {
   131  			save = append(save, tag)
   132  		}
   133  	}
   134  	ctxt.ToolTags = save
   135  
   136  	_, archEnv, _ := GetArchEnv()
   137  	if archEnv != "" {
   138  		ctxt.ToolTags = append(ctxt.ToolTags, Goarch+"."+archEnv)
   139  	}
   140  
   141  	// The go/build rule for whether cgo is enabled is:
   142  	//  1. If $CGO_ENABLED is set, respect it.
   143  	//  2. Otherwise, if this is a cross-compile, disable cgo.
   144  	//  3. Otherwise, use built-in default for GOOS/GOARCH.
   145  	//
   146  	// Recreate that logic here with the new GOOS/GOARCH setting.
   147  	// We need to run steps 2 and 3 to determine what the default value
   148  	// of CgoEnabled would be for computing CGOChanged.
   149  	defaultCgoEnabled := false
   150  	if buildcfg.DefaultCGO_ENABLED == "1" {
   151  		defaultCgoEnabled = true
   152  	} else if buildcfg.DefaultCGO_ENABLED == "0" {
   153  	} else if runtime.GOARCH == ctxt.GOARCH && runtime.GOOS == ctxt.GOOS {
   154  		defaultCgoEnabled = platform.CgoSupported(ctxt.GOOS, ctxt.GOARCH)
   155  		// Use built-in default cgo setting for GOOS/GOARCH.
   156  		// Note that ctxt.GOOS/GOARCH are derived from the preference list
   157  		// (1) environment, (2) go/env file, (3) runtime constants,
   158  		// while go/build.Default.GOOS/GOARCH are derived from the preference list
   159  		// (1) environment, (2) runtime constants.
   160  		//
   161  		// We know ctxt.GOOS/GOARCH == runtime.GOOS/GOARCH;
   162  		// no matter how that happened, go/build.Default will make the
   163  		// same decision (either the environment variables are set explicitly
   164  		// to match the runtime constants, or else they are unset, in which
   165  		// case go/build falls back to the runtime constants), so
   166  		// go/build.Default.GOOS/GOARCH == runtime.GOOS/GOARCH.
   167  		// So ctxt.CgoEnabled (== go/build.Default.CgoEnabled) is correct
   168  		// as is and can be left unmodified.
   169  		//
   170  		// All that said, starting in Go 1.20 we layer one more rule
   171  		// on top of the go/build decision: if CC is unset and
   172  		// the default C compiler we'd look for is not in the PATH,
   173  		// we automatically default cgo to off.
   174  		// This makes go builds work automatically on systems
   175  		// without a C compiler installed.
   176  		if ctxt.CgoEnabled {
   177  			if os.Getenv("CC") == "" {
   178  				cc := DefaultCC(ctxt.GOOS, ctxt.GOARCH)
   179  				if _, err := pathcache.LookPath(cc); err != nil {
   180  					defaultCgoEnabled = false
   181  				}
   182  			}
   183  		}
   184  	}
   185  	ctxt.CgoEnabled = defaultCgoEnabled
   186  	if v := Getenv("CGO_ENABLED"); v == "0" || v == "1" {
   187  		ctxt.CgoEnabled = v[0] == '1'
   188  	}
   189  	CGOChanged = ctxt.CgoEnabled != defaultCgoEnabled
   190  
   191  	ctxt.OpenFile = func(path string) (io.ReadCloser, error) {
   192  		return fsys.Open(path)
   193  	}
   194  	ctxt.ReadDir = func(path string) ([]fs.FileInfo, error) {
   195  		// Convert []fs.DirEntry to []fs.FileInfo using dirInfo.
   196  		dirs, err := fsys.ReadDir(path)
   197  		infos := make([]fs.FileInfo, len(dirs))
   198  		for i, dir := range dirs {
   199  			infos[i] = &dirInfo{dir}
   200  		}
   201  		return infos, err
   202  	}
   203  	ctxt.IsDir = func(path string) bool {
   204  		isDir, err := fsys.IsDir(path)
   205  		return err == nil && isDir
   206  	}
   207  
   208  	return ctxt
   209  }
   210  
   211  func init() {
   212  	SetGOROOT(Getenv("GOROOT"), false)
   213  }
   214  
   215  // ForceHost forces GOOS and GOARCH to runtime.GOOS and runtime.GOARCH.
   216  // This is used by go tool to build tools for the go command's own
   217  // GOOS and GOARCH.
   218  func ForceHost() {
   219  	Goos = runtime.GOOS
   220  	Goarch = runtime.GOARCH
   221  	ExeSuffix = exeSuffix()
   222  	GO386 = buildcfg.DefaultGO386
   223  	GOAMD64 = buildcfg.DefaultGOAMD64
   224  	GOARM = buildcfg.DefaultGOARM
   225  	GOARM64 = buildcfg.DefaultGOARM64
   226  	GOMIPS = buildcfg.DefaultGOMIPS
   227  	GOMIPS64 = buildcfg.DefaultGOMIPS64
   228  	GOPPC64 = buildcfg.DefaultGOPPC64
   229  	GORISCV64 = buildcfg.DefaultGORISCV64
   230  	GOWASM = ""
   231  
   232  	// Recompute the build context using Goos and Goarch to
   233  	// set the correct value for ctx.CgoEnabled.
   234  	BuildContext = defaultContext()
   235  	// Call SetGOROOT to properly set the GOROOT on the new context.
   236  	SetGOROOT(Getenv("GOROOT"), false)
   237  	// Recompute experiments: the settings determined depend on GOOS and GOARCH.
   238  	// This will also update the BuildContext's tool tags to include the new
   239  	// experiment tags.
   240  	computeExperiment()
   241  }
   242  
   243  // SetGOROOT sets GOROOT and associated variables to the given values.
   244  //
   245  // If isTestGo is true, build.ToolDir is set based on the TESTGO_GOHOSTOS and
   246  // TESTGO_GOHOSTARCH environment variables instead of runtime.GOOS and
   247  // runtime.GOARCH.
   248  func SetGOROOT(goroot string, isTestGo bool) {
   249  	BuildContext.GOROOT = goroot
   250  
   251  	GOROOT = goroot
   252  	if goroot == "" {
   253  		GOROOTbin = ""
   254  		GOROOTpkg = ""
   255  		GOROOTsrc = ""
   256  	} else {
   257  		GOROOTbin = filepath.Join(goroot, "bin")
   258  		GOROOTpkg = filepath.Join(goroot, "pkg")
   259  		GOROOTsrc = filepath.Join(goroot, "src")
   260  	}
   261  
   262  	installedGOOS = runtime.GOOS
   263  	installedGOARCH = runtime.GOARCH
   264  	if isTestGo {
   265  		if testOS := os.Getenv("TESTGO_GOHOSTOS"); testOS != "" {
   266  			installedGOOS = testOS
   267  		}
   268  		if testArch := os.Getenv("TESTGO_GOHOSTARCH"); testArch != "" {
   269  			installedGOARCH = testArch
   270  		}
   271  	}
   272  
   273  	if runtime.Compiler != "gccgo" {
   274  		if goroot == "" {
   275  			build.ToolDir = ""
   276  		} else {
   277  			// Note that we must use the installed OS and arch here: the tool
   278  			// directory does not move based on environment variables, and even if we
   279  			// are testing a cross-compiled cmd/go all of the installed packages and
   280  			// tools would have been built using the native compiler and linker (and
   281  			// would spuriously appear stale if we used a cross-compiled compiler and
   282  			// linker).
   283  			//
   284  			// This matches the initialization of ToolDir in go/build, except for
   285  			// using ctxt.GOROOT and the installed GOOS and GOARCH rather than the
   286  			// GOROOT, GOOS, and GOARCH reported by the runtime package.
   287  			build.ToolDir = filepath.Join(GOROOTpkg, "tool", installedGOOS+"_"+installedGOARCH)
   288  		}
   289  	}
   290  }
   291  
   292  // Experiment configuration.
   293  var (
   294  	// RawGOEXPERIMENT is the GOEXPERIMENT value set by the user.
   295  	RawGOEXPERIMENT = envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT)
   296  	// CleanGOEXPERIMENT is the minimal GOEXPERIMENT value needed to reproduce the
   297  	// experiments enabled by RawGOEXPERIMENT.
   298  	CleanGOEXPERIMENT = RawGOEXPERIMENT
   299  
   300  	Experiment    *buildcfg.ExperimentFlags
   301  	ExperimentErr error
   302  )
   303  
   304  func init() {
   305  	computeExperiment()
   306  }
   307  
   308  func computeExperiment() {
   309  	Experiment, ExperimentErr = buildcfg.ParseGOEXPERIMENT(Goos, Goarch, RawGOEXPERIMENT)
   310  	if ExperimentErr != nil {
   311  		return
   312  	}
   313  
   314  	// GOEXPERIMENT is valid, so convert it to canonical form.
   315  	CleanGOEXPERIMENT = Experiment.String()
   316  
   317  	// Add build tags based on the experiments in effect.
   318  	exps := Experiment.Enabled()
   319  	expTags := make([]string, 0, len(exps)+len(BuildContext.ToolTags))
   320  	for _, exp := range exps {
   321  		expTags = append(expTags, "goexperiment."+exp)
   322  	}
   323  	BuildContext.ToolTags = append(expTags, BuildContext.ToolTags...)
   324  }
   325  
   326  // An EnvVar is an environment variable Name=Value.
   327  type EnvVar struct {
   328  	Name    string
   329  	Value   string
   330  	Changed bool // effective Value differs from default
   331  }
   332  
   333  // OrigEnv is the original environment of the program at startup.
   334  var OrigEnv []string
   335  
   336  // CmdEnv is the new environment for running go tool commands.
   337  // User binaries (during go test or go run) are run with OrigEnv,
   338  // not CmdEnv.
   339  var CmdEnv []EnvVar
   340  
   341  var envCache struct {
   342  	once   sync.Once
   343  	m      map[string]string
   344  	goroot map[string]string
   345  }
   346  
   347  // EnvFile returns the name of the Go environment configuration file,
   348  // and reports whether the effective value differs from the default.
   349  func EnvFile() (string, bool, error) {
   350  	if file := os.Getenv("GOENV"); file != "" {
   351  		if file == "off" {
   352  			return "", false, fmt.Errorf("GOENV=off")
   353  		}
   354  		return file, true, nil
   355  	}
   356  	dir, err := os.UserConfigDir()
   357  	if err != nil {
   358  		return "", false, err
   359  	}
   360  	if dir == "" {
   361  		return "", false, fmt.Errorf("missing user-config dir")
   362  	}
   363  	return filepath.Join(dir, "go/env"), false, nil
   364  }
   365  
   366  func initEnvCache() {
   367  	envCache.m = make(map[string]string)
   368  	envCache.goroot = make(map[string]string)
   369  	if file, _, _ := EnvFile(); file != "" {
   370  		readEnvFile(file, "user")
   371  	}
   372  	goroot := findGOROOT(envCache.m["GOROOT"])
   373  	if goroot != "" {
   374  		readEnvFile(filepath.Join(goroot, "go.env"), "GOROOT")
   375  	}
   376  
   377  	// Save the goroot for func init calling SetGOROOT,
   378  	// and also overwrite anything that might have been in go.env.
   379  	// It makes no sense for GOROOT/go.env to specify
   380  	// a different GOROOT.
   381  	envCache.m["GOROOT"] = goroot
   382  }
   383  
   384  func readEnvFile(file string, source string) {
   385  	if file == "" {
   386  		return
   387  	}
   388  	data, err := os.ReadFile(file)
   389  	if err != nil {
   390  		return
   391  	}
   392  
   393  	for len(data) > 0 {
   394  		// Get next line.
   395  		line := data
   396  		i := bytes.IndexByte(data, '\n')
   397  		if i >= 0 {
   398  			line, data = line[:i], data[i+1:]
   399  		} else {
   400  			data = nil
   401  		}
   402  
   403  		i = bytes.IndexByte(line, '=')
   404  		if i < 0 || line[0] < 'A' || 'Z' < line[0] {
   405  			// Line is missing = (or empty) or a comment or not a valid env name. Ignore.
   406  			// This should not happen in the user file, since the file should be maintained almost
   407  			// exclusively by "go env -w", but better to silently ignore than to make
   408  			// the go command unusable just because somehow the env file has
   409  			// gotten corrupted.
   410  			// In the GOROOT/go.env file, we expect comments.
   411  			continue
   412  		}
   413  		key, val := line[:i], line[i+1:]
   414  
   415  		if source == "GOROOT" {
   416  			envCache.goroot[string(key)] = string(val)
   417  			// In the GOROOT/go.env file, do not overwrite fields loaded from the user's go/env file.
   418  			if _, ok := envCache.m[string(key)]; ok {
   419  				continue
   420  			}
   421  		}
   422  		envCache.m[string(key)] = string(val)
   423  	}
   424  }
   425  
   426  // Getenv gets the value for the configuration key.
   427  // It consults the operating system environment
   428  // and then the go/env file.
   429  // If Getenv is called for a key that cannot be set
   430  // in the go/env file (for example GODEBUG), it panics.
   431  // This ensures that CanGetenv is accurate, so that
   432  // 'go env -w' stays in sync with what Getenv can retrieve.
   433  func Getenv(key string) string {
   434  	if !CanGetenv(key) {
   435  		switch key {
   436  		case "CGO_TEST_ALLOW", "CGO_TEST_DISALLOW", "CGO_test_ALLOW", "CGO_test_DISALLOW":
   437  			// used by internal/work/security_test.go; allow
   438  		default:
   439  			panic("internal error: invalid Getenv " + key)
   440  		}
   441  	}
   442  	val := os.Getenv(key)
   443  	if val != "" {
   444  		return val
   445  	}
   446  	envCache.once.Do(initEnvCache)
   447  	return envCache.m[key]
   448  }
   449  
   450  // CanGetenv reports whether key is a valid go/env configuration key.
   451  func CanGetenv(key string) bool {
   452  	envCache.once.Do(initEnvCache)
   453  	if _, ok := envCache.m[key]; ok {
   454  		// Assume anything in the user file or go.env file is valid.
   455  		return true
   456  	}
   457  	return strings.Contains(cfg.KnownEnv, "\t"+key+"\n")
   458  }
   459  
   460  var (
   461  	GOROOT string
   462  
   463  	// Either empty or produced by filepath.Join(GOROOT, …).
   464  	GOROOTbin string
   465  	GOROOTpkg string
   466  	GOROOTsrc string
   467  
   468  	GOBIN, GOBINChanged             = EnvOrAndChanged("GOBIN", "")
   469  	GOCACHEPROG, GOCACHEPROGChanged = EnvOrAndChanged("GOCACHEPROG", "")
   470  	GOMODCACHE, GOMODCACHEChanged   = EnvOrAndChanged("GOMODCACHE", gopathDir("pkg/mod"))
   471  
   472  	// Used in envcmd.MkEnv and build ID computations.
   473  	GOARM64, goARM64Changed     = EnvOrAndChanged("GOARM64", buildcfg.DefaultGOARM64)
   474  	GOARM, goARMChanged         = EnvOrAndChanged("GOARM", buildcfg.DefaultGOARM)
   475  	GO386, go386Changed         = EnvOrAndChanged("GO386", buildcfg.DefaultGO386)
   476  	GOAMD64, goAMD64Changed     = EnvOrAndChanged("GOAMD64", buildcfg.DefaultGOAMD64)
   477  	GOMIPS, goMIPSChanged       = EnvOrAndChanged("GOMIPS", buildcfg.DefaultGOMIPS)
   478  	GOMIPS64, goMIPS64Changed   = EnvOrAndChanged("GOMIPS64", buildcfg.DefaultGOMIPS64)
   479  	GOPPC64, goPPC64Changed     = EnvOrAndChanged("GOPPC64", buildcfg.DefaultGOPPC64)
   480  	GORISCV64, goRISCV64Changed = EnvOrAndChanged("GORISCV64", buildcfg.DefaultGORISCV64)
   481  	GOWASM, goWASMChanged       = EnvOrAndChanged("GOWASM", fmt.Sprint(buildcfg.GOWASM))
   482  
   483  	GOFIPS140, GOFIPS140Changed = EnvOrAndChanged("GOFIPS140", buildcfg.DefaultGOFIPS140)
   484  	GOPROXY, GOPROXYChanged     = EnvOrAndChanged("GOPROXY", "")
   485  	GOSUMDB, GOSUMDBChanged     = EnvOrAndChanged("GOSUMDB", "")
   486  	GOPRIVATE                   = Getenv("GOPRIVATE")
   487  	GONOPROXY, GONOPROXYChanged = EnvOrAndChanged("GONOPROXY", GOPRIVATE)
   488  	GONOSUMDB, GONOSUMDBChanged = EnvOrAndChanged("GONOSUMDB", GOPRIVATE)
   489  	GOINSECURE                  = Getenv("GOINSECURE")
   490  	GOVCS                       = Getenv("GOVCS")
   491  	GOAUTH, GOAUTHChanged       = EnvOrAndChanged("GOAUTH", "netrc")
   492  )
   493  
   494  // EnvOrAndChanged returns the environment variable value
   495  // and reports whether it differs from the default value.
   496  func EnvOrAndChanged(name, def string) (v string, changed bool) {
   497  	val := Getenv(name)
   498  	if val != "" {
   499  		v = val
   500  		if g, ok := envCache.goroot[name]; ok {
   501  			changed = val != g
   502  		} else {
   503  			changed = val != def
   504  		}
   505  		return v, changed
   506  	}
   507  	return def, false
   508  }
   509  
   510  var SumdbDir = gopathDir("pkg/sumdb")
   511  
   512  // GetArchEnv returns the name and setting of the
   513  // GOARCH-specific architecture environment variable.
   514  // If the current architecture has no GOARCH-specific variable,
   515  // GetArchEnv returns empty key and value.
   516  func GetArchEnv() (key, val string, changed bool) {
   517  	switch Goarch {
   518  	case "arm":
   519  		return "GOARM", GOARM, goARMChanged
   520  	case "arm64":
   521  		return "GOARM64", GOARM64, goARM64Changed
   522  	case "386":
   523  		return "GO386", GO386, go386Changed
   524  	case "amd64":
   525  		return "GOAMD64", GOAMD64, goAMD64Changed
   526  	case "mips", "mipsle":
   527  		return "GOMIPS", GOMIPS, goMIPSChanged
   528  	case "mips64", "mips64le":
   529  		return "GOMIPS64", GOMIPS64, goMIPS64Changed
   530  	case "ppc64", "ppc64le":
   531  		return "GOPPC64", GOPPC64, goPPC64Changed
   532  	case "riscv64":
   533  		return "GORISCV64", GORISCV64, goRISCV64Changed
   534  	case "wasm":
   535  		return "GOWASM", GOWASM, goWASMChanged
   536  	}
   537  	return "", "", false
   538  }
   539  
   540  // envOr returns Getenv(key) if set, or else def.
   541  func envOr(key, def string) string {
   542  	val := Getenv(key)
   543  	if val == "" {
   544  		val = def
   545  	}
   546  	return val
   547  }
   548  
   549  // There is a copy of findGOROOT, isSameDir, and isGOROOT in
   550  // x/tools/cmd/godoc/goroot.go.
   551  // Try to keep them in sync for now.
   552  
   553  // findGOROOT returns the GOROOT value, using either an explicitly
   554  // provided environment variable, a GOROOT that contains the current
   555  // os.Executable value, or else the GOROOT that the binary was built
   556  // with from runtime.GOROOT().
   557  //
   558  // There is a copy of this code in x/tools/cmd/godoc/goroot.go.
   559  func findGOROOT(env string) string {
   560  	if env == "" {
   561  		// Not using Getenv because findGOROOT is called
   562  		// to find the GOROOT/go.env file. initEnvCache
   563  		// has passed in the setting from the user go/env file.
   564  		env = os.Getenv("GOROOT")
   565  	}
   566  	if env != "" {
   567  		return filepath.Clean(env)
   568  	}
   569  	def := ""
   570  	if r := runtime.GOROOT(); r != "" {
   571  		def = filepath.Clean(r)
   572  	}
   573  	if runtime.Compiler == "gccgo" {
   574  		// gccgo has no real GOROOT, and it certainly doesn't
   575  		// depend on the executable's location.
   576  		return def
   577  	}
   578  
   579  	// canonical returns a directory path that represents
   580  	// the same directory as dir,
   581  	// preferring the spelling in def if the two are the same.
   582  	canonical := func(dir string) string {
   583  		if isSameDir(def, dir) {
   584  			return def
   585  		}
   586  		return dir
   587  	}
   588  
   589  	exe, err := os.Executable()
   590  	if err == nil {
   591  		exe, err = filepath.Abs(exe)
   592  		if err == nil {
   593  			// cmd/go may be installed in GOROOT/bin or GOROOT/bin/GOOS_GOARCH,
   594  			// depending on whether it was cross-compiled with a different
   595  			// GOHOSTOS (see https://go.dev/issue/62119). Try both.
   596  			if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
   597  				return canonical(dir)
   598  			}
   599  			if dir := filepath.Join(exe, "../../.."); isGOROOT(dir) {
   600  				return canonical(dir)
   601  			}
   602  
   603  			// Depending on what was passed on the command line, it is possible
   604  			// that os.Executable is a symlink (like /usr/local/bin/go) referring
   605  			// to a binary installed in a real GOROOT elsewhere
   606  			// (like /usr/lib/go/bin/go).
   607  			// Try to find that GOROOT by resolving the symlinks.
   608  			exe, err = filepath.EvalSymlinks(exe)
   609  			if err == nil {
   610  				if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
   611  					return canonical(dir)
   612  				}
   613  				if dir := filepath.Join(exe, "../../.."); isGOROOT(dir) {
   614  					return canonical(dir)
   615  				}
   616  			}
   617  		}
   618  	}
   619  	return def
   620  }
   621  
   622  // isSameDir reports whether dir1 and dir2 are the same directory.
   623  func isSameDir(dir1, dir2 string) bool {
   624  	if dir1 == dir2 {
   625  		return true
   626  	}
   627  	info1, err1 := os.Stat(dir1)
   628  	info2, err2 := os.Stat(dir2)
   629  	return err1 == nil && err2 == nil && os.SameFile(info1, info2)
   630  }
   631  
   632  // isGOROOT reports whether path looks like a GOROOT.
   633  //
   634  // It does this by looking for the path/pkg/tool directory,
   635  // which is necessary for useful operation of the cmd/go tool,
   636  // and is not typically present in a GOPATH.
   637  //
   638  // There is a copy of this code in x/tools/cmd/godoc/goroot.go.
   639  func isGOROOT(path string) bool {
   640  	stat, err := os.Stat(filepath.Join(path, "pkg", "tool"))
   641  	if err != nil {
   642  		return false
   643  	}
   644  	return stat.IsDir()
   645  }
   646  
   647  func gopathDir(rel string) string {
   648  	list := filepath.SplitList(BuildContext.GOPATH)
   649  	if len(list) == 0 || list[0] == "" {
   650  		return ""
   651  	}
   652  	return filepath.Join(list[0], rel)
   653  }
   654  
   655  // Keep consistent with go/build.defaultGOPATH.
   656  func gopath(ctxt build.Context) string {
   657  	if len(ctxt.GOPATH) > 0 {
   658  		return ctxt.GOPATH
   659  	}
   660  	env := "HOME"
   661  	if runtime.GOOS == "windows" {
   662  		env = "USERPROFILE"
   663  	} else if runtime.GOOS == "plan9" {
   664  		env = "home"
   665  	}
   666  	if home := os.Getenv(env); home != "" {
   667  		def := filepath.Join(home, "go")
   668  		if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) {
   669  			GoPathError = "cannot set GOROOT as GOPATH"
   670  		}
   671  		return ""
   672  	}
   673  	GoPathError = fmt.Sprintf("%s is not set", env)
   674  	return ""
   675  }
   676  
   677  // WithBuildXWriter returns a Context in which BuildX output is written
   678  // to given io.Writer.
   679  func WithBuildXWriter(ctx context.Context, xLog io.Writer) context.Context {
   680  	return context.WithValue(ctx, buildXContextKey{}, xLog)
   681  }
   682  
   683  type buildXContextKey struct{}
   684  
   685  // BuildXWriter returns nil if BuildX is false, or
   686  // the writer to which BuildX output should be written otherwise.
   687  func BuildXWriter(ctx context.Context) (io.Writer, bool) {
   688  	if !BuildX {
   689  		return nil, false
   690  	}
   691  	if v := ctx.Value(buildXContextKey{}); v != nil {
   692  		return v.(io.Writer), true
   693  	}
   694  	return os.Stderr, true
   695  }
   696  
   697  // A dirInfo implements fs.FileInfo from fs.DirEntry.
   698  // We know that go/build doesn't use the non-DirEntry parts,
   699  // so we can panic instead of doing difficult work.
   700  type dirInfo struct {
   701  	dir fs.DirEntry
   702  }
   703  
   704  func (d *dirInfo) Name() string      { return d.dir.Name() }
   705  func (d *dirInfo) IsDir() bool       { return d.dir.IsDir() }
   706  func (d *dirInfo) Mode() fs.FileMode { return d.dir.Type() }
   707  
   708  func (d *dirInfo) Size() int64        { panic("dirInfo.Size") }
   709  func (d *dirInfo) ModTime() time.Time { panic("dirInfo.ModTime") }
   710  func (d *dirInfo) Sys() any           { panic("dirInfo.Sys") }
   711  

View as plain text