github.com/cheshirekow/buildtools@v0.0.0-20200224190056-5d637702fe81/build/rule.go (about) 1 /* 2 Copyright 2016 Google Inc. All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Rule-level API for inspecting and modifying a build.File syntax tree. 18 19 package build 20 21 import ( 22 "path/filepath" 23 "strings" 24 ) 25 26 // A Rule represents a single BUILD rule. 27 type Rule struct { 28 Call *CallExpr 29 ImplicitName string // The name which should be used if the name attribute is not set. See the comment on File.implicitRuleName. 30 } 31 32 // NewRule is a simple constructor for Rule. 33 func NewRule(call *CallExpr) *Rule { 34 return &Rule{call, ""} 35 } 36 37 func (f *File) Rule(call *CallExpr) *Rule { 38 r := &Rule{call, ""} 39 if r.AttrString("name") == "" { 40 r.ImplicitName = f.implicitRuleName() 41 } 42 return r 43 } 44 45 // Rules returns the rules in the file of the given kind (such as "go_library"). 46 // If kind == "", Rules returns all rules in the file. 47 func (f *File) Rules(kind string) []*Rule { 48 var all []*Rule 49 50 for _, stmt := range f.Stmt { 51 Walk(stmt, func(x Expr, stk []Expr) { 52 call, ok := x.(*CallExpr) 53 if !ok { 54 return 55 } 56 57 // Skip nested calls. 58 for _, frame := range stk { 59 if _, ok := frame.(*CallExpr); ok { 60 return 61 } 62 } 63 64 // Check if the rule kind is correct. 65 rule := f.Rule(call) 66 if kind != "" && rule.Kind() != kind { 67 return 68 } 69 all = append(all, rule) 70 }) 71 } 72 73 return all 74 } 75 76 // RuleAt returns the rule in the file that starts at the specified line, or null if no such rule. 77 func (f *File) RuleAt(linenum int) *Rule { 78 79 for _, stmt := range f.Stmt { 80 call, ok := stmt.(*CallExpr) 81 if !ok { 82 continue 83 } 84 start, end := call.X.Span() 85 if start.Line <= linenum && linenum <= end.Line { 86 return f.Rule(call) 87 } 88 } 89 return nil 90 } 91 92 // DelRules removes rules with the given kind and name from the file. 93 // An empty kind matches all kinds; an empty name matches all names. 94 // It returns the number of rules that were deleted. 95 func (f *File) DelRules(kind, name string) int { 96 var i int 97 for _, stmt := range f.Stmt { 98 if call, ok := stmt.(*CallExpr); ok { 99 r := f.Rule(call) 100 if (kind == "" || r.Kind() == kind) && 101 (name == "" || r.Name() == name) { 102 continue 103 } 104 } 105 f.Stmt[i] = stmt 106 i++ 107 } 108 n := len(f.Stmt) - i 109 f.Stmt = f.Stmt[:i] 110 return n 111 } 112 113 // If a build file contains exactly one unnamed rule, and no rules in the file explicitly have the 114 // same name as the name of the directory the build file is in, we treat the unnamed rule as if it 115 // had the name of the directory containing the BUILD file. 116 // This is following a convention used in the Pants build system to cut down on boilerplate. 117 func (f *File) implicitRuleName() string { 118 // We disallow empty names in the top-level BUILD files. 119 dir := filepath.Dir(f.Path) 120 if dir == "." { 121 return "" 122 } 123 sawAnonymousRule := false 124 possibleImplicitName := filepath.Base(dir) 125 126 for _, stmt := range f.Stmt { 127 call, ok := stmt.(*CallExpr) 128 if !ok { 129 continue 130 } 131 temp := &Rule{call, ""} 132 if temp.AttrString("name") == possibleImplicitName { 133 // A target explicitly has the name of the dir, so no implicit targets are allowed. 134 return "" 135 } 136 if temp.Kind() != "" && temp.AttrString("name") == "" { 137 if sawAnonymousRule { 138 return "" 139 } 140 sawAnonymousRule = true 141 } 142 } 143 if sawAnonymousRule { 144 return possibleImplicitName 145 } 146 return "" 147 } 148 149 // Kind returns the rule's kind (such as "go_library"). 150 // The kind of the rule may be given by a literal or it may be a sequence of dot expressions that 151 // begins with a literal, if the call expression does not conform to either of these forms, an 152 // empty string will be returned 153 func (r *Rule) Kind() string { 154 var names []string 155 expr := r.Call.X 156 for { 157 x, ok := expr.(*DotExpr) 158 if !ok { 159 break 160 } 161 names = append(names, x.Name) 162 expr = x.X 163 } 164 x, ok := expr.(*Ident) 165 if !ok { 166 return "" 167 } 168 names = append(names, x.Name) 169 // Reverse the elements since the deepest expression contains the leading literal 170 for l, r := 0, len(names)-1; l < r; l, r = l+1, r-1 { 171 names[l], names[r] = names[r], names[l] 172 } 173 return strings.Join(names, ".") 174 } 175 176 // SetKind changes rule's kind (such as "go_library"). 177 func (r *Rule) SetKind(kind string) { 178 names := strings.Split(kind, ".") 179 var expr Expr 180 expr = &Ident{Name: names[0]} 181 for _, name := range names[1:] { 182 expr = &DotExpr{X: expr, Name: name} 183 } 184 r.Call.X = expr 185 } 186 187 // ExplicitName returns the rule's target name if it's explicitly provided as a string value, "" otherwise. 188 func (r *Rule) ExplicitName() string { 189 return r.AttrString("name") 190 } 191 192 // Name returns the rule's target name. 193 // If the rule has no explicit target name, Name returns the implicit name if there is one, else the empty string. 194 func (r *Rule) Name() string { 195 explicitName := r.ExplicitName() 196 if explicitName == "" && r.Kind() != "package" { 197 return r.ImplicitName 198 } 199 return explicitName 200 } 201 202 // AttrKeys returns the keys of all the rule's attributes. 203 func (r *Rule) AttrKeys() []string { 204 var keys []string 205 for _, expr := range r.Call.List { 206 if as, ok := expr.(*AssignExpr); ok { 207 if keyExpr, ok := as.LHS.(*Ident); ok { 208 keys = append(keys, keyExpr.Name) 209 } 210 } 211 } 212 return keys 213 } 214 215 // AttrDefn returns the AssignExpr defining the rule's attribute with the given key. 216 // If the rule has no such attribute, AttrDefn returns nil. 217 func (r *Rule) AttrDefn(key string) *AssignExpr { 218 for _, kv := range r.Call.List { 219 as, ok := kv.(*AssignExpr) 220 if !ok { 221 continue 222 } 223 k, ok := as.LHS.(*Ident) 224 if !ok || k.Name != key { 225 continue 226 } 227 return as 228 } 229 return nil 230 } 231 232 // Attr returns the value of the rule's attribute with the given key 233 // (such as "name" or "deps"). 234 // If the rule has no such attribute, Attr returns nil. 235 func (r *Rule) Attr(key string) Expr { 236 as := r.AttrDefn(key) 237 if as == nil { 238 return nil 239 } 240 return as.RHS 241 } 242 243 // DelAttr deletes the rule's attribute with the named key. 244 // It returns the old value of the attribute, or nil if the attribute was not found. 245 func (r *Rule) DelAttr(key string) Expr { 246 list := r.Call.List 247 for i, kv := range list { 248 as, ok := kv.(*AssignExpr) 249 if !ok { 250 continue 251 } 252 k, ok := as.LHS.(*Ident) 253 if !ok || k.Name != key { 254 continue 255 } 256 copy(list[i:], list[i+1:]) 257 r.Call.List = list[:len(list)-1] 258 return as.RHS 259 } 260 return nil 261 } 262 263 // SetAttr sets the rule's attribute with the given key to value. 264 // If the rule has no attribute with the key, SetAttr appends 265 // one to the end of the rule's attribute list. 266 func (r *Rule) SetAttr(key string, val Expr) { 267 as := r.AttrDefn(key) 268 if as != nil { 269 as.RHS = val 270 return 271 } 272 273 r.Call.List = append(r.Call.List, 274 &AssignExpr{ 275 LHS: &Ident{Name: key}, 276 Op: "=", 277 RHS: val, 278 }, 279 ) 280 } 281 282 // AttrLiteral returns the literal form of the rule's attribute 283 // with the given key (such as "cc_api_version"), only when 284 // that value is an identifier or number. 285 // If the rule has no such attribute or the attribute is not an identifier or number, 286 // AttrLiteral returns "". 287 func (r *Rule) AttrLiteral(key string) string { 288 value := r.Attr(key) 289 if ident, ok := value.(*Ident); ok { 290 return ident.Name 291 } 292 if literal, ok := value.(*LiteralExpr); ok { 293 return literal.Token 294 } 295 return "" 296 } 297 298 // AttrString returns the value of the rule's attribute 299 // with the given key (such as "name"), as a string. 300 // If the rule has no such attribute or the attribute has a non-string value, 301 // Attr returns the empty string. 302 func (r *Rule) AttrString(key string) string { 303 str, ok := r.Attr(key).(*StringExpr) 304 if !ok { 305 return "" 306 } 307 return str.Value 308 } 309 310 // AttrStrings returns the value of the rule's attribute 311 // with the given key (such as "srcs"), as a []string. 312 // If the rule has no such attribute or the attribute is not 313 // a list of strings, AttrStrings returns a nil slice. 314 func (r *Rule) AttrStrings(key string) []string { 315 return Strings(r.Attr(key)) 316 } 317 318 // Strings returns expr as a []string. 319 // If expr is not a list of string literals, 320 // Strings returns a nil slice instead. 321 // If expr is an empty list of string literals, 322 // returns a non-nil empty slice. 323 // (this allows differentiating between these two cases) 324 func Strings(expr Expr) []string { 325 list, ok := expr.(*ListExpr) 326 if !ok { 327 return nil 328 } 329 all := []string{} // not nil 330 for _, l := range list.List { 331 str, ok := l.(*StringExpr) 332 if !ok { 333 return nil 334 } 335 all = append(all, str.Value) 336 } 337 return all 338 }