blob: ba1f67121a0837c8f0f5cf5fc0da2a2d7e7f8df7 [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 (
Colin Cross3f40fa42015-01-30 17:27:36 -080018 "fmt"
19 "reflect"
20 "runtime"
21 "strings"
Colin Crossf6566ed2015-03-24 11:13:38 -070022
23 "github.com/google/blueprint"
24 "github.com/google/blueprint/proptools"
Colin Cross3f40fa42015-01-30 17:27:36 -080025)
26
27var (
28 Arm = newArch32("Arm")
29 Arm64 = newArch64("Arm64")
30 Mips = newArch32("Mips")
31 Mips64 = newArch64("Mips64")
32 X86 = newArch32("X86")
33 X86_64 = newArch64("X86_64")
Colin Cross2fe66872015-03-30 17:20:39 -070034
35 Common = ArchType{
36 Name: "common",
37 }
Colin Cross3f40fa42015-01-30 17:27:36 -080038)
39
40/*
41Example blueprints file containing all variant property groups, with comment listing what type
42of variants get properties in that group:
43
44module {
45 arch: {
46 arm: {
47 // Host or device variants with arm architecture
48 },
49 arm64: {
50 // Host or device variants with arm64 architecture
51 },
52 mips: {
53 // Host or device variants with mips architecture
54 },
55 mips64: {
56 // Host or device variants with mips64 architecture
57 },
58 x86: {
59 // Host or device variants with x86 architecture
60 },
61 x86_64: {
62 // Host or device variants with x86_64 architecture
63 },
64 },
65 multilib: {
66 lib32: {
67 // Host or device variants for 32-bit architectures
68 },
69 lib64: {
70 // Host or device variants for 64-bit architectures
71 },
72 },
73 target: {
74 android: {
75 // Device variants
76 },
77 host: {
78 // Host variants
79 },
80 linux: {
81 // Linux host variants
82 },
83 darwin: {
84 // Darwin host variants
85 },
86 windows: {
87 // Windows host variants
88 },
89 not_windows: {
90 // Non-windows host variants
91 },
92 },
93}
94*/
Colin Cross7d5136f2015-05-11 13:39:40 -070095
Colin Cross3f40fa42015-01-30 17:27:36 -080096type archProperties struct {
Colin Cross7d5136f2015-05-11 13:39:40 -070097 // Properties to vary by target architecture
Colin Cross3f40fa42015-01-30 17:27:36 -080098 Arch struct {
Colin Cross7d5136f2015-05-11 13:39:40 -070099 // Properties for module variants being built to run on arm (host or device)
100 Arm interface{} `blueprint:"filter(android:\"arch_variant\")"`
101 // Properties for module variants being built to run on arm64 (host or device)
102 Arm64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
103 // Properties for module variants being built to run on mips (host or device)
104 Mips interface{} `blueprint:"filter(android:\"arch_variant\")"`
105 // Properties for module variants being built to run on mips64 (host or device)
106 Mips64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
107 // Properties for module variants being built to run on x86 (host or device)
108 X86 interface{} `blueprint:"filter(android:\"arch_variant\")"`
109 // Properties for module variants being built to run on x86_64 (host or device)
110 X86_64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
Colin Cross3f40fa42015-01-30 17:27:36 -0800111 }
Colin Cross7d5136f2015-05-11 13:39:40 -0700112 // Properties to vary by 32-bit or 64-bit
Colin Cross3f40fa42015-01-30 17:27:36 -0800113 Multilib struct {
Colin Cross7d5136f2015-05-11 13:39:40 -0700114 // Properties for module variants being built to run on 32-bit devices
115 Lib32 interface{} `blueprint:"filter(android:\"arch_variant\")"`
116 // Properties for module variants being built to run on 64-bit devices
117 Lib64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
Colin Cross3f40fa42015-01-30 17:27:36 -0800118 }
Colin Cross7d5136f2015-05-11 13:39:40 -0700119 // Properties to vary by build target (host or device, os, os+archictecture)
Colin Cross3f40fa42015-01-30 17:27:36 -0800120 Target struct {
Colin Cross7d5136f2015-05-11 13:39:40 -0700121 // Properties for module variants being built to run on the host
122 Host interface{} `blueprint:"filter(android:\"arch_variant\")"`
123 // Properties for module variants being built to run on the device
124 Android interface{} `blueprint:"filter(android:\"arch_variant\")"`
125 // Properties for module variants being built to run on arm devices
126 Android_arm interface{} `blueprint:"filter(android:\"arch_variant\")"`
127 // Properties for module variants being built to run on arm64 devices
128 Android_arm64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
129 // Properties for module variants being built to run on mips devices
130 Android_mips interface{} `blueprint:"filter(android:\"arch_variant\")"`
131 // Properties for module variants being built to run on mips64 devices
132 Android_mips64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
133 // Properties for module variants being built to run on x86 devices
134 Android_x86 interface{} `blueprint:"filter(android:\"arch_variant\")"`
135 // Properties for module variants being built to run on x86_64 devices
136 Android_x86_64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
137 // Properties for module variants being built to run on devices that support 64-bit
138 Android64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
139 // Properties for module variants being built to run on devices that do not support 64-bit
140 Android32 interface{} `blueprint:"filter(android:\"arch_variant\")"`
141 // Properties for module variants being built to run on linux hosts
142 Linux interface{} `blueprint:"filter(android:\"arch_variant\")"`
143 // Properties for module variants being built to run on linux x86 hosts
144 Linux_x86 interface{} `blueprint:"filter(android:\"arch_variant\")"`
145 // Properties for module variants being built to run on linux x86_64 hosts
146 Linux_x86_64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
147 // Properties for module variants being built to run on darwin hosts
148 Darwin interface{} `blueprint:"filter(android:\"arch_variant\")"`
149 // Properties for module variants being built to run on darwin x86 hosts
150 Darwin_x86 interface{} `blueprint:"filter(android:\"arch_variant\")"`
151 // Properties for module variants being built to run on darwin x86_64 hosts
152 Darwin_x86_64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
153 // Properties for module variants being built to run on windows hosts
154 Windows interface{} `blueprint:"filter(android:\"arch_variant\")"`
155 // Properties for module variants being built to run on linux or darwin hosts
156 Not_windows interface{} `blueprint:"filter(android:\"arch_variant\")"`
Colin Cross3f40fa42015-01-30 17:27:36 -0800157 }
158}
159
160// An Arch indicates a single CPU architecture.
161type Arch struct {
Colin Crossd3ba0392015-05-07 14:11:29 -0700162 ArchType ArchType
163 ArchVariant string
164 CpuVariant string
165 Abi string
Colin Cross3f40fa42015-01-30 17:27:36 -0800166}
167
168func (a Arch) String() string {
Colin Crossd3ba0392015-05-07 14:11:29 -0700169 s := a.ArchType.String()
Colin Cross3f40fa42015-01-30 17:27:36 -0800170 if a.ArchVariant != "" {
171 s += "_" + a.ArchVariant
172 }
173 if a.CpuVariant != "" {
174 s += "_" + a.CpuVariant
175 }
176 return s
177}
178
179type ArchType struct {
180 Name string
181 Field string
182 Multilib string
183 MultilibField string
184}
185
186func newArch32(field string) ArchType {
187 return ArchType{
188 Name: strings.ToLower(field),
189 Field: field,
190 Multilib: "lib32",
191 MultilibField: "Lib32",
192 }
193}
194
195func newArch64(field string) ArchType {
196 return ArchType{
197 Name: strings.ToLower(field),
198 Field: field,
199 Multilib: "lib64",
200 MultilibField: "Lib64",
201 }
202}
203
204func (a ArchType) String() string {
205 return a.Name
206}
207
208type HostOrDeviceSupported int
209
210const (
211 _ HostOrDeviceSupported = iota
212 HostSupported
213 DeviceSupported
214 HostAndDeviceSupported
215)
216
217type HostOrDevice int
218
219const (
220 _ HostOrDevice = iota
221 Host
222 Device
223)
224
225func (hod HostOrDevice) String() string {
226 switch hod {
227 case Device:
228 return "device"
229 case Host:
230 return "host"
231 default:
232 panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
233 }
234}
235
236func (hod HostOrDevice) FieldLower() string {
237 switch hod {
238 case Device:
239 return "android"
240 case Host:
241 return "host"
242 default:
243 panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
244 }
245}
246
247func (hod HostOrDevice) Field() string {
248 switch hod {
249 case Device:
250 return "Android"
251 case Host:
252 return "Host"
253 default:
254 panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
255 }
256}
257
258func (hod HostOrDevice) Host() bool {
259 if hod == 0 {
260 panic("HostOrDevice unset")
261 }
262 return hod == Host
263}
264
265func (hod HostOrDevice) Device() bool {
266 if hod == 0 {
267 panic("HostOrDevice unset")
268 }
269 return hod == Device
270}
271
272var hostOrDeviceName = map[HostOrDevice]string{
273 Device: "device",
274 Host: "host",
275}
276
277var (
278 armArch = Arch{
Colin Crossd3ba0392015-05-07 14:11:29 -0700279 ArchType: Arm,
280 ArchVariant: "armv7-a-neon",
281 CpuVariant: "cortex-a15",
282 Abi: "armeabi-v7a",
Colin Cross3f40fa42015-01-30 17:27:36 -0800283 }
284 arm64Arch = Arch{
Colin Crossd3ba0392015-05-07 14:11:29 -0700285 ArchType: Arm64,
286 ArchVariant: "armv8-a",
287 CpuVariant: "denver",
288 Abi: "arm64-v8a",
Colin Cross3f40fa42015-01-30 17:27:36 -0800289 }
Colin Crossd3ba0392015-05-07 14:11:29 -0700290 x86Arch = Arch{
291 ArchType: X86,
Colin Cross3f40fa42015-01-30 17:27:36 -0800292 }
Colin Crossd3ba0392015-05-07 14:11:29 -0700293 x8664Arch = Arch{
294 ArchType: X86_64,
Colin Cross3f40fa42015-01-30 17:27:36 -0800295 }
Colin Crossd3ba0392015-05-07 14:11:29 -0700296 commonArch = Arch{
297 ArchType: Common,
Colin Cross2fe66872015-03-30 17:20:39 -0700298 }
Colin Cross3f40fa42015-01-30 17:27:36 -0800299)
300
Colin Crossd3ba0392015-05-07 14:11:29 -0700301func HostOrDeviceMutator(mctx blueprint.EarlyMutatorContext) {
302 var module AndroidModule
303 var ok bool
304 if module, ok = mctx.Module().(AndroidModule); !ok {
305 return
306 }
307
308 hods := []HostOrDevice{}
309
310 if module.base().HostSupported() {
311 hods = append(hods, Host)
312 }
313
314 if module.base().DeviceSupported() {
315 hods = append(hods, Device)
316 }
317
318 if len(hods) == 0 {
319 return
320 }
321
322 hodNames := []string{}
323 for _, hod := range hods {
324 hodNames = append(hodNames, hod.String())
325 }
326
327 modules := mctx.CreateVariations(hodNames...)
328 for i, m := range modules {
329 m.(AndroidModule).base().SetHostOrDevice(hods[i])
330 }
331}
332
Colin Cross3f40fa42015-01-30 17:27:36 -0800333func ArchMutator(mctx blueprint.EarlyMutatorContext) {
334 var module AndroidModule
335 var ok bool
336 if module, ok = mctx.Module().(AndroidModule); !ok {
337 return
338 }
339
340 // TODO: this is all hardcoded for arm64 primary, arm secondary for now
341 // Replace with a configuration file written by lunch or bootstrap
342
343 arches := []Arch{}
344
Colin Crossd3ba0392015-05-07 14:11:29 -0700345 if module.base().HostSupported() && module.base().HostOrDevice().Host() {
Colin Cross2fe66872015-03-30 17:20:39 -0700346 switch module.base().commonProperties.Compile_multilib {
347 case "common":
Colin Crossd3ba0392015-05-07 14:11:29 -0700348 arches = append(arches, commonArch)
Colin Cross2fe66872015-03-30 17:20:39 -0700349 default:
Colin Crossd3ba0392015-05-07 14:11:29 -0700350 arches = append(arches, x8664Arch)
Colin Cross2fe66872015-03-30 17:20:39 -0700351 }
Colin Cross3f40fa42015-01-30 17:27:36 -0800352 }
353
Colin Crossd3ba0392015-05-07 14:11:29 -0700354 if module.base().DeviceSupported() && module.base().HostOrDevice().Device() {
Colin Cross3f40fa42015-01-30 17:27:36 -0800355 switch module.base().commonProperties.Compile_multilib {
Colin Cross2fe66872015-03-30 17:20:39 -0700356 case "common":
Colin Crossd3ba0392015-05-07 14:11:29 -0700357 arches = append(arches, commonArch)
Colin Cross3f40fa42015-01-30 17:27:36 -0800358 case "both":
359 arches = append(arches, arm64Arch, armArch)
360 case "first", "64":
361 arches = append(arches, arm64Arch)
362 case "32":
363 arches = append(arches, armArch)
364 default:
365 mctx.ModuleErrorf(`compile_multilib must be "both", "first", "32", or "64", found %q`,
366 module.base().commonProperties.Compile_multilib)
367 }
368 }
369
Colin Cross5049f022015-03-18 13:28:46 -0700370 if len(arches) == 0 {
371 return
372 }
373
Colin Cross3f40fa42015-01-30 17:27:36 -0800374 archNames := []string{}
375 for _, arch := range arches {
376 archNames = append(archNames, arch.String())
377 }
378
379 modules := mctx.CreateVariations(archNames...)
380
381 for i, m := range modules {
382 m.(AndroidModule).base().SetArch(arches[i])
Colin Crossd3ba0392015-05-07 14:11:29 -0700383 m.(AndroidModule).base().setArchProperties(mctx)
Colin Cross3f40fa42015-01-30 17:27:36 -0800384 }
385}
386
Colin Crossc472d572015-03-17 15:06:21 -0700387func InitArchModule(m AndroidModule, defaultMultilib Multilib,
Colin Cross3f40fa42015-01-30 17:27:36 -0800388 propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
389
390 base := m.base()
391
Colin Crossc472d572015-03-17 15:06:21 -0700392 base.commonProperties.Compile_multilib = string(defaultMultilib)
Colin Cross3f40fa42015-01-30 17:27:36 -0800393
394 base.generalProperties = append(base.generalProperties,
Colin Cross3f40fa42015-01-30 17:27:36 -0800395 propertyStructs...)
396
397 for _, properties := range base.generalProperties {
398 propertiesValue := reflect.ValueOf(properties)
399 if propertiesValue.Kind() != reflect.Ptr {
400 panic("properties must be a pointer to a struct")
401 }
402
403 propertiesValue = propertiesValue.Elem()
404 if propertiesValue.Kind() != reflect.Struct {
405 panic("properties must be a pointer to a struct")
406 }
407
408 archProperties := &archProperties{}
409 forEachInterface(reflect.ValueOf(archProperties), func(v reflect.Value) {
Colin Cross3ab7d882015-05-19 13:03:01 -0700410 newValue := proptools.CloneEmptyProperties(propertiesValue)
Colin Cross3f40fa42015-01-30 17:27:36 -0800411 v.Set(newValue)
412 })
413
414 base.archProperties = append(base.archProperties, archProperties)
415 }
416
417 var allProperties []interface{}
418 allProperties = append(allProperties, base.generalProperties...)
419 for _, asp := range base.archProperties {
420 allProperties = append(allProperties, asp)
421 }
422
423 return m, allProperties
424}
425
426// Rewrite the module's properties structs to contain arch-specific values.
Colin Crossd3ba0392015-05-07 14:11:29 -0700427func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext) {
428 arch := a.commonProperties.CompileArch
429 hod := a.commonProperties.CompileHostOrDevice
430
Colin Cross2fe66872015-03-30 17:20:39 -0700431 if arch.ArchType == Common {
432 return
433 }
434
Colin Cross3f40fa42015-01-30 17:27:36 -0800435 for i := range a.generalProperties {
436 generalPropsValue := reflect.ValueOf(a.generalProperties[i]).Elem()
437
438 // Handle arch-specific properties in the form:
Colin Crossb05bff22015-04-30 15:08:04 -0700439 // arch: {
440 // arm64: {
Colin Cross3f40fa42015-01-30 17:27:36 -0800441 // key: value,
442 // },
443 // },
444 t := arch.ArchType
Colin Cross28d76592015-03-26 16:14:04 -0700445 a.extendProperties(ctx, "arch", t.Name, generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800446 reflect.ValueOf(a.archProperties[i].Arch).FieldByName(t.Field).Elem().Elem())
447
448 // Handle multilib-specific properties in the form:
Colin Crossb05bff22015-04-30 15:08:04 -0700449 // multilib: {
450 // lib32: {
Colin Cross3f40fa42015-01-30 17:27:36 -0800451 // key: value,
452 // },
453 // },
Colin Cross28d76592015-03-26 16:14:04 -0700454 a.extendProperties(ctx, "multilib", t.Multilib, generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800455 reflect.ValueOf(a.archProperties[i].Multilib).FieldByName(t.MultilibField).Elem().Elem())
456
457 // Handle host-or-device-specific properties in the form:
Colin Crossb05bff22015-04-30 15:08:04 -0700458 // target: {
459 // host: {
Colin Cross3f40fa42015-01-30 17:27:36 -0800460 // key: value,
461 // },
462 // },
Colin Cross28d76592015-03-26 16:14:04 -0700463 a.extendProperties(ctx, "target", hod.FieldLower(), generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800464 reflect.ValueOf(a.archProperties[i].Target).FieldByName(hod.Field()).Elem().Elem())
465
466 // Handle host target properties in the form:
Colin Crossb05bff22015-04-30 15:08:04 -0700467 // target: {
468 // linux: {
Colin Cross3f40fa42015-01-30 17:27:36 -0800469 // key: value,
470 // },
Colin Crossb05bff22015-04-30 15:08:04 -0700471 // not_windows: {
472 // key: value,
473 // },
474 // linux_x86: {
475 // key: value,
476 // },
477 // linux_arm: {
Colin Cross3f40fa42015-01-30 17:27:36 -0800478 // key: value,
479 // },
480 // },
481 var osList = []struct {
482 goos string
483 field string
484 }{
485 {"darwin", "Darwin"},
486 {"linux", "Linux"},
487 {"windows", "Windows"},
488 }
489
490 if hod.Host() {
491 for _, v := range osList {
492 if v.goos == runtime.GOOS {
Colin Cross28d76592015-03-26 16:14:04 -0700493 a.extendProperties(ctx, "target", v.goos, generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800494 reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field).Elem().Elem())
Colin Crossb05bff22015-04-30 15:08:04 -0700495 t := arch.ArchType
496 a.extendProperties(ctx, "target", v.goos+"_"+t.Name, generalPropsValue,
497 reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field+"_"+t.Name).Elem().Elem())
Colin Cross3f40fa42015-01-30 17:27:36 -0800498 }
499 }
Colin Cross28d76592015-03-26 16:14:04 -0700500 a.extendProperties(ctx, "target", "not_windows", generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800501 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Not_windows").Elem().Elem())
502 }
503
Colin Crossf8209412015-03-26 14:44:26 -0700504 // Handle 64-bit device properties in the form:
505 // target {
506 // android64 {
507 // key: value,
508 // },
509 // android32 {
510 // key: value,
511 // },
512 // },
513 // WARNING: this is probably not what you want to use in your blueprints file, it selects
514 // options for all targets on a device that supports 64-bit binaries, not just the targets
515 // that are being compiled for 64-bit. Its expected use case is binaries like linker and
516 // debuggerd that need to know when they are a 32-bit process running on a 64-bit device
517 if hod.Device() {
518 if true /* && target_is_64_bit */ {
Colin Cross28d76592015-03-26 16:14:04 -0700519 a.extendProperties(ctx, "target", "android64", generalPropsValue,
Colin Crossf8209412015-03-26 14:44:26 -0700520 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android64").Elem().Elem())
521 } else {
Colin Cross28d76592015-03-26 16:14:04 -0700522 a.extendProperties(ctx, "target", "android32", generalPropsValue,
Colin Crossf8209412015-03-26 14:44:26 -0700523 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android32").Elem().Elem())
524 }
525 }
Colin Crossb05bff22015-04-30 15:08:04 -0700526
527 // Handle device architecture properties in the form:
528 // target {
529 // android_arm {
530 // key: value,
531 // },
532 // android_x86 {
533 // key: value,
534 // },
535 // },
536 if hod.Device() {
537 t := arch.ArchType
538 a.extendProperties(ctx, "target", "android_"+t.Name, generalPropsValue,
539 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android_"+t.Name).Elem().Elem())
540 }
541
Colin Cross3f40fa42015-01-30 17:27:36 -0800542 if ctx.Failed() {
543 return
544 }
545 }
546}
547
548func forEachInterface(v reflect.Value, f func(reflect.Value)) {
549 switch v.Kind() {
550 case reflect.Interface:
551 f(v)
552 case reflect.Struct:
553 for i := 0; i < v.NumField(); i++ {
554 forEachInterface(v.Field(i), f)
555 }
556 case reflect.Ptr:
557 forEachInterface(v.Elem(), f)
558 default:
559 panic(fmt.Errorf("Unsupported kind %s", v.Kind()))
560 }
561}
562
563// TODO: move this to proptools
Colin Cross28d76592015-03-26 16:14:04 -0700564func (a *AndroidModuleBase) extendProperties(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
Colin Cross3f40fa42015-01-30 17:27:36 -0800565 dstValue, srcValue reflect.Value) {
Colin Cross28d76592015-03-26 16:14:04 -0700566 a.extendPropertiesRecursive(ctx, variationType, variationName, dstValue, srcValue, "")
Colin Cross3f40fa42015-01-30 17:27:36 -0800567}
568
Colin Cross28d76592015-03-26 16:14:04 -0700569func (a *AndroidModuleBase) extendPropertiesRecursive(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
Colin Cross3f40fa42015-01-30 17:27:36 -0800570 dstValue, srcValue reflect.Value, recursePrefix string) {
571
572 typ := dstValue.Type()
573 if srcValue.Type() != typ {
574 panic(fmt.Errorf("can't extend mismatching types (%s <- %s)",
575 dstValue.Kind(), srcValue.Kind()))
576 }
577
578 for i := 0; i < srcValue.NumField(); i++ {
579 field := typ.Field(i)
580 if field.PkgPath != "" {
581 // The field is not exported so just skip it.
582 continue
583 }
584
585 srcFieldValue := srcValue.Field(i)
586 dstFieldValue := dstValue.Field(i)
587
588 localPropertyName := proptools.PropertyNameForField(field.Name)
589 propertyName := fmt.Sprintf("%s.%s.%s%s", variationType, variationName,
590 recursePrefix, localPropertyName)
591 propertyPresentInVariation := ctx.ContainsProperty(propertyName)
592
593 if !propertyPresentInVariation {
594 continue
595 }
596
597 tag := field.Tag.Get("android")
598 tags := map[string]bool{}
599 for _, entry := range strings.Split(tag, ",") {
600 if entry != "" {
601 tags[entry] = true
602 }
603 }
604
605 if !tags["arch_variant"] {
606 ctx.PropertyErrorf(propertyName, "property %q can't be specific to a build variant",
607 recursePrefix+proptools.PropertyNameForField(field.Name))
608 continue
609 }
610
Colin Cross28d76592015-03-26 16:14:04 -0700611 if !ctx.ContainsProperty(propertyName) {
612 continue
613 }
614 a.extendedProperties[localPropertyName] = struct{}{}
615
Colin Cross3f40fa42015-01-30 17:27:36 -0800616 switch srcFieldValue.Kind() {
617 case reflect.Bool:
618 // Replace the original value.
619 dstFieldValue.Set(srcFieldValue)
620 case reflect.String:
621 // Append the extension string.
622 dstFieldValue.SetString(dstFieldValue.String() +
623 srcFieldValue.String())
624 case reflect.Struct:
625 // Recursively extend the struct's fields.
626 newRecursePrefix := fmt.Sprintf("%s%s.", recursePrefix, strings.ToLower(field.Name))
Colin Cross28d76592015-03-26 16:14:04 -0700627 a.extendPropertiesRecursive(ctx, variationType, variationName,
Colin Cross3f40fa42015-01-30 17:27:36 -0800628 dstFieldValue, srcFieldValue,
629 newRecursePrefix)
630 case reflect.Slice:
631 val, err := archCombineSlices(dstFieldValue, srcFieldValue, tags["arch_subtract"])
632 if err != nil {
633 ctx.PropertyErrorf(propertyName, err.Error())
634 continue
635 }
636 dstFieldValue.Set(val)
637 case reflect.Ptr, reflect.Interface:
638 // Recursively extend the pointed-to struct's fields.
639 if dstFieldValue.IsNil() != srcFieldValue.IsNil() {
640 panic(fmt.Errorf("can't extend field %q: nilitude mismatch"))
641 }
642 if dstFieldValue.Type() != srcFieldValue.Type() {
643 panic(fmt.Errorf("can't extend field %q: type mismatch"))
644 }
645 if !dstFieldValue.IsNil() {
646 newRecursePrefix := fmt.Sprintf("%s.%s", recursePrefix, field.Name)
Colin Cross28d76592015-03-26 16:14:04 -0700647 a.extendPropertiesRecursive(ctx, variationType, variationName,
Colin Cross3f40fa42015-01-30 17:27:36 -0800648 dstFieldValue.Elem(), srcFieldValue.Elem(),
649 newRecursePrefix)
650 }
651 default:
652 panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
653 field.Name, srcFieldValue.Kind()))
654 }
655 }
656}
657
658func archCombineSlices(general, arch reflect.Value, canSubtract bool) (reflect.Value, error) {
659 if !canSubtract {
660 // Append the extension slice.
661 return reflect.AppendSlice(general, arch), nil
662 }
663
664 // Support -val in arch list to subtract a value from original list
665 l := general.Interface().([]string)
666 for archIndex := 0; archIndex < arch.Len(); archIndex++ {
667 archString := arch.Index(archIndex).String()
668 if strings.HasPrefix(archString, "-") {
669 generalIndex := findStringInSlice(archString[1:], l)
670 if generalIndex == -1 {
671 return reflect.Value{},
672 fmt.Errorf("can't find %q to subtract from general properties", archString[1:])
673 }
674 l = append(l[:generalIndex], l[generalIndex+1:]...)
675 } else {
676 l = append(l, archString)
677 }
678 }
679
680 return reflect.ValueOf(l), nil
681}
682
683func findStringInSlice(str string, slice []string) int {
684 for i, s := range slice {
685 if s == str {
686 return i
687 }
688 }
689
690 return -1
691}