]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/go/internal/cfg/cfg.go
cmd/go: introduce GOROOT/go.env and move proxy/sumdb config there
[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         "fmt"
12         "go/build"
13         "internal/buildcfg"
14         "internal/cfg"
15         "io"
16         "os"
17         "os/exec"
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)
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(Getenv("GOROOT"), 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         if file, _ := EnvFile(); file != "" {
307                 readEnvFile(file, "user")
308         }
309         goroot := findGOROOT(envCache.m["GOROOT"])
310         if goroot != "" {
311                 readEnvFile(filepath.Join(goroot, "go.env"), "GOROOT")
312         }
313
314         // Save the goroot for func init calling SetGOROOT,
315         // and also overwrite anything that might have been in go.env.
316         // It makes no sense for GOROOT/go.env to specify
317         // a different GOROOT.
318         envCache.m["GOROOT"] = goroot
319 }
320
321 func readEnvFile(file string, source string) {
322         if file == "" {
323                 return
324         }
325         data, err := os.ReadFile(file)
326         if err != nil {
327                 return
328         }
329
330         for len(data) > 0 {
331                 // Get next line.
332                 line := data
333                 i := bytes.IndexByte(data, '\n')
334                 if i >= 0 {
335                         line, data = line[:i], data[i+1:]
336                 } else {
337                         data = nil
338                 }
339
340                 i = bytes.IndexByte(line, '=')
341                 if i < 0 || line[0] < 'A' || 'Z' < line[0] {
342                         // Line is missing = (or empty) or a comment or not a valid env name. Ignore.
343                         // This should not happen in the user file, since the file should be maintained almost
344                         // exclusively by "go env -w", but better to silently ignore than to make
345                         // the go command unusable just because somehow the env file has
346                         // gotten corrupted.
347                         // In the GOROOT/go.env file, we expect comments.
348                         continue
349                 }
350                 key, val := line[:i], line[i+1:]
351
352                 if source == "GOROOT" {
353                         // In the GOROOT/go.env file, do not overwrite fields loaded from the user's go/env file.
354                         if _, ok := envCache.m[string(key)]; ok {
355                                 continue
356                         }
357                 }
358                 envCache.m[string(key)] = string(val)
359         }
360 }
361
362 // Getenv gets the value for the configuration key.
363 // It consults the operating system environment
364 // and then the go/env file.
365 // If Getenv is called for a key that cannot be set
366 // in the go/env file (for example GODEBUG), it panics.
367 // This ensures that CanGetenv is accurate, so that
368 // 'go env -w' stays in sync with what Getenv can retrieve.
369 func Getenv(key string) string {
370         if !CanGetenv(key) {
371                 switch key {
372                 case "CGO_TEST_ALLOW", "CGO_TEST_DISALLOW", "CGO_test_ALLOW", "CGO_test_DISALLOW":
373                         // used by internal/work/security_test.go; allow
374                 default:
375                         panic("internal error: invalid Getenv " + key)
376                 }
377         }
378         val := os.Getenv(key)
379         if val != "" {
380                 return val
381         }
382         envCache.once.Do(initEnvCache)
383         return envCache.m[key]
384 }
385
386 // CanGetenv reports whether key is a valid go/env configuration key.
387 func CanGetenv(key string) bool {
388         return strings.Contains(cfg.KnownEnv, "\t"+key+"\n")
389 }
390
391 var (
392         GOROOT       string
393         GOROOTbin    string
394         GOROOTpkg    string
395         GOROOTsrc    string
396         GOROOT_FINAL string
397         GOBIN        = Getenv("GOBIN")
398         GOMODCACHE   = envOr("GOMODCACHE", gopathDir("pkg/mod"))
399
400         // Used in envcmd.MkEnv and build ID computations.
401         GOARM    = envOr("GOARM", fmt.Sprint(buildcfg.GOARM))
402         GO386    = envOr("GO386", buildcfg.GO386)
403         GOAMD64  = envOr("GOAMD64", fmt.Sprintf("%s%d", "v", buildcfg.GOAMD64))
404         GOMIPS   = envOr("GOMIPS", buildcfg.GOMIPS)
405         GOMIPS64 = envOr("GOMIPS64", buildcfg.GOMIPS64)
406         GOPPC64  = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", buildcfg.GOPPC64))
407         GOWASM   = envOr("GOWASM", fmt.Sprint(buildcfg.GOWASM))
408
409         GOPROXY    = envOr("GOPROXY", "")
410         GOSUMDB    = envOr("GOSUMDB", "")
411         GOPRIVATE  = Getenv("GOPRIVATE")
412         GONOPROXY  = envOr("GONOPROXY", GOPRIVATE)
413         GONOSUMDB  = envOr("GONOSUMDB", GOPRIVATE)
414         GOINSECURE = Getenv("GOINSECURE")
415         GOVCS      = Getenv("GOVCS")
416 )
417
418 var SumdbDir = gopathDir("pkg/sumdb")
419
420 // GetArchEnv returns the name and setting of the
421 // GOARCH-specific architecture environment variable.
422 // If the current architecture has no GOARCH-specific variable,
423 // GetArchEnv returns empty key and value.
424 func GetArchEnv() (key, val string) {
425         switch Goarch {
426         case "arm":
427                 return "GOARM", GOARM
428         case "386":
429                 return "GO386", GO386
430         case "amd64":
431                 return "GOAMD64", GOAMD64
432         case "mips", "mipsle":
433                 return "GOMIPS", GOMIPS
434         case "mips64", "mips64le":
435                 return "GOMIPS64", GOMIPS64
436         case "ppc64", "ppc64le":
437                 return "GOPPC64", GOPPC64
438         case "wasm":
439                 return "GOWASM", GOWASM
440         }
441         return "", ""
442 }
443
444 // envOr returns Getenv(key) if set, or else def.
445 func envOr(key, def string) string {
446         val := Getenv(key)
447         if val == "" {
448                 val = def
449         }
450         return val
451 }
452
453 // There is a copy of findGOROOT, isSameDir, and isGOROOT in
454 // x/tools/cmd/godoc/goroot.go.
455 // Try to keep them in sync for now.
456
457 // findGOROOT returns the GOROOT value, using either an explicitly
458 // provided environment variable, a GOROOT that contains the current
459 // os.Executable value, or else the GOROOT that the binary was built
460 // with from runtime.GOROOT().
461 //
462 // There is a copy of this code in x/tools/cmd/godoc/goroot.go.
463 func findGOROOT(env string) string {
464         if env == "" {
465                 // Not using Getenv because findGOROOT is called
466                 // to find the GOROOT/go.env file. initEnvCache
467                 // has passed in the setting from the user go/env file.
468                 env = os.Getenv("GOROOT")
469         }
470         if env != "" {
471                 return filepath.Clean(env)
472         }
473         def := ""
474         if r := runtime.GOROOT(); r != "" {
475                 def = filepath.Clean(r)
476         }
477         if runtime.Compiler == "gccgo" {
478                 // gccgo has no real GOROOT, and it certainly doesn't
479                 // depend on the executable's location.
480                 return def
481         }
482         exe, err := os.Executable()
483         if err == nil {
484                 exe, err = filepath.Abs(exe)
485                 if err == nil {
486                         if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
487                                 // If def (runtime.GOROOT()) and dir are the same
488                                 // directory, prefer the spelling used in def.
489                                 if isSameDir(def, dir) {
490                                         return def
491                                 }
492                                 return dir
493                         }
494                         exe, err = filepath.EvalSymlinks(exe)
495                         if err == nil {
496                                 if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
497                                         if isSameDir(def, dir) {
498                                                 return def
499                                         }
500                                         return dir
501                                 }
502                         }
503                 }
504         }
505         return def
506 }
507
508 func findGOROOT_FINAL(goroot string) string {
509         // $GOROOT_FINAL is only for use during make.bash
510         // so it is not settable using go/env, so we use os.Getenv here.
511         def := goroot
512         if env := os.Getenv("GOROOT_FINAL"); env != "" {
513                 def = filepath.Clean(env)
514         }
515         return def
516 }
517
518 // isSameDir reports whether dir1 and dir2 are the same directory.
519 func isSameDir(dir1, dir2 string) bool {
520         if dir1 == dir2 {
521                 return true
522         }
523         info1, err1 := os.Stat(dir1)
524         info2, err2 := os.Stat(dir2)
525         return err1 == nil && err2 == nil && os.SameFile(info1, info2)
526 }
527
528 // isGOROOT reports whether path looks like a GOROOT.
529 //
530 // It does this by looking for the path/pkg/tool directory,
531 // which is necessary for useful operation of the cmd/go tool,
532 // and is not typically present in a GOPATH.
533 //
534 // There is a copy of this code in x/tools/cmd/godoc/goroot.go.
535 func isGOROOT(path string) bool {
536         stat, err := os.Stat(filepath.Join(path, "pkg", "tool"))
537         if err != nil {
538                 return false
539         }
540         return stat.IsDir()
541 }
542
543 func gopathDir(rel string) string {
544         list := filepath.SplitList(BuildContext.GOPATH)
545         if len(list) == 0 || list[0] == "" {
546                 return ""
547         }
548         return filepath.Join(list[0], rel)
549 }
550
551 func gopath(ctxt build.Context) string {
552         if len(ctxt.GOPATH) > 0 {
553                 return ctxt.GOPATH
554         }
555         env := "HOME"
556         if runtime.GOOS == "windows" {
557                 env = "USERPROFILE"
558         } else if runtime.GOOS == "plan9" {
559                 env = "home"
560         }
561         if home := os.Getenv(env); home != "" {
562                 def := filepath.Join(home, "go")
563                 if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) {
564                         GoPathError = "cannot set GOROOT as GOPATH"
565                 }
566                 return ""
567         }
568         GoPathError = fmt.Sprintf("%s is not set", env)
569         return ""
570 }