diff options
82 files changed, 2047 insertions, 1510 deletions
diff --git a/android/androidmk_test.go b/android/androidmk_test.go index 2f568fb13..230b1ec0b 100644 --- a/android/androidmk_test.go +++ b/android/androidmk_test.go @@ -141,21 +141,17 @@ func customModuleFactory() Module { // bp module and then returns the config and the custom module called "foo". func buildContextAndCustomModuleFoo(t *testing.T, bp string) (*TestContext, *customModule) { t.Helper() - config := TestConfig(buildDir, nil, bp, nil) - config.katiEnabled = true // Enable androidmk Singleton - - ctx := NewTestContext(config) - ctx.RegisterSingletonType("androidmk", AndroidMkSingleton) - ctx.RegisterModuleType("custom", customModuleFactory) - ctx.Register() - - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) - - module := ctx.ModuleForTests("foo", "").Module().(*customModule) - return ctx, module + result := emptyTestFixtureFactory.RunTest(t, + // Enable androidmk Singleton + PrepareForTestWithAndroidMk, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("custom", customModuleFactory) + }), + FixtureWithRootAndroidBp(bp), + ) + + module := result.ModuleForTests("foo", "").Module().(*customModule) + return result.TestContext, module } func TestAndroidMkSingleton_PassesUpdatedAndroidMkDataToCustomCallback(t *testing.T) { diff --git a/android/apex.go b/android/apex.go index 79d8cdda3..0d5cac812 100644 --- a/android/apex.go +++ b/android/apex.go @@ -257,6 +257,9 @@ type ApexProperties struct { } // Marker interface that identifies dependencies that are excluded from APEX contents. +// +// Unless the tag also implements the AlwaysRequireApexVariantTag this will prevent an apex variant +// from being created for the module. type ExcludeFromApexContentsTag interface { blueprint.DependencyTag @@ -264,6 +267,17 @@ type ExcludeFromApexContentsTag interface { ExcludeFromApexContents() } +// Marker interface that identifies dependencies that always requires an APEX variant to be created. +// +// It is possible for a dependency to require an apex variant but exclude the module from the APEX +// contents. See sdk.sdkMemberDependencyTag. +type AlwaysRequireApexVariantTag interface { + blueprint.DependencyTag + + // Return true if this tag requires that the target dependency has an apex variant. + AlwaysRequireApexVariant() bool +} + // Marker interface that identifies dependencies that should inherit the DirectlyInAnyApex state // from the parent to the child. For example, stubs libraries are marked as DirectlyInAnyApex if // their implementation is in an apex. diff --git a/android/apex_test.go b/android/apex_test.go index 1f786e65f..b5323e8af 100644 --- a/android/apex_test.go +++ b/android/apex_test.go @@ -121,7 +121,7 @@ func Test_mergeApexVariations(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - config := TestConfig(buildDir, nil, "", nil) + config := TestConfig(t.TempDir(), nil, "", nil) ctx := &configErrorWrapper{config: config} gotMerged, gotAliases := mergeApexVariations(ctx, tt.in) if !reflect.DeepEqual(gotMerged, tt.wantMerged) { diff --git a/android/arch_test.go b/android/arch_test.go index 4cef4c8e6..09cb52370 100644 --- a/android/arch_test.go +++ b/android/arch_test.go @@ -273,6 +273,13 @@ func archTestModuleFactory() Module { return m } +var prepareForArchTest = GroupFixturePreparers( + PrepareForTestWithArchMutator, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("module", archTestModuleFactory) + }), +) + func TestArchMutator(t *testing.T) { var buildOSVariants []string var buildOS32Variants []string @@ -309,7 +316,7 @@ func TestArchMutator(t *testing.T) { testCases := []struct { name string - config func(Config) + preparer FixturePreparer fooVariants []string barVariants []string bazVariants []string @@ -317,7 +324,7 @@ func TestArchMutator(t *testing.T) { }{ { name: "normal", - config: nil, + preparer: nil, fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"}, barVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"), bazVariants: nil, @@ -325,11 +332,11 @@ func TestArchMutator(t *testing.T) { }, { name: "host-only", - config: func(config Config) { + preparer: FixtureModifyConfig(func(config Config) { config.BuildOSTarget = Target{} config.BuildOSCommonTarget = Target{} config.Targets[Android] = nil - }, + }), fooVariants: nil, barVariants: buildOSVariants, bazVariants: nil, @@ -351,19 +358,13 @@ func TestArchMutator(t *testing.T) { for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { - config := TestArchConfig(buildDir, nil, bp, nil) - - ctx := NewTestArchContext(config) - ctx.RegisterModuleType("module", archTestModuleFactory) - ctx.Register() - if tt.config != nil { - tt.config(config) - } - - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) + result := emptyTestFixtureFactory.RunTest(t, + prepareForArchTest, + // Test specific preparer + OptionalFixturePreparer(tt.preparer), + FixtureWithRootAndroidBp(bp), + ) + ctx := result.TestContext if g, w := enabledVariants(ctx, "foo"), tt.fooVariants; !reflect.DeepEqual(w, g) { t.Errorf("want foo variants:\n%q\ngot:\n%q\n", w, g) @@ -412,14 +413,14 @@ func TestArchMutatorNativeBridge(t *testing.T) { testCases := []struct { name string - config func(Config) + preparer FixturePreparer fooVariants []string barVariants []string bazVariants []string }{ { name: "normal", - config: nil, + preparer: nil, fooVariants: []string{"android_x86_64_silvermont", "android_x86_silvermont"}, barVariants: []string{"android_x86_64_silvermont", "android_native_bridge_arm64_armv8-a", "android_x86_silvermont", "android_native_bridge_arm_armv7-a-neon"}, bazVariants: []string{"android_native_bridge_arm64_armv8-a", "android_native_bridge_arm_armv7-a-neon"}, @@ -440,19 +441,23 @@ func TestArchMutatorNativeBridge(t *testing.T) { for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { - config := TestArchConfigNativeBridge(buildDir, nil, bp, nil) - - ctx := NewTestArchContext(config) - ctx.RegisterModuleType("module", archTestModuleFactory) - ctx.Register() - if tt.config != nil { - tt.config(config) - } - - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) + result := emptyTestFixtureFactory.RunTest(t, + prepareForArchTest, + // Test specific preparer + OptionalFixturePreparer(tt.preparer), + // Prepare for native bridge test + FixtureModifyConfig(func(config Config) { + config.Targets[Android] = []Target{ + {Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false}, + {Android, Arch{ArchType: X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", "", false}, + {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled, "x86_64", "arm64", false}, + {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled, "x86", "arm", false}, + } + }), + FixtureWithRootAndroidBp(bp), + ) + + ctx := result.TestContext if g, w := enabledVariants(ctx, "foo"), tt.fooVariants; !reflect.DeepEqual(w, g) { t.Errorf("want foo variants:\n%q\ngot:\n%q\n", w, g) diff --git a/android/bazel_handler.go b/android/bazel_handler.go index bbec389e9..0595d68a1 100644 --- a/android/bazel_handler.go +++ b/android/bazel_handler.go @@ -717,7 +717,7 @@ func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) { // Add ninja file dependencies for files which all bazel invocations require. bazelBuildList := absolutePath(filepath.Join( - filepath.Dir(bootstrap.ModuleListFile), "bazel.list")) + filepath.Dir(bootstrap.CmdlineModuleListFile()), "bazel.list")) ctx.AddNinjaFileDeps(bazelBuildList) data, err := ioutil.ReadFile(bazelBuildList) diff --git a/android/config.go b/android/config.go index 614386f6f..e09173155 100644 --- a/android/config.go +++ b/android/config.go @@ -19,6 +19,7 @@ package android import ( "encoding/json" + "errors" "fmt" "io/ioutil" "os" @@ -68,6 +69,18 @@ func (c Config) BuildDir() string { return c.buildDir } +func (c Config) NinjaBuildDir() string { + return c.buildDir +} + +func (c Config) DebugCompilation() bool { + return false // Never compile Go code in the main build for debugging +} + +func (c Config) SrcDir() string { + return c.srcDir +} + // A DeviceConfig object represents the configuration for a particular device // being built. For now there will only be one of these, but in the future there // may be multiple devices being built. @@ -270,23 +283,6 @@ func TestConfig(buildDir string, env map[string]string, bp string, fs map[string return Config{config} } -// TestArchConfigNativeBridge returns a Config object suitable for using -// for tests that need to run the arch mutator for native bridge supported -// archs. -func TestArchConfigNativeBridge(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config { - testConfig := TestArchConfig(buildDir, env, bp, fs) - config := testConfig.config - - config.Targets[Android] = []Target{ - {Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false}, - {Android, Arch{ArchType: X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", "", false}, - {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled, "x86_64", "arm64", false}, - {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled, "x86", "arm", false}, - } - - return testConfig -} - func fuchsiaTargets() map[OsType][]Target { return map[OsType][]Target{ Fuchsia: { @@ -498,6 +494,10 @@ func (c *config) SetStopBefore(stopBefore bootstrap.StopBefore) { c.stopBefore = stopBefore } +func (c *config) SetAllowMissingDependencies() { + c.productVariables.Allow_missing_dependencies = proptools.BoolPtr(true) +} + var _ bootstrap.ConfigStopBefore = (*config)(nil) // BlueprintToolLocation returns the directory containing build system tools @@ -1005,7 +1005,7 @@ func (c *config) DexpreoptGlobalConfig(ctx PathContext) ([]byte, error) { } func (c *config) FrameworksBaseDirExists(ctx PathContext) bool { - return ExistentPathForSource(ctx, "frameworks", "base").Valid() + return ExistentPathForSource(ctx, "frameworks", "base", "Android.bp").Valid() } func (c *config) VndkSnapshotBuildArtifacts() bool { @@ -1623,6 +1623,20 @@ func (l *ConfiguredJarList) UnmarshalJSON(b []byte) error { return nil } +func (l *ConfiguredJarList) MarshalJSON() ([]byte, error) { + if len(l.apexes) != len(l.jars) { + return nil, errors.New(fmt.Sprintf("Inconsistent ConfiguredJarList: apexes: %q, jars: %q", l.apexes, l.jars)) + } + + list := make([]string, 0, len(l.apexes)) + + for i := 0; i < len(l.apexes); i++ { + list = append(list, l.apexes[i]+":"+l.jars[i]) + } + + return json.Marshal(list) +} + // ModuleStem hardcodes the stem of framework-minus-apex to return "framework". // // TODO(b/139391334): hard coded until we find a good way to query the stem of a diff --git a/android/config_test.go b/android/config_test.go index a11115d9d..9df5288a1 100644 --- a/android/config_test.go +++ b/android/config_test.go @@ -16,6 +16,7 @@ package android import ( "fmt" + "path/filepath" "reflect" "strings" "testing" @@ -87,6 +88,37 @@ func TestMissingVendorConfig(t *testing.T) { } } +func verifyProductVariableMarshaling(t *testing.T, v productVariables) { + dir := t.TempDir() + path := filepath.Join(dir, "test.variables") + err := saveToConfigFile(&v, path) + if err != nil { + t.Errorf("Couldn't save default product config: %q", err) + } + + var v2 productVariables + err = loadFromConfigFile(&v2, path) + if err != nil { + t.Errorf("Couldn't load default product config: %q", err) + } +} +func TestDefaultProductVariableMarshaling(t *testing.T) { + v := productVariables{} + v.SetDefaultConfig() + verifyProductVariableMarshaling(t, v) +} + +func TestBootJarsMarshaling(t *testing.T) { + v := productVariables{} + v.SetDefaultConfig() + v.BootJars = ConfiguredJarList{ + apexes: []string{"apex"}, + jars: []string{"jar"}, + } + + verifyProductVariableMarshaling(t, v) +} + func assertStringEquals(t *testing.T, expected, actual string) { if actual != expected { t.Errorf("expected %q found %q", expected, actual) diff --git a/android/csuite_config.go b/android/csuite_config.go index 56d240874..20bd03563 100644 --- a/android/csuite_config.go +++ b/android/csuite_config.go @@ -15,7 +15,11 @@ package android func init() { - RegisterModuleType("csuite_config", CSuiteConfigFactory) + registerCSuiteBuildComponents(InitRegistrationContext) +} + +func registerCSuiteBuildComponents(ctx RegistrationContext) { + ctx.RegisterModuleType("csuite_config", CSuiteConfigFactory) } type csuiteConfigProperties struct { diff --git a/android/csuite_config_test.go b/android/csuite_config_test.go index 9ac959e18..d30ff6926 100644 --- a/android/csuite_config_test.go +++ b/android/csuite_config_test.go @@ -18,32 +18,21 @@ import ( "testing" ) -func testCSuiteConfig(test *testing.T, bpFileContents string) *TestContext { - config := TestArchConfig(buildDir, nil, bpFileContents, nil) - - ctx := NewTestArchContext(config) - ctx.RegisterModuleType("csuite_config", CSuiteConfigFactory) - ctx.Register() - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(test, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(test, errs) - return ctx -} - func TestCSuiteConfig(t *testing.T) { - ctx := testCSuiteConfig(t, ` -csuite_config { name: "plain"} -csuite_config { name: "with_manifest", test_config: "manifest.xml" } -`) + result := emptyTestFixtureFactory.RunTest(t, + PrepareForTestWithArchMutator, + FixtureRegisterWithContext(registerCSuiteBuildComponents), + FixtureWithRootAndroidBp(` + csuite_config { name: "plain"} + csuite_config { name: "with_manifest", test_config: "manifest.xml" } + `), + ) - variants := ctx.ModuleVariantsForTests("plain") + variants := result.ModuleVariantsForTests("plain") if len(variants) > 1 { t.Errorf("expected 1, got %d", len(variants)) } - expectedOutputFilename := ctx.ModuleForTests( + outputFilename := result.ModuleForTests( "plain", variants[0]).Module().(*CSuiteConfig).OutputFilePath.Base() - if expectedOutputFilename != "plain" { - t.Errorf("expected plain, got %q", expectedOutputFilename) - } + AssertStringEquals(t, "output file name", "plain", outputFilename) } diff --git a/android/defaults_test.go b/android/defaults_test.go index 2689d864e..b33cb527c 100644 --- a/android/defaults_test.go +++ b/android/defaults_test.go @@ -15,10 +15,7 @@ package android import ( - "reflect" "testing" - - "github.com/google/blueprint/proptools" ) type defaultsTestProperties struct { @@ -58,6 +55,14 @@ func defaultsTestDefaultsFactory() Module { return defaults } +var prepareForDefaultsTest = GroupFixturePreparers( + PrepareForTestWithDefaults, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("test", defaultsTestModuleFactory) + ctx.RegisterModuleType("defaults", defaultsTestDefaultsFactory) + }), +) + func TestDefaults(t *testing.T) { bp := ` defaults { @@ -78,27 +83,14 @@ func TestDefaults(t *testing.T) { } ` - config := TestConfig(buildDir, nil, bp, nil) - - ctx := NewTestContext(config) - - ctx.RegisterModuleType("test", defaultsTestModuleFactory) - ctx.RegisterModuleType("defaults", defaultsTestDefaultsFactory) - - ctx.PreArchMutators(RegisterDefaultsPreArchMutators) - - ctx.Register() + result := emptyTestFixtureFactory.RunTest(t, + prepareForDefaultsTest, + FixtureWithRootAndroidBp(bp), + ) - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) + foo := result.Module("foo", "").(*defaultsTestModule) - foo := ctx.ModuleForTests("foo", "").Module().(*defaultsTestModule) - - if g, w := foo.properties.Foo, []string{"transitive", "defaults", "module"}; !reflect.DeepEqual(g, w) { - t.Errorf("expected foo %q, got %q", w, g) - } + AssertDeepEquals(t, "foo", []string{"transitive", "defaults", "module"}, foo.properties.Foo) } func TestDefaultsAllowMissingDependencies(t *testing.T) { @@ -122,34 +114,18 @@ func TestDefaultsAllowMissingDependencies(t *testing.T) { } ` - config := TestConfig(buildDir, nil, bp, nil) - config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true) - - ctx := NewTestContext(config) - ctx.SetAllowMissingDependencies(true) - - ctx.RegisterModuleType("test", defaultsTestModuleFactory) - ctx.RegisterModuleType("defaults", defaultsTestDefaultsFactory) - - ctx.PreArchMutators(RegisterDefaultsPreArchMutators) - - ctx.Register() - - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) + result := emptyTestFixtureFactory.RunTest(t, + prepareForDefaultsTest, + PrepareForTestWithAllowMissingDependencies, + FixtureWithRootAndroidBp(bp), + ) - missingDefaults := ctx.ModuleForTests("missing_defaults", "").Output("out") - missingTransitiveDefaults := ctx.ModuleForTests("missing_transitive_defaults", "").Output("out") + missingDefaults := result.ModuleForTests("missing_defaults", "").Output("out") + missingTransitiveDefaults := result.ModuleForTests("missing_transitive_defaults", "").Output("out") - if missingDefaults.Rule != ErrorRule { - t.Errorf("expected missing_defaults rule to be ErrorRule, got %#v", missingDefaults.Rule) - } + AssertSame(t, "missing_defaults rule", ErrorRule, missingDefaults.Rule) - if g, w := missingDefaults.Args["error"], "module missing_defaults missing dependencies: missing\n"; g != w { - t.Errorf("want error %q, got %q", w, g) - } + AssertStringEquals(t, "missing_defaults", "module missing_defaults missing dependencies: missing\n", missingDefaults.Args["error"]) // TODO: missing transitive defaults is currently not handled _ = missingTransitiveDefaults diff --git a/android/defs.go b/android/defs.go index 1a767214c..1a7c459ec 100644 --- a/android/defs.go +++ b/android/defs.go @@ -145,7 +145,7 @@ var ( func buildWriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) { content = echoEscaper.Replace(content) - content = proptools.ShellEscape(content) + content = proptools.NinjaEscape(proptools.ShellEscapeIncludingSpaces(content)) if content == "" { content = "''" } diff --git a/android/deptag_test.go b/android/deptag_test.go index bdd449ee2..7f5589659 100644 --- a/android/deptag_test.go +++ b/android/deptag_test.go @@ -80,21 +80,20 @@ func TestInstallDependencyTag(t *testing.T) { } ` - config := TestArchConfig(buildDir, nil, bp, nil) - ctx := NewTestArchContext(config) + result := emptyTestFixtureFactory.RunTest(t, + PrepareForTestWithArchMutator, + FixtureWithRootAndroidBp(bp), + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("test_module", testInstallDependencyTagModuleFactory) + }), + ) - ctx.RegisterModuleType("test_module", testInstallDependencyTagModuleFactory) + config := result.Config - ctx.Register() - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) - - hostFoo := ctx.ModuleForTests("foo", config.BuildOSCommonTarget.String()).Description("install") - hostInstallDep := ctx.ModuleForTests("install_dep", config.BuildOSCommonTarget.String()).Description("install") - hostTransitive := ctx.ModuleForTests("transitive", config.BuildOSCommonTarget.String()).Description("install") - hostDep := ctx.ModuleForTests("dep", config.BuildOSCommonTarget.String()).Description("install") + hostFoo := result.ModuleForTests("foo", config.BuildOSCommonTarget.String()).Description("install") + hostInstallDep := result.ModuleForTests("install_dep", config.BuildOSCommonTarget.String()).Description("install") + hostTransitive := result.ModuleForTests("transitive", config.BuildOSCommonTarget.String()).Description("install") + hostDep := result.ModuleForTests("dep", config.BuildOSCommonTarget.String()).Description("install") if g, w := hostFoo.Implicits.Strings(), hostInstallDep.Output.String(); !InList(w, g) { t.Errorf("expected host dependency %q, got %q", w, g) @@ -112,10 +111,10 @@ func TestInstallDependencyTag(t *testing.T) { t.Errorf("expected no host dependency %q, got %q", w, g) } - deviceFoo := ctx.ModuleForTests("foo", "android_common").Description("install") - deviceInstallDep := ctx.ModuleForTests("install_dep", "android_common").Description("install") - deviceTransitive := ctx.ModuleForTests("transitive", "android_common").Description("install") - deviceDep := ctx.ModuleForTests("dep", "android_common").Description("install") + deviceFoo := result.ModuleForTests("foo", "android_common").Description("install") + deviceInstallDep := result.ModuleForTests("install_dep", "android_common").Description("install") + deviceTransitive := result.ModuleForTests("transitive", "android_common").Description("install") + deviceDep := result.ModuleForTests("dep", "android_common").Description("install") if g, w := deviceFoo.OrderOnly.Strings(), deviceInstallDep.Output.String(); !InList(w, g) { t.Errorf("expected device dependency %q, got %q", w, g) diff --git a/android/fixture.go b/android/fixture.go index 928967d8c..4e445c0b0 100644 --- a/android/fixture.go +++ b/android/fixture.go @@ -381,6 +381,19 @@ func GroupFixturePreparers(preparers ...FixturePreparer) FixturePreparer { return &compositeFixturePreparer{dedupAndFlattenPreparers(nil, preparers)} } +// NullFixturePreparer is a preparer that does nothing. +var NullFixturePreparer = GroupFixturePreparers() + +// OptionalFixturePreparer will return the supplied preparer if it is non-nil, otherwise it will +// return the NullFixturePreparer +func OptionalFixturePreparer(preparer FixturePreparer) FixturePreparer { + if preparer == nil { + return NullFixturePreparer + } else { + return preparer + } +} + type simpleFixturePreparerVisitor func(preparer *simpleFixturePreparer) // FixturePreparer is an opaque interface that can change a fixture. @@ -493,6 +506,14 @@ var FixtureExpectsNoErrors = FixtureCustomErrorHandler( }, ) +// FixtureIgnoreErrors ignores any errors. +// +// If this is used then it is the responsibility of the test to check the TestResult.Errs does not +// contain any unexpected errors. +var FixtureIgnoreErrors = FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) { + // Ignore the errors +}) + // FixtureExpectsAtLeastOneMatchingError returns an error handler that will cause the test to fail // if at least one error that matches the regular expression is not found. // @@ -561,6 +582,10 @@ type TestResult struct { // The errors that were reported during the test. Errs []error + + // The ninja deps is a list of the ninja files dependencies that were added by the modules and + // singletons via the *.AddNinjaFileDeps() methods. + NinjaDeps []string } var _ FixtureFactory = (*fixtureFactory)(nil) @@ -701,9 +726,14 @@ func (f *fixture) RunTest() *TestResult { } ctx.Register() - _, errs := ctx.ParseBlueprintsFiles("ignored") + var ninjaDeps []string + extraNinjaDeps, errs := ctx.ParseBlueprintsFiles("ignored") if len(errs) == 0 { - _, errs = ctx.PrepareBuildActions(f.config) + ninjaDeps = append(ninjaDeps, extraNinjaDeps...) + extraNinjaDeps, errs = ctx.PrepareBuildActions(f.config) + if len(errs) == 0 { + ninjaDeps = append(ninjaDeps, extraNinjaDeps...) + } } result := &TestResult{ @@ -711,6 +741,7 @@ func (f *fixture) RunTest() *TestResult { fixture: f, Config: f.config, Errs: errs, + NinjaDeps: ninjaDeps, } f.errorHandler.CheckErrors(f.t, result) diff --git a/android/fixture_test.go b/android/fixture_test.go index a31ef1654..0042e5b16 100644 --- a/android/fixture_test.go +++ b/android/fixture_test.go @@ -30,9 +30,10 @@ func TestFixtureDedup(t *testing.T) { preparer1 := appendToList("preparer1") preparer2 := appendToList("preparer2") preparer3 := appendToList("preparer3") - preparer4 := appendToList("preparer4") + preparer4 := OptionalFixturePreparer(appendToList("preparer4")) + nilPreparer := OptionalFixturePreparer(nil) - preparer1Then2 := GroupFixturePreparers(preparer1, preparer2) + preparer1Then2 := GroupFixturePreparers(preparer1, preparer2, nilPreparer) preparer2Then1 := GroupFixturePreparers(preparer2, preparer1) diff --git a/android/module_test.go b/android/module_test.go index e3cc61332..99bf30afc 100644 --- a/android/module_test.go +++ b/android/module_test.go @@ -163,6 +163,10 @@ func depsModuleFactory() Module { return m } +var prepareForModuleTests = FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("deps", depsModuleFactory) +}) + func TestErrorDependsOnDisabledModule(t *testing.T) { bp := ` deps { @@ -175,20 +179,15 @@ func TestErrorDependsOnDisabledModule(t *testing.T) { } ` - config := TestConfig(buildDir, nil, bp, nil) - - ctx := NewTestContext(config) - ctx.RegisterModuleType("deps", depsModuleFactory) - ctx.Register() - - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfNoMatchingErrors(t, `module "foo": depends on disabled module "bar"`, errs) + emptyTestFixtureFactory. + ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo": depends on disabled module "bar"`)). + RunTest(t, + prepareForModuleTests, + FixtureWithRootAndroidBp(bp)) } func TestValidateCorrectBuildParams(t *testing.T) { - config := TestConfig(buildDir, nil, "", nil) + config := TestConfig(t.TempDir(), nil, "", nil) pathContext := PathContextForTesting(config) bparams := convertBuildParams(BuildParams{ // Test with Output @@ -214,7 +213,7 @@ func TestValidateCorrectBuildParams(t *testing.T) { } func TestValidateIncorrectBuildParams(t *testing.T) { - config := TestConfig(buildDir, nil, "", nil) + config := TestConfig(t.TempDir(), nil, "", nil) pathContext := PathContextForTesting(config) params := BuildParams{ Output: PathForOutput(pathContext, "regular_output"), @@ -257,16 +256,6 @@ func TestDistErrorChecking(t *testing.T) { } ` - config := TestConfig(buildDir, nil, bp, nil) - - ctx := NewTestContext(config) - ctx.RegisterModuleType("deps", depsModuleFactory) - ctx.Register() - - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - expectedErrs := []string{ "\\QAndroid.bp:5:13: module \"foo\": dist.dest: Path is outside directory: ../invalid-dest\\E", "\\QAndroid.bp:6:12: module \"foo\": dist.dir: Path is outside directory: ../invalid-dir\\E", @@ -278,5 +267,10 @@ func TestDistErrorChecking(t *testing.T) { "\\QAndroid.bp:17:14: module \"foo\": dists[1].dir: Path is outside directory: ../invalid-dir1\\E", "\\QAndroid.bp:18:17: module \"foo\": dists[1].suffix: Suffix may not contain a '/' character.\\E", } - CheckErrorsAgainstExpectations(t, errs, expectedErrs) + + emptyTestFixtureFactory. + ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(expectedErrs)). + RunTest(t, + prepareForModuleTests, + FixtureWithRootAndroidBp(bp)) } diff --git a/android/mutator_test.go b/android/mutator_test.go index 1c395c782..46d26d1be 100644 --- a/android/mutator_test.go +++ b/android/mutator_test.go @@ -16,12 +16,10 @@ package android import ( "fmt" - "reflect" "strings" "testing" "github.com/google/blueprint" - "github.com/google/blueprint/proptools" ) type mutatorTestModule struct { @@ -67,28 +65,20 @@ func TestMutatorAddMissingDependencies(t *testing.T) { } ` - config := TestConfig(buildDir, nil, bp, nil) - config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true) - - ctx := NewTestContext(config) - ctx.SetAllowMissingDependencies(true) - - ctx.RegisterModuleType("test", mutatorTestModuleFactory) - ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.TopDown("add_missing_dependencies", addMissingDependenciesMutator) - }) - - ctx.Register() - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) + result := emptyTestFixtureFactory.RunTest(t, + PrepareForTestWithAllowMissingDependencies, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("test", mutatorTestModuleFactory) + ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { + ctx.TopDown("add_missing_dependencies", addMissingDependenciesMutator) + }) + }), + FixtureWithRootAndroidBp(bp), + ) - foo := ctx.ModuleForTests("foo", "").Module().(*mutatorTestModule) + foo := result.ModuleForTests("foo", "").Module().(*mutatorTestModule) - if g, w := foo.missingDeps, []string{"added_missing_dep", "regular_missing_dep"}; !reflect.DeepEqual(g, w) { - t.Errorf("want foo missing deps %q, got %q", w, g) - } + AssertDeepEquals(t, "foo missing deps", []string{"added_missing_dep", "regular_missing_dep"}, foo.missingDeps) } func TestModuleString(t *testing.T) { @@ -98,52 +88,47 @@ func TestModuleString(t *testing.T) { } ` - config := TestConfig(buildDir, nil, bp, nil) - - ctx := NewTestContext(config) - var moduleStrings []string - ctx.PreArchMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("pre_arch", func(ctx BottomUpMutatorContext) { - moduleStrings = append(moduleStrings, ctx.Module().String()) - ctx.CreateVariations("a", "b") - }) - ctx.TopDown("rename_top_down", func(ctx TopDownMutatorContext) { - moduleStrings = append(moduleStrings, ctx.Module().String()) - ctx.Rename(ctx.Module().base().Name() + "_renamed1") - }) - }) - - ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("pre_deps", func(ctx BottomUpMutatorContext) { - moduleStrings = append(moduleStrings, ctx.Module().String()) - ctx.CreateVariations("c", "d") - }) - }) - - ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("post_deps", func(ctx BottomUpMutatorContext) { - moduleStrings = append(moduleStrings, ctx.Module().String()) - ctx.CreateLocalVariations("e", "f") - }) - ctx.BottomUp("rename_bottom_up", func(ctx BottomUpMutatorContext) { - moduleStrings = append(moduleStrings, ctx.Module().String()) - ctx.Rename(ctx.Module().base().Name() + "_renamed2") - }) - ctx.BottomUp("final", func(ctx BottomUpMutatorContext) { - moduleStrings = append(moduleStrings, ctx.Module().String()) - }) - }) + emptyTestFixtureFactory.RunTest(t, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + + ctx.PreArchMutators(func(ctx RegisterMutatorsContext) { + ctx.BottomUp("pre_arch", func(ctx BottomUpMutatorContext) { + moduleStrings = append(moduleStrings, ctx.Module().String()) + ctx.CreateVariations("a", "b") + }) + ctx.TopDown("rename_top_down", func(ctx TopDownMutatorContext) { + moduleStrings = append(moduleStrings, ctx.Module().String()) + ctx.Rename(ctx.Module().base().Name() + "_renamed1") + }) + }) - ctx.RegisterModuleType("test", mutatorTestModuleFactory) + ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { + ctx.BottomUp("pre_deps", func(ctx BottomUpMutatorContext) { + moduleStrings = append(moduleStrings, ctx.Module().String()) + ctx.CreateVariations("c", "d") + }) + }) - ctx.Register() + ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) { + ctx.BottomUp("post_deps", func(ctx BottomUpMutatorContext) { + moduleStrings = append(moduleStrings, ctx.Module().String()) + ctx.CreateLocalVariations("e", "f") + }) + ctx.BottomUp("rename_bottom_up", func(ctx BottomUpMutatorContext) { + moduleStrings = append(moduleStrings, ctx.Module().String()) + ctx.Rename(ctx.Module().base().Name() + "_renamed2") + }) + ctx.BottomUp("final", func(ctx BottomUpMutatorContext) { + moduleStrings = append(moduleStrings, ctx.Module().String()) + }) + }) - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) + ctx.RegisterModuleType("test", mutatorTestModuleFactory) + }), + FixtureWithRootAndroidBp(bp), + ) want := []string{ // Initial name. @@ -184,9 +169,7 @@ func TestModuleString(t *testing.T) { "foo_renamed2{pre_arch:b,pre_deps:d,post_deps:f}", } - if !reflect.DeepEqual(moduleStrings, want) { - t.Errorf("want module String() values:\n%q\ngot:\n%q", want, moduleStrings) - } + AssertDeepEquals(t, "module String() values", want, moduleStrings) } func TestFinalDepsPhase(t *testing.T) { @@ -202,52 +185,46 @@ func TestFinalDepsPhase(t *testing.T) { } ` - config := TestConfig(buildDir, nil, bp, nil) - - ctx := NewTestContext(config) - finalGot := map[string]int{} - dep1Tag := struct { - blueprint.BaseDependencyTag - }{} - dep2Tag := struct { - blueprint.BaseDependencyTag - }{} - - ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("far_deps_1", func(ctx BottomUpMutatorContext) { - if !strings.HasPrefix(ctx.ModuleName(), "common_dep") { - ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep1Tag, "common_dep_1") - } - }) - ctx.BottomUp("variant", func(ctx BottomUpMutatorContext) { - ctx.CreateLocalVariations("a", "b") - }) - }) - - ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("far_deps_2", func(ctx BottomUpMutatorContext) { - if !strings.HasPrefix(ctx.ModuleName(), "common_dep") { - ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep2Tag, "common_dep_2") - } - }) - ctx.BottomUp("final", func(ctx BottomUpMutatorContext) { - finalGot[ctx.Module().String()] += 1 - ctx.VisitDirectDeps(func(mod Module) { - finalGot[fmt.Sprintf("%s -> %s", ctx.Module().String(), mod)] += 1 + emptyTestFixtureFactory.RunTest(t, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + dep1Tag := struct { + blueprint.BaseDependencyTag + }{} + dep2Tag := struct { + blueprint.BaseDependencyTag + }{} + + ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) { + ctx.BottomUp("far_deps_1", func(ctx BottomUpMutatorContext) { + if !strings.HasPrefix(ctx.ModuleName(), "common_dep") { + ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep1Tag, "common_dep_1") + } + }) + ctx.BottomUp("variant", func(ctx BottomUpMutatorContext) { + ctx.CreateLocalVariations("a", "b") + }) }) - }) - }) - ctx.RegisterModuleType("test", mutatorTestModuleFactory) - - ctx.Register() + ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) { + ctx.BottomUp("far_deps_2", func(ctx BottomUpMutatorContext) { + if !strings.HasPrefix(ctx.ModuleName(), "common_dep") { + ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep2Tag, "common_dep_2") + } + }) + ctx.BottomUp("final", func(ctx BottomUpMutatorContext) { + finalGot[ctx.Module().String()] += 1 + ctx.VisitDirectDeps(func(mod Module) { + finalGot[fmt.Sprintf("%s -> %s", ctx.Module().String(), mod)] += 1 + }) + }) + }) - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) + ctx.RegisterModuleType("test", mutatorTestModuleFactory) + }), + FixtureWithRootAndroidBp(bp), + ) finalWant := map[string]int{ "common_dep_1{variant:a}": 1, @@ -262,37 +239,31 @@ func TestFinalDepsPhase(t *testing.T) { "foo{variant:b} -> common_dep_2{variant:a}": 1, } - if !reflect.DeepEqual(finalWant, finalGot) { - t.Errorf("want:\n%q\ngot:\n%q", finalWant, finalGot) - } + AssertDeepEquals(t, "final", finalWant, finalGot) } func TestNoCreateVariationsInFinalDeps(t *testing.T) { - config := TestConfig(buildDir, nil, `test {name: "foo"}`, nil) - ctx := NewTestContext(config) - checkErr := func() { if err := recover(); err == nil || !strings.Contains(fmt.Sprintf("%s", err), "not allowed in FinalDepsMutators") { panic("Expected FinalDepsMutators consistency check to fail") } } - ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("vars", func(ctx BottomUpMutatorContext) { - defer checkErr() - ctx.CreateVariations("a", "b") - }) - ctx.BottomUp("local_vars", func(ctx BottomUpMutatorContext) { - defer checkErr() - ctx.CreateLocalVariations("a", "b") - }) - }) - - ctx.RegisterModuleType("test", mutatorTestModuleFactory) - ctx.Register() + emptyTestFixtureFactory.RunTest(t, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) { + ctx.BottomUp("vars", func(ctx BottomUpMutatorContext) { + defer checkErr() + ctx.CreateVariations("a", "b") + }) + ctx.BottomUp("local_vars", func(ctx BottomUpMutatorContext) { + defer checkErr() + ctx.CreateLocalVariations("a", "b") + }) + }) - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) + ctx.RegisterModuleType("test", mutatorTestModuleFactory) + }), + FixtureWithRootAndroidBp(`test {name: "foo"}`), + ) } diff --git a/android/namespace_test.go b/android/namespace_test.go index 45e2cdb7b..1caf5a87f 100644 --- a/android/namespace_test.go +++ b/android/namespace_test.go @@ -143,7 +143,7 @@ func TestDependingOnModuleInImportedNamespace(t *testing.T) { } func TestDependingOnModuleInNonImportedNamespace(t *testing.T) { - _, errs := setupTestExpectErrs( + _, errs := setupTestExpectErrs(t, map[string]string{ "dir1": ` soong_namespace { @@ -378,7 +378,7 @@ func TestTwoNamespacesCanImportEachOther(t *testing.T) { } func TestImportingNonexistentNamespace(t *testing.T) { - _, errs := setupTestExpectErrs( + _, errs := setupTestExpectErrs(t, map[string]string{ "dir1": ` soong_namespace { @@ -402,7 +402,7 @@ func TestImportingNonexistentNamespace(t *testing.T) { } func TestNamespacesDontInheritParentNamespaces(t *testing.T) { - _, errs := setupTestExpectErrs( + _, errs := setupTestExpectErrs(t, map[string]string{ "dir1": ` soong_namespace { @@ -455,7 +455,7 @@ func TestModulesDoReceiveParentNamespace(t *testing.T) { } func TestNamespaceImportsNotTransitive(t *testing.T) { - _, errs := setupTestExpectErrs( + _, errs := setupTestExpectErrs(t, map[string]string{ "dir1": ` soong_namespace { @@ -496,7 +496,7 @@ Module "a" can be found in these namespaces: ["dir1"]`), } func TestTwoNamepacesInSameDir(t *testing.T) { - _, errs := setupTestExpectErrs( + _, errs := setupTestExpectErrs(t, map[string]string{ "dir1": ` soong_namespace { @@ -516,7 +516,7 @@ func TestTwoNamepacesInSameDir(t *testing.T) { } func TestNamespaceNotAtTopOfFile(t *testing.T) { - _, errs := setupTestExpectErrs( + _, errs := setupTestExpectErrs(t, map[string]string{ "dir1": ` test_module { @@ -537,7 +537,7 @@ func TestNamespaceNotAtTopOfFile(t *testing.T) { } func TestTwoModulesWithSameNameInSameNamespace(t *testing.T) { - _, errs := setupTestExpectErrs( + _, errs := setupTestExpectErrs(t, map[string]string{ "dir1": ` soong_namespace { @@ -562,7 +562,7 @@ func TestTwoModulesWithSameNameInSameNamespace(t *testing.T) { } func TestDeclaringNamespaceInNonAndroidBpFile(t *testing.T) { - _, errs := setupTestFromFiles( + _, errs := setupTestFromFiles(t, map[string][]byte{ "Android.bp": []byte(` build = ["include.bp"] @@ -632,39 +632,38 @@ func mockFiles(bps map[string]string) (files map[string][]byte) { return files } -func setupTestFromFiles(bps map[string][]byte) (ctx *TestContext, errs []error) { - config := TestConfig(buildDir, nil, "", bps) +func setupTestFromFiles(t *testing.T, bps MockFS) (ctx *TestContext, errs []error) { + result := emptyTestFixtureFactory. + // Ignore errors for now so tests can check them later. + ExtendWithErrorHandler(FixtureIgnoreErrors). + RunTest(t, + FixtureModifyContext(func(ctx *TestContext) { + ctx.RegisterModuleType("test_module", newTestModule) + ctx.RegisterModuleType("soong_namespace", NamespaceFactory) + ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule) + ctx.PreArchMutators(RegisterNamespaceMutator) + ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { + ctx.BottomUp("rename", renameMutator) + }) + }), + bps.AddToFixture(), + ) - ctx = NewTestContext(config) - ctx.RegisterModuleType("test_module", newTestModule) - ctx.RegisterModuleType("soong_namespace", NamespaceFactory) - ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule) - ctx.PreArchMutators(RegisterNamespaceMutator) - ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("rename", renameMutator) - }) - ctx.Register() - - _, errs = ctx.ParseBlueprintsFiles("Android.bp") - if len(errs) > 0 { - return ctx, errs - } - _, errs = ctx.PrepareBuildActions(config) - return ctx, errs + return result.TestContext, result.Errs } -func setupTestExpectErrs(bps map[string]string) (ctx *TestContext, errs []error) { +func setupTestExpectErrs(t *testing.T, bps map[string]string) (ctx *TestContext, errs []error) { files := make(map[string][]byte, len(bps)) files["Android.bp"] = []byte("") for dir, text := range bps { files[filepath.Join(dir, "Android.bp")] = []byte(text) } - return setupTestFromFiles(files) + return setupTestFromFiles(t, files) } func setupTest(t *testing.T, bps map[string]string) (ctx *TestContext) { t.Helper() - ctx, errs := setupTestExpectErrs(bps) + ctx, errs := setupTestExpectErrs(t, bps) FailIfErrored(t, errs) return ctx } diff --git a/android/neverallow_test.go b/android/neverallow_test.go index b761065ca..5ac97e775 100644 --- a/android/neverallow_test.go +++ b/android/neverallow_test.go @@ -28,7 +28,7 @@ var neverallowTests = []struct { rules []Rule // Additional contents to add to the virtual filesystem used by the tests. - fs map[string][]byte + fs MockFS // The expected error patterns. If empty then no errors are expected, otherwise each error // reported must be matched by at least one of these patterns. A pattern matches if the error @@ -285,41 +285,36 @@ var neverallowTests = []struct { }, } +var prepareForNeverAllowTest = GroupFixturePreparers( + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("cc_library", newMockCcLibraryModule) + ctx.RegisterModuleType("java_library", newMockJavaLibraryModule) + ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule) + ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule) + ctx.RegisterModuleType("makefile_goal", newMockMakefileGoalModule) + ctx.PostDepsMutators(RegisterNeverallowMutator) + }), +) + func TestNeverallow(t *testing.T) { for _, test := range neverallowTests { - // Create a test per config to allow for test specific config, e.g. test rules. - config := TestConfig(buildDir, nil, "", test.fs) - t.Run(test.name, func(t *testing.T) { - // If the test has its own rules then use them instead of the default ones. - if test.rules != nil { - SetTestNeverallowRules(config, test.rules) - } - _, errs := testNeverallow(config) - CheckErrorsAgainstExpectations(t, errs, test.expectedErrors) + emptyTestFixtureFactory. + ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)). + RunTest(t, + prepareForNeverAllowTest, + FixtureModifyConfig(func(config Config) { + // If the test has its own rules then use them instead of the default ones. + if test.rules != nil { + SetTestNeverallowRules(config, test.rules) + } + }), + test.fs.AddToFixture(), + ) }) } } -func testNeverallow(config Config) (*TestContext, []error) { - ctx := NewTestContext(config) - ctx.RegisterModuleType("cc_library", newMockCcLibraryModule) - ctx.RegisterModuleType("java_library", newMockJavaLibraryModule) - ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule) - ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule) - ctx.RegisterModuleType("makefile_goal", newMockMakefileGoalModule) - ctx.PostDepsMutators(RegisterNeverallowMutator) - ctx.Register() - - _, errs := ctx.ParseBlueprintsFiles("Android.bp") - if len(errs) > 0 { - return ctx, errs - } - - _, errs = ctx.PrepareBuildActions(config) - return ctx, errs -} - type mockCcLibraryProperties struct { Include_dirs []string Vendor_available *bool diff --git a/android/ninja_deps_test.go b/android/ninja_deps_test.go index d3775edd9..7e5864d5b 100644 --- a/android/ninja_deps_test.go +++ b/android/ninja_deps_test.go @@ -53,23 +53,20 @@ func (testNinjaDepsSingleton) GenerateBuildActions(ctx SingletonContext) { } func TestNinjaDeps(t *testing.T) { - fs := map[string][]byte{ + fs := MockFS{ "test_ninja_deps/exists": nil, } - config := TestConfig(buildDir, nil, "", fs) - ctx := NewTestContext(config) - ctx.RegisterSingletonType("test_ninja_deps_singleton", testNinjaDepsSingletonFactory) - ctx.RegisterSingletonType("ninja_deps_singleton", ninjaDepsSingletonFactory) - ctx.Register() - - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - ninjaDeps, errs := ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) + result := emptyTestFixtureFactory.RunTest(t, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterSingletonType("test_ninja_deps_singleton", testNinjaDepsSingletonFactory) + ctx.RegisterSingletonType("ninja_deps_singleton", ninjaDepsSingletonFactory) + }), + fs.AddToFixture(), + ) // Verify that the ninja file has a dependency on the test_ninja_deps directory. - if g, w := ninjaDeps, "test_ninja_deps"; !InList(w, g) { + if g, w := result.NinjaDeps, "test_ninja_deps"; !InList(w, g) { t.Errorf("expected %q in %q", w, g) } } diff --git a/android/package_test.go b/android/package_test.go index 99be13ffa..d5b4db4a8 100644 --- a/android/package_test.go +++ b/android/package_test.go @@ -6,7 +6,7 @@ import ( var packageTests = []struct { name string - fs map[string][]byte + fs MockFS expectedErrors []string }{ // Package default_visibility handling is tested in visibility_test.go @@ -61,43 +61,13 @@ var packageTests = []struct { func TestPackage(t *testing.T) { for _, test := range packageTests { t.Run(test.name, func(t *testing.T) { - _, errs := testPackage(test.fs) - - expectedErrors := test.expectedErrors - if expectedErrors == nil { - FailIfErrored(t, errs) - } else { - for _, expectedError := range expectedErrors { - FailIfNoMatchingErrors(t, expectedError, errs) - } - if len(errs) > len(expectedErrors) { - t.Errorf("additional errors found, expected %d, found %d", len(expectedErrors), len(errs)) - for i, expectedError := range expectedErrors { - t.Errorf("expectedErrors[%d] = %s", i, expectedError) - } - for i, err := range errs { - t.Errorf("errs[%d] = %s", i, err) - } - } - } + emptyTestFixtureFactory. + ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)). + RunTest(t, + PrepareForTestWithArchMutator, + PrepareForTestWithPackageModule, + test.fs.AddToFixture(), + ) }) } } - -func testPackage(fs map[string][]byte) (*TestContext, []error) { - - // Create a new config per test as visibility information is stored in the config. - config := TestArchConfig(buildDir, nil, "", fs) - - ctx := NewTestArchContext(config) - RegisterPackageBuildComponents(ctx) - ctx.Register() - - _, errs := ctx.ParseBlueprintsFiles(".") - if len(errs) > 0 { - return ctx, errs - } - - _, errs = ctx.PrepareBuildActions(config) - return ctx, errs -} diff --git a/android/packaging_test.go b/android/packaging_test.go index 2c99b97a2..eb7f26fb8 100644 --- a/android/packaging_test.go +++ b/android/packaging_test.go @@ -15,7 +15,6 @@ package android import ( - "reflect" "testing" "github.com/google/blueprint" @@ -87,33 +86,30 @@ func (m *packageTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { func runPackagingTest(t *testing.T, multitarget bool, bp string, expected []string) { t.Helper() - config := TestArchConfig(buildDir, nil, bp, nil) - - ctx := NewTestArchContext(config) - ctx.RegisterModuleType("component", componentTestModuleFactory) - var archVariant string + var moduleFactory ModuleFactory if multitarget { archVariant = "android_common" - ctx.RegisterModuleType("package_module", packageMultiTargetTestModuleFactory) + moduleFactory = packageMultiTargetTestModuleFactory } else { archVariant = "android_arm64_armv8-a" - ctx.RegisterModuleType("package_module", packageTestModuleFactory) + moduleFactory = packageTestModuleFactory } - ctx.Register() - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) + result := emptyTestFixtureFactory.RunTest(t, + PrepareForTestWithArchMutator, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("component", componentTestModuleFactory) + ctx.RegisterModuleType("package_module", moduleFactory) + }), + FixtureWithRootAndroidBp(bp), + ) - p := ctx.ModuleForTests("package", archVariant).Module().(*packageTestModule) + p := result.Module("package", archVariant).(*packageTestModule) actual := p.entries actual = SortedUniqueStrings(actual) expected = SortedUniqueStrings(expected) - if !reflect.DeepEqual(actual, expected) { - t.Errorf("\ngot: %v\nexpected: %v\n", actual, expected) - } + AssertDeepEquals(t, "package entries", expected, actual) } func TestPackagingBaseMultiTarget(t *testing.T) { diff --git a/android/paths_test.go b/android/paths_test.go index 3734ed201..c5fc10ebb 100644 --- a/android/paths_test.go +++ b/android/paths_test.go @@ -21,8 +21,6 @@ import ( "strconv" "strings" "testing" - - "github.com/google/blueprint/proptools" ) type strsTestCase struct { @@ -977,7 +975,7 @@ type pathForModuleSrcTestCase struct { rel string } -func testPathForModuleSrc(t *testing.T, buildDir string, tests []pathForModuleSrcTestCase) { +func testPathForModuleSrc(t *testing.T, tests []pathForModuleSrcTestCase) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { fgBp := ` @@ -995,7 +993,7 @@ func testPathForModuleSrc(t *testing.T, buildDir string, tests []pathForModuleSr } ` - mockFS := map[string][]byte{ + mockFS := MockFS{ "fg/Android.bp": []byte(fgBp), "foo/Android.bp": []byte(test.bp), "ofp/Android.bp": []byte(ofpBp), @@ -1007,37 +1005,21 @@ func testPathForModuleSrc(t *testing.T, buildDir string, tests []pathForModuleSr "foo/src_special/$": nil, } - config := TestConfig(buildDir, nil, "", mockFS) - - ctx := NewTestContext(config) - - ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory) - ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory) - ctx.RegisterModuleType("filegroup", FileGroupFactory) - - ctx.Register() - _, errs := ctx.ParseFileList(".", []string{"fg/Android.bp", "foo/Android.bp", "ofp/Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) - - m := ctx.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule) - - if g, w := m.srcs, test.srcs; !reflect.DeepEqual(g, w) { - t.Errorf("want srcs %q, got %q", w, g) - } - - if g, w := m.rels, test.rels; !reflect.DeepEqual(g, w) { - t.Errorf("want rels %q, got %q", w, g) - } - - if g, w := m.src, test.src; g != w { - t.Errorf("want src %q, got %q", w, g) - } - - if g, w := m.rel, test.rel; g != w { - t.Errorf("want rel %q, got %q", w, g) - } + result := emptyTestFixtureFactory.RunTest(t, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory) + ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory) + ctx.RegisterModuleType("filegroup", FileGroupFactory) + }), + mockFS.AddToFixture(), + ) + + m := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule) + + AssertStringPathsRelativeToTopEquals(t, "srcs", result.Config, test.srcs, m.srcs) + AssertStringPathsRelativeToTopEquals(t, "rels", result.Config, test.rels, m.rels) + AssertStringPathRelativeToTopEquals(t, "src", result.Config, test.src, m.src) + AssertStringPathRelativeToTopEquals(t, "rel", result.Config, test.rel, m.rel) }) } } @@ -1094,7 +1076,7 @@ func TestPathsForModuleSrc(t *testing.T) { name: "foo", srcs: [":b"], }`, - srcs: []string{buildDir + "/.intermediates/ofp/b/gen/b"}, + srcs: []string{"out/soong/.intermediates/ofp/b/gen/b"}, rels: []string{"gen/b"}, }, { @@ -1104,7 +1086,7 @@ func TestPathsForModuleSrc(t *testing.T) { name: "foo", srcs: [":b{.tagged}"], }`, - srcs: []string{buildDir + "/.intermediates/ofp/b/gen/c"}, + srcs: []string{"out/soong/.intermediates/ofp/b/gen/c"}, rels: []string{"gen/c"}, }, { @@ -1119,7 +1101,7 @@ func TestPathsForModuleSrc(t *testing.T) { name: "c", outs: ["gen/c"], }`, - srcs: []string{buildDir + "/.intermediates/ofp/b/gen/b"}, + srcs: []string{"out/soong/.intermediates/ofp/b/gen/b"}, rels: []string{"gen/b"}, }, { @@ -1134,7 +1116,7 @@ func TestPathsForModuleSrc(t *testing.T) { }, } - testPathForModuleSrc(t, buildDir, tests) + testPathForModuleSrc(t, tests) } func TestPathForModuleSrc(t *testing.T) { @@ -1176,7 +1158,7 @@ func TestPathForModuleSrc(t *testing.T) { name: "foo", src: ":b", }`, - src: buildDir + "/.intermediates/ofp/b/gen/b", + src: "out/soong/.intermediates/ofp/b/gen/b", rel: "gen/b", }, { @@ -1186,7 +1168,7 @@ func TestPathForModuleSrc(t *testing.T) { name: "foo", src: ":b{.tagged}", }`, - src: buildDir + "/.intermediates/ofp/b/gen/c", + src: "out/soong/.intermediates/ofp/b/gen/c", rel: "gen/c", }, { @@ -1201,7 +1183,7 @@ func TestPathForModuleSrc(t *testing.T) { }, } - testPathForModuleSrc(t, buildDir, tests) + testPathForModuleSrc(t, tests) } func TestPathsForModuleSrc_AllowMissingDependencies(t *testing.T) { @@ -1221,44 +1203,24 @@ func TestPathsForModuleSrc_AllowMissingDependencies(t *testing.T) { } ` - config := TestConfig(buildDir, nil, bp, nil) - config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true) - - ctx := NewTestContext(config) - ctx.SetAllowMissingDependencies(true) - - ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory) - - ctx.Register() - - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) - - foo := ctx.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule) - - if g, w := foo.missingDeps, []string{"a", "b", "c"}; !reflect.DeepEqual(g, w) { - t.Errorf("want foo missing deps %q, got %q", w, g) - } - - if g, w := foo.srcs, []string{}; !reflect.DeepEqual(g, w) { - t.Errorf("want foo srcs %q, got %q", w, g) - } + result := emptyTestFixtureFactory.RunTest(t, + PrepareForTestWithAllowMissingDependencies, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory) + }), + FixtureWithRootAndroidBp(bp), + ) - if g, w := foo.src, ""; g != w { - t.Errorf("want foo src %q, got %q", w, g) - } + foo := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule) - bar := ctx.ModuleForTests("bar", "").Module().(*pathForModuleSrcTestModule) + AssertArrayString(t, "foo missing deps", []string{"a", "b", "c"}, foo.missingDeps) + AssertArrayString(t, "foo srcs", []string{}, foo.srcs) + AssertStringEquals(t, "foo src", "", foo.src) - if g, w := bar.missingDeps, []string{"d", "e"}; !reflect.DeepEqual(g, w) { - t.Errorf("want bar missing deps %q, got %q", w, g) - } + bar := result.ModuleForTests("bar", "").Module().(*pathForModuleSrcTestModule) - if g, w := bar.srcs, []string{}; !reflect.DeepEqual(g, w) { - t.Errorf("want bar srcs %q, got %q", w, g) - } + AssertArrayString(t, "bar missing deps", []string{"d", "e"}, bar.missingDeps) + AssertArrayString(t, "bar srcs", []string{}, bar.srcs) } func TestPathRelativeToTop(t *testing.T) { diff --git a/android/rule_builder.go b/android/rule_builder.go index 84501fe9f..75f1b5d9c 100644 --- a/android/rule_builder.go +++ b/android/rule_builder.go @@ -385,21 +385,26 @@ func (r *RuleBuilder) RspFileInputs() Paths { return rspFileInputs } -// Commands returns a slice containing the built command line for each call to RuleBuilder.Command. -func (r *RuleBuilder) Commands() []string { - var commands []string +// RspFile returns the path to the rspfile that was passed to the RuleBuilderCommand.FlagWithRspFileInputList method. +func (r *RuleBuilder) RspFile() WritablePath { + var rspFile WritablePath for _, c := range r.commands { - commands = append(commands, c.String()) + if c.rspFile != nil { + if rspFile != nil { + panic("Multiple commands in a rule may not have rsp file inputs") + } + rspFile = c.rspFile + } } - return commands + + return rspFile } -// NinjaEscapedCommands returns a slice containing the built command line after ninja escaping for each call to -// RuleBuilder.Command. -func (r *RuleBuilder) NinjaEscapedCommands() []string { +// Commands returns a slice containing the built command line for each call to RuleBuilder.Command. +func (r *RuleBuilder) Commands() []string { var commands []string for _, c := range r.commands { - commands = append(commands, c.NinjaEscapedString()) + commands = append(commands, c.String()) } return commands } @@ -458,9 +463,11 @@ func (r *RuleBuilder) Build(name string, desc string) { } tools := r.Tools() - commands := r.NinjaEscapedCommands() + commands := r.Commands() outputs := r.Outputs() inputs := r.Inputs() + rspFileInputs := r.RspFileInputs() + rspFilePath := r.RspFile() if len(commands) == 0 { return @@ -516,6 +523,12 @@ func (r *RuleBuilder) Build(name string, desc string) { }) } + // Outputs that were marked Temporary will not be checked that they are in the output + // directory by the loop above, check them here. + for path := range r.temporariesSet { + Rel(r.ctx, r.outDir.String(), path.String()) + } + // Add a hash of the list of input files to the manifest so that the textproto file // changes when the list of input files changes and causes the sbox rule that // depends on it to rerun. @@ -559,15 +572,14 @@ func (r *RuleBuilder) Build(name string, desc string) { } // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to - // ImplicitOutputs. RuleBuilder only uses "$out" for the rsp file location, so the distinction between Outputs and + // ImplicitOutputs. RuleBuilder doesn't use "$out", so the distinction between Outputs and // ImplicitOutputs doesn't matter. output := outputs[0] implicitOutputs := outputs[1:] var rspFile, rspFileContent string - rspFileInputs := r.RspFileInputs() - if rspFileInputs != nil { - rspFile = "$out.rsp" + if rspFilePath != nil { + rspFile = rspFilePath.String() rspFileContent = "$in" } @@ -585,10 +597,10 @@ func (r *RuleBuilder) Build(name string, desc string) { r.ctx.Build(r.pctx, BuildParams{ Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{ - Command: commandString, - CommandDeps: tools.Strings(), + Command: proptools.NinjaEscape(commandString), + CommandDeps: proptools.NinjaEscapeList(tools.Strings()), Restat: r.restat, - Rspfile: rspFile, + Rspfile: proptools.NinjaEscape(rspFile), RspfileContent: rspFileContent, Pool: pool, }), @@ -620,9 +632,7 @@ type RuleBuilderCommand struct { tools Paths packagedTools []PackagingSpec rspFileInputs Paths - - // spans [start,end) of the command that should not be ninja escaped - unescapedSpans [][2]int + rspFile WritablePath } func (c *RuleBuilderCommand) addInput(path Path) string { @@ -1020,8 +1030,9 @@ func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *Ru } // FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator -// between them. The paths will be written to the rspfile. -func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, paths Paths) *RuleBuilderCommand { +// between them. The paths will be written to the rspfile. If sbox is enabled, the rspfile must +// be outside the sbox directory. +func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand { if c.rspFileInputs != nil { panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided") } @@ -1033,10 +1044,16 @@ func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, paths Paths) } c.rspFileInputs = paths + c.rspFile = rspFile - rspFile := "$out.rsp" - c.FlagWithArg(flag, rspFile) - c.unescapedSpans = append(c.unescapedSpans, [2]int{c.buf.Len() - len(rspFile), c.buf.Len()}) + if c.rule.sbox { + if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel { + panic(fmt.Errorf("FlagWithRspFileInputList rspfile %q must not be inside out dir %q", + rspFile.String(), c.rule.outDir.String())) + } + } + + c.FlagWithArg(flag, rspFile.String()) return c } @@ -1045,11 +1062,6 @@ func (c *RuleBuilderCommand) String() string { return c.buf.String() } -// String returns the command line. -func (c *RuleBuilderCommand) NinjaEscapedString() string { - return ninjaEscapeExceptForSpans(c.String(), c.unescapedSpans) -} - // RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox() // and returns sbox testproto generated by the RuleBuilder. func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox_proto.Manifest { @@ -1063,25 +1075,6 @@ func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox return &manifest } -func ninjaEscapeExceptForSpans(s string, spans [][2]int) string { - if len(spans) == 0 { - return proptools.NinjaEscape(s) - } - - sb := strings.Builder{} - sb.Grow(len(s) * 11 / 10) - - i := 0 - for _, span := range spans { - sb.WriteString(proptools.NinjaEscape(s[i:span[0]])) - sb.WriteString(s[span[0]:span[1]]) - i = span[1] - } - sb.WriteString(proptools.NinjaEscape(s[i:])) - - return sb.String() -} - func ninjaNameEscape(s string) string { b := []byte(s) escaped := false diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go index 06ea1242d..bd3582070 100644 --- a/android/rule_builder_test.go +++ b/android/rule_builder_test.go @@ -17,7 +17,6 @@ package android import ( "fmt" "path/filepath" - "reflect" "regexp" "strings" "testing" @@ -267,10 +266,10 @@ func ExampleRuleBuilderCommand_FlagWithRspFileInputList() { ctx := builderContext() fmt.Println(NewRuleBuilder(pctx, ctx).Command(). Tool(PathForSource(ctx, "javac")). - FlagWithRspFileInputList("@", PathsForTesting("a.java", "b.java")). - NinjaEscapedString()) + FlagWithRspFileInputList("@", PathForOutput(ctx, "foo.rsp"), PathsForTesting("a.java", "b.java")). + String()) // Output: - // javac @$out.rsp + // javac @out/foo.rsp } func ExampleRuleBuilderCommand_String() { @@ -283,16 +282,6 @@ func ExampleRuleBuilderCommand_String() { // FOO=foo echo $FOO } -func ExampleRuleBuilderCommand_NinjaEscapedString() { - ctx := builderContext() - fmt.Println(NewRuleBuilder(pctx, ctx).Command(). - Text("FOO=foo"). - Text("echo $FOO"). - NinjaEscapedString()) - // Output: - // FOO=foo echo $$FOO -} - func TestRuleBuilder(t *testing.T) { fs := map[string][]byte{ "dep_fixer": nil, @@ -371,32 +360,16 @@ func TestRuleBuilder(t *testing.T) { wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer out/DepFile out/depfile out/ImplicitDepFile out/depfile2" - if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) { - t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g) - } + AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands()) - if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g) - } - if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g) - } - if g, w := rule.SymlinkOutputs(), wantSymlinkOutputs; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.SymlinkOutputs() = %#v\n got %#v", w, g) - } - if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g) - } - if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g) - } - if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g) - } + AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs()) + AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs()) + AssertDeepEquals(t, "rule.SymlinkOutputs()", wantSymlinkOutputs, rule.SymlinkOutputs()) + AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles()) + AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools()) + AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys()) - if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w { - t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g) - } + AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String()) }) t.Run("sbox", func(t *testing.T) { @@ -412,29 +385,15 @@ func TestRuleBuilder(t *testing.T) { wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2" - if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) { - t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g) - } + AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands()) - if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g) - } - if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g) - } - if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g) - } - if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g) - } - if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g) - } + AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs()) + AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs()) + AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles()) + AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools()) + AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys()) - if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w { - t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g) - } + AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String()) }) t.Run("sbox tools", func(t *testing.T) { @@ -450,29 +409,15 @@ func TestRuleBuilder(t *testing.T) { wantDepMergerCommand := "__SBOX_SANDBOX_DIR__/tools/out/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2" - if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) { - t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g) - } + AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands()) - if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g) - } - if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g) - } - if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g) - } - if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g) - } - if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) { - t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g) - } + AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs()) + AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs()) + AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles()) + AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools()) + AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys()) - if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w { - t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g) - } + AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String()) }) } @@ -534,8 +479,13 @@ func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir, ma rule.Build("rule", "desc") } +var prepareForRuleBuilderTest = FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory) + ctx.RegisterSingletonType("rule_builder_test", testRuleBuilderSingletonFactory) +}) + func TestRuleBuilder_Build(t *testing.T) { - fs := map[string][]byte{ + fs := MockFS{ "bar": nil, "cp": nil, } @@ -553,60 +503,46 @@ func TestRuleBuilder_Build(t *testing.T) { } ` - config := TestConfig(buildDir, nil, bp, fs) - ctx := NewTestContext(config) - ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory) - ctx.RegisterSingletonType("rule_builder_test", testRuleBuilderSingletonFactory) - ctx.Register() - - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) + result := emptyTestFixtureFactory.RunTest(t, + prepareForRuleBuilderTest, + FixtureWithRootAndroidBp(bp), + fs.AddToFixture(), + ) check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraImplicits, extraCmdDeps []string) { t.Helper() command := params.RuleParams.Command re := regexp.MustCompile(" # hash of input list: [a-z0-9]*$") command = re.ReplaceAllLiteralString(command, "") - if command != wantCommand { - t.Errorf("\nwant RuleParams.Command = %q\n got %q", wantCommand, params.RuleParams.Command) - } + + AssertStringEquals(t, "RuleParams.Command", wantCommand, command) wantDeps := append([]string{"cp"}, extraCmdDeps...) - if !reflect.DeepEqual(params.RuleParams.CommandDeps, wantDeps) { - t.Errorf("\nwant RuleParams.CommandDeps = %q\n got %q", wantDeps, params.RuleParams.CommandDeps) - } + AssertArrayString(t, "RuleParams.CommandDeps", wantDeps, params.RuleParams.CommandDeps) - if params.RuleParams.Restat != wantRestat { - t.Errorf("want RuleParams.Restat = %v, got %v", wantRestat, params.RuleParams.Restat) - } + AssertBoolEquals(t, "RuleParams.Restat", wantRestat, params.RuleParams.Restat) wantImplicits := append([]string{"bar"}, extraImplicits...) - if !reflect.DeepEqual(params.Implicits.Strings(), wantImplicits) { - t.Errorf("want Implicits = [%q], got %q", "bar", params.Implicits.Strings()) - } + AssertArrayString(t, "Implicits", wantImplicits, params.Implicits.Strings()) - if params.Output.String() != wantOutput { - t.Errorf("want Output = %q, got %q", wantOutput, params.Output) - } + AssertStringEquals(t, "Output", wantOutput, params.Output.String()) if len(params.ImplicitOutputs) != 0 { t.Errorf("want ImplicitOutputs = [], got %q", params.ImplicitOutputs.Strings()) } - if params.Depfile.String() != wantDepfile { - t.Errorf("want Depfile = %q, got %q", wantDepfile, params.Depfile) - } + AssertStringEquals(t, "Depfile", wantDepfile, params.Depfile.String()) if params.Deps != blueprint.DepsGCC { t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps) } } + buildDir := result.Config.BuildDir() + t.Run("module", func(t *testing.T) { outFile := filepath.Join(buildDir, ".intermediates", "foo", "gen", "foo") - check(t, ctx.ModuleForTests("foo", "").Rule("rule"), + check(t, result.ModuleForTests("foo", "").Rule("rule"), "cp bar "+outFile, outFile, outFile+".d", true, nil, nil) }) @@ -615,96 +551,22 @@ func TestRuleBuilder_Build(t *testing.T) { outFile := filepath.Join(outDir, "gen/foo_sbox") depFile := filepath.Join(outDir, "gen/foo_sbox.d") manifest := filepath.Join(outDir, "sbox.textproto") - sbox := filepath.Join(buildDir, "host", config.PrebuiltOS(), "bin/sbox") + sbox := filepath.Join(buildDir, "host", result.Config.PrebuiltOS(), "bin/sbox") sandboxPath := shared.TempDirForOutDir(buildDir) cmd := `rm -rf ` + outDir + `/gen && ` + sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest - check(t, ctx.ModuleForTests("foo_sbox", "").Output("gen/foo_sbox"), + check(t, result.ModuleForTests("foo_sbox", "").Output("gen/foo_sbox"), cmd, outFile, depFile, false, []string{manifest}, []string{sbox}) }) t.Run("singleton", func(t *testing.T) { outFile := filepath.Join(buildDir, "singleton/gen/baz") - check(t, ctx.SingletonForTests("rule_builder_test").Rule("rule"), + check(t, result.SingletonForTests("rule_builder_test").Rule("rule"), "cp bar "+outFile, outFile, outFile+".d", true, nil, nil) }) } -func Test_ninjaEscapeExceptForSpans(t *testing.T) { - type args struct { - s string - spans [][2]int - } - tests := []struct { - name string - args args - want string - }{ - { - name: "empty", - args: args{ - s: "", - }, - want: "", - }, - { - name: "unescape none", - args: args{ - s: "$abc", - }, - want: "$$abc", - }, - { - name: "unescape all", - args: args{ - s: "$abc", - spans: [][2]int{{0, 4}}, - }, - want: "$abc", - }, - { - name: "unescape first", - args: args{ - s: "$abc$", - spans: [][2]int{{0, 1}}, - }, - want: "$abc$$", - }, - { - name: "unescape last", - args: args{ - s: "$abc$", - spans: [][2]int{{4, 5}}, - }, - want: "$$abc$", - }, - { - name: "unescape middle", - args: args{ - s: "$a$b$c$", - spans: [][2]int{{2, 5}}, - }, - want: "$$a$b$c$$", - }, - { - name: "unescape multiple", - args: args{ - s: "$a$b$c$", - spans: [][2]int{{2, 3}, {4, 5}}, - }, - want: "$$a$b$c$$", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := ninjaEscapeExceptForSpans(tt.args.s, tt.args.spans); got != tt.want { - t.Errorf("ninjaEscapeExceptForSpans() = %v, want %v", got, tt.want) - } - }) - } -} - func TestRuleBuilderHashInputs(t *testing.T) { // The basic idea here is to verify that the command (in the case of a // non-sbox rule) or the sbox textproto manifest contain a hash of the @@ -750,29 +612,22 @@ func TestRuleBuilderHashInputs(t *testing.T) { }, } - config := TestConfig(buildDir, nil, bp, nil) - ctx := NewTestContext(config) - ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory) - ctx.Register() - - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) + result := emptyTestFixtureFactory.RunTest(t, + prepareForRuleBuilderTest, + FixtureWithRootAndroidBp(bp), + ) for _, test := range testcases { t.Run(test.name, func(t *testing.T) { t.Run("sbox", func(t *testing.T) { - gen := ctx.ModuleForTests(test.name+"_sbox", "") + gen := result.ModuleForTests(test.name+"_sbox", "") manifest := RuleBuilderSboxProtoForTests(t, gen.Output("sbox.textproto")) hash := manifest.Commands[0].GetInputHash() - if g, w := hash, test.expectedHash; g != w { - t.Errorf("Expected has %q, got %q", w, g) - } + AssertStringEquals(t, "hash", test.expectedHash, hash) }) t.Run("", func(t *testing.T) { - gen := ctx.ModuleForTests(test.name+"", "") + gen := result.ModuleForTests(test.name+"", "") command := gen.Output("gen/" + test.name).RuleParams.Command if g, w := command, " # hash of input list: "+test.expectedHash; !strings.HasSuffix(g, w) { t.Errorf("Expected command line to end with %q, got %q", w, g) diff --git a/android/singleton_module_test.go b/android/singleton_module_test.go index 9232eb42e..41dd4bb48 100644 --- a/android/singleton_module_test.go +++ b/android/singleton_module_test.go @@ -15,8 +15,6 @@ package android import ( - "reflect" - "strings" "testing" ) @@ -43,23 +41,14 @@ func testSingletonModuleFactory() SingletonModule { return tsm } -func runSingletonModuleTest(bp string) (*TestContext, []error) { - config := TestConfig(buildDir, nil, bp, nil) +var prepareForSingletonModuleTest = GroupFixturePreparers( // Enable Kati output to test SingletonModules with MakeVars. - config.katiEnabled = true - ctx := NewTestContext(config) - ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory) - ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc) - ctx.Register() - - _, errs := ctx.ParseBlueprintsFiles("Android.bp") - if len(errs) > 0 { - return ctx, errs - } - - _, errs = ctx.PrepareBuildActions(config) - return ctx, errs -} + PrepareForTestWithAndroidMk, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory) + ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc) + }), +) func TestSingletonModule(t *testing.T) { bp := ` @@ -67,16 +56,15 @@ func TestSingletonModule(t *testing.T) { name: "test_singleton_module", } ` - ctx, errs := runSingletonModuleTest(bp) - if len(errs) > 0 { - t.Fatal(errs) - } + result := emptyTestFixtureFactory. + RunTest(t, + prepareForSingletonModuleTest, + FixtureWithRootAndroidBp(bp), + ) - ops := ctx.ModuleForTests("test_singleton_module", "").Module().(*testSingletonModule).ops + ops := result.ModuleForTests("test_singleton_module", "").Module().(*testSingletonModule).ops wantOps := []string{"GenerateAndroidBuildActions", "GenerateSingletonBuildActions", "MakeVars"} - if !reflect.DeepEqual(ops, wantOps) { - t.Errorf("Expected operations %q, got %q", wantOps, ops) - } + AssertDeepEquals(t, "operations", wantOps, ops) } func TestDuplicateSingletonModule(t *testing.T) { @@ -89,23 +77,22 @@ func TestDuplicateSingletonModule(t *testing.T) { name: "test_singleton_module2", } ` - _, errs := runSingletonModuleTest(bp) - if len(errs) == 0 { - t.Fatal("expected duplicate SingletonModule error") - } - if len(errs) != 1 || !strings.Contains(errs[0].Error(), `Duplicate SingletonModule "test_singleton_module", previously used in`) { - t.Fatalf("expected duplicate SingletonModule error, got %q", errs) - } + + emptyTestFixtureFactory. + ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{ + `\QDuplicate SingletonModule "test_singleton_module", previously used in\E`, + })).RunTest(t, + prepareForSingletonModuleTest, + FixtureWithRootAndroidBp(bp), + ) } func TestUnusedSingletonModule(t *testing.T) { - bp := `` - ctx, errs := runSingletonModuleTest(bp) - if len(errs) > 0 { - t.Fatal(errs) - } + result := emptyTestFixtureFactory.RunTest(t, + prepareForSingletonModuleTest, + ) - singleton := ctx.SingletonForTests("test_singleton_module").Singleton() + singleton := result.SingletonForTests("test_singleton_module").Singleton() sm := singleton.(*singletonModuleSingletonAdaptor).sm ops := sm.(*testSingletonModule).ops if ops != nil { @@ -126,24 +113,17 @@ func TestVariantSingletonModule(t *testing.T) { } ` - config := TestConfig(buildDir, nil, bp, nil) - ctx := NewTestContext(config) - ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("test_singleton_module_mutator", testVariantSingletonModuleMutator) - }) - ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory) - ctx.Register() - - _, errs := ctx.ParseBlueprintsFiles("Android.bp") - - if len(errs) == 0 { - _, errs = ctx.PrepareBuildActions(config) - } - - if len(errs) == 0 { - t.Fatal("expected duplicate SingletonModule error") - } - if len(errs) != 1 || !strings.Contains(errs[0].Error(), `GenerateAndroidBuildActions already called for variant`) { - t.Fatalf("expected duplicate SingletonModule error, got %q", errs) - } + emptyTestFixtureFactory. + ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{ + `\QGenerateAndroidBuildActions already called for variant\E`, + })). + RunTest(t, + prepareForSingletonModuleTest, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { + ctx.BottomUp("test_singleton_module_mutator", testVariantSingletonModuleMutator) + }) + }), + FixtureWithRootAndroidBp(bp), + ) } diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go index 45463fd31..a72b160c0 100644 --- a/android/soong_config_modules_test.go +++ b/android/soong_config_modules_test.go @@ -15,7 +15,6 @@ package android import ( - "reflect" "testing" ) @@ -181,17 +180,23 @@ func TestSoongConfigModule(t *testing.T) { } ` - run := func(t *testing.T, bp string, fs map[string][]byte) { + fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer { + return FixtureModifyProductVariables(func(variables FixtureProductVariables) { + variables.VendorVars = vars + }) + } + + run := func(t *testing.T, bp string, fs MockFS) { testCases := []struct { name string - config Config + preparer FixturePreparer fooExpectedFlags []string fooDefaultsExpectedFlags []string }{ { name: "withValues", - config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{ - "acme": map[string]string{ + preparer: fixtureForVendorVars(map[string]map[string]string{ + "acme": { "board": "soc_a", "size": "42", "feature1": "true", @@ -221,8 +226,8 @@ func TestSoongConfigModule(t *testing.T) { }, { name: "empty_prop_for_string_var", - config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{ - "acme": map[string]string{"board": "soc_c"}}), + preparer: fixtureForVendorVars(map[string]map[string]string{ + "acme": {"board": "soc_c"}}), fooExpectedFlags: []string{ "DEFAULT", "-DGENERIC", @@ -237,8 +242,8 @@ func TestSoongConfigModule(t *testing.T) { }, { name: "unused_string_var", - config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{ - "acme": map[string]string{"board": "soc_d"}}), + preparer: fixtureForVendorVars(map[string]map[string]string{ + "acme": {"board": "soc_d"}}), fooExpectedFlags: []string{ "DEFAULT", "-DGENERIC", @@ -254,8 +259,8 @@ func TestSoongConfigModule(t *testing.T) { }, { - name: "conditions_default", - config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{}), + name: "conditions_default", + preparer: fixtureForVendorVars(map[string]map[string]string{}), fooExpectedFlags: []string{ "DEFAULT", "-DGENERIC", @@ -272,32 +277,29 @@ func TestSoongConfigModule(t *testing.T) { } for _, tc := range testCases { - ctx := NewTestContext(tc.config) - ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory) - ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory) - ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory) - ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory) - ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory) - ctx.RegisterModuleType("test", soongConfigTestModuleFactory) - ctx.PreArchMutators(RegisterDefaultsPreArchMutators) - ctx.Register() - - _, errs := ctx.ParseBlueprintsFiles("Android.bp") - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(tc.config) - FailIfErrored(t, errs) - - foo := ctx.ModuleForTests("foo", "").Module().(*soongConfigTestModule) - if g, w := foo.props.Cflags, tc.fooExpectedFlags; !reflect.DeepEqual(g, w) { - t.Errorf("%s: wanted foo cflags %q, got %q", tc.name, w, g) - } - - fooDefaults := ctx.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule) - if g, w := fooDefaults.props.Cflags, tc.fooDefaultsExpectedFlags; !reflect.DeepEqual(g, w) { - t.Errorf("%s: wanted foo_with_defaults cflags %q, got %q", tc.name, w, g) - } + t.Run(tc.name, func(t *testing.T) { + result := emptyTestFixtureFactory.RunTest(t, + tc.preparer, + PrepareForTestWithDefaults, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory) + ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory) + ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory) + ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory) + ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory) + ctx.RegisterModuleType("test", soongConfigTestModuleFactory) + }), + fs.AddToFixture(), + FixtureWithRootAndroidBp(bp), + ) + + foo := result.ModuleForTests("foo", "").Module().(*soongConfigTestModule) + AssertDeepEquals(t, "foo cflags", tc.fooExpectedFlags, foo.props.Cflags) + + fooDefaults := result.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule) + AssertDeepEquals(t, "foo_with_defaults cflags", tc.fooDefaultsExpectedFlags, fooDefaults.props.Cflags) + }) } - } t.Run("single file", func(t *testing.T) { diff --git a/android/test_asserts.go b/android/test_asserts.go index 5100abb79..4b5e9343e 100644 --- a/android/test_asserts.go +++ b/android/test_asserts.go @@ -22,6 +22,15 @@ import ( // This file contains general purpose test assert functions. +// AssertSame checks if the expected and actual values are equal and if they are not then +// it reports an error prefixed with the supplied message and including a reason for why it failed. +func AssertSame(t *testing.T, message string, expected interface{}, actual interface{}) { + t.Helper() + if actual != expected { + t.Errorf("%s: expected:\n%#v\nactual:\n%#v", message, expected, actual) + } +} + // AssertBoolEquals checks if the expected and actual values are equal and if they are not then it // reports an error prefixed with the supplied message and including a reason for why it failed. func AssertBoolEquals(t *testing.T, message string, expected bool, actual bool) { @@ -31,6 +40,15 @@ func AssertBoolEquals(t *testing.T, message string, expected bool, actual bool) } } +// AssertIntEquals checks if the expected and actual values are equal and if they are not then it +// reports an error prefixed with the supplied message and including a reason for why it failed. +func AssertIntEquals(t *testing.T, message string, expected int, actual int) { + t.Helper() + if actual != expected { + t.Errorf("%s: expected %d, actual %d", message, expected, actual) + } +} + // AssertStringEquals checks if the expected and actual values are equal and if they are not then // it reports an error prefixed with the supplied message and including a reason for why it failed. func AssertStringEquals(t *testing.T, message string, expected string, actual string) { diff --git a/android/test_suites.go b/android/test_suites.go index 7ecb8d27b..6b7b909fc 100644 --- a/android/test_suites.go +++ b/android/test_suites.go @@ -68,7 +68,7 @@ func robolectricTestSuite(ctx SingletonContext, files map[string]InstallPaths) W FlagWithOutput("-o ", outputFile). FlagWithArg("-P ", "host/testcases"). FlagWithArg("-C ", testCasesDir.String()). - FlagWithRspFileInputList("-r ", installedPaths.Paths()) + FlagWithRspFileInputList("-r ", outputFile.ReplaceExtension(ctx, "rsp"), installedPaths.Paths()) rule.Build("robolectric_tests_zip", "robolectric-tests.zip") return outputFile diff --git a/android/variable.go b/android/variable.go index a5e9ab4dd..08fa12ca4 100644 --- a/android/variable.go +++ b/android/variable.go @@ -426,6 +426,9 @@ func (v *productVariables) SetDefaultConfig() { Malloc_zero_contents: boolPtr(true), Malloc_pattern_fill_contents: boolPtr(false), Safestack: boolPtr(false), + + BootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}}, + UpdatableBootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}}, } if runtime.GOOS == "linux" { diff --git a/android/variable_test.go b/android/variable_test.go index 393fe0172..d16e45880 100644 --- a/android/variable_test.go +++ b/android/variable_test.go @@ -181,32 +181,30 @@ func TestProductVariables(t *testing.T) { name: "baz", } ` - config := TestConfig(buildDir, nil, bp, nil) - config.TestProductVariables.Eng = proptools.BoolPtr(true) - ctx := NewTestContext(config) - // A module type that has a srcs property but not a cflags property. - ctx.RegisterModuleType("module1", testProductVariableModuleFactoryFactory(&struct { - Srcs []string - }{})) - // A module type that has a cflags property but not a srcs property. - ctx.RegisterModuleType("module2", testProductVariableModuleFactoryFactory(&struct { - Cflags []string - }{})) - // A module type that does not have any properties that match product_variables. - ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(&struct { - Foo []string - }{})) - ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("variable", VariableMutator).Parallel() - }) - - ctx.Register() - - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) + emptyTestFixtureFactory.RunTest(t, + FixtureModifyProductVariables(func(variables FixtureProductVariables) { + variables.Eng = proptools.BoolPtr(true) + }), + FixtureRegisterWithContext(func(ctx RegistrationContext) { + // A module type that has a srcs property but not a cflags property. + ctx.RegisterModuleType("module1", testProductVariableModuleFactoryFactory(&struct { + Srcs []string + }{})) + // A module type that has a cflags property but not a srcs property. + ctx.RegisterModuleType("module2", testProductVariableModuleFactoryFactory(&struct { + Cflags []string + }{})) + // A module type that does not have any properties that match product_variables. + ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(&struct { + Foo []string + }{})) + ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { + ctx.BottomUp("variable", VariableMutator).Parallel() + }) + }), + FixtureWithRootAndroidBp(bp), + ) } var testProductVariableDefaultsProperties = struct { @@ -290,32 +288,23 @@ func TestProductVariablesDefaults(t *testing.T) { } ` - config := TestConfig(buildDir, nil, bp, nil) - config.TestProductVariables.Eng = boolPtr(true) - - ctx := NewTestContext(config) - - ctx.RegisterModuleType("test", productVariablesDefaultsTestModuleFactory) - ctx.RegisterModuleType("defaults", productVariablesDefaultsTestDefaultsFactory) - - ctx.PreArchMutators(RegisterDefaultsPreArchMutators) - ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("variable", VariableMutator).Parallel() - }) - - ctx.Register() - - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) - - foo := ctx.ModuleForTests("foo", "").Module().(*productVariablesDefaultsTestModule) + result := emptyTestFixtureFactory.RunTest(t, + FixtureModifyProductVariables(func(variables FixtureProductVariables) { + variables.Eng = boolPtr(true) + }), + PrepareForTestWithDefaults, + PrepareForTestWithVariables, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("test", productVariablesDefaultsTestModuleFactory) + ctx.RegisterModuleType("defaults", productVariablesDefaultsTestDefaultsFactory) + }), + FixtureWithRootAndroidBp(bp), + ) + + foo := result.ModuleForTests("foo", "").Module().(*productVariablesDefaultsTestModule) want := []string{"defaults", "module", "product_variable_defaults", "product_variable_module"} - if g, w := foo.properties.Foo, want; !reflect.DeepEqual(g, w) { - t.Errorf("expected foo %q, got %q", w, g) - } + AssertDeepEquals(t, "foo", want, foo.properties.Foo) } func BenchmarkSliceToTypeArray(b *testing.B) { diff --git a/apex/apex.go b/apex/apex.go index 3db20f465..429465d80 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -549,24 +549,35 @@ type dependencyTag struct { // Determines if the dependent will be part of the APEX payload. Can be false for the // dependencies to the signing key module, etc. payload bool + + // True if the dependent can only be a source module, false if a prebuilt module is a suitable + // replacement. This is needed because some prebuilt modules do not provide all the information + // needed by the apex. + sourceOnly bool +} + +func (d dependencyTag) ReplaceSourceWithPrebuilt() bool { + return !d.sourceOnly } +var _ android.ReplaceSourceWithPrebuilt = &dependencyTag{} + var ( - androidAppTag = dependencyTag{name: "androidApp", payload: true} - bpfTag = dependencyTag{name: "bpf", payload: true} - certificateTag = dependencyTag{name: "certificate"} - executableTag = dependencyTag{name: "executable", payload: true} - fsTag = dependencyTag{name: "filesystem", payload: true} - bootImageTag = dependencyTag{name: "bootImage", payload: true} - compatConfigsTag = dependencyTag{name: "compatConfig", payload: true} - javaLibTag = dependencyTag{name: "javaLib", payload: true} - jniLibTag = dependencyTag{name: "jniLib", payload: true} - keyTag = dependencyTag{name: "key"} - prebuiltTag = dependencyTag{name: "prebuilt", payload: true} - rroTag = dependencyTag{name: "rro", payload: true} - sharedLibTag = dependencyTag{name: "sharedLib", payload: true} - testForTag = dependencyTag{name: "test for"} - testTag = dependencyTag{name: "test", payload: true} + androidAppTag = dependencyTag{name: "androidApp", payload: true} + bpfTag = dependencyTag{name: "bpf", payload: true} + certificateTag = dependencyTag{name: "certificate"} + executableTag = dependencyTag{name: "executable", payload: true} + fsTag = dependencyTag{name: "filesystem", payload: true} + bootImageTag = dependencyTag{name: "bootImage", payload: true} + compatConfigTag = dependencyTag{name: "compatConfig", payload: true, sourceOnly: true} + javaLibTag = dependencyTag{name: "javaLib", payload: true} + jniLibTag = dependencyTag{name: "jniLib", payload: true} + keyTag = dependencyTag{name: "key"} + prebuiltTag = dependencyTag{name: "prebuilt", payload: true} + rroTag = dependencyTag{name: "rro", payload: true} + sharedLibTag = dependencyTag{name: "sharedLib", payload: true} + testForTag = dependencyTag{name: "test for"} + testTag = dependencyTag{name: "test", payload: true} ) // TODO(jiyong): shorten this function signature @@ -741,7 +752,7 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...) ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.properties.Bpfs...) ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...) - ctx.AddFarVariationDependencies(commonVariation, compatConfigsTag, a.properties.Compat_configs...) + ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...) if a.artApex { // With EMMA_INSTRUMENT_FRAMEWORK=true the ART boot image includes jacoco library. @@ -835,6 +846,19 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { if !ok || !am.CanHaveApexVariants() { return false } + depTag := mctx.OtherModuleDependencyTag(child) + + // Check to see if the tag always requires that the child module has an apex variant for every + // apex variant of the parent module. If it does not then it is still possible for something + // else, e.g. the DepIsInSameApex(...) method to decide that a variant is required. + if required, ok := depTag.(android.AlwaysRequireApexVariantTag); ok && required.AlwaysRequireApexVariant() { + return true + } + if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok { + // The tag defines a dependency that never requires the child module to be part of the same + // apex as the parent so it does not need an apex variant created. + return false + } if !parent.(android.DepIsInSameApex).DepIsInSameApex(mctx, child) { return false } @@ -1743,7 +1767,7 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { } else { ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName) } - case compatConfigsTag: + case compatConfigTag: if compatConfig, ok := child.(java.PlatformCompatConfigIntf); ok { filesInfo = append(filesInfo, apexFileForCompatConfig(ctx, compatConfig, depName)) } else { diff --git a/apex/apex_test.go b/apex/apex_test.go index 21cf5dfab..c01525b2a 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -6018,6 +6018,13 @@ func TestCompatConfig(t *testing.T) { system_modules: "none", apex_available: [ "myapex" ], } + + // Make sure that a preferred prebuilt does not affect the apex contents. + prebuilt_platform_compat_config { + name: "myjar-platform-compat-config", + metadata: "compat-config/metadata.xml", + prefer: true, + } `) ctx := result.TestContext ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ diff --git a/bootstrap_test.sh b/bootstrap_test.sh new file mode 100755 index 000000000..87f5e31fc --- /dev/null +++ b/bootstrap_test.sh @@ -0,0 +1,319 @@ +#!/bin/bash -eu + +# This test exercises the bootstrapping process of the build system +# in a source tree that only contains enough files for Bazel and Soong to work. + +HARDWIRED_MOCK_TOP= +# Uncomment this to be able to view the source tree after a test is run +# HARDWIRED_MOCK_TOP=/tmp/td + +REAL_TOP="$(readlink -f "$(dirname "$0")"/../..)" + +function fail { + echo ERROR: $1 + exit 1 +} + +function copy_directory() { + local dir="$1" + local parent="$(dirname "$dir")" + + mkdir -p "$MOCK_TOP/$parent" + cp -R "$REAL_TOP/$dir" "$MOCK_TOP/$parent" +} + +function symlink_file() { + local file="$1" + + mkdir -p "$MOCK_TOP/$(dirname "$file")" + ln -s "$REAL_TOP/$file" "$MOCK_TOP/$file" +} + +function symlink_directory() { + local dir="$1" + + mkdir -p "$MOCK_TOP/$dir" + # We need to symlink the contents of the directory individually instead of + # using one symlink for the whole directory because finder.go doesn't follow + # symlinks when looking for Android.bp files + for i in $(ls "$REAL_TOP/$dir"); do + local target="$MOCK_TOP/$dir/$i" + local source="$REAL_TOP/$dir/$i" + + if [[ -e "$target" ]]; then + if [[ ! -d "$source" || ! -d "$target" ]]; then + fail "Trying to symlink $dir twice" + fi + else + ln -s "$REAL_TOP/$dir/$i" "$MOCK_TOP/$dir/$i"; + fi + done +} + +function setup_bazel() { + copy_directory build/bazel + + symlink_directory prebuilts/bazel + symlink_directory prebuilts/jdk + + symlink_file WORKSPACE + symlink_file tools/bazel +} + +function setup() { + if [[ ! -z "$HARDWIRED_MOCK_TOP" ]]; then + MOCK_TOP="$HARDWIRED_MOCK_TOP" + rm -fr "$MOCK_TOP" + mkdir -p "$MOCK_TOP" + else + MOCK_TOP=$(mktemp -t -d st.XXXXX) + trap 'echo cd / && echo rm -fr "$MOCK_TOP"' EXIT + fi + + echo "Test case: ${FUNCNAME[1]}, mock top path: $MOCK_TOP" + cd "$MOCK_TOP" + + copy_directory build/blueprint + copy_directory build/soong + + symlink_directory prebuilts/go + symlink_directory prebuilts/build-tools + symlink_directory external/golang-protobuf + + touch "$MOCK_TOP/Android.bp" + + export ALLOW_MISSING_DEPENDENCIES=true + + mkdir -p out/soong +} + +function run_soong() { + build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests +} + +function test_smoke { + setup + run_soong +} + +function test_bazel_smoke { + setup + setup_bazel + + tools/bazel info + +} +function test_null_build() { + setup + run_soong + local bootstrap_mtime1=$(stat -c "%y" out/soong/.bootstrap/build.ninja) + local output_mtime1=$(stat -c "%y" out/soong/build.ninja) + run_soong + local bootstrap_mtime2=$(stat -c "%y" out/soong/.bootstrap/build.ninja) + local output_mtime2=$(stat -c "%y" out/soong/build.ninja) + + if [[ "$bootstrap_mtime1" == "$bootstrap_mtime2" ]]; then + # Bootstrapping is always done. It doesn't take a measurable amount of time. + fail "Bootstrap Ninja file did not change on null build" + fi + + if [[ "$output_mtime1" != "$output_mtime2" ]]; then + fail "Output Ninja file changed on null build" + fi +} + +function test_soong_build_rebuilt_if_blueprint_changes() { + setup + run_soong + local mtime1=$(stat -c "%y" out/soong/.bootstrap/build.ninja) + + sed -i 's/pluginGenSrcCmd/pluginGenSrcCmd2/g' build/blueprint/bootstrap/bootstrap.go + + run_soong + local mtime2=$(stat -c "%y" out/soong/.bootstrap/build.ninja) + + if [[ "$mtime1" == "$mtime2" ]]; then + fail "Bootstrap Ninja file did not change" + fi +} + +function test_change_android_bp() { + setup + mkdir -p a + cat > a/Android.bp <<'EOF' +python_binary_host { + name: "my_little_binary_host", + srcs: ["my_little_binary_host.py"] +} +EOF + touch a/my_little_binary_host.py + run_soong + + grep -q "^# Module:.*my_little_binary_host" out/soong/build.ninja || fail "module not found" + + cat > a/Android.bp <<'EOF' +python_binary_host { + name: "my_great_binary_host", + srcs: ["my_great_binary_host.py"] +} +EOF + touch a/my_great_binary_host.py + run_soong + + grep -q "^# Module:.*my_little_binary_host" out/soong/build.ninja && fail "old module found" + grep -q "^# Module:.*my_great_binary_host" out/soong/build.ninja || fail "new module not found" +} + + +function test_add_android_bp() { + setup + run_soong + local mtime1=$(stat -c "%y" out/soong/build.ninja) + + mkdir -p a + cat > a/Android.bp <<'EOF' +python_binary_host { + name: "my_little_binary_host", + srcs: ["my_little_binary_host.py"] +} +EOF + touch a/my_little_binary_host.py + run_soong + + local mtime2=$(stat -c "%y" out/soong/build.ninja) + if [[ "$mtime1" == "$mtime2" ]]; then + fail "Output Ninja file did not change" + fi + + grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja || fail "New module not in output" + + run_soong +} + +function test_delete_android_bp() { + setup + mkdir -p a + cat > a/Android.bp <<'EOF' +python_binary_host { + name: "my_little_binary_host", + srcs: ["my_little_binary_host.py"] +} +EOF + touch a/my_little_binary_host.py + run_soong + + grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja || fail "Module not in output" + + rm a/Android.bp + run_soong + + grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja && fail "Old module in output" +} + +function test_add_file_to_glob() { + setup + + mkdir -p a + cat > a/Android.bp <<'EOF' +python_binary_host { + name: "my_little_binary_host", + srcs: ["*.py"], +} +EOF + touch a/my_little_binary_host.py + run_soong + local mtime1=$(stat -c "%y" out/soong/build.ninja) + + touch a/my_little_library.py + run_soong + + local mtime2=$(stat -c "%y" out/soong/build.ninja) + if [[ "$mtime1" == "$mtime2" ]]; then + fail "Output Ninja file did not change" + fi + + grep -q my_little_library.py out/soong/build.ninja || fail "new file is not in output" +} + +function test_add_file_to_soong_build() { + setup + run_soong + local mtime1=$(stat -c "%y" out/soong/build.ninja) + + mkdir -p a + cat > a/Android.bp <<'EOF' +bootstrap_go_package { + name: "picard-soong-rules", + pkgPath: "android/soong/picard", + deps: [ + "blueprint", + "soong", + "soong-android", + ], + srcs: [ + "picard.go", + ], + pluginFor: ["soong_build"], +} +EOF + + cat > a/picard.go <<'EOF' +package picard + +import ( + "android/soong/android" + "github.com/google/blueprint" +) + +var ( + pctx = android.NewPackageContext("picard") +) + +func init() { + android.RegisterSingletonType("picard", PicardSingleton) +} + +func PicardSingleton() android.Singleton { + return &picardSingleton{} +} + +type picardSingleton struct{} + +func (p *picardSingleton) GenerateBuildActions(ctx android.SingletonContext) { + picardRule := ctx.Rule(pctx, "picard", + blueprint.RuleParams{ + Command: "echo Make it so. > ${out}", + CommandDeps: []string{}, + Description: "Something quotable", + }) + + outputFile := android.PathForOutput(ctx, "picard", "picard.txt") + var deps android.Paths + + ctx.Build(pctx, android.BuildParams{ + Rule: picardRule, + Output: outputFile, + Inputs: deps, + }) +} + +EOF + + run_soong + local mtime2=$(stat -c "%y" out/soong/build.ninja) + if [[ "$mtime1" == "$mtime2" ]]; then + fail "Output Ninja file did not change" + fi + + grep -q "Make it so" out/soong/build.ninja || fail "New action not present" +} + +test_bazel_smoke +test_smoke +test_null_build +test_soong_build_rebuilt_if_blueprint_changes +test_add_file_to_glob +test_add_android_bp +test_change_android_bp +test_delete_android_bp +test_add_file_to_soong_build diff --git a/bpf/bpf_test.go b/bpf/bpf_test.go index eb0d8c8f7..0bf15db75 100644 --- a/bpf/bpf_test.go +++ b/bpf/bpf_test.go @@ -15,7 +15,6 @@ package bpf import ( - "io/ioutil" "os" "testing" @@ -23,34 +22,12 @@ import ( "android/soong/cc" ) -var buildDir string - -func setUp() { - var err error - buildDir, err = ioutil.TempDir("", "genrule_test") - if err != nil { - panic(err) - } -} - -func tearDown() { - os.RemoveAll(buildDir) -} - func TestMain(m *testing.M) { - run := func() int { - setUp() - defer tearDown() - - return m.Run() - } - - os.Exit(run()) - + os.Exit(m.Run()) } var bpfFactory = android.NewFixtureFactory( - &buildDir, + nil, cc.PrepareForTestWithCcDefaultModules, android.FixtureMergeMockFs( map[string][]byte{ diff --git a/cc/fuzz.go b/cc/fuzz.go index 9fe5b17f5..5219ebc04 100644 --- a/cc/fuzz.go +++ b/cc/fuzz.go @@ -440,7 +440,8 @@ func (s *fuzzPackager) GenerateBuildActions(ctx android.SingletonContext) { command := builder.Command().BuiltTool("soong_zip"). Flag("-j"). FlagWithOutput("-o ", corpusZip) - command.FlagWithRspFileInputList("-r ", fuzzModule.corpus) + rspFile := corpusZip.ReplaceExtension(ctx, "rsp") + command.FlagWithRspFileInputList("-r ", rspFile, fuzzModule.corpus) files = append(files, fileToZip{corpusZip, ""}) } diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go index fdd1fec20..4014fe0c4 100644 --- a/cc/vendor_snapshot.go +++ b/cc/vendor_snapshot.go @@ -528,10 +528,11 @@ func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { ctx, snapshotDir, c.name+"-"+ctx.Config().DeviceName()+"_list") + rspFile := snapshotOutputList.ReplaceExtension(ctx, "rsp") zipRule.Command(). Text("tr"). FlagWithArg("-d ", "\\'"). - FlagWithRspFileInputList("< ", snapshotOutputs). + FlagWithRspFileInputList("< ", rspFile, snapshotOutputs). FlagWithOutput("> ", snapshotOutputList) zipRule.Temporary(snapshotOutputList) diff --git a/cc/vndk.go b/cc/vndk.go index 85028d0aa..b7047e92e 100644 --- a/cc/vndk.go +++ b/cc/vndk.go @@ -827,10 +827,11 @@ func (c *vndkSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContex // filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr snapshotOutputList := android.PathForOutput(ctx, snapshotDir, "android-vndk-"+ctx.DeviceConfig().DeviceArch()+"_list") + rspFile := snapshotOutputList.ReplaceExtension(ctx, "rsp") zipRule.Command(). Text("tr"). FlagWithArg("-d ", "\\'"). - FlagWithRspFileInputList("< ", snapshotOutputs). + FlagWithRspFileInputList("< ", rspFile, snapshotOutputs). FlagWithOutput("> ", snapshotOutputList) zipRule.Temporary(snapshotOutputList) diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go index f8919a400..f47c6012e 100644 --- a/cmd/sbox/sbox.go +++ b/cmd/sbox/sbox.go @@ -229,13 +229,18 @@ func runCommand(command *sbox_proto.Command, tempDir string) (depFile string, er return "", err } + pathToTempDirInSbox := tempDir + if command.GetChdir() { + pathToTempDirInSbox = "." + } + if strings.Contains(rawCommand, depFilePlaceholder) { - depFile = filepath.Join(tempDir, "deps.d") + depFile = filepath.Join(pathToTempDirInSbox, "deps.d") rawCommand = strings.Replace(rawCommand, depFilePlaceholder, depFile, -1) } if strings.Contains(rawCommand, sandboxDirPlaceholder) { - rawCommand = strings.Replace(rawCommand, sandboxDirPlaceholder, tempDir, -1) + rawCommand = strings.Replace(rawCommand, sandboxDirPlaceholder, pathToTempDirInSbox, -1) } // Emulate ninja's behavior of creating the directories for any output files before @@ -254,6 +259,15 @@ func runCommand(command *sbox_proto.Command, tempDir string) (depFile string, er if command.GetChdir() { cmd.Dir = tempDir + path := os.Getenv("PATH") + absPath, err := makeAbsPathEnv(path) + if err != nil { + return "", err + } + err = os.Setenv("PATH", absPath) + if err != nil { + return "", fmt.Errorf("Failed to update PATH: %w", err) + } } err = cmd.Run() @@ -466,3 +480,17 @@ func joinPath(dir, file string) string { } return filepath.Join(dir, file) } + +func makeAbsPathEnv(pathEnv string) (string, error) { + pathEnvElements := filepath.SplitList(pathEnv) + for i, p := range pathEnvElements { + if !filepath.IsAbs(p) { + absPath, err := filepath.Abs(p) + if err != nil { + return "", fmt.Errorf("failed to make PATH entry %q absolute: %w", p, err) + } + pathEnvElements[i] = absPath + } + } + return strings.Join(pathEnvElements, string(filepath.ListSeparator)), nil +} diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go index 8322fbe72..11d362067 100644 --- a/cmd/soong_build/main.go +++ b/cmd/soong_build/main.go @@ -80,7 +80,7 @@ func newContext(configuration android.Config) *android.Context { } func newConfig(srcDir string) android.Config { - configuration, err := android.NewConfig(srcDir, bootstrap.BuildDir, bootstrap.ModuleListFile) + configuration, err := android.NewConfig(srcDir, bootstrap.CmdlineBuildDir(), bootstrap.CmdlineModuleListFile()) if err != nil { fmt.Fprintf(os.Stderr, "%s", err) os.Exit(1) @@ -101,6 +101,10 @@ func main() { configuration := newConfig(srcDir) extraNinjaDeps := []string{configuration.ProductVariablesFileName} + if configuration.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" { + configuration.SetAllowMissingDependencies() + } + // These two are here so that we restart a non-debugged soong_build when the // user sets SOONG_DELVE the first time. configuration.Getenv("SOONG_DELVE") @@ -127,7 +131,7 @@ func main() { // the incorrect results from the first pass, and file I/O is expensive. firstCtx := newContext(configuration) configuration.SetStopBefore(bootstrap.StopBeforeWriteNinja) - bootstrap.Main(firstCtx.Context, configuration, extraNinjaDeps...) + bootstrap.Main(firstCtx.Context, configuration, false, extraNinjaDeps...) // Invoke bazel commands and save results for second pass. if err := configuration.BazelContext.InvokeBazel(); err != nil { fmt.Fprintf(os.Stderr, "%s", err) @@ -140,10 +144,10 @@ func main() { os.Exit(1) } ctx = newContext(secondPassConfig) - bootstrap.Main(ctx.Context, secondPassConfig, extraNinjaDeps...) + bootstrap.Main(ctx.Context, secondPassConfig, false, extraNinjaDeps...) } else { ctx = newContext(configuration) - bootstrap.Main(ctx.Context, configuration, extraNinjaDeps...) + bootstrap.Main(ctx.Context, configuration, false, extraNinjaDeps...) } // Convert the Soong module graph into Bazel BUILD files. @@ -158,7 +162,7 @@ func main() { } if docFile != "" { - if err := writeDocs(ctx, docFile); err != nil { + if err := writeDocs(ctx, configuration, docFile); err != nil { fmt.Fprintf(os.Stderr, "%s", err) os.Exit(1) } @@ -167,7 +171,7 @@ func main() { // TODO(ccross): make this a command line argument. Requires plumbing through blueprint // to affect the command line of the primary builder. if shouldPrepareBuildActions(configuration) { - metricsFile := filepath.Join(bootstrap.BuildDir, "soong_build_metrics.pb") + metricsFile := filepath.Join(bootstrap.CmdlineBuildDir(), "soong_build_metrics.pb") err := android.WriteMetrics(configuration, metricsFile) if err != nil { fmt.Fprintf(os.Stderr, "error writing soong_build metrics %s: %s", metricsFile, err) @@ -193,7 +197,7 @@ func runBp2Build(srcDir string, configuration android.Config) { // Android.bp files. It must not depend on the values of per-build product // configurations or variables, since those will generate different BUILD // files based on how the user has configured their tree. - bp2buildCtx.SetModuleListFile(bootstrap.ModuleListFile) + bp2buildCtx.SetModuleListFile(bootstrap.CmdlineModuleListFile()) extraNinjaDeps, err := bp2buildCtx.ListModulePaths(srcDir) if err != nil { panic(err) @@ -202,7 +206,7 @@ func runBp2Build(srcDir string, configuration android.Config) { // Run the loading and analysis pipeline to prepare the graph of regular // Modules parsed from Android.bp files, and the BazelTargetModules mapped // from the regular Modules. - bootstrap.Main(bp2buildCtx.Context, configuration, extraNinjaDeps...) + bootstrap.Main(bp2buildCtx.Context, configuration, false, extraNinjaDeps...) // Run the code-generation phase to convert BazelTargetModules to BUILD files // and print conversion metrics to the user. diff --git a/cmd/soong_build/writedocs.go b/cmd/soong_build/writedocs.go index f2c2c9b9c..a69de6adf 100644 --- a/cmd/soong_build/writedocs.go +++ b/cmd/soong_build/writedocs.go @@ -95,13 +95,13 @@ func moduleTypeDocsToTemplates(moduleTypeList []*bpdoc.ModuleType) []moduleTypeT return result } -func getPackages(ctx *android.Context) ([]*bpdoc.Package, error) { +func getPackages(ctx *android.Context, config interface{}) ([]*bpdoc.Package, error) { moduleTypeFactories := android.ModuleTypeFactoriesForDocs() - return bootstrap.ModuleTypeDocs(ctx.Context, moduleTypeFactories) + return bootstrap.ModuleTypeDocs(ctx.Context, config, moduleTypeFactories) } -func writeDocs(ctx *android.Context, filename string) error { - packages, err := getPackages(ctx) +func writeDocs(ctx *android.Context, config interface{}, filename string) error { + packages, err := getPackages(ctx, config) if err != nil { return err } diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go index 1c5e78ad6..390a9ecdf 100644 --- a/cmd/soong_ui/main.go +++ b/cmd/soong_ui/main.go @@ -70,7 +70,7 @@ var commands []command = []command{ return build.NewConfig(ctx, args...) }, stdio: stdio, - run: make, + run: runMake, }, { flag: "--dumpvar-mode", description: "print the value of the legacy make variable VAR to stdout", @@ -92,7 +92,7 @@ var commands []command = []command{ description: "build modules based on the specified build action", config: buildActionConfig, stdio: stdio, - run: make, + run: runMake, }, } @@ -478,7 +478,7 @@ func buildActionConfig(ctx build.Context, args ...string) build.Config { return build.NewBuildActionConfig(buildAction, *dir, ctx, args...) } -func make(ctx build.Context, config build.Config, _ []string, logsDir string) { +func runMake(ctx build.Context, config build.Config, _ []string, logsDir string) { if config.IsVerbose() { writer := ctx.Writer fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.") diff --git a/filesystem/Android.bp b/filesystem/Android.bp index dcdbdcf43..791019ddb 100644 --- a/filesystem/Android.bp +++ b/filesystem/Android.bp @@ -14,6 +14,7 @@ bootstrap_go_package { "bootimg.go", "filesystem.go", "logical_partition.go", + "vbmeta.go", ], testSrcs: [ ], diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go index 372a610e0..3dcc4165c 100644 --- a/filesystem/bootimg.go +++ b/filesystem/bootimg.go @@ -17,6 +17,7 @@ package filesystem import ( "fmt" "strconv" + "strings" "github.com/google/blueprint" "github.com/google/blueprint/proptools" @@ -217,22 +218,46 @@ func (b *bootimg) buildBootImage(ctx android.ModuleContext, vendor bool) android } func (b *bootimg) signImage(ctx android.ModuleContext, unsignedImage android.OutputPath) android.OutputPath { - output := android.PathForModuleOut(ctx, b.installFileName()).OutputPath - key := android.PathForModuleSrc(ctx, proptools.String(b.properties.Avb_private_key)) + propFile, toolDeps := b.buildPropFile(ctx) + output := android.PathForModuleOut(ctx, b.installFileName()).OutputPath builder := android.NewRuleBuilder(pctx, ctx) builder.Command().Text("cp").Input(unsignedImage).Output(output) - builder.Command(). - BuiltTool("avbtool"). - Flag("add_hash_footer"). - FlagWithArg("--partition_name ", b.partitionName()). - FlagWithInput("--key ", key). - FlagWithOutput("--image ", output) + builder.Command().BuiltTool("verity_utils"). + Input(propFile). + Implicits(toolDeps). + Output(output) builder.Build("sign_bootimg", fmt.Sprintf("Signing %s", b.BaseModuleName())) return output } +func (b *bootimg) buildPropFile(ctx android.ModuleContext) (propFile android.OutputPath, toolDeps android.Paths) { + var sb strings.Builder + var deps android.Paths + addStr := func(name string, value string) { + fmt.Fprintf(&sb, "%s=%s\n", name, value) + } + addPath := func(name string, path android.Path) { + addStr(name, path.String()) + deps = append(deps, path) + } + + addStr("avb_hash_enable", "true") + addPath("avb_avbtool", ctx.Config().HostToolPath(ctx, "avbtool")) + algorithm := proptools.StringDefault(b.properties.Avb_algorithm, "SHA256_RSA4096") + addStr("avb_algorithm", algorithm) + key := android.PathForModuleSrc(ctx, proptools.String(b.properties.Avb_private_key)) + addPath("avb_key_path", key) + addStr("avb_add_hash_footer_args", "") // TODO(jiyong): add --rollback_index + partitionName := proptools.StringDefault(b.properties.Partition_name, b.Name()) + addStr("partition_name", partitionName) + + propFile = android.PathForModuleOut(ctx, "prop").OutputPath + android.WriteFileRule(ctx, propFile, sb.String()) + return propFile, deps +} + var _ android.AndroidMkEntriesProvider = (*bootimg)(nil) // Implements android.AndroidMkEntriesProvider @@ -255,6 +280,13 @@ func (b *bootimg) OutputPath() android.Path { return b.output } +func (b *bootimg) SignedOutputPath() android.Path { + if proptools.Bool(b.properties.Use_avb) { + return b.OutputPath() + } + return nil +} + var _ android.OutputFileProducer = (*bootimg)(nil) // Implements android.OutputFileProducer diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index 3b0a7ae5a..8974eba4f 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -55,6 +55,9 @@ type filesystemProperties struct { // Hash and signing algorithm for avbtool. Default is SHA256_RSA4096. Avb_algorithm *string + // Name of the partition stored in vbmeta desc. Defaults to the name of this module. + Partition_name *string + // Type of the filesystem. Currently, ext4, cpio, and compressed_cpio are supported. Default // is ext4. Type *string @@ -279,7 +282,8 @@ func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android. key := android.PathForModuleSrc(ctx, proptools.String(f.properties.Avb_private_key)) addPath("avb_key_path", key) addStr("avb_add_hashtree_footer_args", "--do_not_generate_fec") - addStr("partition_name", f.Name()) + partitionName := proptools.StringDefault(f.properties.Partition_name, f.Name()) + addStr("partition_name", partitionName) } if proptools.String(f.properties.File_contexts) != "" { @@ -381,6 +385,10 @@ func (f *filesystem) OutputFiles(tag string) (android.Paths, error) { type Filesystem interface { android.Module OutputPath() android.Path + + // Returns the output file that is signed by avbtool. If this module is not signed, returns + // nil. + SignedOutputPath() android.Path } var _ Filesystem = (*filesystem)(nil) @@ -388,3 +396,10 @@ var _ Filesystem = (*filesystem)(nil) func (f *filesystem) OutputPath() android.Path { return f.output } + +func (f *filesystem) SignedOutputPath() android.Path { + if proptools.Bool(f.properties.Use_avb) { + return f.OutputPath() + } + return nil +} diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go index 16b6037cf..20d9622dc 100644 --- a/filesystem/logical_partition.go +++ b/filesystem/logical_partition.go @@ -209,6 +209,10 @@ func (l *logicalPartition) OutputPath() android.Path { return l.output } +func (l *logicalPartition) SignedOutputPath() android.Path { + return nil // logical partition is not signed by itself +} + var _ android.OutputFileProducer = (*logicalPartition)(nil) // Implements android.OutputFileProducer diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go new file mode 100644 index 000000000..f823387b0 --- /dev/null +++ b/filesystem/vbmeta.go @@ -0,0 +1,265 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// 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 filesystem + +import ( + "fmt" + "strconv" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" + + "android/soong/android" +) + +func init() { + android.RegisterModuleType("vbmeta", vbmetaFactory) +} + +type vbmeta struct { + android.ModuleBase + + properties vbmetaProperties + + output android.OutputPath + installDir android.InstallPath +} + +type vbmetaProperties struct { + // Name of the partition stored in vbmeta desc. Defaults to the name of this module. + Partition_name *string + + // Set the name of the output. Defaults to <module_name>.img. + Stem *string + + // Path to the private key that avbtool will use to sign this vbmeta image. + Private_key *string `android:"path"` + + // Algorithm that avbtool will use to sign this vbmeta image. Default is SHA256_RSA4096. + Algorithm *string + + // File whose content will provide the rollback index. If unspecified, the rollback index + // is from PLATFORM_SECURITY_PATCH + Rollback_index_file *string `android:"path"` + + // Rollback index location of this vbmeta image. Must be 0, 1, 2, etc. Default is 0. + Rollback_index_location *int64 + + // List of filesystem modules that this vbmeta has descriptors for. The filesystem modules + // have to be signed (use_avb: true). + Partitions []string + + // List of chained partitions that this vbmeta deletages the verification. + Chained_partitions []chainedPartitionProperties +} + +type chainedPartitionProperties struct { + // Name of the chained partition + Name *string + + // Rollback index location of the chained partition. Must be 0, 1, 2, etc. Default is the + // index of this partition in the list + 1. + Rollback_index_location *int64 + + // Path to the public key that the chained partition is signed with. If this is specified, + // private_key is ignored. + Public_key *string `android:"path"` + + // Path to the private key that the chained partition is signed with. If this is specified, + // and public_key is not specified, a public key is extracted from this private key and + // the extracted public key is embedded in the vbmeta image. + Private_key *string `android:"path"` +} + +// vbmeta is the partition image that has the verification information for other partitions. +func vbmetaFactory() android.Module { + module := &vbmeta{} + module.AddProperties(&module.properties) + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) + return module +} + +type vbmetaDep struct { + blueprint.BaseDependencyTag + kind string +} + +var vbmetaPartitionDep = vbmetaDep{kind: "partition"} + +func (v *vbmeta) DepsMutator(ctx android.BottomUpMutatorContext) { + ctx.AddDependency(ctx.Module(), vbmetaPartitionDep, v.properties.Partitions...) +} + +func (v *vbmeta) installFileName() string { + return proptools.StringDefault(v.properties.Stem, v.BaseModuleName()+".img") +} + +func (v *vbmeta) partitionName() string { + return proptools.StringDefault(v.properties.Partition_name, v.BaseModuleName()) +} + +func (v *vbmeta) GenerateAndroidBuildActions(ctx android.ModuleContext) { + extractedPublicKeys := v.extractPublicKeys(ctx) + + v.output = android.PathForModuleOut(ctx, v.installFileName()).OutputPath + + builder := android.NewRuleBuilder(pctx, ctx) + cmd := builder.Command().BuiltTool("avbtool").Text("make_vbmeta_image") + + key := android.PathForModuleSrc(ctx, proptools.String(v.properties.Private_key)) + cmd.FlagWithInput("--key ", key) + + algorithm := proptools.StringDefault(v.properties.Algorithm, "SHA256_RSA4096") + cmd.FlagWithArg("--algorithm ", algorithm) + + cmd.FlagWithArg("--rollback_index ", v.rollbackIndexCommand(ctx)) + ril := proptools.IntDefault(v.properties.Rollback_index_location, 0) + if ril < 0 { + ctx.PropertyErrorf("rollback_index_location", "must be 0, 1, 2, ...") + return + } + cmd.FlagWithArg("--rollback_index_location ", strconv.Itoa(ril)) + + for _, p := range ctx.GetDirectDepsWithTag(vbmetaPartitionDep) { + f, ok := p.(Filesystem) + if !ok { + ctx.PropertyErrorf("partitions", "%q(type: %s) is not supported", + p.Name(), ctx.OtherModuleType(p)) + continue + } + signedImage := f.SignedOutputPath() + if signedImage == nil { + ctx.PropertyErrorf("partitions", "%q(type: %s) is not signed. Use `use_avb: true`", + p.Name(), ctx.OtherModuleType(p)) + continue + } + cmd.FlagWithInput("--include_descriptors_from_image ", signedImage) + } + + for i, cp := range v.properties.Chained_partitions { + name := proptools.String(cp.Name) + if name == "" { + ctx.PropertyErrorf("chained_partitions", "name must be specified") + continue + } + + ril := proptools.IntDefault(cp.Rollback_index_location, i+1) + if ril < 0 { + ctx.PropertyErrorf("chained_partitions", "must be 0, 1, 2, ...") + continue + } + + var publicKey android.Path + if cp.Public_key != nil { + publicKey = android.PathForModuleSrc(ctx, proptools.String(cp.Public_key)) + } else { + publicKey = extractedPublicKeys[name] + } + cmd.FlagWithArg("--chain_partition ", fmt.Sprintf("%s:%d:%s", name, ril, publicKey.String())) + cmd.Implicit(publicKey) + } + + cmd.FlagWithOutput("--output ", v.output) + builder.Build("vbmeta", fmt.Sprintf("vbmeta %s", ctx.ModuleName())) + + v.installDir = android.PathForModuleInstall(ctx, "etc") + ctx.InstallFile(v.installDir, v.installFileName(), v.output) +} + +// Returns the embedded shell command that prints the rollback index +func (v *vbmeta) rollbackIndexCommand(ctx android.ModuleContext) string { + var cmd string + if v.properties.Rollback_index_file != nil { + f := android.PathForModuleSrc(ctx, proptools.String(v.properties.Rollback_index_file)) + cmd = "cat " + f.String() + } else { + cmd = "date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s" + } + // Take the first line and remove the newline char + return "$(" + cmd + " | head -1 | tr -d '\n'" + ")" +} + +// Extract public keys from chained_partitions.private_key. The keys are indexed with the partition +// name. +func (v *vbmeta) extractPublicKeys(ctx android.ModuleContext) map[string]android.OutputPath { + result := make(map[string]android.OutputPath) + + builder := android.NewRuleBuilder(pctx, ctx) + for _, cp := range v.properties.Chained_partitions { + if cp.Private_key == nil { + continue + } + + name := proptools.String(cp.Name) + if name == "" { + ctx.PropertyErrorf("chained_partitions", "name must be specified") + continue + } + + if _, ok := result[name]; ok { + ctx.PropertyErrorf("chained_partitions", "name %q is duplicated", name) + continue + } + + privateKeyFile := android.PathForModuleSrc(ctx, proptools.String(cp.Private_key)) + publicKeyFile := android.PathForModuleOut(ctx, name+".avbpubkey").OutputPath + + builder.Command(). + BuiltTool("avbtool"). + Text("extract_public_key"). + FlagWithInput("--key ", privateKeyFile). + FlagWithOutput("--output ", publicKeyFile) + + result[name] = publicKeyFile + } + builder.Build("vbmeta_extract_public_key", fmt.Sprintf("Extract public keys for %s", ctx.ModuleName())) + return result +} + +var _ android.AndroidMkEntriesProvider = (*vbmeta)(nil) + +// Implements android.AndroidMkEntriesProvider +func (v *vbmeta) AndroidMkEntries() []android.AndroidMkEntries { + return []android.AndroidMkEntries{android.AndroidMkEntries{ + Class: "ETC", + OutputFile: android.OptionalPathForPath(v.output), + ExtraEntries: []android.AndroidMkExtraEntriesFunc{ + func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.SetString("LOCAL_MODULE_PATH", v.installDir.ToMakePath().String()) + entries.SetString("LOCAL_INSTALLED_MODULE_STEM", v.installFileName()) + }, + }, + }} +} + +var _ Filesystem = (*vbmeta)(nil) + +func (v *vbmeta) OutputPath() android.Path { + return v.output +} + +func (v *vbmeta) SignedOutputPath() android.Path { + return v.OutputPath() // vbmeta is always signed +} + +var _ android.OutputFileProducer = (*vbmeta)(nil) + +// Implements android.OutputFileProducer +func (v *vbmeta) OutputFiles(tag string) (android.Paths, error) { + if tag == "" { + return []android.Path{v.output}, nil + } + return nil, fmt.Errorf("unsupported module reference tag %q", tag) +} diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go index d131e94f9..199a7df7b 100644 --- a/genrule/genrule_test.go +++ b/genrule/genrule_test.go @@ -298,6 +298,14 @@ func TestGenruleCmd(t *testing.T) { `, expect: "echo foo > __SBOX_SANDBOX_DIR__/out/foo && cp __SBOX_SANDBOX_DIR__/out/foo __SBOX_SANDBOX_DIR__/out/out", }, + { + name: "$", + prop: ` + out: ["out"], + cmd: "echo $$ > $(out)", + `, + expect: "echo $ > __SBOX_SANDBOX_DIR__/out/out", + }, { name: "error empty location", diff --git a/java/Android.bp b/java/Android.bp index 9e2db8314..56cc40129 100644 --- a/java/Android.bp +++ b/java/Android.bp @@ -75,6 +75,7 @@ bootstrap_go_package { "java_test.go", "jdeps_test.go", "kotlin_test.go", + "platform_compat_config_test.go", "plugin_test.go", "rro_test.go", "sdk_test.go", diff --git a/java/app_test.go b/java/app_test.go index c189ee5d3..7168a9645 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -370,11 +370,15 @@ func TestUpdatableApps(t *testing.T) { for _, test := range testCases { t.Run(test.name, func(t *testing.T) { - if test.expectedError == "" { - testJava(t, test.bp) - } else { - testJavaError(t, test.expectedError, test.bp) + errorHandler := android.FixtureExpectsNoErrors + if test.expectedError != "" { + errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError) } + javaFixtureFactory. + Extend(FixtureWithPrebuiltApis(map[string][]string{ + "29": {"foo"}, + })). + ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, test.bp) }) } } @@ -984,12 +988,8 @@ func TestAndroidResources(t *testing.T) { } } -func checkSdkVersion(t *testing.T, config android.Config, expectedSdkVersion string) { - ctx := testContext(config) - - run(t, ctx, config) - - foo := ctx.ModuleForTests("foo", "android_common") +func checkSdkVersion(t *testing.T, result *android.TestResult, expectedSdkVersion string) { + foo := result.ModuleForTests("foo", "android_common") link := foo.Output("package-res.apk") linkFlags := strings.Split(link.Args["flags"], " ") min := android.IndexList("--min-sdk-version", linkFlags) @@ -1002,15 +1002,9 @@ func checkSdkVersion(t *testing.T, config android.Config, expectedSdkVersion str gotMinSdkVersion := linkFlags[min+1] gotTargetSdkVersion := linkFlags[target+1] - if gotMinSdkVersion != expectedSdkVersion { - t.Errorf("incorrect --min-sdk-version, expected %q got %q", - expectedSdkVersion, gotMinSdkVersion) - } + android.AssertStringEquals(t, "incorrect --min-sdk-version", expectedSdkVersion, gotMinSdkVersion) - if gotTargetSdkVersion != expectedSdkVersion { - t.Errorf("incorrect --target-sdk-version, expected %q got %q", - expectedSdkVersion, gotTargetSdkVersion) - } + android.AssertStringEquals(t, "incorrect --target-sdk-version", expectedSdkVersion, gotTargetSdkVersion) } func TestAppSdkVersion(t *testing.T) { @@ -1083,13 +1077,19 @@ func TestAppSdkVersion(t *testing.T) { %s }`, moduleType, test.sdkVersion, platformApiProp) - config := testAppConfig(nil, bp, nil) - config.TestProductVariables.Platform_sdk_version = &test.platformSdkInt - config.TestProductVariables.Platform_sdk_codename = &test.platformSdkCodename - config.TestProductVariables.Platform_version_active_codenames = test.activeCodenames - config.TestProductVariables.Platform_sdk_final = &test.platformSdkFinal - checkSdkVersion(t, config, test.expectedMinSdkVersion) - + result := javaFixtureFactory.Extend( + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_sdk_version = &test.platformSdkInt + variables.Platform_sdk_codename = &test.platformSdkCodename + variables.Platform_version_active_codenames = test.activeCodenames + variables.Platform_sdk_final = &test.platformSdkFinal + }), + FixtureWithPrebuiltApis(map[string][]string{ + "14": {"foo"}, + }), + ).RunTestWithBp(t, bp) + + checkSdkVersion(t, result, test.expectedMinSdkVersion) }) } } @@ -1145,13 +1145,22 @@ func TestVendorAppSdkVersion(t *testing.T) { vendor: true, }`, moduleType, sdkKind, test.sdkVersion) - config := testAppConfig(nil, bp, nil) - config.TestProductVariables.Platform_sdk_version = &test.platformSdkInt - config.TestProductVariables.Platform_sdk_codename = &test.platformSdkCodename - config.TestProductVariables.Platform_sdk_final = &test.platformSdkFinal - config.TestProductVariables.DeviceCurrentApiLevelForVendorModules = &test.deviceCurrentApiLevelForVendorModules - config.TestProductVariables.DeviceSystemSdkVersions = []string{"28", "29"} - checkSdkVersion(t, config, test.expectedMinSdkVersion) + result := javaFixtureFactory.Extend( + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_sdk_version = &test.platformSdkInt + variables.Platform_sdk_codename = &test.platformSdkCodename + variables.Platform_sdk_final = &test.platformSdkFinal + variables.DeviceCurrentApiLevelForVendorModules = &test.deviceCurrentApiLevelForVendorModules + variables.DeviceSystemSdkVersions = []string{"28", "29"} + }), + FixtureWithPrebuiltApis(map[string][]string{ + "28": {"foo"}, + "29": {"foo"}, + "current": {"foo"}, + }), + ).RunTestWithBp(t, bp) + + checkSdkVersion(t, result, test.expectedMinSdkVersion) }) } } @@ -2360,15 +2369,16 @@ func TestUsesLibraries(t *testing.T) { } ` - config := testAppConfig(nil, bp, nil) - config.TestProductVariables.MissingUsesLibraries = []string{"baz"} + result := javaFixtureFactory.Extend( + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("runtime-library", "foo", "quuz", "qux", "bar", "fred"), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.MissingUsesLibraries = []string{"baz"} + }), + ).RunTestWithBp(t, bp) - ctx := testContext(config) - - run(t, ctx, config) - - app := ctx.ModuleForTests("app", "android_common") - prebuilt := ctx.ModuleForTests("prebuilt", "android_common") + app := result.ModuleForTests("app", "android_common") + prebuilt := result.ModuleForTests("prebuilt", "android_common") // Test that implicit dependencies on java_sdk_library instances are passed to the manifest. // This should not include explicit `uses_libs`/`optional_uses_libs` entries. @@ -2380,10 +2390,7 @@ func TestUsesLibraries(t *testing.T) { `--uses-library com.non.sdk.lib ` + // TODO(b/132357300): "com.non.sdk.lib" should not be passed to manifest_fixer `--uses-library bar ` + // TODO(b/132357300): "bar" should not be passed to manifest_fixer `--uses-library runtime-library` - if actualManifestFixerArgs != expectManifestFixerArgs { - t.Errorf("unexpected manifest_fixer args:\n\texpect: %q\n\tactual: %q", - expectManifestFixerArgs, actualManifestFixerArgs) - } + android.AssertStringEquals(t, "manifest_fixer args", expectManifestFixerArgs, actualManifestFixerArgs) // Test that all libraries are verified (library order matters). verifyCmd := app.Rule("verify_uses_libraries").RuleParams.Command @@ -2394,9 +2401,7 @@ func TestUsesLibraries(t *testing.T) { `--uses-library runtime-library ` + `--optional-uses-library bar ` + `--optional-uses-library baz ` - if !strings.Contains(verifyCmd, verifyArgs) { - t.Errorf("wanted %q in %q", verifyArgs, verifyCmd) - } + android.AssertStringDoesContain(t, "verify cmd args", verifyCmd, verifyArgs) // Test that all libraries are verified for an APK (library order matters). verifyApkCmd := prebuilt.Rule("verify_uses_libraries").RuleParams.Command @@ -2405,9 +2410,7 @@ func TestUsesLibraries(t *testing.T) { `--uses-library android.test.runner ` + `--optional-uses-library bar ` + `--optional-uses-library baz ` - if !strings.Contains(verifyApkCmd, verifyApkArgs) { - t.Errorf("wanted %q in %q", verifyApkArgs, verifyApkCmd) - } + android.AssertStringDoesContain(t, "verify apk cmd args", verifyApkCmd, verifyApkArgs) // Test that all present libraries are preopted, including implicit SDK dependencies, possibly stubs cmd := app.Rule("dexpreopt").RuleParams.Command @@ -2418,46 +2421,39 @@ func TestUsesLibraries(t *testing.T) { `PCL[/system/framework/non-sdk-lib.jar]#` + `PCL[/system/framework/bar.jar]#` + `PCL[/system/framework/runtime-library.jar]` - if !strings.Contains(cmd, w) { - t.Errorf("wanted %q in %q", w, cmd) - } + android.AssertStringDoesContain(t, "dexpreopt app cmd args", cmd, w) // Test conditional context for target SDK version 28. - if w := `--target-context-for-sdk 28` + - ` PCL[/system/framework/org.apache.http.legacy.jar] `; !strings.Contains(cmd, w) { - t.Errorf("wanted %q in %q", w, cmd) - } + android.AssertStringDoesContain(t, "dexpreopt app cmd 28", cmd, + `--target-context-for-sdk 28`+ + ` PCL[/system/framework/org.apache.http.legacy.jar] `) // Test conditional context for target SDK version 29. - if w := `--target-context-for-sdk 29` + - ` PCL[/system/framework/android.hidl.manager-V1.0-java.jar]` + - `#PCL[/system/framework/android.hidl.base-V1.0-java.jar] `; !strings.Contains(cmd, w) { - t.Errorf("wanted %q in %q", w, cmd) - } + android.AssertStringDoesContain(t, "dexpreopt app cmd 29", cmd, + `--target-context-for-sdk 29`+ + ` PCL[/system/framework/android.hidl.manager-V1.0-java.jar]`+ + `#PCL[/system/framework/android.hidl.base-V1.0-java.jar] `) // Test conditional context for target SDK version 30. // "android.test.mock" is absent because "android.test.runner" is not used. - if w := `--target-context-for-sdk 30` + - ` PCL[/system/framework/android.test.base.jar] `; !strings.Contains(cmd, w) { - t.Errorf("wanted %q in %q", w, cmd) - } + android.AssertStringDoesContain(t, "dexpreopt app cmd 30", cmd, + `--target-context-for-sdk 30`+ + ` PCL[/system/framework/android.test.base.jar] `) cmd = prebuilt.Rule("dexpreopt").RuleParams.Command - if w := `--target-context-for-sdk any` + - ` PCL[/system/framework/foo.jar]` + - `#PCL[/system/framework/non-sdk-lib.jar]` + - `#PCL[/system/framework/android.test.runner.jar]` + - `#PCL[/system/framework/bar.jar] `; !strings.Contains(cmd, w) { - t.Errorf("wanted %q in %q", w, cmd) - } + android.AssertStringDoesContain(t, "dexpreopt prebuilt cmd", cmd, + `--target-context-for-sdk any`+ + ` PCL[/system/framework/foo.jar]`+ + `#PCL[/system/framework/non-sdk-lib.jar]`+ + `#PCL[/system/framework/android.test.runner.jar]`+ + `#PCL[/system/framework/bar.jar] `) // Test conditional context for target SDK version 30. // "android.test.mock" is present because "android.test.runner" is used. - if w := `--target-context-for-sdk 30` + - ` PCL[/system/framework/android.test.base.jar]` + - `#PCL[/system/framework/android.test.mock.jar] `; !strings.Contains(cmd, w) { - t.Errorf("wanted %q in %q", w, cmd) - } + android.AssertStringDoesContain(t, "dexpreopt prebuilt cmd 30", cmd, + `--target-context-for-sdk 30`+ + ` PCL[/system/framework/android.test.base.jar]`+ + `#PCL[/system/framework/android.test.mock.jar] `) } func TestCodelessApp(t *testing.T) { @@ -2722,28 +2718,24 @@ func TestUncompressDex(t *testing.T) { test := func(t *testing.T, bp string, want bool, unbundled bool) { t.Helper() - config := testAppConfig(nil, bp, nil) - if unbundled { - config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true) - config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true) - } - - ctx := testContext(config) - - run(t, ctx, config) + result := javaFixtureFactory.Extend( + PrepareForTestWithPrebuiltsOfCurrentApi, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + if unbundled { + variables.Unbundled_build = proptools.BoolPtr(true) + variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true) + } + }), + ).RunTestWithBp(t, bp) - foo := ctx.ModuleForTests("foo", "android_common") + foo := result.ModuleForTests("foo", "android_common") dex := foo.Rule("r8") uncompressedInDexJar := strings.Contains(dex.Args["zipFlags"], "-L 0") aligned := foo.MaybeRule("zipalign").Rule != nil - if uncompressedInDexJar != want { - t.Errorf("want uncompressed in dex %v, got %v", want, uncompressedInDexJar) - } + android.AssertBoolEquals(t, "uncompressed in dex", want, uncompressedInDexJar) - if aligned != want { - t.Errorf("want aligned %v, got %v", want, aligned) - } + android.AssertBoolEquals(t, "aligne", want, aligned) } for _, tt := range testCases { diff --git a/java/boot_image.go b/java/boot_image.go index 12e287459..25a4f17f4 100644 --- a/java/boot_image.go +++ b/java/boot_image.go @@ -20,6 +20,7 @@ import ( "android/soong/android" "android/soong/dexpreopt" + "github.com/google/blueprint" ) @@ -56,9 +57,9 @@ type BootImageModule struct { func bootImageFactory() android.Module { m := &BootImageModule{} m.AddProperties(&m.properties) - android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon) android.InitApexModule(m) android.InitSdkAwareModule(m) + android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon) return m } @@ -210,11 +211,11 @@ func (module *prebuiltBootImageModule) Name() string { func prebuiltBootImageFactory() android.Module { m := &prebuiltBootImageModule{} m.AddProperties(&m.properties) - android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon) // This doesn't actually have any prebuilt files of its own so pass a placeholder for the srcs // array. android.InitPrebuiltModule(m, &[]string{"placeholder"}) android.InitApexModule(m) android.InitSdkAwareModule(m) + android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon) return m } diff --git a/java/droiddoc.go b/java/droiddoc.go index f0decec74..da13c621b 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -812,7 +812,7 @@ func javadocCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs andro BuiltTool("soong_javac_wrapper").Tool(config.JavadocCmd(ctx)). Flag(config.JavacVmFlags). FlagWithArg("-encoding ", "UTF-8"). - FlagWithRspFileInputList("@", srcs). + FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "javadoc.rsp"), srcs). FlagWithInput("@", srcJarList) // TODO(ccross): Remove this if- statement once we finish migration for all Doclava @@ -1243,7 +1243,7 @@ func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersi Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED"). FlagWithArg("-encoding ", "UTF-8"). FlagWithArg("-source ", javaVersion.String()). - FlagWithRspFileInputList("@", srcs). + FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "metalava.rsp"), srcs). FlagWithInput("@", srcJarList) if javaHome := ctx.Config().Getenv("ANDROID_JAVA_HOME"); javaHome != "" { @@ -1446,7 +1446,9 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { // add a large number of inputs to a file without exceeding bash command length limits (which // would happen if we use the WriteFile rule). The cp is needed because RuleBuilder sets the // rsp file to be ${output}.rsp. - impCmd.Text("cp").FlagWithRspFileInputList("", cmd.GetImplicits()).Output(implicitsRsp) + impCmd.Text("cp"). + FlagWithRspFileInputList("", android.PathForModuleOut(ctx, "metalava-implicits.rsp"), cmd.GetImplicits()). + Output(implicitsRsp) impRule.Build("implicitsGen", "implicits generation") cmd.Implicit(implicitsRsp) @@ -1750,7 +1752,7 @@ func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleCon Flag("-jar"). FlagWithOutput("-o ", p.stubsSrcJar). FlagWithArg("-C ", srcDir.String()). - FlagWithRspFileInputList("-r ", srcPaths) + FlagWithRspFileInputList("-r ", p.stubsSrcJar.ReplaceExtension(ctx, "rsp"), srcPaths) rule.Restat() diff --git a/java/java_test.go b/java/java_test.go index 2eb724185..b68945fd1 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -48,19 +48,19 @@ func tearDown() { os.RemoveAll(buildDir) } +var emptyFixtureFactory = android.NewFixtureFactory(&buildDir) + // Factory to use to create fixtures for tests in this package. -var javaFixtureFactory = android.NewFixtureFactory( - &buildDir, +var javaFixtureFactory = emptyFixtureFactory.Extend( genrule.PrepareForTestWithGenRuleBuildComponents, // Get the CC build components but not default modules. cc.PrepareForTestWithCcBuildComponents, // Include all the default java modules. PrepareForTestWithJavaDefaultModules, + python.PrepareForTestWithPythonBuildComponents, android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { ctx.RegisterModuleType("java_plugin", PluginFactory) - ctx.RegisterModuleType("python_binary_host", python.PythonBinaryHostFactory) - ctx.PreDepsMutators(python.RegisterPythonPreDepsMutators) ctx.RegisterPreSingletonType("overlay", OverlaySingletonFactory) ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory) }), @@ -100,11 +100,9 @@ func testContext(config android.Config) *android.TestContext { RegisterRequiredBuildComponentsForTest(ctx) ctx.RegisterModuleType("java_plugin", PluginFactory) ctx.RegisterModuleType("filegroup", android.FileGroupFactory) - ctx.RegisterModuleType("python_binary_host", python.PythonBinaryHostFactory) ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) ctx.PreArchMutators(android.RegisterComponentsMutator) - ctx.PreDepsMutators(python.RegisterPythonPreDepsMutators) ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators) ctx.RegisterPreSingletonType("overlay", OverlaySingletonFactory) ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory) diff --git a/java/kotlin.go b/java/kotlin.go index 8067ad521..2960f819d 100644 --- a/java/kotlin.go +++ b/java/kotlin.go @@ -64,7 +64,9 @@ func kotlinCommonSrcsList(ctx android.ModuleContext, commonSrcFiles android.Path // Insert a second rule to write out the list of resources to a file. commonSrcsList := android.PathForModuleOut(ctx, "kotlinc_common_srcs.list") rule := android.NewRuleBuilder(pctx, ctx) - rule.Command().Text("cp").FlagWithRspFileInputList("", commonSrcFiles).Output(commonSrcsList) + rule.Command().Text("cp"). + FlagWithRspFileInputList("", commonSrcsList.ReplaceExtension(ctx, "rsp"), commonSrcFiles). + Output(commonSrcsList) rule.Build("kotlin_common_srcs_list", "kotlin common_srcs list") return android.OptionalPathForPath(commonSrcsList) } diff --git a/java/lint.go b/java/lint.go index 9f677db3a..fccd1a552 100644 --- a/java/lint.go +++ b/java/lint.go @@ -220,7 +220,9 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.Ru // Insert a second rule to write out the list of resources to a file. resourcesList = android.PathForModuleOut(ctx, "lint", "resources.list") resListRule := android.NewRuleBuilder(pctx, ctx) - resListRule.Command().Text("cp").FlagWithRspFileInputList("", l.resources).Output(resourcesList) + resListRule.Command().Text("cp"). + FlagWithRspFileInputList("", resourcesList.ReplaceExtension(ctx, "rsp"), l.resources). + Output(resourcesList) resListRule.Build("lint_resources_list", "lint resources list") trackRSPDependency(l.resources, resourcesList) } @@ -241,7 +243,10 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.Ru // TODO(ccross): some of the files in l.srcs are generated sources and should be passed to // lint separately. srcsList := android.PathForModuleOut(ctx, "lint", "srcs.list") - rule.Command().Text("cp").FlagWithRspFileInputList("", l.srcs).Output(srcsList) + srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp") + rule.Command().Text("cp"). + FlagWithRspFileInputList("", srcsListRsp, l.srcs). + Output(srcsList) trackRSPDependency(l.srcs, srcsList) cmd := rule.Command(). @@ -635,7 +640,7 @@ func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android rule.Command().BuiltTool("soong_zip"). FlagWithOutput("-o ", outputPath). FlagWithArg("-C ", android.PathForIntermediates(ctx).String()). - FlagWithRspFileInputList("-r ", paths) + FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths) rule.Build(outputPath.Base(), outputPath.Base()) } diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go index 4bfd4e2f9..3c43a8e55 100644 --- a/java/platform_compat_config.go +++ b/java/platform_compat_config.go @@ -26,6 +26,7 @@ func init() { func registerPlatformCompatConfigBuildComponents(ctx android.RegistrationContext) { ctx.RegisterSingletonType("platform_compat_config_singleton", platformCompatConfigSingletonFactory) ctx.RegisterModuleType("platform_compat_config", PlatformCompatConfigFactory) + ctx.RegisterModuleType("prebuilt_platform_compat_config", prebuiltCompatConfigFactory) ctx.RegisterModuleType("global_compat_config", globalCompatConfigFactory) } @@ -35,10 +36,6 @@ func platformCompatConfigPath(ctx android.PathContext) android.OutputPath { return android.PathForOutput(ctx, "compat_config", "merged_compat_config.xml") } -type platformCompatConfigSingleton struct { - metadata android.Path -} - type platformCompatConfigProperties struct { Src *string `android:"path"` } @@ -52,7 +49,7 @@ type platformCompatConfig struct { metadataFile android.OutputPath } -func (p *platformCompatConfig) compatConfigMetadata() android.OutputPath { +func (p *platformCompatConfig) compatConfigMetadata() android.Path { return p.metadataFile } @@ -64,52 +61,20 @@ func (p *platformCompatConfig) SubDir() string { return "compatconfig" } +type platformCompatConfigMetadataProvider interface { + compatConfigMetadata() android.Path +} + type PlatformCompatConfigIntf interface { android.Module - compatConfigMetadata() android.OutputPath CompatConfig() android.OutputPath // Sub dir under etc dir. SubDir() string } var _ PlatformCompatConfigIntf = (*platformCompatConfig)(nil) - -// compat singleton rules -func (p *platformCompatConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) { - - var compatConfigMetadata android.Paths - - ctx.VisitAllModules(func(module android.Module) { - if c, ok := module.(PlatformCompatConfigIntf); ok { - metadata := c.compatConfigMetadata() - compatConfigMetadata = append(compatConfigMetadata, metadata) - } - }) - - if compatConfigMetadata == nil { - // nothing to do. - return - } - - rule := android.NewRuleBuilder(pctx, ctx) - outputPath := platformCompatConfigPath(ctx) - - rule.Command(). - BuiltTool("process-compat-config"). - FlagForEachInput("--xml ", compatConfigMetadata). - FlagWithOutput("--merged-config ", outputPath) - - rule.Build("merged-compat-config", "Merge compat config") - - p.metadata = outputPath -} - -func (p *platformCompatConfigSingleton) MakeVars(ctx android.MakeVarsContext) { - if p.metadata != nil { - ctx.Strict("INTERNAL_PLATFORM_MERGED_COMPAT_CONFIG", p.metadata.String()) - } -} +var _ platformCompatConfigMetadataProvider = (*platformCompatConfig)(nil) func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) { rule := android.NewRuleBuilder(pctx, ctx) @@ -145,10 +110,6 @@ func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries { }} } -func platformCompatConfigSingletonFactory() android.Singleton { - return &platformCompatConfigSingleton{} -} - func PlatformCompatConfigFactory() android.Module { module := &platformCompatConfig{} module.AddProperties(&module.properties) @@ -156,6 +117,93 @@ func PlatformCompatConfigFactory() android.Module { return module } +// A prebuilt version of the platform compat config module. +type prebuiltCompatConfigModule struct { + android.ModuleBase + android.SdkBase + prebuilt android.Prebuilt + + properties prebuiltCompatConfigProperties + + metadataFile android.Path +} + +type prebuiltCompatConfigProperties struct { + Metadata *string `android:"path"` +} + +func (module *prebuiltCompatConfigModule) Prebuilt() *android.Prebuilt { + return &module.prebuilt +} + +func (module *prebuiltCompatConfigModule) Name() string { + return module.prebuilt.Name(module.ModuleBase.Name()) +} + +func (module *prebuiltCompatConfigModule) compatConfigMetadata() android.Path { + return module.metadataFile +} + +var _ platformCompatConfigMetadataProvider = (*prebuiltCompatConfigModule)(nil) + +func (module *prebuiltCompatConfigModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + module.metadataFile = module.prebuilt.SingleSourcePath(ctx) +} + +// A prebuilt version of platform_compat_config that provides the metadata. +func prebuiltCompatConfigFactory() android.Module { + m := &prebuiltCompatConfigModule{} + m.AddProperties(&m.properties) + android.InitSingleSourcePrebuiltModule(m, &m.properties, "Metadata") + android.InitSdkAwareModule(m) + android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) + return m +} + +// compat singleton rules +type platformCompatConfigSingleton struct { + metadata android.Path +} + +func (p *platformCompatConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) { + + var compatConfigMetadata android.Paths + + ctx.VisitAllModules(func(module android.Module) { + if c, ok := module.(platformCompatConfigMetadataProvider); ok { + metadata := c.compatConfigMetadata() + compatConfigMetadata = append(compatConfigMetadata, metadata) + } + }) + + if compatConfigMetadata == nil { + // nothing to do. + return + } + + rule := android.NewRuleBuilder(pctx, ctx) + outputPath := platformCompatConfigPath(ctx) + + rule.Command(). + BuiltTool("process-compat-config"). + FlagForEachInput("--xml ", compatConfigMetadata). + FlagWithOutput("--merged-config ", outputPath) + + rule.Build("merged-compat-config", "Merge compat config") + + p.metadata = outputPath +} + +func (p *platformCompatConfigSingleton) MakeVars(ctx android.MakeVarsContext) { + if p.metadata != nil { + ctx.Strict("INTERNAL_PLATFORM_MERGED_COMPAT_CONFIG", p.metadata.String()) + } +} + +func platformCompatConfigSingletonFactory() android.Singleton { + return &platformCompatConfigSingleton{} +} + //============== merged_compat_config ================= type globalCompatConfigProperties struct { // name of the file into which the metadata will be copied. diff --git a/java/platform_compat_config_test.go b/java/platform_compat_config_test.go new file mode 100644 index 000000000..0c5d001ac --- /dev/null +++ b/java/platform_compat_config_test.go @@ -0,0 +1,53 @@ +// Copyright 2021 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 java + +import ( + "testing" + + "android/soong/android" +) + +func TestPlatformCompatConfig(t *testing.T) { + result := emptyFixtureFactory.RunTest(t, + PrepareForTestWithPlatformCompatConfig, + android.FixtureWithRootAndroidBp(` + platform_compat_config { + name: "myconfig2", + } + platform_compat_config { + name: "myconfig1", + } + platform_compat_config { + name: "myconfig3", + } + `), + ) + + checkMergedCompatConfigInputs(t, result, "myconfig", + "out/soong/.intermediates/myconfig1/myconfig1_meta.xml", + "out/soong/.intermediates/myconfig2/myconfig2_meta.xml", + "out/soong/.intermediates/myconfig3/myconfig3_meta.xml", + ) +} + +// Check that the merged file create by platform_compat_config_singleton has the correct inputs. +func checkMergedCompatConfigInputs(t *testing.T, result *android.TestResult, message string, expectedPaths ...string) { + sourceGlobalCompatConfig := result.SingletonForTests("platform_compat_config_singleton") + allOutputs := sourceGlobalCompatConfig.AllOutputs() + android.AssertIntEquals(t, message+": output len", 1, len(allOutputs)) + output := sourceGlobalCompatConfig.Output(allOutputs[0]) + android.AssertPathsRelativeToTopEquals(t, message+": inputs", expectedPaths, output.Implicits) +} diff --git a/python/Android.bp b/python/Android.bp index b633f1e3f..e49fa6a3c 100644 --- a/python/Android.bp +++ b/python/Android.bp @@ -20,6 +20,7 @@ bootstrap_go_package { "proto.go", "python.go", "test.go", + "testing.go", ], testSrcs: [ "python_test.go", diff --git a/python/binary.go b/python/binary.go index 372b8a8c1..6061ad4f7 100644 --- a/python/binary.go +++ b/python/binary.go @@ -26,10 +26,14 @@ import ( ) func init() { - android.RegisterModuleType("python_binary_host", PythonBinaryHostFactory) + registerPythonBinaryComponents(android.InitRegistrationContext) android.RegisterBp2BuildMutator("python_binary_host", PythonBinaryBp2Build) } +func registerPythonBinaryComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory) +} + type bazelPythonBinaryAttributes struct { Main string Srcs bazel.LabelList diff --git a/python/library.go b/python/library.go index b724d2b9f..9663b3c75 100644 --- a/python/library.go +++ b/python/library.go @@ -21,8 +21,12 @@ import ( ) func init() { - android.RegisterModuleType("python_library_host", PythonLibraryHostFactory) - android.RegisterModuleType("python_library", PythonLibraryFactory) + registerPythonLibraryComponents(android.InitRegistrationContext) +} + +func registerPythonLibraryComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("python_library_host", PythonLibraryHostFactory) + ctx.RegisterModuleType("python_library", PythonLibraryFactory) } func PythonLibraryHostFactory() android.Module { diff --git a/python/python.go b/python/python.go index a078c0b58..4444a70e6 100644 --- a/python/python.go +++ b/python/python.go @@ -29,7 +29,11 @@ import ( ) func init() { - android.PreDepsMutators(RegisterPythonPreDepsMutators) + registerPythonMutators(android.InitRegistrationContext) +} + +func registerPythonMutators(ctx android.RegistrationContext) { + ctx.PreDepsMutators(RegisterPythonPreDepsMutators) } // Exported to support other packages using Python modules in tests. diff --git a/python/python_test.go b/python/python_test.go index 5c4efa763..6263c8a25 100644 --- a/python/python_test.go +++ b/python/python_test.go @@ -15,21 +15,15 @@ package python import ( - "errors" "fmt" - "io/ioutil" "os" "path/filepath" - "reflect" - "sort" - "strings" + "regexp" "testing" "android/soong/android" ) -var buildDir string - type pyModule struct { name string actualVersion string @@ -56,7 +50,7 @@ var ( data = []struct { desc string - mockFiles map[string][]byte + mockFiles android.MockFS errors []string expectedBinaries []pyModule @@ -64,7 +58,6 @@ var ( { desc: "module without any src files", mockFiles: map[string][]byte{ - bpFile: []byte(`subdirs = ["dir"]`), filepath.Join("dir", bpFile): []byte( `python_library_host { name: "lib1", @@ -79,7 +72,6 @@ var ( { desc: "module with bad src file ext", mockFiles: map[string][]byte{ - bpFile: []byte(`subdirs = ["dir"]`), filepath.Join("dir", bpFile): []byte( `python_library_host { name: "lib1", @@ -98,7 +90,6 @@ var ( { desc: "module with bad data file ext", mockFiles: map[string][]byte{ - bpFile: []byte(`subdirs = ["dir"]`), filepath.Join("dir", bpFile): []byte( `python_library_host { name: "lib1", @@ -121,7 +112,6 @@ var ( { desc: "module with bad pkg_path format", mockFiles: map[string][]byte{ - bpFile: []byte(`subdirs = ["dir"]`), filepath.Join("dir", bpFile): []byte( `python_library_host { name: "lib1", @@ -159,7 +149,6 @@ var ( { desc: "module with bad runfile src path format", mockFiles: map[string][]byte{ - bpFile: []byte(`subdirs = ["dir"]`), filepath.Join("dir", bpFile): []byte( `python_library_host { name: "lib1", @@ -187,7 +176,6 @@ var ( { desc: "module with duplicate runfile path", mockFiles: map[string][]byte{ - bpFile: []byte(`subdirs = ["dir"]`), filepath.Join("dir", bpFile): []byte( `python_library_host { name: "lib1", @@ -207,21 +195,32 @@ var ( "lib1", ], } + + python_binary_host { + name: "bin", + pkg_path: "e/", + srcs: [ + "bin.py", + ], + libs: [ + "lib2", + ], + } `, ), "dir/c/file1.py": nil, "dir/file1.py": nil, + "dir/bin.py": nil, }, errors: []string{ - fmt.Sprintf(dupRunfileErrTemplate, "dir/Android.bp:9:6", - "lib2", "PY3", "a/b/c/file1.py", "lib2", "dir/file1.py", + fmt.Sprintf(dupRunfileErrTemplate, "dir/Android.bp:20:6", + "bin", "PY3", "a/b/c/file1.py", "bin", "dir/file1.py", "lib1", "dir/c/file1.py"), }, }, { desc: "module for testing dependencies", mockFiles: map[string][]byte{ - bpFile: []byte(`subdirs = ["dir"]`), filepath.Join("dir", bpFile): []byte( `python_defaults { name: "default_lib", @@ -314,10 +313,10 @@ var ( "e/default_py3.py", "e/file4.py", }, - srcsZip: "@prefix@/.intermediates/dir/bin/PY3/bin.py.srcszip", + srcsZip: "out/soong/.intermediates/dir/bin/PY3/bin.py.srcszip", depsSrcsZips: []string{ - "@prefix@/.intermediates/dir/lib5/PY3/lib5.py.srcszip", - "@prefix@/.intermediates/dir/lib6/PY3/lib6.py.srcszip", + "out/soong/.intermediates/dir/lib5/PY3/lib5.py.srcszip", + "out/soong/.intermediates/dir/lib6/PY3/lib6.py.srcszip", }, }, }, @@ -327,60 +326,37 @@ var ( func TestPythonModule(t *testing.T) { for _, d := range data { + if d.desc != "module with duplicate runfile path" { + continue + } + errorPatterns := make([]string, len(d.errors)) + for i, s := range d.errors { + errorPatterns[i] = regexp.QuoteMeta(s) + } + t.Run(d.desc, func(t *testing.T) { - config := android.TestConfig(buildDir, nil, "", d.mockFiles) - ctx := android.NewTestContext(config) - ctx.PreDepsMutators(RegisterPythonPreDepsMutators) - ctx.RegisterModuleType("python_library_host", PythonLibraryHostFactory) - ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory) - ctx.RegisterModuleType("python_defaults", defaultsFactory) - ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) - ctx.Register() - _, testErrs := ctx.ParseBlueprintsFiles(bpFile) - android.FailIfErrored(t, testErrs) - _, actErrs := ctx.PrepareBuildActions(config) - if len(actErrs) > 0 { - testErrs = append(testErrs, expectErrors(t, actErrs, d.errors)...) - } else { - for _, e := range d.expectedBinaries { - testErrs = append(testErrs, - expectModule(t, ctx, buildDir, e.name, - e.actualVersion, - e.srcsZip, - e.pyRunfiles, - e.depsSrcsZips)...) - } + result := emptyFixtureFactory. + ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(errorPatterns)). + RunTest(t, + android.PrepareForTestWithDefaults, + PrepareForTestWithPythonBuildComponents, + d.mockFiles.AddToFixture(), + ) + + if len(result.Errs) > 0 { + return } - android.FailIfErrored(t, testErrs) - }) - } -} -func expectErrors(t *testing.T, actErrs []error, expErrs []string) (testErrs []error) { - actErrStrs := []string{} - for _, v := range actErrs { - actErrStrs = append(actErrStrs, v.Error()) - } - sort.Strings(actErrStrs) - if len(actErrStrs) != len(expErrs) { - t.Errorf("got (%d) errors, expected (%d) errors!", len(actErrStrs), len(expErrs)) - for _, v := range actErrStrs { - testErrs = append(testErrs, errors.New(v)) - } - } else { - sort.Strings(expErrs) - for i, v := range actErrStrs { - if !strings.Contains(v, expErrs[i]) { - testErrs = append(testErrs, errors.New(v)) + for _, e := range d.expectedBinaries { + t.Run(e.name, func(t *testing.T) { + expectModule(t, result.TestContext, e.name, e.actualVersion, e.srcsZip, e.pyRunfiles, e.depsSrcsZips) + }) } - } + }) } - - return } -func expectModule(t *testing.T, ctx *android.TestContext, buildDir, name, variant, expectedSrcsZip string, - expectedPyRunfiles, expectedDepsSrcsZips []string) (testErrs []error) { +func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles, expectedDepsSrcsZips []string) { module := ctx.ModuleForTests(name, variant) base, baseOk := module.Module().(*Module) @@ -393,56 +369,15 @@ func expectModule(t *testing.T, ctx *android.TestContext, buildDir, name, varian actualPyRunfiles = append(actualPyRunfiles, path.dest) } - if !reflect.DeepEqual(actualPyRunfiles, expectedPyRunfiles) { - testErrs = append(testErrs, errors.New(fmt.Sprintf( - `binary "%s" variant "%s" has unexpected pyRunfiles: %q! (expected: %q)`, - base.Name(), - base.properties.Actual_version, - actualPyRunfiles, - expectedPyRunfiles))) - } + android.AssertDeepEquals(t, "pyRunfiles", expectedPyRunfiles, actualPyRunfiles) - if base.srcsZip.String() != strings.Replace(expectedSrcsZip, "@prefix@", buildDir, 1) { - testErrs = append(testErrs, errors.New(fmt.Sprintf( - `binary "%s" variant "%s" has unexpected srcsZip: %q!`, - base.Name(), - base.properties.Actual_version, - base.srcsZip))) - } + android.AssertPathRelativeToTopEquals(t, "srcsZip", expectedSrcsZip, base.srcsZip) - for i, _ := range expectedDepsSrcsZips { - expectedDepsSrcsZips[i] = strings.Replace(expectedDepsSrcsZips[i], "@prefix@", buildDir, 1) - } - if !reflect.DeepEqual(base.depsSrcsZips.Strings(), expectedDepsSrcsZips) { - testErrs = append(testErrs, errors.New(fmt.Sprintf( - `binary "%s" variant "%s" has unexpected depsSrcsZips: %q!`, - base.Name(), - base.properties.Actual_version, - base.depsSrcsZips))) - } - - return -} - -func setUp() { - var err error - buildDir, err = ioutil.TempDir("", "soong_python_test") - if err != nil { - panic(err) - } + android.AssertPathsRelativeToTopEquals(t, "depsSrcsZips", expectedDepsSrcsZips, base.depsSrcsZips) } -func tearDown() { - os.RemoveAll(buildDir) -} +var emptyFixtureFactory = android.NewFixtureFactory(nil) func TestMain(m *testing.M) { - run := func() int { - setUp() - defer tearDown() - - return m.Run() - } - - os.Exit(run()) + os.Exit(m.Run()) } diff --git a/python/test.go b/python/test.go index b7cd4756a..6713189fd 100644 --- a/python/test.go +++ b/python/test.go @@ -22,8 +22,12 @@ import ( // This file contains the module types for building Python test. func init() { - android.RegisterModuleType("python_test_host", PythonTestHostFactory) - android.RegisterModuleType("python_test", PythonTestFactory) + registerPythonTestComponents(android.InitRegistrationContext) +} + +func registerPythonTestComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("python_test_host", PythonTestHostFactory) + ctx.RegisterModuleType("python_test", PythonTestFactory) } // Test option struct. diff --git a/python/testing.go b/python/testing.go new file mode 100644 index 000000000..ce1a5ab27 --- /dev/null +++ b/python/testing.go @@ -0,0 +1,24 @@ +// Copyright 2021 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 python + +import "android/soong/android" + +var PrepareForTestWithPythonBuildComponents = android.GroupFixturePreparers( + android.FixtureRegisterWithContext(registerPythonBinaryComponents), + android.FixtureRegisterWithContext(registerPythonLibraryComponents), + android.FixtureRegisterWithContext(registerPythonTestComponents), + android.FixtureRegisterWithContext(registerPythonMutators), +) diff --git a/rust/bindgen.go b/rust/bindgen.go index 56d660eca..db69e2337 100644 --- a/rust/bindgen.go +++ b/rust/bindgen.go @@ -29,7 +29,7 @@ var ( defaultBindgenFlags = []string{""} // bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures. - bindgenClangVersion = "clang-r399163b" + bindgenClangVersion = "clang-r412851" _ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string { if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" { diff --git a/rust/sanitize.go b/rust/sanitize.go index 67460bac4..2f44b204b 100644 --- a/rust/sanitize.go +++ b/rust/sanitize.go @@ -46,7 +46,6 @@ var fuzzerFlags = []string{ "-C llvm-args=-sanitizer-coverage-trace-geps", "-C llvm-args=-sanitizer-coverage-prune-blocks=0", "-C llvm-args=-sanitizer-coverage-pc-table", - "-C link-dead-code=y", "-Z sanitizer=address", // Sancov breaks with lto diff --git a/sdk/sdk.go b/sdk/sdk.go index 2c84a2e88..6ca851217 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -358,15 +358,36 @@ var _ android.ExcludeFromApexContentsTag = dependencyTag{} // For dependencies from an in-development version of an SDK member to frozen versions of the same member // e.g. libfoo -> libfoo.mysdk.11 and libfoo.mysdk.12 +// +// The dependency represented by this tag requires that for every APEX variant created for the +// `from` module that an equivalent APEX variant is created for the 'to' module. This is because an +// APEX that requires a specific version of an sdk (via the `uses_sdks` property will replace +// dependencies on the unversioned sdk member with a dependency on the appropriate versioned sdk +// member. In order for that to work the versioned sdk member needs to have a variant for that APEX. +// As it is not known at the time that the APEX variants are created which specific APEX variants of +// a versioned sdk members will be required it is necessary for the versioned sdk members to have +// variants for any APEX that it could be used within. +// +// If the APEX selects a versioned sdk member then it will not have a dependency on the `from` +// module at all so any dependencies of that module will not affect the APEX. However, if the APEX +// selects the unversioned sdk member then it must exclude all the versioned sdk members. In no +// situation would this dependency cause the `to` module to be added to the APEX hence why this tag +// also excludes the `to` module from being added to the APEX contents. type sdkMemberVersionedDepTag struct { dependencyTag member string version string } +func (t sdkMemberVersionedDepTag) AlwaysRequireApexVariant() bool { + return true +} + // Mark this tag so dependencies that use it are excluded from visibility enforcement. func (t sdkMemberVersionedDepTag) ExcludeFromVisibilityEnforcement() {} +var _ android.AlwaysRequireApexVariantTag = sdkMemberVersionedDepTag{} + // Step 1: create dependencies from an SDK module to its members. func memberMutator(mctx android.BottomUpMutatorContext) { if s, ok := mctx.Module().(*sdk); ok { diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index 05d8bdb92..b7da95cae 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -31,7 +31,7 @@ func TestMain(m *testing.M) { os.Exit(0) } - runTestWithBuildDir(m) + os.Exit(m.Run()) } func TestDepNotInRequiredSdks(t *testing.T) { diff --git a/sdk/testing.go b/sdk/testing.go index a5519f8f0..6df402cff 100644 --- a/sdk/testing.go +++ b/sdk/testing.go @@ -16,8 +16,6 @@ package sdk import ( "fmt" - "io/ioutil" - "os" "path/filepath" "strings" "testing" @@ -30,7 +28,7 @@ import ( ) var sdkFixtureFactory = android.NewFixtureFactory( - &buildDir, + nil, apex.PrepareForTestWithApexBuildComponents, cc.PrepareForTestWithCcDefaultModules, genrule.PrepareForTestWithGenRuleBuildComponents, @@ -326,28 +324,3 @@ type snapshotBuildInfo struct { // The final output zip. outputZip string } - -var buildDir string - -func setUp() { - var err error - buildDir, err = ioutil.TempDir("", "soong_sdk_test") - if err != nil { - panic(err) - } -} - -func tearDown() { - _ = os.RemoveAll(buildDir) -} - -func runTestWithBuildDir(m *testing.M) { - run := func() int { - setUp() - defer tearDown() - - return m.Run() - } - - os.Exit(run()) -} diff --git a/sysprop/Android.bp b/sysprop/Android.bp index 540a8dae8..1d5eb3130 100644 --- a/sysprop/Android.bp +++ b/sysprop/Android.bp @@ -14,6 +14,7 @@ bootstrap_go_package { ], srcs: [ "sysprop_library.go", + "testing.go", ], testSrcs: [ "sysprop_test.go", diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go index 892a16c8d..f1c2d0df0 100644 --- a/sysprop/sysprop_library.go +++ b/sysprop/sysprop_library.go @@ -193,7 +193,11 @@ func SyspropLibraries(config android.Config) []string { } func init() { - android.RegisterModuleType("sysprop_library", syspropLibraryFactory) + registerSyspropBuildComponents(android.InitRegistrationContext) +} + +func registerSyspropBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("sysprop_library", syspropLibraryFactory) } func (m *syspropLibrary) Name() string { diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go index fde41d67e..cb1e36230 100644 --- a/sysprop/sysprop_test.go +++ b/sysprop/sysprop_test.go @@ -15,72 +15,26 @@ package sysprop import ( - "reflect" + "os" + "strings" + "testing" "android/soong/android" "android/soong/cc" "android/soong/java" - "io/ioutil" - "os" - "strings" - "testing" - "github.com/google/blueprint/proptools" ) -var buildDir string - -func setUp() { - var err error - buildDir, err = ioutil.TempDir("", "soong_sysprop_test") - if err != nil { - panic(err) - } -} - -func tearDown() { - os.RemoveAll(buildDir) -} - func TestMain(m *testing.M) { - run := func() int { - setUp() - defer tearDown() - - return m.Run() - } - - os.Exit(run()) + os.Exit(m.Run()) } -func testContext(config android.Config) *android.TestContext { - - ctx := android.NewTestArchContext(config) - java.RegisterRequiredBuildComponentsForTest(ctx) - - ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) - - android.RegisterPrebuiltMutators(ctx) - - cc.RegisterRequiredBuildComponentsForTest(ctx) - - ctx.RegisterModuleType("sysprop_library", syspropLibraryFactory) +var emptyFixtureFactory = android.NewFixtureFactory(nil) - ctx.Register() - - return ctx -} - -func run(t *testing.T, ctx *android.TestContext, config android.Config) { +func test(t *testing.T, bp string) *android.TestResult { t.Helper() - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - android.FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - android.FailIfErrored(t, errs) -} -func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config { bp += ` cc_library { name: "libbase", @@ -126,9 +80,7 @@ func testConfig(env map[string]string, bp string, fs map[string][]byte) android. } ` - bp += cc.GatherRequiredDepsForTest(android.Android) - - mockFS := map[string][]byte{ + mockFS := android.MockFS{ "a.java": nil, "b.java": nil, "c.java": nil, @@ -172,31 +124,24 @@ func testConfig(env map[string]string, bp string, fs map[string][]byte) android. "com/android2/OdmProperties.sysprop": nil, } - for k, v := range fs { - mockFS[k] = v - } - - config := java.TestConfig(buildDir, env, bp, mockFS) - - config.TestProductVariables.DeviceSystemSdkVersions = []string{"28"} - config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current") - config.TestProductVariables.Platform_vndk_version = proptools.StringPtr("VER") - - return config - -} - -func test(t *testing.T, bp string) *android.TestContext { - t.Helper() - config := testConfig(nil, bp, nil) - ctx := testContext(config) - run(t, ctx, config) - - return ctx + result := emptyFixtureFactory.RunTest(t, + cc.PrepareForTestWithCcDefaultModules, + java.PrepareForTestWithJavaDefaultModules, + PrepareForTestWithSyspropBuildComponents, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.DeviceSystemSdkVersions = []string{"28"} + variables.DeviceVndkVersion = proptools.StringPtr("current") + variables.Platform_vndk_version = proptools.StringPtr("VER") + }), + mockFS.AddToFixture(), + android.FixtureWithRootAndroidBp(bp), + ) + + return result } func TestSyspropLibrary(t *testing.T) { - ctx := test(t, ` + result := test(t, ` sysprop_library { name: "sysprop-platform", apex_available: ["//apex_available:platform"], @@ -308,9 +253,9 @@ func TestSyspropLibrary(t *testing.T) { "android_vendor.VER_arm64_armv8-a_shared", "android_vendor.VER_arm64_armv8-a_static", } { - ctx.ModuleForTests("libsysprop-platform", variant) - ctx.ModuleForTests("libsysprop-vendor", variant) - ctx.ModuleForTests("libsysprop-odm", variant) + result.ModuleForTests("libsysprop-platform", variant) + result.ModuleForTests("libsysprop-vendor", variant) + result.ModuleForTests("libsysprop-odm", variant) } for _, variant := range []string{ @@ -319,21 +264,18 @@ func TestSyspropLibrary(t *testing.T) { "android_arm64_armv8-a_shared", "android_arm64_armv8-a_static", } { - library := ctx.ModuleForTests("libsysprop-platform", variant).Module().(*cc.Module) + library := result.ModuleForTests("libsysprop-platform", variant).Module().(*cc.Module) expectedApexAvailableOnLibrary := []string{"//apex_available:platform"} - if !reflect.DeepEqual(library.ApexProperties.Apex_available, expectedApexAvailableOnLibrary) { - t.Errorf("apex available property on libsysprop-platform must be %#v, but was %#v.", - expectedApexAvailableOnLibrary, library.ApexProperties.Apex_available) - } + android.AssertDeepEquals(t, "apex available property on libsysprop-platform", expectedApexAvailableOnLibrary, library.ApexProperties.Apex_available) // product variant of vendor-owned sysprop_library - ctx.ModuleForTests("libsysprop-vendor-on-product", variant) + result.ModuleForTests("libsysprop-vendor-on-product", variant) } - ctx.ModuleForTests("sysprop-platform", "android_common") - ctx.ModuleForTests("sysprop-platform_public", "android_common") - ctx.ModuleForTests("sysprop-vendor", "android_common") - ctx.ModuleForTests("sysprop-vendor-on-product", "android_common") + result.ModuleForTests("sysprop-platform", "android_common") + result.ModuleForTests("sysprop-platform_public", "android_common") + result.ModuleForTests("sysprop-vendor", "android_common") + result.ModuleForTests("sysprop-vendor-on-product", "android_common") // Check for exported includes coreVariant := "android_arm64_armv8-a_static" @@ -348,25 +290,19 @@ func TestSyspropLibrary(t *testing.T) { vendorInternalPath := "libsysprop-vendor/android_vendor.VER_arm64_armv8-a_static/gen/sysprop/include" vendorPublicPath := "libsysprop-vendor-on-product/android_arm64_armv8-a_static/gen/sysprop/public/include" - platformClient := ctx.ModuleForTests("cc-client-platform", coreVariant) + platformClient := result.ModuleForTests("cc-client-platform", coreVariant) platformFlags := platformClient.Rule("cc").Args["cFlags"] // platform should use platform's internal header - if !strings.Contains(platformFlags, platformInternalPath) { - t.Errorf("flags for platform must contain %#v, but was %#v.", - platformInternalPath, platformFlags) - } + android.AssertStringDoesContain(t, "flags for platform", platformFlags, platformInternalPath) - platformStaticClient := ctx.ModuleForTests("cc-client-platform-static", coreVariant) + platformStaticClient := result.ModuleForTests("cc-client-platform-static", coreVariant) platformStaticFlags := platformStaticClient.Rule("cc").Args["cFlags"] // platform-static should use platform's internal header - if !strings.Contains(platformStaticFlags, platformInternalPath) { - t.Errorf("flags for platform-static must contain %#v, but was %#v.", - platformInternalPath, platformStaticFlags) - } + android.AssertStringDoesContain(t, "flags for platform-static", platformStaticFlags, platformInternalPath) - productClient := ctx.ModuleForTests("cc-client-product", coreVariant) + productClient := result.ModuleForTests("cc-client-product", coreVariant) productFlags := productClient.Rule("cc").Args["cFlags"] // Product should use platform's and vendor's public headers @@ -376,7 +312,7 @@ func TestSyspropLibrary(t *testing.T) { platformPublicCorePath, vendorPublicPath, productFlags) } - vendorClient := ctx.ModuleForTests("cc-client-vendor", vendorVariant) + vendorClient := result.ModuleForTests("cc-client-vendor", vendorVariant) vendorFlags := vendorClient.Rule("cc").Args["cFlags"] // Vendor should use platform's public header and vendor's internal header @@ -387,15 +323,15 @@ func TestSyspropLibrary(t *testing.T) { } // Java modules linking against system API should use public stub - javaSystemApiClient := ctx.ModuleForTests("java-platform", "android_common").Rule("javac") - syspropPlatformPublic := ctx.ModuleForTests("sysprop-platform_public", "android_common").Description("for turbine") + javaSystemApiClient := result.ModuleForTests("java-platform", "android_common").Rule("javac") + syspropPlatformPublic := result.ModuleForTests("sysprop-platform_public", "android_common").Description("for turbine") if g, w := javaSystemApiClient.Implicits.Strings(), syspropPlatformPublic.Output.String(); !android.InList(w, g) { t.Errorf("system api client should use public stub %q, got %q", w, g) } } func TestApexAvailabilityIsForwarded(t *testing.T) { - ctx := test(t, ` + result := test(t, ` sysprop_library { name: "sysprop-platform", apex_available: ["//apex_available:platform"], @@ -407,23 +343,17 @@ func TestApexAvailabilityIsForwarded(t *testing.T) { expected := []string{"//apex_available:platform"} - ccModule := ctx.ModuleForTests("libsysprop-platform", "android_arm64_armv8-a_shared").Module().(*cc.Module) + ccModule := result.ModuleForTests("libsysprop-platform", "android_arm64_armv8-a_shared").Module().(*cc.Module) propFromCc := ccModule.ApexProperties.Apex_available - if !reflect.DeepEqual(propFromCc, expected) { - t.Errorf("apex_available not forwarded to cc module. expected %#v, got %#v", - expected, propFromCc) - } + android.AssertDeepEquals(t, "apex_available forwarding to cc module", expected, propFromCc) - javaModule := ctx.ModuleForTests("sysprop-platform", "android_common").Module().(*java.Library) + javaModule := result.ModuleForTests("sysprop-platform", "android_common").Module().(*java.Library) propFromJava := javaModule.ApexProperties.Apex_available - if !reflect.DeepEqual(propFromJava, expected) { - t.Errorf("apex_available not forwarded to java module. expected %#v, got %#v", - expected, propFromJava) - } + android.AssertDeepEquals(t, "apex_available forwarding to java module", expected, propFromJava) } func TestMinSdkVersionIsForwarded(t *testing.T) { - ctx := test(t, ` + result := test(t, ` sysprop_library { name: "sysprop-platform", srcs: ["android/sysprop/PlatformProperties.sysprop"], @@ -438,17 +368,11 @@ func TestMinSdkVersionIsForwarded(t *testing.T) { } `) - ccModule := ctx.ModuleForTests("libsysprop-platform", "android_arm64_armv8-a_shared").Module().(*cc.Module) + ccModule := result.ModuleForTests("libsysprop-platform", "android_arm64_armv8-a_shared").Module().(*cc.Module) propFromCc := proptools.String(ccModule.Properties.Min_sdk_version) - if propFromCc != "29" { - t.Errorf("min_sdk_version not forwarded to cc module. expected %#v, got %#v", - "29", propFromCc) - } + android.AssertStringEquals(t, "min_sdk_version forwarding to cc module", "29", propFromCc) - javaModule := ctx.ModuleForTests("sysprop-platform", "android_common").Module().(*java.Library) + javaModule := result.ModuleForTests("sysprop-platform", "android_common").Module().(*java.Library) propFromJava := javaModule.MinSdkVersion() - if propFromJava != "30" { - t.Errorf("min_sdk_version not forwarded to java module. expected %#v, got %#v", - "30", propFromJava) - } + android.AssertStringEquals(t, "min_sdk_version forwarding to java module", "30", propFromJava) } diff --git a/sysprop/testing.go b/sysprop/testing.go new file mode 100644 index 000000000..3e14be1a8 --- /dev/null +++ b/sysprop/testing.go @@ -0,0 +1,19 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// 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 sysprop + +import "android/soong/android" + +var PrepareForTestWithSyspropBuildComponents = android.FixtureRegisterWithContext(registerSyspropBuildComponents) diff --git a/ui/build/Android.bp b/ui/build/Android.bp index 32b6edade..d17b4645c 100644 --- a/ui/build/Android.bp +++ b/ui/build/Android.bp @@ -32,6 +32,8 @@ bootstrap_go_package { name: "soong-ui-build", pkgPath: "android/soong/ui/build", deps: [ + "blueprint", + "blueprint-bootstrap", "soong-ui-build-paths", "soong-ui-logger", "soong-ui-metrics", diff --git a/ui/build/bazel.go b/ui/build/bazel.go index 81ce9397a..ec561d540 100644 --- a/ui/build/bazel.go +++ b/ui/build/bazel.go @@ -116,6 +116,7 @@ func runBazel(ctx Context, config Config) { "RBE_exec_strategy", "RBE_invocation_id", "RBE_log_dir", + "RBE_num_retries_if_mismatched", "RBE_platform", "RBE_remote_accept_cache", "RBE_remote_update_cache", diff --git a/ui/build/build.go b/ui/build/build.go index 215a6c8ce..3692f4fb4 100644 --- a/ui/build/build.go +++ b/ui/build/build.go @@ -218,6 +218,11 @@ func Build(ctx Context, config Config, what int) { what = what &^ BuildKati } + if config.SkipNinja() { + ctx.Verboseln("Skipping Ninja as requested") + what = what &^ BuildNinja + } + if config.StartGoma() { // Ensure start Goma compiler_proxy startGoma(ctx, config) @@ -290,7 +295,7 @@ func Build(ctx Context, config Config, what int) { } // Run ninja - runNinja(ctx, config) + runNinjaForBuild(ctx, config) } // Currently, using Bazel requires Kati and Soong to run first, so check whether to run Bazel last. diff --git a/ui/build/config.go b/ui/build/config.go index 1152cd790..4816d1f15 100644 --- a/ui/build/config.go +++ b/ui/build/config.go @@ -48,6 +48,7 @@ type configImpl struct { dist bool skipConfig bool skipKati bool + skipNinja bool skipSoongTests bool // From the product config @@ -552,6 +553,8 @@ func (c *configImpl) parseArgs(ctx Context, args []string) { if arg == "--make-mode" { } else if arg == "showcommands" { c.verbose = true + } else if arg == "--skip-ninja" { + c.skipNinja = true } else if arg == "--skip-make" { c.skipConfig = true c.skipKati = true @@ -772,6 +775,10 @@ func (c *configImpl) SkipKati() bool { return c.skipKati } +func (c *configImpl) SkipNinja() bool { + return c.skipNinja +} + func (c *configImpl) SkipConfig() bool { return c.skipConfig } diff --git a/ui/build/ninja.go b/ui/build/ninja.go index 779976653..5961c4525 100644 --- a/ui/build/ninja.go +++ b/ui/build/ninja.go @@ -30,7 +30,7 @@ import ( // Constructs and runs the Ninja command line with a restricted set of // environment variables. It's important to restrict the environment Ninja runs // for hermeticity reasons, and to avoid spurious rebuilds. -func runNinja(ctx Context, config Config) { +func runNinjaForBuild(ctx Context, config Config) { ctx.BeginTrace(metrics.PrimaryNinja, "ninja") defer ctx.EndTrace() @@ -145,6 +145,7 @@ func runNinja(ctx Context, config Config) { "RBE_exec_strategy", "RBE_invocation_id", "RBE_log_dir", + "RBE_num_retries_if_mismatched", "RBE_platform", "RBE_remote_accept_cache", "RBE_remote_update_cache", diff --git a/ui/build/soong.go b/ui/build/soong.go index 884e95741..9afcb88ca 100644 --- a/ui/build/soong.go +++ b/ui/build/soong.go @@ -23,6 +23,8 @@ import ( "android/soong/shared" soong_metrics_proto "android/soong/ui/metrics/metrics_proto" + "github.com/google/blueprint" + "github.com/google/blueprint/bootstrap" "github.com/golang/protobuf/proto" "github.com/google/blueprint/microfactory" @@ -42,17 +44,78 @@ func writeEnvironmentFile(ctx Context, envFile string, envDeps map[string]string // This uses Android.bp files and various tools to generate <builddir>/build.ninja. // -// However, the execution of <builddir>/build.ninja happens later in build/soong/ui/build/build.go#Build() +// However, the execution of <builddir>/build.ninja happens later in +// build/soong/ui/build/build.go#Build() // -// We want to rely on as few prebuilts as possible, so there is some bootstrapping here. +// We want to rely on as few prebuilts as possible, so we need to bootstrap +// Soong. The process is as follows: // -// "Microfactory" is a tool for compiling Go code. We use it to build two other tools: -// - minibp, used to generate build.ninja files. This is really build/blueprint/bootstrap/command.go#Main() -// - bpglob, used during incremental builds to identify files in a glob that have changed -// -// In reality, several build.ninja files are generated and/or used during the bootstrapping and build process. -// See build/blueprint/bootstrap/doc.go for more information. +// 1. We use "Microfactory", a simple tool to compile Go code, to build +// first itself, then soong_ui from soong_ui.bash. This binary contains +// parts of soong_build that are needed to build itself. +// 2. This simplified version of soong_build then reads the Blueprint files +// that describe itself and emits .bootstrap/build.ninja that describes +// how to build its full version and use that to produce the final Ninja +// file Soong emits. +// 3. soong_ui executes .bootstrap/build.ninja // +// (After this, Kati is executed to parse the Makefiles, but that's not part of +// bootstrapping Soong) + +// A tiny struct used to tell Blueprint that it's in bootstrap mode. It would +// probably be nicer to use a flag in bootstrap.Args instead. +type BlueprintConfig struct { + srcDir string + buildDir string + ninjaBuildDir string + debugCompilation bool +} + +func (c BlueprintConfig) SrcDir() string { + return "." +} + +func (c BlueprintConfig) BuildDir() string { + return c.buildDir +} + +func (c BlueprintConfig) NinjaBuildDir() string { + return c.ninjaBuildDir +} + +func (c BlueprintConfig) DebugCompilation() bool { + return c.debugCompilation +} + +func bootstrapBlueprint(ctx Context, config Config) { + ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap") + defer ctx.EndTrace() + + var args bootstrap.Args + + args.RunGoTests = !config.skipSoongTests + args.UseValidations = true // Use validations to depend on tests + args.BuildDir = config.SoongOutDir() + args.NinjaBuildDir = config.OutDir() + args.TopFile = "Android.bp" + args.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list") + args.OutFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja") + args.DepFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja.d") + args.GlobFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/soong-build-globs.ninja") + args.GeneratingPrimaryBuilder = true + + blueprintCtx := blueprint.NewContext() + blueprintCtx.SetIgnoreUnknownModuleTypes(true) + blueprintConfig := BlueprintConfig{ + srcDir: os.Getenv("TOP"), + buildDir: config.SoongOutDir(), + ninjaBuildDir: config.OutDir(), + debugCompilation: os.Getenv("SOONG_DELVE") != "", + } + + bootstrap.RunBlueprint(args, blueprintCtx, blueprintConfig) +} + func runSoong(ctx Context, config Config) { ctx.BeginTrace(metrics.RunSoong, "soong") defer ctx.EndTrace() @@ -63,33 +126,15 @@ func runSoong(ctx Context, config Config) { // unused variables were changed? envFile := filepath.Join(config.SoongOutDir(), "soong.environment.available") - // Use an anonymous inline function for tracing purposes (this pattern is used several times below). - func() { - ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap") - defer ctx.EndTrace() - - // Use validations to depend on tests. - args := []string{"-n"} - - if !config.skipSoongTests { - // Run tests. - args = append(args, "-t") + for _, n := range []string{".bootstrap", ".minibootstrap"} { + dir := filepath.Join(config.SoongOutDir(), n) + if err := os.MkdirAll(dir, 0755); err != nil { + ctx.Fatalf("Cannot mkdir " + dir) } + } - cmd := Command(ctx, config, "blueprint bootstrap", "build/blueprint/bootstrap.bash", args...) - - cmd.Environment.Set("BLUEPRINTDIR", "./build/blueprint") - cmd.Environment.Set("BOOTSTRAP", "./build/blueprint/bootstrap.bash") - cmd.Environment.Set("BUILDDIR", config.SoongOutDir()) - cmd.Environment.Set("GOROOT", "./"+filepath.Join("prebuilts/go", config.HostPrebuiltTag())) - cmd.Environment.Set("BLUEPRINT_LIST_FILE", filepath.Join(config.FileListDir(), "Android.bp.list")) - cmd.Environment.Set("NINJA_BUILDDIR", config.OutDir()) - cmd.Environment.Set("SRCDIR", ".") - cmd.Environment.Set("TOPNAME", "Android.bp") - cmd.Sandbox = soongSandbox - - cmd.RunAndPrintOrFatal() - }() + // This is done unconditionally, but does not take a measurable amount of time + bootstrapBlueprint(ctx, config) soongBuildEnv := config.Environment().Copy() soongBuildEnv.Set("TOP", os.Getenv("TOP")) @@ -105,6 +150,11 @@ func runSoong(ctx Context, config Config) { soongBuildEnv.Set("BAZEL_WORKSPACE", absPath(ctx, ".")) soongBuildEnv.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir()) + // For Soong bootstrapping tests + if os.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" { + soongBuildEnv.Set("ALLOW_MISSING_DEPENDENCIES", "true") + } + err := writeEnvironmentFile(ctx, envFile, soongBuildEnv.AsMap()) if err != nil { ctx.Fatalf("failed to write environment file %s: %s", envFile, err) @@ -130,16 +180,6 @@ func runSoong(ctx Context, config Config) { cfg.TrimPath = absPath(ctx, ".") func() { - ctx.BeginTrace(metrics.RunSoong, "minibp") - defer ctx.EndTrace() - - minibp := filepath.Join(config.SoongOutDir(), ".minibootstrap/minibp") - if _, err := microfactory.Build(&cfg, minibp, "github.com/google/blueprint/bootstrap/minibp"); err != nil { - ctx.Fatalln("Failed to build minibp:", err) - } - }() - - func() { ctx.BeginTrace(metrics.RunSoong, "bpglob") defer ctx.EndTrace() @@ -187,10 +227,6 @@ func runSoong(ctx Context, config Config) { cmd.Sandbox = soongSandbox cmd.RunAndStreamOrFatal() } - - // This build generates .bootstrap/build.ninja, which is used in the next step. - ninja("minibootstrap", ".minibootstrap/build.ninja") - // This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build(). ninja("bootstrap", ".bootstrap/build.ninja") diff --git a/xml/Android.bp b/xml/Android.bp index a5e5f4c7f..154293032 100644 --- a/xml/Android.bp +++ b/xml/Android.bp @@ -13,6 +13,7 @@ bootstrap_go_package { "soong-etc", ], srcs: [ + "testing.go", "xml.go", ], testSrcs: [ diff --git a/xml/testing.go b/xml/testing.go new file mode 100644 index 000000000..1d09f1055 --- /dev/null +++ b/xml/testing.go @@ -0,0 +1,19 @@ +// Copyright 2018 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 xml + +import "android/soong/android" + +var PreparerForTestWithXmlBuildComponents = android.FixtureRegisterWithContext(registerXmlBuildComponents) diff --git a/xml/xml.go b/xml/xml.go index 8810ae4d5..c28107847 100644 --- a/xml/xml.go +++ b/xml/xml.go @@ -53,10 +53,14 @@ var ( ) func init() { - android.RegisterModuleType("prebuilt_etc_xml", PrebuiltEtcXmlFactory) + registerXmlBuildComponents(android.InitRegistrationContext) pctx.HostBinToolVariable("XmlLintCmd", "xmllint") } +func registerXmlBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("prebuilt_etc_xml", PrebuiltEtcXmlFactory) +} + type prebuiltEtcXmlProperties struct { // Optional DTD that will be used to validate the xml file. Schema *string `android:"path"` diff --git a/xml/xml_test.go b/xml/xml_test.go index 138503c6d..83ae51c4a 100644 --- a/xml/xml_test.go +++ b/xml/xml_test.go @@ -15,7 +15,6 @@ package xml import ( - "io/ioutil" "os" "testing" @@ -23,62 +22,33 @@ import ( "android/soong/etc" ) -var buildDir string - -func setUp() { - var err error - buildDir, err = ioutil.TempDir("", "soong_xml_test") - if err != nil { - panic(err) - } -} - -func tearDown() { - os.RemoveAll(buildDir) -} - func TestMain(m *testing.M) { - run := func() int { - setUp() - defer tearDown() - - return m.Run() - } - - os.Exit(run()) + os.Exit(m.Run()) } -func testXml(t *testing.T, bp string) *android.TestContext { - fs := map[string][]byte{ +var emptyFixtureFactory = android.NewFixtureFactory(nil) + +func testXml(t *testing.T, bp string) *android.TestResult { + fs := android.MockFS{ "foo.xml": nil, "foo.dtd": nil, "bar.xml": nil, "bar.xsd": nil, "baz.xml": nil, } - config := android.TestArchConfig(buildDir, nil, bp, fs) - ctx := android.NewTestArchContext(config) - ctx.RegisterModuleType("prebuilt_etc", etc.PrebuiltEtcFactory) - ctx.RegisterModuleType("prebuilt_etc_xml", PrebuiltEtcXmlFactory) - ctx.Register() - _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) - android.FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - android.FailIfErrored(t, errs) - return ctx -} - -func assertEqual(t *testing.T, name, expected, actual string) { - t.Helper() - if expected != actual { - t.Errorf(name+" expected %q != got %q", expected, actual) - } + return emptyFixtureFactory.RunTest(t, + android.PrepareForTestWithArchMutator, + etc.PrepareForTestWithPrebuiltEtc, + PreparerForTestWithXmlBuildComponents, + fs.AddToFixture(), + android.FixtureWithRootAndroidBp(bp), + ) } // Minimal test func TestPrebuiltEtcXml(t *testing.T) { - ctx := testXml(t, ` + result := testXml(t, ` prebuilt_etc_xml { name: "foo.xml", src: "foo.xml", @@ -103,14 +73,14 @@ func TestPrebuiltEtcXml(t *testing.T) { {rule: "xmllint-minimal", input: "baz.xml"}, } { t.Run(tc.schemaType, func(t *testing.T) { - rule := ctx.ModuleForTests(tc.input, "android_arm64_armv8-a").Rule(tc.rule) - assertEqual(t, "input", tc.input, rule.Input.String()) + rule := result.ModuleForTests(tc.input, "android_arm64_armv8-a").Rule(tc.rule) + android.AssertStringEquals(t, "input", tc.input, rule.Input.String()) if tc.schemaType != "" { - assertEqual(t, "schema", tc.schema, rule.Args[tc.schemaType]) + android.AssertStringEquals(t, "schema", tc.schema, rule.Args[tc.schemaType]) } }) } - m := ctx.ModuleForTests("foo.xml", "android_arm64_armv8-a").Module().(*prebuiltEtcXml) - assertEqual(t, "installDir", buildDir+"/target/product/test_device/system/etc", m.InstallDirPath().String()) + m := result.ModuleForTests("foo.xml", "android_arm64_armv8-a").Module().(*prebuiltEtcXml) + android.AssertPathRelativeToTopEquals(t, "installDir", "out/soong/target/product/test_device/system/etc", m.InstallDirPath()) } |