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