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  }