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 }