summaryrefslogtreecommitdiffstats
path: root/src/cmd/go/internal/script/conds.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go/internal/script/conds.go')
-rw-r--r--src/cmd/go/internal/script/conds.go205
1 files changed, 205 insertions, 0 deletions
diff --git a/src/cmd/go/internal/script/conds.go b/src/cmd/go/internal/script/conds.go
new file mode 100644
index 0000000..d70f274
--- /dev/null
+++ b/src/cmd/go/internal/script/conds.go
@@ -0,0 +1,205 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package script
+
+import (
+ "cmd/go/internal/imports"
+ "fmt"
+ "os"
+ "runtime"
+ "sync"
+)
+
+// DefaultConds returns a set of broadly useful script conditions.
+//
+// Run the 'help' command within a script engine to view a list of the available
+// conditions.
+func DefaultConds() map[string]Cond {
+ conds := make(map[string]Cond)
+
+ conds["GOOS"] = PrefixCondition(
+ "runtime.GOOS == <suffix>",
+ func(_ *State, suffix string) (bool, error) {
+ if suffix == runtime.GOOS {
+ return true, nil
+ }
+ if _, ok := imports.KnownOS[suffix]; !ok {
+ return false, fmt.Errorf("unrecognized GOOS %q", suffix)
+ }
+ return false, nil
+ })
+
+ conds["GOARCH"] = PrefixCondition(
+ "runtime.GOARCH == <suffix>",
+ func(_ *State, suffix string) (bool, error) {
+ if suffix == runtime.GOARCH {
+ return true, nil
+ }
+ if _, ok := imports.KnownArch[suffix]; !ok {
+ return false, fmt.Errorf("unrecognized GOOS %q", suffix)
+ }
+ return false, nil
+ })
+
+ conds["compiler"] = PrefixCondition(
+ "runtime.Compiler == <suffix>",
+ func(_ *State, suffix string) (bool, error) {
+ if suffix == runtime.Compiler {
+ return true, nil
+ }
+ switch suffix {
+ case "gc", "gccgo":
+ return false, nil
+ default:
+ return false, fmt.Errorf("unrecognized compiler %q", suffix)
+ }
+ })
+
+ conds["root"] = BoolCondition("os.Geteuid() == 0", os.Geteuid() == 0)
+
+ return conds
+}
+
+// Condition returns a Cond with the given summary and evaluation function.
+func Condition(summary string, eval func(*State) (bool, error)) Cond {
+ return &funcCond{eval: eval, usage: CondUsage{Summary: summary}}
+}
+
+type funcCond struct {
+ eval func(*State) (bool, error)
+ usage CondUsage
+}
+
+func (c *funcCond) Usage() *CondUsage { return &c.usage }
+
+func (c *funcCond) Eval(s *State, suffix string) (bool, error) {
+ if suffix != "" {
+ return false, ErrUsage
+ }
+ return c.eval(s)
+}
+
+// PrefixCondition returns a Cond with the given summary and evaluation function.
+func PrefixCondition(summary string, eval func(*State, string) (bool, error)) Cond {
+ return &prefixCond{eval: eval, usage: CondUsage{Summary: summary, Prefix: true}}
+}
+
+type prefixCond struct {
+ eval func(*State, string) (bool, error)
+ usage CondUsage
+}
+
+func (c *prefixCond) Usage() *CondUsage { return &c.usage }
+
+func (c *prefixCond) Eval(s *State, suffix string) (bool, error) {
+ return c.eval(s, suffix)
+}
+
+// BoolCondition returns a Cond with the given truth value and summary.
+// The Cond rejects the use of condition suffixes.
+func BoolCondition(summary string, v bool) Cond {
+ return &boolCond{v: v, usage: CondUsage{Summary: summary}}
+}
+
+type boolCond struct {
+ v bool
+ usage CondUsage
+}
+
+func (b *boolCond) Usage() *CondUsage { return &b.usage }
+
+func (b *boolCond) Eval(s *State, suffix string) (bool, error) {
+ if suffix != "" {
+ return false, ErrUsage
+ }
+ return b.v, nil
+}
+
+// OnceCondition returns a Cond that calls eval the first time the condition is
+// evaluated. Future calls reuse the same result.
+//
+// The eval function is not passed a *State because the condition is cached
+// across all execution states and must not vary by state.
+func OnceCondition(summary string, eval func() (bool, error)) Cond {
+ return &onceCond{eval: eval, usage: CondUsage{Summary: summary}}
+}
+
+type onceCond struct {
+ once sync.Once
+ v bool
+ err error
+ eval func() (bool, error)
+ usage CondUsage
+}
+
+func (l *onceCond) Usage() *CondUsage { return &l.usage }
+
+func (l *onceCond) Eval(s *State, suffix string) (bool, error) {
+ if suffix != "" {
+ return false, ErrUsage
+ }
+ l.once.Do(func() { l.v, l.err = l.eval() })
+ return l.v, l.err
+}
+
+// CachedCondition is like Condition but only calls eval the first time the
+// condition is evaluated for a given suffix.
+// Future calls with the same suffix reuse the earlier result.
+//
+// The eval function is not passed a *State because the condition is cached
+// across all execution states and must not vary by state.
+func CachedCondition(summary string, eval func(string) (bool, error)) Cond {
+ return &cachedCond{eval: eval, usage: CondUsage{Summary: summary, Prefix: true}}
+}
+
+type cachedCond struct {
+ m sync.Map
+ eval func(string) (bool, error)
+ usage CondUsage
+}
+
+func (c *cachedCond) Usage() *CondUsage { return &c.usage }
+
+func (c *cachedCond) Eval(_ *State, suffix string) (bool, error) {
+ for {
+ var ready chan struct{}
+
+ v, loaded := c.m.Load(suffix)
+ if !loaded {
+ ready = make(chan struct{})
+ v, loaded = c.m.LoadOrStore(suffix, (<-chan struct{})(ready))
+
+ if !loaded {
+ inPanic := true
+ defer func() {
+ if inPanic {
+ c.m.Delete(suffix)
+ }
+ close(ready)
+ }()
+
+ b, err := c.eval(suffix)
+ inPanic = false
+
+ if err == nil {
+ c.m.Store(suffix, b)
+ return b, nil
+ } else {
+ c.m.Store(suffix, err)
+ return false, err
+ }
+ }
+ }
+
+ switch v := v.(type) {
+ case bool:
+ return v, nil
+ case error:
+ return false, v
+ case <-chan struct{}:
+ <-v
+ }
+ }
+}