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