...

Source file src/github.com/bazelbuild/buildtools/build/rule.go

Documentation: github.com/bazelbuild/buildtools/build

     1  /*
     2  Copyright 2016 Google LLC
     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      https://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  func (f *File) rules(p func(r *Rule) bool) []*Rule {
    46  	var all []*Rule
    47  
    48  	for _, stmt := range f.Stmt {
    49  		Walk(stmt, func(x Expr, stk []Expr) {
    50  			call, ok := x.(*CallExpr)
    51  			if !ok {
    52  				return
    53  			}
    54  
    55  			// Skip nested calls.
    56  			for _, frame := range stk {
    57  				if _, ok := frame.(*CallExpr); ok {
    58  					return
    59  				}
    60  			}
    61  
    62  			// Check if the rule is correct.
    63  			rule := f.Rule(call)
    64  			if !p(rule) {
    65  				return
    66  			}
    67  			all = append(all, rule)
    68  		})
    69  	}
    70  
    71  	return all
    72  }
    73  
    74  // Rules returns the rules in the file of the given kind (such as "go_library").
    75  // If kind == "", Rules returns all rules in the file.
    76  func (f *File) Rules(kind string) []*Rule {
    77  	return f.rules(func(rule *Rule) bool {
    78  		// Check if the rule kind is correct.
    79  		return kind == "" || rule.Kind() == kind
    80  	})
    81  }
    82  
    83  // RuleAt returns the rule in the file that starts at the specified line, or null if no such rule.
    84  func (f *File) RuleAt(linenum int) *Rule {
    85  	all := f.rules(func(rule *Rule) bool {
    86  		start, end := rule.Call.X.Span()
    87  		return start.Line <= linenum && linenum <= end.Line
    88  	})
    89  	if len(all) != 1 {
    90  		return nil
    91  	}
    92  	return all[0]
    93  }
    94  
    95  // RuleNamed returns the rule in the file that has the specified name, or null if no such rule.
    96  func (f *File) RuleNamed(name string) *Rule {
    97  	all := f.rules(func(rule *Rule) bool {
    98  		return rule.Name() == name
    99  	})
   100  	if len(all) != 1 {
   101  		return nil
   102  	}
   103  	return all[0]
   104  }
   105  
   106  // DelRules removes rules with the given kind and name from the file.
   107  // An empty kind matches all kinds; an empty name matches all names.
   108  // It returns the number of rules that were deleted.
   109  func (f *File) DelRules(kind, name string) int {
   110  	var i int
   111  	for _, stmt := range f.Stmt {
   112  		if call, ok := stmt.(*CallExpr); ok {
   113  			r := f.Rule(call)
   114  			if (kind == "" || r.Kind() == kind) &&
   115  				(name == "" || r.Name() == name) {
   116  				continue
   117  			}
   118  		}
   119  		f.Stmt[i] = stmt
   120  		i++
   121  	}
   122  	n := len(f.Stmt) - i
   123  	f.Stmt = f.Stmt[:i]
   124  	return n
   125  }
   126  
   127  // If a build file contains exactly one unnamed rule, and no rules in the file explicitly have the
   128  // same name as the name of the directory the build file is in, we treat the unnamed rule as if it
   129  // had the name of the directory containing the BUILD file.
   130  // This is following a convention used in the Pants build system to cut down on boilerplate.
   131  func (f *File) implicitRuleName() string {
   132  	// We disallow empty names in the top-level BUILD files.
   133  	dir := filepath.Dir(f.Path)
   134  	if dir == "." {
   135  		return ""
   136  	}
   137  	sawAnonymousRule := false
   138  	possibleImplicitName := filepath.Base(dir)
   139  
   140  	for _, stmt := range f.Stmt {
   141  		call, ok := stmt.(*CallExpr)
   142  		if !ok {
   143  			continue
   144  		}
   145  		temp := &Rule{call, ""}
   146  		if temp.AttrString("name") == possibleImplicitName {
   147  			// A target explicitly has the name of the dir, so no implicit targets are allowed.
   148  			return ""
   149  		}
   150  		if temp.Kind() != "" && temp.AttrString("name") == "" {
   151  			if sawAnonymousRule {
   152  				return ""
   153  			}
   154  			sawAnonymousRule = true
   155  		}
   156  	}
   157  	if sawAnonymousRule {
   158  		return possibleImplicitName
   159  	}
   160  	return ""
   161  }
   162  
   163  // Kind returns the rule's kind (such as "go_library").
   164  // The kind of the rule may be given by a literal or it may be a sequence of dot expressions that
   165  // begins with a literal, if the call expression does not conform to either of these forms, an
   166  // empty string will be returned
   167  func (r *Rule) Kind() string {
   168  	var names []string
   169  	expr := r.Call.X
   170  	for {
   171  		x, ok := expr.(*DotExpr)
   172  		if !ok {
   173  			break
   174  		}
   175  		names = append(names, x.Name)
   176  		expr = x.X
   177  	}
   178  	x, ok := expr.(*Ident)
   179  	if !ok {
   180  		return ""
   181  	}
   182  	names = append(names, x.Name)
   183  	// Reverse the elements since the deepest expression contains the leading literal
   184  	for l, r := 0, len(names)-1; l < r; l, r = l+1, r-1 {
   185  		names[l], names[r] = names[r], names[l]
   186  	}
   187  	return strings.Join(names, ".")
   188  }
   189  
   190  // SetKind changes rule's kind (such as "go_library").
   191  func (r *Rule) SetKind(kind string) {
   192  	names := strings.Split(kind, ".")
   193  	var expr Expr
   194  	expr = &Ident{Name: names[0]}
   195  	for _, name := range names[1:] {
   196  		expr = &DotExpr{X: expr, Name: name}
   197  	}
   198  	r.Call.X = expr
   199  }
   200  
   201  // ExplicitName returns the rule's target name if it's explicitly provided as a string value, "" otherwise.
   202  func (r *Rule) ExplicitName() string {
   203  	return r.AttrString("name")
   204  }
   205  
   206  // Name returns the rule's target name.
   207  // If the rule has no explicit target name, Name returns the implicit name if there is one, else the empty string.
   208  func (r *Rule) Name() string {
   209  	explicitName := r.ExplicitName()
   210  	if explicitName == "" && r.Kind() != "package" {
   211  		return r.ImplicitName
   212  	}
   213  	return explicitName
   214  }
   215  
   216  // AttrKeys returns the keys of all the rule's attributes.
   217  func (r *Rule) AttrKeys() []string {
   218  	var keys []string
   219  	for _, expr := range r.Call.List {
   220  		if as, ok := expr.(*AssignExpr); ok {
   221  			if keyExpr, ok := as.LHS.(*Ident); ok {
   222  				keys = append(keys, keyExpr.Name)
   223  			}
   224  		}
   225  	}
   226  	return keys
   227  }
   228  
   229  // AttrDefn returns the AssignExpr defining the rule's attribute with the given key.
   230  // If the rule has no such attribute, AttrDefn returns nil.
   231  func (r *Rule) AttrDefn(key string) *AssignExpr {
   232  	for _, kv := range r.Call.List {
   233  		as, ok := kv.(*AssignExpr)
   234  		if !ok {
   235  			continue
   236  		}
   237  		k, ok := as.LHS.(*Ident)
   238  		if !ok || k.Name != key {
   239  			continue
   240  		}
   241  		return as
   242  	}
   243  	return nil
   244  }
   245  
   246  // Attr returns the value of the rule's attribute with the given key
   247  // (such as "name" or "deps").
   248  // If the rule has no such attribute, Attr returns nil.
   249  func (r *Rule) Attr(key string) Expr {
   250  	as := r.AttrDefn(key)
   251  	if as == nil {
   252  		return nil
   253  	}
   254  	return as.RHS
   255  }
   256  
   257  // DelAttr deletes the rule's attribute with the named key.
   258  // It returns the old value of the attribute, or nil if the attribute was not found.
   259  func (r *Rule) DelAttr(key string) Expr {
   260  	list := r.Call.List
   261  	for i, kv := range list {
   262  		as, ok := kv.(*AssignExpr)
   263  		if !ok {
   264  			continue
   265  		}
   266  		k, ok := as.LHS.(*Ident)
   267  		if !ok || k.Name != key {
   268  			continue
   269  		}
   270  		copy(list[i:], list[i+1:])
   271  		r.Call.List = list[:len(list)-1]
   272  		return as.RHS
   273  	}
   274  	return nil
   275  }
   276  
   277  // SetAttr sets the rule's attribute with the given key to value.
   278  // If the rule has no attribute with the key, SetAttr appends
   279  // one to the end of the rule's attribute list.
   280  func (r *Rule) SetAttr(key string, val Expr) {
   281  	as := r.AttrDefn(key)
   282  	if as != nil {
   283  		as.RHS = val
   284  		return
   285  	}
   286  
   287  	r.Call.List = append(r.Call.List,
   288  		&AssignExpr{
   289  			LHS: &Ident{Name: key},
   290  			Op:  "=",
   291  			RHS: val,
   292  		},
   293  	)
   294  }
   295  
   296  // AttrLiteral returns the literal form of the rule's attribute
   297  // with the given key (such as "cc_api_version"), only when
   298  // that value is an identifier or number.
   299  // If the rule has no such attribute or the attribute is not an identifier or number,
   300  // AttrLiteral returns "".
   301  func (r *Rule) AttrLiteral(key string) string {
   302  	value := r.Attr(key)
   303  	if ident, ok := value.(*Ident); ok {
   304  		return ident.Name
   305  	}
   306  	if literal, ok := value.(*LiteralExpr); ok {
   307  		return literal.Token
   308  	}
   309  	return ""
   310  }
   311  
   312  // AttrString returns the value of the rule's attribute
   313  // with the given key (such as "name"), as a string.
   314  // If the rule has no such attribute or the attribute has a non-string value,
   315  // Attr returns the empty string.
   316  func (r *Rule) AttrString(key string) string {
   317  	str, ok := r.Attr(key).(*StringExpr)
   318  	if !ok {
   319  		return ""
   320  	}
   321  	return str.Value
   322  }
   323  
   324  // AttrStrings returns the value of the rule's attribute
   325  // with the given key (such as "srcs"), as a []string.
   326  // If the rule has no such attribute or the attribute is not
   327  // a list of strings, AttrStrings returns a nil slice.
   328  func (r *Rule) AttrStrings(key string) []string {
   329  	return Strings(r.Attr(key))
   330  }
   331  
   332  // Strings returns expr as a []string.
   333  // If expr is not a list of string literals,
   334  // Strings returns a nil slice instead.
   335  // If expr is an empty list of string literals,
   336  // returns a non-nil empty slice.
   337  // (this allows differentiating between these two cases)
   338  func Strings(expr Expr) []string {
   339  	list, ok := expr.(*ListExpr)
   340  	if !ok {
   341  		return nil
   342  	}
   343  	all := []string{} // not nil
   344  	for _, l := range list.List {
   345  		str, ok := l.(*StringExpr)
   346  		if !ok {
   347  			return nil
   348  		}
   349  		all = append(all, str.Value)
   350  	}
   351  	return all
   352  }
   353  

View as plain text