github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/envcmd/env.go (about) 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 "github.com/gagliardetto/golang-go/cmd/go/not-internal/base" 21 "github.com/gagliardetto/golang-go/cmd/go/not-internal/cache" 22 "github.com/gagliardetto/golang-go/cmd/go/not-internal/cfg" 23 "github.com/gagliardetto/golang-go/cmd/go/not-internal/load" 24 "github.com/gagliardetto/golang-go/cmd/go/not-internal/modload" 25 "github.com/gagliardetto/golang-go/cmd/go/not-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 }