Rename common to android
Rename the "common" package to "android", because common is too
generic. Also removes all android.Android naming stutter.
Ran:
gomvpkg -from 'android/soong/common' -to 'android/soong/android'
gorename -from '"android/soong/android".AndroidModuleContext' -to 'ModuleContext'
gorename -from '"android/soong/android".AndroidBaseContext' -to 'BaseContext'
gorename -from '"android/soong/android".AndroidModuleBase' -to 'ModuleBase'
gorename -from '"android/soong/android".AndroidBottomUpMutatorContext' -to 'BottomUpMutatorContext'
gorename -from '"android/soong/android".AndroidTopDownMutatorContext' -to 'TopDownMutatorContext'
gorename -from '"android/soong/android".AndroidModule' -to 'Module'
Change-Id: I3b23590b8ce7c8a1ea1139411d84a53163288da7
diff --git a/android/module.go b/android/module.go
new file mode 100644
index 0000000..08abf78
--- /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] }