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 }