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