Source file src/cmd/go/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  	"cmd/go/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  }
   206  

View as plain text