From 9ba47d813075fcb05c5e1532c137c93b394631cb Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Tue, 13 Oct 2015 11:37:10 -0700 Subject: 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 --- tools/aapt2/ResourceParser.cpp | 306 +++++++++++++++++++++++++---------------- 1 file changed, 190 insertions(+), 116 deletions(-) (limited to 'tools/aapt2/ResourceParser.cpp') diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 5e5fc5338e29..63629f0d6c10 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -49,8 +49,9 @@ static Maybe findNonEmptyAttribute(XmlPullParser* parser, } 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 @@ bool ResourceParser::parse(XmlPullParser* parser) { return !error; } +static bool shouldStripResource(XmlPullParser* parser, const Maybe productToMatch) { + assert(parser->getEvent() == XmlPullParser::Event::kStartElement); + + if (Maybe 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; + std::list 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 strippedResources; + bool error = false; std::u16string comment; const size_t depth = parser->getDepth(); @@ -198,9 +254,8 @@ bool ResourceParser::parseResources(XmlPullParser* parser) { 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 @@ bool ResourceParser::parseResources(XmlPullParser* parser) { } } - if (elementName == u"id") { - error |= !mTable->addResource(ResourceNameRef{ {}, ResourceType::kId, name }, - {}, mSource.withLine(parser->getLineNumber()), - util::make_unique(), 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(); } 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 @@ std::unique_ptr ResourceParser::parseXml(XmlPullParser* parser, uint32_t t 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 maybeProduct = findAttribute(parser, u"product")) { - if (maybeProduct.value() != u"default" && maybeProduct.value() != u"phone") { - // TODO(adamlesinski): Actually match product. - return true; - } - } - - std::unique_ptr 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 = 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 @@ bool ResourceParser::parsePrimitive(XmlPullParser* parser, const ResourceNameRef break; } - std::unique_ptr 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 maybeType = findNonEmptyAttribute(parser, u"type"); @@ -405,27 +486,28 @@ bool ResourceParser::parsePublic(XmlPullParser* parser, const StringPiece16& nam return false; } - ResourceNameRef resourceName { {}, *parsedType, name }; - ResourceId resourceId; + outResource->name.type = *parsedType; if (Maybe 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 "); 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(), - mDiag); + outResource->value = util::make_unique(); } - return mTable->markPublic(resourceName, resourceId, source, mDiag); + + outResource->markPublic = true; + return true; } static uint32_t parseFormatType(const StringPiece16& piece) { @@ -455,20 +537,13 @@ static uint32_t parseFormatAttribute(const StringPiece16& str) { return mask; } -bool ResourceParser::parseAttr(XmlPullParser* parser, const ResourceNameRef& resourceName) { - const Source source = mSource.withLine(parser->getLineNumber()); - ResourceName actualName = resourceName.toResourceName(); - std::unique_ptr 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 ResourceParser::parseAttrImpl(XmlPullParser* parser, - ResourceName* resourceName, - bool weak) { +bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outResource, bool weak) { uint32_t typeMask = 0; Maybe maybeFormat = findAttribute(parser, u"format"); @@ -477,7 +552,7 @@ std::unique_ptr ResourceParser::parseAttrImpl(XmlPullParser* parser, if (typeMask == 0) { mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber())) << "invalid attribute format '" << maybeFormat.value() << "'"); - return {}; + return false; } } @@ -486,10 +561,10 @@ std::unique_ptr ResourceParser::parseAttrImpl(XmlPullParser* parser, // 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 @@ std::unique_ptr ResourceParser::parseAttrImpl(XmlPullParser* parser, } if (Maybe s = parseEnumOrFlagItem(parser, elementName)) { - if (mTable->addResource(s.value().symbol.name.value(), mConfig, - mSource.withLine(parser->getLineNumber()), - util::make_unique(), - 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(); + outResource->childResources.push_back(std::move(childResource)); + items.push_back(std::move(s.value())); } else { error = true; } @@ -548,13 +621,14 @@ std::unique_ptr ResourceParser::parseAttrImpl(XmlPullParser* parser, } if (error) { - return {}; + return false; } std::unique_ptr attr = util::make_unique(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 ResourceParser::parseEnumOrFlagItem(XmlPullParser* parser, @@ -582,8 +656,8 @@ Maybe ResourceParser::parseEnumOrFlagItem(XmlPullParser* pars } return Attribute::Symbol{ - Reference(ResourceName{ {}, ResourceType::kId, maybeName.value().toString() }), - val.data }; + Reference(ResourceName{ {}, ResourceType::kId, maybeName.value().toString() }), + val.data }; } static Maybe parseXmlAttributeName(StringPiece16 str) { @@ -604,7 +678,7 @@ static Maybe parseXmlAttributeName(StringPiece16 str) { } return ResourceName{ package.toString(), ResourceType::kAttr, - name.empty() ? str.toString() : name.toString() }; + name.empty() ? str.toString() : name.toString() }; } @@ -637,7 +711,7 @@ bool ResourceParser::parseStyleItem(XmlPullParser* parser, Style* style) { 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