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  }