Source file src/cmd/internal/script/conds.go

     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  	"internal/syslist"
    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 := syslist.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 := syslist.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{
   127  		eval:  sync.OnceValues(eval),
   128  		usage: CondUsage{Summary: summary},
   129  	}
   130  }
   131  
   132  type onceCond struct {
   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  	return l.eval()
   144  }
   145  
   146  // CachedCondition is like Condition but only calls eval the first time the
   147  // condition is evaluated for a given suffix.
   148  // Future calls with the same suffix reuse the earlier result.
   149  //
   150  // The eval function is not passed a *State because the condition is cached
   151  // across all execution states and must not vary by state.
   152  func CachedCondition(summary string, eval func(string) (bool, error)) Cond {
   153  	return &cachedCond{eval: eval, usage: CondUsage{Summary: summary, Prefix: true}}
   154  }
   155  
   156  type cachedCond struct {
   157  	m     sync.Map
   158  	eval  func(string) (bool, error)
   159  	usage CondUsage
   160  }
   161  
   162  func (c *cachedCond) Usage() *CondUsage { return &c.usage }
   163  
   164  func (c *cachedCond) Eval(_ *State, suffix string) (bool, error) {
   165  	for {
   166  		var ready chan struct{}
   167  
   168  		v, loaded := c.m.Load(suffix)
   169  		if !loaded {
   170  			ready = make(chan struct{})
   171  			v, loaded = c.m.LoadOrStore(suffix, (<-chan struct{})(ready))
   172  
   173  			if !loaded {
   174  				inPanic := true
   175  				defer func() {
   176  					if inPanic {
   177  						c.m.Delete(suffix)
   178  					}
   179  					close(ready)
   180  				}()
   181  
   182  				b, err := c.eval(suffix)
   183  				inPanic = false
   184  
   185  				if err == nil {
   186  					c.m.Store(suffix, b)
   187  					return b, nil
   188  				} else {
   189  					c.m.Store(suffix, err)
   190  					return false, err
   191  				}
   192  			}
   193  		}
   194  
   195  		switch v := v.(type) {
   196  		case bool:
   197  			return v, nil
   198  		case error:
   199  			return false, v
   200  		case <-chan struct{}:
   201  			<-v
   202  		}
   203  	}
   204  }
   205  

View as plain text