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