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