github.com/tiagovtristao/plz@v13.4.0+incompatible/src/parse/rules.go (about)

     1  package parse
     2  
     3  import (
     4  	"encoding/json"
     5  	"os"
     6  	"path"
     7  	"regexp"
     8  	"sort"
     9  	"strings"
    10  
    11  	"github.com/thought-machine/please/src/core"
    12  	"github.com/thought-machine/please/src/parse/asp"
    13  	"github.com/thought-machine/please/src/parse/rules"
    14  )
    15  
    16  // PrintRuleArgs prints the arguments of all builtin rules (plus any associated ones from the given targets)
    17  func PrintRuleArgs(state *core.BuildState, labels []core.BuildLabel) {
    18  	env := getRuleArgs(state, labels)
    19  	b, err := json.MarshalIndent(env, "", "  ")
    20  	if err != nil {
    21  		log.Fatalf("Failed JSON encoding: %s", err)
    22  	}
    23  	os.Stdout.Write(b)
    24  }
    25  
    26  // AllBuiltinFunctions returns all the builtin functions, including those in a given set of labels.
    27  func AllBuiltinFunctions(state *core.BuildState, labels []core.BuildLabel) map[string]*asp.FuncDef {
    28  	p := newAspParser(state)
    29  	m := map[string]*asp.FuncDef{}
    30  	dir, _ := rules.AssetDir("")
    31  	sort.Strings(dir)
    32  	for _, filename := range dir {
    33  		if !strings.HasSuffix(filename, ".gob") && filename != "builtins.build_defs" {
    34  			stmts, err := p.ParseData(rules.MustAsset(filename), filename)
    35  			if err != nil {
    36  				log.Fatalf("%s", err)
    37  			}
    38  			addAllFunctions(m, stmts)
    39  		}
    40  	}
    41  	for _, l := range labels {
    42  		t := state.Graph.TargetOrDie(l)
    43  		for _, out := range t.Outputs() {
    44  			stmts, err := p.ParseFileOnly(path.Join(t.OutDir(), out))
    45  			if err != nil {
    46  				log.Fatalf("%s", err)
    47  			}
    48  			addAllFunctions(m, stmts)
    49  		}
    50  	}
    51  	return m
    52  }
    53  
    54  // addAllFunctions adds all the functions from a set of statements to the given map.
    55  func addAllFunctions(m map[string]*asp.FuncDef, stmts []*asp.Statement) {
    56  	for _, stmt := range stmts {
    57  		if f := stmt.FuncDef; f != nil && !f.IsPrivate && f.Docstring != "" {
    58  			f.Docstring = strings.TrimSpace(strings.Trim(f.Docstring, `"`))
    59  			m[f.Name] = f
    60  		}
    61  	}
    62  }
    63  
    64  // getRuleArgs retrieves the arguments of builtin rules. It's split from PrintRuleArgs for testing.
    65  func getRuleArgs(state *core.BuildState, labels []core.BuildLabel) environment {
    66  	argsRegex := regexp.MustCompile("\n +Args: *\n")
    67  	env := environment{Functions: map[string]function{}}
    68  	for name, f := range AllBuiltinFunctions(state, labels) {
    69  		r := function{Docstring: f.Docstring}
    70  		if strings.HasSuffix(f.EoDef.Filename, "_rules.build_defs") {
    71  			r.Language = strings.TrimSuffix(f.EoDef.Filename, "_rules.build_defs")
    72  		}
    73  		if indices := argsRegex.FindStringIndex(r.Docstring); indices != nil {
    74  			r.Comment = strings.TrimSpace(r.Docstring[:indices[0]])
    75  		}
    76  		r.Args = make([]functionArg, len(f.Arguments))
    77  		for i, a := range f.Arguments {
    78  			r.Args[i] = functionArg{
    79  				Name:     a.Name,
    80  				Types:    a.Type,
    81  				Required: a.Value == nil,
    82  			}
    83  			regex := regexp.MustCompile(a.Name + `(?: \(.*\))?: ((?s:.*))`)
    84  			if match := regex.FindStringSubmatch(r.Docstring); match != nil {
    85  				r.Args[i].Comment = filterMatch(match[1])
    86  			}
    87  		}
    88  		env.Functions[name] = r
    89  	}
    90  	return env
    91  }
    92  
    93  type environment struct {
    94  	Functions map[string]function `json:"functions"`
    95  }
    96  
    97  // A function describes a function within the global environment
    98  type function struct {
    99  	Args      []functionArg `json:"args"`
   100  	Comment   string        `json:"comment,omitempty"`
   101  	Docstring string        `json:"docstring,omitempty"`
   102  	Language  string        `json:"language,omitempty"`
   103  }
   104  
   105  // A functionArg represents a single argument to a function.
   106  type functionArg struct {
   107  	Comment    string   `json:"comment,omitempty"`
   108  	Deprecated bool     `json:"deprecated,omitempty"`
   109  	Name       string   `json:"name"`
   110  	Required   bool     `json:"required,omitempty"`
   111  	Types      []string `json:"types"`
   112  }
   113  
   114  // filterMatch filters a regex match to the part we want.
   115  // It's pretty much impossible to handle this as just a regex so we do it here instead.
   116  func filterMatch(match string) string {
   117  	lines := strings.Split(match, "\n")
   118  	regex := regexp.MustCompile(`^ *[a-z_]+(?: \([^)]+\))?:`)
   119  	for i, line := range lines {
   120  		if regex.MatchString(line) {
   121  			return strings.Join(lines[:i], " ")
   122  		}
   123  		lines[i] = strings.TrimSpace(line)
   124  	}
   125  	return strings.TrimSpace(match)
   126  }