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