AAPT2: Introduce notion of 'product' to ResourceTable
This allows us to preserve the various product definitions during the compile
phase, and allows us to select the product in the link phase.
This allows compiled files to remain product-independent, so that they do not need
to be recompiled when switching targets.
Bug:25958912
Change-Id: Iaa7eed25c834b67a39cdc9be43613e8b5ab6cdd7
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
index c7e603e..459c330 100644
--- a/tools/aapt2/link/AutoVersioner.cpp
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -18,9 +18,7 @@
#include "ResourceTable.h"
#include "SdkConstants.h"
#include "ValueVisitor.h"
-
#include "link/Linkers.h"
-#include "util/Comparators.h"
#include <algorithm>
#include <cassert>
@@ -31,7 +29,12 @@
const int sdkVersionToGenerate) {
assert(sdkVersionToGenerate > config.sdkVersion);
const auto endIter = entry->values.end();
- auto iter = std::lower_bound(entry->values.begin(), endIter, config, cmp::lessThanConfig);
+ auto iter = entry->values.begin();
+ for (; iter != endIter; ++iter) {
+ if ((*iter)->config == config) {
+ break;
+ }
+ }
// The source config came from this list, so it should be here.
assert(iter != entry->values.end());
@@ -45,10 +48,10 @@
// are no higher sdk level versions of this resource.
ConfigDescription tempConfig(config);
for (; iter != endIter; ++iter) {
- tempConfig.sdkVersion = iter->config.sdkVersion;
- if (tempConfig == iter->config) {
+ tempConfig.sdkVersion = (*iter)->config.sdkVersion;
+ if (tempConfig == (*iter)->config) {
// The two configs are the same, check the sdk version.
- return sdkVersionToGenerate < iter->config.sdkVersion;
+ return sdkVersionToGenerate < (*iter)->config.sdkVersion;
}
}
@@ -65,14 +68,14 @@
for (auto& entry : type->entries) {
for (size_t i = 0; i < entry->values.size(); i++) {
- ResourceConfigValue& configValue = entry->values[i];
- if (configValue.config.sdkVersion >= SDK_LOLLIPOP_MR1) {
+ ResourceConfigValue* configValue = entry->values[i].get();
+ if (configValue->config.sdkVersion >= SDK_LOLLIPOP_MR1) {
// If this configuration is only used on L-MR1 then we don't need
// to do anything since we use private attributes since that version.
continue;
}
- if (Style* style = valueCast<Style>(configValue.value.get())) {
+ if (Style* style = valueCast<Style>(configValue->value.get())) {
Maybe<size_t> minSdkStripped;
std::vector<Style::Entry> stripped;
@@ -82,7 +85,7 @@
// Find the SDK level that is higher than the configuration allows.
const size_t sdkLevel = findAttributeSdkLevel(iter->key.id.value());
- if (sdkLevel > std::max<size_t>(configValue.config.sdkVersion, 1)) {
+ if (sdkLevel > std::max<size_t>(configValue->config.sdkVersion, 1)) {
// Record that we are about to strip this.
stripped.emplace_back(std::move(*iter));
@@ -105,10 +108,10 @@
// there is no other defined resource for the version we want to
// generate.
if (shouldGenerateVersionedResource(entry.get(),
- configValue.config,
+ configValue->config,
minSdkStripped.value())) {
// Let's create a new Style for this versioned resource.
- ConfigDescription newConfig(configValue.config);
+ ConfigDescription newConfig(configValue->config);
newConfig.sdkVersion = minSdkStripped.value();
std::unique_ptr<Style> newStyle(style->clone(&table->stringPool));
@@ -121,14 +124,8 @@
std::make_move_iterator(stripped.end()));
// Insert the new Resource into the correct place.
- auto iter = std::lower_bound(entry->values.begin(),
- entry->values.end(),
- newConfig,
- cmp::lessThanConfig);
-
- entry->values.insert(
- iter,
- ResourceConfigValue{ newConfig, std::move(newStyle) });
+ entry->findOrCreateValue(newConfig, {})->value =
+ std::move(newStyle);
}
}
}
diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp
index 29bcc93..9b3a87c 100644
--- a/tools/aapt2/link/AutoVersioner_test.cpp
+++ b/tools/aapt2/link/AutoVersioner_test.cpp
@@ -15,9 +15,7 @@
*/
#include "ConfigDescription.h"
-
#include "link/Linkers.h"
-
#include "test/Builders.h"
#include "test/Context.h"
@@ -31,9 +29,9 @@
const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land");
ResourceEntry entry(u"foo");
- entry.values.push_back(ResourceConfigValue{ defaultConfig });
- entry.values.push_back(ResourceConfigValue{ landConfig });
- entry.values.push_back(ResourceConfigValue{ sw600dpLandConfig });
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(sw600dpLandConfig, ""));
EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
EXPECT_TRUE(shouldGenerateVersionedResource(&entry, landConfig, 17));
@@ -45,9 +43,9 @@
const ConfigDescription v21Config = test::parseConfigOrDie("v21");
ResourceEntry entry(u"foo");
- entry.values.push_back(ResourceConfigValue{ defaultConfig });
- entry.values.push_back(ResourceConfigValue{ sw600dpV13Config });
- entry.values.push_back(ResourceConfigValue{ v21Config });
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(sw600dpV13Config, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(v21Config, ""));
EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
EXPECT_FALSE(shouldGenerateVersionedResource(&entry, defaultConfig, 22));
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 8e32179..3437ac0 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -31,6 +31,7 @@
#include "java/ManifestClassGenerator.h"
#include "java/ProguardRules.h"
#include "link/Linkers.h"
+#include "link/ProductFilter.h"
#include "link/ReferenceLinker.h"
#include "link/ManifestFixer.h"
#include "link/TableMerger.h"
@@ -70,6 +71,7 @@
Maybe<std::u16string> privateSymbols;
ManifestFixerOptions manifestFixerOptions;
IConfigFilter* configFilter = nullptr;
+ std::unordered_set<std::string> products;
};
struct LinkContext : public IAaptContext {
@@ -292,16 +294,16 @@
for (const auto& configValue : entry->values) {
// Special case the occurrence of an ID that is being generated for the
// 'android' package. This is due to legacy reasons.
- if (valueCast<Id>(configValue.value.get()) &&
+ if (valueCast<Id>(configValue->value.get()) &&
package->name == u"android") {
mContext->getDiagnostics()->warn(
- DiagMessage(configValue.value->getSource())
+ DiagMessage(configValue->value->getSource())
<< "generated id '" << resName
<< "' for external package '" << package->name
<< "'");
} else {
mContext->getDiagnostics()->error(
- DiagMessage(configValue.value->getSource())
+ DiagMessage(configValue->value->getSource())
<< "defined resource '" << resName
<< "' for external package '" << package->name
<< "'");
@@ -512,7 +514,10 @@
std::unique_ptr<Id> id = util::make_unique<Id>();
id->setSource(fileDesc->source.withLine(exportedSymbol.line));
- bool result = mFinalTable.addResourceAllowMangled(resName, {}, std::move(id),
+ bool result = mFinalTable.addResourceAllowMangled(resName,
+ ConfigDescription::defaultConfig(),
+ std::string(),
+ std::move(id),
mContext->getDiagnostics());
if (!result) {
return false;
@@ -681,6 +686,12 @@
mContext->getDiagnostics()->error(DiagMessage() << "failed linking references");
return 1;
}
+
+ ProductFilter productFilter(mOptions.products);
+ if (!productFilter.consume(mContext, &mFinalTable)) {
+ mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products");
+ return 1;
+ }
}
proguard::KeepSet proguardKeepSet;
@@ -931,6 +942,7 @@
Maybe<std::string> customJavaPackage;
std::vector<std::string> extraJavaPackages;
Maybe<std::string> configs;
+ Maybe<std::string> productList;
bool legacyXFlag = false;
bool requireLocalization = false;
Flags flags = Flags()
@@ -954,6 +966,8 @@
&requireLocalization)
.optionalFlag("-c", "Comma separated list of configurations to include. The default\n"
"is all configurations", &configs)
+ .optionalFlag("--product", "Comma separated list of product names to keep",
+ &productList)
.optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
"by -o",
&options.outputToDirectory)
@@ -1039,6 +1053,14 @@
}
}
+ if (productList) {
+ for (StringPiece product : util::tokenize<char>(productList.value(), ',')) {
+ if (product != "" && product != "default") {
+ options.products.insert(product.toString());
+ }
+ }
+ }
+
AxisConfigFilter filter;
if (configs) {
for (const StringPiece& configStr : util::tokenize<char>(configs.value(), ',')) {
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index 4d3a483..ec532ab 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -26,7 +26,7 @@
namespace aapt {
class ResourceTable;
-struct ResourceEntry;
+class ResourceEntry;
struct ConfigDescription;
/**
diff --git a/tools/aapt2/link/ProductFilter.cpp b/tools/aapt2/link/ProductFilter.cpp
new file mode 100644
index 0000000..8784e89
--- /dev/null
+++ b/tools/aapt2/link/ProductFilter.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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/ProductFilter.h"
+
+namespace aapt {
+
+ProductFilter::ResourceConfigValueIter
+ProductFilter::selectProductToKeep(const ResourceNameRef& name,
+ const ResourceConfigValueIter begin,
+ const ResourceConfigValueIter end,
+ IDiagnostics* diag) {
+ ResourceConfigValueIter defaultProductIter = end;
+ ResourceConfigValueIter selectedProductIter = end;
+
+ for (ResourceConfigValueIter iter = begin; iter != end; ++iter) {
+ ResourceConfigValue* configValue = iter->get();
+ if (mProducts.find(configValue->product) != mProducts.end()) {
+ if (selectedProductIter != end) {
+ // We have two possible values for this product!
+ diag->error(DiagMessage(configValue->value->getSource())
+ << "selection of product '" << configValue->product
+ << "' for resource " << name << " is ambiguous");
+
+ ResourceConfigValue* previouslySelectedConfigValue = selectedProductIter->get();
+ diag->note(DiagMessage(previouslySelectedConfigValue->value->getSource())
+ << "product '" << previouslySelectedConfigValue->product
+ << "' is also a candidate");
+ return end;
+ }
+
+ // Select this product.
+ selectedProductIter = iter;
+ }
+
+ if (configValue->product.empty() || configValue->product == "default") {
+ if (defaultProductIter != end) {
+ // We have two possible default values.
+ diag->error(DiagMessage(configValue->value->getSource())
+ << "multiple default products defined for resource " << name);
+
+ ResourceConfigValue* previouslyDefaultConfigValue = defaultProductIter->get();
+ diag->note(DiagMessage(previouslyDefaultConfigValue->value->getSource())
+ << "default product also defined here");
+ return end;
+ }
+
+ // Mark the default.
+ defaultProductIter = iter;
+ }
+ }
+
+ if (defaultProductIter == end) {
+ diag->error(DiagMessage() << "no default product defined for resource " << name);
+ return end;
+ }
+
+ if (selectedProductIter == end) {
+ selectedProductIter = defaultProductIter;
+ }
+ return selectedProductIter;
+}
+
+bool ProductFilter::consume(IAaptContext* context, ResourceTable* table) {
+ bool error = false;
+ for (auto& pkg : table->packages) {
+ for (auto& type : pkg->types) {
+ for (auto& entry : type->entries) {
+ std::vector<std::unique_ptr<ResourceConfigValue>> newValues;
+
+ ResourceConfigValueIter iter = entry->values.begin();
+ ResourceConfigValueIter startRangeIter = iter;
+ while (iter != entry->values.end()) {
+ ++iter;
+ if (iter == entry->values.end() ||
+ (*iter)->config != (*startRangeIter)->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->type, entry->name);
+ auto valueToKeep = selectProductToKeep(name, startRangeIter, iter,
+ context->getDiagnostics());
+ if (valueToKeep == 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.
+ newValues.push_back(std::move(*valueToKeep));
+ }
+
+ // Start the next range of products.
+ startRangeIter = iter;
+ }
+ }
+
+ // Now move the new values in to place.
+ entry->values = std::move(newValues);
+ }
+ }
+ }
+ return !error;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/ProductFilter.h b/tools/aapt2/link/ProductFilter.h
new file mode 100644
index 0000000..d2edd38
--- /dev/null
+++ b/tools/aapt2/link/ProductFilter.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_LINK_PRODUCTFILTER_H
+#define AAPT_LINK_PRODUCTFILTER_H
+
+#include "ResourceTable.h"
+#include "process/IResourceTableConsumer.h"
+
+#include <android-base/macros.h>
+#include <unordered_set>
+
+namespace aapt {
+
+class ProductFilter {
+public:
+ using ResourceConfigValueIter = std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
+
+ ProductFilter(std::unordered_set<std::string> products) : mProducts(products) { }
+
+ ResourceConfigValueIter selectProductToKeep(const ResourceNameRef& name,
+ const ResourceConfigValueIter begin,
+ const ResourceConfigValueIter end,
+ IDiagnostics* diag);
+
+ bool consume(IAaptContext* context, ResourceTable* table);
+
+private:
+ std::unordered_set<std::string> mProducts;
+
+ DISALLOW_COPY_AND_ASSIGN(ProductFilter);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_LINK_PRODUCTFILTER_H */
diff --git a/tools/aapt2/link/ProductFilter_test.cpp b/tools/aapt2/link/ProductFilter_test.cpp
new file mode 100644
index 0000000..f4f756a
--- /dev/null
+++ b/tools/aapt2/link/ProductFilter_test.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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/ProductFilter.h"
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+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(test::parseNameOrDie(u"@android:string/one"),
+ land, "",
+ test::ValueBuilder<Id>()
+ .setSource(Source("land/default.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ land, "tablet",
+ test::ValueBuilder<Id>()
+ .setSource(Source("land/tablet.xml")).build(),
+ context->getDiagnostics()));
+
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ port, "",
+ test::ValueBuilder<Id>()
+ .setSource(Source("port/default.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ port, "tablet",
+ test::ValueBuilder<Id>()
+ .setSource(Source("port/tablet.xml")).build(),
+ context->getDiagnostics()));
+
+ ProductFilter filter({ "tablet" });
+ ASSERT_TRUE(filter.consume(context.get(), &table));
+
+ EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ land, ""));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ land, "tablet"));
+ EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ port, ""));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ port, "tablet"));
+}
+
+TEST(ProductFilterTest, SelectDefaultProduct) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+ ResourceTable table;
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "",
+ test::ValueBuilder<Id>()
+ .setSource(Source("default.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "tablet",
+ test::ValueBuilder<Id>()
+ .setSource(Source("tablet.xml")).build(),
+ context->getDiagnostics()));
+
+ ProductFilter filter({});
+ ASSERT_TRUE(filter.consume(context.get(), &table));
+
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ ConfigDescription::defaultConfig(),
+ ""));
+ EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one",
+ ConfigDescription::defaultConfig(),
+ "tablet"));
+}
+
+TEST(ProductFilterTest, FailOnAmbiguousProduct) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+ ResourceTable table;
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "",
+ test::ValueBuilder<Id>()
+ .setSource(Source("default.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "tablet",
+ test::ValueBuilder<Id>()
+ .setSource(Source("tablet.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "no-sdcard",
+ test::ValueBuilder<Id>()
+ .setSource(Source("no-sdcard.xml")).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(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "",
+ test::ValueBuilder<Id>()
+ .setSource(Source(".xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"),
+ ConfigDescription::defaultConfig(), "default",
+ test::ValueBuilder<Id>()
+ .setSource(Source("default.xml")).build(),
+ context->getDiagnostics()));
+
+ ProductFilter filter({});
+ ASSERT_FALSE(filter.consume(context.get(), &table));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 2743539..ef3fe4f 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -316,7 +316,7 @@
&table->stringPool, &declStack, &callSite);
for (auto& configValue : entry->values) {
- configValue.value->accept(&visitor);
+ configValue->value->accept(&visitor);
}
if (visitor.hasError()) {
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index e01a004..2ecd5b0 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -18,9 +18,7 @@
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-
#include "link/TableMerger.h"
-#include "util/Comparators.h"
#include "util/Util.h"
#include <cassert>
@@ -197,28 +195,28 @@
ResourceNameRef resName(mMasterPackage->name, dstType->type, dstEntry->name);
- for (ResourceConfigValue& srcValue : srcEntry->values) {
- auto iter = std::lower_bound(dstEntry->values.begin(), dstEntry->values.end(),
- srcValue.config, cmp::lessThanConfig);
+ for (auto& srcValue : srcEntry->values) {
+ ResourceConfigValue* dstValue = dstEntry->findValue(srcValue->config,
+ srcValue->product);
const bool stripConfig = mOptions.filter ?
- !mOptions.filter->match(srcValue.config) : false;
+ !mOptions.filter->match(srcValue->config) : false;
- if (iter != dstEntry->values.end() && iter->config == srcValue.config) {
+ if (dstValue) {
const int collisionResult = ResourceTable::resolveValueCollision(
- iter->value.get(), srcValue.value.get());
+ dstValue->value.get(), srcValue->value.get());
if (collisionResult == 0 && !overlay) {
// Error!
ResourceNameRef resourceName(srcPackage->name,
srcType->type,
srcEntry->name);
- mContext->getDiagnostics()->error(DiagMessage(srcValue.value->getSource())
+ mContext->getDiagnostics()->error(DiagMessage(srcValue->value->getSource())
<< "resource '" << resourceName
<< "' has a conflicting value for "
<< "configuration ("
- << srcValue.config << ")");
- mContext->getDiagnostics()->note(DiagMessage(iter->value->getSource())
+ << srcValue->config << ")");
+ mContext->getDiagnostics()->note(DiagMessage(dstValue->value->getSource())
<< "originally defined here");
error = true;
continue;
@@ -227,16 +225,18 @@
continue;
}
- } else if (!stripConfig){
- // Insert a place holder value. We will fill it in below.
- iter = dstEntry->values.insert(iter, ResourceConfigValue{ srcValue.config });
}
if (stripConfig) {
continue;
}
- if (FileReference* f = valueCast<FileReference>(srcValue.value.get())) {
+ if (!dstValue) {
+ // Force create the entry if we didn't have it.
+ dstValue = dstEntry->findOrCreateValue(srcValue->config, srcValue->product);
+ }
+
+ if (FileReference* f = valueCast<FileReference>(srcValue->value.get())) {
std::unique_ptr<FileReference> newFileRef;
if (manglePackage) {
newFileRef = cloneAndMangleFile(srcPackage->name, *f);
@@ -246,15 +246,15 @@
}
if (callback) {
- if (!callback(resName, iter->config, newFileRef.get(), f)) {
+ if (!callback(resName, srcValue->config, newFileRef.get(), f)) {
error = true;
continue;
}
}
- iter->value = std::move(newFileRef);
+ dstValue->value = std::move(newFileRef);
} else {
- iter->value = std::unique_ptr<Value>(srcValue.value->clone(
+ dstValue->value = std::unique_ptr<Value>(srcValue->value->clone(
&mMasterTable->stringPool));
}
}
@@ -290,7 +290,8 @@
ResourceTablePackage* pkg = table.createPackage(fileDesc.name.package, 0x0);
pkg->findOrCreateType(fileDesc.name.type)
->findOrCreateEntry(fileDesc.name.entry)
- ->values.push_back(ResourceConfigValue{ fileDesc.config, std::move(fileRef) });
+ ->findOrCreateValue(fileDesc.config, {})
+ ->value = std::move(fileRef);
auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
FileReference* newFile, FileReference* oldFile) -> bool {