Add soong_build primary builder
Initial build logic for building android with soong. It can build
a variety of C and C++ files for arm/arm64 and host.
Change-Id: I10eb37c2c2a50be6af1bb5fd568c0962b9476bf0
diff --git a/common/arch.go b/common/arch.go
new file mode 100644
index 0000000..8daade0
--- /dev/null
+++ b/common/arch.go
@@ -0,0 +1,545 @@
+// 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 (
+ "blueprint"
+ "blueprint/proptools"
+ "fmt"
+ "reflect"
+ "runtime"
+ "strings"
+)
+
+var (
+ Arm = newArch32("Arm")
+ Arm64 = newArch64("Arm64")
+ Mips = newArch32("Mips")
+ Mips64 = newArch64("Mips64")
+ X86 = newArch32("X86")
+ X86_64 = newArch64("X86_64")
+)
+
+/*
+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 {
+ Arch struct {
+ Arm interface{}
+ Arm64 interface{}
+ Mips interface{}
+ Mips64 interface{}
+ X86 interface{}
+ X86_64 interface{}
+ }
+ Multilib struct {
+ Lib32 interface{}
+ Lib64 interface{}
+ }
+ Target struct {
+ Host interface{}
+ Android interface{}
+ Linux interface{}
+ Darwin interface{}
+ Windows interface{}
+ Not_windows interface{}
+ }
+}
+
+// An Arch indicates a single CPU architecture.
+type Arch struct {
+ HostOrDevice HostOrDevice
+ ArchType ArchType
+ ArchVariant string
+ CpuVariant string
+}
+
+func (a Arch) String() string {
+ s := a.HostOrDevice.String() + "_" + a.ArchType.String()
+ if a.ArchVariant != "" {
+ s += "_" + a.ArchVariant
+ }
+ if a.CpuVariant != "" {
+ s += "_" + a.CpuVariant
+ }
+ return s
+}
+
+type ArchType struct {
+ Name string
+ Field string
+ Multilib string
+ MultilibField string
+}
+
+func newArch32(field string) ArchType {
+ return ArchType{
+ Name: strings.ToLower(field),
+ Field: field,
+ Multilib: "lib32",
+ MultilibField: "Lib32",
+ }
+}
+
+func newArch64(field string) ArchType {
+ return ArchType{
+ Name: strings.ToLower(field),
+ Field: field,
+ Multilib: "lib64",
+ MultilibField: "Lib64",
+ }
+}
+
+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) FieldLower() string {
+ switch hod {
+ case Device:
+ return "android"
+ case Host:
+ return "host"
+ default:
+ panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
+ }
+}
+
+func (hod HostOrDevice) Field() 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{
+ HostOrDevice: Device,
+ ArchType: Arm,
+ ArchVariant: "armv7-a-neon",
+ CpuVariant: "cortex-a15",
+ }
+ arm64Arch = Arch{
+ HostOrDevice: Device,
+ ArchType: Arm64,
+ ArchVariant: "armv8-a",
+ CpuVariant: "denver",
+ }
+ hostArch = Arch{
+ HostOrDevice: Host,
+ ArchType: X86,
+ }
+ host64Arch = Arch{
+ HostOrDevice: Host,
+ ArchType: X86_64,
+ }
+)
+
+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() {
+ arches = append(arches, host64Arch)
+ }
+
+ if module.base().DeviceSupported() {
+ switch module.base().commonProperties.Compile_multilib {
+ 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)
+ }
+ }
+
+ 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, arches[i])
+ }
+}
+
+func InitArchModule(m AndroidModule, defaultMultilib string,
+ propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
+
+ base := m.base()
+
+ base.commonProperties.Compile_multilib = defaultMultilib
+
+ base.generalProperties = append(base.generalProperties,
+ &base.commonProperties)
+ 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.CloneProperties(propertiesValue)
+ proptools.ZeroProperties(newValue.Elem())
+ 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
+}
+
+// Rewrite the module's properties structs to contain arch-specific values.
+func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext, arch Arch) {
+ 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
+ extendProperties(ctx, "arch", t.Name, generalPropsValue,
+ reflect.ValueOf(a.archProperties[i].Arch).FieldByName(t.Field).Elem().Elem())
+
+ // Handle multilib-specific properties in the form:
+ // multilib {
+ // lib32 {
+ // key: value,
+ // },
+ // },
+ extendProperties(ctx, "multilib", t.Multilib, generalPropsValue,
+ reflect.ValueOf(a.archProperties[i].Multilib).FieldByName(t.MultilibField).Elem().Elem())
+
+ // Handle host-or-device-specific properties in the form:
+ // target {
+ // host {
+ // key: value,
+ // },
+ // },
+ hod := arch.HostOrDevice
+ extendProperties(ctx, "target", hod.FieldLower(), generalPropsValue,
+ reflect.ValueOf(a.archProperties[i].Target).FieldByName(hod.Field()).Elem().Elem())
+
+ // Handle host target properties in the form:
+ // target {
+ // linux {
+ // key: value,
+ // },
+ // not_windows {
+ // 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 {
+ extendProperties(ctx, "target", v.goos, generalPropsValue,
+ reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field).Elem().Elem())
+ }
+ }
+ extendProperties(ctx, "target", "not_windows", generalPropsValue,
+ reflect.ValueOf(a.archProperties[i].Target).FieldByName("Not_windows").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 extendProperties(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
+ dstValue, srcValue reflect.Value) {
+ extendPropertiesRecursive(ctx, variationType, variationName, dstValue, srcValue, "")
+}
+
+func 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
+ }
+
+ 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))
+ extendPropertiesRecursive(ctx, variationType, variationName,
+ dstFieldValue, srcFieldValue,
+ newRecursePrefix)
+ case reflect.Slice:
+ val, err := archCombineSlices(dstFieldValue, srcFieldValue, tags["arch_subtract"])
+ if err != nil {
+ ctx.PropertyErrorf(propertyName, err.Error())
+ continue
+ }
+ dstFieldValue.Set(val)
+ 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)
+ 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()))
+ }
+ }
+}
+
+func archCombineSlices(general, arch reflect.Value, canSubtract bool) (reflect.Value, error) {
+ if !canSubtract {
+ // Append the extension slice.
+ return reflect.AppendSlice(general, arch), nil
+ }
+
+ // Support -val in arch list to subtract a value from original list
+ l := general.Interface().([]string)
+ for archIndex := 0; archIndex < arch.Len(); archIndex++ {
+ archString := arch.Index(archIndex).String()
+ if strings.HasPrefix(archString, "-") {
+ generalIndex := findStringInSlice(archString[1:], l)
+ if generalIndex == -1 {
+ return reflect.Value{},
+ fmt.Errorf("can't find %q to subtract from general properties", archString[1:])
+ }
+ l = append(l[:generalIndex], l[generalIndex+1:]...)
+ } else {
+ l = append(l, archString)
+ }
+ }
+
+ return reflect.ValueOf(l), nil
+}
+
+func findStringInSlice(str string, slice []string) int {
+ for i, s := range slice {
+ if s == str {
+ return i
+ }
+ }
+
+ return -1
+}