github.com/bir3/gocompiler@v0.3.205/src/cmd/gocmd/internal/cfg/cfg.go.orig (about)

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