Loading tools/aapt2/link/NoDefaultResourceRemover.cpp +31 −19 Original line number Diff line number Diff line Loading @@ -22,17 +22,7 @@ namespace aapt { static bool IsDefaultConfigRequired(const ConfigDescription& config) { // We don't want to be overzealous with resource removal, so have strict requirements. // If a resource defines a value for a locale-only configuration, the default configuration is // required. if (ConfigDescription::DefaultConfig().diff(config) == ConfigDescription::CONFIG_LOCALE) { return true; } return false; } static bool KeepResource(const std::unique_ptr<ResourceEntry>& entry) { static bool KeepResource(const std::unique_ptr<ResourceEntry>& entry, int minSdk) { if (entry->visibility.level == Visibility::Level::kPublic) { // Removing a public API without the developer knowing is bad, so just leave this here for now. return true; Loading @@ -44,22 +34,44 @@ static bool KeepResource(const std::unique_ptr<ResourceEntry>& entry) { } // There is no default value defined, check if removal is required. bool defaultRequired = false; for (const auto& config_value : entry->values) { if (IsDefaultConfigRequired(config_value->config)) { return false; const int config = ConfigDescription::DefaultConfig().diff(config_value->config); // If a resource defines a value for a locale-only configuration, the default configuration is // required. if (config == ConfigDescription::CONFIG_LOCALE) { defaultRequired = true; } // If a resource defines a version-only config, the config value can be used as a default if // the version is at most the minimum sdk version else if (config == ConfigDescription::CONFIG_VERSION && config_value->config.sdkVersion <= minSdk) { return true; } // If a resource defines a value for a density only configuration, then that value could be used // as a default and the entry should not be removed else if (config == ConfigDescription::CONFIG_DENSITY || (config == (ConfigDescription::CONFIG_DENSITY | ConfigDescription::CONFIG_VERSION) && config_value->config.sdkVersion <= minSdk)) { return true; } } return !defaultRequired; } bool NoDefaultResourceRemover::Consume(IAaptContext* context, ResourceTable* table) { const ConfigDescription default_config = ConfigDescription::DefaultConfig(); for (auto& pkg : table->packages) { for (auto& type : pkg->types) { // Gather the entries without defaults that must be removed const int minSdk = context->GetMinSdkVersion(); const auto end_iter = type->entries.end(); const auto new_end_iter = std::stable_partition(type->entries.begin(), end_iter, KeepResource); for (auto iter = new_end_iter; iter != end_iter; ++iter) { const auto remove_iter = std::stable_partition(type->entries.begin(), end_iter, [&minSdk](const std::unique_ptr<ResourceEntry>& entry) -> bool { return KeepResource(entry, minSdk); }); for (auto iter = remove_iter; iter != end_iter; ++iter) { const ResourceName name(pkg->name, type->type, (*iter)->name); IDiagnostics* diag = context->GetDiagnostics(); diag->Warn(DiagMessage() << "removing resource " << name Loading @@ -74,7 +86,7 @@ bool NoDefaultResourceRemover::Consume(IAaptContext* context, ResourceTable* tab } } type->entries.erase(new_end_iter, type->entries.end()); type->entries.erase(remove_iter, end_iter); } } return true; Loading tools/aapt2/link/NoDefaultResourceRemover_test.cpp +60 −0 Original line number Diff line number Diff line Loading @@ -46,4 +46,64 @@ TEST(NoDefaultResourceRemoverTest, RemoveEntryWithNoDefaultAndOnlyLocales) { EXPECT_TRUE(table->FindResource(test::ParseNameOrDie("android:string/baz"))); } TEST(NoDefaultResourceRemoverTest, KeepEntryWithLocalesAndDensities) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().SetMinSdkVersion(26).Build(); std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() .SetPackageId("android", 0x01) .AddSimple("android:drawable/keep1", test::ParseConfigOrDie("mdpi")) // v4 .AddSimple("android:drawable/keep1", test::ParseConfigOrDie("en-rGB")) .AddSimple("android:drawable/keep1", test::ParseConfigOrDie("fr-rFR")) .AddSimple("android:drawable/keep2", test::ParseConfigOrDie("fr-rFR")) .AddSimple("android:drawable/keep2", test::ParseConfigOrDie("en-rGB")) .AddSimple("android:drawable/keep2", test::ParseConfigOrDie("xxxhdpi")) // v4 .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("fr-rFR")) .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("en-rGB")) .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("w600dp-xhdpi")) // v13 .Build(); NoDefaultResourceRemover remover; ASSERT_TRUE(remover.Consume(context.get(), table.get())); EXPECT_TRUE(table->FindResource(test::ParseNameOrDie("android:drawable/keep1"))); EXPECT_TRUE(table->FindResource(test::ParseNameOrDie("android:drawable/keep2"))); EXPECT_FALSE(table->FindResource(test::ParseNameOrDie("android:drawable/remove1"))); } TEST(NoDefaultResourceRemoverTest, RemoveEntryWithLocalesAndDensitiesLowVersion) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().SetMinSdkVersion(3).Build(); std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() .SetPackageId("android", 0x01) .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("mdpi")) // v4 .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("en-rGB")) .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("fr-rFR")) .Build(); NoDefaultResourceRemover remover; ASSERT_TRUE(remover.Consume(context.get(), table.get())); EXPECT_FALSE(table->FindResource(test::ParseNameOrDie("android:drawable/remove1"))); } TEST(NoDefaultResourceRemoverTest, KeepEntryWithVersion) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().SetMinSdkVersion(8).Build(); std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() .SetPackageId("android", 0x01) .AddSimple("android:drawable/keep1", test::ParseConfigOrDie("v8")) .AddSimple("android:drawable/keep1", test::ParseConfigOrDie("en-rGB")) .AddSimple("android:drawable/keep1", test::ParseConfigOrDie("fr-rFR")) .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("v9")) .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("en-rGB")) .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("fr-rFR")) .Build(); NoDefaultResourceRemover remover; ASSERT_TRUE(remover.Consume(context.get(), table.get())); EXPECT_TRUE(table->FindResource(test::ParseNameOrDie("android:drawable/keep1"))); EXPECT_FALSE(table->FindResource(test::ParseNameOrDie("android:drawable/remove1"))); } } // namespace aapt Loading
tools/aapt2/link/NoDefaultResourceRemover.cpp +31 −19 Original line number Diff line number Diff line Loading @@ -22,17 +22,7 @@ namespace aapt { static bool IsDefaultConfigRequired(const ConfigDescription& config) { // We don't want to be overzealous with resource removal, so have strict requirements. // If a resource defines a value for a locale-only configuration, the default configuration is // required. if (ConfigDescription::DefaultConfig().diff(config) == ConfigDescription::CONFIG_LOCALE) { return true; } return false; } static bool KeepResource(const std::unique_ptr<ResourceEntry>& entry) { static bool KeepResource(const std::unique_ptr<ResourceEntry>& entry, int minSdk) { if (entry->visibility.level == Visibility::Level::kPublic) { // Removing a public API without the developer knowing is bad, so just leave this here for now. return true; Loading @@ -44,22 +34,44 @@ static bool KeepResource(const std::unique_ptr<ResourceEntry>& entry) { } // There is no default value defined, check if removal is required. bool defaultRequired = false; for (const auto& config_value : entry->values) { if (IsDefaultConfigRequired(config_value->config)) { return false; const int config = ConfigDescription::DefaultConfig().diff(config_value->config); // If a resource defines a value for a locale-only configuration, the default configuration is // required. if (config == ConfigDescription::CONFIG_LOCALE) { defaultRequired = true; } // If a resource defines a version-only config, the config value can be used as a default if // the version is at most the minimum sdk version else if (config == ConfigDescription::CONFIG_VERSION && config_value->config.sdkVersion <= minSdk) { return true; } // If a resource defines a value for a density only configuration, then that value could be used // as a default and the entry should not be removed else if (config == ConfigDescription::CONFIG_DENSITY || (config == (ConfigDescription::CONFIG_DENSITY | ConfigDescription::CONFIG_VERSION) && config_value->config.sdkVersion <= minSdk)) { return true; } } return !defaultRequired; } bool NoDefaultResourceRemover::Consume(IAaptContext* context, ResourceTable* table) { const ConfigDescription default_config = ConfigDescription::DefaultConfig(); for (auto& pkg : table->packages) { for (auto& type : pkg->types) { // Gather the entries without defaults that must be removed const int minSdk = context->GetMinSdkVersion(); const auto end_iter = type->entries.end(); const auto new_end_iter = std::stable_partition(type->entries.begin(), end_iter, KeepResource); for (auto iter = new_end_iter; iter != end_iter; ++iter) { const auto remove_iter = std::stable_partition(type->entries.begin(), end_iter, [&minSdk](const std::unique_ptr<ResourceEntry>& entry) -> bool { return KeepResource(entry, minSdk); }); for (auto iter = remove_iter; iter != end_iter; ++iter) { const ResourceName name(pkg->name, type->type, (*iter)->name); IDiagnostics* diag = context->GetDiagnostics(); diag->Warn(DiagMessage() << "removing resource " << name Loading @@ -74,7 +86,7 @@ bool NoDefaultResourceRemover::Consume(IAaptContext* context, ResourceTable* tab } } type->entries.erase(new_end_iter, type->entries.end()); type->entries.erase(remove_iter, end_iter); } } return true; Loading
tools/aapt2/link/NoDefaultResourceRemover_test.cpp +60 −0 Original line number Diff line number Diff line Loading @@ -46,4 +46,64 @@ TEST(NoDefaultResourceRemoverTest, RemoveEntryWithNoDefaultAndOnlyLocales) { EXPECT_TRUE(table->FindResource(test::ParseNameOrDie("android:string/baz"))); } TEST(NoDefaultResourceRemoverTest, KeepEntryWithLocalesAndDensities) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().SetMinSdkVersion(26).Build(); std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() .SetPackageId("android", 0x01) .AddSimple("android:drawable/keep1", test::ParseConfigOrDie("mdpi")) // v4 .AddSimple("android:drawable/keep1", test::ParseConfigOrDie("en-rGB")) .AddSimple("android:drawable/keep1", test::ParseConfigOrDie("fr-rFR")) .AddSimple("android:drawable/keep2", test::ParseConfigOrDie("fr-rFR")) .AddSimple("android:drawable/keep2", test::ParseConfigOrDie("en-rGB")) .AddSimple("android:drawable/keep2", test::ParseConfigOrDie("xxxhdpi")) // v4 .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("fr-rFR")) .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("en-rGB")) .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("w600dp-xhdpi")) // v13 .Build(); NoDefaultResourceRemover remover; ASSERT_TRUE(remover.Consume(context.get(), table.get())); EXPECT_TRUE(table->FindResource(test::ParseNameOrDie("android:drawable/keep1"))); EXPECT_TRUE(table->FindResource(test::ParseNameOrDie("android:drawable/keep2"))); EXPECT_FALSE(table->FindResource(test::ParseNameOrDie("android:drawable/remove1"))); } TEST(NoDefaultResourceRemoverTest, RemoveEntryWithLocalesAndDensitiesLowVersion) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().SetMinSdkVersion(3).Build(); std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() .SetPackageId("android", 0x01) .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("mdpi")) // v4 .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("en-rGB")) .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("fr-rFR")) .Build(); NoDefaultResourceRemover remover; ASSERT_TRUE(remover.Consume(context.get(), table.get())); EXPECT_FALSE(table->FindResource(test::ParseNameOrDie("android:drawable/remove1"))); } TEST(NoDefaultResourceRemoverTest, KeepEntryWithVersion) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().SetMinSdkVersion(8).Build(); std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() .SetPackageId("android", 0x01) .AddSimple("android:drawable/keep1", test::ParseConfigOrDie("v8")) .AddSimple("android:drawable/keep1", test::ParseConfigOrDie("en-rGB")) .AddSimple("android:drawable/keep1", test::ParseConfigOrDie("fr-rFR")) .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("v9")) .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("en-rGB")) .AddSimple("android:drawable/remove1", test::ParseConfigOrDie("fr-rFR")) .Build(); NoDefaultResourceRemover remover; ASSERT_TRUE(remover.Consume(context.get(), table.get())); EXPECT_TRUE(table->FindResource(test::ParseNameOrDie("android:drawable/keep1"))); EXPECT_FALSE(table->FindResource(test::ParseNameOrDie("android:drawable/remove1"))); } } // namespace aapt