]> git.ipfire.org Git - thirdparty/gcc.git/blob - libgo/go/cmd/go/internal/envcmd/env.go
libgo: update to Go1.14beta1
[thirdparty/gcc.git] / libgo / go / cmd / go / internal / envcmd / env.go
1 // Copyright 2012 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 envcmd implements the ``go env'' command.
6 package envcmd
7
8 import (
9 "encoding/json"
10 "fmt"
11 "go/build"
12 "io/ioutil"
13 "os"
14 "path/filepath"
15 "runtime"
16 "sort"
17 "strings"
18 "unicode/utf8"
19
20 "cmd/go/internal/base"
21 "cmd/go/internal/cache"
22 "cmd/go/internal/cfg"
23 "cmd/go/internal/load"
24 "cmd/go/internal/modload"
25 "cmd/go/internal/work"
26 )
27
28 var CmdEnv = &base.Command{
29 UsageLine: "go env [-json] [-u] [-w] [var ...]",
30 Short: "print Go environment information",
31 Long: `
32 Env prints Go environment information.
33
34 By default env prints information as a shell script
35 (on Windows, a batch file). If one or more variable
36 names is given as arguments, env prints the value of
37 each named variable on its own line.
38
39 The -json flag prints the environment in JSON format
40 instead of as a shell script.
41
42 The -u flag requires one or more arguments and unsets
43 the default setting for the named environment variables,
44 if one has been set with 'go env -w'.
45
46 The -w flag requires one or more arguments of the
47 form NAME=VALUE and changes the default settings
48 of the named environment variables to the given values.
49
50 For more about environment variables, see 'go help environment'.
51 `,
52 }
53
54 func init() {
55 CmdEnv.Run = runEnv // break init cycle
56 }
57
58 var (
59 envJson = CmdEnv.Flag.Bool("json", false, "")
60 envU = CmdEnv.Flag.Bool("u", false, "")
61 envW = CmdEnv.Flag.Bool("w", false, "")
62 )
63
64 func MkEnv() []cfg.EnvVar {
65 var b work.Builder
66 b.Init()
67
68 envFile, _ := cfg.EnvFile()
69 env := []cfg.EnvVar{
70 {Name: "GO111MODULE", Value: cfg.Getenv("GO111MODULE")},
71 {Name: "GOARCH", Value: cfg.Goarch},
72 {Name: "GOBIN", Value: cfg.GOBIN},
73 {Name: "GOCACHE", Value: cache.DefaultDir()},
74 {Name: "GOENV", Value: envFile},
75 {Name: "GOEXE", Value: cfg.ExeSuffix},
76 {Name: "GOFLAGS", Value: cfg.Getenv("GOFLAGS")},
77 {Name: "GOHOSTARCH", Value: runtime.GOARCH},
78 {Name: "GOHOSTOS", Value: runtime.GOOS},
79 {Name: "GOINSECURE", Value: cfg.GOINSECURE},
80 {Name: "GONOPROXY", Value: cfg.GONOPROXY},
81 {Name: "GONOSUMDB", Value: cfg.GONOSUMDB},
82 {Name: "GOOS", Value: cfg.Goos},
83 {Name: "GOPATH", Value: cfg.BuildContext.GOPATH},
84 {Name: "GOPRIVATE", Value: cfg.GOPRIVATE},
85 {Name: "GOPROXY", Value: cfg.GOPROXY},
86 {Name: "GOROOT", Value: cfg.GOROOT},
87 {Name: "GOSUMDB", Value: cfg.GOSUMDB},
88 {Name: "GOTMPDIR", Value: cfg.Getenv("GOTMPDIR")},
89 {Name: "GOTOOLDIR", Value: base.ToolDir},
90 }
91
92 if work.GccgoBin != "" {
93 env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoBin})
94 } else {
95 env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoName})
96 }
97
98 key, val := cfg.GetArchEnv()
99 if key != "" {
100 env = append(env, cfg.EnvVar{Name: key, Value: val})
101 }
102
103 cc := cfg.DefaultCC(cfg.Goos, cfg.Goarch)
104 if env := strings.Fields(cfg.Getenv("CC")); len(env) > 0 {
105 cc = env[0]
106 }
107 cxx := cfg.DefaultCXX(cfg.Goos, cfg.Goarch)
108 if env := strings.Fields(cfg.Getenv("CXX")); len(env) > 0 {
109 cxx = env[0]
110 }
111 env = append(env, cfg.EnvVar{Name: "AR", Value: envOr("AR", "ar")})
112 env = append(env, cfg.EnvVar{Name: "CC", Value: cc})
113 env = append(env, cfg.EnvVar{Name: "CXX", Value: cxx})
114
115 if cfg.BuildContext.CgoEnabled {
116 env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "1"})
117 } else {
118 env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "0"})
119 }
120
121 return env
122 }
123
124 func envOr(name, def string) string {
125 val := cfg.Getenv(name)
126 if val != "" {
127 return val
128 }
129 return def
130 }
131
132 func findEnv(env []cfg.EnvVar, name string) string {
133 for _, e := range env {
134 if e.Name == name {
135 return e.Value
136 }
137 }
138 return ""
139 }
140
141 // ExtraEnvVars returns environment variables that should not leak into child processes.
142 func ExtraEnvVars() []cfg.EnvVar {
143 gomod := ""
144 if modload.HasModRoot() {
145 gomod = filepath.Join(modload.ModRoot(), "go.mod")
146 } else if modload.Enabled() {
147 gomod = os.DevNull
148 }
149 return []cfg.EnvVar{
150 {Name: "GOMOD", Value: gomod},
151 }
152 }
153
154 // ExtraEnvVarsCostly returns environment variables that should not leak into child processes
155 // but are costly to evaluate.
156 func ExtraEnvVarsCostly() []cfg.EnvVar {
157 var b work.Builder
158 b.Init()
159 cppflags, cflags, cxxflags, fflags, ldflags, err := b.CFlags(&load.Package{})
160 if err != nil {
161 // Should not happen - b.CFlags was given an empty package.
162 fmt.Fprintf(os.Stderr, "go: invalid cflags: %v\n", err)
163 return nil
164 }
165 cmd := b.GccCmd(".", "")
166
167 return []cfg.EnvVar{
168 // Note: Update the switch in runEnv below when adding to this list.
169 {Name: "CGO_CFLAGS", Value: strings.Join(cflags, " ")},
170 {Name: "CGO_CPPFLAGS", Value: strings.Join(cppflags, " ")},
171 {Name: "CGO_CXXFLAGS", Value: strings.Join(cxxflags, " ")},
172 {Name: "CGO_FFLAGS", Value: strings.Join(fflags, " ")},
173 {Name: "CGO_LDFLAGS", Value: strings.Join(ldflags, " ")},
174 {Name: "PKG_CONFIG", Value: b.PkgconfigCmd()},
175 {Name: "GOGCCFLAGS", Value: strings.Join(cmd[3:], " ")},
176 }
177 }
178
179 // argKey returns the KEY part of the arg KEY=VAL, or else arg itself.
180 func argKey(arg string) string {
181 i := strings.Index(arg, "=")
182 if i < 0 {
183 return arg
184 }
185 return arg[:i]
186 }
187
188 func runEnv(cmd *base.Command, args []string) {
189 if *envJson && *envU {
190 base.Fatalf("go env: cannot use -json with -u")
191 }
192 if *envJson && *envW {
193 base.Fatalf("go env: cannot use -json with -w")
194 }
195 if *envU && *envW {
196 base.Fatalf("go env: cannot use -u with -w")
197 }
198 env := cfg.CmdEnv
199 env = append(env, ExtraEnvVars()...)
200
201 // Do we need to call ExtraEnvVarsCostly, which is a bit expensive?
202 // Only if we're listing all environment variables ("go env")
203 // or the variables being requested are in the extra list.
204 needCostly := true
205 if len(args) > 0 {
206 needCostly = false
207 for _, arg := range args {
208 switch argKey(arg) {
209 case "CGO_CFLAGS",
210 "CGO_CPPFLAGS",
211 "CGO_CXXFLAGS",
212 "CGO_FFLAGS",
213 "CGO_LDFLAGS",
214 "PKG_CONFIG",
215 "GOGCCFLAGS":
216 needCostly = true
217 }
218 }
219 }
220 if needCostly {
221 env = append(env, ExtraEnvVarsCostly()...)
222 }
223
224 if *envW {
225 // Process and sanity-check command line.
226 if len(args) == 0 {
227 base.Fatalf("go env -w: no KEY=VALUE arguments given")
228 }
229 osEnv := make(map[string]string)
230 for _, e := range cfg.OrigEnv {
231 if i := strings.Index(e, "="); i >= 0 {
232 osEnv[e[:i]] = e[i+1:]
233 }
234 }
235 add := make(map[string]string)
236 for _, arg := range args {
237 i := strings.Index(arg, "=")
238 if i < 0 {
239 base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg)
240 }
241 key, val := arg[:i], arg[i+1:]
242 if err := checkEnvWrite(key, val); err != nil {
243 base.Fatalf("go env -w: %v", err)
244 }
245 if _, ok := add[key]; ok {
246 base.Fatalf("go env -w: multiple values for key: %s", key)
247 }
248 add[key] = val
249 if osVal := osEnv[key]; osVal != "" && osVal != val {
250 fmt.Fprintf(os.Stderr, "warning: go env -w %s=... does not override conflicting OS environment variable\n", key)
251 }
252 }
253
254 goos, okGOOS := add["GOOS"]
255 goarch, okGOARCH := add["GOARCH"]
256 if okGOOS || okGOARCH {
257 if !okGOOS {
258 goos = cfg.Goos
259 }
260 if !okGOARCH {
261 goarch = cfg.Goarch
262 }
263 if err := work.CheckGOOSARCHPair(goos, goarch); err != nil {
264 base.Fatalf("go env -w: %v", err)
265 }
266 }
267
268 updateEnvFile(add, nil)
269 return
270 }
271
272 if *envU {
273 // Process and sanity-check command line.
274 if len(args) == 0 {
275 base.Fatalf("go env -u: no arguments given")
276 }
277 del := make(map[string]bool)
278 for _, arg := range args {
279 if err := checkEnvWrite(arg, ""); err != nil {
280 base.Fatalf("go env -u: %v", err)
281 }
282 del[arg] = true
283 }
284 if del["GOOS"] || del["GOARCH"] {
285 goos, goarch := cfg.Goos, cfg.Goarch
286 if del["GOOS"] {
287 goos = getOrigEnv("GOOS")
288 if goos == "" {
289 goos = build.Default.GOOS
290 }
291 }
292 if del["GOARCH"] {
293 goarch = getOrigEnv("GOARCH")
294 if goarch == "" {
295 goarch = build.Default.GOARCH
296 }
297 }
298 if err := work.CheckGOOSARCHPair(goos, goarch); err != nil {
299 base.Fatalf("go env -u: %v", err)
300 }
301 }
302 updateEnvFile(nil, del)
303 return
304 }
305
306 if len(args) > 0 {
307 if *envJson {
308 var es []cfg.EnvVar
309 for _, name := range args {
310 e := cfg.EnvVar{Name: name, Value: findEnv(env, name)}
311 es = append(es, e)
312 }
313 printEnvAsJSON(es)
314 } else {
315 for _, name := range args {
316 fmt.Printf("%s\n", findEnv(env, name))
317 }
318 }
319 return
320 }
321
322 if *envJson {
323 printEnvAsJSON(env)
324 return
325 }
326
327 for _, e := range env {
328 if e.Name != "TERM" {
329 switch runtime.GOOS {
330 default:
331 fmt.Printf("%s=\"%s\"\n", e.Name, e.Value)
332 case "plan9":
333 if strings.IndexByte(e.Value, '\x00') < 0 {
334 fmt.Printf("%s='%s'\n", e.Name, strings.ReplaceAll(e.Value, "'", "''"))
335 } else {
336 v := strings.Split(e.Value, "\x00")
337 fmt.Printf("%s=(", e.Name)
338 for x, s := range v {
339 if x > 0 {
340 fmt.Printf(" ")
341 }
342 fmt.Printf("%s", s)
343 }
344 fmt.Printf(")\n")
345 }
346 case "windows":
347 fmt.Printf("set %s=%s\n", e.Name, e.Value)
348 }
349 }
350 }
351 }
352
353 func printEnvAsJSON(env []cfg.EnvVar) {
354 m := make(map[string]string)
355 for _, e := range env {
356 if e.Name == "TERM" {
357 continue
358 }
359 m[e.Name] = e.Value
360 }
361 enc := json.NewEncoder(os.Stdout)
362 enc.SetIndent("", "\t")
363 if err := enc.Encode(m); err != nil {
364 base.Fatalf("go env -json: %s", err)
365 }
366 }
367
368 func getOrigEnv(key string) string {
369 for _, v := range cfg.OrigEnv {
370 if strings.HasPrefix(v, key+"=") {
371 return strings.TrimPrefix(v, key+"=")
372 }
373 }
374 return ""
375 }
376
377 func checkEnvWrite(key, val string) error {
378 switch key {
379 case "GOEXE", "GOGCCFLAGS", "GOHOSTARCH", "GOHOSTOS", "GOMOD", "GOTOOLDIR":
380 return fmt.Errorf("%s cannot be modified", key)
381 case "GOENV":
382 return fmt.Errorf("%s can only be set using the OS environment", key)
383 }
384
385 // To catch typos and the like, check that we know the variable.
386 if !cfg.CanGetenv(key) {
387 return fmt.Errorf("unknown go command variable %s", key)
388 }
389
390 // Some variables can only have one of a few valid values. If set to an
391 // invalid value, the next cmd/go invocation might fail immediately,
392 // even 'go env -w' itself.
393 switch key {
394 case "GO111MODULE":
395 switch val {
396 case "", "auto", "on", "off":
397 default:
398 return fmt.Errorf("invalid %s value %q", key, val)
399 }
400 case "GOPATH":
401 if strings.HasPrefix(val, "~") {
402 return fmt.Errorf("GOPATH entry cannot start with shell metacharacter '~': %q", val)
403 }
404 if !filepath.IsAbs(val) && val != "" {
405 return fmt.Errorf("GOPATH entry is relative; must be absolute path: %q", val)
406 }
407 }
408
409 if !utf8.ValidString(val) {
410 return fmt.Errorf("invalid UTF-8 in %s=... value", key)
411 }
412 if strings.Contains(val, "\x00") {
413 return fmt.Errorf("invalid NUL in %s=... value", key)
414 }
415 if strings.ContainsAny(val, "\v\r\n") {
416 return fmt.Errorf("invalid newline in %s=... value", key)
417 }
418 return nil
419 }
420
421 func updateEnvFile(add map[string]string, del map[string]bool) {
422 file, err := cfg.EnvFile()
423 if file == "" {
424 base.Fatalf("go env: cannot find go env config: %v", err)
425 }
426 data, err := ioutil.ReadFile(file)
427 if err != nil && (!os.IsNotExist(err) || len(add) == 0) {
428 base.Fatalf("go env: reading go env config: %v", err)
429 }
430
431 lines := strings.SplitAfter(string(data), "\n")
432 if lines[len(lines)-1] == "" {
433 lines = lines[:len(lines)-1]
434 } else {
435 lines[len(lines)-1] += "\n"
436 }
437
438 // Delete all but last copy of any duplicated variables,
439 // since the last copy is the one that takes effect.
440 prev := make(map[string]int)
441 for l, line := range lines {
442 if key := lineToKey(line); key != "" {
443 if p, ok := prev[key]; ok {
444 lines[p] = ""
445 }
446 prev[key] = l
447 }
448 }
449
450 // Add variables (go env -w). Update existing lines in file if present, add to end otherwise.
451 for key, val := range add {
452 if p, ok := prev[key]; ok {
453 lines[p] = key + "=" + val + "\n"
454 delete(add, key)
455 }
456 }
457 for key, val := range add {
458 lines = append(lines, key+"="+val+"\n")
459 }
460
461 // Delete requested variables (go env -u).
462 for key := range del {
463 if p, ok := prev[key]; ok {
464 lines[p] = ""
465 }
466 }
467
468 // Sort runs of KEY=VALUE lines
469 // (that is, blocks of lines where blocks are separated
470 // by comments, blank lines, or invalid lines).
471 start := 0
472 for i := 0; i <= len(lines); i++ {
473 if i == len(lines) || lineToKey(lines[i]) == "" {
474 sortKeyValues(lines[start:i])
475 start = i + 1
476 }
477 }
478
479 data = []byte(strings.Join(lines, ""))
480 err = ioutil.WriteFile(file, data, 0666)
481 if err != nil {
482 // Try creating directory.
483 os.MkdirAll(filepath.Dir(file), 0777)
484 err = ioutil.WriteFile(file, data, 0666)
485 if err != nil {
486 base.Fatalf("go env: writing go env config: %v", err)
487 }
488 }
489 }
490
491 // lineToKey returns the KEY part of the line KEY=VALUE or else an empty string.
492 func lineToKey(line string) string {
493 i := strings.Index(line, "=")
494 if i < 0 || strings.Contains(line[:i], "#") {
495 return ""
496 }
497 return line[:i]
498 }
499
500 // sortKeyValues sorts a sequence of lines by key.
501 // It differs from sort.Strings in that GO386= sorts after GO=.
502 func sortKeyValues(lines []string) {
503 sort.Slice(lines, func(i, j int) bool {
504 return lineToKey(lines[i]) < lineToKey(lines[j])
505 })
506 }