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.
5 // Package envcmd implements the ``go env'' command.
20 "cmd/go/internal/base"
21 "cmd/go/internal/cache"
23 "cmd/go/internal/load"
24 "cmd/go/internal/modload"
25 "cmd/go/internal/work"
28 var CmdEnv = &base.Command{
29 UsageLine: "go env [-json] [-u] [-w] [var ...]",
30 Short: "print Go environment information",
32 Env prints Go environment information.
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.
39 The -json flag prints the environment in JSON format
40 instead of as a shell script.
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'.
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.
50 For more about environment variables, see 'go help environment'.
55 CmdEnv.Run = runEnv // break init cycle
59 envJson = CmdEnv.Flag.Bool("json", false, "")
60 envU = CmdEnv.Flag.Bool("u", false, "")
61 envW = CmdEnv.Flag.Bool("w", false, "")
64 func MkEnv() []cfg.EnvVar {
68 envFile, _ := cfg.EnvFile()
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},
92 if work.GccgoBin != "" {
93 env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoBin})
95 env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoName})
98 key, val := cfg.GetArchEnv()
100 env = append(env, cfg.EnvVar{Name: key, Value: val})
103 cc := cfg.DefaultCC(cfg.Goos, cfg.Goarch)
104 if env := strings.Fields(cfg.Getenv("CC")); len(env) > 0 {
107 cxx := cfg.DefaultCXX(cfg.Goos, cfg.Goarch)
108 if env := strings.Fields(cfg.Getenv("CXX")); len(env) > 0 {
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})
115 if cfg.BuildContext.CgoEnabled {
116 env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "1"})
118 env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "0"})
124 func envOr(name, def string) string {
125 val := cfg.Getenv(name)
132 func findEnv(env []cfg.EnvVar, name string) string {
133 for _, e := range env {
141 // ExtraEnvVars returns environment variables that should not leak into child processes.
142 func ExtraEnvVars() []cfg.EnvVar {
144 if modload.HasModRoot() {
145 gomod = filepath.Join(modload.ModRoot(), "go.mod")
146 } else if modload.Enabled() {
150 {Name: "GOMOD", Value: gomod},
154 // ExtraEnvVarsCostly returns environment variables that should not leak into child processes
155 // but are costly to evaluate.
156 func ExtraEnvVarsCostly() []cfg.EnvVar {
159 cppflags, cflags, cxxflags, fflags, ldflags, err := b.CFlags(&load.Package{})
161 // Should not happen - b.CFlags was given an empty package.
162 fmt.Fprintf(os.Stderr, "go: invalid cflags: %v\n", err)
165 cmd := b.GccCmd(".", "")
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:], " ")},
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, "=")
188 func runEnv(cmd *base.Command, args []string) {
189 if *envJson && *envU {
190 base.Fatalf("go env: cannot use -json with -u")
192 if *envJson && *envW {
193 base.Fatalf("go env: cannot use -json with -w")
196 base.Fatalf("go env: cannot use -u with -w")
199 env = append(env, ExtraEnvVars()...)
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.
207 for _, arg := range args {
221 env = append(env, ExtraEnvVarsCostly()...)
225 // Process and sanity-check command line.
227 base.Fatalf("go env -w: no KEY=VALUE arguments given")
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:]
235 add := make(map[string]string)
236 for _, arg := range args {
237 i := strings.Index(arg, "=")
239 base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg)
241 key, val := arg[:i], arg[i+1:]
242 if err := checkEnvWrite(key, val); err != nil {
243 base.Fatalf("go env -w: %v", err)
245 if _, ok := add[key]; ok {
246 base.Fatalf("go env -w: multiple values for key: %s", key)
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)
254 goos, okGOOS := add["GOOS"]
255 goarch, okGOARCH := add["GOARCH"]
256 if okGOOS || okGOARCH {
263 if err := work.CheckGOOSARCHPair(goos, goarch); err != nil {
264 base.Fatalf("go env -w: %v", err)
268 updateEnvFile(add, nil)
273 // Process and sanity-check command line.
275 base.Fatalf("go env -u: no arguments given")
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)
284 if del["GOOS"] || del["GOARCH"] {
285 goos, goarch := cfg.Goos, cfg.Goarch
287 goos = getOrigEnv("GOOS")
289 goos = build.Default.GOOS
293 goarch = getOrigEnv("GOARCH")
295 goarch = build.Default.GOARCH
298 if err := work.CheckGOOSARCHPair(goos, goarch); err != nil {
299 base.Fatalf("go env -u: %v", err)
302 updateEnvFile(nil, del)
309 for _, name := range args {
310 e := cfg.EnvVar{Name: name, Value: findEnv(env, name)}
315 for _, name := range args {
316 fmt.Printf("%s\n", findEnv(env, name))
327 for _, e := range env {
328 if e.Name != "TERM" {
329 switch runtime.GOOS {
331 fmt.Printf("%s=\"%s\"\n", e.Name, e.Value)
333 if strings.IndexByte(e.Value, '\x00') < 0 {
334 fmt.Printf("%s='%s'\n", e.Name, strings.ReplaceAll(e.Value, "'", "''"))
336 v := strings.Split(e.Value, "\x00")
337 fmt.Printf("%s=(", e.Name)
338 for x, s := range v {
347 fmt.Printf("set %s=%s\n", e.Name, e.Value)
353 func printEnvAsJSON(env []cfg.EnvVar) {
354 m := make(map[string]string)
355 for _, e := range env {
356 if e.Name == "TERM" {
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)
368 func getOrigEnv(key string) string {
369 for _, v := range cfg.OrigEnv {
370 if strings.HasPrefix(v, key+"=") {
371 return strings.TrimPrefix(v, key+"=")
377 func checkEnvWrite(key, val string) error {
379 case "GOEXE", "GOGCCFLAGS", "GOHOSTARCH", "GOHOSTOS", "GOMOD", "GOTOOLDIR":
380 return fmt.Errorf("%s cannot be modified", key)
382 return fmt.Errorf("%s can only be set using the OS environment", key)
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)
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.
396 case "", "auto", "on", "off":
398 return fmt.Errorf("invalid %s value %q", key, val)
401 if strings.HasPrefix(val, "~") {
402 return fmt.Errorf("GOPATH entry cannot start with shell metacharacter '~': %q", val)
404 if !filepath.IsAbs(val) && val != "" {
405 return fmt.Errorf("GOPATH entry is relative; must be absolute path: %q", val)
409 if !utf8.ValidString(val) {
410 return fmt.Errorf("invalid UTF-8 in %s=... value", key)
412 if strings.Contains(val, "\x00") {
413 return fmt.Errorf("invalid NUL in %s=... value", key)
415 if strings.ContainsAny(val, "\v\r\n") {
416 return fmt.Errorf("invalid newline in %s=... value", key)
421 func updateEnvFile(add map[string]string, del map[string]bool) {
422 file, err := cfg.EnvFile()
424 base.Fatalf("go env: cannot find go env config: %v", err)
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)
431 lines := strings.SplitAfter(string(data), "\n")
432 if lines[len(lines)-1] == "" {
433 lines = lines[:len(lines)-1]
435 lines[len(lines)-1] += "\n"
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 {
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"
457 for key, val := range add {
458 lines = append(lines, key+"="+val+"\n")
461 // Delete requested variables (go env -u).
462 for key := range del {
463 if p, ok := prev[key]; ok {
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).
472 for i := 0; i <= len(lines); i++ {
473 if i == len(lines) || lineToKey(lines[i]) == "" {
474 sortKeyValues(lines[start:i])
479 data = []byte(strings.Join(lines, ""))
480 err = ioutil.WriteFile(file, data, 0666)
482 // Try creating directory.
483 os.MkdirAll(filepath.Dir(file), 0777)
484 err = ioutil.WriteFile(file, data, 0666)
486 base.Fatalf("go env: writing go env config: %v", err)
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], "#") {
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])