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