github.com/afking/bazel-gazelle@v0.0.0-20180301150245-c02bc0f529e8/internal/rules/construct.go (about)

     1  /* Copyright 2016 The Bazel Authors. All rights reserved.
     2  
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7     http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package rules
    17  
    18  import (
    19  	"fmt"
    20  	"log"
    21  	"reflect"
    22  	"sort"
    23  
    24  	"github.com/bazelbuild/bazel-gazelle/internal/config"
    25  	"github.com/bazelbuild/bazel-gazelle/internal/packages"
    26  	bf "github.com/bazelbuild/buildtools/build"
    27  	bt "github.com/bazelbuild/buildtools/tables"
    28  )
    29  
    30  // KeyValue represents a key-value pair. This gets converted into a
    31  // rule attribute, i.e., a Skylark keyword argument.
    32  type KeyValue struct {
    33  	Key   string
    34  	Value interface{}
    35  }
    36  
    37  // GlobValue represents a Bazel glob expression.
    38  type GlobValue struct {
    39  	Patterns []string
    40  	Excludes []string
    41  }
    42  
    43  // EmptyRule generates an empty rule with the given kind and name.
    44  func EmptyRule(kind, name string) *bf.CallExpr {
    45  	return NewRule(kind, []KeyValue{{"name", name}})
    46  }
    47  
    48  // NewRule generates a rule of the given kind with the given attributes.
    49  func NewRule(kind string, kwargs []KeyValue) *bf.CallExpr {
    50  	sort.Sort(byAttrName(kwargs))
    51  
    52  	var list []bf.Expr
    53  	for _, arg := range kwargs {
    54  		expr := newValue(arg.Value)
    55  		list = append(list, &bf.BinaryExpr{
    56  			X:  &bf.LiteralExpr{Token: arg.Key},
    57  			Op: "=",
    58  			Y:  expr,
    59  		})
    60  	}
    61  
    62  	return &bf.CallExpr{
    63  		X:    &bf.LiteralExpr{Token: kind},
    64  		List: list,
    65  	}
    66  }
    67  
    68  // newValue converts a Go value into the corresponding expression in Bazel BUILD file.
    69  func newValue(val interface{}) bf.Expr {
    70  	rv := reflect.ValueOf(val)
    71  	switch rv.Kind() {
    72  	case reflect.Bool:
    73  		tok := "False"
    74  		if rv.Bool() {
    75  			tok = "True"
    76  		}
    77  		return &bf.LiteralExpr{Token: tok}
    78  
    79  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
    80  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    81  		return &bf.LiteralExpr{Token: fmt.Sprintf("%d", val)}
    82  
    83  	case reflect.Float32, reflect.Float64:
    84  		return &bf.LiteralExpr{Token: fmt.Sprintf("%f", val)}
    85  
    86  	case reflect.String:
    87  		return &bf.StringExpr{Value: val.(string)}
    88  
    89  	case reflect.Slice, reflect.Array:
    90  		var list []bf.Expr
    91  		for i := 0; i < rv.Len(); i++ {
    92  			elem := newValue(rv.Index(i).Interface())
    93  			list = append(list, elem)
    94  		}
    95  		return &bf.ListExpr{List: list}
    96  
    97  	case reflect.Map:
    98  		rkeys := rv.MapKeys()
    99  		sort.Sort(byString(rkeys))
   100  		args := make([]bf.Expr, len(rkeys))
   101  		for i, rk := range rkeys {
   102  			label := fmt.Sprintf("@%s//go/platform:%s", config.RulesGoRepoName, mapKeyString(rk))
   103  			k := &bf.StringExpr{Value: label}
   104  			v := newValue(rv.MapIndex(rk).Interface())
   105  			if l, ok := v.(*bf.ListExpr); ok {
   106  				l.ForceMultiLine = true
   107  			}
   108  			args[i] = &bf.KeyValueExpr{Key: k, Value: v}
   109  		}
   110  		args = append(args, &bf.KeyValueExpr{
   111  			Key:   &bf.StringExpr{Value: "//conditions:default"},
   112  			Value: &bf.ListExpr{},
   113  		})
   114  		sel := &bf.CallExpr{
   115  			X:    &bf.LiteralExpr{Token: "select"},
   116  			List: []bf.Expr{&bf.DictExpr{List: args, ForceMultiLine: true}},
   117  		}
   118  		return sel
   119  
   120  	case reflect.Struct:
   121  		switch val := val.(type) {
   122  		case GlobValue:
   123  			patternsValue := newValue(val.Patterns)
   124  			globArgs := []bf.Expr{patternsValue}
   125  			if len(val.Excludes) > 0 {
   126  				excludesValue := newValue(val.Excludes)
   127  				globArgs = append(globArgs, &bf.KeyValueExpr{
   128  					Key:   &bf.StringExpr{Value: "excludes"},
   129  					Value: excludesValue,
   130  				})
   131  			}
   132  			return &bf.CallExpr{
   133  				X:    &bf.LiteralExpr{Token: "glob"},
   134  				List: globArgs,
   135  			}
   136  
   137  		case packages.PlatformStrings:
   138  			var pieces []bf.Expr
   139  			if len(val.Generic) > 0 {
   140  				pieces = append(pieces, newValue(val.Generic))
   141  			}
   142  			if len(val.OS) > 0 {
   143  				pieces = append(pieces, newValue(val.OS))
   144  			}
   145  			if len(val.Arch) > 0 {
   146  				pieces = append(pieces, newValue(val.Arch))
   147  			}
   148  			if len(val.Platform) > 0 {
   149  				pieces = append(pieces, newValue(val.Platform))
   150  			}
   151  			if len(pieces) == 0 {
   152  				return &bf.ListExpr{}
   153  			} else if len(pieces) == 1 {
   154  				return pieces[0]
   155  			} else {
   156  				e := pieces[0]
   157  				if list, ok := e.(*bf.ListExpr); ok {
   158  					list.ForceMultiLine = true
   159  				}
   160  				for _, piece := range pieces[1:] {
   161  					e = &bf.BinaryExpr{X: e, Y: piece, Op: "+"}
   162  				}
   163  				return e
   164  			}
   165  		}
   166  	}
   167  
   168  	log.Panicf("type not supported: %T", val)
   169  	return nil
   170  }
   171  
   172  func mapKeyString(k reflect.Value) string {
   173  	switch s := k.Interface().(type) {
   174  	case string:
   175  		return s
   176  	case config.Platform:
   177  		return s.String()
   178  	default:
   179  		log.Panicf("unexpected map key: %v", k)
   180  		return ""
   181  	}
   182  }
   183  
   184  type byAttrName []KeyValue
   185  
   186  var _ sort.Interface = byAttrName{}
   187  
   188  func (s byAttrName) Len() int {
   189  	return len(s)
   190  }
   191  
   192  func (s byAttrName) Less(i, j int) bool {
   193  	if cmp := bt.NamePriority[s[i].Key] - bt.NamePriority[s[j].Key]; cmp != 0 {
   194  		return cmp < 0
   195  	}
   196  	return s[i].Key < s[j].Key
   197  }
   198  
   199  func (s byAttrName) Swap(i, j int) {
   200  	s[i], s[j] = s[j], s[i]
   201  }
   202  
   203  type byString []reflect.Value
   204  
   205  var _ sort.Interface = byString{}
   206  
   207  func (s byString) Len() int {
   208  	return len(s)
   209  }
   210  
   211  func (s byString) Less(i, j int) bool {
   212  	return mapKeyString(s[i]) < mapKeyString(s[j])
   213  }
   214  
   215  func (s byString) Swap(i, j int) {
   216  	s[i], s[j] = s[j], s[i]
   217  }