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