golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/envutil/envutil.go (about)

     1  // Copyright 2015 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 envutil provides utilities for working with environment variables.
     6  package envutil
     7  
     8  import (
     9  	"os"
    10  	"os/exec"
    11  	"runtime"
    12  	"strings"
    13  )
    14  
    15  // Dedup returns a copy of env with any duplicates removed, in favor of
    16  // later values.
    17  // Items are expected to be on the normal environment "key=value" form.
    18  //
    19  // Keys are interpreted as if on the given GOOS.
    20  // (On Windows, key comparison is case-insensitive.)
    21  func Dedup(goos string, env []string) []string {
    22  	caseInsensitive := (goos == "windows")
    23  
    24  	// Construct the output in reverse order, to preserve the
    25  	// last occurrence of each key.
    26  	saw := map[string]bool{}
    27  	out := make([]string, 0, len(env))
    28  	for n := len(env); n > 0; n-- {
    29  		kv := env[n-1]
    30  
    31  		k, _ := Split(kv)
    32  		if caseInsensitive {
    33  			k = strings.ToLower(k)
    34  		}
    35  		if saw[k] {
    36  			continue
    37  		}
    38  
    39  		saw[k] = true
    40  		out = append(out, kv)
    41  	}
    42  
    43  	// Now reverse the slice to restore the original order.
    44  	for i := 0; i < len(out)/2; i++ {
    45  		j := len(out) - i - 1
    46  		out[i], out[j] = out[j], out[i]
    47  	}
    48  
    49  	return out
    50  }
    51  
    52  // Get returns the value of key in env, interpreted according to goos.
    53  func Get(goos string, env []string, key string) string {
    54  	for n := len(env); n > 0; n-- {
    55  		kv := env[n-1]
    56  		if v, ok := Match(goos, kv, key); ok {
    57  			return v
    58  		}
    59  	}
    60  	return ""
    61  }
    62  
    63  // Match checks whether a "key=value" string matches key and, if so,
    64  // returns the value.
    65  //
    66  // On Windows, the key comparison is case-insensitive.
    67  func Match(goos, kv, key string) (value string, ok bool) {
    68  	if len(kv) <= len(key) || kv[len(key)] != '=' {
    69  		return "", false
    70  	}
    71  
    72  	if goos == "windows" {
    73  		// Case insensitive.
    74  		if !strings.EqualFold(kv[:len(key)], key) {
    75  			return "", false
    76  		}
    77  	} else {
    78  		// Case sensitive.
    79  		if kv[:len(key)] != key {
    80  			return "", false
    81  		}
    82  	}
    83  
    84  	return kv[len(key)+1:], true
    85  }
    86  
    87  // Split splits a "key=value" string into a key and value.
    88  func Split(kv string) (key, value string) {
    89  	parts := strings.SplitN(kv, "=", 2)
    90  	if len(parts) < 2 {
    91  		return parts[0], ""
    92  	}
    93  	return parts[0], parts[1]
    94  }
    95  
    96  // SetDir sets cmd.Dir to dir, and also updates cmd.Env to ensure that PWD matches.
    97  //
    98  // If dir is the empty string, SetDir clears cmd.Dir and sets PWD to the current
    99  // working directory.
   100  func SetDir(cmd *exec.Cmd, dir string) {
   101  	if dir == "" {
   102  		cmd.Dir = ""
   103  		dir, _ = os.Getwd()
   104  	} else {
   105  		cmd.Dir = dir
   106  	}
   107  	SetEnv(cmd, "PWD="+dir)
   108  }
   109  
   110  // SetEnv sets cmd.Env to include the given key=value pairs,
   111  // removing any duplicates for the key and leaving all other keys unchanged.
   112  //
   113  // (Removing duplicates is not strictly necessary with modern versions of the Go
   114  // standard library, but causes less confusion if cmd.Env is written to a log —
   115  // as is sometimes done in packages within this module.)
   116  func SetEnv(cmd *exec.Cmd, kv ...string) {
   117  	if len(kv) == 0 {
   118  		return
   119  	}
   120  	env := cmd.Env
   121  	if env == nil {
   122  		env = os.Environ()
   123  	}
   124  	cmd.Env = Dedup(runtime.GOOS, append(env, kv...))
   125  }