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