Initial prebuilt support
Support prebuilt shared libraries as an initial proof-of-concept of
prebuilts. Future changes will support binaries and static libraries,
and the ability to select which to use based on something besides
blueprint properties.
Test: TestPrebuilts run during m -j
Change-Id: I6e84da667e9005ae11844bad01d25cbe4ced1ce3
diff --git a/android/androidmk.go b/android/androidmk.go
index 28c2290..469ac7f 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -34,6 +34,7 @@
type AndroidMkDataProvider interface {
AndroidMk() (AndroidMkData, error)
+ BaseModuleName() string
}
type AndroidMkData struct {
@@ -142,13 +143,12 @@
}
func translateAndroidMkModule(ctx blueprint.SingletonContext, w io.Writer, mod blueprint.Module) error {
- name := ctx.ModuleName(mod)
-
provider, ok := mod.(AndroidMkDataProvider)
if !ok {
return nil
}
+ name := provider.BaseModuleName()
amod := mod.(Module).base()
data, err := provider.AndroidMk()
if err != nil {
@@ -156,7 +156,11 @@
}
if !amod.Enabled() {
- return err
+ return nil
+ }
+
+ if amod.commonProperties.SkipInstall {
+ return nil
}
if data.SubName != "" {
diff --git a/android/config.go b/android/config.go
index b29012c..1d3fba2 100644
--- a/android/config.go
+++ b/android/config.go
@@ -151,6 +151,11 @@
return nil
}
+// TestConfig returns a Config object suitable for using for tests
+func TestConfig() Config {
+ return Config{&config{}}
+}
+
// 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) {
diff --git a/android/module.go b/android/module.go
index 52280d2..572b162 100644
--- a/android/module.go
+++ b/android/module.go
@@ -147,6 +147,8 @@
// Set by InitAndroidModule
HostOrDeviceSupported HostOrDeviceSupported `blueprint:"mutated"`
ArchSpecific bool `blueprint:"mutated"`
+
+ SkipInstall bool `blueprint:"mutated"`
}
type hostAndDeviceProperties struct {
@@ -277,10 +279,17 @@
hooks hooks
}
+// Name returns the name of the module. It may be overridden by individual module types, for
+// example prebuilts will prepend prebuilt_ to the name.
func (a *ModuleBase) Name() string {
return a.nameProperties.Name
}
+// BaseModuleName returns the name of the module as specified in the blueprints file.
+func (a *ModuleBase) BaseModuleName() string {
+ return a.nameProperties.Name
+}
+
func (a *ModuleBase) base() *ModuleBase {
return a
}
@@ -348,6 +357,10 @@
return *a.commonProperties.Enabled
}
+func (a *ModuleBase) SkipInstall() {
+ a.commonProperties.SkipInstall = true
+}
+
func (a *ModuleBase) computeInstallDeps(
ctx blueprint.ModuleContext) Paths {
@@ -600,7 +613,9 @@
fullInstallPath := installPath.Join(a, name)
a.module.base().hooks.runInstallHooks(a, fullInstallPath, false)
- if a.Host() || !a.AConfig().SkipDeviceInstall() {
+ if !a.module.base().commonProperties.SkipInstall &&
+ (a.Host() || !a.AConfig().SkipDeviceInstall()) {
+
deps = append(deps, a.installDeps...)
var implicitDeps, orderOnlyDeps Paths
@@ -636,7 +651,9 @@
fullInstallPath := installPath.Join(a, name)
a.module.base().hooks.runInstallHooks(a, fullInstallPath, true)
- if a.Host() || !a.AConfig().SkipDeviceInstall() {
+ if !a.module.base().commonProperties.SkipInstall &&
+ (a.Host() || !a.AConfig().SkipDeviceInstall()) {
+
a.ModuleBuild(pctx, ModuleBuildParams{
Rule: Symlink,
Output: fullInstallPath,
diff --git a/android/mutator.go b/android/mutator.go
index b375bce..8114b3e 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -33,6 +33,7 @@
}
ctx.TopDown("load_hooks", loadHookMutator).Parallel()
+ ctx.BottomUp("prebuilts", prebuiltMutator).Parallel()
ctx.BottomUp("defaults_deps", defaultsDepsMutator).Parallel()
ctx.TopDown("defaults", defaultsMutator).Parallel()
@@ -45,6 +46,9 @@
ctx.BottomUp("deps", depsMutator).Parallel()
+ ctx.BottomUp("prebuilt_replace", PrebuiltReplaceMutator).Parallel()
+ ctx.TopDown("prebuilt_disable", PrebuiltDisableMutator).Parallel()
+
register(postDeps)
}
diff --git a/android/prebuilt.go b/android/prebuilt.go
new file mode 100644
index 0000000..fb9e515
--- /dev/null
+++ b/android/prebuilt.go
@@ -0,0 +1,121 @@
+// 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 "github.com/google/blueprint"
+
+// This file implements common functionality for handling modules that may exist as prebuilts,
+// source, or both.
+
+var prebuiltDependencyTag blueprint.BaseDependencyTag
+
+func SourceModuleHasPrebuilt(ctx ModuleContext) OptionalPath {
+ var path Path
+ ctx.VisitDirectDeps(func(m blueprint.Module) {
+ if ctx.OtherModuleDependencyTag(m) == prebuiltDependencyTag {
+ p := m.(PrebuiltInterface).Prebuilt()
+ if p.usePrebuilt(ctx) {
+ path = p.Path(ctx)
+ }
+ }
+ })
+
+ return OptionalPathForPath(path)
+}
+
+type Prebuilt struct {
+ Properties struct {
+ Srcs []string `android:"arch_variant"`
+ // When prefer is set to true the prebuilt will be used instead of any source module with
+ // a matching name.
+ Prefer bool `android:"arch_variant"`
+
+ SourceExists bool `blueprint:"mutated"`
+ }
+ module Module
+}
+
+func (p *Prebuilt) Name(name string) string {
+ return "prebuilt_" + name
+}
+
+func (p *Prebuilt) Path(ctx ModuleContext) Path {
+ if len(p.Properties.Srcs) == 0 {
+ ctx.PropertyErrorf("srcs", "missing prebuilt source file")
+ return nil
+ }
+
+ if len(p.Properties.Srcs) > 1 {
+ ctx.PropertyErrorf("srcs", "multiple prebuilt source files")
+ return nil
+ }
+
+ return PathForModuleSrc(ctx, p.Properties.Srcs[0])
+}
+
+type PrebuiltInterface interface {
+ Module
+ Prebuilt() *Prebuilt
+}
+
+type PrebuiltSourceInterface interface {
+ SkipInstall()
+}
+
+// prebuiltMutator ensures that there is always a module with an undecorated name, and marks
+// prebuilt modules that have both a prebuilt and a source module.
+func prebuiltMutator(ctx BottomUpMutatorContext) {
+ if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
+ p := m.Prebuilt()
+ name := m.base().BaseModuleName()
+ if ctx.OtherModuleExists(name) {
+ ctx.AddReverseDependency(ctx.Module(), prebuiltDependencyTag, name)
+ p.Properties.SourceExists = true
+ } else {
+ ctx.Rename(name)
+ }
+ }
+}
+
+// PrebuiltReplaceMutator replaces dependencies on the source module with dependencies on the prebuilt
+// when both modules exist and the prebuilt should be used.
+func PrebuiltReplaceMutator(ctx BottomUpMutatorContext) {
+ if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
+ p := m.Prebuilt()
+ name := m.base().BaseModuleName()
+ if p.Properties.SourceExists && p.usePrebuilt(ctx) {
+ ctx.ReplaceDependencies(name)
+ }
+ }
+}
+
+// PrebuiltDisableMutator disables source modules that have prebuilts that should be used instead.
+func PrebuiltDisableMutator(ctx TopDownMutatorContext) {
+ if s, ok := ctx.Module().(PrebuiltSourceInterface); ok {
+ ctx.VisitDirectDeps(func(m blueprint.Module) {
+ if ctx.OtherModuleDependencyTag(m) == prebuiltDependencyTag {
+ p := m.(PrebuiltInterface).Prebuilt()
+ if p.usePrebuilt(ctx) {
+ s.SkipInstall()
+ }
+ }
+ })
+ }
+}
+
+func (p *Prebuilt) usePrebuilt(ctx BaseContext) bool {
+ // TODO: use p.Properties.Name and ctx.ModuleDir to override prefer
+ return p.Properties.Prefer && len(p.Properties.Srcs) > 0
+}
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
new file mode 100644
index 0000000..92d6481
--- /dev/null
+++ b/android/prebuilt_test.go
@@ -0,0 +1,206 @@
+// 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 (
+ "testing"
+
+ "github.com/google/blueprint"
+)
+
+var prebuiltsTests = []struct {
+ name string
+ modules string
+ prebuilt bool
+}{
+ {
+ name: "no prebuilt",
+ modules: `
+ source {
+ name: "bar",
+ }`,
+ prebuilt: false,
+ },
+ {
+ name: "no source prebuilt not preferred",
+ modules: `
+ prebuilt {
+ name: "bar",
+ prefer: false,
+ srcs: ["prebuilt"],
+ }`,
+ prebuilt: true,
+ },
+ {
+ name: "no source prebuilt preferred",
+ modules: `
+ prebuilt {
+ name: "bar",
+ prefer: true,
+ srcs: ["prebuilt"],
+ }`,
+ prebuilt: true,
+ },
+ {
+ name: "prebuilt not preferred",
+ modules: `
+ source {
+ name: "bar",
+ }
+
+ prebuilt {
+ name: "bar",
+ prefer: false,
+ srcs: ["prebuilt"],
+ }`,
+ prebuilt: false,
+ },
+ {
+ name: "prebuilt preferred",
+ modules: `
+ source {
+ name: "bar",
+ }
+
+ prebuilt {
+ name: "bar",
+ prefer: true,
+ srcs: ["prebuilt"],
+ }`,
+ prebuilt: true,
+ },
+}
+
+func TestPrebuilts(t *testing.T) {
+ for _, test := range prebuiltsTests {
+ t.Run(test.name, func(t *testing.T) {
+ ctx := NewContext()
+ ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
+ ctx.RegisterModuleType("source", newSourceModule)
+ ctx.MockFileSystem(map[string][]byte{
+ "Blueprints": []byte(`
+ source {
+ name: "foo",
+ deps: ["bar"],
+ }
+ ` + test.modules),
+ })
+
+ config := TestConfig()
+
+ _, errs := ctx.ParseBlueprintsFiles("Blueprints")
+ fail(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ fail(t, errs)
+
+ foo := findModule(ctx, "foo")
+ if foo == nil {
+ t.Fatalf("failed to find module foo")
+ }
+
+ if test.prebuilt {
+ if !foo.(*sourceModule).dependsOnPrebuiltModule {
+ t.Errorf("doesn't depend on prebuilt module")
+ }
+
+ if foo.(*sourceModule).dependsOnSourceModule {
+ t.Errorf("depends on source module")
+ }
+ } else {
+ if foo.(*sourceModule).dependsOnPrebuiltModule {
+ t.Errorf("depends on prebuilt module")
+ }
+
+ if !foo.(*sourceModule).dependsOnSourceModule {
+ t.Errorf("doens't depend on source module")
+ }
+ }
+ })
+ }
+
+}
+
+type prebuiltModule struct {
+ ModuleBase
+ prebuilt Prebuilt
+}
+
+func newPrebuiltModule() (blueprint.Module, []interface{}) {
+ m := &prebuiltModule{}
+ return InitAndroidModule(m, &m.prebuilt.Properties)
+}
+
+func (p *prebuiltModule) Name() string {
+ return p.prebuilt.Name(p.ModuleBase.Name())
+}
+
+func (p *prebuiltModule) DepsMutator(ctx BottomUpMutatorContext) {
+}
+
+func (p *prebuiltModule) GenerateAndroidBuildActions(ModuleContext) {
+}
+
+func (p *prebuiltModule) Prebuilt() *Prebuilt {
+ return &p.prebuilt
+}
+
+type sourceModule struct {
+ ModuleBase
+ properties struct {
+ Deps []string
+ }
+ dependsOnSourceModule, dependsOnPrebuiltModule bool
+}
+
+func newSourceModule() (blueprint.Module, []interface{}) {
+ m := &sourceModule{}
+ return InitAndroidModule(m, &m.properties)
+}
+
+func (s *sourceModule) DepsMutator(ctx BottomUpMutatorContext) {
+ for _, d := range s.properties.Deps {
+ ctx.AddDependency(ctx.Module(), nil, d)
+ }
+}
+
+func (s *sourceModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ ctx.VisitDirectDeps(func(m blueprint.Module) {
+ if _, ok := m.(*sourceModule); ok {
+ s.dependsOnSourceModule = true
+ }
+ if _, ok := m.(*prebuiltModule); ok {
+ s.dependsOnPrebuiltModule = true
+ }
+ })
+}
+
+func findModule(ctx *blueprint.Context, name string) blueprint.Module {
+ var ret blueprint.Module
+ ctx.VisitAllModules(func(m blueprint.Module) {
+ if ctx.ModuleName(m) == name {
+ ret = m
+ }
+ })
+ return ret
+}
+
+func fail(t *testing.T, errs []error) {
+ if len(errs) > 0 {
+ for _, err := range errs {
+ t.Error(err)
+ }
+ t.FailNow()
+ }
+}