1
2
3
4
5
6
7 package cfg
8
9 import (
10 "bytes"
11 "context"
12 "fmt"
13 "go/build"
14 "internal/buildcfg"
15 "internal/cfg"
16 "io"
17 "io/fs"
18 "os"
19 "path/filepath"
20 "runtime"
21 "strings"
22 "sync"
23 "time"
24
25 "cmd/go/internal/fsys"
26 "cmd/internal/pathcache"
27 )
28
29
30 var (
31 Goos = envOr("GOOS", build.Default.GOOS)
32 Goarch = envOr("GOARCH", build.Default.GOARCH)
33
34 ExeSuffix = exeSuffix()
35
36
37
38
39 ModulesEnabled bool
40 )
41
42 func exeSuffix() string {
43 if Goos == "windows" {
44 return ".exe"
45 }
46 return ""
47 }
48
49
50
51
52
53
54 var (
55 installedGOOS string
56 installedGOARCH string
57 )
58
59
60
61 func ToolExeSuffix() string {
62 if installedGOOS == "windows" {
63 return ".exe"
64 }
65 return ""
66 }
67
68
69 var (
70 BuildA bool
71 BuildBuildmode string
72 BuildBuildvcs = "auto"
73 BuildContext = defaultContext()
74 BuildMod string
75 BuildModExplicit bool
76 BuildModReason string
77 BuildLinkshared bool
78 BuildMSan bool
79 BuildASan bool
80 BuildCover bool
81 BuildCoverMode string
82 BuildCoverPkg []string
83 BuildJSON bool
84 BuildN bool
85 BuildO string
86 BuildP = runtime.GOMAXPROCS(0)
87 BuildPGO string
88 BuildPkgdir string
89 BuildRace bool
90 BuildToolexec []string
91 BuildToolchainName string
92 BuildToolchainCompiler func() string
93 BuildToolchainLinker func() string
94 BuildTrimpath bool
95 BuildV bool
96 BuildWork bool
97 BuildX bool
98
99 ModCacheRW bool
100 ModFile string
101
102 CmdName string
103
104 DebugActiongraph string
105 DebugTrace string
106 DebugRuntimeTrace string
107
108
109
110 GoPathError string
111 GOPATHChanged bool
112 CGOChanged bool
113 )
114
115 func defaultContext() build.Context {
116 ctxt := build.Default
117
118 ctxt.JoinPath = filepath.Join
119
120
121
122 ctxt.GOPATH, GOPATHChanged = EnvOrAndChanged("GOPATH", gopath(ctxt))
123 ctxt.GOOS = Goos
124 ctxt.GOARCH = Goarch
125
126
127 var save []string
128 for _, tag := range ctxt.ToolTags {
129 if !strings.HasPrefix(tag, "goexperiment.") {
130 save = append(save, tag)
131 }
132 }
133 ctxt.ToolTags = save
134
135
136
137
138
139
140
141
142
143 defaultCgoEnabled := ctxt.CgoEnabled
144 if ctxt.GOOS != runtime.GOOS || ctxt.GOARCH != runtime.GOARCH {
145 defaultCgoEnabled = false
146 } else {
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168 if ctxt.CgoEnabled {
169 if os.Getenv("CC") == "" {
170 cc := DefaultCC(ctxt.GOOS, ctxt.GOARCH)
171 if _, err := pathcache.LookPath(cc); err != nil {
172 defaultCgoEnabled = false
173 }
174 }
175 }
176 }
177 ctxt.CgoEnabled = defaultCgoEnabled
178 if v := Getenv("CGO_ENABLED"); v == "0" || v == "1" {
179 ctxt.CgoEnabled = v[0] == '1'
180 }
181 CGOChanged = ctxt.CgoEnabled != defaultCgoEnabled
182
183 ctxt.OpenFile = func(path string) (io.ReadCloser, error) {
184 return fsys.Open(path)
185 }
186 ctxt.ReadDir = func(path string) ([]fs.FileInfo, error) {
187
188 dirs, err := fsys.ReadDir(path)
189 infos := make([]fs.FileInfo, len(dirs))
190 for i, dir := range dirs {
191 infos[i] = &dirInfo{dir}
192 }
193 return infos, err
194 }
195 ctxt.IsDir = func(path string) bool {
196 isDir, err := fsys.IsDir(path)
197 return err == nil && isDir
198 }
199
200 return ctxt
201 }
202
203 func init() {
204 SetGOROOT(Getenv("GOROOT"), false)
205 }
206
207
208
209
210
211
212 func SetGOROOT(goroot string, isTestGo bool) {
213 BuildContext.GOROOT = goroot
214
215 GOROOT = goroot
216 if goroot == "" {
217 GOROOTbin = ""
218 GOROOTpkg = ""
219 GOROOTsrc = ""
220 } else {
221 GOROOTbin = filepath.Join(goroot, "bin")
222 GOROOTpkg = filepath.Join(goroot, "pkg")
223 GOROOTsrc = filepath.Join(goroot, "src")
224 }
225
226 installedGOOS = runtime.GOOS
227 installedGOARCH = runtime.GOARCH
228 if isTestGo {
229 if testOS := os.Getenv("TESTGO_GOHOSTOS"); testOS != "" {
230 installedGOOS = testOS
231 }
232 if testArch := os.Getenv("TESTGO_GOHOSTARCH"); testArch != "" {
233 installedGOARCH = testArch
234 }
235 }
236
237 if runtime.Compiler != "gccgo" {
238 if goroot == "" {
239 build.ToolDir = ""
240 } else {
241
242
243
244
245
246
247
248
249
250
251 build.ToolDir = filepath.Join(GOROOTpkg, "tool", installedGOOS+"_"+installedGOARCH)
252 }
253 }
254 }
255
256
257 var (
258
259 RawGOEXPERIMENT = envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT)
260
261
262 CleanGOEXPERIMENT = RawGOEXPERIMENT
263
264 Experiment *buildcfg.ExperimentFlags
265 ExperimentErr error
266 )
267
268 func init() {
269 Experiment, ExperimentErr = buildcfg.ParseGOEXPERIMENT(Goos, Goarch, RawGOEXPERIMENT)
270 if ExperimentErr != nil {
271 return
272 }
273
274
275 CleanGOEXPERIMENT = Experiment.String()
276
277
278 exps := Experiment.Enabled()
279 expTags := make([]string, 0, len(exps)+len(BuildContext.ToolTags))
280 for _, exp := range exps {
281 expTags = append(expTags, "goexperiment."+exp)
282 }
283 BuildContext.ToolTags = append(expTags, BuildContext.ToolTags...)
284 }
285
286
287 type EnvVar struct {
288 Name string
289 Value string
290 Changed bool
291 }
292
293
294 var OrigEnv []string
295
296
297
298
299 var CmdEnv []EnvVar
300
301 var envCache struct {
302 once sync.Once
303 m map[string]string
304 goroot map[string]string
305 }
306
307
308
309 func EnvFile() (string, bool, error) {
310 if file := os.Getenv("GOENV"); file != "" {
311 if file == "off" {
312 return "", false, fmt.Errorf("GOENV=off")
313 }
314 return file, true, nil
315 }
316 dir, err := os.UserConfigDir()
317 if err != nil {
318 return "", false, err
319 }
320 if dir == "" {
321 return "", false, fmt.Errorf("missing user-config dir")
322 }
323 return filepath.Join(dir, "go/env"), false, nil
324 }
325
326 func initEnvCache() {
327 envCache.m = make(map[string]string)
328 envCache.goroot = make(map[string]string)
329 if file, _, _ := EnvFile(); file != "" {
330 readEnvFile(file, "user")
331 }
332 goroot := findGOROOT(envCache.m["GOROOT"])
333 if goroot != "" {
334 readEnvFile(filepath.Join(goroot, "go.env"), "GOROOT")
335 }
336
337
338
339
340
341 envCache.m["GOROOT"] = goroot
342 }
343
344 func readEnvFile(file string, source string) {
345 if file == "" {
346 return
347 }
348 data, err := os.ReadFile(file)
349 if err != nil {
350 return
351 }
352
353 for len(data) > 0 {
354
355 line := data
356 i := bytes.IndexByte(data, '\n')
357 if i >= 0 {
358 line, data = line[:i], data[i+1:]
359 } else {
360 data = nil
361 }
362
363 i = bytes.IndexByte(line, '=')
364 if i < 0 || line[0] < 'A' || 'Z' < line[0] {
365
366
367
368
369
370
371 continue
372 }
373 key, val := line[:i], line[i+1:]
374
375 if source == "GOROOT" {
376 envCache.goroot[string(key)] = string(val)
377
378 if _, ok := envCache.m[string(key)]; ok {
379 continue
380 }
381 }
382 envCache.m[string(key)] = string(val)
383 }
384 }
385
386
387
388
389
390
391
392
393 func Getenv(key string) string {
394 if !CanGetenv(key) {
395 switch key {
396 case "CGO_TEST_ALLOW", "CGO_TEST_DISALLOW", "CGO_test_ALLOW", "CGO_test_DISALLOW":
397
398 default:
399 panic("internal error: invalid Getenv " + key)
400 }
401 }
402 val := os.Getenv(key)
403 if val != "" {
404 return val
405 }
406 envCache.once.Do(initEnvCache)
407 return envCache.m[key]
408 }
409
410
411 func CanGetenv(key string) bool {
412 envCache.once.Do(initEnvCache)
413 if _, ok := envCache.m[key]; ok {
414
415 return true
416 }
417 return strings.Contains(cfg.KnownEnv, "\t"+key+"\n")
418 }
419
420 var (
421 GOROOT string
422
423
424 GOROOTbin string
425 GOROOTpkg string
426 GOROOTsrc string
427
428 GOBIN = Getenv("GOBIN")
429 GOCACHEPROG, GOCACHEPROGChanged = EnvOrAndChanged("GOCACHEPROG", "")
430 GOMODCACHE, GOMODCACHEChanged = EnvOrAndChanged("GOMODCACHE", gopathDir("pkg/mod"))
431
432
433 GOARM64, goARM64Changed = EnvOrAndChanged("GOARM64", buildcfg.DefaultGOARM64)
434 GOARM, goARMChanged = EnvOrAndChanged("GOARM", buildcfg.DefaultGOARM)
435 GO386, go386Changed = EnvOrAndChanged("GO386", buildcfg.DefaultGO386)
436 GOAMD64, goAMD64Changed = EnvOrAndChanged("GOAMD64", buildcfg.DefaultGOAMD64)
437 GOMIPS, goMIPSChanged = EnvOrAndChanged("GOMIPS", buildcfg.DefaultGOMIPS)
438 GOMIPS64, goMIPS64Changed = EnvOrAndChanged("GOMIPS64", buildcfg.DefaultGOMIPS64)
439 GOPPC64, goPPC64Changed = EnvOrAndChanged("GOPPC64", buildcfg.DefaultGOPPC64)
440 GORISCV64, goRISCV64Changed = EnvOrAndChanged("GORISCV64", buildcfg.DefaultGORISCV64)
441 GOWASM, goWASMChanged = EnvOrAndChanged("GOWASM", fmt.Sprint(buildcfg.GOWASM))
442
443 GOFIPS140, GOFIPS140Changed = EnvOrAndChanged("GOFIPS140", buildcfg.DefaultGOFIPS140)
444 GOPROXY, GOPROXYChanged = EnvOrAndChanged("GOPROXY", "")
445 GOSUMDB, GOSUMDBChanged = EnvOrAndChanged("GOSUMDB", "")
446 GOPRIVATE = Getenv("GOPRIVATE")
447 GONOPROXY, GONOPROXYChanged = EnvOrAndChanged("GONOPROXY", GOPRIVATE)
448 GONOSUMDB, GONOSUMDBChanged = EnvOrAndChanged("GONOSUMDB", GOPRIVATE)
449 GOINSECURE = Getenv("GOINSECURE")
450 GOVCS = Getenv("GOVCS")
451 GOAUTH, GOAUTHChanged = EnvOrAndChanged("GOAUTH", "netrc")
452 )
453
454
455
456 func EnvOrAndChanged(name, def string) (v string, changed bool) {
457 val := Getenv(name)
458 if val != "" {
459 v = val
460 if g, ok := envCache.goroot[name]; ok {
461 changed = val != g
462 } else {
463 changed = val != def
464 }
465 return v, changed
466 }
467 return def, false
468 }
469
470 var SumdbDir = gopathDir("pkg/sumdb")
471
472
473
474
475
476 func GetArchEnv() (key, val string, changed bool) {
477 switch Goarch {
478 case "arm":
479 return "GOARM", GOARM, goARMChanged
480 case "arm64":
481 return "GOARM64", GOARM64, goARM64Changed
482 case "386":
483 return "GO386", GO386, go386Changed
484 case "amd64":
485 return "GOAMD64", GOAMD64, goAMD64Changed
486 case "mips", "mipsle":
487 return "GOMIPS", GOMIPS, goMIPSChanged
488 case "mips64", "mips64le":
489 return "GOMIPS64", GOMIPS64, goMIPS64Changed
490 case "ppc64", "ppc64le":
491 return "GOPPC64", GOPPC64, goPPC64Changed
492 case "riscv64":
493 return "GORISCV64", GORISCV64, goRISCV64Changed
494 case "wasm":
495 return "GOWASM", GOWASM, goWASMChanged
496 }
497 return "", "", false
498 }
499
500
501 func envOr(key, def string) string {
502 val := Getenv(key)
503 if val == "" {
504 val = def
505 }
506 return val
507 }
508
509
510
511
512
513
514
515
516
517
518
519 func findGOROOT(env string) string {
520 if env == "" {
521
522
523
524 env = os.Getenv("GOROOT")
525 }
526 if env != "" {
527 return filepath.Clean(env)
528 }
529 def := ""
530 if r := runtime.GOROOT(); r != "" {
531 def = filepath.Clean(r)
532 }
533 if runtime.Compiler == "gccgo" {
534
535
536 return def
537 }
538
539
540
541
542 canonical := func(dir string) string {
543 if isSameDir(def, dir) {
544 return def
545 }
546 return dir
547 }
548
549 exe, err := os.Executable()
550 if err == nil {
551 exe, err = filepath.Abs(exe)
552 if err == nil {
553
554
555
556 if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
557 return canonical(dir)
558 }
559 if dir := filepath.Join(exe, "../../.."); isGOROOT(dir) {
560 return canonical(dir)
561 }
562
563
564
565
566
567
568 exe, err = filepath.EvalSymlinks(exe)
569 if err == nil {
570 if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
571 return canonical(dir)
572 }
573 if dir := filepath.Join(exe, "../../.."); isGOROOT(dir) {
574 return canonical(dir)
575 }
576 }
577 }
578 }
579 return def
580 }
581
582
583 func isSameDir(dir1, dir2 string) bool {
584 if dir1 == dir2 {
585 return true
586 }
587 info1, err1 := os.Stat(dir1)
588 info2, err2 := os.Stat(dir2)
589 return err1 == nil && err2 == nil && os.SameFile(info1, info2)
590 }
591
592
593
594
595
596
597
598
599 func isGOROOT(path string) bool {
600 stat, err := os.Stat(filepath.Join(path, "pkg", "tool"))
601 if err != nil {
602 return false
603 }
604 return stat.IsDir()
605 }
606
607 func gopathDir(rel string) string {
608 list := filepath.SplitList(BuildContext.GOPATH)
609 if len(list) == 0 || list[0] == "" {
610 return ""
611 }
612 return filepath.Join(list[0], rel)
613 }
614
615
616 func gopath(ctxt build.Context) string {
617 if len(ctxt.GOPATH) > 0 {
618 return ctxt.GOPATH
619 }
620 env := "HOME"
621 if runtime.GOOS == "windows" {
622 env = "USERPROFILE"
623 } else if runtime.GOOS == "plan9" {
624 env = "home"
625 }
626 if home := os.Getenv(env); home != "" {
627 def := filepath.Join(home, "go")
628 if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) {
629 GoPathError = "cannot set GOROOT as GOPATH"
630 }
631 return ""
632 }
633 GoPathError = fmt.Sprintf("%s is not set", env)
634 return ""
635 }
636
637
638
639 func WithBuildXWriter(ctx context.Context, xLog io.Writer) context.Context {
640 return context.WithValue(ctx, buildXContextKey{}, xLog)
641 }
642
643 type buildXContextKey struct{}
644
645
646
647 func BuildXWriter(ctx context.Context) (io.Writer, bool) {
648 if !BuildX {
649 return nil, false
650 }
651 if v := ctx.Value(buildXContextKey{}); v != nil {
652 return v.(io.Writer), true
653 }
654 return os.Stderr, true
655 }
656
657
658
659
660 type dirInfo struct {
661 dir fs.DirEntry
662 }
663
664 func (d *dirInfo) Name() string { return d.dir.Name() }
665 func (d *dirInfo) IsDir() bool { return d.dir.IsDir() }
666 func (d *dirInfo) Mode() fs.FileMode { return d.dir.Type() }
667
668 func (d *dirInfo) Size() int64 { panic("dirInfo.Size") }
669 func (d *dirInfo) ModTime() time.Time { panic("dirInfo.ModTime") }
670 func (d *dirInfo) Sys() any { panic("dirInfo.Sys") }
671
View as plain text