blob: 8daade0f17d9906454bed5f5723bbbf1b898cdb3 [file] [log] [blame]
Colin Cross3f40fa42015-01-30 17:27:36 -08001// 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 "blueprint"
19 "blueprint/proptools"
20 "fmt"
21 "reflect"
22 "runtime"
23 "strings"
24)
25
26var (
27 Arm = newArch32("Arm")
28 Arm64 = newArch64("Arm64")
29 Mips = newArch32("Mips")
30 Mips64 = newArch64("Mips64")
31 X86 = newArch32("X86")
32 X86_64 = newArch64("X86_64")
33)
34
35/*
36Example blueprints file containing all variant property groups, with comment listing what type
37of variants get properties in that group:
38
39module {
40 arch: {
41 arm: {
42 // Host or device variants with arm architecture
43 },
44 arm64: {
45 // Host or device variants with arm64 architecture
46 },
47 mips: {
48 // Host or device variants with mips architecture
49 },
50 mips64: {
51 // Host or device variants with mips64 architecture
52 },
53 x86: {
54 // Host or device variants with x86 architecture
55 },
56 x86_64: {
57 // Host or device variants with x86_64 architecture
58 },
59 },
60 multilib: {
61 lib32: {
62 // Host or device variants for 32-bit architectures
63 },
64 lib64: {
65 // Host or device variants for 64-bit architectures
66 },
67 },
68 target: {
69 android: {
70 // Device variants
71 },
72 host: {
73 // Host variants
74 },
75 linux: {
76 // Linux host variants
77 },
78 darwin: {
79 // Darwin host variants
80 },
81 windows: {
82 // Windows host variants
83 },
84 not_windows: {
85 // Non-windows host variants
86 },
87 },
88}
89*/
90type archProperties struct {
91 Arch struct {
92 Arm interface{}
93 Arm64 interface{}
94 Mips interface{}
95 Mips64 interface{}
96 X86 interface{}
97 X86_64 interface{}
98 }
99 Multilib struct {
100 Lib32 interface{}
101 Lib64 interface{}
102 }
103 Target struct {
104 Host interface{}
105 Android interface{}
106 Linux interface{}
107 Darwin interface{}
108 Windows interface{}
109 Not_windows interface{}
110 }
111}
112
113// An Arch indicates a single CPU architecture.
114type Arch struct {
115 HostOrDevice HostOrDevice
116 ArchType ArchType
117 ArchVariant string
118 CpuVariant string
119}
120
121func (a Arch) String() string {
122 s := a.HostOrDevice.String() + "_" + a.ArchType.String()
123 if a.ArchVariant != "" {
124 s += "_" + a.ArchVariant
125 }
126 if a.CpuVariant != "" {
127 s += "_" + a.CpuVariant
128 }
129 return s
130}
131
132type ArchType struct {
133 Name string
134 Field string
135 Multilib string
136 MultilibField string
137}
138
139func newArch32(field string) ArchType {
140 return ArchType{
141 Name: strings.ToLower(field),
142 Field: field,
143 Multilib: "lib32",
144 MultilibField: "Lib32",
145 }
146}
147
148func newArch64(field string) ArchType {
149 return ArchType{
150 Name: strings.ToLower(field),
151 Field: field,
152 Multilib: "lib64",
153 MultilibField: "Lib64",
154 }
155}
156
157func (a ArchType) String() string {
158 return a.Name
159}
160
161type HostOrDeviceSupported int
162
163const (
164 _ HostOrDeviceSupported = iota
165 HostSupported
166 DeviceSupported
167 HostAndDeviceSupported
168)
169
170type HostOrDevice int
171
172const (
173 _ HostOrDevice = iota
174 Host
175 Device
176)
177
178func (hod HostOrDevice) String() string {
179 switch hod {
180 case Device:
181 return "device"
182 case Host:
183 return "host"
184 default:
185 panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
186 }
187}
188
189func (hod HostOrDevice) FieldLower() string {
190 switch hod {
191 case Device:
192 return "android"
193 case Host:
194 return "host"
195 default:
196 panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
197 }
198}
199
200func (hod HostOrDevice) Field() string {
201 switch hod {
202 case Device:
203 return "Android"
204 case Host:
205 return "Host"
206 default:
207 panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
208 }
209}
210
211func (hod HostOrDevice) Host() bool {
212 if hod == 0 {
213 panic("HostOrDevice unset")
214 }
215 return hod == Host
216}
217
218func (hod HostOrDevice) Device() bool {
219 if hod == 0 {
220 panic("HostOrDevice unset")
221 }
222 return hod == Device
223}
224
225var hostOrDeviceName = map[HostOrDevice]string{
226 Device: "device",
227 Host: "host",
228}
229
230var (
231 armArch = Arch{
232 HostOrDevice: Device,
233 ArchType: Arm,
234 ArchVariant: "armv7-a-neon",
235 CpuVariant: "cortex-a15",
236 }
237 arm64Arch = Arch{
238 HostOrDevice: Device,
239 ArchType: Arm64,
240 ArchVariant: "armv8-a",
241 CpuVariant: "denver",
242 }
243 hostArch = Arch{
244 HostOrDevice: Host,
245 ArchType: X86,
246 }
247 host64Arch = Arch{
248 HostOrDevice: Host,
249 ArchType: X86_64,
250 }
251)
252
253func ArchMutator(mctx blueprint.EarlyMutatorContext) {
254 var module AndroidModule
255 var ok bool
256 if module, ok = mctx.Module().(AndroidModule); !ok {
257 return
258 }
259
260 // TODO: this is all hardcoded for arm64 primary, arm secondary for now
261 // Replace with a configuration file written by lunch or bootstrap
262
263 arches := []Arch{}
264
265 if module.base().HostSupported() {
266 arches = append(arches, host64Arch)
267 }
268
269 if module.base().DeviceSupported() {
270 switch module.base().commonProperties.Compile_multilib {
271 case "both":
272 arches = append(arches, arm64Arch, armArch)
273 case "first", "64":
274 arches = append(arches, arm64Arch)
275 case "32":
276 arches = append(arches, armArch)
277 default:
278 mctx.ModuleErrorf(`compile_multilib must be "both", "first", "32", or "64", found %q`,
279 module.base().commonProperties.Compile_multilib)
280 }
281 }
282
283 archNames := []string{}
284 for _, arch := range arches {
285 archNames = append(archNames, arch.String())
286 }
287
288 modules := mctx.CreateVariations(archNames...)
289
290 for i, m := range modules {
291 m.(AndroidModule).base().SetArch(arches[i])
292 m.(AndroidModule).base().setArchProperties(mctx, arches[i])
293 }
294}
295
296func InitArchModule(m AndroidModule, defaultMultilib string,
297 propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
298
299 base := m.base()
300
301 base.commonProperties.Compile_multilib = defaultMultilib
302
303 base.generalProperties = append(base.generalProperties,
304 &base.commonProperties)
305 base.generalProperties = append(base.generalProperties,
306 propertyStructs...)
307
308 for _, properties := range base.generalProperties {
309 propertiesValue := reflect.ValueOf(properties)
310 if propertiesValue.Kind() != reflect.Ptr {
311 panic("properties must be a pointer to a struct")
312 }
313
314 propertiesValue = propertiesValue.Elem()
315 if propertiesValue.Kind() != reflect.Struct {
316 panic("properties must be a pointer to a struct")
317 }
318
319 archProperties := &archProperties{}
320 forEachInterface(reflect.ValueOf(archProperties), func(v reflect.Value) {
321 newValue := proptools.CloneProperties(propertiesValue)
322 proptools.ZeroProperties(newValue.Elem())
323 v.Set(newValue)
324 })
325
326 base.archProperties = append(base.archProperties, archProperties)
327 }
328
329 var allProperties []interface{}
330 allProperties = append(allProperties, base.generalProperties...)
331 for _, asp := range base.archProperties {
332 allProperties = append(allProperties, asp)
333 }
334
335 return m, allProperties
336}
337
338// Rewrite the module's properties structs to contain arch-specific values.
339func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext, arch Arch) {
340 for i := range a.generalProperties {
341 generalPropsValue := reflect.ValueOf(a.generalProperties[i]).Elem()
342
343 // Handle arch-specific properties in the form:
344 // arch {
345 // arm64 {
346 // key: value,
347 // },
348 // },
349 t := arch.ArchType
350 extendProperties(ctx, "arch", t.Name, generalPropsValue,
351 reflect.ValueOf(a.archProperties[i].Arch).FieldByName(t.Field).Elem().Elem())
352
353 // Handle multilib-specific properties in the form:
354 // multilib {
355 // lib32 {
356 // key: value,
357 // },
358 // },
359 extendProperties(ctx, "multilib", t.Multilib, generalPropsValue,
360 reflect.ValueOf(a.archProperties[i].Multilib).FieldByName(t.MultilibField).Elem().Elem())
361
362 // Handle host-or-device-specific properties in the form:
363 // target {
364 // host {
365 // key: value,
366 // },
367 // },
368 hod := arch.HostOrDevice
369 extendProperties(ctx, "target", hod.FieldLower(), generalPropsValue,
370 reflect.ValueOf(a.archProperties[i].Target).FieldByName(hod.Field()).Elem().Elem())
371
372 // Handle host target properties in the form:
373 // target {
374 // linux {
375 // key: value,
376 // },
377 // not_windows {
378 // key: value,
379 // },
380 // },
381 var osList = []struct {
382 goos string
383 field string
384 }{
385 {"darwin", "Darwin"},
386 {"linux", "Linux"},
387 {"windows", "Windows"},
388 }
389
390 if hod.Host() {
391 for _, v := range osList {
392 if v.goos == runtime.GOOS {
393 extendProperties(ctx, "target", v.goos, generalPropsValue,
394 reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field).Elem().Elem())
395 }
396 }
397 extendProperties(ctx, "target", "not_windows", generalPropsValue,
398 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Not_windows").Elem().Elem())
399 }
400
401 if ctx.Failed() {
402 return
403 }
404 }
405}
406
407func forEachInterface(v reflect.Value, f func(reflect.Value)) {
408 switch v.Kind() {
409 case reflect.Interface:
410 f(v)
411 case reflect.Struct:
412 for i := 0; i < v.NumField(); i++ {
413 forEachInterface(v.Field(i), f)
414 }
415 case reflect.Ptr:
416 forEachInterface(v.Elem(), f)
417 default:
418 panic(fmt.Errorf("Unsupported kind %s", v.Kind()))
419 }
420}
421
422// TODO: move this to proptools
423func extendProperties(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
424 dstValue, srcValue reflect.Value) {
425 extendPropertiesRecursive(ctx, variationType, variationName, dstValue, srcValue, "")
426}
427
428func extendPropertiesRecursive(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
429 dstValue, srcValue reflect.Value, recursePrefix string) {
430
431 typ := dstValue.Type()
432 if srcValue.Type() != typ {
433 panic(fmt.Errorf("can't extend mismatching types (%s <- %s)",
434 dstValue.Kind(), srcValue.Kind()))
435 }
436
437 for i := 0; i < srcValue.NumField(); i++ {
438 field := typ.Field(i)
439 if field.PkgPath != "" {
440 // The field is not exported so just skip it.
441 continue
442 }
443
444 srcFieldValue := srcValue.Field(i)
445 dstFieldValue := dstValue.Field(i)
446
447 localPropertyName := proptools.PropertyNameForField(field.Name)
448 propertyName := fmt.Sprintf("%s.%s.%s%s", variationType, variationName,
449 recursePrefix, localPropertyName)
450 propertyPresentInVariation := ctx.ContainsProperty(propertyName)
451
452 if !propertyPresentInVariation {
453 continue
454 }
455
456 tag := field.Tag.Get("android")
457 tags := map[string]bool{}
458 for _, entry := range strings.Split(tag, ",") {
459 if entry != "" {
460 tags[entry] = true
461 }
462 }
463
464 if !tags["arch_variant"] {
465 ctx.PropertyErrorf(propertyName, "property %q can't be specific to a build variant",
466 recursePrefix+proptools.PropertyNameForField(field.Name))
467 continue
468 }
469
470 switch srcFieldValue.Kind() {
471 case reflect.Bool:
472 // Replace the original value.
473 dstFieldValue.Set(srcFieldValue)
474 case reflect.String:
475 // Append the extension string.
476 dstFieldValue.SetString(dstFieldValue.String() +
477 srcFieldValue.String())
478 case reflect.Struct:
479 // Recursively extend the struct's fields.
480 newRecursePrefix := fmt.Sprintf("%s%s.", recursePrefix, strings.ToLower(field.Name))
481 extendPropertiesRecursive(ctx, variationType, variationName,
482 dstFieldValue, srcFieldValue,
483 newRecursePrefix)
484 case reflect.Slice:
485 val, err := archCombineSlices(dstFieldValue, srcFieldValue, tags["arch_subtract"])
486 if err != nil {
487 ctx.PropertyErrorf(propertyName, err.Error())
488 continue
489 }
490 dstFieldValue.Set(val)
491 case reflect.Ptr, reflect.Interface:
492 // Recursively extend the pointed-to struct's fields.
493 if dstFieldValue.IsNil() != srcFieldValue.IsNil() {
494 panic(fmt.Errorf("can't extend field %q: nilitude mismatch"))
495 }
496 if dstFieldValue.Type() != srcFieldValue.Type() {
497 panic(fmt.Errorf("can't extend field %q: type mismatch"))
498 }
499 if !dstFieldValue.IsNil() {
500 newRecursePrefix := fmt.Sprintf("%s.%s", recursePrefix, field.Name)
501 extendPropertiesRecursive(ctx, variationType, variationName,
502 dstFieldValue.Elem(), srcFieldValue.Elem(),
503 newRecursePrefix)
504 }
505 default:
506 panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
507 field.Name, srcFieldValue.Kind()))
508 }
509 }
510}
511
512func archCombineSlices(general, arch reflect.Value, canSubtract bool) (reflect.Value, error) {
513 if !canSubtract {
514 // Append the extension slice.
515 return reflect.AppendSlice(general, arch), nil
516 }
517
518 // Support -val in arch list to subtract a value from original list
519 l := general.Interface().([]string)
520 for archIndex := 0; archIndex < arch.Len(); archIndex++ {
521 archString := arch.Index(archIndex).String()
522 if strings.HasPrefix(archString, "-") {
523 generalIndex := findStringInSlice(archString[1:], l)
524 if generalIndex == -1 {
525 return reflect.Value{},
526 fmt.Errorf("can't find %q to subtract from general properties", archString[1:])
527 }
528 l = append(l[:generalIndex], l[generalIndex+1:]...)
529 } else {
530 l = append(l, archString)
531 }
532 }
533
534 return reflect.ValueOf(l), nil
535}
536
537func findStringInSlice(str string, slice []string) int {
538 for i, s := range slice {
539 if s == str {
540 return i
541 }
542 }
543
544 return -1
545}