ffce3fcf33
Change-Id: I17405452a141d9ce15e4093f6610e8a9eceb98a9
688 lines
20 KiB
Go
688 lines
20 KiB
Go
// Copyright 2015 Google Inc. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package common
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/google/blueprint"
|
|
"github.com/google/blueprint/proptools"
|
|
)
|
|
|
|
var (
|
|
Arm = newArch("arm", "lib32")
|
|
Arm64 = newArch("arm64", "lib64")
|
|
Mips = newArch("mips", "lib32")
|
|
Mips64 = newArch("mips64", "lib64")
|
|
X86 = newArch("x86", "lib32")
|
|
X86_64 = newArch("x86_64", "lib64")
|
|
|
|
Common = ArchType{
|
|
Name: "common",
|
|
}
|
|
)
|
|
|
|
/*
|
|
Example blueprints file containing all variant property groups, with comment listing what type
|
|
of variants get properties in that group:
|
|
|
|
module {
|
|
arch: {
|
|
arm: {
|
|
// Host or device variants with arm architecture
|
|
},
|
|
arm64: {
|
|
// Host or device variants with arm64 architecture
|
|
},
|
|
mips: {
|
|
// Host or device variants with mips architecture
|
|
},
|
|
mips64: {
|
|
// Host or device variants with mips64 architecture
|
|
},
|
|
x86: {
|
|
// Host or device variants with x86 architecture
|
|
},
|
|
x86_64: {
|
|
// Host or device variants with x86_64 architecture
|
|
},
|
|
},
|
|
multilib: {
|
|
lib32: {
|
|
// Host or device variants for 32-bit architectures
|
|
},
|
|
lib64: {
|
|
// Host or device variants for 64-bit architectures
|
|
},
|
|
},
|
|
target: {
|
|
android: {
|
|
// Device variants
|
|
},
|
|
host: {
|
|
// Host variants
|
|
},
|
|
linux: {
|
|
// Linux host variants
|
|
},
|
|
darwin: {
|
|
// Darwin host variants
|
|
},
|
|
windows: {
|
|
// Windows host variants
|
|
},
|
|
not_windows: {
|
|
// Non-windows host variants
|
|
},
|
|
},
|
|
}
|
|
*/
|
|
|
|
type archProperties struct {
|
|
// Properties to vary by target architecture
|
|
Arch struct {
|
|
// Properties for module variants being built to run on arm (host or device)
|
|
Arm interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on arm64 (host or device)
|
|
Arm64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on mips (host or device)
|
|
Mips interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on mips64 (host or device)
|
|
Mips64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on x86 (host or device)
|
|
X86 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on x86_64 (host or device)
|
|
X86_64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
|
|
// Arm arch variants
|
|
Armv5te interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
Armv7_a interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
Armv7_a_neon interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
|
|
// Arm cpu variants
|
|
Cortex_a7 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
Cortex_a8 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
Cortex_a9 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
Cortex_a15 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
Krait interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
Denver interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
|
|
// Arm64 cpu variants
|
|
Denver64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
|
|
// Mips arch variants
|
|
Mips_rev6 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
|
|
// X86 cpu variants
|
|
Atom interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
Silvermont interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
}
|
|
|
|
// Properties to vary by 32-bit or 64-bit
|
|
Multilib struct {
|
|
// Properties for module variants being built to run on 32-bit devices
|
|
Lib32 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on 64-bit devices
|
|
Lib64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
}
|
|
// Properties to vary by build target (host or device, os, os+archictecture)
|
|
Target struct {
|
|
// Properties for module variants being built to run on the host
|
|
Host interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on the device
|
|
Android interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on arm devices
|
|
Android_arm interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on arm64 devices
|
|
Android_arm64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on mips devices
|
|
Android_mips interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on mips64 devices
|
|
Android_mips64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on x86 devices
|
|
Android_x86 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on x86_64 devices
|
|
Android_x86_64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on devices that support 64-bit
|
|
Android64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on devices that do not support 64-bit
|
|
Android32 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on linux hosts
|
|
Linux interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on linux x86 hosts
|
|
Linux_x86 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on linux x86_64 hosts
|
|
Linux_x86_64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on darwin hosts
|
|
Darwin interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on darwin x86 hosts
|
|
Darwin_x86 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on darwin x86_64 hosts
|
|
Darwin_x86_64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on windows hosts
|
|
Windows interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
// Properties for module variants being built to run on linux or darwin hosts
|
|
Not_windows interface{} `blueprint:"filter(android:\"arch_variant\")"`
|
|
}
|
|
}
|
|
|
|
// An Arch indicates a single CPU architecture.
|
|
type Arch struct {
|
|
ArchType ArchType
|
|
ArchVariant string
|
|
CpuVariant string
|
|
Abi string
|
|
}
|
|
|
|
func (a Arch) String() string {
|
|
s := a.ArchType.String()
|
|
if a.ArchVariant != "" {
|
|
s += "_" + a.ArchVariant
|
|
}
|
|
if a.CpuVariant != "" {
|
|
s += "_" + a.CpuVariant
|
|
}
|
|
return s
|
|
}
|
|
|
|
type ArchType struct {
|
|
Name string
|
|
Multilib string
|
|
}
|
|
|
|
func newArch(name, multilib string) ArchType {
|
|
return ArchType{
|
|
Name: name,
|
|
Multilib: multilib,
|
|
}
|
|
}
|
|
|
|
func (a ArchType) String() string {
|
|
return a.Name
|
|
}
|
|
|
|
type HostOrDeviceSupported int
|
|
|
|
const (
|
|
_ HostOrDeviceSupported = iota
|
|
HostSupported
|
|
DeviceSupported
|
|
HostAndDeviceSupported
|
|
)
|
|
|
|
type HostOrDevice int
|
|
|
|
const (
|
|
_ HostOrDevice = iota
|
|
Host
|
|
Device
|
|
)
|
|
|
|
func (hod HostOrDevice) String() string {
|
|
switch hod {
|
|
case Device:
|
|
return "device"
|
|
case Host:
|
|
return "host"
|
|
default:
|
|
panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
|
|
}
|
|
}
|
|
|
|
func (hod HostOrDevice) Property() string {
|
|
switch hod {
|
|
case Device:
|
|
return "android"
|
|
case Host:
|
|
return "host"
|
|
default:
|
|
panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
|
|
}
|
|
}
|
|
|
|
func (hod HostOrDevice) Host() bool {
|
|
if hod == 0 {
|
|
panic("HostOrDevice unset")
|
|
}
|
|
return hod == Host
|
|
}
|
|
|
|
func (hod HostOrDevice) Device() bool {
|
|
if hod == 0 {
|
|
panic("HostOrDevice unset")
|
|
}
|
|
return hod == Device
|
|
}
|
|
|
|
var hostOrDeviceName = map[HostOrDevice]string{
|
|
Device: "device",
|
|
Host: "host",
|
|
}
|
|
|
|
var (
|
|
armArch = Arch{
|
|
ArchType: Arm,
|
|
ArchVariant: "armv7-a-neon",
|
|
CpuVariant: "cortex-a15",
|
|
Abi: "armeabi-v7a",
|
|
}
|
|
arm64Arch = Arch{
|
|
ArchType: Arm64,
|
|
CpuVariant: "denver64",
|
|
Abi: "arm64-v8a",
|
|
}
|
|
x86Arch = Arch{
|
|
ArchType: X86,
|
|
}
|
|
x8664Arch = Arch{
|
|
ArchType: X86_64,
|
|
}
|
|
commonArch = Arch{
|
|
ArchType: Common,
|
|
}
|
|
)
|
|
|
|
func HostOrDeviceMutator(mctx blueprint.EarlyMutatorContext) {
|
|
var module AndroidModule
|
|
var ok bool
|
|
if module, ok = mctx.Module().(AndroidModule); !ok {
|
|
return
|
|
}
|
|
|
|
hods := []HostOrDevice{}
|
|
|
|
if module.base().HostSupported() {
|
|
hods = append(hods, Host)
|
|
}
|
|
|
|
if module.base().DeviceSupported() {
|
|
hods = append(hods, Device)
|
|
}
|
|
|
|
if len(hods) == 0 {
|
|
return
|
|
}
|
|
|
|
hodNames := []string{}
|
|
for _, hod := range hods {
|
|
hodNames = append(hodNames, hod.String())
|
|
}
|
|
|
|
modules := mctx.CreateVariations(hodNames...)
|
|
for i, m := range modules {
|
|
m.(AndroidModule).base().SetHostOrDevice(hods[i])
|
|
}
|
|
}
|
|
|
|
func ArchMutator(mctx blueprint.EarlyMutatorContext) {
|
|
var module AndroidModule
|
|
var ok bool
|
|
if module, ok = mctx.Module().(AndroidModule); !ok {
|
|
return
|
|
}
|
|
|
|
// TODO: this is all hardcoded for arm64 primary, arm secondary for now
|
|
// Replace with a configuration file written by lunch or bootstrap
|
|
|
|
arches := []Arch{}
|
|
|
|
if module.base().HostSupported() && module.base().HostOrDevice().Host() {
|
|
switch module.base().commonProperties.Compile_multilib {
|
|
case "common":
|
|
arches = append(arches, commonArch)
|
|
case "both":
|
|
arches = append(arches, x8664Arch, x86Arch)
|
|
case "first", "64":
|
|
arches = append(arches, x8664Arch)
|
|
case "32":
|
|
arches = append(arches, x86Arch)
|
|
default:
|
|
arches = append(arches, x8664Arch)
|
|
}
|
|
}
|
|
|
|
if module.base().DeviceSupported() && module.base().HostOrDevice().Device() {
|
|
switch module.base().commonProperties.Compile_multilib {
|
|
case "common":
|
|
arches = append(arches, commonArch)
|
|
case "both":
|
|
arches = append(arches, arm64Arch, armArch)
|
|
case "first", "64":
|
|
arches = append(arches, arm64Arch)
|
|
case "32":
|
|
arches = append(arches, armArch)
|
|
default:
|
|
mctx.ModuleErrorf(`compile_multilib must be "both", "first", "32", or "64", found %q`,
|
|
module.base().commonProperties.Compile_multilib)
|
|
}
|
|
}
|
|
|
|
if len(arches) == 0 {
|
|
return
|
|
}
|
|
|
|
archNames := []string{}
|
|
for _, arch := range arches {
|
|
archNames = append(archNames, arch.String())
|
|
}
|
|
|
|
modules := mctx.CreateVariations(archNames...)
|
|
|
|
for i, m := range modules {
|
|
m.(AndroidModule).base().SetArch(arches[i])
|
|
m.(AndroidModule).base().setArchProperties(mctx)
|
|
}
|
|
}
|
|
|
|
func InitArchModule(m AndroidModule, defaultMultilib Multilib,
|
|
propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
|
|
|
|
base := m.base()
|
|
|
|
base.commonProperties.Compile_multilib = string(defaultMultilib)
|
|
|
|
base.generalProperties = append(base.generalProperties,
|
|
propertyStructs...)
|
|
|
|
for _, properties := range base.generalProperties {
|
|
propertiesValue := reflect.ValueOf(properties)
|
|
if propertiesValue.Kind() != reflect.Ptr {
|
|
panic("properties must be a pointer to a struct")
|
|
}
|
|
|
|
propertiesValue = propertiesValue.Elem()
|
|
if propertiesValue.Kind() != reflect.Struct {
|
|
panic("properties must be a pointer to a struct")
|
|
}
|
|
|
|
archProperties := &archProperties{}
|
|
forEachInterface(reflect.ValueOf(archProperties), func(v reflect.Value) {
|
|
newValue := proptools.CloneEmptyProperties(propertiesValue)
|
|
v.Set(newValue)
|
|
})
|
|
|
|
base.archProperties = append(base.archProperties, archProperties)
|
|
}
|
|
|
|
var allProperties []interface{}
|
|
allProperties = append(allProperties, base.generalProperties...)
|
|
for _, asp := range base.archProperties {
|
|
allProperties = append(allProperties, asp)
|
|
}
|
|
|
|
return m, allProperties
|
|
}
|
|
|
|
var dashToUnderscoreReplacer = strings.NewReplacer("-", "_")
|
|
|
|
// Rewrite the module's properties structs to contain arch-specific values.
|
|
func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext) {
|
|
arch := a.commonProperties.CompileArch
|
|
hod := a.commonProperties.CompileHostOrDevice
|
|
|
|
if arch.ArchType == Common {
|
|
return
|
|
}
|
|
|
|
for i := range a.generalProperties {
|
|
generalPropsValue := reflect.ValueOf(a.generalProperties[i]).Elem()
|
|
|
|
// Handle arch-specific properties in the form:
|
|
// arch: {
|
|
// arm64: {
|
|
// key: value,
|
|
// },
|
|
// },
|
|
t := arch.ArchType
|
|
field := proptools.FieldNameForProperty(t.Name)
|
|
a.extendProperties(ctx, "arch", t.Name, generalPropsValue,
|
|
reflect.ValueOf(a.archProperties[i].Arch).FieldByName(field).Elem().Elem())
|
|
|
|
// Handle arch-variant-specific properties in the form:
|
|
// arch: {
|
|
// variant: {
|
|
// key: value,
|
|
// },
|
|
// },
|
|
v := dashToUnderscoreReplacer.Replace(arch.ArchVariant)
|
|
if v != "" {
|
|
field := proptools.FieldNameForProperty(v)
|
|
a.extendProperties(ctx, "arch", v, generalPropsValue,
|
|
reflect.ValueOf(a.archProperties[i].Arch).FieldByName(field).Elem().Elem())
|
|
}
|
|
|
|
// Handle cpu-variant-specific properties in the form:
|
|
// arch: {
|
|
// variant: {
|
|
// key: value,
|
|
// },
|
|
// },
|
|
c := dashToUnderscoreReplacer.Replace(arch.CpuVariant)
|
|
if c != "" {
|
|
field := proptools.FieldNameForProperty(c)
|
|
a.extendProperties(ctx, "arch", c, generalPropsValue,
|
|
reflect.ValueOf(a.archProperties[i].Arch).FieldByName(field).Elem().Elem())
|
|
}
|
|
|
|
// Handle multilib-specific properties in the form:
|
|
// multilib: {
|
|
// lib32: {
|
|
// key: value,
|
|
// },
|
|
// },
|
|
multilibField := proptools.FieldNameForProperty(t.Multilib)
|
|
a.extendProperties(ctx, "multilib", t.Multilib, generalPropsValue,
|
|
reflect.ValueOf(a.archProperties[i].Multilib).FieldByName(multilibField).Elem().Elem())
|
|
|
|
// Handle host-or-device-specific properties in the form:
|
|
// target: {
|
|
// host: {
|
|
// key: value,
|
|
// },
|
|
// },
|
|
hodProperty := hod.Property()
|
|
hodField := proptools.FieldNameForProperty(hodProperty)
|
|
a.extendProperties(ctx, "target", hodProperty, generalPropsValue,
|
|
reflect.ValueOf(a.archProperties[i].Target).FieldByName(hodField).Elem().Elem())
|
|
|
|
// Handle host target properties in the form:
|
|
// target: {
|
|
// linux: {
|
|
// key: value,
|
|
// },
|
|
// not_windows: {
|
|
// key: value,
|
|
// },
|
|
// linux_x86: {
|
|
// key: value,
|
|
// },
|
|
// linux_arm: {
|
|
// key: value,
|
|
// },
|
|
// },
|
|
var osList = []struct {
|
|
goos string
|
|
field string
|
|
}{
|
|
{"darwin", "Darwin"},
|
|
{"linux", "Linux"},
|
|
{"windows", "Windows"},
|
|
}
|
|
|
|
if hod.Host() {
|
|
for _, v := range osList {
|
|
if v.goos == runtime.GOOS {
|
|
a.extendProperties(ctx, "target", v.goos, generalPropsValue,
|
|
reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field).Elem().Elem())
|
|
t := arch.ArchType
|
|
a.extendProperties(ctx, "target", v.goos+"_"+t.Name, generalPropsValue,
|
|
reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field+"_"+t.Name).Elem().Elem())
|
|
}
|
|
}
|
|
a.extendProperties(ctx, "target", "not_windows", generalPropsValue,
|
|
reflect.ValueOf(a.archProperties[i].Target).FieldByName("Not_windows").Elem().Elem())
|
|
}
|
|
|
|
// Handle 64-bit device properties in the form:
|
|
// target {
|
|
// android64 {
|
|
// key: value,
|
|
// },
|
|
// android32 {
|
|
// key: value,
|
|
// },
|
|
// },
|
|
// WARNING: this is probably not what you want to use in your blueprints file, it selects
|
|
// options for all targets on a device that supports 64-bit binaries, not just the targets
|
|
// that are being compiled for 64-bit. Its expected use case is binaries like linker and
|
|
// debuggerd that need to know when they are a 32-bit process running on a 64-bit device
|
|
if hod.Device() {
|
|
if true /* && target_is_64_bit */ {
|
|
a.extendProperties(ctx, "target", "android64", generalPropsValue,
|
|
reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android64").Elem().Elem())
|
|
} else {
|
|
a.extendProperties(ctx, "target", "android32", generalPropsValue,
|
|
reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android32").Elem().Elem())
|
|
}
|
|
}
|
|
|
|
// Handle device architecture properties in the form:
|
|
// target {
|
|
// android_arm {
|
|
// key: value,
|
|
// },
|
|
// android_x86 {
|
|
// key: value,
|
|
// },
|
|
// },
|
|
if hod.Device() {
|
|
t := arch.ArchType
|
|
a.extendProperties(ctx, "target", "android_"+t.Name, generalPropsValue,
|
|
reflect.ValueOf(a.archProperties[i].Target).FieldByName("Android_"+t.Name).Elem().Elem())
|
|
}
|
|
|
|
if ctx.Failed() {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func forEachInterface(v reflect.Value, f func(reflect.Value)) {
|
|
switch v.Kind() {
|
|
case reflect.Interface:
|
|
f(v)
|
|
case reflect.Struct:
|
|
for i := 0; i < v.NumField(); i++ {
|
|
forEachInterface(v.Field(i), f)
|
|
}
|
|
case reflect.Ptr:
|
|
forEachInterface(v.Elem(), f)
|
|
default:
|
|
panic(fmt.Errorf("Unsupported kind %s", v.Kind()))
|
|
}
|
|
}
|
|
|
|
// TODO: move this to proptools
|
|
func (a *AndroidModuleBase) extendProperties(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
|
|
dstValue, srcValue reflect.Value) {
|
|
a.extendPropertiesRecursive(ctx, variationType, variationName, dstValue, srcValue, "")
|
|
}
|
|
|
|
func (a *AndroidModuleBase) extendPropertiesRecursive(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
|
|
dstValue, srcValue reflect.Value, recursePrefix string) {
|
|
|
|
typ := dstValue.Type()
|
|
if srcValue.Type() != typ {
|
|
panic(fmt.Errorf("can't extend mismatching types (%s <- %s)",
|
|
dstValue.Kind(), srcValue.Kind()))
|
|
}
|
|
|
|
for i := 0; i < srcValue.NumField(); i++ {
|
|
field := typ.Field(i)
|
|
if field.PkgPath != "" {
|
|
// The field is not exported so just skip it.
|
|
continue
|
|
}
|
|
|
|
srcFieldValue := srcValue.Field(i)
|
|
dstFieldValue := dstValue.Field(i)
|
|
|
|
localPropertyName := proptools.PropertyNameForField(field.Name)
|
|
propertyName := fmt.Sprintf("%s.%s.%s%s", variationType, variationName,
|
|
recursePrefix, localPropertyName)
|
|
propertyPresentInVariation := ctx.ContainsProperty(propertyName)
|
|
|
|
if !propertyPresentInVariation {
|
|
continue
|
|
}
|
|
|
|
tag := field.Tag.Get("android")
|
|
tags := map[string]bool{}
|
|
for _, entry := range strings.Split(tag, ",") {
|
|
if entry != "" {
|
|
tags[entry] = true
|
|
}
|
|
}
|
|
|
|
if !tags["arch_variant"] {
|
|
ctx.PropertyErrorf(propertyName, "property %q can't be specific to a build variant",
|
|
recursePrefix+proptools.PropertyNameForField(field.Name))
|
|
continue
|
|
}
|
|
|
|
if !ctx.ContainsProperty(propertyName) {
|
|
continue
|
|
}
|
|
a.extendedProperties[localPropertyName] = struct{}{}
|
|
|
|
switch srcFieldValue.Kind() {
|
|
case reflect.Bool:
|
|
// Replace the original value.
|
|
dstFieldValue.Set(srcFieldValue)
|
|
case reflect.String:
|
|
// Append the extension string.
|
|
dstFieldValue.SetString(dstFieldValue.String() +
|
|
srcFieldValue.String())
|
|
case reflect.Struct:
|
|
// Recursively extend the struct's fields.
|
|
newRecursePrefix := fmt.Sprintf("%s%s.", recursePrefix, strings.ToLower(field.Name))
|
|
a.extendPropertiesRecursive(ctx, variationType, variationName,
|
|
dstFieldValue, srcFieldValue,
|
|
newRecursePrefix)
|
|
case reflect.Slice:
|
|
dstFieldValue.Set(reflect.AppendSlice(dstFieldValue, srcFieldValue))
|
|
case reflect.Ptr, reflect.Interface:
|
|
// Recursively extend the pointed-to struct's fields.
|
|
if dstFieldValue.IsNil() != srcFieldValue.IsNil() {
|
|
panic(fmt.Errorf("can't extend field %q: nilitude mismatch"))
|
|
}
|
|
if dstFieldValue.Type() != srcFieldValue.Type() {
|
|
panic(fmt.Errorf("can't extend field %q: type mismatch"))
|
|
}
|
|
if !dstFieldValue.IsNil() {
|
|
newRecursePrefix := fmt.Sprintf("%s.%s", recursePrefix, field.Name)
|
|
a.extendPropertiesRecursive(ctx, variationType, variationName,
|
|
dstFieldValue.Elem(), srcFieldValue.Elem(),
|
|
newRecursePrefix)
|
|
}
|
|
default:
|
|
panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
|
|
field.Name, srcFieldValue.Kind()))
|
|
}
|
|
}
|
|
}
|