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 }