aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2015-01-30 17:27:36 -0800
committerColin Cross <ccross@android.com>2015-03-13 20:28:16 -0700
commit3f40fa460d85b10646d383a3b6b01ea6d569b01b (patch)
tree542d913a3f0f818042b503948869818a77e99ebc /common
parente441b9df9a68595d0dd7b8ed184aecb27c86054b (diff)
downloadbuild_soong-3f40fa460d85b10646d383a3b6b01ea6d569b01b.tar.gz
build_soong-3f40fa460d85b10646d383a3b6b01ea6d569b01b.tar.bz2
build_soong-3f40fa460d85b10646d383a3b6b01ea6d569b01b.zip
Add soong_build primary builder
Initial build logic for building android with soong. It can build a variety of C and C++ files for arm/arm64 and host. Change-Id: I10eb37c2c2a50be6af1bb5fd568c0962b9476bf0
Diffstat (limited to 'common')
-rw-r--r--common/arch.go545
-rw-r--r--common/defs.go61
-rw-r--r--common/glob.go133
-rw-r--r--common/module.go327
-rw-r--r--common/paths.go83
5 files changed, 1149 insertions, 0 deletions
diff --git a/common/arch.go b/common/arch.go
new file mode 100644
index 00000000..8daade0f
--- /dev/null
+++ b/common/arch.go
@@ -0,0 +1,545 @@
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package common
+
+import (
+ "blueprint"
+ "blueprint/proptools"
+ "fmt"
+ "reflect"
+ "runtime"
+ "strings"
+)
+
+var (
+ Arm = newArch32("Arm")
+ Arm64 = newArch64("Arm64")
+ Mips = newArch32("Mips")
+ Mips64 = newArch64("Mips64")
+ X86 = newArch32("X86")
+ X86_64 = newArch64("X86_64")
+)
+
+/*
+Example blueprints file containing all variant property groups, with comment listing what type
+of variants get properties in that group:
+
+module {
+ arch: {
+ arm: {
+ // Host or device variants with arm architecture
+ },
+ arm64: {
+ // Host or device variants with arm64 architecture
+ },
+ mips: {
+ // Host or device variants with mips architecture
+ },
+ mips64: {
+ // Host or device variants with mips64 architecture
+ },
+ x86: {
+ // Host or device variants with x86 architecture
+ },
+ x86_64: {
+ // Host or device variants with x86_64 architecture
+ },
+ },
+ multilib: {
+ lib32: {
+ // Host or device variants for 32-bit architectures
+ },
+ lib64: {
+ // Host or device variants for 64-bit architectures
+ },
+ },
+ target: {
+ android: {
+ // Device variants
+ },
+ host: {
+ // Host variants
+ },
+ linux: {
+ // Linux host variants
+ },
+ darwin: {
+ // Darwin host variants
+ },
+ windows: {
+ // Windows host variants
+ },
+ not_windows: {
+ // Non-windows host variants
+ },
+ },
+}
+*/
+type archProperties struct {
+ Arch struct {
+ Arm interface{}
+ Arm64 interface{}
+ Mips interface{}
+ Mips64 interface{}
+ X86 interface{}
+ X86_64 interface{}
+ }
+ Multilib struct {
+ Lib32 interface{}
+ Lib64 interface{}
+ }
+ Target struct {
+ Host interface{}
+ Android interface{}
+ Linux interface{}
+ Darwin interface{}
+ Windows interface{}
+ Not_windows interface{}
+ }
+}
+
+// An Arch indicates a single CPU architecture.
+type Arch struct {
+ HostOrDevice HostOrDevice
+ ArchType ArchType
+ ArchVariant string
+ CpuVariant string
+}
+
+func (a Arch) String() string {
+ s := a.HostOrDevice.String() + "_" + a.ArchType.String()
+ if a.ArchVariant != "" {
+ s += "_" + a.ArchVariant
+ }
+ if a.CpuVariant != "" {
+ s += "_" + a.CpuVariant
+ }
+ return s
+}
+
+type ArchType struct {
+ Name string
+ Field string
+ Multilib string
+ MultilibField string
+}
+
+func newArch32(field string) ArchType {
+ return ArchType{
+ Name: strings.ToLower(field),
+ Field: field,
+ Multilib: "lib32",
+ MultilibField: "Lib32",
+ }
+}
+
+func newArch64(field string) ArchType {
+ return ArchType{
+ Name: strings.ToLower(field),
+ Field: field,
+ Multilib: "lib64",
+ MultilibField: "Lib64",
+ }
+}
+
+func (a ArchType) String() string {
+ return a.Name
+}
+
+type HostOrDeviceSupported int
+
+const (
+ _ HostOrDeviceSupported = iota
+ HostSupported
+ DeviceSupported
+ HostAndDeviceSupported
+)
+
+type HostOrDevice int
+
+const (
+ _ HostOrDevice = iota
+ Host
+ Device
+)
+
+func (hod HostOrDevice) String() string {
+ switch hod {
+ case Device:
+ return "device"
+ case Host:
+ return "host"
+ default:
+ panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
+ }
+}
+
+func (hod HostOrDevice) FieldLower() string {
+ switch hod {
+ case Device:
+ return "android"
+ case Host:
+ return "host"
+ default:
+ panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
+ }
+}
+
+func (hod HostOrDevice) Field() string {
+ switch hod {
+ case Device:
+ return "Android"
+ case Host:
+ return "Host"
+ default:
+ panic(fmt.Sprintf("unexpected HostOrDevice value %d", hod))
+ }
+}
+
+func (hod HostOrDevice) Host() bool {
+ if hod == 0 {
+ panic("HostOrDevice unset")
+ }
+ return hod == Host
+}
+
+func (hod HostOrDevice) Device() bool {
+ if hod == 0 {
+ panic("HostOrDevice unset")
+ }
+ return hod == Device
+}
+
+var hostOrDeviceName = map[HostOrDevice]string{
+ Device: "device",
+ Host: "host",
+}
+
+var (
+ armArch = Arch{
+ HostOrDevice: Device,
+ ArchType: Arm,
+ ArchVariant: "armv7-a-neon",
+ CpuVariant: "cortex-a15",
+ }
+ arm64Arch = Arch{
+ HostOrDevice: Device,
+ ArchType: Arm64,
+ ArchVariant: "armv8-a",
+ CpuVariant: "denver",
+ }
+ hostArch = Arch{
+ HostOrDevice: Host,
+ ArchType: X86,
+ }
+ host64Arch = Arch{
+ HostOrDevice: Host,
+ ArchType: X86_64,
+ }
+)
+
+func ArchMutator(mctx blueprint.EarlyMutatorContext) {
+ var module AndroidModule
+ var ok bool
+ if module, ok = mctx.Module().(AndroidModule); !ok {
+ return
+ }
+
+ // TODO: this is all hardcoded for arm64 primary, arm secondary for now
+ // Replace with a configuration file written by lunch or bootstrap
+
+ arches := []Arch{}
+
+ if module.base().HostSupported() {
+ arches = append(arches, host64Arch)
+ }
+
+ if module.base().DeviceSupported() {
+ switch module.base().commonProperties.Compile_multilib {
+ case "both":
+ arches = append(arches, arm64Arch, armArch)
+ case "first", "64":
+ arches = append(arches, arm64Arch)
+ case "32":
+ arches = append(arches, armArch)
+ default:
+ mctx.ModuleErrorf(`compile_multilib must be "both", "first", "32", or "64", found %q`,
+ module.base().commonProperties.Compile_multilib)
+ }
+ }
+
+ archNames := []string{}
+ for _, arch := range arches {
+ archNames = append(archNames, arch.String())
+ }
+
+ modules := mctx.CreateVariations(archNames...)
+
+ for i, m := range modules {
+ m.(AndroidModule).base().SetArch(arches[i])
+ m.(AndroidModule).base().setArchProperties(mctx, arches[i])
+ }
+}
+
+func InitArchModule(m AndroidModule, defaultMultilib string,
+ propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
+
+ base := m.base()
+
+ base.commonProperties.Compile_multilib = defaultMultilib
+
+ base.generalProperties = append(base.generalProperties,
+ &base.commonProperties)
+ base.generalProperties = append(base.generalProperties,
+ propertyStructs...)
+
+ for _, properties := range base.generalProperties {
+ propertiesValue := reflect.ValueOf(properties)
+ if propertiesValue.Kind() != reflect.Ptr {
+ panic("properties must be a pointer to a struct")
+ }
+
+ propertiesValue = propertiesValue.Elem()
+ if propertiesValue.Kind() != reflect.Struct {
+ panic("properties must be a pointer to a struct")
+ }
+
+ archProperties := &archProperties{}
+ forEachInterface(reflect.ValueOf(archProperties), func(v reflect.Value) {
+ newValue := proptools.CloneProperties(propertiesValue)
+ proptools.ZeroProperties(newValue.Elem())
+ v.Set(newValue)
+ })
+
+ base.archProperties = append(base.archProperties, archProperties)
+ }
+
+ var allProperties []interface{}
+ allProperties = append(allProperties, base.generalProperties...)
+ for _, asp := range base.archProperties {
+ allProperties = append(allProperties, asp)
+ }
+
+ return m, allProperties
+}
+
+// Rewrite the module's properties structs to contain arch-specific values.
+func (a *AndroidModuleBase) setArchProperties(ctx blueprint.EarlyMutatorContext, arch Arch) {
+ for i := range a.generalProperties {
+ generalPropsValue := reflect.ValueOf(a.generalProperties[i]).Elem()
+
+ // Handle arch-specific properties in the form:
+ // arch {
+ // arm64 {
+ // key: value,
+ // },
+ // },
+ t := arch.ArchType
+ extendProperties(ctx, "arch", t.Name, generalPropsValue,
+ reflect.ValueOf(a.archProperties[i].Arch).FieldByName(t.Field).Elem().Elem())
+
+ // Handle multilib-specific properties in the form:
+ // multilib {
+ // lib32 {
+ // key: value,
+ // },
+ // },
+ extendProperties(ctx, "multilib", t.Multilib, generalPropsValue,
+ reflect.ValueOf(a.archProperties[i].Multilib).FieldByName(t.MultilibField).Elem().Elem())
+
+ // Handle host-or-device-specific properties in the form:
+ // target {
+ // host {
+ // key: value,
+ // },
+ // },
+ hod := arch.HostOrDevice
+ extendProperties(ctx, "target", hod.FieldLower(), generalPropsValue,
+ reflect.ValueOf(a.archProperties[i].Target).FieldByName(hod.Field()).Elem().Elem())
+
+ // Handle host target properties in the form:
+ // target {
+ // linux {
+ // key: value,
+ // },
+ // not_windows {
+ // key: value,
+ // },
+ // },
+ var osList = []struct {
+ goos string
+ field string
+ }{
+ {"darwin", "Darwin"},
+ {"linux", "Linux"},
+ {"windows", "Windows"},
+ }
+
+ if hod.Host() {
+ for _, v := range osList {
+ if v.goos == runtime.GOOS {
+ extendProperties(ctx, "target", v.goos, generalPropsValue,
+ reflect.ValueOf(a.archProperties[i].Target).FieldByName(v.field).Elem().Elem())
+ }
+ }
+ extendProperties(ctx, "target", "not_windows", generalPropsValue,
+ reflect.ValueOf(a.archProperties[i].Target).FieldByName("Not_windows").Elem().Elem())
+ }
+
+ if ctx.Failed() {
+ return
+ }
+ }
+}
+
+func forEachInterface(v reflect.Value, f func(reflect.Value)) {
+ switch v.Kind() {
+ case reflect.Interface:
+ f(v)
+ case reflect.Struct:
+ for i := 0; i < v.NumField(); i++ {
+ forEachInterface(v.Field(i), f)
+ }
+ case reflect.Ptr:
+ forEachInterface(v.Elem(), f)
+ default:
+ panic(fmt.Errorf("Unsupported kind %s", v.Kind()))
+ }
+}
+
+// TODO: move this to proptools
+func extendProperties(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
+ dstValue, srcValue reflect.Value) {
+ extendPropertiesRecursive(ctx, variationType, variationName, dstValue, srcValue, "")
+}
+
+func extendPropertiesRecursive(ctx blueprint.EarlyMutatorContext, variationType, variationName string,
+ dstValue, srcValue reflect.Value, recursePrefix string) {
+
+ typ := dstValue.Type()
+ if srcValue.Type() != typ {
+ panic(fmt.Errorf("can't extend mismatching types (%s <- %s)",
+ dstValue.Kind(), srcValue.Kind()))
+ }
+
+ for i := 0; i < srcValue.NumField(); i++ {
+ field := typ.Field(i)
+ if field.PkgPath != "" {
+ // The field is not exported so just skip it.
+ continue
+ }
+
+ srcFieldValue := srcValue.Field(i)
+ dstFieldValue := dstValue.Field(i)
+
+ localPropertyName := proptools.PropertyNameForField(field.Name)
+ propertyName := fmt.Sprintf("%s.%s.%s%s", variationType, variationName,
+ recursePrefix, localPropertyName)
+ propertyPresentInVariation := ctx.ContainsProperty(propertyName)
+
+ if !propertyPresentInVariation {
+ continue
+ }
+
+ tag := field.Tag.Get("android")
+ tags := map[string]bool{}
+ for _, entry := range strings.Split(tag, ",") {
+ if entry != "" {
+ tags[entry] = true
+ }
+ }
+
+ if !tags["arch_variant"] {
+ ctx.PropertyErrorf(propertyName, "property %q can't be specific to a build variant",
+ recursePrefix+proptools.PropertyNameForField(field.Name))
+ continue
+ }
+
+ switch srcFieldValue.Kind() {
+ case reflect.Bool:
+ // Replace the original value.
+ dstFieldValue.Set(srcFieldValue)
+ case reflect.String:
+ // Append the extension string.
+ dstFieldValue.SetString(dstFieldValue.String() +
+ srcFieldValue.String())
+ case reflect.Struct:
+ // Recursively extend the struct's fields.
+ newRecursePrefix := fmt.Sprintf("%s%s.", recursePrefix, strings.ToLower(field.Name))
+ extendPropertiesRecursive(ctx, variationType, variationName,
+ dstFieldValue, srcFieldValue,
+ newRecursePrefix)
+ case reflect.Slice:
+ val, err := archCombineSlices(dstFieldValue, srcFieldValue, tags["arch_subtract"])
+ if err != nil {
+ ctx.PropertyErrorf(propertyName, err.Error())
+ continue
+ }
+ dstFieldValue.Set(val)
+ case reflect.Ptr, reflect.Interface:
+ // Recursively extend the pointed-to struct's fields.
+ if dstFieldValue.IsNil() != srcFieldValue.IsNil() {
+ panic(fmt.Errorf("can't extend field %q: nilitude mismatch"))
+ }
+ if dstFieldValue.Type() != srcFieldValue.Type() {
+ panic(fmt.Errorf("can't extend field %q: type mismatch"))
+ }
+ if !dstFieldValue.IsNil() {
+ newRecursePrefix := fmt.Sprintf("%s.%s", recursePrefix, field.Name)
+ extendPropertiesRecursive(ctx, variationType, variationName,
+ dstFieldValue.Elem(), srcFieldValue.Elem(),
+ newRecursePrefix)
+ }
+ default:
+ panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
+ field.Name, srcFieldValue.Kind()))
+ }
+ }
+}
+
+func archCombineSlices(general, arch reflect.Value, canSubtract bool) (reflect.Value, error) {
+ if !canSubtract {
+ // Append the extension slice.
+ return reflect.AppendSlice(general, arch), nil
+ }
+
+ // Support -val in arch list to subtract a value from original list
+ l := general.Interface().([]string)
+ for archIndex := 0; archIndex < arch.Len(); archIndex++ {
+ archString := arch.Index(archIndex).String()
+ if strings.HasPrefix(archString, "-") {
+ generalIndex := findStringInSlice(archString[1:], l)
+ if generalIndex == -1 {
+ return reflect.Value{},
+ fmt.Errorf("can't find %q to subtract from general properties", archString[1:])
+ }
+ l = append(l[:generalIndex], l[generalIndex+1:]...)
+ } else {
+ l = append(l, archString)
+ }
+ }
+
+ return reflect.ValueOf(l), nil
+}
+
+func findStringInSlice(str string, slice []string) int {
+ for i, s := range slice {
+ if s == str {
+ return i
+ }
+ }
+
+ return -1
+}
diff --git a/common/defs.go b/common/defs.go
new file mode 100644
index 00000000..44992166
--- /dev/null
+++ b/common/defs.go
@@ -0,0 +1,61 @@
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package common
+
+import (
+ "blueprint"
+)
+
+var (
+ pctx = blueprint.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")
+)
diff --git a/common/glob.go b/common/glob.go
new file mode 100644
index 00000000..9aada958
--- /dev/null
+++ b/common/glob.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 common
+
+import (
+ "fmt"
+ "path/filepath"
+
+ "blueprint"
+ "blueprint/bootstrap"
+
+ "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 "$glob"`, globCmd),
+ Description: "glob $glob",
+
+ Restat: true,
+ Generator: true,
+ Deps: blueprint.DepsGCC,
+ Depfile: "$out.d",
+ },
+ "glob")
+)
+
+func hasGlob(in []string) bool {
+ for _, s := range in {
+ if glob.IsGlob(s) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func ExpandGlobs(ctx AndroidModuleContext, in []string) []string {
+ if !hasGlob(in) {
+ return in
+ }
+
+ out := make([]string, 0, len(in))
+ for _, s := range in {
+ if glob.IsGlob(s) {
+ out = append(out, Glob(ctx, s)...)
+ } else {
+ out = append(out, s)
+ }
+ }
+
+ return out
+}
+
+func Glob(ctx AndroidModuleContext, globPattern string) []string {
+ fileListFile := filepath.Join(ModuleOutDir(ctx), "glob", globToString(globPattern))
+ depFile := fileListFile + ".d"
+
+ // Get a globbed file list, and write out fileListFile and depFile
+ files, err := glob.GlobWithDepFile(globPattern, fileListFile, depFile)
+ if err != nil {
+ ctx.ModuleErrorf("glob: %s", err.Error())
+ return []string{globPattern}
+ }
+
+ // 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},
+ Implicits: []string{globCmd},
+ Args: map[string]string{
+ "glob": globPattern,
+ },
+ })
+
+ // Phony rule so the cleanup phase doesn't delete the depFile
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: blueprint.Phony,
+ Outputs: []string{depFile},
+ })
+
+ // Make build.ninja depend on the fileListFile
+ ctx.AddNinjaFileDeps(fileListFile)
+
+ return files
+}
+
+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/common/module.go b/common/module.go
new file mode 100644
index 00000000..0cbe4b07
--- /dev/null
+++ b/common/module.go
@@ -0,0 +1,327 @@
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package common
+
+import (
+ "blueprint"
+ "path/filepath"
+)
+
+var (
+ DeviceSharedLibrary = "shared_library"
+ DeviceStaticLibrary = "static_library"
+ DeviceExecutable = "executable"
+ HostSharedLibrary = "host_shared_library"
+ HostStaticLibrary = "host_static_library"
+ HostExecutable = "host_executable"
+)
+
+type AndroidModuleContext interface {
+ blueprint.ModuleContext
+
+ Arch() Arch
+ InstallFile(installPath, srcPath string)
+ CheckbuildFile(srcPath string)
+}
+
+type AndroidModule interface {
+ blueprint.Module
+
+ GenerateAndroidBuildActions(AndroidModuleContext)
+
+ base() *AndroidModuleBase
+ Disabled() bool
+ HostOrDevice() HostOrDevice
+}
+
+type AndroidDynamicDepender interface {
+ AndroidDynamicDependencies(ctx AndroidDynamicDependerModuleContext) []string
+}
+
+type AndroidDynamicDependerModuleContext interface {
+ blueprint.DynamicDependerModuleContext
+}
+
+type commonProperties struct {
+ Name string
+ Deps []string
+ ResourceDirs []string
+
+ // disabled: don't emit any build rules for this module
+ Disabled bool `android:"arch_variant"`
+
+ // multilib: 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
+
+ // Set by ArchMutator
+ CompileArch Arch `blueprint:"mutated"`
+
+ // Set by InitAndroidModule
+ HostOrDeviceSupported HostOrDeviceSupported `blueprint:"mutated"`
+}
+
+type hostAndDeviceProperties struct {
+ Host_supported bool
+ Device_supported bool
+}
+
+func InitAndroidModule(m AndroidModule, hod HostOrDeviceSupported, defaultMultilib string,
+ propertyStructs ...interface{}) (blueprint.Module, []interface{}) {
+
+ base := m.base()
+ base.module = m
+ base.commonProperties.HostOrDeviceSupported = hod
+
+ if hod == HostAndDeviceSupported {
+ // Default to module to device supported, host not supported, can override in module
+ // properties
+ base.hostAndDeviceProperties.Device_supported = true
+ propertyStructs = append(propertyStructs, &base.hostAndDeviceProperties)
+ }
+
+ return InitArchModule(m, defaultMultilib, 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"
+// "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 AndroidModuleBase struct {
+ // Putting the curiously recurring thing pointing to the thing that contains
+ // the thing pattern to good use.
+ module AndroidModule
+
+ commonProperties commonProperties
+ hostAndDeviceProperties hostAndDeviceProperties
+ generalProperties []interface{}
+ archProperties []*archProperties
+
+ noAddressSanitizer bool
+ installFiles []string
+ checkbuildFiles []string
+}
+
+func (a *AndroidModuleBase) base() *AndroidModuleBase {
+ return a
+}
+
+func (a *AndroidModuleBase) SetArch(arch Arch) {
+ a.commonProperties.CompileArch = arch
+}
+
+func (a *AndroidModuleBase) HostOrDevice() HostOrDevice {
+ return a.commonProperties.CompileArch.HostOrDevice
+}
+
+func (a *AndroidModuleBase) HostSupported() bool {
+ return a.commonProperties.HostOrDeviceSupported == HostSupported ||
+ a.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
+ a.hostAndDeviceProperties.Host_supported
+}
+
+func (a *AndroidModuleBase) DeviceSupported() bool {
+ return a.commonProperties.HostOrDeviceSupported == DeviceSupported ||
+ a.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
+ a.hostAndDeviceProperties.Device_supported
+}
+
+func (a *AndroidModuleBase) Disabled() bool {
+ return a.commonProperties.Disabled
+}
+
+func (a *AndroidModuleBase) computeInstallDeps(
+ ctx blueprint.ModuleContext) []string {
+
+ result := []string{}
+ ctx.VisitDepsDepthFirstIf(isFileInstaller,
+ func(m blueprint.Module) {
+ fileInstaller := m.(fileInstaller)
+ files := fileInstaller.filesToInstall()
+ result = append(result, files...)
+ })
+
+ return result
+}
+
+func (a *AndroidModuleBase) filesToInstall() []string {
+ return a.installFiles
+}
+
+func (p *AndroidModuleBase) NoAddressSanitizer() bool {
+ return p.noAddressSanitizer
+}
+
+func (p *AndroidModuleBase) resourceDirs() []string {
+ return p.commonProperties.ResourceDirs
+}
+
+func (a *AndroidModuleBase) generateModuleTarget(ctx blueprint.ModuleContext) {
+ if a != ctx.FinalModule().(AndroidModule).base() {
+ return
+ }
+
+ allInstalledFiles := []string{}
+ ctx.VisitAllModuleVariants(func(module blueprint.Module) {
+ if androidModule, ok := module.(AndroidModule); ok {
+ files := androidModule.base().installFiles
+ allInstalledFiles = append(allInstalledFiles, files...)
+ }
+ })
+
+ if len(allInstalledFiles) > 0 {
+ ctx.Build(pctx, blueprint.BuildParams{
+ Rule: blueprint.Phony,
+ Outputs: []string{ctx.ModuleName()},
+ Inputs: allInstalledFiles,
+ })
+ }
+}
+
+func (a *AndroidModuleBase) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string {
+ actx := &androidDynamicDependerContext{
+ DynamicDependerModuleContext: ctx,
+ module: a,
+ }
+
+ if dynamic, ok := a.module.(AndroidDynamicDepender); ok {
+ return dynamic.AndroidDynamicDependencies(actx)
+ }
+
+ return nil
+}
+
+func (a *AndroidModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) {
+ androidCtx := &androidModuleContext{
+ ModuleContext: ctx,
+ installDeps: a.computeInstallDeps(ctx),
+ installFiles: a.installFiles,
+ arch: a.commonProperties.CompileArch,
+ }
+
+ if a.commonProperties.Disabled {
+ return
+ }
+
+ a.module.GenerateAndroidBuildActions(androidCtx)
+ if ctx.Failed() {
+ return
+ }
+
+ a.generateModuleTarget(ctx)
+ if ctx.Failed() {
+ return
+ }
+}
+
+type androidModuleContext struct {
+ blueprint.ModuleContext
+ arch Arch
+ installDeps []string
+ installFiles []string
+ checkbuildFiles []string
+}
+
+func (a *androidModuleContext) Build(pctx *blueprint.PackageContext, params blueprint.BuildParams) {
+ params.Optional = true
+ a.ModuleContext.Build(pctx, params)
+}
+
+func (a *androidModuleContext) Arch() Arch {
+ return a.arch
+}
+
+func (a *androidModuleContext) InstallFile(installPath, srcPath string) {
+ var fullInstallPath string
+ if a.arch.HostOrDevice.Device() {
+ // TODO: replace unset with a device name once we have device targeting
+ fullInstallPath = filepath.Join("out/target/product/unset/system", installPath,
+ filepath.Base(srcPath))
+ } else {
+ // TODO: replace unset with a host name
+ fullInstallPath = filepath.Join("out/host/unset/", installPath, filepath.Base(srcPath))
+ }
+
+ a.ModuleContext.Build(pctx, blueprint.BuildParams{
+ Rule: Cp,
+ Outputs: []string{fullInstallPath},
+ Inputs: []string{srcPath},
+ OrderOnly: a.installDeps,
+ })
+
+ a.installFiles = append(a.installFiles, fullInstallPath)
+ a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
+}
+
+func (a *androidModuleContext) CheckbuildFile(srcPath string) {
+ a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
+}
+
+type androidDynamicDependerContext struct {
+ blueprint.DynamicDependerModuleContext
+ module *AndroidModuleBase
+}
+
+type fileInstaller interface {
+ filesToInstall() []string
+}
+
+func isFileInstaller(m blueprint.Module) bool {
+ _, ok := m.(fileInstaller)
+ return ok
+}
+
+func isAndroidModule(m blueprint.Module) bool {
+ _, ok := m.(AndroidModule)
+ return ok
+}
diff --git a/common/paths.go b/common/paths.go
new file mode 100644
index 00000000..91b8f99c
--- /dev/null
+++ b/common/paths.go
@@ -0,0 +1,83 @@
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package common
+
+import (
+ "path/filepath"
+
+ "blueprint"
+)
+
+type Config interface {
+ CpPreserveSymlinksFlags() string
+ SrcDir() string
+}
+
+// ModuleOutDir returns the path to the module-specific output directory.
+func ModuleOutDir(ctx AndroidModuleContext) string {
+ return filepath.Join(".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
+}
+
+// ModuleSrcDir returns the path of the directory that all source file paths are
+// specified relative to.
+func ModuleSrcDir(ctx blueprint.ModuleContext) string {
+ config := ctx.Config().(Config)
+ return filepath.Join(config.SrcDir(), ctx.ModuleDir())
+}
+
+// ModuleBinDir returns the path to the module- and architecture-specific binary
+// output directory.
+func ModuleBinDir(ctx AndroidModuleContext) string {
+ return filepath.Join(ModuleOutDir(ctx), "bin")
+}
+
+// ModuleLibDir returns the path to the module- and architecture-specific
+// library output directory.
+func ModuleLibDir(ctx AndroidModuleContext) string {
+ return filepath.Join(ModuleOutDir(ctx), "lib")
+}
+
+// ModuleGenDir returns the module directory for generated files
+// path.
+func ModuleGenDir(ctx AndroidModuleContext) string {
+ return filepath.Join(ModuleOutDir(ctx), "gen")
+}
+
+// ModuleObjDir returns the module- and architecture-specific object directory
+// path.
+func ModuleObjDir(ctx AndroidModuleContext) string {
+ return filepath.Join(ModuleOutDir(ctx), "obj")
+}
+
+// ModuleGoPackageDir returns the module-specific package root directory path.
+// This directory is where the final package .a files are output and where
+// dependent modules search for this package via -I arguments.
+func ModuleGoPackageDir(ctx AndroidModuleContext) string {
+ return filepath.Join(ModuleOutDir(ctx), "pkg")
+}
+
+// ModuleIncludeDir returns the module-specific public include directory path.
+func ModuleIncludeDir(ctx AndroidModuleContext) string {
+ return filepath.Join(ModuleOutDir(ctx), "include")
+}
+
+// ModuleProtoDir returns the module-specific public proto include directory path.
+func ModuleProtoDir(ctx AndroidModuleContext) string {
+ return filepath.Join(ModuleOutDir(ctx), "proto")
+}
+
+func ModuleJSCompiledDir(ctx AndroidModuleContext) string {
+ return filepath.Join(ModuleOutDir(ctx), "js")
+}