218 lines
5.3 KiB
Go
218 lines
5.3 KiB
Go
/* Copyright 2016 The Bazel Authors. All rights reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package generator
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"reflect"
|
|
"sort"
|
|
|
|
"github.com/bazelbuild/bazel-gazelle/internal/config"
|
|
"github.com/bazelbuild/bazel-gazelle/internal/packages"
|
|
bf "github.com/bazelbuild/buildtools/build"
|
|
bt "github.com/bazelbuild/buildtools/tables"
|
|
)
|
|
|
|
// KeyValue represents a key-value pair. This gets converted into a
|
|
// rule attribute, i.e., a Skylark keyword argument.
|
|
type KeyValue struct {
|
|
Key string
|
|
Value interface{}
|
|
}
|
|
|
|
// GlobValue represents a Bazel glob expression.
|
|
type GlobValue struct {
|
|
Patterns []string
|
|
Excludes []string
|
|
}
|
|
|
|
// EmptyRule generates an empty rule with the given kind and name.
|
|
func EmptyRule(kind, name string) *bf.CallExpr {
|
|
return NewRule(kind, []KeyValue{{"name", name}})
|
|
}
|
|
|
|
// NewRule generates a rule of the given kind with the given attributes.
|
|
func NewRule(kind string, kwargs []KeyValue) *bf.CallExpr {
|
|
sort.Sort(byAttrName(kwargs))
|
|
|
|
var list []bf.Expr
|
|
for _, arg := range kwargs {
|
|
expr := newValue(arg.Value)
|
|
list = append(list, &bf.BinaryExpr{
|
|
X: &bf.LiteralExpr{Token: arg.Key},
|
|
Op: "=",
|
|
Y: expr,
|
|
})
|
|
}
|
|
|
|
return &bf.CallExpr{
|
|
X: &bf.LiteralExpr{Token: kind},
|
|
List: list,
|
|
}
|
|
}
|
|
|
|
// newValue converts a Go value into the corresponding expression in Bazel BUILD file.
|
|
func newValue(val interface{}) bf.Expr {
|
|
rv := reflect.ValueOf(val)
|
|
switch rv.Kind() {
|
|
case reflect.Bool:
|
|
tok := "False"
|
|
if rv.Bool() {
|
|
tok = "True"
|
|
}
|
|
return &bf.LiteralExpr{Token: tok}
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
return &bf.LiteralExpr{Token: fmt.Sprintf("%d", val)}
|
|
|
|
case reflect.Float32, reflect.Float64:
|
|
return &bf.LiteralExpr{Token: fmt.Sprintf("%f", val)}
|
|
|
|
case reflect.String:
|
|
return &bf.StringExpr{Value: val.(string)}
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
var list []bf.Expr
|
|
for i := 0; i < rv.Len(); i++ {
|
|
elem := newValue(rv.Index(i).Interface())
|
|
list = append(list, elem)
|
|
}
|
|
return &bf.ListExpr{List: list}
|
|
|
|
case reflect.Map:
|
|
rkeys := rv.MapKeys()
|
|
sort.Sort(byString(rkeys))
|
|
args := make([]bf.Expr, len(rkeys))
|
|
for i, rk := range rkeys {
|
|
label := fmt.Sprintf("@%s//go/platform:%s", config.RulesGoRepoName, mapKeyString(rk))
|
|
k := &bf.StringExpr{Value: label}
|
|
v := newValue(rv.MapIndex(rk).Interface())
|
|
if l, ok := v.(*bf.ListExpr); ok {
|
|
l.ForceMultiLine = true
|
|
}
|
|
args[i] = &bf.KeyValueExpr{Key: k, Value: v}
|
|
}
|
|
args = append(args, &bf.KeyValueExpr{
|
|
Key: &bf.StringExpr{Value: "//conditions:default"},
|
|
Value: &bf.ListExpr{},
|
|
})
|
|
sel := &bf.CallExpr{
|
|
X: &bf.LiteralExpr{Token: "select"},
|
|
List: []bf.Expr{&bf.DictExpr{List: args, ForceMultiLine: true}},
|
|
}
|
|
return sel
|
|
|
|
case reflect.Struct:
|
|
switch val := val.(type) {
|
|
case GlobValue:
|
|
patternsValue := newValue(val.Patterns)
|
|
globArgs := []bf.Expr{patternsValue}
|
|
if len(val.Excludes) > 0 {
|
|
excludesValue := newValue(val.Excludes)
|
|
globArgs = append(globArgs, &bf.KeyValueExpr{
|
|
Key: &bf.StringExpr{Value: "excludes"},
|
|
Value: excludesValue,
|
|
})
|
|
}
|
|
return &bf.CallExpr{
|
|
X: &bf.LiteralExpr{Token: "glob"},
|
|
List: globArgs,
|
|
}
|
|
|
|
case packages.PlatformStrings:
|
|
var pieces []bf.Expr
|
|
if len(val.Generic) > 0 {
|
|
pieces = append(pieces, newValue(val.Generic))
|
|
}
|
|
if len(val.OS) > 0 {
|
|
pieces = append(pieces, newValue(val.OS))
|
|
}
|
|
if len(val.Arch) > 0 {
|
|
pieces = append(pieces, newValue(val.Arch))
|
|
}
|
|
if len(val.Platform) > 0 {
|
|
pieces = append(pieces, newValue(val.Platform))
|
|
}
|
|
if len(pieces) == 0 {
|
|
return &bf.ListExpr{}
|
|
} else if len(pieces) == 1 {
|
|
return pieces[0]
|
|
} else {
|
|
e := pieces[0]
|
|
if list, ok := e.(*bf.ListExpr); ok {
|
|
list.ForceMultiLine = true
|
|
}
|
|
for _, piece := range pieces[1:] {
|
|
e = &bf.BinaryExpr{X: e, Y: piece, Op: "+"}
|
|
}
|
|
return e
|
|
}
|
|
}
|
|
}
|
|
|
|
log.Panicf("type not supported: %T", val)
|
|
return nil
|
|
}
|
|
|
|
func mapKeyString(k reflect.Value) string {
|
|
switch s := k.Interface().(type) {
|
|
case string:
|
|
return s
|
|
case config.Platform:
|
|
return s.String()
|
|
default:
|
|
log.Panicf("unexpected map key: %v", k)
|
|
return ""
|
|
}
|
|
}
|
|
|
|
type byAttrName []KeyValue
|
|
|
|
var _ sort.Interface = byAttrName{}
|
|
|
|
func (s byAttrName) Len() int {
|
|
return len(s)
|
|
}
|
|
|
|
func (s byAttrName) Less(i, j int) bool {
|
|
if cmp := bt.NamePriority[s[i].Key] - bt.NamePriority[s[j].Key]; cmp != 0 {
|
|
return cmp < 0
|
|
}
|
|
return s[i].Key < s[j].Key
|
|
}
|
|
|
|
func (s byAttrName) Swap(i, j int) {
|
|
s[i], s[j] = s[j], s[i]
|
|
}
|
|
|
|
type byString []reflect.Value
|
|
|
|
var _ sort.Interface = byString{}
|
|
|
|
func (s byString) Len() int {
|
|
return len(s)
|
|
}
|
|
|
|
func (s byString) Less(i, j int) bool {
|
|
return mapKeyString(s[i]) < mapKeyString(s[j])
|
|
}
|
|
|
|
func (s byString) Swap(i, j int) {
|
|
s[i], s[j] = s[j], s[i]
|
|
}
|