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 }