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