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