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.
5 // Package cfg holds configuration shared by multiple parts
21 "cmd/go/internal/fsys"
26 // These are general "build flags" used by build and other commands.
28 BuildA bool // -a flag
29 BuildBuildmode string // -buildmode flag
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 BuildN bool // -n flag
38 BuildO string // -o flag
39 BuildP = runtime.GOMAXPROCS(0) // -p flag
40 BuildPkgdir string // -pkgdir flag
41 BuildRace bool // -race flag
42 BuildToolexec []string // -toolexec flag
43 BuildToolchainName string
44 BuildToolchainCompiler func() string
45 BuildToolchainLinker func() string
46 BuildTrimpath bool // -trimpath flag
47 BuildV bool // -v flag
48 BuildWork bool // -work flag
49 BuildX bool // -x flag
51 ModCacheRW bool // -modcacherw flag
52 ModFile string // -modfile flag
54 CmdName string // "build", "install", "list", "mod tidy", etc.
56 DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)
57 DebugTrace string // -debug-trace flag
60 func defaultContext() build.Context {
62 ctxt.JoinPath = filepath.Join // back door to say "do not use go command"
64 ctxt.GOROOT = findGOROOT()
65 if runtime.Compiler != "gccgo" {
66 // Note that we must use runtime.GOOS and runtime.GOARCH here,
67 // as the tool directory does not move based on environment
68 // variables. This matches the initialization of ToolDir in
69 // go/build, except for using ctxt.GOROOT rather than
71 build.ToolDir = filepath.Join(ctxt.GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
74 ctxt.GOPATH = envOr("GOPATH", ctxt.GOPATH)
76 // Override defaults computed in go/build with defaults
77 // from go environment configuration file, if known.
78 ctxt.GOOS = envOr("GOOS", ctxt.GOOS)
79 ctxt.GOARCH = envOr("GOARCH", ctxt.GOARCH)
81 // The go/build rule for whether cgo is enabled is:
82 // 1. If $CGO_ENABLED is set, respect it.
83 // 2. Otherwise, if this is a cross-compile, disable cgo.
84 // 3. Otherwise, use built-in default for GOOS/GOARCH.
85 // Recreate that logic here with the new GOOS/GOARCH setting.
86 if v := Getenv("CGO_ENABLED"); v == "0" || v == "1" {
87 ctxt.CgoEnabled = v[0] == '1'
88 } else if ctxt.GOOS != runtime.GOOS || ctxt.GOARCH != runtime.GOARCH {
89 ctxt.CgoEnabled = false
91 // Use built-in default cgo setting for GOOS/GOARCH.
92 // Note that ctxt.GOOS/GOARCH are derived from the preference list
93 // (1) environment, (2) go/env file, (3) runtime constants,
94 // while go/build.Default.GOOS/GOARCH are derived from the preference list
95 // (1) environment, (2) runtime constants.
96 // We know ctxt.GOOS/GOARCH == runtime.GOOS/GOARCH;
97 // no matter how that happened, go/build.Default will make the
98 // same decision (either the environment variables are set explicitly
99 // to match the runtime constants, or else they are unset, in which
100 // case go/build falls back to the runtime constants), so
101 // go/build.Default.GOOS/GOARCH == runtime.GOOS/GOARCH.
102 // So ctxt.CgoEnabled (== go/build.Default.CgoEnabled) is correct
103 // as is and can be left unmodified.
104 // Nothing to do here.
107 ctxt.OpenFile = func(path string) (io.ReadCloser, error) {
108 return fsys.Open(path)
110 ctxt.ReadDir = fsys.ReadDir
111 ctxt.IsDir = func(path string) bool {
112 isDir, err := fsys.IsDir(path)
113 return err == nil && isDir
120 BuildToolchainCompiler = func() string { return "missing-compiler" }
121 BuildToolchainLinker = func() string { return "missing-linker" }
124 // An EnvVar is an environment variable Name=Value.
130 // OrigEnv is the original environment of the program at startup.
133 // CmdEnv is the new environment for running go tool commands.
134 // User binaries (during go test or go run) are run with OrigEnv,
138 // Global build parameters (used during package load)
140 Goarch = BuildContext.GOARCH
141 Goos = BuildContext.GOOS
143 ExeSuffix = exeSuffix()
145 // ModulesEnabled specifies whether the go command is running
146 // in module-aware mode (as opposed to GOPATH mode).
147 // It is equal to modload.Enabled, but not all packages can import modload.
151 func exeSuffix() string {
152 if Goos == "windows" {
158 var envCache struct {
163 // EnvFile returns the name of the Go environment configuration file.
164 func EnvFile() (string, error) {
165 if file := os.Getenv("GOENV"); file != "" {
167 return "", fmt.Errorf("GOENV=off")
171 dir, err := os.UserConfigDir()
176 return "", fmt.Errorf("missing user-config dir")
178 return filepath.Join(dir, "go/env"), nil
181 func initEnvCache() {
182 envCache.m = make(map[string]string)
187 data, err := os.ReadFile(file)
195 i := bytes.IndexByte(data, '\n')
197 line, data = line[:i], data[i+1:]
202 i = bytes.IndexByte(line, '=')
203 if i < 0 || line[0] < 'A' || 'Z' < line[0] {
204 // Line is missing = (or empty) or a comment or not a valid env name. Ignore.
205 // (This should not happen, since the file should be maintained almost
206 // exclusively by "go env -w", but better to silently ignore than to make
207 // the go command unusable just because somehow the env file has
208 // gotten corrupted.)
211 key, val := line[:i], line[i+1:]
212 envCache.m[string(key)] = string(val)
216 // Getenv gets the value for the configuration key.
217 // It consults the operating system environment
218 // and then the go/env file.
219 // If Getenv is called for a key that cannot be set
220 // in the go/env file (for example GODEBUG), it panics.
221 // This ensures that CanGetenv is accurate, so that
222 // 'go env -w' stays in sync with what Getenv can retrieve.
223 func Getenv(key string) string {
226 case "CGO_TEST_ALLOW", "CGO_TEST_DISALLOW", "CGO_test_ALLOW", "CGO_test_DISALLOW":
227 // used by internal/work/security_test.go; allow
229 panic("internal error: invalid Getenv " + key)
232 val := os.Getenv(key)
236 envCache.once.Do(initEnvCache)
237 return envCache.m[key]
240 // CanGetenv reports whether key is a valid go/env configuration key.
241 func CanGetenv(key string) bool {
242 return strings.Contains(cfg.KnownEnv, "\t"+key+"\n")
246 GOROOT = BuildContext.GOROOT
247 GOBIN = Getenv("GOBIN")
248 GOROOTbin = filepath.Join(GOROOT, "bin")
249 GOROOTpkg = filepath.Join(GOROOT, "pkg")
250 GOROOTsrc = filepath.Join(GOROOT, "src")
251 GOROOT_FINAL = findGOROOT_FINAL()
252 GOMODCACHE = envOr("GOMODCACHE", gopathDir("pkg/mod"))
254 // Used in envcmd.MkEnv and build ID computations.
255 GOARM = envOr("GOARM", fmt.Sprint(objabi.GOARM))
256 GO386 = envOr("GO386", objabi.GO386)
257 GOMIPS = envOr("GOMIPS", objabi.GOMIPS)
258 GOMIPS64 = envOr("GOMIPS64", objabi.GOMIPS64)
259 GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64))
260 GOWASM = envOr("GOWASM", fmt.Sprint(objabi.GOWASM))
262 GOPROXY = envOr("GOPROXY", "https://proxy.golang.org,direct")
263 GOSUMDB = envOr("GOSUMDB", "sum.golang.org")
264 GOPRIVATE = Getenv("GOPRIVATE")
265 GONOPROXY = envOr("GONOPROXY", GOPRIVATE)
266 GONOSUMDB = envOr("GONOSUMDB", GOPRIVATE)
267 GOINSECURE = Getenv("GOINSECURE")
268 GOVCS = Getenv("GOVCS")
271 var SumdbDir = gopathDir("pkg/sumdb")
273 // GetArchEnv returns the name and setting of the
274 // GOARCH-specific architecture environment variable.
275 // If the current architecture has no GOARCH-specific variable,
276 // GetArchEnv returns empty key and value.
277 func GetArchEnv() (key, val string) {
280 return "GOARM", GOARM
282 return "GO386", GO386
283 case "mips", "mipsle":
284 return "GOMIPS", GOMIPS
285 case "mips64", "mips64le":
286 return "GOMIPS64", GOMIPS64
287 case "ppc64", "ppc64le":
288 return "GOPPC64", GOPPC64
290 return "GOWASM", GOWASM
295 // envOr returns Getenv(key) if set, or else def.
296 func envOr(key, def string) string {
304 // There is a copy of findGOROOT, isSameDir, and isGOROOT in
305 // x/tools/cmd/godoc/goroot.go.
306 // Try to keep them in sync for now.
308 // findGOROOT returns the GOROOT value, using either an explicitly
309 // provided environment variable, a GOROOT that contains the current
310 // os.Executable value, or else the GOROOT that the binary was built
311 // with from runtime.GOROOT().
313 // There is a copy of this code in x/tools/cmd/godoc/goroot.go.
314 func findGOROOT() string {
315 if env := Getenv("GOROOT"); env != "" {
316 return filepath.Clean(env)
318 def := filepath.Clean(runtime.GOROOT())
319 if runtime.Compiler == "gccgo" {
320 // gccgo has no real GOROOT, and it certainly doesn't
321 // depend on the executable's location.
324 exe, err := os.Executable()
326 exe, err = filepath.Abs(exe)
328 if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
329 // If def (runtime.GOROOT()) and dir are the same
330 // directory, prefer the spelling used in def.
331 if isSameDir(def, dir) {
336 exe, err = filepath.EvalSymlinks(exe)
338 if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
339 if isSameDir(def, dir) {
350 func findGOROOT_FINAL() string {
351 // $GOROOT_FINAL is only for use during make.bash
352 // so it is not settable using go/env, so we use os.Getenv here.
354 if env := os.Getenv("GOROOT_FINAL"); env != "" {
355 def = filepath.Clean(env)
360 // isSameDir reports whether dir1 and dir2 are the same directory.
361 func isSameDir(dir1, dir2 string) bool {
365 info1, err1 := os.Stat(dir1)
366 info2, err2 := os.Stat(dir2)
367 return err1 == nil && err2 == nil && os.SameFile(info1, info2)
370 // isGOROOT reports whether path looks like a GOROOT.
372 // It does this by looking for the path/pkg/tool directory,
373 // which is necessary for useful operation of the cmd/go tool,
374 // and is not typically present in a GOPATH.
376 // There is a copy of this code in x/tools/cmd/godoc/goroot.go.
377 func isGOROOT(path string) bool {
378 stat, err := os.Stat(filepath.Join(path, "pkg", "tool"))
385 func gopathDir(rel string) string {
386 list := filepath.SplitList(BuildContext.GOPATH)
387 if len(list) == 0 || list[0] == "" {
390 return filepath.Join(list[0], rel)