blob: 38613a06f00a5d68d90e25fc4bce2211bb5ace2c [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*/
95type archProperties struct {
96 Arch struct {
97 Arm interface{}
98 Arm64 interface{}
99 Mips interface{}
100 Mips64 interface{}
101 X86 interface{}
102 X86_64 interface{}
103 }
104 Multilib struct {
105 Lib32 interface{}
106 Lib64 interface{}
107 }
108 Target struct {
Colin Crossb05bff22015-04-30 15:08:04 -0700109 Host interface{}
110 Android interface{}
111 Android_arm interface{}
112 Android_arm64 interface{}
113 Android_mips interface{}
114 Android_mips64 interface{}
115 Android_x86 interface{}
116 Android_x86_64 interface{}
117 Android64 interface{}
118 Android32 interface{}
119 Linux interface{}
120 Linux_x86 interface{}
121 Linux_x86_64 interface{}
122 Darwin interface{}
123 Darwin_x86 interface{}
124 Darwin_x86_64 interface{}
125 Windows interface{}
126 Not_windows interface{}
Colin Cross3f40fa42015-01-30 17:27:36 -0800127 }
128}
129
130// An Arch indicates a single CPU architecture.
131type Arch struct {
132 HostOrDevice HostOrDevice
133 ArchType ArchType
134 ArchVariant string
135 CpuVariant string
Dan Albertbe961682015-03-18 23:38:50 -0700136 Abi string
Colin Cross3f40fa42015-01-30 17:27:36 -0800137}
138
139func (a Arch) String() string {
140 s := a.HostOrDevice.String() + "_" + a.ArchType.String()
141 if a.ArchVariant != "" {
142 s += "_" + a.ArchVariant
143 }
144 if a.CpuVariant != "" {
145 s += "_" + a.CpuVariant
146 }
147 return s
148}
149
150type ArchType struct {
151 Name string
152 Field string
153 Multilib string
154 MultilibField string
155}
156
157func newArch32(field string) ArchType {
158 return ArchType{
159 Name: strings.ToLower(field),
160 Field: field,
161 Multilib: "lib32",
162 MultilibField: "Lib32",
163 }
164}
165
166func newArch64(field string) ArchType {
167 return ArchType{
168 Name: strings.ToLower(field),
169 Field: field,
170 Multilib: "lib64",
171 MultilibField: "Lib64",
172 }
173}
174
175func (a ArchType) String() string {
176 return a.Name
177}
178
179type HostOrDeviceSupported int
180
181const (
182 _ HostOrDeviceSupported = iota
183 HostSupported
184 DeviceSupported
185 HostAndDeviceSupported
186)
187
188type HostOrDevice int
189
190const (
191 _ HostOrDevice = iota
192 Host
193 Device
194)
195
196func (hod HostOrDevice) String() string {
197 switch hod {
198 case Device:
199 return "device"
200 case Host:
201 return "host"
202 default:
203 panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
204 }
205}
206
207func (hod HostOrDevice) FieldLower() string {
208 switch hod {
209 case Device:
210 return "android"
211 case Host:
212 return "host"
213 default:
214 panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
215 }
216}
217
218func (hod HostOrDevice) Field() string {
219 switch hod {
220 case Device:
221 return "Android"
222 case Host:
223 return "Host"
224 default:
225 panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
226 }
227}
228
229func (hod HostOrDevice) Host() bool {
230 if hod == 0 {
231 panic("HostOrDevice unset")
232 }
233 return hod == Host
234}
235
236func (hod HostOrDevice) Device() bool {
237 if hod == 0 {
238 panic("HostOrDevice unset")
239 }
240 return hod == Device
241}
242
243var hostOrDeviceName = map[HostOrDevice]string{
244 Device: "device",
245 Host: "host",
246}
247
248var (
249 armArch = Arch{
250 HostOrDevice: Device,
251 ArchType: Arm,
252 ArchVariant: "armv7-a-neon",
253 CpuVariant: "cortex-a15",
Dan Albertbe961682015-03-18 23:38:50 -0700254 Abi: "armeabi-v7a",
Colin Cross3f40fa42015-01-30 17:27:36 -0800255 }
256 arm64Arch = Arch{
257 HostOrDevice: Device,
258 ArchType: Arm64,
259 ArchVariant: "armv8-a",
260 CpuVariant: "denver",
Dan Albertbe961682015-03-18 23:38:50 -0700261 Abi: "arm64-v8a",
Colin Cross3f40fa42015-01-30 17:27:36 -0800262 }
263 hostArch = Arch{
264 HostOrDevice: Host,
265 ArchType: X86,
266 }
267 host64Arch = Arch{
268 HostOrDevice: Host,
269 ArchType: X86_64,
270 }
Colin Cross2fe66872015-03-30 17:20:39 -0700271 commonDevice = Arch{
272 HostOrDevice: Device,
273 ArchType: Common,
274 }
275 commonHost = Arch{
276 HostOrDevice: Host,
277 ArchType: Common,
278 }
Colin Cross3f40fa42015-01-30 17:27:36 -0800279)
280
281func ArchMutator(mctx blueprint.EarlyMutatorContext) {
282 var module AndroidModule
283 var ok bool
284 if module, ok = mctx.Module().(AndroidModule); !ok {
285 return
286 }
287
288 // TODO: this is all hardcoded for arm64 primary, arm secondary for now
289 // Replace with a configuration file written by lunch or bootstrap
290
291 arches := []Arch{}
292
293 if module.base().HostSupported() {
Colin Cross2fe66872015-03-30 17:20:39 -0700294 switch module.base().commonProperties.Compile_multilib {
295 case "common":
296 arches = append(arches, commonHost)
297 default:
298 arches = append(arches, host64Arch)
299 }
Colin Cross3f40fa42015-01-30 17:27:36 -0800300 }
301
302 if module.base().DeviceSupported() {
303 switch module.base().commonProperties.Compile_multilib {
Colin Cross2fe66872015-03-30 17:20:39 -0700304 case "common":
305 arches = append(arches, commonDevice)
Colin Cross3f40fa42015-01-30 17:27:36 -0800306 case "both":
307 arches = append(arches, arm64Arch, armArch)
308 case "first", "64":
309 arches = append(arches, arm64Arch)
310 case "32":
311 arches = append(arches, armArch)
312 default:
313 mctx.ModuleErrorf(`compile_multilib must be "both", "first", "32", or "64", found %q`,
314 module.base().commonProperties.Compile_multilib)
315 }
316 }
317
Colin Cross5049f022015-03-18 13:28:46 -0700318 if len(arches) == 0 {
319 return
320 }
321
Colin Cross3f40fa42015-01-30 17:27:36 -0800322 archNames := []string{}
323 for _, arch := range arches {
324 archNames = append(archNames, arch.String())
325 }
326
327 modules := mctx.CreateVariations(archNames...)
328
329 for i, m := range modules {
330 m.(AndroidModule).base().SetArch(arches[i])
331 m.(AndroidModule).base().setArchProperties(mctx, arches[i])
332 }
333}
334
Colin Crossc472d572015-03-17 15:06:21 -0700335func InitArchModule(m AndroidModule, defaultMultilib Multilib,
Colin Cross3f40fa42015-01-30 17:27:36 -0800336 propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
337
338 base := m.base()
339
Colin Crossc472d572015-03-17 15:06:21 -0700340 base.commonProperties.Compile_multilib = string(defaultMultilib)
Colin Cross3f40fa42015-01-30 17:27:36 -0800341
342 base.generalProperties = append(base.generalProperties,
Colin Cross3f40fa42015-01-30 17:27:36 -0800343 propertyStructs...)
344
345 for _, properties := range base.generalProperties {
346 propertiesValue := reflect.ValueOf(properties)
347 if propertiesValue.Kind() != reflect.Ptr {
348 panic("properties must be a pointer to a struct")
349 }
350
351 propertiesValue = propertiesValue.Elem()
352 if propertiesValue.Kind() != reflect.Struct {
353 panic("properties must be a pointer to a struct")
354 }
355
356 archProperties := &archProperties{}
357 forEachInterface(reflect.ValueOf(archProperties), func(v reflect.Value) {
358 newValue := proptools.CloneProperties(propertiesValue)
359 proptools.ZeroProperties(newValue.Elem())
360 v.Set(newValue)
361 })
362
363 base.archProperties = append(base.archProperties, archProperties)
364 }
365
366 var allProperties []interface{}
367 allProperties = append(allProperties, base.generalProperties...)
368 for _, asp := range base.archProperties {
369 allProperties = append(allProperties, asp)
370 }
371
372 return m, allProperties
373}
374
375// Rewrite the module's properties structs to contain arch-specific values.
376func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext, arch Arch) {
Colin Cross2fe66872015-03-30 17:20:39 -0700377 if arch.ArchType == Common {
378 return
379 }
380
Colin Cross3f40fa42015-01-30 17:27:36 -0800381 for i := range a.generalProperties {
382 generalPropsValue := reflect.ValueOf(a.generalProperties[i]).Elem()
383
384 // Handle arch-specific properties in the form:
Colin Crossb05bff22015-04-30 15:08:04 -0700385 // arch: {
386 // arm64: {
Colin Cross3f40fa42015-01-30 17:27:36 -0800387 // key: value,
388 // },
389 // },
390 t := arch.ArchType
Colin Cross28d76592015-03-26 16:14:04 -0700391 a.extendProperties(ctx, "arch", t.Name, generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800392 reflect.ValueOf(a.archProperties[i].Arch).FieldByName(t.Field).Elem().Elem())
393
394 // Handle multilib-specific properties in the form:
Colin Crossb05bff22015-04-30 15:08:04 -0700395 // multilib: {
396 // lib32: {
Colin Cross3f40fa42015-01-30 17:27:36 -0800397 // key: value,
398 // },
399 // },
Colin Cross28d76592015-03-26 16:14:04 -0700400 a.extendProperties(ctx, "multilib", t.Multilib, generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800401 reflect.ValueOf(a.archProperties[i].Multilib).FieldByName(t.MultilibField).Elem().Elem())
402
403 // Handle host-or-device-specific properties in the form:
Colin Crossb05bff22015-04-30 15:08:04 -0700404 // target: {
405 // host: {
Colin Cross3f40fa42015-01-30 17:27:36 -0800406 // key: value,
407 // },
408 // },
409 hod := arch.HostOrDevice
Colin Cross28d76592015-03-26 16:14:04 -0700410 a.extendProperties(ctx, "target", hod.FieldLower(), generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800411 reflect.ValueOf(a.archProperties[i].Target).FieldByName(hod.Field()).Elem().Elem())
412
413 // Handle host target properties in the form:
Colin Crossb05bff22015-04-30 15:08:04 -0700414 // target: {
415 // linux: {
Colin Cross3f40fa42015-01-30 17:27:36 -0800416 // key: value,
417 // },
Colin Crossb05bff22015-04-30 15:08:04 -0700418 // not_windows: {
419 // key: value,
420 // },
421 // linux_x86: {
422 // key: value,
423 // },
424 // linux_arm: {
Colin Cross3f40fa42015-01-30 17:27:36 -0800425 // key: value,
426 // },
427 // },
428 var osList = []struct {
429 goos string
430 field string
431 }{
432 {"darwin", "Darwin"},
433 {"linux", "Linux"},
434 {"windows", "Windows"},
435 }
436
437 if hod.Host() {
438 for _, v := range osList {
439 if v.goos == runtime.GOOS {
Colin Cross28d76592015-03-26 16:14:04 -0700440 a.extendProperties(ctx, "target", v.goos, generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800441 reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field).Elem().Elem())
Colin Crossb05bff22015-04-30 15:08:04 -0700442 t := arch.ArchType
443 a.extendProperties(ctx, "target", v.goos+"_"+t.Name, generalPropsValue,
444 reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field+"_"+t.Name).Elem().Elem())
Colin Cross3f40fa42015-01-30 17:27:36 -0800445 }
446 }
Colin Cross28d76592015-03-26 16:14:04 -0700447 a.extendProperties(ctx, "target", "not_windows", generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800448 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Not_windows").Elem().Elem())
449 }
450
Colin Crossf8209412015-03-26 14:44:26 -0700451 // Handle 64-bit device properties in the form:
452 // target {
453 // android64 {
454 // key: value,
455 // },
456 // android32 {
457 // key: value,
458 // },
459 // },
460 // WARNING: this is probably not what you want to use in your blueprints file, it selects
461 // options for all targets on a device that supports 64-bit binaries, not just the targets
462 // that are being compiled for 64-bit. Its expected use case is binaries like linker and
463 // debuggerd that need to know when they are a 32-bit process running on a 64-bit device
464 if hod.Device() {
465 if true /* && target_is_64_bit */ {
Colin Cross28d76592015-03-26 16:14:04 -0700466 a.extendProperties(ctx, "target", "android64", generalPropsValue,
Colin Crossf8209412015-03-26 14:44:26 -0700467 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android64").Elem().Elem())
468 } else {
Colin Cross28d76592015-03-26 16:14:04 -0700469 a.extendProperties(ctx, "target", "android32", generalPropsValue,
Colin Crossf8209412015-03-26 14:44:26 -0700470 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android32").Elem().Elem())
471 }
472 }
Colin Crossb05bff22015-04-30 15:08:04 -0700473
474 // Handle device architecture properties in the form:
475 // target {
476 // android_arm {
477 // key: value,
478 // },
479 // android_x86 {
480 // key: value,
481 // },
482 // },
483 if hod.Device() {
484 t := arch.ArchType
485 a.extendProperties(ctx, "target", "android_"+t.Name, generalPropsValue,
486 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android_"+t.Name).Elem().Elem())
487 }
488
Colin Cross3f40fa42015-01-30 17:27:36 -0800489 if ctx.Failed() {
490 return
491 }
492 }
493}
494
495func forEachInterface(v reflect.Value, f func(reflect.Value)) {
496 switch v.Kind() {
497 case reflect.Interface:
498 f(v)
499 case reflect.Struct:
500 for i := 0; i < v.NumField(); i++ {
501 forEachInterface(v.Field(i), f)
502 }
503 case reflect.Ptr:
504 forEachInterface(v.Elem(), f)
505 default:
506 panic(fmt.Errorf("Unsupported kind %s", v.Kind()))
507 }
508}
509
510// TODO: move this to proptools
Colin Cross28d76592015-03-26 16:14:04 -0700511func (a *AndroidModuleBase) extendProperties(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
Colin Cross3f40fa42015-01-30 17:27:36 -0800512 dstValue, srcValue reflect.Value) {
Colin Cross28d76592015-03-26 16:14:04 -0700513 a.extendPropertiesRecursive(ctx, variationType, variationName, dstValue, srcValue, "")
Colin Cross3f40fa42015-01-30 17:27:36 -0800514}
515
Colin Cross28d76592015-03-26 16:14:04 -0700516func (a *AndroidModuleBase) extendPropertiesRecursive(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
Colin Cross3f40fa42015-01-30 17:27:36 -0800517 dstValue, srcValue reflect.Value, recursePrefix string) {
518
519 typ := dstValue.Type()
520 if srcValue.Type() != typ {
521 panic(fmt.Errorf("can't extend mismatching types (%s <- %s)",
522 dstValue.Kind(), srcValue.Kind()))
523 }
524
525 for i := 0; i < srcValue.NumField(); i++ {
526 field := typ.Field(i)
527 if field.PkgPath != "" {
528 // The field is not exported so just skip it.
529 continue
530 }
531
532 srcFieldValue := srcValue.Field(i)
533 dstFieldValue := dstValue.Field(i)
534
535 localPropertyName := proptools.PropertyNameForField(field.Name)
536 propertyName := fmt.Sprintf("%s.%s.%s%s", variationType, variationName,
537 recursePrefix, localPropertyName)
538 propertyPresentInVariation := ctx.ContainsProperty(propertyName)
539
540 if !propertyPresentInVariation {
541 continue
542 }
543
544 tag := field.Tag.Get("android")
545 tags := map[string]bool{}
546 for _, entry := range strings.Split(tag, ",") {
547 if entry != "" {
548 tags[entry] = true
549 }
550 }
551
552 if !tags["arch_variant"] {
553 ctx.PropertyErrorf(propertyName, "property %q can't be specific to a build variant",
554 recursePrefix+proptools.PropertyNameForField(field.Name))
555 continue
556 }
557
Colin Cross28d76592015-03-26 16:14:04 -0700558 if !ctx.ContainsProperty(propertyName) {
559 continue
560 }
561 a.extendedProperties[localPropertyName] = struct{}{}
562
Colin Cross3f40fa42015-01-30 17:27:36 -0800563 switch srcFieldValue.Kind() {
564 case reflect.Bool:
565 // Replace the original value.
566 dstFieldValue.Set(srcFieldValue)
567 case reflect.String:
568 // Append the extension string.
569 dstFieldValue.SetString(dstFieldValue.String() +
570 srcFieldValue.String())
571 case reflect.Struct:
572 // Recursively extend the struct's fields.
573 newRecursePrefix := fmt.Sprintf("%s%s.", recursePrefix, strings.ToLower(field.Name))
Colin Cross28d76592015-03-26 16:14:04 -0700574 a.extendPropertiesRecursive(ctx, variationType, variationName,
Colin Cross3f40fa42015-01-30 17:27:36 -0800575 dstFieldValue, srcFieldValue,
576 newRecursePrefix)
577 case reflect.Slice:
578 val, err := archCombineSlices(dstFieldValue, srcFieldValue, tags["arch_subtract"])
579 if err != nil {
580 ctx.PropertyErrorf(propertyName, err.Error())
581 continue
582 }
583 dstFieldValue.Set(val)
584 case reflect.Ptr, reflect.Interface:
585 // Recursively extend the pointed-to struct's fields.
586 if dstFieldValue.IsNil() != srcFieldValue.IsNil() {
587 panic(fmt.Errorf("can't extend field %q: nilitude mismatch"))
588 }
589 if dstFieldValue.Type() != srcFieldValue.Type() {
590 panic(fmt.Errorf("can't extend field %q: type mismatch"))
591 }
592 if !dstFieldValue.IsNil() {
593 newRecursePrefix := fmt.Sprintf("%s.%s", recursePrefix, field.Name)
Colin Cross28d76592015-03-26 16:14:04 -0700594 a.extendPropertiesRecursive(ctx, variationType, variationName,
Colin Cross3f40fa42015-01-30 17:27:36 -0800595 dstFieldValue.Elem(), srcFieldValue.Elem(),
596 newRecursePrefix)
597 }
598 default:
599 panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
600 field.Name, srcFieldValue.Kind()))
601 }
602 }
603}
604
605func archCombineSlices(general, arch reflect.Value, canSubtract bool) (reflect.Value, error) {
606 if !canSubtract {
607 // Append the extension slice.
608 return reflect.AppendSlice(general, arch), nil
609 }
610
611 // Support -val in arch list to subtract a value from original list
612 l := general.Interface().([]string)
613 for archIndex := 0; archIndex < arch.Len(); archIndex++ {
614 archString := arch.Index(archIndex).String()
615 if strings.HasPrefix(archString, "-") {
616 generalIndex := findStringInSlice(archString[1:], l)
617 if generalIndex == -1 {
618 return reflect.Value{},
619 fmt.Errorf("can't find %q to subtract from general properties", archString[1:])
620 }
621 l = append(l[:generalIndex], l[generalIndex+1:]...)
622 } else {
623 l = append(l, archString)
624 }
625 }
626
627 return reflect.ValueOf(l), nil
628}
629
630func findStringInSlice(str string, slice []string) int {
631 for i, s := range slice {
632 if s == str {
633 return i
634 }
635 }
636
637 return -1
638}