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