blob: f2d061bde557021a5d1ba7097e405670a774fe3d [file] [log] [blame]
Colin Cross7f64b6d2015-07-09 13:57:48 -07001// Copyright 2015 Google Inc. 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
15package common
16
17import (
18 "fmt"
19 "reflect"
20 "strings"
21
22 "github.com/google/blueprint"
23 "github.com/google/blueprint/proptools"
24)
25
26// TODO: move this to proptools
27func extendProperties(ctx blueprint.EarlyMutatorContext,
28 requiredTag, srcPrefix string, dstValues []reflect.Value, srcValue reflect.Value,
29 callback func(string, string)) {
30 if srcPrefix != "" {
31 srcPrefix += "."
32 }
33 extendPropertiesRecursive(ctx, requiredTag, srcValue, dstValues, srcPrefix, "", callback)
34}
35
36func extendPropertiesRecursive(ctx blueprint.EarlyMutatorContext, requiredTag string,
37 srcValue reflect.Value, dstValues []reflect.Value, srcPrefix, dstPrefix string,
38 callback func(string, string)) {
39
40 typ := srcValue.Type()
41 for i := 0; i < srcValue.NumField(); i++ {
42 srcField := typ.Field(i)
43 if srcField.PkgPath != "" {
44 // The field is not exported so just skip it.
45 continue
46 }
47
48 localPropertyName := proptools.PropertyNameForField(srcField.Name)
49 srcPropertyName := srcPrefix + localPropertyName
50 srcFieldValue := srcValue.Field(i)
51
52 if !ctx.ContainsProperty(srcPropertyName) {
53 continue
54 }
55
56 found := false
57 for _, dstValue := range dstValues {
58 dstField, ok := dstValue.Type().FieldByName(srcField.Name)
59 if !ok {
60 continue
61 }
62
63 dstFieldValue := dstValue.FieldByIndex(dstField.Index)
64
65 if srcFieldValue.Type() != dstFieldValue.Type() {
66 panic(fmt.Errorf("can't extend mismatching types for %q (%s <- %s)",
67 srcPropertyName, dstFieldValue.Type(), srcFieldValue.Type()))
68 }
69
70 dstPropertyName := dstPrefix + localPropertyName
71
72 if requiredTag != "" {
73 tag := dstField.Tag.Get("android")
74 tags := map[string]bool{}
75 for _, entry := range strings.Split(tag, ",") {
76 if entry != "" {
77 tags[entry] = true
78 }
79 }
80
81 if !tags[requiredTag] {
82 ctx.PropertyErrorf(srcPropertyName, "property can't be specific to a build variant")
83 continue
84 }
85 }
86
87 if callback != nil {
88 callback(srcPropertyName, dstPropertyName)
89 }
90
91 found = true
92
93 switch srcFieldValue.Kind() {
94 case reflect.Bool:
95 // Replace the original value.
96 dstFieldValue.Set(srcFieldValue)
97 case reflect.String:
98 // Append the extension string.
99 dstFieldValue.SetString(dstFieldValue.String() +
100 srcFieldValue.String())
101 case reflect.Slice:
102 dstFieldValue.Set(reflect.AppendSlice(dstFieldValue, srcFieldValue))
103 case reflect.Interface:
104 if dstFieldValue.IsNil() != srcFieldValue.IsNil() {
105 panic(fmt.Errorf("can't extend field %q: nilitude mismatch", srcPropertyName))
106 }
107 if dstFieldValue.IsNil() {
108 continue
109 }
110
111 dstFieldValue = dstFieldValue.Elem()
112 srcFieldValue = srcFieldValue.Elem()
113
114 if dstFieldValue.Type() != srcFieldValue.Type() {
115 panic(fmt.Errorf("can't extend field %q: type mismatch", srcPropertyName))
116 }
117 if srcFieldValue.Kind() != reflect.Ptr {
118 panic(fmt.Errorf("can't extend field %q: interface not a pointer", srcPropertyName))
119 }
120 fallthrough
121 case reflect.Ptr:
122 if dstFieldValue.IsNil() != srcFieldValue.IsNil() {
123 panic(fmt.Errorf("can't extend field %q: nilitude mismatch", srcPropertyName))
124 }
125 if dstFieldValue.IsNil() {
126 continue
127 }
128
129 dstFieldValue = dstFieldValue.Elem()
130 srcFieldValue = srcFieldValue.Elem()
131
132 if dstFieldValue.Type() != srcFieldValue.Type() {
133 panic(fmt.Errorf("can't extend field %q: type mismatch", srcPropertyName))
134 }
135 if srcFieldValue.Kind() != reflect.Struct {
136 panic(fmt.Errorf("can't extend field %q: pointer not to a struct", srcPropertyName))
137 }
138 fallthrough
139 case reflect.Struct:
140 // Recursively extend the struct's fields.
141 extendPropertiesRecursive(ctx, requiredTag, srcFieldValue, []reflect.Value{dstFieldValue},
142 srcPropertyName+".", srcPropertyName+".", callback)
143 default:
144 panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
145 srcPropertyName, srcFieldValue.Kind()))
146 }
147 }
148 if !found {
149 ctx.PropertyErrorf(srcPropertyName, "failed to find property to extend")
150 }
151 }
152}