github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/go/script/conds.go (about) 1 // Copyright 2022 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 script 6 7 import ( 8 "fmt" 9 "os" 10 "runtime" 11 "sync" 12 13 "github.com/go-asm/go/cmd/go/imports" 14 ) 15 16 // DefaultConds returns a set of broadly useful script conditions. 17 // 18 // Run the 'help' command within a script engine to view a list of the available 19 // conditions. 20 func DefaultConds() map[string]Cond { 21 conds := make(map[string]Cond) 22 23 conds["GOOS"] = PrefixCondition( 24 "runtime.GOOS == <suffix>", 25 func(_ *State, suffix string) (bool, error) { 26 if suffix == runtime.GOOS { 27 return true, nil 28 } 29 if _, ok := imports.KnownOS[suffix]; !ok { 30 return false, fmt.Errorf("unrecognized GOOS %q", suffix) 31 } 32 return false, nil 33 }) 34 35 conds["GOARCH"] = PrefixCondition( 36 "runtime.GOARCH == <suffix>", 37 func(_ *State, suffix string) (bool, error) { 38 if suffix == runtime.GOARCH { 39 return true, nil 40 } 41 if _, ok := imports.KnownArch[suffix]; !ok { 42 return false, fmt.Errorf("unrecognized GOOS %q", suffix) 43 } 44 return false, nil 45 }) 46 47 conds["compiler"] = PrefixCondition( 48 "runtime.Compiler == <suffix>", 49 func(_ *State, suffix string) (bool, error) { 50 if suffix == runtime.Compiler { 51 return true, nil 52 } 53 switch suffix { 54 case "gc", "gccgo": 55 return false, nil 56 default: 57 return false, fmt.Errorf("unrecognized compiler %q", suffix) 58 } 59 }) 60 61 conds["root"] = BoolCondition("os.Geteuid() == 0", os.Geteuid() == 0) 62 63 return conds 64 } 65 66 // Condition returns a Cond with the given summary and evaluation function. 67 func Condition(summary string, eval func(*State) (bool, error)) Cond { 68 return &funcCond{eval: eval, usage: CondUsage{Summary: summary}} 69 } 70 71 type funcCond struct { 72 eval func(*State) (bool, error) 73 usage CondUsage 74 } 75 76 func (c *funcCond) Usage() *CondUsage { return &c.usage } 77 78 func (c *funcCond) Eval(s *State, suffix string) (bool, error) { 79 if suffix != "" { 80 return false, ErrUsage 81 } 82 return c.eval(s) 83 } 84 85 // PrefixCondition returns a Cond with the given summary and evaluation function. 86 func PrefixCondition(summary string, eval func(*State, string) (bool, error)) Cond { 87 return &prefixCond{eval: eval, usage: CondUsage{Summary: summary, Prefix: true}} 88 } 89 90 type prefixCond struct { 91 eval func(*State, string) (bool, error) 92 usage CondUsage 93 } 94 95 func (c *prefixCond) Usage() *CondUsage { return &c.usage } 96 97 func (c *prefixCond) Eval(s *State, suffix string) (bool, error) { 98 return c.eval(s, suffix) 99 } 100 101 // BoolCondition returns a Cond with the given truth value and summary. 102 // The Cond rejects the use of condition suffixes. 103 func BoolCondition(summary string, v bool) Cond { 104 return &boolCond{v: v, usage: CondUsage{Summary: summary}} 105 } 106 107 type boolCond struct { 108 v bool 109 usage CondUsage 110 } 111 112 func (b *boolCond) Usage() *CondUsage { return &b.usage } 113 114 func (b *boolCond) Eval(s *State, suffix string) (bool, error) { 115 if suffix != "" { 116 return false, ErrUsage 117 } 118 return b.v, nil 119 } 120 121 // OnceCondition returns a Cond that calls eval the first time the condition is 122 // evaluated. Future calls reuse the same result. 123 // 124 // The eval function is not passed a *State because the condition is cached 125 // across all execution states and must not vary by state. 126 func OnceCondition(summary string, eval func() (bool, error)) Cond { 127 return &onceCond{eval: eval, usage: CondUsage{Summary: summary}} 128 } 129 130 type onceCond struct { 131 once sync.Once 132 v bool 133 err error 134 eval func() (bool, error) 135 usage CondUsage 136 } 137 138 func (l *onceCond) Usage() *CondUsage { return &l.usage } 139 140 func (l *onceCond) Eval(s *State, suffix string) (bool, error) { 141 if suffix != "" { 142 return false, ErrUsage 143 } 144 l.once.Do(func() { l.v, l.err = l.eval() }) 145 return l.v, l.err 146 } 147 148 // CachedCondition is like Condition but only calls eval the first time the 149 // condition is evaluated for a given suffix. 150 // Future calls with the same suffix reuse the earlier result. 151 // 152 // The eval function is not passed a *State because the condition is cached 153 // across all execution states and must not vary by state. 154 func CachedCondition(summary string, eval func(string) (bool, error)) Cond { 155 return &cachedCond{eval: eval, usage: CondUsage{Summary: summary, Prefix: true}} 156 } 157 158 type cachedCond struct { 159 m sync.Map 160 eval func(string) (bool, error) 161 usage CondUsage 162 } 163 164 func (c *cachedCond) Usage() *CondUsage { return &c.usage } 165 166 func (c *cachedCond) Eval(_ *State, suffix string) (bool, error) { 167 for { 168 var ready chan struct{} 169 170 v, loaded := c.m.Load(suffix) 171 if !loaded { 172 ready = make(chan struct{}) 173 v, loaded = c.m.LoadOrStore(suffix, (<-chan struct{})(ready)) 174 175 if !loaded { 176 inPanic := true 177 defer func() { 178 if inPanic { 179 c.m.Delete(suffix) 180 } 181 close(ready) 182 }() 183 184 b, err := c.eval(suffix) 185 inPanic = false 186 187 if err == nil { 188 c.m.Store(suffix, b) 189 return b, nil 190 } else { 191 c.m.Store(suffix, err) 192 return false, err 193 } 194 } 195 } 196 197 switch v := v.(type) { 198 case bool: 199 return v, nil 200 case error: 201 return false, v 202 case <-chan struct{}: 203 <-v 204 } 205 } 206 }