Add tests for ctx.InstallFile
Add tests that cover Soong-only installation as well as installation with
InstallBypassMake.
Bug: 204136549
Test: TestInstall
Test: TestInstallBypassMake
Change-Id: Iac22c9fdf99994e06b419623ee5fa399ef6957fb
diff --git a/android/Android.bp b/android/Android.bp
index 6450a06..cfa2be3 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -19,6 +19,9 @@
"soong-ui-metrics_proto",
"golang-protobuf-proto",
"golang-protobuf-encoding-prototext",
+
+ // Only used for tests.
+ "androidmk-parser",
],
srcs: [
"androidmk.go",
diff --git a/android/makevars.go b/android/makevars.go
index 20db65a..7d8864a 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -142,15 +142,19 @@
var singletonMakeVarsProvidersKey = NewOnceKey("singletonMakeVarsProvidersKey")
+func getSingletonMakevarsProviders(config Config) *[]makeVarsProvider {
+ return config.Once(singletonMakeVarsProvidersKey, func() interface{} {
+ return &[]makeVarsProvider{}
+ }).(*[]makeVarsProvider)
+}
+
// registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to
// the list of MakeVarsProviders to run.
func registerSingletonMakeVarsProvider(config Config, singleton SingletonMakeVarsProvider) {
// Singletons are registered on the Context and may be different between different Contexts,
// for example when running multiple tests. Store the SingletonMakeVarsProviders in the
// Config so they are attached to the Context.
- singletonMakeVarsProviders := config.Once(singletonMakeVarsProvidersKey, func() interface{} {
- return &[]makeVarsProvider{}
- }).(*[]makeVarsProvider)
+ singletonMakeVarsProviders := getSingletonMakevarsProviders(config)
*singletonMakeVarsProviders = append(*singletonMakeVarsProviders,
makeVarsProvider{pctx, singletonMakeVarsProviderAdapter(singleton)})
@@ -175,7 +179,9 @@
return &makeVarsSingleton{}
}
-type makeVarsSingleton struct{}
+type makeVarsSingleton struct {
+ installsForTesting []byte
+}
type makeVarsProvider struct {
pctx PackageContext
@@ -238,7 +244,7 @@
var katiSymlinks []katiInstall
providers := append([]makeVarsProvider(nil), makeVarsInitProviders...)
- providers = append(providers, *ctx.Config().Get(singletonMakeVarsProvidersKey).(*[]makeVarsProvider)...)
+ providers = append(providers, *getSingletonMakevarsProviders(ctx.Config())...)
for _, provider := range providers {
mctx := &makeVarsContext{
@@ -313,6 +319,8 @@
if err := pathtools.WriteFileIfChanged(installsFile, installsBytes, 0666); err != nil {
ctx.Errorf(err.Error())
}
+
+ s.installsForTesting = installsBytes
}
func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {
diff --git a/android/module_test.go b/android/module_test.go
index 9e2b0ca..236cae7 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -15,7 +15,12 @@
package android
import (
+ "bytes"
+ "path/filepath"
+ "runtime"
"testing"
+
+ mkparser "android/soong/androidmk/parser"
)
func TestSrcIsModule(t *testing.T) {
@@ -199,17 +204,28 @@
}
}
+func (m *depsModule) InstallBypassMake() bool {
+ return true
+}
+
func (m *depsModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ outputFile := PathForModuleOut(ctx, ctx.ModuleName())
+ ctx.Build(pctx, BuildParams{
+ Rule: Touch,
+ Output: outputFile,
+ })
+ installFile := ctx.InstallFile(PathForModuleInstall(ctx), ctx.ModuleName(), outputFile)
+ ctx.InstallSymlink(PathForModuleInstall(ctx, "symlinks"), ctx.ModuleName(), installFile)
}
func (m *depsModule) DepsMutator(ctx BottomUpMutatorContext) {
- ctx.AddDependency(ctx.Module(), nil, m.props.Deps...)
+ ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
}
func depsModuleFactory() Module {
m := &depsModule{}
m.AddProperties(&m.props)
- InitAndroidModule(m)
+ InitAndroidArchModule(m, HostAndDeviceDefault, MultilibCommon)
return m
}
@@ -320,3 +336,282 @@
ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(expectedErrs)).
RunTestWithBp(t, bp)
}
+
+func TestInstall(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ t.Skip("requires linux")
+ }
+ bp := `
+ deps {
+ name: "foo",
+ deps: ["bar"],
+ }
+
+ deps {
+ name: "bar",
+ deps: ["baz", "qux"],
+ }
+
+ deps {
+ name: "baz",
+ deps: ["qux"],
+ }
+
+ deps {
+ name: "qux",
+ }
+ `
+
+ result := GroupFixturePreparers(
+ prepareForModuleTests,
+ PrepareForTestWithArchMutator,
+ ).RunTestWithBp(t, bp)
+
+ module := func(name string, host bool) TestingModule {
+ variant := "android_common"
+ if host {
+ variant = result.Config.BuildOSCommonTarget.String()
+ }
+ return result.ModuleForTests(name, variant)
+ }
+
+ outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
+
+ installRule := func(name string) TestingBuildParams {
+ return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system", name))
+ }
+
+ symlinkRule := func(name string) TestingBuildParams {
+ return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system/symlinks", name))
+ }
+
+ hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
+
+ hostInstallRule := func(name string) TestingBuildParams {
+ return module(name, true).Output(filepath.Join("out/soong/host/linux-x86", name))
+ }
+
+ hostSymlinkRule := func(name string) TestingBuildParams {
+ return module(name, true).Output(filepath.Join("out/soong/host/linux-x86/symlinks", name))
+ }
+
+ assertInputs := func(params TestingBuildParams, inputs ...Path) {
+ t.Helper()
+ AssertArrayString(t, "expected inputs", Paths(inputs).Strings(),
+ append(PathsIfNonNil(params.Input), params.Inputs...).Strings())
+ }
+
+ assertImplicits := func(params TestingBuildParams, implicits ...Path) {
+ t.Helper()
+ AssertArrayString(t, "expected implicit dependencies", Paths(implicits).Strings(),
+ append(PathsIfNonNil(params.Implicit), params.Implicits...).Strings())
+ }
+
+ assertOrderOnlys := func(params TestingBuildParams, orderonlys ...Path) {
+ t.Helper()
+ AssertArrayString(t, "expected orderonly dependencies", Paths(orderonlys).Strings(),
+ params.OrderOnly.Strings())
+ }
+
+ // Check host install rule dependencies
+ assertInputs(hostInstallRule("foo"), hostOutputRule("foo").Output)
+ assertImplicits(hostInstallRule("foo"),
+ hostInstallRule("bar").Output,
+ hostSymlinkRule("bar").Output,
+ hostInstallRule("baz").Output,
+ hostSymlinkRule("baz").Output,
+ hostInstallRule("qux").Output,
+ hostSymlinkRule("qux").Output,
+ )
+ assertOrderOnlys(hostInstallRule("foo"))
+
+ // Check host symlink rule dependencies
+ assertInputs(hostSymlinkRule("foo"), hostInstallRule("foo").Output)
+ assertImplicits(hostSymlinkRule("foo"))
+ assertOrderOnlys(hostSymlinkRule("foo"))
+
+ // Check device install rule dependencies
+ assertInputs(installRule("foo"), outputRule("foo").Output)
+ assertImplicits(installRule("foo"))
+ assertOrderOnlys(installRule("foo"),
+ installRule("bar").Output,
+ symlinkRule("bar").Output,
+ installRule("baz").Output,
+ symlinkRule("baz").Output,
+ installRule("qux").Output,
+ symlinkRule("qux").Output,
+ )
+
+ // Check device symlink rule dependencies
+ assertInputs(symlinkRule("foo"), installRule("foo").Output)
+ assertImplicits(symlinkRule("foo"))
+ assertOrderOnlys(symlinkRule("foo"))
+}
+
+func TestInstallBypassMake(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ t.Skip("requires linux")
+ }
+ bp := `
+ deps {
+ name: "foo",
+ deps: ["bar"],
+ }
+
+ deps {
+ name: "bar",
+ deps: ["baz", "qux"],
+ }
+
+ deps {
+ name: "baz",
+ deps: ["qux"],
+ }
+
+ deps {
+ name: "qux",
+ }
+ `
+
+ result := GroupFixturePreparers(
+ prepareForModuleTests,
+ PrepareForTestWithArchMutator,
+ FixtureModifyConfig(SetKatiEnabledForTests),
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
+ }),
+ ).RunTestWithBp(t, bp)
+
+ installs := result.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).installsForTesting
+ buf := bytes.NewBuffer(append([]byte(nil), installs...))
+ parser := mkparser.NewParser("makevars", buf)
+
+ nodes, errs := parser.Parse()
+ if len(errs) > 0 {
+ t.Fatalf("error parsing install rules: %s", errs[0])
+ }
+
+ rules := parseMkRules(t, result.Config, nodes)
+
+ module := func(name string, host bool) TestingModule {
+ variant := "android_common"
+ if host {
+ variant = result.Config.BuildOSCommonTarget.String()
+ }
+ return result.ModuleForTests(name, variant)
+ }
+
+ outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
+
+ ruleForOutput := func(output string) installMakeRule {
+ for _, rule := range rules {
+ if rule.target == output {
+ return rule
+ }
+ }
+ t.Fatalf("no make install rule for %s", output)
+ return installMakeRule{}
+ }
+
+ installRule := func(name string) installMakeRule {
+ return ruleForOutput(filepath.Join("out/target/product/test_device/system", name))
+ }
+
+ symlinkRule := func(name string) installMakeRule {
+ return ruleForOutput(filepath.Join("out/target/product/test_device/system/symlinks", name))
+ }
+
+ hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
+
+ hostInstallRule := func(name string) installMakeRule {
+ return ruleForOutput(filepath.Join("out/host/linux-x86", name))
+ }
+
+ hostSymlinkRule := func(name string) installMakeRule {
+ return ruleForOutput(filepath.Join("out/host/linux-x86/symlinks", name))
+ }
+
+ assertDeps := func(rule installMakeRule, deps ...string) {
+ t.Helper()
+ AssertArrayString(t, "expected inputs", deps, rule.deps)
+ }
+
+ assertOrderOnlys := func(rule installMakeRule, orderonlys ...string) {
+ t.Helper()
+ AssertArrayString(t, "expected orderonly dependencies", orderonlys, rule.orderOnlyDeps)
+ }
+
+ // Check host install rule dependencies
+ assertDeps(hostInstallRule("foo"),
+ hostOutputRule("foo").Output.String(),
+ hostInstallRule("bar").target,
+ hostSymlinkRule("bar").target,
+ hostInstallRule("baz").target,
+ hostSymlinkRule("baz").target,
+ hostInstallRule("qux").target,
+ hostSymlinkRule("qux").target,
+ )
+ assertOrderOnlys(hostInstallRule("foo"))
+
+ // Check host symlink rule dependencies
+ assertDeps(hostSymlinkRule("foo"))
+ assertOrderOnlys(hostSymlinkRule("foo"), hostInstallRule("foo").target)
+
+ // Check device install rule dependencies
+ assertDeps(installRule("foo"), outputRule("foo").Output.String())
+ assertOrderOnlys(installRule("foo"),
+ installRule("bar").target,
+ symlinkRule("bar").target,
+ installRule("baz").target,
+ symlinkRule("baz").target,
+ installRule("qux").target,
+ symlinkRule("qux").target,
+ )
+
+ // Check device symlink rule dependencies
+ assertDeps(symlinkRule("foo"))
+ assertOrderOnlys(symlinkRule("foo"), installRule("foo").target)
+}
+
+type installMakeRule struct {
+ target string
+ deps []string
+ orderOnlyDeps []string
+}
+
+func parseMkRules(t *testing.T, config Config, nodes []mkparser.Node) []installMakeRule {
+ var rules []installMakeRule
+ for _, node := range nodes {
+ if mkParserRule, ok := node.(*mkparser.Rule); ok {
+ var rule installMakeRule
+
+ if targets := mkParserRule.Target.Words(); len(targets) == 0 {
+ t.Fatalf("no targets for rule %s", mkParserRule.Dump())
+ } else if len(targets) > 1 {
+ t.Fatalf("unsupported multiple targets for rule %s", mkParserRule.Dump())
+ } else if !targets[0].Const() {
+ t.Fatalf("unsupported non-const target for rule %s", mkParserRule.Dump())
+ } else {
+ rule.target = normalizeStringRelativeToTop(config, targets[0].Value(nil))
+ }
+
+ prereqList := &rule.deps
+ for _, prereq := range mkParserRule.Prerequisites.Words() {
+ if !prereq.Const() {
+ t.Fatalf("unsupported non-const prerequisite for rule %s", mkParserRule.Dump())
+ }
+
+ if prereq.Value(nil) == "|" {
+ prereqList = &rule.orderOnlyDeps
+ continue
+ }
+
+ *prereqList = append(*prereqList, normalizeStringRelativeToTop(config, prereq.Value(nil)))
+ }
+
+ rules = append(rules, rule)
+ }
+ }
+
+ return rules
+}