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