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

View as plain text