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 }
291
292
293
294 func EnvFile() (string, bool, error) {
295 if file := os.Getenv("GOENV"); file != "" {
296 if file == "off" {
297 return "", false, fmt.Errorf("GOENV=off")
298 }
299 return file, true, nil
300 }
301 dir, err := os.UserConfigDir()
302 if err != nil {
303 return "", false, err
304 }
305 if dir == "" {
306 return "", false, fmt.Errorf("missing user-config dir")
307 }
308 return filepath.Join(dir, "go/env"), false, nil
309 }
310
311 func initEnvCache() {
312 envCache.m = make(map[string]string)
313 if file, _, _ := EnvFile(); file != "" {
314 readEnvFile(file, "user")
315 }
316 goroot := findGOROOT(envCache.m["GOROOT"])
317 if goroot != "" {
318 readEnvFile(filepath.Join(goroot, "go.env"), "GOROOT")
319 }
320
321
322
323
324
325 envCache.m["GOROOT"] = goroot
326 }
327
328 func readEnvFile(file string, source string) {
329 if file == "" {
330 return
331 }
332 data, err := os.ReadFile(file)
333 if err != nil {
334 return
335 }
336
337 for len(data) > 0 {
338
339 line := data
340 i := bytes.IndexByte(data, '\n')
341 if i >= 0 {
342 line, data = line[:i], data[i+1:]
343 } else {
344 data = nil
345 }
346
347 i = bytes.IndexByte(line, '=')
348 if i < 0 || line[0] < 'A' || 'Z' < line[0] {
349
350
351
352
353
354
355 continue
356 }
357 key, val := line[:i], line[i+1:]
358
359 if source == "GOROOT" {
360
361 if _, ok := envCache.m[string(key)]; ok {
362 continue
363 }
364 }
365 envCache.m[string(key)] = string(val)
366 }
367 }
368
369
370
371
372
373
374
375
376 func Getenv(key string) string {
377 if !CanGetenv(key) {
378 switch key {
379 case "CGO_TEST_ALLOW", "CGO_TEST_DISALLOW", "CGO_test_ALLOW", "CGO_test_DISALLOW":
380
381 default:
382 panic("internal error: invalid Getenv " + key)
383 }
384 }
385 val := os.Getenv(key)
386 if val != "" {
387 return val
388 }
389 envCache.once.Do(initEnvCache)
390 return envCache.m[key]
391 }
392
393
394 func CanGetenv(key string) bool {
395 envCache.once.Do(initEnvCache)
396 if _, ok := envCache.m[key]; ok {
397
398 return true
399 }
400 return strings.Contains(cfg.KnownEnv, "\t"+key+"\n")
401 }
402
403 var (
404 GOROOT string
405
406
407 GOROOTbin string
408 GOROOTpkg string
409 GOROOTsrc string
410
411 GOBIN = Getenv("GOBIN")
412 GOMODCACHE, GOMODCACHEChanged = EnvOrAndChanged("GOMODCACHE", gopathDir("pkg/mod"))
413
414
415 GOARM64, goARM64Changed = EnvOrAndChanged("GOARM64", fmt.Sprint(buildcfg.GOARM64))
416 GOARM, goARMChanged = EnvOrAndChanged("GOARM", fmt.Sprint(buildcfg.GOARM))
417 GO386, go386Changed = EnvOrAndChanged("GO386", buildcfg.GO386)
418 GOAMD64, goAMD64Changed = EnvOrAndChanged("GOAMD64", fmt.Sprintf("%s%d", "v", buildcfg.GOAMD64))
419 GOMIPS, goMIPSChanged = EnvOrAndChanged("GOMIPS", buildcfg.GOMIPS)
420 GOMIPS64, goMIPS64Changed = EnvOrAndChanged("GOMIPS64", buildcfg.GOMIPS64)
421 GOPPC64, goPPC64Changed = EnvOrAndChanged("GOPPC64", fmt.Sprintf("%s%d", "power", buildcfg.GOPPC64))
422 GORISCV64, goRISCV64Changed = EnvOrAndChanged("GORISCV64", fmt.Sprintf("rva%du64", buildcfg.GORISCV64))
423 GOWASM, goWASMChanged = EnvOrAndChanged("GOWASM", fmt.Sprint(buildcfg.GOWASM))
424
425 GOPROXY, GOPROXYChanged = EnvOrAndChanged("GOPROXY", "")
426 GOSUMDB, GOSUMDBChanged = EnvOrAndChanged("GOSUMDB", "")
427 GOPRIVATE = Getenv("GOPRIVATE")
428 GONOPROXY, GONOPROXYChanged = EnvOrAndChanged("GONOPROXY", GOPRIVATE)
429 GONOSUMDB, GONOSUMDBChanged = EnvOrAndChanged("GONOSUMDB", GOPRIVATE)
430 GOINSECURE = Getenv("GOINSECURE")
431 GOVCS = Getenv("GOVCS")
432 )
433
434
435
436 func EnvOrAndChanged(name, def string) (string, bool) {
437 val := Getenv(name)
438 if val != "" {
439 return val, val != def
440 }
441 return def, false
442 }
443
444 var SumdbDir = gopathDir("pkg/sumdb")
445
446
447
448
449
450 func GetArchEnv() (key, val string, changed bool) {
451 switch Goarch {
452 case "arm":
453 return "GOARM", GOARM, goARMChanged
454 case "arm64":
455 return "GOARM64", GOARM64, goARM64Changed
456 case "386":
457 return "GO386", GO386, go386Changed
458 case "amd64":
459 return "GOAMD64", GOAMD64, goAMD64Changed
460 case "mips", "mipsle":
461 return "GOMIPS", GOMIPS, goMIPSChanged
462 case "mips64", "mips64le":
463 return "GOMIPS64", GOMIPS64, goMIPS64Changed
464 case "ppc64", "ppc64le":
465 return "GOPPC64", GOPPC64, goPPC64Changed
466 case "riscv64":
467 return "GORISCV64", GORISCV64, goRISCV64Changed
468 case "wasm":
469 return "GOWASM", GOWASM, goWASMChanged
470 }
471 return "", "", false
472 }
473
474
475 func envOr(key, def string) string {
476 val := Getenv(key)
477 if val == "" {
478 val = def
479 }
480 return val
481 }
482
483
484
485
486
487
488
489
490
491
492
493 func findGOROOT(env string) string {
494 if env == "" {
495
496
497
498 env = os.Getenv("GOROOT")
499 }
500 if env != "" {
501 return filepath.Clean(env)
502 }
503 def := ""
504 if r := runtime.GOROOT(); r != "" {
505 def = filepath.Clean(r)
506 }
507 if runtime.Compiler == "gccgo" {
508
509
510 return def
511 }
512
513
514
515
516 canonical := func(dir string) string {
517 if isSameDir(def, dir) {
518 return def
519 }
520 return dir
521 }
522
523 exe, err := os.Executable()
524 if err == nil {
525 exe, err = filepath.Abs(exe)
526 if err == nil {
527
528
529
530 if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
531 return canonical(dir)
532 }
533 if dir := filepath.Join(exe, "../../.."); isGOROOT(dir) {
534 return canonical(dir)
535 }
536
537
538
539
540
541
542 exe, err = filepath.EvalSymlinks(exe)
543 if err == nil {
544 if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
545 return canonical(dir)
546 }
547 if dir := filepath.Join(exe, "../../.."); isGOROOT(dir) {
548 return canonical(dir)
549 }
550 }
551 }
552 }
553 return def
554 }
555
556
557 func isSameDir(dir1, dir2 string) bool {
558 if dir1 == dir2 {
559 return true
560 }
561 info1, err1 := os.Stat(dir1)
562 info2, err2 := os.Stat(dir2)
563 return err1 == nil && err2 == nil && os.SameFile(info1, info2)
564 }
565
566
567
568
569
570
571
572
573 func isGOROOT(path string) bool {
574 stat, err := os.Stat(filepath.Join(path, "pkg", "tool"))
575 if err != nil {
576 return false
577 }
578 return stat.IsDir()
579 }
580
581 func gopathDir(rel string) string {
582 list := filepath.SplitList(BuildContext.GOPATH)
583 if len(list) == 0 || list[0] == "" {
584 return ""
585 }
586 return filepath.Join(list[0], rel)
587 }
588
589
590 func gopath(ctxt build.Context) string {
591 if len(ctxt.GOPATH) > 0 {
592 return ctxt.GOPATH
593 }
594 env := "HOME"
595 if runtime.GOOS == "windows" {
596 env = "USERPROFILE"
597 } else if runtime.GOOS == "plan9" {
598 env = "home"
599 }
600 if home := os.Getenv(env); home != "" {
601 def := filepath.Join(home, "go")
602 if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) {
603 GoPathError = "cannot set GOROOT as GOPATH"
604 }
605 return ""
606 }
607 GoPathError = fmt.Sprintf("%s is not set", env)
608 return ""
609 }
610
611
612
613 func WithBuildXWriter(ctx context.Context, xLog io.Writer) context.Context {
614 return context.WithValue(ctx, buildXContextKey{}, xLog)
615 }
616
617 type buildXContextKey struct{}
618
619
620
621 func BuildXWriter(ctx context.Context) (io.Writer, bool) {
622 if !BuildX {
623 return nil, false
624 }
625 if v := ctx.Value(buildXContextKey{}); v != nil {
626 return v.(io.Writer), true
627 }
628 return os.Stderr, true
629 }
630
View as plain text