...

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

View as plain text