blob: d4c17cf05828ad080a43ad7996543938d98b69f8 [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 {
109 Host interface{}
110 Android interface{}
Colin Crossf8209412015-03-26 14:44:26 -0700111 Android64 interface{}
112 Android32 interface{}
Colin Cross3f40fa42015-01-30 17:27:36 -0800113 Linux interface{}
114 Darwin interface{}
115 Windows interface{}
116 Not_windows interface{}
117 }
118}
119
120// An Arch indicates a single CPU architecture.
121type Arch struct {
122 HostOrDevice HostOrDevice
123 ArchType ArchType
124 ArchVariant string
125 CpuVariant string
Dan Albertbe961682015-03-18 23:38:50 -0700126 Abi string
Colin Cross3f40fa42015-01-30 17:27:36 -0800127}
128
129func (a Arch) String() string {
130 s := a.HostOrDevice.String() + "_" + a.ArchType.String()
131 if a.ArchVariant != "" {
132 s += "_" + a.ArchVariant
133 }
134 if a.CpuVariant != "" {
135 s += "_" + a.CpuVariant
136 }
137 return s
138}
139
140type ArchType struct {
141 Name string
142 Field string
143 Multilib string
144 MultilibField string
145}
146
147func newArch32(field string) ArchType {
148 return ArchType{
149 Name: strings.ToLower(field),
150 Field: field,
151 Multilib: "lib32",
152 MultilibField: "Lib32",
153 }
154}
155
156func newArch64(field string) ArchType {
157 return ArchType{
158 Name: strings.ToLower(field),
159 Field: field,
160 Multilib: "lib64",
161 MultilibField: "Lib64",
162 }
163}
164
165func (a ArchType) String() string {
166 return a.Name
167}
168
169type HostOrDeviceSupported int
170
171const (
172 _ HostOrDeviceSupported = iota
173 HostSupported
174 DeviceSupported
175 HostAndDeviceSupported
176)
177
178type HostOrDevice int
179
180const (
181 _ HostOrDevice = iota
182 Host
183 Device
184)
185
186func (hod HostOrDevice) String() string {
187 switch hod {
188 case Device:
189 return "device"
190 case Host:
191 return "host"
192 default:
193 panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
194 }
195}
196
197func (hod HostOrDevice) FieldLower() string {
198 switch hod {
199 case Device:
200 return "android"
201 case Host:
202 return "host"
203 default:
204 panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
205 }
206}
207
208func (hod HostOrDevice) Field() string {
209 switch hod {
210 case Device:
211 return "Android"
212 case Host:
213 return "Host"
214 default:
215 panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
216 }
217}
218
219func (hod HostOrDevice) Host() bool {
220 if hod == 0 {
221 panic("HostOrDevice unset")
222 }
223 return hod == Host
224}
225
226func (hod HostOrDevice) Device() bool {
227 if hod == 0 {
228 panic("HostOrDevice unset")
229 }
230 return hod == Device
231}
232
233var hostOrDeviceName = map[HostOrDevice]string{
234 Device: "device",
235 Host: "host",
236}
237
238var (
239 armArch = Arch{
240 HostOrDevice: Device,
241 ArchType: Arm,
242 ArchVariant: "armv7-a-neon",
243 CpuVariant: "cortex-a15",
Dan Albertbe961682015-03-18 23:38:50 -0700244 Abi: "armeabi-v7a",
Colin Cross3f40fa42015-01-30 17:27:36 -0800245 }
246 arm64Arch = Arch{
247 HostOrDevice: Device,
248 ArchType: Arm64,
249 ArchVariant: "armv8-a",
250 CpuVariant: "denver",
Dan Albertbe961682015-03-18 23:38:50 -0700251 Abi: "arm64-v8a",
Colin Cross3f40fa42015-01-30 17:27:36 -0800252 }
253 hostArch = Arch{
254 HostOrDevice: Host,
255 ArchType: X86,
256 }
257 host64Arch = Arch{
258 HostOrDevice: Host,
259 ArchType: X86_64,
260 }
Colin Cross2fe66872015-03-30 17:20:39 -0700261 commonDevice = Arch{
262 HostOrDevice: Device,
263 ArchType: Common,
264 }
265 commonHost = Arch{
266 HostOrDevice: Host,
267 ArchType: Common,
268 }
Colin Cross3f40fa42015-01-30 17:27:36 -0800269)
270
271func ArchMutator(mctx blueprint.EarlyMutatorContext) {
272 var module AndroidModule
273 var ok bool
274 if module, ok = mctx.Module().(AndroidModule); !ok {
275 return
276 }
277
278 // TODO: this is all hardcoded for arm64 primary, arm secondary for now
279 // Replace with a configuration file written by lunch or bootstrap
280
281 arches := []Arch{}
282
283 if module.base().HostSupported() {
Colin Cross2fe66872015-03-30 17:20:39 -0700284 switch module.base().commonProperties.Compile_multilib {
285 case "common":
286 arches = append(arches, commonHost)
287 default:
288 arches = append(arches, host64Arch)
289 }
Colin Cross3f40fa42015-01-30 17:27:36 -0800290 }
291
292 if module.base().DeviceSupported() {
293 switch module.base().commonProperties.Compile_multilib {
Colin Cross2fe66872015-03-30 17:20:39 -0700294 case "common":
295 arches = append(arches, commonDevice)
Colin Cross3f40fa42015-01-30 17:27:36 -0800296 case "both":
297 arches = append(arches, arm64Arch, armArch)
298 case "first", "64":
299 arches = append(arches, arm64Arch)
300 case "32":
301 arches = append(arches, armArch)
302 default:
303 mctx.ModuleErrorf(`compile_multilib must be "both", "first", "32", or "64", found %q`,
304 module.base().commonProperties.Compile_multilib)
305 }
306 }
307
Colin Cross5049f022015-03-18 13:28:46 -0700308 if len(arches) == 0 {
309 return
310 }
311
Colin Cross3f40fa42015-01-30 17:27:36 -0800312 archNames := []string{}
313 for _, arch := range arches {
314 archNames = append(archNames, arch.String())
315 }
316
317 modules := mctx.CreateVariations(archNames...)
318
319 for i, m := range modules {
320 m.(AndroidModule).base().SetArch(arches[i])
321 m.(AndroidModule).base().setArchProperties(mctx, arches[i])
322 }
323}
324
Colin Crossc472d572015-03-17 15:06:21 -0700325func InitArchModule(m AndroidModule, defaultMultilib Multilib,
Colin Cross3f40fa42015-01-30 17:27:36 -0800326 propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
327
328 base := m.base()
329
Colin Crossc472d572015-03-17 15:06:21 -0700330 base.commonProperties.Compile_multilib = string(defaultMultilib)
Colin Cross3f40fa42015-01-30 17:27:36 -0800331
332 base.generalProperties = append(base.generalProperties,
Colin Cross3f40fa42015-01-30 17:27:36 -0800333 propertyStructs...)
334
335 for _, properties := range base.generalProperties {
336 propertiesValue := reflect.ValueOf(properties)
337 if propertiesValue.Kind() != reflect.Ptr {
338 panic("properties must be a pointer to a struct")
339 }
340
341 propertiesValue = propertiesValue.Elem()
342 if propertiesValue.Kind() != reflect.Struct {
343 panic("properties must be a pointer to a struct")
344 }
345
346 archProperties := &archProperties{}
347 forEachInterface(reflect.ValueOf(archProperties), func(v reflect.Value) {
348 newValue := proptools.CloneProperties(propertiesValue)
349 proptools.ZeroProperties(newValue.Elem())
350 v.Set(newValue)
351 })
352
353 base.archProperties = append(base.archProperties, archProperties)
354 }
355
356 var allProperties []interface{}
357 allProperties = append(allProperties, base.generalProperties...)
358 for _, asp := range base.archProperties {
359 allProperties = append(allProperties, asp)
360 }
361
362 return m, allProperties
363}
364
365// Rewrite the module's properties structs to contain arch-specific values.
366func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext, arch Arch) {
Colin Cross2fe66872015-03-30 17:20:39 -0700367 if arch.ArchType == Common {
368 return
369 }
370
Colin Cross3f40fa42015-01-30 17:27:36 -0800371 for i := range a.generalProperties {
372 generalPropsValue := reflect.ValueOf(a.generalProperties[i]).Elem()
373
374 // Handle arch-specific properties in the form:
375 // arch {
376 // arm64 {
377 // key: value,
378 // },
379 // },
380 t := arch.ArchType
Colin Cross28d76592015-03-26 16:14:04 -0700381 a.extendProperties(ctx, "arch", t.Name, generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800382 reflect.ValueOf(a.archProperties[i].Arch).FieldByName(t.Field).Elem().Elem())
383
384 // Handle multilib-specific properties in the form:
385 // multilib {
386 // lib32 {
387 // key: value,
388 // },
389 // },
Colin Cross28d76592015-03-26 16:14:04 -0700390 a.extendProperties(ctx, "multilib", t.Multilib, generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800391 reflect.ValueOf(a.archProperties[i].Multilib).FieldByName(t.MultilibField).Elem().Elem())
392
393 // Handle host-or-device-specific properties in the form:
394 // target {
395 // host {
396 // key: value,
397 // },
398 // },
399 hod := arch.HostOrDevice
Colin Cross28d76592015-03-26 16:14:04 -0700400 a.extendProperties(ctx, "target", hod.FieldLower(), generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800401 reflect.ValueOf(a.archProperties[i].Target).FieldByName(hod.Field()).Elem().Elem())
402
403 // Handle host target properties in the form:
404 // target {
405 // linux {
406 // key: value,
407 // },
408 // not_windows {
409 // key: value,
410 // },
411 // },
412 var osList = []struct {
413 goos string
414 field string
415 }{
416 {"darwin", "Darwin"},
417 {"linux", "Linux"},
418 {"windows", "Windows"},
419 }
420
421 if hod.Host() {
422 for _, v := range osList {
423 if v.goos == runtime.GOOS {
Colin Cross28d76592015-03-26 16:14:04 -0700424 a.extendProperties(ctx, "target", v.goos, generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800425 reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field).Elem().Elem())
426 }
427 }
Colin Cross28d76592015-03-26 16:14:04 -0700428 a.extendProperties(ctx, "target", "not_windows", generalPropsValue,
Colin Cross3f40fa42015-01-30 17:27:36 -0800429 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Not_windows").Elem().Elem())
430 }
431
Colin Crossf8209412015-03-26 14:44:26 -0700432 // Handle 64-bit device properties in the form:
433 // target {
434 // android64 {
435 // key: value,
436 // },
437 // android32 {
438 // key: value,
439 // },
440 // },
441 // WARNING: this is probably not what you want to use in your blueprints file, it selects
442 // options for all targets on a device that supports 64-bit binaries, not just the targets
443 // that are being compiled for 64-bit. Its expected use case is binaries like linker and
444 // debuggerd that need to know when they are a 32-bit process running on a 64-bit device
445 if hod.Device() {
446 if true /* && target_is_64_bit */ {
Colin Cross28d76592015-03-26 16:14:04 -0700447 a.extendProperties(ctx, "target", "android64", generalPropsValue,
Colin Crossf8209412015-03-26 14:44:26 -0700448 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android64").Elem().Elem())
449 } else {
Colin Cross28d76592015-03-26 16:14:04 -0700450 a.extendProperties(ctx, "target", "android32", generalPropsValue,
Colin Crossf8209412015-03-26 14:44:26 -0700451 reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android32").Elem().Elem())
452 }
453 }
Colin Cross3f40fa42015-01-30 17:27:36 -0800454 if ctx.Failed() {
455 return
456 }
457 }
458}
459
460func forEachInterface(v reflect.Value, f func(reflect.Value)) {
461 switch v.Kind() {
462 case reflect.Interface:
463 f(v)
464 case reflect.Struct:
465 for i := 0; i < v.NumField(); i++ {
466 forEachInterface(v.Field(i), f)
467 }
468 case reflect.Ptr:
469 forEachInterface(v.Elem(), f)
470 default:
471 panic(fmt.Errorf("Unsupported kind %s", v.Kind()))
472 }
473}
474
475// TODO: move this to proptools
Colin Cross28d76592015-03-26 16:14:04 -0700476func (a *AndroidModuleBase) extendProperties(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
Colin Cross3f40fa42015-01-30 17:27:36 -0800477 dstValue, srcValue reflect.Value) {
Colin Cross28d76592015-03-26 16:14:04 -0700478 a.extendPropertiesRecursive(ctx, variationType, variationName, dstValue, srcValue, "")
Colin Cross3f40fa42015-01-30 17:27:36 -0800479}
480
Colin Cross28d76592015-03-26 16:14:04 -0700481func (a *AndroidModuleBase) extendPropertiesRecursive(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
Colin Cross3f40fa42015-01-30 17:27:36 -0800482 dstValue, srcValue reflect.Value, recursePrefix string) {
483
484 typ := dstValue.Type()
485 if srcValue.Type() != typ {
486 panic(fmt.Errorf("can't extend mismatching types (%s <- %s)",
487 dstValue.Kind(), srcValue.Kind()))
488 }
489
490 for i := 0; i < srcValue.NumField(); i++ {
491 field := typ.Field(i)
492 if field.PkgPath != "" {
493 // The field is not exported so just skip it.
494 continue
495 }
496
497 srcFieldValue := srcValue.Field(i)
498 dstFieldValue := dstValue.Field(i)
499
500 localPropertyName := proptools.PropertyNameForField(field.Name)
501 propertyName := fmt.Sprintf("%s.%s.%s%s", variationType, variationName,
502 recursePrefix, localPropertyName)
503 propertyPresentInVariation := ctx.ContainsProperty(propertyName)
504
505 if !propertyPresentInVariation {
506 continue
507 }
508
509 tag := field.Tag.Get("android")
510 tags := map[string]bool{}
511 for _, entry := range strings.Split(tag, ",") {
512 if entry != "" {
513 tags[entry] = true
514 }
515 }
516
517 if !tags["arch_variant"] {
518 ctx.PropertyErrorf(propertyName, "property %q can't be specific to a build variant",
519 recursePrefix+proptools.PropertyNameForField(field.Name))
520 continue
521 }
522
Colin Cross28d76592015-03-26 16:14:04 -0700523 if !ctx.ContainsProperty(propertyName) {
524 continue
525 }
526 a.extendedProperties[localPropertyName] = struct{}{}
527
Colin Cross3f40fa42015-01-30 17:27:36 -0800528 switch srcFieldValue.Kind() {
529 case reflect.Bool:
530 // Replace the original value.
531 dstFieldValue.Set(srcFieldValue)
532 case reflect.String:
533 // Append the extension string.
534 dstFieldValue.SetString(dstFieldValue.String() +
535 srcFieldValue.String())
536 case reflect.Struct:
537 // Recursively extend the struct's fields.
538 newRecursePrefix := fmt.Sprintf("%s%s.", recursePrefix, strings.ToLower(field.Name))
Colin Cross28d76592015-03-26 16:14:04 -0700539 a.extendPropertiesRecursive(ctx, variationType, variationName,
Colin Cross3f40fa42015-01-30 17:27:36 -0800540 dstFieldValue, srcFieldValue,
541 newRecursePrefix)
542 case reflect.Slice:
543 val, err := archCombineSlices(dstFieldValue, srcFieldValue, tags["arch_subtract"])
544 if err != nil {
545 ctx.PropertyErrorf(propertyName, err.Error())
546 continue
547 }
548 dstFieldValue.Set(val)
549 case reflect.Ptr, reflect.Interface:
550 // Recursively extend the pointed-to struct's fields.
551 if dstFieldValue.IsNil() != srcFieldValue.IsNil() {
552 panic(fmt.Errorf("can't extend field %q: nilitude mismatch"))
553 }
554 if dstFieldValue.Type() != srcFieldValue.Type() {
555 panic(fmt.Errorf("can't extend field %q: type mismatch"))
556 }
557 if !dstFieldValue.IsNil() {
558 newRecursePrefix := fmt.Sprintf("%s.%s", recursePrefix, field.Name)
Colin Cross28d76592015-03-26 16:14:04 -0700559 a.extendPropertiesRecursive(ctx, variationType, variationName,
Colin Cross3f40fa42015-01-30 17:27:36 -0800560 dstFieldValue.Elem(), srcFieldValue.Elem(),
561 newRecursePrefix)
562 }
563 default:
564 panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
565 field.Name, srcFieldValue.Kind()))
566 }
567 }
568}
569
570func archCombineSlices(general, arch reflect.Value, canSubtract bool) (reflect.Value, error) {
571 if !canSubtract {
572 // Append the extension slice.
573 return reflect.AppendSlice(general, arch), nil
574 }
575
576 // Support -val in arch list to subtract a value from original list
577 l := general.Interface().([]string)
578 for archIndex := 0; archIndex < arch.Len(); archIndex++ {
579 archString := arch.Index(archIndex).String()
580 if strings.HasPrefix(archString, "-") {
581 generalIndex := findStringInSlice(archString[1:], l)
582 if generalIndex == -1 {
583 return reflect.Value{},
584 fmt.Errorf("can't find %q to subtract from general properties", archString[1:])
585 }
586 l = append(l[:generalIndex], l[generalIndex+1:]...)
587 } else {
588 l = append(l, archString)
589 }
590 }
591
592 return reflect.ValueOf(l), nil
593}
594
595func findStringInSlice(str string, slice []string) int {
596 for i, s := range slice {
597 if s == str {
598 return i
599 }
600 }
601
602 return -1
603}