aboutsummaryrefslogtreecommitdiffstats
path: root/android
diff options
context:
space:
mode:
Diffstat (limited to 'android')
-rw-r--r--android/androidmk.go215
-rw-r--r--android/arch.go1061
-rw-r--r--android/config.go323
-rw-r--r--android/defaults.go133
-rw-r--r--android/defs.go73
-rw-r--r--android/env.go55
-rw-r--r--android/glob.go120
-rw-r--r--android/makevars.go242
-rw-r--r--android/module.go695
-rw-r--r--android/mutator.go69
-rw-r--r--android/package_ctx.go133
-rw-r--r--android/paths.go668
-rw-r--r--android/paths_test.go182
-rw-r--r--android/util.go78
-rw-r--r--android/variable.go232
15 files changed, 4279 insertions, 0 deletions
diff --git a/android/androidmk.go b/android/androidmk.go
new file mode 100644
index 00000000..c8dce0f8
--- /dev/null
+++ b/android/androidmk.go
@@ -0,0 +1,215 @@
+// 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 android
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sort"
+
+ "android/soong"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+func init() {
+ soong.RegisterSingletonType("androidmk", AndroidMkSingleton)
+}
+
+type AndroidMkDataProvider interface {
+ AndroidMk() (AndroidMkData, error)
+}
+
+type AndroidMkData struct {
+ Class string
+ SubName string
+ OutputFile OptionalPath
+ Disabled bool
+
+ Custom func(w io.Writer, name, prefix string) error
+
+ Extra []func(w io.Writer, outputFile Path) error
+}
+
+func AndroidMkSingleton() blueprint.Singleton {
+ return &androidMkSingleton{}
+}
+
+type androidMkSingleton struct{}
+
+func (c *androidMkSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+ config := ctx.Config().(Config)
+
+ if !config.EmbeddedInMake() {
+ return
+ }
+
+ ctx.SetNinjaBuildDir(pctx, filepath.Join(config.buildDir, ".."))
+
+ var androidMkModulesList []Module
+
+ ctx.VisitAllModules(func(module blueprint.Module) {
+ if amod, ok := module.(Module); ok {
+ androidMkModulesList = append(androidMkModulesList, amod)
+ }
+ })
+
+ sort.Sort(AndroidModulesByName{androidMkModulesList, ctx})
+
+ transMk := PathForOutput(ctx, "Android"+proptools.String(config.ProductVariables.Make_suffix)+".mk")
+ if ctx.Failed() {
+ return
+ }
+
+ err := translateAndroidMk(ctx, transMk.String(), androidMkModulesList)
+ if err != nil {
+ ctx.Errorf(err.Error())
+ }
+
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: blueprint.Phony,
+ Outputs: []string{transMk.String()},
+ Optional: true,
+ })
+}
+
+func translateAndroidMk(ctx blueprint.SingletonContext, mkFile string, mods []Module) error {
+ buf := &bytes.Buffer{}
+
+ fmt.Fprintln(buf, "LOCAL_MODULE_MAKEFILE := $(lastword $(MAKEFILE_LIST))")
+
+ for _, mod := range mods {
+ err := translateAndroidMkModule(ctx, buf, mod)
+ if err != nil {
+ os.Remove(mkFile)
+ return err
+ }
+ }
+
+ // Don't write to the file if it hasn't changed
+ if _, err := os.Stat(mkFile); !os.IsNotExist(err) {
+ if data, err := ioutil.ReadFile(mkFile); err == nil {
+ matches := buf.Len() == len(data)
+
+ if matches {
+ for i, value := range buf.Bytes() {
+ if value != data[i] {
+ matches = false
+ break
+ }
+ }
+ }
+
+ if matches {
+ return nil
+ }
+ }
+ }
+
+ return ioutil.WriteFile(mkFile, buf.Bytes(), 0666)
+}
+
+func translateAndroidMkModule(ctx blueprint.SingletonContext, w io.Writer, mod blueprint.Module) error {
+ name := ctx.ModuleName(mod)
+
+ provider, ok := mod.(AndroidMkDataProvider)
+ if !ok {
+ return nil
+ }
+
+ amod := mod.(Module).base()
+ data, err := provider.AndroidMk()
+ if err != nil {
+ return err
+ }
+
+ if !amod.Enabled() {
+ return err
+ }
+
+ if data.SubName != "" {
+ name += "_" + data.SubName
+ }
+
+ hostCross := false
+ if amod.Host() && amod.HostType() != CurrentHostType() {
+ hostCross = true
+ }
+
+ if data.Custom != nil {
+ prefix := ""
+ if amod.Host() {
+ if hostCross {
+ prefix = "HOST_CROSS_"
+ } else {
+ prefix = "HOST_"
+ }
+ if amod.Arch().ArchType != ctx.Config().(Config).HostArches[amod.HostType()][0].ArchType {
+ prefix = "2ND_" + prefix
+ }
+ } else {
+ prefix = "TARGET_"
+ if amod.Arch().ArchType != ctx.Config().(Config).DeviceArches[0].ArchType {
+ prefix = "2ND_" + prefix
+ }
+ }
+
+ return data.Custom(w, name, prefix)
+ }
+
+ if data.Disabled {
+ return nil
+ }
+
+ if !data.OutputFile.Valid() {
+ return err
+ }
+
+ fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+ fmt.Fprintln(w, "LOCAL_PATH :=", filepath.Dir(ctx.BlueprintFile(mod)))
+ fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+ fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", data.Class)
+ fmt.Fprintln(w, "LOCAL_MULTILIB :=", amod.commonProperties.Compile_multilib)
+ fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", data.OutputFile.String())
+
+ archStr := amod.Arch().ArchType.String()
+ if amod.Host() {
+ if hostCross {
+ fmt.Fprintln(w, "LOCAL_MODULE_HOST_CROSS_ARCH :=", archStr)
+ } else {
+ fmt.Fprintln(w, "LOCAL_MODULE_HOST_ARCH :=", archStr)
+ }
+ fmt.Fprintln(w, "LOCAL_MODULE_HOST_OS :=", amod.HostType().String())
+ fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
+ } else {
+ fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", archStr)
+ }
+
+ for _, extra := range data.Extra {
+ err = extra(w, data.OutputFile.Path())
+ if err != nil {
+ return err
+ }
+ }
+
+ fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+
+ return err
+}
diff --git a/android/arch.go b/android/arch.go
new file mode 100644
index 00000000..952b7207
--- /dev/null
+++ b/android/arch.go
@@ -0,0 +1,1061 @@
+// 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 android
+
+import (
+ "fmt"
+ "reflect"
+ "runtime"
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+func init() {
+ RegisterBottomUpMutator("defaults_deps", defaultsDepsMutator)
+ RegisterTopDownMutator("defaults", defaultsMutator)
+
+ RegisterBottomUpMutator("host_or_device", HostOrDeviceMutator)
+ RegisterBottomUpMutator("host_type", HostTypeMutator)
+ RegisterBottomUpMutator("arch", ArchMutator)
+}
+
+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",
+ }
+)
+
+var archTypeMap = map[string]ArchType{
+ "arm": Arm,
+ "arm64": Arm64,
+ "mips": Mips,
+ "mips64": Mips64,
+ "x86": X86,
+ "x86_64": 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 Embed interface{}
+
+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 struct {
+ Embed `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\")"`
+ Cortex_a53 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Cortex_a53_a57 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Krait interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Denver interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ }
+
+ // Properties for module variants being built to run on arm64 (host or device)
+ Arm64 struct {
+ Embed `blueprint:"filter(android:\"arch_variant\")"`
+
+ // Arm64 arch variants
+ Armv8_a interface{} `blueprint:"filter(android:\"arch_variant\")"`
+
+ // Arm64 cpu variants
+ Cortex_a53 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Denver64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ }
+
+ // Properties for module variants being built to run on mips (host or device)
+ Mips struct {
+ Embed `blueprint:"filter(android:\"arch_variant\")"`
+
+ // Mips arch variants
+ Mips32_fp interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Mips32r2_fp interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Mips32r2_fp_xburst interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Mips32r2dsp_fp interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Mips32r2dspr2_fp interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Mips32r6 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+
+ // Mips arch features
+ Rev6 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ }
+
+ // Properties for module variants being built to run on mips64 (host or device)
+ Mips64 struct {
+ Embed `blueprint:"filter(android:\"arch_variant\")"`
+
+ // Mips64 arch variants
+ Mips64r2 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Mips64r6 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+
+ // Mips64 arch features
+ Rev6 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ }
+
+ // Properties for module variants being built to run on x86 (host or device)
+ X86 struct {
+ Embed `blueprint:"filter(android:\"arch_variant\")"`
+
+ // X86 arch variants
+ Atom interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Haswell interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Ivybridge interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Sandybridge interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Silvermont interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ // Generic variant for X86 on X86_64
+ X86_64 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+
+ // X86 arch features
+ Ssse3 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Sse4 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Sse4_1 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Sse4_2 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Aes_ni interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Avx interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Popcnt interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Movbe interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ }
+
+ // Properties for module variants being built to run on x86_64 (host or device)
+ X86_64 struct {
+ Embed `blueprint:"filter(android:\"arch_variant\")"`
+
+ // X86 arch variants
+ Haswell interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Ivybridge interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Sandybridge interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Silvermont interface{} `blueprint:"filter(android:\"arch_variant\")"`
+
+ // X86 arch features
+ Ssse3 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Sse4 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Sse4_1 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Sse4_2 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Aes_ni interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Avx interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ Popcnt 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 windows x86 hosts
+ Windows_x86 interface{} `blueprint:"filter(android:\"arch_variant\")"`
+ // Properties for module variants being built to run on windows x86_64 hosts
+ Windows_x86_64 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\")"`
+ }
+}
+
+var archFeatureMap = map[ArchType]map[string][]string{}
+
+func RegisterArchFeatures(arch ArchType, variant string, features ...string) {
+ archField := proptools.FieldNameForProperty(arch.Name)
+ variantField := proptools.FieldNameForProperty(variant)
+ archStruct := reflect.ValueOf(archProperties{}.Arch).FieldByName(archField)
+ if variant != "" {
+ if !archStruct.FieldByName(variantField).IsValid() {
+ panic(fmt.Errorf("Invalid variant %q for arch %q", variant, arch))
+ }
+ }
+ for _, feature := range features {
+ field := proptools.FieldNameForProperty(feature)
+ if !archStruct.FieldByName(field).IsValid() {
+ panic(fmt.Errorf("Invalid feature %q for arch %q variant %q", feature, arch, variant))
+ }
+ }
+ if archFeatureMap[arch] == nil {
+ archFeatureMap[arch] = make(map[string][]string)
+ }
+ archFeatureMap[arch][variant] = features
+}
+
+// An Arch indicates a single CPU architecture.
+type Arch struct {
+ ArchType ArchType
+ ArchVariant string
+ CpuVariant string
+ Abi []string
+ ArchFeatures []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
+ HostAndDeviceDefault
+)
+
+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",
+}
+
+type HostType int
+
+const (
+ NoHostType HostType = iota
+ Linux
+ Darwin
+ Windows
+)
+
+func CurrentHostType() HostType {
+ switch runtime.GOOS {
+ case "linux":
+ return Linux
+ case "darwin":
+ return Darwin
+ default:
+ panic(fmt.Sprintf("unsupported OS: %s", runtime.GOOS))
+ }
+}
+
+func (ht HostType) String() string {
+ switch ht {
+ case Linux:
+ return "linux"
+ case Darwin:
+ return "darwin"
+ case Windows:
+ return "windows"
+ default:
+ panic(fmt.Sprintf("unexpected HostType value %d", ht))
+ }
+}
+
+func (ht HostType) Field() string {
+ switch ht {
+ case Linux:
+ return "Linux"
+ case Darwin:
+ return "Darwin"
+ case Windows:
+ return "Windows"
+ default:
+ panic(fmt.Sprintf("unexpected HostType value %d", ht))
+ }
+}
+
+var (
+ commonArch = Arch{
+ ArchType: Common,
+ }
+)
+
+func HostOrDeviceMutator(mctx BottomUpMutatorContext) {
+ var module Module
+ var ok bool
+ if module, ok = mctx.Module().(Module); !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.(Module).base().SetHostOrDevice(hods[i])
+ }
+}
+
+func HostTypeMutator(mctx BottomUpMutatorContext) {
+ var module Module
+ var ok bool
+ if module, ok = mctx.Module().(Module); !ok {
+ return
+ }
+
+ if !module.base().HostSupported() || !module.base().HostOrDevice().Host() {
+ return
+ }
+
+ buildTypes, err := decodeHostTypesProductVariables(mctx.Config().(Config).ProductVariables)
+ if err != nil {
+ mctx.ModuleErrorf("%s", err.Error())
+ return
+ }
+
+ typeNames := []string{}
+ for _, ht := range buildTypes {
+ typeNames = append(typeNames, ht.String())
+ }
+
+ modules := mctx.CreateVariations(typeNames...)
+ for i, m := range modules {
+ m.(Module).base().SetHostType(buildTypes[i])
+ }
+}
+
+func ArchMutator(mctx BottomUpMutatorContext) {
+ var module Module
+ var ok bool
+ if module, ok = mctx.Module().(Module); !ok {
+ return
+ }
+
+ moduleArches := []Arch{}
+ multilib := module.base().commonProperties.Compile_multilib
+
+ if module.base().HostSupported() && module.base().HostOrDevice().Host() {
+ hostModuleArches, err := decodeMultilib(multilib, mctx.Config().(Config).HostArches[module.base().HostType()])
+ if err != nil {
+ mctx.ModuleErrorf("%s", err.Error())
+ }
+
+ moduleArches = append(moduleArches, hostModuleArches...)
+ }
+
+ if module.base().DeviceSupported() && module.base().HostOrDevice().Device() {
+ deviceModuleArches, err := decodeMultilib(multilib, mctx.Config().(Config).DeviceArches)
+ if err != nil {
+ mctx.ModuleErrorf("%s", err.Error())
+ }
+
+ moduleArches = append(moduleArches, deviceModuleArches...)
+ }
+
+ if len(moduleArches) == 0 {
+ return
+ }
+
+ archNames := []string{}
+ for _, arch := range moduleArches {
+ archNames = append(archNames, arch.String())
+ }
+
+ modules := mctx.CreateVariations(archNames...)
+
+ for i, m := range modules {
+ m.(Module).base().SetArch(moduleArches[i])
+ m.(Module).base().setArchProperties(mctx)
+ }
+}
+
+func InitArchModule(m Module,
+ propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
+
+ base := m.base()
+
+ base.generalProperties = append(base.generalProperties,
+ propertyStructs...)
+
+ for _, properties := range base.generalProperties {
+ propertiesValue := reflect.ValueOf(properties)
+ if propertiesValue.Kind() != reflect.Ptr {
+ panic(fmt.Errorf("properties must be a pointer to a struct, got %T",
+ propertiesValue.Interface()))
+ }
+
+ propertiesValue = propertiesValue.Elem()
+ if propertiesValue.Kind() != reflect.Struct {
+ panic(fmt.Errorf("properties must be a pointer to a struct, got %T",
+ propertiesValue.Interface()))
+ }
+
+ 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 variantReplacer = strings.NewReplacer("-", "_", ".", "_")
+
+func (a *ModuleBase) appendProperties(ctx BottomUpMutatorContext,
+ dst, src interface{}, field, srcPrefix string) interface{} {
+
+ srcField := reflect.ValueOf(src).FieldByName(field)
+ if !srcField.IsValid() {
+ ctx.ModuleErrorf("field %q does not exist", srcPrefix)
+ return nil
+ }
+
+ ret := srcField
+
+ if srcField.Kind() == reflect.Struct {
+ srcField = srcField.FieldByName("Embed")
+ }
+
+ src = srcField.Elem().Interface()
+
+ filter := func(property string,
+ dstField, srcField reflect.StructField,
+ dstValue, srcValue interface{}) (bool, error) {
+
+ srcProperty := srcPrefix + "." + property
+
+ if !proptools.HasTag(dstField, "android", "arch_variant") {
+ if ctx.ContainsProperty(srcProperty) {
+ return false, fmt.Errorf("can't be specific to a build variant")
+ } else {
+ return false, nil
+ }
+ }
+
+ return true, nil
+ }
+
+ order := func(property string,
+ dstField, srcField reflect.StructField,
+ dstValue, srcValue interface{}) (proptools.Order, error) {
+ if proptools.HasTag(dstField, "android", "variant_prepend") {
+ return proptools.Prepend, nil
+ } else {
+ return proptools.Append, nil
+ }
+ }
+
+ err := proptools.ExtendProperties(dst, src, filter, order)
+ if err != nil {
+ if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+ ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+ } else {
+ panic(err)
+ }
+ }
+
+ return ret.Interface()
+}
+
+// Rewrite the module's properties structs to contain arch-specific values.
+func (a *ModuleBase) setArchProperties(ctx BottomUpMutatorContext) {
+ arch := a.commonProperties.CompileArch
+ hod := a.commonProperties.CompileHostOrDevice
+ ht := a.commonProperties.CompileHostType
+
+ if arch.ArchType == Common {
+ return
+ }
+
+ for i := range a.generalProperties {
+ genProps := a.generalProperties[i]
+ archProps := a.archProperties[i]
+ // Handle arch-specific properties in the form:
+ // arch: {
+ // arm64: {
+ // key: value,
+ // },
+ // },
+ t := arch.ArchType
+
+ field := proptools.FieldNameForProperty(t.Name)
+ prefix := "arch." + t.Name
+ archStruct := a.appendProperties(ctx, genProps, archProps.Arch, field, prefix)
+
+ // Handle arch-variant-specific properties in the form:
+ // arch: {
+ // variant: {
+ // key: value,
+ // },
+ // },
+ v := variantReplacer.Replace(arch.ArchVariant)
+ if v != "" {
+ field := proptools.FieldNameForProperty(v)
+ prefix := "arch." + t.Name + "." + v
+ a.appendProperties(ctx, genProps, archStruct, field, prefix)
+ }
+
+ // Handle cpu-variant-specific properties in the form:
+ // arch: {
+ // variant: {
+ // key: value,
+ // },
+ // },
+ c := variantReplacer.Replace(arch.CpuVariant)
+ if c != "" {
+ field := proptools.FieldNameForProperty(c)
+ prefix := "arch." + t.Name + "." + c
+ a.appendProperties(ctx, genProps, archStruct, field, prefix)
+ }
+
+ // Handle arch-feature-specific properties in the form:
+ // arch: {
+ // feature: {
+ // key: value,
+ // },
+ // },
+ for _, feature := range arch.ArchFeatures {
+ field := proptools.FieldNameForProperty(feature)
+ prefix := "arch." + t.Name + "." + feature
+ a.appendProperties(ctx, genProps, archStruct, field, prefix)
+ }
+
+ // Handle multilib-specific properties in the form:
+ // multilib: {
+ // lib32: {
+ // key: value,
+ // },
+ // },
+ field = proptools.FieldNameForProperty(t.Multilib)
+ prefix = "multilib." + t.Multilib
+ a.appendProperties(ctx, genProps, archProps.Multilib, field, prefix)
+
+ // Handle host-or-device-specific properties in the form:
+ // target: {
+ // host: {
+ // key: value,
+ // },
+ // },
+ hodProperty := hod.Property()
+ field = proptools.FieldNameForProperty(hodProperty)
+ prefix = "target." + hodProperty
+ a.appendProperties(ctx, genProps, archProps.Target, field, prefix)
+
+ // Handle host target properties in the form:
+ // target: {
+ // linux: {
+ // key: value,
+ // },
+ // not_windows: {
+ // key: value,
+ // },
+ // linux_x86: {
+ // key: value,
+ // },
+ // linux_arm: {
+ // key: value,
+ // },
+ // },
+ if hod.Host() {
+ field := ht.Field()
+ prefix := "target." + ht.String()
+ a.appendProperties(ctx, genProps, archProps.Target, field, prefix)
+
+ t := arch.ArchType
+ field = ht.Field() + "_" + t.Name
+ prefix = "target." + ht.String() + "_" + t.Name
+ a.appendProperties(ctx, genProps, archProps.Target, field, prefix)
+
+ if ht != Windows {
+ field := "Not_windows"
+ prefix := "target.not_windows"
+ a.appendProperties(ctx, genProps, archProps.Target, field, prefix)
+ }
+ }
+
+ // 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 */ {
+ field := "Android64"
+ prefix := "target.android64"
+ a.appendProperties(ctx, genProps, archProps.Target, field, prefix)
+ } else {
+ field := "Android32"
+ prefix := "target.android32"
+ a.appendProperties(ctx, genProps, archProps.Target, field, prefix)
+ }
+ }
+
+ // Handle device architecture properties in the form:
+ // target {
+ // android_arm {
+ // key: value,
+ // },
+ // android_x86 {
+ // key: value,
+ // },
+ // },
+ if hod.Device() {
+ t := arch.ArchType
+ field := "Android_" + t.Name
+ prefix := "target.android_" + t.Name
+ a.appendProperties(ctx, genProps, archProps.Target, field, prefix)
+ }
+
+ 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()))
+ }
+}
+
+// Get a list of HostTypes from the product variables
+func decodeHostTypesProductVariables(variables productVariables) ([]HostType, error) {
+ ret := []HostType{CurrentHostType()}
+
+ if variables.CrossHost != nil && *variables.CrossHost != "" {
+ switch *variables.CrossHost {
+ case "windows":
+ ret = append(ret, Windows)
+ default:
+ return nil, fmt.Errorf("Unsupported secondary host: %s", *variables.CrossHost)
+ }
+ }
+
+ return ret, nil
+}
+
+// Convert the arch product variables into a list of host and device Arch structs
+func decodeArchProductVariables(variables productVariables) (map[HostType][]Arch, []Arch, error) {
+ if variables.HostArch == nil {
+ return nil, nil, fmt.Errorf("No host primary architecture set")
+ }
+
+ hostArch, err := decodeArch(*variables.HostArch, nil, nil, nil)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ hostArches := []Arch{hostArch}
+
+ if variables.HostSecondaryArch != nil && *variables.HostSecondaryArch != "" {
+ hostSecondaryArch, err := decodeArch(*variables.HostSecondaryArch, nil, nil, nil)
+ if err != nil {
+ return nil, nil, err
+ }
+ hostArches = append(hostArches, hostSecondaryArch)
+ }
+
+ hostTypeArches := map[HostType][]Arch{
+ CurrentHostType(): hostArches,
+ }
+
+ if variables.CrossHost != nil && *variables.CrossHost != "" {
+ if variables.CrossHostArch == nil || *variables.CrossHostArch == "" {
+ return nil, nil, fmt.Errorf("No cross-host primary architecture set")
+ }
+
+ crossHostArch, err := decodeArch(*variables.CrossHostArch, nil, nil, nil)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ crossHostArches := []Arch{crossHostArch}
+
+ if variables.CrossHostSecondaryArch != nil && *variables.CrossHostSecondaryArch != "" {
+ crossHostSecondaryArch, err := decodeArch(*variables.CrossHostSecondaryArch, nil, nil, nil)
+ if err != nil {
+ return nil, nil, err
+ }
+ crossHostArches = append(crossHostArches, crossHostSecondaryArch)
+ }
+
+ switch *variables.CrossHost {
+ case "windows":
+ hostTypeArches[Windows] = crossHostArches
+ default:
+ return nil, nil, fmt.Errorf("Unsupported cross-host: %s", *variables.CrossHost)
+ }
+ }
+
+ if variables.DeviceArch == nil {
+ return nil, nil, fmt.Errorf("No device primary architecture set")
+ }
+
+ deviceArch, err := decodeArch(*variables.DeviceArch, variables.DeviceArchVariant,
+ variables.DeviceCpuVariant, variables.DeviceAbi)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ deviceArches := []Arch{deviceArch}
+
+ if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" {
+ deviceSecondaryArch, err := decodeArch(*variables.DeviceSecondaryArch,
+ variables.DeviceSecondaryArchVariant, variables.DeviceSecondaryCpuVariant,
+ variables.DeviceSecondaryAbi)
+ if err != nil {
+ return nil, nil, err
+ }
+ deviceArches = append(deviceArches, deviceSecondaryArch)
+ }
+
+ return hostTypeArches, deviceArches, nil
+}
+
+func decodeMegaDevice() ([]Arch, error) {
+ archSettings := []struct {
+ arch string
+ archVariant string
+ cpuVariant string
+ abi []string
+ }{
+ // armv5 is only used for unbundled apps
+ //{"arm", "armv5te", "", []string{"armeabi"}},
+ {"arm", "armv7-a", "generic", []string{"armeabi-v7a"}},
+ {"arm", "armv7-a-neon", "generic", []string{"armeabi-v7a"}},
+ {"arm", "armv7-a-neon", "cortex-a7", []string{"armeabi-v7a"}},
+ {"arm", "armv7-a-neon", "cortex-a8", []string{"armeabi-v7a"}},
+ {"arm", "armv7-a-neon", "cortex-a9", []string{"armeabi-v7a"}},
+ {"arm", "armv7-a-neon", "cortex-a15", []string{"armeabi-v7a"}},
+ {"arm", "armv7-a-neon", "cortex-a53", []string{"armeabi-v7a"}},
+ {"arm", "armv7-a-neon", "cortex-a53.a57", []string{"armeabi-v7a"}},
+ {"arm", "armv7-a-neon", "denver", []string{"armeabi-v7a"}},
+ {"arm", "armv7-a-neon", "krait", []string{"armeabi-v7a"}},
+ {"arm64", "armv8-a", "cortex-a53", []string{"arm64-v8a"}},
+ {"arm64", "armv8-a", "denver64", []string{"arm64-v8a"}},
+ {"mips", "mips32-fp", "", []string{"mips"}},
+ {"mips", "mips32r2-fp", "", []string{"mips"}},
+ {"mips", "mips32r2-fp-xburst", "", []string{"mips"}},
+ {"mips", "mips32r6", "", []string{"mips"}},
+ // mips32r2dsp[r2]-fp fails in the assembler for divdf3.c in compiler-rt:
+ // (same errors in make and soong)
+ // Error: invalid operands `mtlo $ac0,$11'
+ // Error: invalid operands `mthi $ac0,$12'
+ //{"mips", "mips32r2dsp-fp", "", []string{"mips"}},
+ //{"mips", "mips32r2dspr2-fp", "", []string{"mips"}},
+ // mips64r2 is mismatching 64r2 and 64r6 libraries during linking to libgcc
+ //{"mips64", "mips64r2", "", []string{"mips64"}},
+ {"mips64", "mips64r6", "", []string{"mips64"}},
+ {"x86", "", "", []string{"x86"}},
+ {"x86", "atom", "", []string{"x86"}},
+ {"x86", "haswell", "", []string{"x86"}},
+ {"x86", "ivybridge", "", []string{"x86"}},
+ {"x86", "sandybridge", "", []string{"x86"}},
+ {"x86", "silvermont", "", []string{"x86"}},
+ {"x86", "x86_64", "", []string{"x86"}},
+ {"x86_64", "", "", []string{"x86_64"}},
+ {"x86_64", "haswell", "", []string{"x86_64"}},
+ {"x86_64", "ivybridge", "", []string{"x86_64"}},
+ {"x86_64", "sandybridge", "", []string{"x86_64"}},
+ {"x86_64", "silvermont", "", []string{"x86_64"}},
+ }
+
+ var ret []Arch
+
+ for _, config := range archSettings {
+ arch, err := decodeArch(config.arch, &config.archVariant,
+ &config.cpuVariant, &config.abi)
+ if err != nil {
+ return nil, err
+ }
+ ret = append(ret, arch)
+ }
+
+ return ret, nil
+}
+
+// Convert a set of strings from product variables into a single Arch struct
+func decodeArch(arch string, archVariant, cpuVariant *string, abi *[]string) (Arch, error) {
+ stringPtr := func(p *string) string {
+ if p != nil {
+ return *p
+ }
+ return ""
+ }
+
+ slicePtr := func(p *[]string) []string {
+ if p != nil {
+ return *p
+ }
+ return nil
+ }
+
+ archType, ok := archTypeMap[arch]
+ if !ok {
+ return Arch{}, fmt.Errorf("unknown arch %q", arch)
+ }
+
+ a := Arch{
+ ArchType: archType,
+ ArchVariant: stringPtr(archVariant),
+ CpuVariant: stringPtr(cpuVariant),
+ Abi: slicePtr(abi),
+ }
+
+ if a.ArchVariant == a.ArchType.Name || a.ArchVariant == "generic" {
+ a.ArchVariant = ""
+ }
+
+ if a.CpuVariant == a.ArchType.Name || a.CpuVariant == "generic" {
+ a.CpuVariant = ""
+ }
+
+ for i := 0; i < len(a.Abi); i++ {
+ if a.Abi[i] == "" {
+ a.Abi = append(a.Abi[:i], a.Abi[i+1:]...)
+ i--
+ }
+ }
+
+ if featureMap, ok := archFeatureMap[archType]; ok {
+ a.ArchFeatures = featureMap[a.ArchVariant]
+ }
+
+ return a, nil
+}
+
+// Use the module multilib setting to select one or more arches from an arch list
+func decodeMultilib(multilib string, arches []Arch) ([]Arch, error) {
+ buildArches := []Arch{}
+ switch multilib {
+ case "common":
+ buildArches = append(buildArches, commonArch)
+ case "both":
+ buildArches = append(buildArches, arches...)
+ case "first":
+ buildArches = append(buildArches, arches[0])
+ case "32":
+ for _, a := range arches {
+ if a.ArchType.Multilib == "lib32" {
+ buildArches = append(buildArches, a)
+ }
+ }
+ case "64":
+ for _, a := range arches {
+ if a.ArchType.Multilib == "lib64" {
+ buildArches = append(buildArches, a)
+ }
+ }
+ default:
+ return nil, fmt.Errorf(`compile_multilib must be "both", "first", "32", or "64", found %q`,
+ multilib)
+ //buildArches = append(buildArches, arches[0])
+ }
+
+ return buildArches, nil
+}
diff --git a/android/config.go b/android/config.go
new file mode 100644
index 00000000..8701960e
--- /dev/null
+++ b/android/config.go
@@ -0,0 +1,323 @@
+// 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 android
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "sync"
+
+ "github.com/google/blueprint/proptools"
+)
+
+var Bool = proptools.Bool
+
+// The configuration file name
+const configFileName = "soong.config"
+const productVariablesFileName = "soong.variables"
+
+// A FileConfigurableOptions contains options which can be configured by the
+// config file. These will be included in the config struct.
+type FileConfigurableOptions struct {
+ Mega_device *bool `json:",omitempty"`
+}
+
+func (f *FileConfigurableOptions) SetDefaultConfig() {
+ *f = FileConfigurableOptions{}
+}
+
+type Config struct {
+ *config
+}
+
+// A config object represents the entire build configuration for Android.
+type config struct {
+ FileConfigurableOptions
+ ProductVariables productVariables
+
+ ConfigFileName string
+ ProductVariablesFileName string
+
+ DeviceArches []Arch
+ HostArches map[HostType][]Arch
+
+ srcDir string // the path of the root source directory
+ buildDir string // the path of the build output directory
+
+ envLock sync.Mutex
+ envDeps map[string]string
+ envFrozen bool
+
+ inMake bool
+}
+
+type jsonConfigurable interface {
+ SetDefaultConfig()
+}
+
+func loadConfig(config *config) error {
+ err := loadFromConfigFile(&config.FileConfigurableOptions, config.ConfigFileName)
+ if err != nil {
+ return err
+ }
+
+ return loadFromConfigFile(&config.ProductVariables, config.ProductVariablesFileName)
+}
+
+// loads configuration options from a JSON file in the cwd.
+func loadFromConfigFile(configurable jsonConfigurable, filename string) error {
+ // Try to open the file
+ configFileReader, err := os.Open(filename)
+ defer configFileReader.Close()
+ if os.IsNotExist(err) {
+ // Need to create a file, so that blueprint & ninja don't get in
+ // a dependency tracking loop.
+ // Make a file-configurable-options with defaults, write it out using
+ // a json writer.
+ configurable.SetDefaultConfig()
+ err = saveToConfigFile(configurable, filename)
+ if err != nil {
+ return err
+ }
+ } else {
+ // Make a decoder for it
+ jsonDecoder := json.NewDecoder(configFileReader)
+ err = jsonDecoder.Decode(configurable)
+ if err != nil {
+ return fmt.Errorf("config file: %s did not parse correctly: "+err.Error(), filename)
+ }
+ }
+
+ // No error
+ return nil
+}
+
+func saveToConfigFile(config jsonConfigurable, filename string) error {
+ data, err := json.MarshalIndent(&config, "", " ")
+ if err != nil {
+ return fmt.Errorf("cannot marshal config data: %s", err.Error())
+ }
+
+ configFileWriter, err := os.Create(filename)
+ if err != nil {
+ return fmt.Errorf("cannot create empty config file %s: %s\n", filename, err.Error())
+ }
+ defer configFileWriter.Close()
+
+ _, err = configFileWriter.Write(data)
+ if err != nil {
+ return fmt.Errorf("default config file: %s could not be written: %s", filename, err.Error())
+ }
+
+ _, err = configFileWriter.WriteString("\n")
+ if err != nil {
+ return fmt.Errorf("default config file: %s could not be written: %s", filename, err.Error())
+ }
+
+ return nil
+}
+
+// New creates a new Config object. The srcDir argument specifies the path to
+// the root source directory. It also loads the config file, if found.
+func NewConfig(srcDir, buildDir string) (Config, error) {
+ // Make a config with default options
+ config := Config{
+ config: &config{
+ ConfigFileName: filepath.Join(buildDir, configFileName),
+ ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
+
+ srcDir: srcDir,
+ buildDir: buildDir,
+ envDeps: make(map[string]string),
+ },
+ }
+
+ // Sanity check the build and source directories. This won't catch strange
+ // configurations with symlinks, but at least checks the obvious cases.
+ absBuildDir, err := filepath.Abs(buildDir)
+ if err != nil {
+ return Config{}, err
+ }
+
+ absSrcDir, err := filepath.Abs(srcDir)
+ if err != nil {
+ return Config{}, err
+ }
+
+ if strings.HasPrefix(absSrcDir, absBuildDir) {
+ return Config{}, fmt.Errorf("Build dir must not contain source directory")
+ }
+
+ // Load any configurable options from the configuration file
+ err = loadConfig(config.config)
+ if err != nil {
+ return Config{}, err
+ }
+
+ inMakeFile := filepath.Join(buildDir, ".soong.in_make")
+ if _, err := os.Stat(inMakeFile); err == nil {
+ config.inMake = true
+ }
+
+ hostArches, deviceArches, err := decodeArchProductVariables(config.ProductVariables)
+ if err != nil {
+ return Config{}, err
+ }
+
+ if Bool(config.Mega_device) {
+ deviceArches, err = decodeMegaDevice()
+ if err != nil {
+ return Config{}, err
+ }
+ }
+
+ config.HostArches = hostArches
+ config.DeviceArches = deviceArches
+
+ return config, nil
+}
+
+func (c *config) RemoveAbandonedFiles() bool {
+ return false
+}
+
+// PrebuiltOS returns the name of the host OS used in prebuilts directories
+func (c *config) PrebuiltOS() string {
+ switch runtime.GOOS {
+ case "linux":
+ return "linux-x86"
+ case "darwin":
+ return "darwin-x86"
+ default:
+ panic("Unknown GOOS")
+ }
+}
+
+// GoRoot returns the path to the root directory of the Go toolchain.
+func (c *config) GoRoot() string {
+ return fmt.Sprintf("%s/prebuilts/go/%s", c.srcDir, c.PrebuiltOS())
+}
+
+func (c *config) CpPreserveSymlinksFlags() string {
+ switch runtime.GOOS {
+ case "darwin":
+ return "-R"
+ case "linux":
+ return "-d"
+ default:
+ return ""
+ }
+}
+
+func (c *config) Getenv(key string) string {
+ var val string
+ var exists bool
+ c.envLock.Lock()
+ if val, exists = c.envDeps[key]; !exists {
+ if c.envFrozen {
+ panic("Cannot access new environment variables after envdeps are frozen")
+ }
+ val = os.Getenv(key)
+ c.envDeps[key] = val
+ }
+ c.envLock.Unlock()
+ return val
+}
+
+func (c *config) EnvDeps() map[string]string {
+ c.envLock.Lock()
+ c.envFrozen = true
+ c.envLock.Unlock()
+ return c.envDeps
+}
+
+func (c *config) EmbeddedInMake() bool {
+ return c.inMake
+}
+
+// DeviceName returns the name of the current device target
+// TODO: take an AndroidModuleContext to select the device name for multi-device builds
+func (c *config) DeviceName() string {
+ return *c.ProductVariables.DeviceName
+}
+
+func (c *config) DeviceUsesClang() bool {
+ if c.ProductVariables.DeviceUsesClang != nil {
+ return *c.ProductVariables.DeviceUsesClang
+ }
+ return true
+}
+
+func (c *config) ResourceOverlays() []SourcePath {
+ return nil
+}
+
+func (c *config) PlatformVersion() string {
+ return "M"
+}
+
+func (c *config) PlatformSdkVersion() string {
+ return "22"
+}
+
+func (c *config) BuildNumber() string {
+ return "000000"
+}
+
+func (c *config) ProductAaptConfig() []string {
+ return []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"}
+}
+
+func (c *config) ProductAaptPreferredConfig() string {
+ return "xhdpi"
+}
+
+func (c *config) ProductAaptCharacteristics() string {
+ return "nosdcard"
+}
+
+func (c *config) DefaultAppCertificateDir(ctx PathContext) SourcePath {
+ return PathForSource(ctx, "build/target/product/security")
+}
+
+func (c *config) DefaultAppCertificate(ctx PathContext) SourcePath {
+ return c.DefaultAppCertificateDir(ctx).Join(ctx, "testkey")
+}
+
+func (c *config) AllowMissingDependencies() bool {
+ return Bool(c.ProductVariables.Allow_missing_dependencies)
+}
+
+func (c *config) SkipDeviceInstall() bool {
+ return c.EmbeddedInMake() || Bool(c.Mega_device)
+}
+
+func (c *config) SanitizeHost() []string {
+ if c.ProductVariables.SanitizeHost == nil {
+ return nil
+ }
+ return *c.ProductVariables.SanitizeHost
+}
+
+func (c *config) SanitizeDevice() []string {
+ if c.ProductVariables.SanitizeDevice == nil {
+ return nil
+ }
+ return *c.ProductVariables.SanitizeDevice
+}
diff --git a/android/defaults.go b/android/defaults.go
new file mode 100644
index 00000000..f9cf0cc8
--- /dev/null
+++ b/android/defaults.go
@@ -0,0 +1,133 @@
+// 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 android
+
+import (
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+type defaultsDependencyTag struct {
+ blueprint.BaseDependencyTag
+}
+
+var DefaultsDepTag defaultsDependencyTag
+
+type defaultsProperties struct {
+ Defaults []string
+}
+
+type DefaultableModule struct {
+ defaultsProperties defaultsProperties
+ defaultableProperties []interface{}
+}
+
+func (d *DefaultableModule) defaults() *defaultsProperties {
+ return &d.defaultsProperties
+}
+
+func (d *DefaultableModule) setProperties(props []interface{}) {
+ d.defaultableProperties = props
+}
+
+type Defaultable interface {
+ defaults() *defaultsProperties
+ setProperties([]interface{})
+ applyDefaults(TopDownMutatorContext, Defaults)
+}
+
+var _ Defaultable = (*DefaultableModule)(nil)
+
+func InitDefaultableModule(module Module, d Defaultable,
+ props ...interface{}) (blueprint.Module, []interface{}) {
+
+ d.setProperties(props)
+
+ props = append(props, d.defaults())
+
+ return module, props
+}
+
+type DefaultsModule struct {
+ defaultProperties []interface{}
+}
+
+type Defaults interface {
+ isDefaults() bool
+ setProperties([]interface{})
+ properties() []interface{}
+}
+
+func (d *DefaultsModule) isDefaults() bool {
+ return true
+}
+
+func (d *DefaultsModule) properties() []interface{} {
+ return d.defaultProperties
+}
+
+func (d *DefaultsModule) setProperties(props []interface{}) {
+ d.defaultProperties = props
+}
+
+func InitDefaultsModule(module Module, d Defaults, props ...interface{}) (blueprint.Module, []interface{}) {
+ d.setProperties(props)
+
+ return module, props
+}
+
+var _ Defaults = (*DefaultsModule)(nil)
+
+func (defaultable *DefaultableModule) applyDefaults(ctx TopDownMutatorContext,
+ defaults Defaults) {
+
+ for _, prop := range defaultable.defaultableProperties {
+ for _, def := range defaults.properties() {
+ if proptools.TypeEqual(prop, def) {
+ err := proptools.PrependProperties(prop, def, nil)
+ if err != nil {
+ if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+ ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+ } else {
+ panic(err)
+ }
+ }
+ }
+ }
+ }
+}
+
+func defaultsDepsMutator(ctx BottomUpMutatorContext) {
+ if defaultable, ok := ctx.Module().(Defaultable); ok {
+ ctx.AddDependency(ctx.Module(), DefaultsDepTag, defaultable.defaults().Defaults...)
+ }
+}
+
+func defaultsMutator(ctx TopDownMutatorContext) {
+ if defaultable, ok := ctx.Module().(Defaultable); ok {
+ for _, defaultsDep := range defaultable.defaults().Defaults {
+ ctx.VisitDirectDeps(func(m blueprint.Module) {
+ if ctx.OtherModuleName(m) == defaultsDep {
+ if defaultsModule, ok := m.(Defaults); ok {
+ defaultable.applyDefaults(ctx, defaultsModule)
+ } else {
+ ctx.PropertyErrorf("defaults", "module %s is not an defaults module",
+ ctx.OtherModuleName(m))
+ }
+ }
+ })
+ }
+ }
+}
diff --git a/android/defs.go b/android/defs.go
new file mode 100644
index 00000000..be28e8bc
--- /dev/null
+++ b/android/defs.go
@@ -0,0 +1,73 @@
+// 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 android
+
+import (
+ "github.com/google/blueprint"
+ _ "github.com/google/blueprint/bootstrap"
+)
+
+var (
+ pctx = NewPackageContext("android/soong/common")
+
+ cpPreserveSymlinks = pctx.VariableConfigMethod("cpPreserveSymlinks",
+ Config.CpPreserveSymlinksFlags)
+
+ // A phony rule that is not the built-in Ninja phony rule. The built-in
+ // phony rule has special behavior that is sometimes not desired. See the
+ // Ninja docs for more details.
+ Phony = pctx.StaticRule("Phony",
+ blueprint.RuleParams{
+ Command: "# phony $out",
+ Description: "phony $out",
+ })
+
+ // GeneratedFile is a rule for indicating that a given file was generated
+ // while running soong. This allows the file to be cleaned up if it ever
+ // stops being generated by soong.
+ GeneratedFile = pctx.StaticRule("GeneratedFile",
+ blueprint.RuleParams{
+ Command: "# generated $out",
+ Description: "generated $out",
+ Generator: true,
+ })
+
+ // A copy rule.
+ Cp = pctx.StaticRule("Cp",
+ blueprint.RuleParams{
+ Command: "cp $cpPreserveSymlinks $cpFlags $in $out",
+ Description: "cp $out",
+ },
+ "cpFlags")
+
+ // A symlink rule.
+ Symlink = pctx.StaticRule("Symlink",
+ blueprint.RuleParams{
+ Command: "ln -f -s $fromPath $out",
+ Description: "symlink $out",
+ },
+ "fromPath")
+
+ ErrorRule = pctx.StaticRule("Error",
+ blueprint.RuleParams{
+ Command: `echo "$error" && false`,
+ Description: "error building $out",
+ },
+ "error")
+)
+
+func init() {
+ pctx.Import("github.com/google/blueprint/bootstrap")
+}
diff --git a/android/env.go b/android/env.go
new file mode 100644
index 00000000..f9d8030a
--- /dev/null
+++ b/android/env.go
@@ -0,0 +1,55 @@
+// 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 android
+
+import (
+ "android/soong"
+ "android/soong/env"
+
+ "github.com/google/blueprint"
+)
+
+// This file supports dependencies on environment variables. During build manifest generation,
+// any dependency on an environment variable is added to a list. During the singleton phase
+// a JSON file is written containing the current value of all used environment variables.
+// The next time the top-level build script is run, it uses the soong_env executable to
+// compare the contents of the environment variables, rewriting the file if necessary to cause
+// a manifest regeneration.
+
+func init() {
+ soong.RegisterSingletonType("env", EnvSingleton)
+}
+
+func EnvSingleton() blueprint.Singleton {
+ return &envSingleton{}
+}
+
+type envSingleton struct{}
+
+func (c *envSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+ envDeps := ctx.Config().(Config).EnvDeps()
+
+ envFile := PathForOutput(ctx, ".soong.environment")
+ if ctx.Failed() {
+ return
+ }
+
+ err := env.WriteEnvFile(envFile.String(), envDeps)
+ if err != nil {
+ ctx.Errorf(err.Error())
+ }
+
+ ctx.AddNinjaFileDeps(envFile.String())
+}
diff --git a/android/glob.go b/android/glob.go
new file mode 100644
index 00000000..aec5fa9a
--- /dev/null
+++ b/android/glob.go
@@ -0,0 +1,120 @@
+// 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 android
+
+import (
+ "fmt"
+ "path/filepath"
+
+ "github.com/google/blueprint"
+
+ "android/soong/glob"
+)
+
+// This file supports globbing source files in Blueprints files.
+//
+// The build.ninja file needs to be regenerated any time a file matching the glob is added
+// or removed. The naive solution is to have the build.ninja file depend on all the
+// traversed directories, but this will cause the regeneration step to run every time a
+// non-matching file is added to a traversed directory, including backup files created by
+// editors.
+//
+// The solution implemented here optimizes out regenerations when the directory modifications
+// don't match the glob by having the build.ninja file depend on an intermedate file that
+// is only updated when a file matching the glob is added or removed. The intermediate file
+// depends on the traversed directories via a depfile. The depfile is used to avoid build
+// errors if a directory is deleted - a direct dependency on the deleted directory would result
+// in a build failure with a "missing and no known rule to make it" error.
+
+var (
+ globCmd = filepath.Join("${bootstrap.BinDir}", "soong_glob")
+
+ // globRule rule traverses directories to produce a list of files that match $glob
+ // and writes it to $out if it has changed, and writes the directories to $out.d
+ globRule = pctx.StaticRule("globRule",
+ blueprint.RuleParams{
+ Command: fmt.Sprintf(`%s -o $out $excludes "$glob"`, globCmd),
+ CommandDeps: []string{globCmd},
+ Description: "glob $glob",
+
+ Restat: true,
+ Deps: blueprint.DepsGCC,
+ Depfile: "$out.d",
+ },
+ "glob", "excludes")
+)
+
+func hasGlob(in []string) bool {
+ for _, s := range in {
+ if glob.IsGlob(s) {
+ return true
+ }
+ }
+
+ return false
+}
+
+// The subset of ModuleContext and SingletonContext needed by Glob
+type globContext interface {
+ Build(pctx blueprint.PackageContext, params blueprint.BuildParams)
+ AddNinjaFileDeps(deps ...string)
+}
+
+func Glob(ctx globContext, outDir string, globPattern string, excludes []string) ([]string, error) {
+ fileListFile := filepath.Join(outDir, "glob", globToString(globPattern)+".glob")
+ depFile := fileListFile + ".d"
+
+ // Get a globbed file list, and write out fileListFile and depFile
+ files, err := glob.GlobWithDepFile(globPattern, fileListFile, depFile, excludes)
+ if err != nil {
+ return nil, err
+ }
+
+ GlobRule(ctx, globPattern, excludes, fileListFile, depFile)
+
+ // Make build.ninja depend on the fileListFile
+ ctx.AddNinjaFileDeps(fileListFile)
+
+ return files, nil
+}
+
+func GlobRule(ctx globContext, globPattern string, excludes []string,
+ fileListFile, depFile string) {
+
+ // Create a rule to rebuild fileListFile if a directory in depFile changes. fileListFile
+ // will only be rewritten if it has changed, preventing unnecesary build.ninja regenerations.
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: globRule,
+ Outputs: []string{fileListFile},
+ Args: map[string]string{
+ "glob": globPattern,
+ "excludes": JoinWithPrefixAndQuote(excludes, "-e "),
+ },
+ })
+}
+
+func globToString(glob string) string {
+ ret := ""
+ for _, c := range glob {
+ if c >= 'a' && c <= 'z' ||
+ c >= 'A' && c <= 'Z' ||
+ c >= '0' && c <= '9' ||
+ c == '_' || c == '-' || c == '/' {
+ ret += string(c)
+ }
+ }
+
+ return ret
+}
diff --git a/android/makevars.go b/android/makevars.go
new file mode 100644
index 00000000..d9c5c6d7
--- /dev/null
+++ b/android/makevars.go
@@ -0,0 +1,242 @@
+// Copyright 2016 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 android
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+
+ "android/soong"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+///////////////////////////////////////////////////////////////////////////////
+// Interface for other packages to use to declare make variables
+type MakeVarsContext interface {
+ Config() Config
+
+ // Verify the make variable matches the Soong version, fail the build
+ // if it does not. If the make variable is empty, just set it.
+ Strict(name, ninjaStr string)
+ // Check to see if the make variable matches the Soong version, warn if
+ // it does not. If the make variable is empty, just set it.
+ Check(name, ninjaStr string)
+
+ // These are equivalent to the above, but sort the make and soong
+ // variables before comparing them. They also show the unique entries
+ // in each list when displaying the difference, instead of the entire
+ // string.
+ StrictSorted(name, ninjaStr string)
+ CheckSorted(name, ninjaStr string)
+}
+
+type MakeVarsProvider func(ctx MakeVarsContext)
+
+func RegisterMakeVarsProvider(pctx blueprint.PackageContext, provider MakeVarsProvider) {
+ makeVarsProviders = append(makeVarsProviders, makeVarsProvider{pctx, provider})
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+func init() {
+ soong.RegisterSingletonType("makevars", makeVarsSingletonFunc)
+}
+
+func makeVarsSingletonFunc() blueprint.Singleton {
+ return &makeVarsSingleton{}
+}
+
+type makeVarsSingleton struct{}
+
+type makeVarsProvider struct {
+ pctx blueprint.PackageContext
+ call MakeVarsProvider
+}
+
+var makeVarsProviders []makeVarsProvider
+
+type makeVarsContext struct {
+ config Config
+ ctx blueprint.SingletonContext
+ pctx blueprint.PackageContext
+ vars []makeVarsVariable
+}
+
+var _ MakeVarsContext = &makeVarsContext{}
+
+type makeVarsVariable struct {
+ name string
+ value string
+ sort bool
+ strict bool
+}
+
+func (s *makeVarsSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+ config := ctx.Config().(Config)
+
+ if !config.EmbeddedInMake() {
+ return
+ }
+
+ outFile := PathForOutput(ctx, "make_vars"+proptools.String(config.ProductVariables.Make_suffix)+".mk").String()
+
+ if ctx.Failed() {
+ return
+ }
+
+ vars := []makeVarsVariable{}
+ for _, provider := range makeVarsProviders {
+ mctx := &makeVarsContext{
+ config: config,
+ ctx: ctx,
+ pctx: provider.pctx,
+ }
+
+ provider.call(mctx)
+
+ vars = append(vars, mctx.vars...)
+ }
+
+ if ctx.Failed() {
+ return
+ }
+
+ outBytes := s.writeVars(vars)
+
+ if _, err := os.Stat(outFile); err == nil {
+ if data, err := ioutil.ReadFile(outFile); err == nil {
+ if bytes.Equal(data, outBytes) {
+ return
+ }
+ }
+ }
+
+ if err := ioutil.WriteFile(outFile, outBytes, 0666); err != nil {
+ ctx.Errorf(err.Error())
+ }
+}
+
+func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {
+ buf := &bytes.Buffer{}
+
+ fmt.Fprintln(buf, `# Autogenerated file
+
+# Compares SOONG_$(1) against $(1), and warns if they are not equal.
+#
+# If the original variable is empty, then just set it to the SOONG_ version.
+#
+# $(1): Name of the variable to check
+# $(2): If not-empty, sort the values before comparing
+# $(3): Extra snippet to run if it does not match
+define soong-compare-var
+ifneq ($$($(1)),)
+ my_val_make := $(if $(2),$$(sort $$($(1))),$$($(1)))
+ my_val_soong := $(if $(2),$$(sort $$(SOONG_$(1))),$$(SOONG_$(1)))
+ ifneq ($$(my_val_make),$$(my_val_soong))
+ $$(warning $(1) does not match between Make and Soong:)
+ $(if $(2),$$(warning Make adds: $$(filter-out $$(my_val_soong),$$(my_val_make))),$$(warning Make : $$(my_val_make)))
+ $(if $(2),$$(warning Soong adds: $$(filter-out $$(my_val_make),$$(my_val_soong))),$$(warning Soong: $$(my_val_soong)))
+ $(3)
+ endif
+ my_val_make :=
+ my_val_soong :=
+else
+ $(1) := $$(SOONG_$(1))
+endif
+endef
+
+my_check_failed := false
+
+`)
+
+ // Write all the strict checks out first so that if one of them errors,
+ // we get all of the strict errors printed, but not the non-strict
+ // warnings.
+ for _, v := range vars {
+ if !v.strict {
+ continue
+ }
+
+ sort := ""
+ if v.sort {
+ sort = "true"
+ }
+
+ fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
+ fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s,my_check_failed := true))\n\n", v.name, sort)
+ }
+
+ fmt.Fprintln(buf, `
+ifneq ($(my_check_failed),false)
+ $(error Soong variable check failed)
+endif
+my_check_failed :=
+
+
+`)
+
+ for _, v := range vars {
+ if v.strict {
+ continue
+ }
+
+ sort := ""
+ if v.sort {
+ sort = "true"
+ }
+
+ fmt.Fprintf(buf, "SOONG_%s := %s\n", v.name, v.value)
+ fmt.Fprintf(buf, "$(eval $(call soong-compare-var,%s,%s))\n\n", v.name, sort)
+ }
+
+ fmt.Fprintln(buf, "\nsoong-compare-var :=")
+
+ return buf.Bytes()
+}
+
+func (c *makeVarsContext) Config() Config {
+ return c.config
+}
+
+func (c *makeVarsContext) addVariable(name, ninjaStr string, strict, sort bool) {
+ value, err := c.ctx.Eval(c.pctx, ninjaStr)
+ if err != nil {
+ c.ctx.Errorf(err.Error())
+ }
+ c.vars = append(c.vars, makeVarsVariable{
+ name: name,
+ value: value,
+ strict: strict,
+ sort: sort,
+ })
+}
+
+func (c *makeVarsContext) Strict(name, ninjaStr string) {
+ c.addVariable(name, ninjaStr, true, false)
+}
+func (c *makeVarsContext) StrictSorted(name, ninjaStr string) {
+ c.addVariable(name, ninjaStr, true, true)
+}
+
+func (c *makeVarsContext) Check(name, ninjaStr string) {
+ c.addVariable(name, ninjaStr, false, false)
+}
+func (c *makeVarsContext) CheckSorted(name, ninjaStr string) {
+ c.addVariable(name, ninjaStr, false, true)
+}
diff --git a/android/module.go b/android/module.go
new file mode 100644
index 00000000..08abf78a
--- /dev/null
+++ b/android/module.go
@@ -0,0 +1,695 @@
+// 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 android
+
+import (
+ "fmt"
+ "path/filepath"
+ "strings"
+
+ "android/soong"
+ "android/soong/glob"
+
+ "github.com/google/blueprint"
+)
+
+var (
+ DeviceSharedLibrary = "shared_library"
+ DeviceStaticLibrary = "static_library"
+ DeviceExecutable = "executable"
+ HostSharedLibrary = "host_shared_library"
+ HostStaticLibrary = "host_static_library"
+ HostExecutable = "host_executable"
+)
+
+type ModuleBuildParams struct {
+ Rule blueprint.Rule
+ Output WritablePath
+ Outputs WritablePaths
+ Input Path
+ Inputs Paths
+ Implicit Path
+ Implicits Paths
+ OrderOnly Paths
+ Default bool
+ Args map[string]string
+}
+
+type androidBaseContext interface {
+ Arch() Arch
+ HostOrDevice() HostOrDevice
+ HostType() HostType
+ Host() bool
+ Device() bool
+ Darwin() bool
+ Debug() bool
+ AConfig() Config
+ Proprietary() bool
+ InstallInData() bool
+}
+
+type BaseContext interface {
+ blueprint.BaseModuleContext
+ androidBaseContext
+}
+
+type ModuleContext interface {
+ blueprint.ModuleContext
+ androidBaseContext
+
+ // Similar to Build, but takes Paths instead of []string,
+ // and performs more verification.
+ ModuleBuild(pctx blueprint.PackageContext, params ModuleBuildParams)
+
+ ExpandSources(srcFiles, excludes []string) Paths
+ Glob(outDir, globPattern string, excludes []string) Paths
+
+ InstallFile(installPath OutputPath, srcPath Path, deps ...Path) OutputPath
+ InstallFileName(installPath OutputPath, name string, srcPath Path, deps ...Path) OutputPath
+ CheckbuildFile(srcPath Path)
+
+ AddMissingDependencies(deps []string)
+}
+
+type Module interface {
+ blueprint.Module
+
+ GenerateAndroidBuildActions(ModuleContext)
+
+ base() *ModuleBase
+ Enabled() bool
+ HostOrDevice() HostOrDevice
+ InstallInData() bool
+}
+
+type commonProperties struct {
+ Name string
+ Deps []string
+ Tags []string
+
+ // emit build rules for this module
+ Enabled *bool `android:"arch_variant"`
+
+ // control whether this module compiles for 32-bit, 64-bit, or both. Possible values
+ // are "32" (compile for 32-bit only), "64" (compile for 64-bit only), "both" (compile for both
+ // architectures), or "first" (compile for 64-bit on a 64-bit platform, and 32-bit on a 32-bit
+ // platform
+ Compile_multilib string
+
+ // whether this is a proprietary vendor module, and should be installed into /vendor
+ Proprietary bool
+
+ // Set by HostOrDeviceMutator
+ CompileHostOrDevice HostOrDevice `blueprint:"mutated"`
+
+ // Set by HostTypeMutator
+ CompileHostType HostType `blueprint:"mutated"`
+
+ // Set by ArchMutator
+ CompileArch Arch `blueprint:"mutated"`
+
+ // Set by InitAndroidModule
+ HostOrDeviceSupported HostOrDeviceSupported `blueprint:"mutated"`
+}
+
+type hostAndDeviceProperties struct {
+ Host_supported bool
+ Device_supported bool
+}
+
+type Multilib string
+
+const (
+ MultilibBoth Multilib = "both"
+ MultilibFirst Multilib = "first"
+ MultilibCommon Multilib = "common"
+ MultilibDefault Multilib = ""
+)
+
+func InitAndroidModule(m Module,
+ propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
+
+ base := m.base()
+ base.module = m
+
+ propertyStructs = append(propertyStructs, &base.commonProperties, &base.variableProperties)
+
+ return m, propertyStructs
+}
+
+func InitAndroidArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib,
+ propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
+
+ _, propertyStructs = InitAndroidModule(m, propertyStructs...)
+
+ base := m.base()
+ base.commonProperties.HostOrDeviceSupported = hod
+ base.commonProperties.Compile_multilib = string(defaultMultilib)
+
+ switch hod {
+ case HostAndDeviceSupported:
+ // Default to module to device supported, host not supported, can override in module
+ // properties
+ base.hostAndDeviceProperties.Device_supported = true
+ fallthrough
+ case HostAndDeviceDefault:
+ propertyStructs = append(propertyStructs, &base.hostAndDeviceProperties)
+ }
+
+ return InitArchModule(m, propertyStructs...)
+}
+
+// A AndroidModuleBase object contains the properties that are common to all Android
+// modules. It should be included as an anonymous field in every module
+// struct definition. InitAndroidModule should then be called from the module's
+// factory function, and the return values from InitAndroidModule should be
+// returned from the factory function.
+//
+// The AndroidModuleBase type is responsible for implementing the
+// GenerateBuildActions method to support the blueprint.Module interface. This
+// method will then call the module's GenerateAndroidBuildActions method once
+// for each build variant that is to be built. GenerateAndroidBuildActions is
+// passed a AndroidModuleContext rather than the usual blueprint.ModuleContext.
+// AndroidModuleContext exposes extra functionality specific to the Android build
+// system including details about the particular build variant that is to be
+// generated.
+//
+// For example:
+//
+// import (
+// "android/soong/common"
+// "github.com/google/blueprint"
+// )
+//
+// type myModule struct {
+// common.AndroidModuleBase
+// properties struct {
+// MyProperty string
+// }
+// }
+//
+// func NewMyModule() (blueprint.Module, []interface{}) {
+// m := &myModule{}
+// return common.InitAndroidModule(m, &m.properties)
+// }
+//
+// func (m *myModule) GenerateAndroidBuildActions(ctx common.AndroidModuleContext) {
+// // Get the CPU architecture for the current build variant.
+// variantArch := ctx.Arch()
+//
+// // ...
+// }
+type ModuleBase struct {
+ // Putting the curiously recurring thing pointing to the thing that contains
+ // the thing pattern to good use.
+ module Module
+
+ commonProperties commonProperties
+ variableProperties variableProperties
+ hostAndDeviceProperties hostAndDeviceProperties
+ generalProperties []interface{}
+ archProperties []*archProperties
+
+ noAddressSanitizer bool
+ installFiles Paths
+ checkbuildFiles Paths
+
+ // Used by buildTargetSingleton to create checkbuild and per-directory build targets
+ // Only set on the final variant of each module
+ installTarget string
+ checkbuildTarget string
+ blueprintDir string
+}
+
+func (a *ModuleBase) base() *ModuleBase {
+ return a
+}
+
+func (a *ModuleBase) SetHostOrDevice(hod HostOrDevice) {
+ a.commonProperties.CompileHostOrDevice = hod
+}
+
+func (a *ModuleBase) SetHostType(ht HostType) {
+ a.commonProperties.CompileHostType = ht
+}
+
+func (a *ModuleBase) SetArch(arch Arch) {
+ a.commonProperties.CompileArch = arch
+}
+
+func (a *ModuleBase) HostOrDevice() HostOrDevice {
+ return a.commonProperties.CompileHostOrDevice
+}
+
+func (a *ModuleBase) HostType() HostType {
+ return a.commonProperties.CompileHostType
+}
+
+func (a *ModuleBase) Host() bool {
+ return a.HostOrDevice().Host()
+}
+
+func (a *ModuleBase) Arch() Arch {
+ return a.commonProperties.CompileArch
+}
+
+func (a *ModuleBase) HostSupported() bool {
+ return a.commonProperties.HostOrDeviceSupported == HostSupported ||
+ a.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
+ a.hostAndDeviceProperties.Host_supported
+}
+
+func (a *ModuleBase) DeviceSupported() bool {
+ return a.commonProperties.HostOrDeviceSupported == DeviceSupported ||
+ a.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
+ a.hostAndDeviceProperties.Device_supported
+}
+
+func (a *ModuleBase) Enabled() bool {
+ if a.commonProperties.Enabled == nil {
+ if a.HostSupported() && a.HostOrDevice().Host() && a.HostType() == Windows {
+ return false
+ } else {
+ return true
+ }
+ }
+ return *a.commonProperties.Enabled
+}
+
+func (a *ModuleBase) computeInstallDeps(
+ ctx blueprint.ModuleContext) Paths {
+
+ result := Paths{}
+ ctx.VisitDepsDepthFirstIf(isFileInstaller,
+ func(m blueprint.Module) {
+ fileInstaller := m.(fileInstaller)
+ files := fileInstaller.filesToInstall()
+ result = append(result, files...)
+ })
+
+ return result
+}
+
+func (a *ModuleBase) filesToInstall() Paths {
+ return a.installFiles
+}
+
+func (p *ModuleBase) NoAddressSanitizer() bool {
+ return p.noAddressSanitizer
+}
+
+func (p *ModuleBase) InstallInData() bool {
+ return false
+}
+
+func (a *ModuleBase) generateModuleTarget(ctx blueprint.ModuleContext) {
+ if a != ctx.FinalModule().(Module).base() {
+ return
+ }
+
+ allInstalledFiles := Paths{}
+ allCheckbuildFiles := Paths{}
+ ctx.VisitAllModuleVariants(func(module blueprint.Module) {
+ a := module.(Module).base()
+ allInstalledFiles = append(allInstalledFiles, a.installFiles...)
+ allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...)
+ })
+
+ deps := []string{}
+
+ if len(allInstalledFiles) > 0 {
+ name := ctx.ModuleName() + "-install"
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: blueprint.Phony,
+ Outputs: []string{name},
+ Implicits: allInstalledFiles.Strings(),
+ Optional: ctx.Config().(Config).EmbeddedInMake(),
+ })
+ deps = append(deps, name)
+ a.installTarget = name
+ }
+
+ if len(allCheckbuildFiles) > 0 {
+ name := ctx.ModuleName() + "-checkbuild"
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: blueprint.Phony,
+ Outputs: []string{name},
+ Implicits: allCheckbuildFiles.Strings(),
+ Optional: true,
+ })
+ deps = append(deps, name)
+ a.checkbuildTarget = name
+ }
+
+ if len(deps) > 0 {
+ suffix := ""
+ if ctx.Config().(Config).EmbeddedInMake() {
+ suffix = "-soong"
+ }
+
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: blueprint.Phony,
+ Outputs: []string{ctx.ModuleName() + suffix},
+ Implicits: deps,
+ Optional: true,
+ })
+
+ a.blueprintDir = ctx.ModuleDir()
+ }
+}
+
+func (a *ModuleBase) androidBaseContextFactory(ctx blueprint.BaseModuleContext) androidBaseContextImpl {
+ return androidBaseContextImpl{
+ arch: a.commonProperties.CompileArch,
+ hod: a.commonProperties.CompileHostOrDevice,
+ ht: a.commonProperties.CompileHostType,
+ proprietary: a.commonProperties.Proprietary,
+ config: ctx.Config().(Config),
+ installInData: a.module.InstallInData(),
+ }
+}
+
+func (a *ModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) {
+ androidCtx := &androidModuleContext{
+ ModuleContext: ctx,
+ androidBaseContextImpl: a.androidBaseContextFactory(ctx),
+ installDeps: a.computeInstallDeps(ctx),
+ installFiles: a.installFiles,
+ missingDeps: ctx.GetMissingDependencies(),
+ }
+
+ if !a.Enabled() {
+ return
+ }
+
+ a.module.GenerateAndroidBuildActions(androidCtx)
+ if ctx.Failed() {
+ return
+ }
+
+ a.installFiles = append(a.installFiles, androidCtx.installFiles...)
+ a.checkbuildFiles = append(a.checkbuildFiles, androidCtx.checkbuildFiles...)
+
+ a.generateModuleTarget(ctx)
+ if ctx.Failed() {
+ return
+ }
+}
+
+type androidBaseContextImpl struct {
+ arch Arch
+ hod HostOrDevice
+ ht HostType
+ debug bool
+ config Config
+ proprietary bool
+ installInData bool
+}
+
+type androidModuleContext struct {
+ blueprint.ModuleContext
+ androidBaseContextImpl
+ installDeps Paths
+ installFiles Paths
+ checkbuildFiles Paths
+ missingDeps []string
+}
+
+func (a *androidModuleContext) ninjaError(outputs []string, err error) {
+ a.ModuleContext.Build(pctx, blueprint.BuildParams{
+ Rule: ErrorRule,
+ Outputs: outputs,
+ Optional: true,
+ Args: map[string]string{
+ "error": err.Error(),
+ },
+ })
+ return
+}
+
+func (a *androidModuleContext) Build(pctx blueprint.PackageContext, params blueprint.BuildParams) {
+ if a.missingDeps != nil && params.Rule != globRule {
+ a.ninjaError(params.Outputs, fmt.Errorf("module %s missing dependencies: %s\n",
+ a.ModuleName(), strings.Join(a.missingDeps, ", ")))
+ return
+ }
+
+ params.Optional = true
+ a.ModuleContext.Build(pctx, params)
+}
+
+func (a *androidModuleContext) ModuleBuild(pctx blueprint.PackageContext, params ModuleBuildParams) {
+ bparams := blueprint.BuildParams{
+ Rule: params.Rule,
+ Outputs: params.Outputs.Strings(),
+ Inputs: params.Inputs.Strings(),
+ Implicits: params.Implicits.Strings(),
+ OrderOnly: params.OrderOnly.Strings(),
+ Args: params.Args,
+ Optional: !params.Default,
+ }
+
+ if params.Output != nil {
+ bparams.Outputs = append(bparams.Outputs, params.Output.String())
+ }
+ if params.Input != nil {
+ bparams.Inputs = append(bparams.Inputs, params.Input.String())
+ }
+ if params.Implicit != nil {
+ bparams.Implicits = append(bparams.Implicits, params.Implicit.String())
+ }
+
+ if a.missingDeps != nil {
+ a.ninjaError(bparams.Outputs, fmt.Errorf("module %s missing dependencies: %s\n",
+ a.ModuleName(), strings.Join(a.missingDeps, ", ")))
+ return
+ }
+
+ a.ModuleContext.Build(pctx, bparams)
+}
+
+func (a *androidModuleContext) GetMissingDependencies() []string {
+ return a.missingDeps
+}
+
+func (a *androidModuleContext) AddMissingDependencies(deps []string) {
+ if deps != nil {
+ a.missingDeps = append(a.missingDeps, deps...)
+ }
+}
+
+func (a *androidBaseContextImpl) Arch() Arch {
+ return a.arch
+}
+
+func (a *androidBaseContextImpl) HostOrDevice() HostOrDevice {
+ return a.hod
+}
+
+func (a *androidBaseContextImpl) HostType() HostType {
+ return a.ht
+}
+
+func (a *androidBaseContextImpl) Host() bool {
+ return a.hod.Host()
+}
+
+func (a *androidBaseContextImpl) Device() bool {
+ return a.hod.Device()
+}
+
+func (a *androidBaseContextImpl) Darwin() bool {
+ return a.hod.Host() && a.ht == Darwin
+}
+
+func (a *androidBaseContextImpl) Debug() bool {
+ return a.debug
+}
+
+func (a *androidBaseContextImpl) AConfig() Config {
+ return a.config
+}
+
+func (a *androidBaseContextImpl) Proprietary() bool {
+ return a.proprietary
+}
+
+func (a *androidBaseContextImpl) InstallInData() bool {
+ return a.installInData
+}
+
+func (a *androidModuleContext) InstallFileName(installPath OutputPath, name string, srcPath Path,
+ deps ...Path) OutputPath {
+
+ fullInstallPath := installPath.Join(a, name)
+
+ if a.Host() || !a.AConfig().SkipDeviceInstall() {
+ deps = append(deps, a.installDeps...)
+
+ a.ModuleBuild(pctx, ModuleBuildParams{
+ Rule: Cp,
+ Output: fullInstallPath,
+ Input: srcPath,
+ OrderOnly: Paths(deps),
+ Default: !a.AConfig().EmbeddedInMake(),
+ })
+
+ a.installFiles = append(a.installFiles, fullInstallPath)
+ }
+ a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
+ return fullInstallPath
+}
+
+func (a *androidModuleContext) InstallFile(installPath OutputPath, srcPath Path, deps ...Path) OutputPath {
+ return a.InstallFileName(installPath, filepath.Base(srcPath.String()), srcPath, deps...)
+}
+
+func (a *androidModuleContext) CheckbuildFile(srcPath Path) {
+ a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
+}
+
+type fileInstaller interface {
+ filesToInstall() Paths
+}
+
+func isFileInstaller(m blueprint.Module) bool {
+ _, ok := m.(fileInstaller)
+ return ok
+}
+
+func isAndroidModule(m blueprint.Module) bool {
+ _, ok := m.(Module)
+ return ok
+}
+
+func findStringInSlice(str string, slice []string) int {
+ for i, s := range slice {
+ if s == str {
+ return i
+ }
+ }
+ return -1
+}
+
+func (ctx *androidModuleContext) ExpandSources(srcFiles, excludes []string) Paths {
+ prefix := PathForModuleSrc(ctx).String()
+ for i, e := range excludes {
+ j := findStringInSlice(e, srcFiles)
+ if j != -1 {
+ srcFiles = append(srcFiles[:j], srcFiles[j+1:]...)
+ }
+
+ excludes[i] = filepath.Join(prefix, e)
+ }
+
+ globbedSrcFiles := make(Paths, 0, len(srcFiles))
+ for _, s := range srcFiles {
+ if glob.IsGlob(s) {
+ globbedSrcFiles = append(globbedSrcFiles, ctx.Glob("src_glob", filepath.Join(prefix, s), excludes)...)
+ } else {
+ globbedSrcFiles = append(globbedSrcFiles, PathForModuleSrc(ctx, s))
+ }
+ }
+
+ return globbedSrcFiles
+}
+
+func (ctx *androidModuleContext) Glob(outDir, globPattern string, excludes []string) Paths {
+ ret, err := Glob(ctx, PathForModuleOut(ctx, outDir).String(), globPattern, excludes)
+ if err != nil {
+ ctx.ModuleErrorf("glob: %s", err.Error())
+ }
+ return pathsForModuleSrcFromFullPath(ctx, ret)
+}
+
+func init() {
+ soong.RegisterSingletonType("buildtarget", BuildTargetSingleton)
+}
+
+func BuildTargetSingleton() blueprint.Singleton {
+ return &buildTargetSingleton{}
+}
+
+type buildTargetSingleton struct{}
+
+func (c *buildTargetSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
+ checkbuildDeps := []string{}
+
+ dirModules := make(map[string][]string)
+
+ ctx.VisitAllModules(func(module blueprint.Module) {
+ if a, ok := module.(Module); ok {
+ blueprintDir := a.base().blueprintDir
+ installTarget := a.base().installTarget
+ checkbuildTarget := a.base().checkbuildTarget
+
+ if checkbuildTarget != "" {
+ checkbuildDeps = append(checkbuildDeps, checkbuildTarget)
+ dirModules[blueprintDir] = append(dirModules[blueprintDir], checkbuildTarget)
+ }
+
+ if installTarget != "" {
+ dirModules[blueprintDir] = append(dirModules[blueprintDir], installTarget)
+ }
+ }
+ })
+
+ suffix := ""
+ if ctx.Config().(Config).EmbeddedInMake() {
+ suffix = "-soong"
+ }
+
+ // Create a top-level checkbuild target that depends on all modules
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: blueprint.Phony,
+ Outputs: []string{"checkbuild" + suffix},
+ Implicits: checkbuildDeps,
+ Optional: true,
+ })
+
+ // Create a mm/<directory> target that depends on all modules in a directory
+ dirs := sortedKeys(dirModules)
+ for _, dir := range dirs {
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: blueprint.Phony,
+ Outputs: []string{filepath.Join("mm", dir)},
+ Implicits: dirModules[dir],
+ // HACK: checkbuild should be an optional build, but force it
+ // enabled for now in standalone builds
+ Optional: ctx.Config().(Config).EmbeddedInMake(),
+ })
+ }
+}
+
+type AndroidModulesByName struct {
+ slice []Module
+ ctx interface {
+ ModuleName(blueprint.Module) string
+ ModuleSubDir(blueprint.Module) string
+ }
+}
+
+func (s AndroidModulesByName) Len() int { return len(s.slice) }
+func (s AndroidModulesByName) Less(i, j int) bool {
+ mi, mj := s.slice[i], s.slice[j]
+ ni, nj := s.ctx.ModuleName(mi), s.ctx.ModuleName(mj)
+
+ if ni != nj {
+ return ni < nj
+ } else {
+ return s.ctx.ModuleSubDir(mi) < s.ctx.ModuleSubDir(mj)
+ }
+}
+func (s AndroidModulesByName) Swap(i, j int) { s.slice[i], s.slice[j] = s.slice[j], s.slice[i] }
diff --git a/android/mutator.go b/android/mutator.go
new file mode 100644
index 00000000..9405f2d2
--- /dev/null
+++ b/android/mutator.go
@@ -0,0 +1,69 @@
+// 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 android
+
+import (
+ "android/soong"
+
+ "github.com/google/blueprint"
+)
+
+type AndroidTopDownMutator func(TopDownMutatorContext)
+
+type TopDownMutatorContext interface {
+ blueprint.TopDownMutatorContext
+ androidBaseContext
+}
+
+type androidTopDownMutatorContext struct {
+ blueprint.TopDownMutatorContext
+ androidBaseContextImpl
+}
+
+type AndroidBottomUpMutator func(BottomUpMutatorContext)
+
+type BottomUpMutatorContext interface {
+ blueprint.BottomUpMutatorContext
+ androidBaseContext
+}
+
+type androidBottomUpMutatorContext struct {
+ blueprint.BottomUpMutatorContext
+ androidBaseContextImpl
+}
+
+func RegisterBottomUpMutator(name string, mutator AndroidBottomUpMutator) {
+ soong.RegisterBottomUpMutator(name, func(ctx blueprint.BottomUpMutatorContext) {
+ if a, ok := ctx.Module().(Module); ok {
+ actx := &androidBottomUpMutatorContext{
+ BottomUpMutatorContext: ctx,
+ androidBaseContextImpl: a.base().androidBaseContextFactory(ctx),
+ }
+ mutator(actx)
+ }
+ })
+}
+
+func RegisterTopDownMutator(name string, mutator AndroidTopDownMutator) {
+ soong.RegisterTopDownMutator(name, func(ctx blueprint.TopDownMutatorContext) {
+ if a, ok := ctx.Module().(Module); ok {
+ actx := &androidTopDownMutatorContext{
+ TopDownMutatorContext: ctx,
+ androidBaseContextImpl: a.base().androidBaseContextFactory(ctx),
+ }
+ mutator(actx)
+ }
+ })
+}
diff --git a/android/package_ctx.go b/android/package_ctx.go
new file mode 100644
index 00000000..56ba2d82
--- /dev/null
+++ b/android/package_ctx.go
@@ -0,0 +1,133 @@
+// 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 android
+
+import (
+ "fmt"
+
+ "github.com/google/blueprint"
+)
+
+// AndroidPackageContext is a wrapper for blueprint.PackageContext that adds
+// some android-specific helper functions.
+type AndroidPackageContext struct {
+ blueprint.PackageContext
+}
+
+func NewPackageContext(pkgPath string) AndroidPackageContext {
+ return AndroidPackageContext{blueprint.NewPackageContext(pkgPath)}
+}
+
+// configErrorWrapper can be used with Path functions when a Context is not
+// available. A Config can be provided, and errors are stored as a list for
+// later retrieval.
+//
+// The most common use here will be with VariableFunc, where only a config is
+// provided, and an error should be returned.
+type configErrorWrapper struct {
+ pctx AndroidPackageContext
+ config Config
+ errors []error
+}
+
+var _ PathContext = &configErrorWrapper{}
+var _ errorfContext = &configErrorWrapper{}
+
+func (e *configErrorWrapper) Config() interface{} {
+ return e.config
+}
+func (e *configErrorWrapper) Errorf(format string, args ...interface{}) {
+ e.errors = append(e.errors, fmt.Errorf(format, args...))
+}
+func (e *configErrorWrapper) AddNinjaFileDeps(deps ...string) {
+ e.pctx.AddNinjaFileDeps(deps...)
+}
+
+// SourcePathVariable returns a Variable whose value is the source directory
+// appended with the supplied path. It may only be called during a Go package's
+// initialization - either from the init() function or as part of a
+// package-scoped variable's initialization.
+func (p AndroidPackageContext) SourcePathVariable(name, path string) blueprint.Variable {
+ return p.VariableFunc(name, func(config interface{}) (string, error) {
+ ctx := &configErrorWrapper{p, config.(Config), []error{}}
+ p := safePathForSource(ctx, path)
+ if len(ctx.errors) > 0 {
+ return "", ctx.errors[0]
+ }
+ return p.String(), nil
+ })
+}
+
+// HostBinVariable returns a Variable whose value is the path to a host tool
+// in the bin directory for host targets. It may only be called during a Go
+// package's initialization - either from the init() function or as part of a
+// package-scoped variable's initialization.
+func (p AndroidPackageContext) HostBinToolVariable(name, path string) blueprint.Variable {
+ return p.VariableFunc(name, func(config interface{}) (string, error) {
+ ctx := &configErrorWrapper{p, config.(Config), []error{}}
+ p := PathForOutput(ctx, "host", ctx.config.PrebuiltOS(), "bin", path)
+ if len(ctx.errors) > 0 {
+ return "", ctx.errors[0]
+ }
+ return p.String(), nil
+ })
+}
+
+// HostJavaToolVariable returns a Variable whose value is the path to a host
+// tool in the frameworks directory for host targets. It may only be called
+// during a Go package's initialization - either from the init() function or as
+// part of a package-scoped variable's initialization.
+func (p AndroidPackageContext) HostJavaToolVariable(name, path string) blueprint.Variable {
+ return p.VariableFunc(name, func(config interface{}) (string, error) {
+ ctx := &configErrorWrapper{p, config.(Config), []error{}}
+ p := PathForOutput(ctx, "host", ctx.config.PrebuiltOS(), "framework", path)
+ if len(ctx.errors) > 0 {
+ return "", ctx.errors[0]
+ }
+ return p.String(), nil
+ })
+}
+
+// IntermediatesPathVariable returns a Variable whose value is the intermediate
+// directory appended with the supplied path. It may only be called during a Go
+// package's initialization - either from the init() function or as part of a
+// package-scoped variable's initialization.
+func (p AndroidPackageContext) IntermediatesPathVariable(name, path string) blueprint.Variable {
+ return p.VariableFunc(name, func(config interface{}) (string, error) {
+ ctx := &configErrorWrapper{p, config.(Config), []error{}}
+ p := PathForIntermediates(ctx, path)
+ if len(ctx.errors) > 0 {
+ return "", ctx.errors[0]
+ }
+ return p.String(), nil
+ })
+}
+
+// PrefixedPathsForOptionalSourceVariable returns a Variable whose value is the
+// list of present source paths prefixed with the supplied prefix. It may only
+// be called during a Go package's initialization - either from the init()
+// function or as part of a package-scoped variable's initialization.
+func (p AndroidPackageContext) PrefixedPathsForOptionalSourceVariable(
+ name, prefix string, paths []string) blueprint.Variable {
+
+ return p.VariableFunc(name, func(config interface{}) (string, error) {
+ ctx := &configErrorWrapper{p, config.(Config), []error{}}
+ paths := PathsForOptionalSource(ctx, "", paths)
+ if len(ctx.errors) > 0 {
+ return "", ctx.errors[0]
+ }
+ return JoinWithPrefix(paths.Strings(), prefix), nil
+ })
+}
diff --git a/android/paths.go b/android/paths.go
new file mode 100644
index 00000000..910ebd28
--- /dev/null
+++ b/android/paths.go
@@ -0,0 +1,668 @@
+// 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 android
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "reflect"
+ "strings"
+
+ "android/soong/glob"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/pathtools"
+)
+
+// PathContext is the subset of a (Module|Singleton)Context required by the
+// Path methods.
+type PathContext interface {
+ Config() interface{}
+ AddNinjaFileDeps(deps ...string)
+}
+
+var _ PathContext = blueprint.SingletonContext(nil)
+var _ PathContext = blueprint.ModuleContext(nil)
+
+// errorfContext is the interface containing the Errorf method matching the
+// Errorf method in blueprint.SingletonContext.
+type errorfContext interface {
+ Errorf(format string, args ...interface{})
+}
+
+var _ errorfContext = blueprint.SingletonContext(nil)
+
+// moduleErrorf is the interface containing the ModuleErrorf method matching
+// the ModuleErrorf method in blueprint.ModuleContext.
+type moduleErrorf interface {
+ ModuleErrorf(format string, args ...interface{})
+}
+
+var _ moduleErrorf = blueprint.ModuleContext(nil)
+
+// pathConfig returns the android Config interface associated to the context.
+// Panics if the context isn't affiliated with an android build.
+func pathConfig(ctx PathContext) Config {
+ if ret, ok := ctx.Config().(Config); ok {
+ return ret
+ }
+ panic("Paths may only be used on Soong builds")
+}
+
+// reportPathError will register an error with the attached context. It
+// attempts ctx.ModuleErrorf for a better error message first, then falls
+// back to ctx.Errorf.
+func reportPathError(ctx PathContext, format string, args ...interface{}) {
+ if mctx, ok := ctx.(moduleErrorf); ok {
+ mctx.ModuleErrorf(format, args...)
+ } else if ectx, ok := ctx.(errorfContext); ok {
+ ectx.Errorf(format, args...)
+ } else {
+ panic(fmt.Sprintf(format, args...))
+ }
+}
+
+type Path interface {
+ // Returns the path in string form
+ String() string
+
+ // Returns the current file extension of the path
+ Ext() string
+}
+
+// WritablePath is a type of path that can be used as an output for build rules.
+type WritablePath interface {
+ Path
+
+ writablePath()
+}
+
+type genPathProvider interface {
+ genPathWithExt(ctx ModuleContext, ext string) ModuleGenPath
+}
+type objPathProvider interface {
+ objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath
+}
+type resPathProvider interface {
+ resPathWithName(ctx ModuleContext, name string) ModuleResPath
+}
+
+// GenPathWithExt derives a new file path in ctx's generated sources directory
+// from the current path, but with the new extension.
+func GenPathWithExt(ctx ModuleContext, p Path, ext string) ModuleGenPath {
+ if path, ok := p.(genPathProvider); ok {
+ return path.genPathWithExt(ctx, ext)
+ }
+ reportPathError(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p)
+ return PathForModuleGen(ctx)
+}
+
+// ObjPathWithExt derives a new file path in ctx's object directory from the
+// current path, but with the new extension.
+func ObjPathWithExt(ctx ModuleContext, p Path, subdir, ext string) ModuleObjPath {
+ if path, ok := p.(objPathProvider); ok {
+ return path.objPathWithExt(ctx, subdir, ext)
+ }
+ reportPathError(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
+ return PathForModuleObj(ctx)
+}
+
+// ResPathWithName derives a new path in ctx's output resource directory, using
+// the current path to create the directory name, and the `name` argument for
+// the filename.
+func ResPathWithName(ctx ModuleContext, p Path, name string) ModuleResPath {
+ if path, ok := p.(resPathProvider); ok {
+ return path.resPathWithName(ctx, name)
+ }
+ reportPathError(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p)
+ return PathForModuleRes(ctx)
+}
+
+// OptionalPath is a container that may or may not contain a valid Path.
+type OptionalPath struct {
+ valid bool
+ path Path
+}
+
+// OptionalPathForPath returns an OptionalPath containing the path.
+func OptionalPathForPath(path Path) OptionalPath {
+ if path == nil {
+ return OptionalPath{}
+ }
+ return OptionalPath{valid: true, path: path}
+}
+
+// Valid returns whether there is a valid path
+func (p OptionalPath) Valid() bool {
+ return p.valid
+}
+
+// Path returns the Path embedded in this OptionalPath. You must be sure that
+// there is a valid path, since this method will panic if there is not.
+func (p OptionalPath) Path() Path {
+ if !p.valid {
+ panic("Requesting an invalid path")
+ }
+ return p.path
+}
+
+// String returns the string version of the Path, or "" if it isn't valid.
+func (p OptionalPath) String() string {
+ if p.valid {
+ return p.path.String()
+ } else {
+ return ""
+ }
+}
+
+// Paths is a slice of Path objects, with helpers to operate on the collection.
+type Paths []Path
+
+// PathsForSource returns Paths rooted from SrcDir
+func PathsForSource(ctx PathContext, paths []string) Paths {
+ if pathConfig(ctx).AllowMissingDependencies() {
+ if modCtx, ok := ctx.(ModuleContext); ok {
+ ret := make(Paths, 0, len(paths))
+ intermediates := filepath.Join(modCtx.ModuleDir(), modCtx.ModuleName(), modCtx.ModuleSubDir(), "missing")
+ for _, path := range paths {
+ p := OptionalPathForSource(ctx, intermediates, path)
+ if p.Valid() {
+ ret = append(ret, p.Path())
+ } else {
+ modCtx.AddMissingDependencies([]string{path})
+ }
+ }
+ return ret
+ }
+ }
+ ret := make(Paths, len(paths))
+ for i, path := range paths {
+ ret[i] = PathForSource(ctx, path)
+ }
+ return ret
+}
+
+// PathsForOptionalSource returns a list of Paths rooted from SrcDir that are
+// found in the tree. If any are not found, they are omitted from the list,
+// and dependencies are added so that we're re-run when they are added.
+func PathsForOptionalSource(ctx PathContext, intermediates string, paths []string) Paths {
+ ret := make(Paths, 0, len(paths))
+ for _, path := range paths {
+ p := OptionalPathForSource(ctx, intermediates, path)
+ if p.Valid() {
+ ret = append(ret, p.Path())
+ }
+ }
+ return ret
+}
+
+// PathsForModuleSrc returns Paths rooted from the module's local source
+// directory
+func PathsForModuleSrc(ctx ModuleContext, paths []string) Paths {
+ ret := make(Paths, len(paths))
+ for i, path := range paths {
+ ret[i] = PathForModuleSrc(ctx, path)
+ }
+ return ret
+}
+
+// pathsForModuleSrcFromFullPath returns Paths rooted from the module's local
+// source directory, but strip the local source directory from the beginning of
+// each string.
+func pathsForModuleSrcFromFullPath(ctx ModuleContext, paths []string) Paths {
+ prefix := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir()) + "/"
+ ret := make(Paths, 0, len(paths))
+ for _, p := range paths {
+ path := filepath.Clean(p)
+ if !strings.HasPrefix(path, prefix) {
+ reportPathError(ctx, "Path '%s' is not in module source directory '%s'", p, prefix)
+ continue
+ }
+ ret = append(ret, PathForModuleSrc(ctx, path[len(prefix):]))
+ }
+ return ret
+}
+
+// PathsWithOptionalDefaultForModuleSrc returns Paths rooted from the module's
+// local source directory. If none are provided, use the default if it exists.
+func PathsWithOptionalDefaultForModuleSrc(ctx ModuleContext, input []string, def string) Paths {
+ if len(input) > 0 {
+ return PathsForModuleSrc(ctx, input)
+ }
+ // Use Glob so that if the default doesn't exist, a dependency is added so that when it
+ // is created, we're run again.
+ path := filepath.Join(ctx.AConfig().srcDir, ctx.ModuleDir(), def)
+ return ctx.Glob("default", path, []string{})
+}
+
+// Strings returns the Paths in string form
+func (p Paths) Strings() []string {
+ if p == nil {
+ return nil
+ }
+ ret := make([]string, len(p))
+ for i, path := range p {
+ ret[i] = path.String()
+ }
+ return ret
+}
+
+// WritablePaths is a slice of WritablePaths, used for multiple outputs.
+type WritablePaths []WritablePath
+
+// Strings returns the string forms of the writable paths.
+func (p WritablePaths) Strings() []string {
+ if p == nil {
+ return nil
+ }
+ ret := make([]string, len(p))
+ for i, path := range p {
+ ret[i] = path.String()
+ }
+ return ret
+}
+
+type basePath struct {
+ path string
+ config Config
+}
+
+func (p basePath) Ext() string {
+ return filepath.Ext(p.path)
+}
+
+// SourcePath is a Path representing a file path rooted from SrcDir
+type SourcePath struct {
+ basePath
+}
+
+var _ Path = SourcePath{}
+
+// safePathForSource is for paths that we expect are safe -- only for use by go
+// code that is embedding ninja variables in paths
+func safePathForSource(ctx PathContext, path string) SourcePath {
+ p := validateSafePath(ctx, path)
+ ret := SourcePath{basePath{p, pathConfig(ctx)}}
+
+ abs, err := filepath.Abs(ret.String())
+ if err != nil {
+ reportPathError(ctx, "%s", err.Error())
+ return ret
+ }
+ buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
+ if err != nil {
+ reportPathError(ctx, "%s", err.Error())
+ return ret
+ }
+ if strings.HasPrefix(abs, buildroot) {
+ reportPathError(ctx, "source path %s is in output", abs)
+ return ret
+ }
+
+ return ret
+}
+
+// PathForSource returns a SourcePath for the provided paths... (which are
+// joined together with filepath.Join). This also validates that the path
+// doesn't escape the source dir, or is contained in the build dir. On error, it
+// will return a usable, but invalid SourcePath, and report a ModuleError.
+func PathForSource(ctx PathContext, paths ...string) SourcePath {
+ p := validatePath(ctx, paths...)
+ ret := SourcePath{basePath{p, pathConfig(ctx)}}
+
+ abs, err := filepath.Abs(ret.String())
+ if err != nil {
+ reportPathError(ctx, "%s", err.Error())
+ return ret
+ }
+ buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
+ if err != nil {
+ reportPathError(ctx, "%s", err.Error())
+ return ret
+ }
+ if strings.HasPrefix(abs, buildroot) {
+ reportPathError(ctx, "source path %s is in output", abs)
+ return ret
+ }
+
+ if _, err = os.Stat(ret.String()); err != nil {
+ if os.IsNotExist(err) {
+ reportPathError(ctx, "source path %s does not exist", ret)
+ } else {
+ reportPathError(ctx, "%s: %s", ret, err.Error())
+ }
+ }
+ return ret
+}
+
+// OptionalPathForSource returns an OptionalPath with the SourcePath if the
+// path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added
+// so that the ninja file will be regenerated if the state of the path changes.
+func OptionalPathForSource(ctx PathContext, intermediates string, paths ...string) OptionalPath {
+ if len(paths) == 0 {
+ // For when someone forgets the 'intermediates' argument
+ panic("Missing path(s)")
+ }
+
+ p := validatePath(ctx, paths...)
+ path := SourcePath{basePath{p, pathConfig(ctx)}}
+
+ abs, err := filepath.Abs(path.String())
+ if err != nil {
+ reportPathError(ctx, "%s", err.Error())
+ return OptionalPath{}
+ }
+ buildroot, err := filepath.Abs(pathConfig(ctx).buildDir)
+ if err != nil {
+ reportPathError(ctx, "%s", err.Error())
+ return OptionalPath{}
+ }
+ if strings.HasPrefix(abs, buildroot) {
+ reportPathError(ctx, "source path %s is in output", abs)
+ return OptionalPath{}
+ }
+
+ if glob.IsGlob(path.String()) {
+ reportPathError(ctx, "path may not contain a glob: %s", path.String())
+ return OptionalPath{}
+ }
+
+ if gctx, ok := ctx.(globContext); ok {
+ // Use glob to produce proper dependencies, even though we only want
+ // a single file.
+ files, err := Glob(gctx, PathForIntermediates(ctx, intermediates).String(), path.String(), nil)
+ if err != nil {
+ reportPathError(ctx, "glob: %s", err.Error())
+ return OptionalPath{}
+ }
+
+ if len(files) == 0 {
+ return OptionalPath{}
+ }
+ } else {
+ // We cannot add build statements in this context, so we fall back to
+ // AddNinjaFileDeps
+ files, dirs, err := pathtools.Glob(path.String())
+ if err != nil {
+ reportPathError(ctx, "glob: %s", err.Error())
+ return OptionalPath{}
+ }
+
+ ctx.AddNinjaFileDeps(dirs...)
+
+ if len(files) == 0 {
+ return OptionalPath{}
+ }
+
+ ctx.AddNinjaFileDeps(path.String())
+ }
+ return OptionalPathForPath(path)
+}
+
+func (p SourcePath) String() string {
+ return filepath.Join(p.config.srcDir, p.path)
+}
+
+// Join creates a new SourcePath with paths... joined with the current path. The
+// provided paths... may not use '..' to escape from the current path.
+func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
+ path := validatePath(ctx, paths...)
+ return PathForSource(ctx, p.path, path)
+}
+
+// OverlayPath returns the overlay for `path' if it exists. This assumes that the
+// SourcePath is the path to a resource overlay directory.
+func (p SourcePath) OverlayPath(ctx ModuleContext, path Path) OptionalPath {
+ var relDir string
+ if moduleSrcPath, ok := path.(ModuleSrcPath); ok {
+ relDir = moduleSrcPath.sourcePath.path
+ } else if srcPath, ok := path.(SourcePath); ok {
+ relDir = srcPath.path
+ } else {
+ reportPathError(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
+ return OptionalPath{}
+ }
+ dir := filepath.Join(p.config.srcDir, p.path, relDir)
+ // Use Glob so that we are run again if the directory is added.
+ if glob.IsGlob(dir) {
+ reportPathError(ctx, "Path may not contain a glob: %s", dir)
+ }
+ paths, err := Glob(ctx, PathForModuleOut(ctx, "overlay").String(), dir, []string{})
+ if err != nil {
+ reportPathError(ctx, "glob: %s", err.Error())
+ return OptionalPath{}
+ }
+ if len(paths) == 0 {
+ return OptionalPath{}
+ }
+ relPath, err := filepath.Rel(p.config.srcDir, paths[0])
+ if err != nil {
+ reportPathError(ctx, "%s", err.Error())
+ return OptionalPath{}
+ }
+ return OptionalPathForPath(PathForSource(ctx, relPath))
+}
+
+// OutputPath is a Path representing a file path rooted from the build directory
+type OutputPath struct {
+ basePath
+}
+
+var _ Path = OutputPath{}
+
+// PathForOutput returns an OutputPath for the provided paths... (which are
+// joined together with filepath.Join). This also validates that the path
+// does not escape the build dir. On error, it will return a usable, but invalid
+// OutputPath, and report a ModuleError.
+func PathForOutput(ctx PathContext, paths ...string) OutputPath {
+ path := validatePath(ctx, paths...)
+ return OutputPath{basePath{path, pathConfig(ctx)}}
+}
+
+func (p OutputPath) writablePath() {}
+
+func (p OutputPath) String() string {
+ return filepath.Join(p.config.buildDir, p.path)
+}
+
+func (p OutputPath) RelPathString() string {
+ return p.path
+}
+
+// Join creates a new OutputPath with paths... joined with the current path. The
+// provided paths... may not use '..' to escape from the current path.
+func (p OutputPath) Join(ctx PathContext, paths ...string) OutputPath {
+ path := validatePath(ctx, paths...)
+ return PathForOutput(ctx, p.path, path)
+}
+
+// PathForIntermediates returns an OutputPath representing the top-level
+// intermediates directory.
+func PathForIntermediates(ctx PathContext, paths ...string) OutputPath {
+ path := validatePath(ctx, paths...)
+ return PathForOutput(ctx, ".intermediates", path)
+}
+
+// ModuleSrcPath is a Path representing a file rooted from a module's local source dir
+type ModuleSrcPath struct {
+ basePath
+ sourcePath SourcePath
+ moduleDir string
+}
+
+var _ Path = ModuleSrcPath{}
+var _ genPathProvider = ModuleSrcPath{}
+var _ objPathProvider = ModuleSrcPath{}
+var _ resPathProvider = ModuleSrcPath{}
+
+// PathForModuleSrc returns a ModuleSrcPath representing the paths... under the
+// module's local source directory.
+func PathForModuleSrc(ctx ModuleContext, paths ...string) ModuleSrcPath {
+ path := validatePath(ctx, paths...)
+ return ModuleSrcPath{basePath{path, ctx.AConfig()}, PathForSource(ctx, ctx.ModuleDir(), path), ctx.ModuleDir()}
+}
+
+// OptionalPathForModuleSrc returns an OptionalPath. The OptionalPath contains a
+// valid path if p is non-nil.
+func OptionalPathForModuleSrc(ctx ModuleContext, p *string) OptionalPath {
+ if p == nil {
+ return OptionalPath{}
+ }
+ return OptionalPathForPath(PathForModuleSrc(ctx, *p))
+}
+
+func (p ModuleSrcPath) String() string {
+ return p.sourcePath.String()
+}
+
+func (p ModuleSrcPath) genPathWithExt(ctx ModuleContext, ext string) ModuleGenPath {
+ return PathForModuleGen(ctx, p.moduleDir, pathtools.ReplaceExtension(p.path, ext))
+}
+
+func (p ModuleSrcPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
+ return PathForModuleObj(ctx, subdir, p.moduleDir, pathtools.ReplaceExtension(p.path, ext))
+}
+
+func (p ModuleSrcPath) resPathWithName(ctx ModuleContext, name string) ModuleResPath {
+ // TODO: Use full directory if the new ctx is not the current ctx?
+ return PathForModuleRes(ctx, p.path, name)
+}
+
+// ModuleOutPath is a Path representing a module's output directory.
+type ModuleOutPath struct {
+ OutputPath
+}
+
+var _ Path = ModuleOutPath{}
+
+// PathForModuleOut returns a Path representing the paths... under the module's
+// output directory.
+func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath {
+ p := validatePath(ctx, paths...)
+ return ModuleOutPath{PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), p)}
+}
+
+// ModuleGenPath is a Path representing the 'gen' directory in a module's output
+// directory. Mainly used for generated sources.
+type ModuleGenPath struct {
+ ModuleOutPath
+ path string
+}
+
+var _ Path = ModuleGenPath{}
+var _ genPathProvider = ModuleGenPath{}
+var _ objPathProvider = ModuleGenPath{}
+
+// PathForModuleGen returns a Path representing the paths... under the module's
+// `gen' directory.
+func PathForModuleGen(ctx ModuleContext, paths ...string) ModuleGenPath {
+ p := validatePath(ctx, paths...)
+ return ModuleGenPath{
+ PathForModuleOut(ctx, "gen", p),
+ p,
+ }
+}
+
+func (p ModuleGenPath) genPathWithExt(ctx ModuleContext, ext string) ModuleGenPath {
+ // TODO: make a different path for local vs remote generated files?
+ return PathForModuleGen(ctx, pathtools.ReplaceExtension(p.path, ext))
+}
+
+func (p ModuleGenPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
+ return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
+}
+
+// ModuleObjPath is a Path representing the 'obj' directory in a module's output
+// directory. Used for compiled objects.
+type ModuleObjPath struct {
+ ModuleOutPath
+}
+
+var _ Path = ModuleObjPath{}
+
+// PathForModuleObj returns a Path representing the paths... under the module's
+// 'obj' directory.
+func PathForModuleObj(ctx ModuleContext, paths ...string) ModuleObjPath {
+ p := validatePath(ctx, paths...)
+ return ModuleObjPath{PathForModuleOut(ctx, "obj", p)}
+}
+
+// ModuleResPath is a a Path representing the 'res' directory in a module's
+// output directory.
+type ModuleResPath struct {
+ ModuleOutPath
+}
+
+var _ Path = ModuleResPath{}
+
+// PathForModuleRes returns a Path representing the paths... under the module's
+// 'res' directory.
+func PathForModuleRes(ctx ModuleContext, paths ...string) ModuleResPath {
+ p := validatePath(ctx, paths...)
+ return ModuleResPath{PathForModuleOut(ctx, "res", p)}
+}
+
+// PathForModuleInstall returns a Path representing the install path for the
+// module appended with paths...
+func PathForModuleInstall(ctx ModuleContext, paths ...string) OutputPath {
+ var outPaths []string
+ if ctx.Device() {
+ partition := "system"
+ if ctx.Proprietary() {
+ partition = "vendor"
+ }
+ if ctx.InstallInData() {
+ partition = "data"
+ }
+ outPaths = []string{"target", "product", ctx.AConfig().DeviceName(), partition}
+ } else {
+ outPaths = []string{"host", ctx.HostType().String() + "-x86"}
+ }
+ if ctx.Debug() {
+ outPaths = append([]string{"debug"}, outPaths...)
+ }
+ outPaths = append(outPaths, paths...)
+ return PathForOutput(ctx, outPaths...)
+}
+
+// validateSafePath validates a path that we trust (may contain ninja variables).
+// Ensures that each path component does not attempt to leave its component.
+func validateSafePath(ctx PathContext, paths ...string) string {
+ for _, path := range paths {
+ path := filepath.Clean(path)
+ if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") {
+ reportPathError(ctx, "Path is outside directory: %s", path)
+ return ""
+ }
+ }
+ // TODO: filepath.Join isn't necessarily correct with embedded ninja
+ // variables. '..' may remove the entire ninja variable, even if it
+ // will be expanded to multiple nested directories.
+ return filepath.Join(paths...)
+}
+
+// validatePath validates that a path does not include ninja variables, and that
+// each path component does not attempt to leave its component. Returns a joined
+// version of each path component.
+func validatePath(ctx PathContext, paths ...string) string {
+ for _, path := range paths {
+ if strings.Contains(path, "$") {
+ reportPathError(ctx, "Path contains invalid character($): %s", path)
+ return ""
+ }
+ }
+ return validateSafePath(ctx, paths...)
+}
diff --git a/android/paths_test.go b/android/paths_test.go
new file mode 100644
index 00000000..9d69473a
--- /dev/null
+++ b/android/paths_test.go
@@ -0,0 +1,182 @@
+// 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 android
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+type strsTestCase struct {
+ in []string
+ out string
+ err []error
+}
+
+var commonValidatePathTestCases = []strsTestCase{
+ {
+ in: []string{""},
+ out: "",
+ },
+ {
+ in: []string{"a/b"},
+ out: "a/b",
+ },
+ {
+ in: []string{"a/b", "c"},
+ out: "a/b/c",
+ },
+ {
+ in: []string{"a/.."},
+ out: ".",
+ },
+ {
+ in: []string{"."},
+ out: ".",
+ },
+ {
+ in: []string{".."},
+ out: "",
+ err: []error{errors.New("Path is outside directory: ..")},
+ },
+ {
+ in: []string{"../a"},
+ out: "",
+ err: []error{errors.New("Path is outside directory: ../a")},
+ },
+ {
+ in: []string{"b/../../a"},
+ out: "",
+ err: []error{errors.New("Path is outside directory: ../a")},
+ },
+ {
+ in: []string{"/a"},
+ out: "",
+ err: []error{errors.New("Path is outside directory: /a")},
+ },
+ {
+ in: []string{"a", "../b"},
+ out: "",
+ err: []error{errors.New("Path is outside directory: ../b")},
+ },
+ {
+ in: []string{"a", "b/../../c"},
+ out: "",
+ err: []error{errors.New("Path is outside directory: ../c")},
+ },
+ {
+ in: []string{"a", "./.."},
+ out: "",
+ err: []error{errors.New("Path is outside directory: ..")},
+ },
+}
+
+var validateSafePathTestCases = append(commonValidatePathTestCases, []strsTestCase{
+ {
+ in: []string{"$host/../$a"},
+ out: "$a",
+ },
+}...)
+
+var validatePathTestCases = append(commonValidatePathTestCases, []strsTestCase{
+ {
+ in: []string{"$host/../$a"},
+ out: "",
+ err: []error{errors.New("Path contains invalid character($): $host/../$a")},
+ },
+ {
+ in: []string{"$host/.."},
+ out: "",
+ err: []error{errors.New("Path contains invalid character($): $host/..")},
+ },
+}...)
+
+func TestValidateSafePath(t *testing.T) {
+ for _, testCase := range validateSafePathTestCases {
+ ctx := &configErrorWrapper{}
+ out := validateSafePath(ctx, testCase.in...)
+ check(t, "validateSafePath", p(testCase.in), out, ctx.errors, testCase.out, testCase.err)
+ }
+}
+
+func TestValidatePath(t *testing.T) {
+ for _, testCase := range validatePathTestCases {
+ ctx := &configErrorWrapper{}
+ out := validatePath(ctx, testCase.in...)
+ check(t, "validatePath", p(testCase.in), out, ctx.errors, testCase.out, testCase.err)
+ }
+}
+
+func TestOptionalPath(t *testing.T) {
+ var path OptionalPath
+ checkInvalidOptionalPath(t, path)
+
+ path = OptionalPathForPath(nil)
+ checkInvalidOptionalPath(t, path)
+}
+
+func checkInvalidOptionalPath(t *testing.T, path OptionalPath) {
+ if path.Valid() {
+ t.Errorf("Uninitialized OptionalPath should not be valid")
+ }
+ if path.String() != "" {
+ t.Errorf("Uninitialized OptionalPath String() should return \"\", not %q", path.String())
+ }
+ defer func() {
+ if r := recover(); r == nil {
+ t.Errorf("Expected a panic when calling Path() on an uninitialized OptionalPath")
+ }
+ }()
+ path.Path()
+}
+
+func check(t *testing.T, testType, testString string,
+ got interface{}, err []error,
+ expected interface{}, expectedErr []error) {
+
+ printedTestCase := false
+ e := func(s string, expected, got interface{}) {
+ if !printedTestCase {
+ t.Errorf("test case %s: %s", testType, testString)
+ printedTestCase = true
+ }
+ t.Errorf("incorrect %s", s)
+ t.Errorf(" expected: %s", p(expected))
+ t.Errorf(" got: %s", p(got))
+ }
+
+ if !reflect.DeepEqual(expectedErr, err) {
+ e("errors:", expectedErr, err)
+ }
+
+ if !reflect.DeepEqual(expected, got) {
+ e("output:", expected, got)
+ }
+}
+
+func p(in interface{}) string {
+ if v, ok := in.([]interface{}); ok {
+ s := make([]string, len(v))
+ for i := range v {
+ s[i] = fmt.Sprintf("%#v", v[i])
+ }
+ return "[" + strings.Join(s, ", ") + "]"
+ } else {
+ return fmt.Sprintf("%#v", in)
+ }
+}
diff --git a/android/util.go b/android/util.go
new file mode 100644
index 00000000..60c8a63a
--- /dev/null
+++ b/android/util.go
@@ -0,0 +1,78 @@
+// 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 android
+
+import "sort"
+
+func JoinWithPrefix(strs []string, prefix string) string {
+ if len(strs) == 0 {
+ return ""
+ }
+
+ if len(strs) == 1 {
+ return prefix + strs[0]
+ }
+
+ n := len(" ") * (len(strs) - 1)
+ for _, s := range strs {
+ n += len(prefix) + len(s)
+ }
+
+ ret := make([]byte, 0, n)
+ for i, s := range strs {
+ if i != 0 {
+ ret = append(ret, ' ')
+ }
+ ret = append(ret, prefix...)
+ ret = append(ret, s...)
+ }
+ return string(ret)
+}
+
+func JoinWithPrefixAndQuote(strs []string, prefix string) string {
+ if len(strs) == 0 {
+ return ""
+ }
+
+ if len(strs) == 1 {
+ return prefix + `"` + strs[0] + `"`
+ }
+
+ n := len(" ") * (len(strs) - 1)
+ for _, s := range strs {
+ n += len(prefix) + len(s) + len(`""`)
+ }
+
+ ret := make([]byte, 0, n)
+ for i, s := range strs {
+ if i != 0 {
+ ret = append(ret, ' ')
+ }
+ ret = append(ret, prefix...)
+ ret = append(ret, '"')
+ ret = append(ret, s...)
+ ret = append(ret, '"')
+ }
+ return string(ret)
+}
+
+func sortedKeys(m map[string][]string) []string {
+ s := make([]string, 0, len(m))
+ for k := range m {
+ s = append(s, k)
+ }
+ sort.Strings(s)
+ return s
+}
diff --git a/android/variable.go b/android/variable.go
new file mode 100644
index 00000000..29d7a080
--- /dev/null
+++ b/android/variable.go
@@ -0,0 +1,232 @@
+// 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 android
+
+import (
+ "fmt"
+ "reflect"
+ "runtime"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+func init() {
+ RegisterBottomUpMutator("variable", variableMutator)
+}
+
+type variableProperties struct {
+ Product_variables struct {
+ Platform_sdk_version struct {
+ Asflags []string
+ }
+
+ // unbundled_build is a catch-all property to annotate modules that don't build in one or
+ // more unbundled branches, usually due to dependencies missing from the manifest.
+ Unbundled_build struct {
+ Enabled *bool `android:"arch_variant"`
+ } `android:"arch_variant"`
+
+ Brillo struct {
+ Version_script *string `android:"arch_variant"`
+ } `android:"arch_variant"`
+
+ Malloc_not_svelte struct {
+ Cflags []string
+ }
+
+ Safestack struct {
+ Cflags []string `android:"arch_variant"`
+ } `android:"arch_variant"`
+ } `android:"arch_variant"`
+}
+
+var zeroProductVariables variableProperties
+
+type productVariables struct {
+ // Suffix to add to generated Makefiles
+ Make_suffix *string `json:",omitempty"`
+
+ Platform_sdk_version *int `json:",omitempty"`
+
+ DeviceName *string `json:",omitempty"`
+ DeviceArch *string `json:",omitempty"`
+ DeviceArchVariant *string `json:",omitempty"`
+ DeviceCpuVariant *string `json:",omitempty"`
+ DeviceAbi *[]string `json:",omitempty"`
+ DeviceUsesClang *bool `json:",omitempty"`
+
+ DeviceSecondaryArch *string `json:",omitempty"`
+ DeviceSecondaryArchVariant *string `json:",omitempty"`
+ DeviceSecondaryCpuVariant *string `json:",omitempty"`
+ DeviceSecondaryAbi *[]string `json:",omitempty"`
+
+ HostArch *string `json:",omitempty"`
+ HostSecondaryArch *string `json:",omitempty"`
+
+ CrossHost *string `json:",omitempty"`
+ CrossHostArch *string `json:",omitempty"`
+ CrossHostSecondaryArch *string `json:",omitempty"`
+
+ Allow_missing_dependencies *bool `json:",omitempty"`
+ Unbundled_build *bool `json:",omitempty"`
+ Brillo *bool `json:",omitempty"`
+ Malloc_not_svelte *bool `json:",omitempty"`
+ Safestack *bool `json:",omitempty"`
+ HostStaticBinaries *bool `json:",omitempty"`
+
+ SanitizeHost *[]string `json:",omitempty"`
+ SanitizeDevice *[]string `json:",omitempty"`
+}
+
+func boolPtr(v bool) *bool {
+ return &v
+}
+
+func intPtr(v int) *int {
+ return &v
+}
+
+func stringPtr(v string) *string {
+ return &v
+}
+
+func (v *productVariables) SetDefaultConfig() {
+ *v = productVariables{
+ Platform_sdk_version: intPtr(22),
+ HostArch: stringPtr("x86_64"),
+ HostSecondaryArch: stringPtr("x86"),
+ DeviceName: stringPtr("flounder"),
+ DeviceArch: stringPtr("arm64"),
+ DeviceArchVariant: stringPtr("armv8-a"),
+ DeviceCpuVariant: stringPtr("denver64"),
+ DeviceAbi: &[]string{"arm64-v8a"},
+ DeviceUsesClang: boolPtr(true),
+ DeviceSecondaryArch: stringPtr("arm"),
+ DeviceSecondaryArchVariant: stringPtr("armv7-a-neon"),
+ DeviceSecondaryCpuVariant: stringPtr("denver"),
+ DeviceSecondaryAbi: &[]string{"armeabi-v7a"},
+ Malloc_not_svelte: boolPtr(false),
+ Safestack: boolPtr(false),
+ }
+
+ if runtime.GOOS == "linux" {
+ v.CrossHost = stringPtr("windows")
+ v.CrossHostArch = stringPtr("x86")
+ v.CrossHostSecondaryArch = stringPtr("x86_64")
+ }
+}
+
+func variableMutator(mctx BottomUpMutatorContext) {
+ var module Module
+ var ok bool
+ if module, ok = mctx.Module().(Module); !ok {
+ return
+ }
+
+ // TODO: depend on config variable, create variants, propagate variants up tree
+ a := module.base()
+ variableValues := reflect.ValueOf(&a.variableProperties.Product_variables).Elem()
+ zeroValues := reflect.ValueOf(zeroProductVariables.Product_variables)
+
+ for i := 0; i < variableValues.NumField(); i++ {
+ variableValue := variableValues.Field(i)
+ zeroValue := zeroValues.Field(i)
+ name := variableValues.Type().Field(i).Name
+ property := "product_variables." + proptools.PropertyNameForField(name)
+
+ // Check that the variable was set for the product
+ val := reflect.ValueOf(mctx.Config().(Config).ProductVariables).FieldByName(name)
+ if !val.IsValid() || val.Kind() != reflect.Ptr || val.IsNil() {
+ continue
+ }
+
+ val = val.Elem()
+
+ // For bools, check that the value is true
+ if val.Kind() == reflect.Bool && val.Bool() == false {
+ continue
+ }
+
+ // Check if any properties were set for the module
+ if reflect.DeepEqual(variableValue.Interface(), zeroValue.Interface()) {
+ continue
+ }
+
+ a.setVariableProperties(mctx, property, variableValue, val.Interface())
+ }
+}
+
+func (a *ModuleBase) setVariableProperties(ctx BottomUpMutatorContext,
+ prefix string, productVariablePropertyValue reflect.Value, variableValue interface{}) {
+
+ printfIntoProperties(productVariablePropertyValue, variableValue)
+
+ err := proptools.AppendMatchingProperties(a.generalProperties,
+ productVariablePropertyValue.Addr().Interface(), nil)
+ if err != nil {
+ if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+ ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+ } else {
+ panic(err)
+ }
+ }
+}
+
+func printfIntoProperties(productVariablePropertyValue reflect.Value, variableValue interface{}) {
+ for i := 0; i < productVariablePropertyValue.NumField(); i++ {
+ propertyValue := productVariablePropertyValue.Field(i)
+ kind := propertyValue.Kind()
+ if kind == reflect.Ptr {
+ if propertyValue.IsNil() {
+ continue
+ }
+ propertyValue = propertyValue.Elem()
+ }
+ switch propertyValue.Kind() {
+ case reflect.String:
+ printfIntoProperty(propertyValue, variableValue)
+ case reflect.Slice:
+ for j := 0; j < propertyValue.Len(); j++ {
+ printfIntoProperty(propertyValue.Index(j), variableValue)
+ }
+ case reflect.Bool:
+ // Nothing
+ case reflect.Struct:
+ printfIntoProperties(propertyValue, variableValue)
+ default:
+ panic(fmt.Errorf("unsupported field kind %q", propertyValue.Kind()))
+ }
+ }
+}
+
+func printfIntoProperty(propertyValue reflect.Value, variableValue interface{}) {
+ s := propertyValue.String()
+ // For now, we only support int formats
+ var i int
+ if strings.Contains(s, "%d") {
+ switch v := variableValue.(type) {
+ case int:
+ i = v
+ case bool:
+ if v {
+ i = 1
+ }
+ default:
+ panic(fmt.Errorf("unsupported type %T", variableValue))
+ }
+ propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, i)))
+ }
+}