]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/go/internal/cfg/cfg.go
810189c15d782589d8aa3acce6cde782680db282
[gostls13.git] / src / cmd / go / internal / cfg / cfg.go
1 // Copyright 2017 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Package cfg holds configuration shared by multiple parts
6 // of the go command.
7 package cfg
8
9 import (
10         "bytes"
11         "fmt"
12         "go/build"
13         "internal/cfg"
14         "io"
15         "os"
16         "path/filepath"
17         "runtime"
18         "strings"
19         "sync"
20
21         "cmd/go/internal/fsys"
22
23         "cmd/internal/objabi"
24 )
25
26 // These are general "build flags" used by build and other commands.
27 var (
28         BuildA                 bool   // -a flag
29         BuildBuildmode         string // -buildmode flag
30         BuildContext           = defaultContext()
31         BuildMod               string                  // -mod flag
32         BuildModExplicit       bool                    // whether -mod was set explicitly
33         BuildModReason         string                  // reason -mod was set, if set by default
34         BuildI                 bool                    // -i flag
35         BuildLinkshared        bool                    // -linkshared flag
36         BuildMSan              bool                    // -msan flag
37         BuildN                 bool                    // -n flag
38         BuildO                 string                  // -o flag
39         BuildP                 = runtime.GOMAXPROCS(0) // -p flag
40         BuildPkgdir            string                  // -pkgdir flag
41         BuildRace              bool                    // -race flag
42         BuildToolexec          []string                // -toolexec flag
43         BuildToolchainName     string
44         BuildToolchainCompiler func() string
45         BuildToolchainLinker   func() string
46         BuildTrimpath          bool // -trimpath flag
47         BuildV                 bool // -v flag
48         BuildWork              bool // -work flag
49         BuildX                 bool // -x flag
50
51         ModCacheRW bool   // -modcacherw flag
52         ModFile    string // -modfile flag
53
54         CmdName string // "build", "install", "list", "mod tidy", etc.
55
56         DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)
57         DebugTrace       string // -debug-trace flag
58 )
59
60 func defaultContext() build.Context {
61         ctxt := build.Default
62         ctxt.JoinPath = filepath.Join // back door to say "do not use go command"
63
64         ctxt.GOROOT = findGOROOT()
65         if runtime.Compiler != "gccgo" {
66                 // Note that we must use runtime.GOOS and runtime.GOARCH here,
67                 // as the tool directory does not move based on environment
68                 // variables. This matches the initialization of ToolDir in
69                 // go/build, except for using ctxt.GOROOT rather than
70                 // runtime.GOROOT.
71                 build.ToolDir = filepath.Join(ctxt.GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
72         }
73
74         ctxt.GOPATH = envOr("GOPATH", ctxt.GOPATH)
75
76         // Override defaults computed in go/build with defaults
77         // from go environment configuration file, if known.
78         ctxt.GOOS = envOr("GOOS", ctxt.GOOS)
79         ctxt.GOARCH = envOr("GOARCH", ctxt.GOARCH)
80
81         // The go/build rule for whether cgo is enabled is:
82         //      1. If $CGO_ENABLED is set, respect it.
83         //      2. Otherwise, if this is a cross-compile, disable cgo.
84         //      3. Otherwise, use built-in default for GOOS/GOARCH.
85         // Recreate that logic here with the new GOOS/GOARCH setting.
86         if v := Getenv("CGO_ENABLED"); v == "0" || v == "1" {
87                 ctxt.CgoEnabled = v[0] == '1'
88         } else if ctxt.GOOS != runtime.GOOS || ctxt.GOARCH != runtime.GOARCH {
89                 ctxt.CgoEnabled = false
90         } else {
91                 // Use built-in default cgo setting for GOOS/GOARCH.
92                 // Note that ctxt.GOOS/GOARCH are derived from the preference list
93                 // (1) environment, (2) go/env file, (3) runtime constants,
94                 // while go/build.Default.GOOS/GOARCH are derived from the preference list
95                 // (1) environment, (2) runtime constants.
96                 // We know ctxt.GOOS/GOARCH == runtime.GOOS/GOARCH;
97                 // no matter how that happened, go/build.Default will make the
98                 // same decision (either the environment variables are set explicitly
99                 // to match the runtime constants, or else they are unset, in which
100                 // case go/build falls back to the runtime constants), so
101                 // go/build.Default.GOOS/GOARCH == runtime.GOOS/GOARCH.
102                 // So ctxt.CgoEnabled (== go/build.Default.CgoEnabled) is correct
103                 // as is and can be left unmodified.
104                 // Nothing to do here.
105         }
106
107         ctxt.OpenFile = func(path string) (io.ReadCloser, error) {
108                 return fsys.Open(path)
109         }
110         ctxt.ReadDir = fsys.ReadDir
111         ctxt.IsDir = func(path string) bool {
112                 isDir, err := fsys.IsDir(path)
113                 return err == nil && isDir
114         }
115
116         return ctxt
117 }
118
119 func init() {
120         BuildToolchainCompiler = func() string { return "missing-compiler" }
121         BuildToolchainLinker = func() string { return "missing-linker" }
122 }
123
124 // An EnvVar is an environment variable Name=Value.
125 type EnvVar struct {
126         Name  string
127         Value string
128 }
129
130 // OrigEnv is the original environment of the program at startup.
131 var OrigEnv []string
132
133 // CmdEnv is the new environment for running go tool commands.
134 // User binaries (during go test or go run) are run with OrigEnv,
135 // not CmdEnv.
136 var CmdEnv []EnvVar
137
138 // Global build parameters (used during package load)
139 var (
140         Goarch = BuildContext.GOARCH
141         Goos   = BuildContext.GOOS
142
143         ExeSuffix = exeSuffix()
144
145         // ModulesEnabled specifies whether the go command is running
146         // in module-aware mode (as opposed to GOPATH mode).
147         // It is equal to modload.Enabled, but not all packages can import modload.
148         ModulesEnabled bool
149 )
150
151 func exeSuffix() string {
152         if Goos == "windows" {
153                 return ".exe"
154         }
155         return ""
156 }
157
158 var envCache struct {
159         once sync.Once
160         m    map[string]string
161 }
162
163 // EnvFile returns the name of the Go environment configuration file.
164 func EnvFile() (string, error) {
165         if file := os.Getenv("GOENV"); file != "" {
166                 if file == "off" {
167                         return "", fmt.Errorf("GOENV=off")
168                 }
169                 return file, nil
170         }
171         dir, err := os.UserConfigDir()
172         if err != nil {
173                 return "", err
174         }
175         if dir == "" {
176                 return "", fmt.Errorf("missing user-config dir")
177         }
178         return filepath.Join(dir, "go/env"), nil
179 }
180
181 func initEnvCache() {
182         envCache.m = make(map[string]string)
183         file, _ := EnvFile()
184         if file == "" {
185                 return
186         }
187         data, err := os.ReadFile(file)
188         if err != nil {
189                 return
190         }
191
192         for len(data) > 0 {
193                 // Get next line.
194                 line := data
195                 i := bytes.IndexByte(data, '\n')
196                 if i >= 0 {
197                         line, data = line[:i], data[i+1:]
198                 } else {
199                         data = nil
200                 }
201
202                 i = bytes.IndexByte(line, '=')
203                 if i < 0 || line[0] < 'A' || 'Z' < line[0] {
204                         // Line is missing = (or empty) or a comment or not a valid env name. Ignore.
205                         // (This should not happen, since the file should be maintained almost
206                         // exclusively by "go env -w", but better to silently ignore than to make
207                         // the go command unusable just because somehow the env file has
208                         // gotten corrupted.)
209                         continue
210                 }
211                 key, val := line[:i], line[i+1:]
212                 envCache.m[string(key)] = string(val)
213         }
214 }
215
216 // Getenv gets the value for the configuration key.
217 // It consults the operating system environment
218 // and then the go/env file.
219 // If Getenv is called for a key that cannot be set
220 // in the go/env file (for example GODEBUG), it panics.
221 // This ensures that CanGetenv is accurate, so that
222 // 'go env -w' stays in sync with what Getenv can retrieve.
223 func Getenv(key string) string {
224         if !CanGetenv(key) {
225                 switch key {
226                 case "CGO_TEST_ALLOW", "CGO_TEST_DISALLOW", "CGO_test_ALLOW", "CGO_test_DISALLOW":
227                         // used by internal/work/security_test.go; allow
228                 default:
229                         panic("internal error: invalid Getenv " + key)
230                 }
231         }
232         val := os.Getenv(key)
233         if val != "" {
234                 return val
235         }
236         envCache.once.Do(initEnvCache)
237         return envCache.m[key]
238 }
239
240 // CanGetenv reports whether key is a valid go/env configuration key.
241 func CanGetenv(key string) bool {
242         return strings.Contains(cfg.KnownEnv, "\t"+key+"\n")
243 }
244
245 var (
246         GOROOT       = BuildContext.GOROOT
247         GOBIN        = Getenv("GOBIN")
248         GOROOTbin    = filepath.Join(GOROOT, "bin")
249         GOROOTpkg    = filepath.Join(GOROOT, "pkg")
250         GOROOTsrc    = filepath.Join(GOROOT, "src")
251         GOROOT_FINAL = findGOROOT_FINAL()
252         GOMODCACHE   = envOr("GOMODCACHE", gopathDir("pkg/mod"))
253
254         // Used in envcmd.MkEnv and build ID computations.
255         GOARM    = envOr("GOARM", fmt.Sprint(objabi.GOARM))
256         GO386    = envOr("GO386", objabi.GO386)
257         GOMIPS   = envOr("GOMIPS", objabi.GOMIPS)
258         GOMIPS64 = envOr("GOMIPS64", objabi.GOMIPS64)
259         GOPPC64  = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64))
260         GOWASM   = envOr("GOWASM", fmt.Sprint(objabi.GOWASM))
261
262         GOPROXY    = envOr("GOPROXY", "https://proxy.golang.org,direct")
263         GOSUMDB    = envOr("GOSUMDB", "sum.golang.org")
264         GOPRIVATE  = Getenv("GOPRIVATE")
265         GONOPROXY  = envOr("GONOPROXY", GOPRIVATE)
266         GONOSUMDB  = envOr("GONOSUMDB", GOPRIVATE)
267         GOINSECURE = Getenv("GOINSECURE")
268         GOVCS      = Getenv("GOVCS")
269 )
270
271 var SumdbDir = gopathDir("pkg/sumdb")
272
273 // GetArchEnv returns the name and setting of the
274 // GOARCH-specific architecture environment variable.
275 // If the current architecture has no GOARCH-specific variable,
276 // GetArchEnv returns empty key and value.
277 func GetArchEnv() (key, val string) {
278         switch Goarch {
279         case "arm":
280                 return "GOARM", GOARM
281         case "386":
282                 return "GO386", GO386
283         case "mips", "mipsle":
284                 return "GOMIPS", GOMIPS
285         case "mips64", "mips64le":
286                 return "GOMIPS64", GOMIPS64
287         case "ppc64", "ppc64le":
288                 return "GOPPC64", GOPPC64
289         case "wasm":
290                 return "GOWASM", GOWASM
291         }
292         return "", ""
293 }
294
295 // envOr returns Getenv(key) if set, or else def.
296 func envOr(key, def string) string {
297         val := Getenv(key)
298         if val == "" {
299                 val = def
300         }
301         return val
302 }
303
304 // There is a copy of findGOROOT, isSameDir, and isGOROOT in
305 // x/tools/cmd/godoc/goroot.go.
306 // Try to keep them in sync for now.
307
308 // findGOROOT returns the GOROOT value, using either an explicitly
309 // provided environment variable, a GOROOT that contains the current
310 // os.Executable value, or else the GOROOT that the binary was built
311 // with from runtime.GOROOT().
312 //
313 // There is a copy of this code in x/tools/cmd/godoc/goroot.go.
314 func findGOROOT() string {
315         if env := Getenv("GOROOT"); env != "" {
316                 return filepath.Clean(env)
317         }
318         def := filepath.Clean(runtime.GOROOT())
319         if runtime.Compiler == "gccgo" {
320                 // gccgo has no real GOROOT, and it certainly doesn't
321                 // depend on the executable's location.
322                 return def
323         }
324         exe, err := os.Executable()
325         if err == nil {
326                 exe, err = filepath.Abs(exe)
327                 if err == nil {
328                         if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
329                                 // If def (runtime.GOROOT()) and dir are the same
330                                 // directory, prefer the spelling used in def.
331                                 if isSameDir(def, dir) {
332                                         return def
333                                 }
334                                 return dir
335                         }
336                         exe, err = filepath.EvalSymlinks(exe)
337                         if err == nil {
338                                 if dir := filepath.Join(exe, "../.."); isGOROOT(dir) {
339                                         if isSameDir(def, dir) {
340                                                 return def
341                                         }
342                                         return dir
343                                 }
344                         }
345                 }
346         }
347         return def
348 }
349
350 func findGOROOT_FINAL() string {
351         // $GOROOT_FINAL is only for use during make.bash
352         // so it is not settable using go/env, so we use os.Getenv here.
353         def := GOROOT
354         if env := os.Getenv("GOROOT_FINAL"); env != "" {
355                 def = filepath.Clean(env)
356         }
357         return def
358 }
359
360 // isSameDir reports whether dir1 and dir2 are the same directory.
361 func isSameDir(dir1, dir2 string) bool {
362         if dir1 == dir2 {
363                 return true
364         }
365         info1, err1 := os.Stat(dir1)
366         info2, err2 := os.Stat(dir2)
367         return err1 == nil && err2 == nil && os.SameFile(info1, info2)
368 }
369
370 // isGOROOT reports whether path looks like a GOROOT.
371 //
372 // It does this by looking for the path/pkg/tool directory,
373 // which is necessary for useful operation of the cmd/go tool,
374 // and is not typically present in a GOPATH.
375 //
376 // There is a copy of this code in x/tools/cmd/godoc/goroot.go.
377 func isGOROOT(path string) bool {
378         stat, err := os.Stat(filepath.Join(path, "pkg", "tool"))
379         if err != nil {
380                 return false
381         }
382         return stat.IsDir()
383 }
384
385 func gopathDir(rel string) string {
386         list := filepath.SplitList(BuildContext.GOPATH)
387         if len(list) == 0 || list[0] == "" {
388                 return ""
389         }
390         return filepath.Join(list[0], rel)
391 }