Filter products during compile phase
Unfortunately there is no good way to deal with products in the link phase.
Products are like preprocessor defines in that they are processed early
and change the composition of the compiled unit.
Change-Id: I6d5e15ef60d29df8e83e059ba857c09333993779
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 5e5fc53..63629f0 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -49,8 +49,9 @@
}
ResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source,
- const ConfigDescription& config) :
- mDiag(diag), mTable(table), mSource(source), mConfig(config) {
+ const ConfigDescription& config,
+ const ResourceParserOptions& options) :
+ mDiag(diag), mTable(table), mSource(source), mConfig(config), mOptions(options) {
}
/**
@@ -157,7 +158,62 @@
return !error;
}
+static bool shouldStripResource(XmlPullParser* parser, const Maybe<std::u16string> productToMatch) {
+ assert(parser->getEvent() == XmlPullParser::Event::kStartElement);
+
+ if (Maybe<StringPiece16> maybeProduct = findNonEmptyAttribute(parser, u"product")) {
+ if (!productToMatch) {
+ if (maybeProduct.value() != u"default" && maybeProduct.value() != u"phone") {
+ // We didn't specify a product and this is not a default product, so skip.
+ return true;
+ }
+ } else {
+ if (productToMatch && maybeProduct.value() != productToMatch.value()) {
+ // We specified a product, but they don't match.
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * A parsed resource ready to be added to the ResourceTable.
+ */
+struct ParsedResource {
+ ResourceName name;
+ Source source;
+ ResourceId id;
+ bool markPublic = false;
+ std::unique_ptr<Value> value;
+ std::list<ParsedResource> childResources;
+};
+
+// Recursively adds resources to the ResourceTable.
+static bool addResourcesToTable(ResourceTable* table, const ConfigDescription& config,
+ IDiagnostics* diag, ParsedResource* res) {
+ if (res->markPublic && !table->markPublic(res->name, res->id, res->source, diag)) {
+ return false;
+ }
+
+ if (!res->value) {
+ return true;
+ }
+
+ if (!table->addResource(res->name, res->id, config, res->source, std::move(res->value), diag)) {
+ return false;
+ }
+
+ bool error = false;
+ for (ParsedResource& child : res->childResources) {
+ error |= !addResourcesToTable(table, config, diag, &child);
+ }
+ return !error;
+}
+
bool ResourceParser::parseResources(XmlPullParser* parser) {
+ std::set<ResourceName> strippedResources;
+
bool error = false;
std::u16string comment;
const size_t depth = parser->getDepth();
@@ -198,9 +254,8 @@
continue;
}
- // Copy because our iterator will go out of scope when
- // we parse more XML.
- std::u16string name = maybeName.value().toString();
+ // Check if we should skip this product.
+ const bool stripResource = shouldStripResource(parser, mOptions.product);
if (elementName == u"item") {
// Items simply have their type encoded in the type attribute.
@@ -214,48 +269,85 @@
}
}
- if (elementName == u"id") {
- error |= !mTable->addResource(ResourceNameRef{ {}, ResourceType::kId, name },
- {}, mSource.withLine(parser->getLineNumber()),
- util::make_unique<Id>(), mDiag);
+ ParsedResource parsedResource;
+ parsedResource.name.entry = maybeName.value().toString();
+ parsedResource.source = mSource.withLine(parser->getLineNumber());
+ bool result = true;
+ if (elementName == u"id") {
+ parsedResource.name.type = ResourceType::kId;
+ parsedResource.value = util::make_unique<Id>();
} else if (elementName == u"string") {
- error |= !parseString(parser, ResourceNameRef{ {}, ResourceType::kString, name });
+ parsedResource.name.type = ResourceType::kString;
+ result = parseString(parser, &parsedResource);
} else if (elementName == u"color") {
- error |= !parseColor(parser, ResourceNameRef{ {}, ResourceType::kColor, name });
+ parsedResource.name.type = ResourceType::kColor;
+ result = parseColor(parser, &parsedResource);
} else if (elementName == u"drawable") {
- error |= !parseColor(parser, ResourceNameRef{ {}, ResourceType::kDrawable, name });
+ parsedResource.name.type = ResourceType::kDrawable;
+ result = parseColor(parser, &parsedResource);
} else if (elementName == u"bool") {
- error |= !parsePrimitive(parser, ResourceNameRef{ {}, ResourceType::kBool, name });
+ parsedResource.name.type = ResourceType::kBool;
+ result = parsePrimitive(parser, &parsedResource);
} else if (elementName == u"integer") {
- error |= !parsePrimitive(parser, ResourceNameRef{ {}, ResourceType::kInteger, name });
+ parsedResource.name.type = ResourceType::kInteger;
+ result = parsePrimitive(parser, &parsedResource);
} else if (elementName == u"dimen") {
- error |= !parsePrimitive(parser, ResourceNameRef{ {}, ResourceType::kDimen, name });
+ parsedResource.name.type = ResourceType::kDimen;
+ result = parsePrimitive(parser, &parsedResource);
} else if (elementName == u"style") {
- error |= !parseStyle(parser, ResourceNameRef{ {}, ResourceType::kStyle, name });
+ parsedResource.name.type = ResourceType::kStyle;
+ result = parseStyle(parser, &parsedResource);
} else if (elementName == u"plurals") {
- error |= !parsePlural(parser, ResourceNameRef{ {}, ResourceType::kPlurals, name });
+ parsedResource.name.type = ResourceType::kPlurals;
+ result = parsePlural(parser, &parsedResource);
} else if (elementName == u"array") {
- error |= !parseArray(parser, ResourceNameRef{ {}, ResourceType::kArray, name },
- android::ResTable_map::TYPE_ANY);
+ parsedResource.name.type = ResourceType::kArray;
+ result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_ANY);
} else if (elementName == u"string-array") {
- error |= !parseArray(parser, ResourceNameRef{ {}, ResourceType::kArray, name },
- android::ResTable_map::TYPE_STRING);
+ parsedResource.name.type = ResourceType::kArray;
+ result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_STRING);
} else if (elementName == u"integer-array") {
- error |= !parseArray(parser, ResourceNameRef{ {}, ResourceType::kArray, name },
- android::ResTable_map::TYPE_INTEGER);
- } else if (elementName == u"public") {
- error |= !parsePublic(parser, name);
+ parsedResource.name.type = ResourceType::kIntegerArray;
+ result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_INTEGER);
} else if (elementName == u"declare-styleable") {
- error |= !parseDeclareStyleable(parser,
- ResourceNameRef{ {}, ResourceType::kStyleable, name });
+ parsedResource.name.type = ResourceType::kStyleable;
+ result = parseDeclareStyleable(parser, &parsedResource);
} else if (elementName == u"attr") {
- error |= !parseAttr(parser, ResourceNameRef{ {}, ResourceType::kAttr, name });
+ parsedResource.name.type = ResourceType::kAttr;
+ result = parseAttr(parser, &parsedResource);
+ } else if (elementName == u"public") {
+ result = parsePublic(parser, &parsedResource);
} else {
mDiag->warn(DiagMessage(mSource.withLine(parser->getLineNumber()))
<< "unknown resource type '" << elementName << "'");
}
+
+ if (result) {
+ // We successfully parsed the resource.
+
+ if (stripResource) {
+ // Record that we stripped out this resource name.
+ // We will check that at least one variant of this resource was included.
+ strippedResources.insert(parsedResource.name);
+ } else {
+ error |= !addResourcesToTable(mTable, mConfig, mDiag, &parsedResource);
+ }
+ } else {
+ error = true;
+ }
}
+
+ // Check that we included at least one variant of each stripped resource.
+ for (const ResourceName& strippedResource : strippedResources) {
+ if (!mTable->findResource(strippedResource)) {
+ // Failed to find the resource.
+ mDiag->error(DiagMessage(mSource) << "resource '" << strippedResource << "' "
+ "was filtered out but no product variant remains");
+ error = true;
+ }
+ }
+
return !error;
}
@@ -322,53 +414,43 @@
return {};
}
-bool ResourceParser::parseString(XmlPullParser* parser, const ResourceNameRef& resourceName) {
+bool ResourceParser::parseString(XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
// TODO(adamlesinski): Read "untranslateable" attribute.
- if (Maybe<StringPiece16> maybeProduct = findAttribute(parser, u"product")) {
- if (maybeProduct.value() != u"default" && maybeProduct.value() != u"phone") {
- // TODO(adamlesinski): Actually match product.
- return true;
- }
- }
-
- std::unique_ptr<Item> processedItem = parseXml(parser, android::ResTable_map::TYPE_STRING,
- kNoRawString);
- if (!processedItem) {
+ outResource->value = parseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString);
+ if (!outResource->value) {
mDiag->error(DiagMessage(source) << "not a valid string");
return false;
}
- return mTable->addResource(resourceName, mConfig, source, std::move(processedItem),
- mDiag);
+ return true;
}
-bool ResourceParser::parseColor(XmlPullParser* parser, const ResourceNameRef& resourceName) {
+bool ResourceParser::parseColor(XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
- std::unique_ptr<Item> item = parseXml(parser, android::ResTable_map::TYPE_COLOR, kNoRawString);
- if (!item) {
+ outResource->value = parseXml(parser, android::ResTable_map::TYPE_COLOR, kNoRawString);
+ if (!outResource->value) {
mDiag->error(DiagMessage(source) << "invalid color");
return false;
}
- return mTable->addResource(resourceName, mConfig, source, std::move(item),
- mDiag);
+ return true;
}
-bool ResourceParser::parsePrimitive(XmlPullParser* parser, const ResourceNameRef& resourceName) {
+bool ResourceParser::parsePrimitive(XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
uint32_t typeMask = 0;
- switch (resourceName.type) {
+ switch (outResource->name.type) {
case ResourceType::kInteger:
typeMask |= android::ResTable_map::TYPE_INTEGER;
break;
case ResourceType::kDimen:
typeMask |= android::ResTable_map::TYPE_DIMENSION
- | android::ResTable_map::TYPE_FLOAT
- | android::ResTable_map::TYPE_FRACTION;
+ | android::ResTable_map::TYPE_FLOAT
+ | android::ResTable_map::TYPE_FRACTION;
break;
case ResourceType::kBool:
@@ -380,16 +462,15 @@
break;
}
- std::unique_ptr<Item> item = parseXml(parser, typeMask, kNoRawString);
- if (!item) {
- mDiag->error(DiagMessage(source) << "invalid " << resourceName.type);
+ outResource->value = parseXml(parser, typeMask, kNoRawString);
+ if (!outResource->value) {
+ mDiag->error(DiagMessage(source) << "invalid " << outResource->name.type);
return false;
}
- return mTable->addResource(resourceName, mConfig, source, std::move(item),
- mDiag);
+ return true;
}
-bool ResourceParser::parsePublic(XmlPullParser* parser, const StringPiece16& name) {
+bool ResourceParser::parsePublic(XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type");
@@ -405,27 +486,28 @@
return false;
}
- ResourceNameRef resourceName { {}, *parsedType, name };
- ResourceId resourceId;
+ outResource->name.type = *parsedType;
if (Maybe<StringPiece16> maybeId = findNonEmptyAttribute(parser, u"id")) {
android::Res_value val;
bool result = android::ResTable::stringToInt(maybeId.value().data(),
maybeId.value().size(), &val);
- resourceId.id = val.data;
+ ResourceId resourceId(val.data);
if (!result || !resourceId.isValid()) {
mDiag->error(DiagMessage(source) << "invalid resource ID '" << maybeId.value()
<< "' in <public>");
return false;
}
+ outResource->id = resourceId;
}
if (*parsedType == ResourceType::kId) {
// An ID marked as public is also the definition of an ID.
- mTable->addResource(resourceName, {}, source, util::make_unique<Id>(),
- mDiag);
+ outResource->value = util::make_unique<Id>();
}
- return mTable->markPublic(resourceName, resourceId, source, mDiag);
+
+ outResource->markPublic = true;
+ return true;
}
static uint32_t parseFormatType(const StringPiece16& piece) {
@@ -455,20 +537,13 @@
return mask;
}
-bool ResourceParser::parseAttr(XmlPullParser* parser, const ResourceNameRef& resourceName) {
- const Source source = mSource.withLine(parser->getLineNumber());
- ResourceName actualName = resourceName.toResourceName();
- std::unique_ptr<Attribute> attr = parseAttrImpl(parser, &actualName, false);
- if (!attr) {
- return false;
- }
- return mTable->addResource(actualName, mConfig, source, std::move(attr),
- mDiag);
+
+bool ResourceParser::parseAttr(XmlPullParser* parser, ParsedResource* outResource) {
+ outResource->source = mSource.withLine(parser->getLineNumber());
+ return parseAttrImpl(parser, outResource, false);
}
-std::unique_ptr<Attribute> ResourceParser::parseAttrImpl(XmlPullParser* parser,
- ResourceName* resourceName,
- bool weak) {
+bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outResource, bool weak) {
uint32_t typeMask = 0;
Maybe<StringPiece16> maybeFormat = findAttribute(parser, u"format");
@@ -477,7 +552,7 @@
if (typeMask == 0) {
mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
<< "invalid attribute format '" << maybeFormat.value() << "'");
- return {};
+ return false;
}
}
@@ -486,10 +561,10 @@
// No format attribute is allowed.
if (weak && !maybeFormat) {
StringPiece16 package, type, name;
- ResourceUtils::extractResourceName(resourceName->entry, &package, &type, &name);
+ ResourceUtils::extractResourceName(outResource->name.entry, &package, &type, &name);
if (type.empty() && !package.empty()) {
- resourceName->package = package.toString();
- resourceName->entry = name.toString();
+ outResource->name.package = package.toString();
+ outResource->name.entry = name.toString();
}
}
@@ -526,14 +601,12 @@
}
if (Maybe<Attribute::Symbol> s = parseEnumOrFlagItem(parser, elementName)) {
- if (mTable->addResource(s.value().symbol.name.value(), mConfig,
- mSource.withLine(parser->getLineNumber()),
- util::make_unique<Id>(),
- mDiag)) {
- items.push_back(std::move(s.value()));
- } else {
- error = true;
- }
+ ParsedResource childResource;
+ childResource.name = s.value().symbol.name.value();
+ childResource.source = mSource.withLine(parser->getLineNumber());
+ childResource.value = util::make_unique<Id>();
+ outResource->childResources.push_back(std::move(childResource));
+ items.push_back(std::move(s.value()));
} else {
error = true;
}
@@ -548,13 +621,14 @@
}
if (error) {
- return {};
+ return false;
}
std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(weak);
attr->symbols.swap(items);
attr->typeMask = typeMask ? typeMask : uint32_t(android::ResTable_map::TYPE_ANY);
- return attr;
+ outResource->value = std::move(attr);
+ return true;
}
Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(XmlPullParser* parser,
@@ -582,8 +656,8 @@
}
return Attribute::Symbol{
- Reference(ResourceName{ {}, ResourceType::kId, maybeName.value().toString() }),
- val.data };
+ Reference(ResourceName{ {}, ResourceType::kId, maybeName.value().toString() }),
+ val.data };
}
static Maybe<ResourceName> parseXmlAttributeName(StringPiece16 str) {
@@ -604,7 +678,7 @@
}
return ResourceName{ package.toString(), ResourceType::kAttr,
- name.empty() ? str.toString() : name.toString() };
+ name.empty() ? str.toString() : name.toString() };
}
@@ -637,7 +711,7 @@
return true;
}
-bool ResourceParser::parseStyle(XmlPullParser* parser, const ResourceNameRef& resourceName) {
+bool ResourceParser::parseStyle(XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
std::unique_ptr<Style> style = util::make_unique<Style>();
@@ -660,12 +734,12 @@
} else {
// No parent was specified, so try inferring it from the style name.
- std::u16string styleName = resourceName.entry.toString();
+ std::u16string styleName = outResource->name.entry;
size_t pos = styleName.find_last_of(u'.');
if (pos != std::string::npos) {
style->parentInferred = true;
- style->parent = Reference(ResourceName{
- {}, ResourceType::kStyle, styleName.substr(0, pos) });
+ style->parent = Reference(
+ ResourceName({}, ResourceType::kStyle, styleName.substr(0, pos)));
}
}
@@ -697,11 +771,12 @@
if (error) {
return false;
}
- return mTable->addResource(resourceName, mConfig, source, std::move(style),
- mDiag);
+
+ outResource->value = std::move(style);
+ return true;
}
-bool ResourceParser::parseArray(XmlPullParser* parser, const ResourceNameRef& resourceName,
+bool ResourceParser::parseArray(XmlPullParser* parser, ParsedResource* outResource,
uint32_t typeMask) {
const Source source = mSource.withLine(parser->getLineNumber());
std::unique_ptr<Array> array = util::make_unique<Array>();
@@ -741,11 +816,12 @@
if (error) {
return false;
}
- return mTable->addResource(resourceName, mConfig, source, std::move(array),
- mDiag);
+
+ outResource->value = std::move(array);
+ return true;
}
-bool ResourceParser::parsePlural(XmlPullParser* parser, const ResourceNameRef& resourceName) {
+bool ResourceParser::parsePlural(XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
@@ -816,11 +892,12 @@
if (error) {
return false;
}
- return mTable->addResource(resourceName, mConfig, source, std::move(plural), mDiag);
+
+ outResource->value = std::move(plural);
+ return true;
}
-bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser,
- const ResourceNameRef& resourceName) {
+bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
@@ -844,22 +921,17 @@
continue;
}
- // Copy because our iterator will be invalidated.
- ResourceName attrResourceName = { {}, ResourceType::kAttr, attrIter->value };
+ ParsedResource childResource;
+ childResource.name = ResourceName({}, ResourceType::kAttr, attrIter->value);
+ childResource.source = mSource.withLine(parser->getLineNumber());
- std::unique_ptr<Attribute> attr = parseAttrImpl(parser, &attrResourceName, true);
- if (!attr) {
+ if (!parseAttrImpl(parser, &childResource, true)) {
error = true;
continue;
}
- styleable->entries.emplace_back(attrResourceName);
-
- // Add the attribute to the resource table. Since it is weakly defined,
- // it won't collide.
- error |= !mTable->addResource(attrResourceName, mConfig,
- mSource.withLine(parser->getLineNumber()),
- std::move(attr), mDiag);
+ styleable->entries.push_back(Reference(childResource.name));
+ outResource->childResources.push_back(std::move(childResource));
} else if (elementNamespace.empty() &&
(elementName == u"skip" || elementName == u"eat-comment")) {
@@ -875,7 +947,9 @@
if (error) {
return false;
}
- return mTable->addResource(resourceName, mConfig, source, std::move(styleable), mDiag);
+
+ outResource->value = std::move(styleable);
+ return true;
}
} // namespace aapt