Implement filter-product
filter-product option is added to aapt2 compile, which filters values
with a given product and removes all other values (including default).
The motivation of this change is to generate product-specific RRO
automatically, making a main resource APK (e.g. framework-res.apk)
identical in all targets.
Bug: 294799593
Test: build
Change-Id: I42eb3e134c7aa120f6bbe2d26d311bd46b586595
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index 44cd276..18165f7 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -20,12 +20,12 @@
#include <set>
#include <unordered_set>
-#include "android-base/macros.h"
-#include "androidfw/ConfigDescription.h"
-#include "androidfw/StringPiece.h"
-
#include "Resource.h"
#include "SdkConstants.h"
+#include "android-base/macros.h"
+#include "android-base/result.h"
+#include "androidfw/ConfigDescription.h"
+#include "androidfw/StringPiece.h"
#include "process/IResourceTableConsumer.h"
#include "xml/XmlDom.h"
@@ -92,28 +92,6 @@
DISALLOW_COPY_AND_ASSIGN(PrivateAttributeMover);
};
-class ResourceConfigValue;
-
-class ProductFilter : public IResourceTableConsumer {
- public:
- using ResourceConfigValueIter = std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
-
- explicit ProductFilter(std::unordered_set<std::string> products) : products_(products) {
- }
-
- ResourceConfigValueIter SelectProductToKeep(const ResourceNameRef& name,
- const ResourceConfigValueIter begin,
- const ResourceConfigValueIter end,
- android::IDiagnostics* diag);
-
- bool Consume(IAaptContext* context, ResourceTable* table) override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ProductFilter);
-
- std::unordered_set<std::string> products_;
-};
-
// Removes namespace nodes and URI information from the XmlResource.
//
// Once an XmlResource is processed by this consumer, it is no longer able to have its attributes
diff --git a/tools/aapt2/link/ProductFilter.cpp b/tools/aapt2/link/ProductFilter.cpp
deleted file mode 100644
index 9544986..0000000
--- a/tools/aapt2/link/ProductFilter.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#include "link/Linkers.h"
-
-#include "ResourceTable.h"
-#include "trace/TraceBuffer.h"
-
-namespace aapt {
-
-ProductFilter::ResourceConfigValueIter ProductFilter::SelectProductToKeep(
- const ResourceNameRef& name, const ResourceConfigValueIter begin,
- const ResourceConfigValueIter end, android::IDiagnostics* diag) {
- ResourceConfigValueIter default_product_iter = end;
- ResourceConfigValueIter selected_product_iter = end;
-
- for (ResourceConfigValueIter iter = begin; iter != end; ++iter) {
- ResourceConfigValue* config_value = iter->get();
- if (products_.find(config_value->product) != products_.end()) {
- if (selected_product_iter != end) {
- // We have two possible values for this product!
- diag->Error(android::DiagMessage(config_value->value->GetSource())
- << "selection of product '" << config_value->product << "' for resource "
- << name << " is ambiguous");
-
- ResourceConfigValue* previously_selected_config_value =
- selected_product_iter->get();
- diag->Note(android::DiagMessage(previously_selected_config_value->value->GetSource())
- << "product '" << previously_selected_config_value->product
- << "' is also a candidate");
- return end;
- }
-
- // Select this product.
- selected_product_iter = iter;
- }
-
- if (config_value->product.empty() || config_value->product == "default") {
- if (default_product_iter != end) {
- // We have two possible default values.
- diag->Error(android::DiagMessage(config_value->value->GetSource())
- << "multiple default products defined for resource " << name);
-
- ResourceConfigValue* previously_default_config_value =
- default_product_iter->get();
- diag->Note(android::DiagMessage(previously_default_config_value->value->GetSource())
- << "default product also defined here");
- return end;
- }
-
- // Mark the default.
- default_product_iter = iter;
- }
- }
-
- if (default_product_iter == end) {
- diag->Error(android::DiagMessage() << "no default product defined for resource " << name);
- return end;
- }
-
- if (selected_product_iter == end) {
- selected_product_iter = default_product_iter;
- }
- return selected_product_iter;
-}
-
-bool ProductFilter::Consume(IAaptContext* context, ResourceTable* table) {
- TRACE_NAME("ProductFilter::Consume");
- bool error = false;
- for (auto& pkg : table->packages) {
- for (auto& type : pkg->types) {
- for (auto& entry : type->entries) {
- std::vector<std::unique_ptr<ResourceConfigValue>> new_values;
-
- ResourceConfigValueIter iter = entry->values.begin();
- ResourceConfigValueIter start_range_iter = iter;
- while (iter != entry->values.end()) {
- ++iter;
- if (iter == entry->values.end() ||
- (*iter)->config != (*start_range_iter)->config) {
- // End of the array, or we saw a different config,
- // so this must be the end of a range of products.
- // Select the product to keep from the set of products defined.
- ResourceNameRef name(pkg->name, type->named_type, entry->name);
- auto value_to_keep = SelectProductToKeep(
- name, start_range_iter, iter, context->GetDiagnostics());
- if (value_to_keep == iter) {
- // An error occurred, we could not pick a product.
- error = true;
- } else {
- // We selected a product to keep. Move it to the new array.
- new_values.push_back(std::move(*value_to_keep));
- }
-
- // Start the next range of products.
- start_range_iter = iter;
- }
- }
-
- // Now move the new values in to place.
- entry->values = std::move(new_values);
- }
- }
- }
- return !error;
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/link/ProductFilter_test.cpp b/tools/aapt2/link/ProductFilter_test.cpp
deleted file mode 100644
index 2cb9afa..0000000
--- a/tools/aapt2/link/ProductFilter_test.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#include "link/Linkers.h"
-
-#include "test/Test.h"
-
-using ::android::ConfigDescription;
-
-namespace aapt {
-
-TEST(ProductFilterTest, SelectTwoProducts) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
-
- const ConfigDescription land = test::ParseConfigOrDie("land");
- const ConfigDescription port = test::ParseConfigOrDie("port");
-
- ResourceTable table;
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("land/default.xml")).Build(),
- land)
- .Build(),
- context->GetDiagnostics()));
-
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("land/tablet.xml")).Build(),
- land, "tablet")
- .Build(),
- context->GetDiagnostics()));
-
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("port/default.xml")).Build(),
- port)
- .Build(),
- context->GetDiagnostics()));
-
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("port/tablet.xml")).Build(),
- port, "tablet")
- .Build(),
- context->GetDiagnostics()));
-
- ProductFilter filter({"tablet"});
- ASSERT_TRUE(filter.Consume(context.get(), &table));
-
- EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one", land, ""));
- EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one", land, "tablet"));
- EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one", port, ""));
- EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one", port, "tablet"));
-}
-
-TEST(ProductFilterTest, SelectDefaultProduct) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
-
- ResourceTable table;
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("default.xml")).Build())
- .Build(),
- context->GetDiagnostics()));
-
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("tablet.xml")).Build(), {},
- "tablet")
- .Build(),
- context->GetDiagnostics()));
- ;
-
- ProductFilter filter(std::unordered_set<std::string>{});
- ASSERT_TRUE(filter.Consume(context.get(), &table));
-
- EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one",
- ConfigDescription::DefaultConfig(), ""));
- EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one",
- ConfigDescription::DefaultConfig(), "tablet"));
-}
-
-TEST(ProductFilterTest, FailOnAmbiguousProduct) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
-
- ResourceTable table;
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("default.xml")).Build())
- .Build(),
- context->GetDiagnostics()));
-
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("tablet.xml")).Build(), {},
- "tablet")
- .Build(),
- context->GetDiagnostics()));
-
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("no-sdcard.xml")).Build(),
- {}, "no-sdcard")
- .Build(),
- context->GetDiagnostics()));
-
- ProductFilter filter({"tablet", "no-sdcard"});
- ASSERT_FALSE(filter.Consume(context.get(), &table));
-}
-
-TEST(ProductFilterTest, FailOnMultipleDefaults) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
-
- ResourceTable table;
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source(".xml")).Build())
- .Build(),
- context->GetDiagnostics()));
-
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("default.xml")).Build(), {},
- "default")
- .Build(),
- context->GetDiagnostics()));
-
- ProductFilter filter(std::unordered_set<std::string>{});
- ASSERT_FALSE(filter.Consume(context.get(), &table));
-}
-
-} // namespace aapt