Source file src/pkg/cmd/go/internal/envcmd/env.go
1
2
3
4
5
6 package envcmd
7
8 import (
9 "encoding/json"
10 "fmt"
11 "io/ioutil"
12 "os"
13 "path/filepath"
14 "runtime"
15 "sort"
16 "strings"
17 "unicode/utf8"
18
19 "cmd/go/internal/base"
20 "cmd/go/internal/cache"
21 "cmd/go/internal/cfg"
22 "cmd/go/internal/load"
23 "cmd/go/internal/modload"
24 "cmd/go/internal/work"
25 )
26
27 var CmdEnv = &base.Command{
28 UsageLine: "go env [-json] [-u] [-w] [var ...]",
29 Short: "print Go environment information",
30 Long: `
31 Env prints Go environment information.
32
33 By default env prints information as a shell script
34 (on Windows, a batch file). If one or more variable
35 names is given as arguments, env prints the value of
36 each named variable on its own line.
37
38 The -json flag prints the environment in JSON format
39 instead of as a shell script.
40
41 The -u flag requires one or more arguments and unsets
42 the default setting for the named environment variables,
43 if one has been set with 'go env -w'.
44
45 The -w flag requires one or more arguments of the
46 form NAME=VALUE and changes the default settings
47 of the named environment variables to the given values.
48
49 For more about environment variables, see 'go help environment'.
50 `,
51 }
52
53 func init() {
54 CmdEnv.Run = runEnv
55 }
56
57 var (
58 envJson = CmdEnv.Flag.Bool("json", false, "")
59 envU = CmdEnv.Flag.Bool("u", false, "")
60 envW = CmdEnv.Flag.Bool("w", false, "")
61 )
62
63 func MkEnv() []cfg.EnvVar {
64 var b work.Builder
65 b.Init()
66
67 envFile, _ := cfg.EnvFile()
68 env := []cfg.EnvVar{
69 {Name: "GO111MODULE", Value: cfg.Getenv("GO111MODULE")},
70 {Name: "GOARCH", Value: cfg.Goarch},
71 {Name: "GOBIN", Value: cfg.GOBIN},
72 {Name: "GOCACHE", Value: cache.DefaultDir()},
73 {Name: "GOENV", Value: envFile},
74 {Name: "GOEXE", Value: cfg.ExeSuffix},
75 {Name: "GOFLAGS", Value: cfg.Getenv("GOFLAGS")},
76 {Name: "GOHOSTARCH", Value: runtime.GOARCH},
77 {Name: "GOHOSTOS", Value: runtime.GOOS},
78 {Name: "GONOPROXY", Value: cfg.GONOPROXY},
79 {Name: "GONOSUMDB", Value: cfg.GONOSUMDB},
80 {Name: "GOOS", Value: cfg.Goos},
81 {Name: "GOPATH", Value: cfg.BuildContext.GOPATH},
82 {Name: "GOPRIVATE", Value: cfg.GOPRIVATE},
83 {Name: "GOPROXY", Value: cfg.GOPROXY},
84 {Name: "GOROOT", Value: cfg.GOROOT},
85 {Name: "GOSUMDB", Value: cfg.GOSUMDB},
86 {Name: "GOTMPDIR", Value: cfg.Getenv("GOTMPDIR")},
87 {Name: "GOTOOLDIR", Value: base.ToolDir},
88 }
89
90 if work.GccgoBin != "" {
91 env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoBin})
92 } else {
93 env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoName})
94 }
95
96 key, val := cfg.GetArchEnv()
97 if key != "" {
98 env = append(env, cfg.EnvVar{Name: key, Value: val})
99 }
100
101 cc := cfg.DefaultCC(cfg.Goos, cfg.Goarch)
102 if env := strings.Fields(cfg.Getenv("CC")); len(env) > 0 {
103 cc = env[0]
104 }
105 cxx := cfg.DefaultCXX(cfg.Goos, cfg.Goarch)
106 if env := strings.Fields(cfg.Getenv("CXX")); len(env) > 0 {
107 cxx = env[0]
108 }
109 env = append(env, cfg.EnvVar{Name: "AR", Value: envOr("AR", "ar")})
110 env = append(env, cfg.EnvVar{Name: "CC", Value: cc})
111 env = append(env, cfg.EnvVar{Name: "CXX", Value: cxx})
112
113 if cfg.BuildContext.CgoEnabled {
114 env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "1"})
115 } else {
116 env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "0"})
117 }
118
119 return env
120 }
121
122 func envOr(name, def string) string {
123 val := cfg.Getenv(name)
124 if val != "" {
125 return val
126 }
127 return def
128 }
129
130 func findEnv(env []cfg.EnvVar, name string) string {
131 for _, e := range env {
132 if e.Name == name {
133 return e.Value
134 }
135 }
136 return ""
137 }
138
139
140 func ExtraEnvVars() []cfg.EnvVar {
141 gomod := ""
142 if modload.HasModRoot() {
143 gomod = filepath.Join(modload.ModRoot(), "go.mod")
144 } else if modload.Enabled() {
145 gomod = os.DevNull
146 }
147 return []cfg.EnvVar{
148 {Name: "GOMOD", Value: gomod},
149 }
150 }
151
152
153
154 func ExtraEnvVarsCostly() []cfg.EnvVar {
155 var b work.Builder
156 b.Init()
157 cppflags, cflags, cxxflags, fflags, ldflags, err := b.CFlags(&load.Package{})
158 if err != nil {
159
160 fmt.Fprintf(os.Stderr, "go: invalid cflags: %v\n", err)
161 return nil
162 }
163 cmd := b.GccCmd(".", "")
164
165 return []cfg.EnvVar{
166
167 {Name: "CGO_CFLAGS", Value: strings.Join(cflags, " ")},
168 {Name: "CGO_CPPFLAGS", Value: strings.Join(cppflags, " ")},
169 {Name: "CGO_CXXFLAGS", Value: strings.Join(cxxflags, " ")},
170 {Name: "CGO_FFLAGS", Value: strings.Join(fflags, " ")},
171 {Name: "CGO_LDFLAGS", Value: strings.Join(ldflags, " ")},
172 {Name: "PKG_CONFIG", Value: b.PkgconfigCmd()},
173 {Name: "GOGCCFLAGS", Value: strings.Join(cmd[3:], " ")},
174 }
175 }
176
177
178 func argKey(arg string) string {
179 i := strings.Index(arg, "=")
180 if i < 0 {
181 return arg
182 }
183 return arg[:i]
184 }
185
186 func runEnv(cmd *base.Command, args []string) {
187 if *envJson && *envU {
188 base.Fatalf("go env: cannot use -json with -u")
189 }
190 if *envJson && *envW {
191 base.Fatalf("go env: cannot use -json with -w")
192 }
193 if *envU && *envW {
194 base.Fatalf("go env: cannot use -u with -w")
195 }
196 env := cfg.CmdEnv
197 env = append(env, ExtraEnvVars()...)
198
199
200
201
202 needCostly := true
203 if len(args) > 0 {
204 needCostly = false
205 for _, arg := range args {
206 switch argKey(arg) {
207 case "CGO_CFLAGS",
208 "CGO_CPPFLAGS",
209 "CGO_CXXFLAGS",
210 "CGO_FFLAGS",
211 "CGO_LDFLAGS",
212 "PKG_CONFIG",
213 "GOGCCFLAGS":
214 needCostly = true
215 }
216 }
217 }
218 if needCostly {
219 env = append(env, ExtraEnvVarsCostly()...)
220 }
221
222 if *envW {
223
224 if len(args) == 0 {
225 base.Fatalf("go env -w: no KEY=VALUE arguments given")
226 }
227 osEnv := make(map[string]string)
228 for _, e := range cfg.OrigEnv {
229 if i := strings.Index(e, "="); i >= 0 {
230 osEnv[e[:i]] = e[i+1:]
231 }
232 }
233 add := make(map[string]string)
234 for _, arg := range args {
235 i := strings.Index(arg, "=")
236 if i < 0 {
237 base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg)
238 }
239 key, val := arg[:i], arg[i+1:]
240 if err := checkEnvWrite(key, val, env); err != nil {
241 base.Fatalf("go env -w: %v", err)
242 }
243 if _, ok := add[key]; ok {
244 base.Fatalf("go env -w: multiple values for key: %s", key)
245 }
246 add[key] = val
247 if osVal := osEnv[key]; osVal != "" && osVal != val {
248 fmt.Fprintf(os.Stderr, "warning: go env -w %s=... does not override conflicting OS environment variable\n", key)
249 }
250 }
251 updateEnvFile(add, nil)
252 return
253 }
254
255 if *envU {
256
257 if len(args) == 0 {
258 base.Fatalf("go env -u: no arguments given")
259 }
260 del := make(map[string]bool)
261 for _, arg := range args {
262 if err := checkEnvWrite(arg, "", env); err != nil {
263 base.Fatalf("go env -u: %v", err)
264 }
265 del[arg] = true
266 }
267 updateEnvFile(nil, del)
268 return
269 }
270
271 if len(args) > 0 {
272 if *envJson {
273 var es []cfg.EnvVar
274 for _, name := range args {
275 e := cfg.EnvVar{Name: name, Value: findEnv(env, name)}
276 es = append(es, e)
277 }
278 printEnvAsJSON(es)
279 } else {
280 for _, name := range args {
281 fmt.Printf("%s\n", findEnv(env, name))
282 }
283 }
284 return
285 }
286
287 if *envJson {
288 printEnvAsJSON(env)
289 return
290 }
291
292 for _, e := range env {
293 if e.Name != "TERM" {
294 switch runtime.GOOS {
295 default:
296 fmt.Printf("%s=\"%s\"\n", e.Name, e.Value)
297 case "plan9":
298 if strings.IndexByte(e.Value, '\x00') < 0 {
299 fmt.Printf("%s='%s'\n", e.Name, strings.ReplaceAll(e.Value, "'", "''"))
300 } else {
301 v := strings.Split(e.Value, "\x00")
302 fmt.Printf("%s=(", e.Name)
303 for x, s := range v {
304 if x > 0 {
305 fmt.Printf(" ")
306 }
307 fmt.Printf("%s", s)
308 }
309 fmt.Printf(")\n")
310 }
311 case "windows":
312 fmt.Printf("set %s=%s\n", e.Name, e.Value)
313 }
314 }
315 }
316 }
317
318 func printEnvAsJSON(env []cfg.EnvVar) {
319 m := make(map[string]string)
320 for _, e := range env {
321 if e.Name == "TERM" {
322 continue
323 }
324 m[e.Name] = e.Value
325 }
326 enc := json.NewEncoder(os.Stdout)
327 enc.SetIndent("", "\t")
328 if err := enc.Encode(m); err != nil {
329 base.Fatalf("go env -json: %s", err)
330 }
331 }
332
333 func checkEnvWrite(key, val string, env []cfg.EnvVar) error {
334 switch key {
335 case "GOEXE", "GOGCCFLAGS", "GOHOSTARCH", "GOHOSTOS", "GOMOD", "GOTOOLDIR":
336 return fmt.Errorf("%s cannot be modified", key)
337 case "GOENV":
338 return fmt.Errorf("%s can only be set using the OS environment", key)
339 }
340
341
342 if !cfg.CanGetenv(key) {
343 return fmt.Errorf("unknown go command variable %s", key)
344 }
345
346 if !utf8.ValidString(val) {
347 return fmt.Errorf("invalid UTF-8 in %s=... value", key)
348 }
349 if strings.Contains(val, "\x00") {
350 return fmt.Errorf("invalid NUL in %s=... value", key)
351 }
352 if strings.ContainsAny(val, "\v\r\n") {
353 return fmt.Errorf("invalid newline in %s=... value", key)
354 }
355 return nil
356 }
357
358 func updateEnvFile(add map[string]string, del map[string]bool) {
359 file, err := cfg.EnvFile()
360 if file == "" {
361 base.Fatalf("go env: cannot find go env config: %v", err)
362 }
363 data, err := ioutil.ReadFile(file)
364 if err != nil && (!os.IsNotExist(err) || len(add) == 0) {
365 base.Fatalf("go env: reading go env config: %v", err)
366 }
367
368 lines := strings.SplitAfter(string(data), "\n")
369 if lines[len(lines)-1] == "" {
370 lines = lines[:len(lines)-1]
371 } else {
372 lines[len(lines)-1] += "\n"
373 }
374
375
376
377 prev := make(map[string]int)
378 for l, line := range lines {
379 if key := lineToKey(line); key != "" {
380 if p, ok := prev[key]; ok {
381 lines[p] = ""
382 }
383 prev[key] = l
384 }
385 }
386
387
388 for key, val := range add {
389 if p, ok := prev[key]; ok {
390 lines[p] = key + "=" + val + "\n"
391 delete(add, key)
392 }
393 }
394 for key, val := range add {
395 lines = append(lines, key+"="+val+"\n")
396 }
397
398
399 for key := range del {
400 if p, ok := prev[key]; ok {
401 lines[p] = ""
402 }
403 }
404
405
406
407
408 start := 0
409 for i := 0; i <= len(lines); i++ {
410 if i == len(lines) || lineToKey(lines[i]) == "" {
411 sortKeyValues(lines[start:i])
412 start = i + 1
413 }
414 }
415
416 data = []byte(strings.Join(lines, ""))
417 err = ioutil.WriteFile(file, data, 0666)
418 if err != nil {
419
420 os.MkdirAll(filepath.Dir(file), 0777)
421 err = ioutil.WriteFile(file, data, 0666)
422 if err != nil {
423 base.Fatalf("go env: writing go env config: %v", err)
424 }
425 }
426 }
427
428
429 func lineToKey(line string) string {
430 i := strings.Index(line, "=")
431 if i < 0 || strings.Contains(line[:i], "#") {
432 return ""
433 }
434 return line[:i]
435 }
436
437
438
439 func sortKeyValues(lines []string) {
440 sort.Slice(lines, func(i, j int) bool {
441 return lineToKey(lines[i]) < lineToKey(lines[j])
442 })
443 }
444
View as plain text