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  }