Add support for targets in Blueprints.

The default target selector uses the name of the host OS.
Modules can implement their own target selector by implementing
the context.TargetSelector interface and its unique selectTarget()
method.

Targets are defined this way in Blueprint files:

cc_library {
    name:  "libmylib",
    deps:  ["libmath", "libutils"],
    zones: ["frontend"],
    srcs:  ["main.cpp"],
    targets: {
        darwin: {
            moduleCflags: "-framework OpenGL -framework GLUT",
        },
        linux:  {
            deps: ["libx11headers", "libglu"],
        },
    },
}

In this example, a set of C flags are defined on OS X only and on
Linux, two new dependencies are added.

When a target is selected, its properties are merged with the properties
of the modules:
- a list is appended to the original list (see deps above)
- a string is concatenated to the original string
- a bool replaces the original bool
- a map adds or replaces the key/value pairs of the original map

Change-Id: Ic627d47f795d6a4ff56ca5f6f099cad157621af1
This commit is contained in:
Romain Guy 2014-08-12 17:50:11 -07:00
parent 2476f81d84
commit 285296519a
2 changed files with 140 additions and 14 deletions

View file

@ -9,6 +9,7 @@ import (
"os"
"path/filepath"
"reflect"
"runtime"
"sort"
"strings"
"text/scanner"
@ -90,8 +91,9 @@ type moduleInfo struct {
pos scanner.Position
propertyPos map[string]scanner.Position
properties struct {
Name string
Deps []string
Name string
Deps []string
Targets map[string][]*parser.Property
}
// set during ResolveDependencies
@ -109,6 +111,18 @@ type singletonInfo struct {
actionDefs localBuildActions
}
type TargetSelector interface {
SelectTarget() string
}
// Default target selector that simply returns the host OS name
type goosTargetSelector struct {
}
func (g *goosTargetSelector) SelectTarget() string {
return runtime.GOOS
}
func (e *Error) Error() string {
return fmt.Sprintf("%s: %s", e.Pos, e.Err)
@ -474,6 +488,21 @@ func (c *Context) processModuleDef(moduleDef *parser.Module,
return errs
}
var targetName string
if selector, ok := module.(TargetSelector); ok {
targetName = selector.SelectTarget()
} else {
defaultSelector := goosTargetSelector{}
targetName = defaultSelector.SelectTarget()
}
if targetProperties, ok := info.properties.Targets[targetName]; ok {
errs = mergeProperties(targetProperties, properties...)
if len(errs) > 0 {
return errs
}
}
info.pos = moduleDef.Pos
info.propertyPos = make(map[string]scanner.Position)
for _, propertyDef := range moduleDef.Properties {

View file

@ -13,9 +13,71 @@ type packedProperty struct {
unpacked bool
}
type valueHandler interface {
assignBool(value reflect.Value, data bool)
assignString(value reflect.Value, data string)
assignValue(value reflect.Value, data reflect.Value)
}
type valueSetter struct {
}
func (v *valueSetter) assignBool(value reflect.Value, data bool) {
value.SetBool(data)
}
func (v *valueSetter) assignString(value reflect.Value, data string) {
value.SetString(data)
}
func (v *valueSetter) assignValue(value reflect.Value, data reflect.Value) {
value.Set(data)
}
type valueMerger struct {
}
func (v *valueMerger) assignBool(value reflect.Value, data bool) {
// when merging bools, the original value is replaced
value.SetBool(data)
}
func (v *valueMerger) assignString(value reflect.Value, data string) {
value.SetString(value.String() + data)
}
func (v *valueMerger) assignValue(value reflect.Value, data reflect.Value) {
// this should never happen
if value.Kind() != data.Kind() {
panic("attempting to merge values of different kinds")
}
if value.Kind() == reflect.Slice {
value.Set(reflect.AppendSlice(value, data))
} else if value.Kind() == reflect.Map {
for _, key := range data.MapKeys() {
value.SetMapIndex(key, data.MapIndex(key))
}
}
}
func unpackProperties(propertyDefs []*parser.Property,
propertiesStructs ...interface{}) (errs []error) {
setter := new(valueSetter);
return unpackPropertiesWithHandler(setter, propertyDefs, propertiesStructs...)
}
func mergeProperties(propertyDefs []*parser.Property,
propertiesStructs ...interface{}) (errs []error) {
merger := new(valueMerger);
return unpackPropertiesWithHandler(merger, propertyDefs, propertiesStructs...)
}
func unpackPropertiesWithHandler(handler valueHandler, propertyDefs []*parser.Property,
propertiesStructs ...interface{}) (errs []error) {
propertyMap := make(map[string]*packedProperty)
for _, propertyDef := range propertyDefs {
name := propertyDef.Name
@ -51,7 +113,7 @@ func unpackProperties(propertyDefs []*parser.Property,
panic("properties must be a pointer to a struct")
}
newErrs := unpackStruct(propertiesValue, propertyMap)
newErrs := unpackStruct(propertiesValue, propertyMap, handler)
errs = append(errs, newErrs...)
if len(errs) >= maxErrors {
@ -75,7 +137,7 @@ func unpackProperties(propertyDefs []*parser.Property,
}
func unpackStruct(structValue reflect.Value,
propertyMap map[string]*packedProperty) []error {
propertyMap map[string]*packedProperty, handler valueHandler) []error {
structType := structValue.Type()
@ -104,12 +166,20 @@ func unpackStruct(structValue reflect.Value,
panic(fmt.Errorf("field %s is a non-string slice", field.Name))
}
case reflect.Struct:
newErrs := unpackStruct(fieldValue, propertyMap)
newErrs := unpackStruct(fieldValue, propertyMap, handler)
errs = append(errs, newErrs...)
if len(errs) >= maxErrors {
return errs
}
continue // This field doesn't correspond to a specific property.
case reflect.Map:
fieldType := field.Type
if fieldType.Key().Kind() != reflect.String {
panic(fmt.Errorf("field %s uses a non-string key", field.Name))
}
if fieldType.Elem().Kind() != reflect.TypeOf(([]*parser.Property)(nil)).Kind() {
panic(fmt.Errorf("field %s uses a non-parser.Property value", field.Name))
}
default:
panic(fmt.Errorf("unsupported kind for field %s: %s",
field.Name, kind))
@ -128,11 +198,13 @@ func unpackStruct(structValue reflect.Value,
var newErrs []error
switch kind := fieldValue.Kind(); kind {
case reflect.Bool:
newErrs = unpackBool(fieldValue, packedProperty.property)
newErrs = unpackBool(fieldValue, packedProperty.property, handler)
case reflect.String:
newErrs = unpackString(fieldValue, packedProperty.property)
newErrs = unpackString(fieldValue, packedProperty.property, handler)
case reflect.Slice:
newErrs = unpackSlice(fieldValue, packedProperty.property)
newErrs = unpackSlice(fieldValue, packedProperty.property, handler)
case reflect.Map:
newErrs = unpackMap(fieldValue, packedProperty.property, handler)
}
errs = append(errs, newErrs...)
if len(errs) >= maxErrors {
@ -143,7 +215,7 @@ func unpackStruct(structValue reflect.Value,
return errs
}
func unpackBool(boolValue reflect.Value, property *parser.Property) []error {
func unpackBool(boolValue reflect.Value, property *parser.Property, handler valueHandler) []error {
if property.Value.Type != parser.Bool {
return []error{
fmt.Errorf("%s: can't assign %s value to %s property %q",
@ -151,12 +223,12 @@ func unpackBool(boolValue reflect.Value, property *parser.Property) []error {
property.Name),
}
}
boolValue.SetBool(property.Value.BoolValue)
handler.assignBool(boolValue, property.Value.BoolValue)
return nil
}
func unpackString(stringValue reflect.Value,
property *parser.Property) []error {
property *parser.Property, handler valueHandler) []error {
if property.Value.Type != parser.String {
return []error{
@ -165,11 +237,13 @@ func unpackString(stringValue reflect.Value,
property.Name),
}
}
stringValue.SetString(property.Value.StringValue)
handler.assignString(stringValue, property.Value.StringValue)
return nil
}
func unpackSlice(sliceValue reflect.Value, property *parser.Property) []error {
func unpackSlice(sliceValue reflect.Value, property *parser.Property,
handler valueHandler) []error {
if property.Value.Type != parser.List {
return []error{
fmt.Errorf("%s: can't assign %s value to %s property %q",
@ -187,10 +261,33 @@ func unpackSlice(sliceValue reflect.Value, property *parser.Property) []error {
list = append(list, value.StringValue)
}
sliceValue.Set(reflect.ValueOf(list))
handler.assignValue(sliceValue, reflect.ValueOf(list))
return nil
}
func unpackMap(mapValue reflect.Value, property *parser.Property,
handler valueHandler) []error {
if property.Value.Type != parser.Map {
return []error{
fmt.Errorf("%s: can't assign %s value to %s property %q",
property.Value.Pos, property.Value.Type, parser.Map,
property.Name),
}
}
m := make(map[string][]*parser.Property)
for _, p := range property.Value.MapValue {
if p.Value.Type != parser.Map {
panic("non-map value found in map")
}
m[p.Name] = p.Value.MapValue
}
handler.assignValue(mapValue, reflect.ValueOf(m))
return nil;
}
func propertyNameForField(field reflect.StructField) string {
r, size := utf8.DecodeRuneInString(field.Name)
propertyName := string(unicode.ToLower(r))