diff options
-rw-r--r-- | tools/aapt2/Diagnostics.h | 13 | ||||
-rw-r--r-- | tools/aapt2/Resource.h | 7 | ||||
-rw-r--r-- | tools/aapt2/ResourceParser.cpp | 306 | ||||
-rw-r--r-- | tools/aapt2/ResourceParser.h | 36 | ||||
-rw-r--r-- | tools/aapt2/ResourceParser_test.cpp | 46 | ||||
-rw-r--r-- | tools/aapt2/ResourceTable.cpp | 10 | ||||
-rw-r--r-- | tools/aapt2/ResourceTable.h | 8 | ||||
-rw-r--r-- | tools/aapt2/compile/Compile.cpp | 19 | ||||
-rw-r--r-- | tools/aapt2/flatten/TableFlattener.cpp | 270 | ||||
-rw-r--r-- | tools/aapt2/link/Link.cpp | 12 | ||||
-rw-r--r-- | tools/aapt2/link/TableMerger.cpp | 2 | ||||
-rw-r--r-- | tools/aapt2/test/Common.h | 1 | ||||
-rw-r--r-- | tools/aapt2/unflatten/BinaryResourceParser.cpp | 33 |
13 files changed, 473 insertions, 290 deletions
diff --git a/tools/aapt2/Diagnostics.h b/tools/aapt2/Diagnostics.h index d20ae1b92191..7ea26b3ed0df 100644 --- a/tools/aapt2/Diagnostics.h +++ b/tools/aapt2/Diagnostics.h @@ -51,12 +51,6 @@ public: mMessage << value; return *this; } -/* - template <typename T> DiagMessage& operator<<( - const ::std::function<::std::ostream&(::std::ostream&)>& f) { - f(mMessage); - return *this; - }*/ DiagMessageActual build() const { return DiagMessageActual{ mSource, mMessage.str() }; @@ -72,6 +66,8 @@ struct IDiagnostics { }; struct StdErrDiagnostics : public IDiagnostics { + size_t mNumErrors = 0; + void emit(const DiagMessage& msg, const char* tag) { DiagMessageActual actual = msg.build(); if (!actual.source.path.empty()) { @@ -81,7 +77,10 @@ struct StdErrDiagnostics : public IDiagnostics { } void error(const DiagMessage& msg) override { - emit(msg, "error: "); + if (mNumErrors < 20) { + emit(msg, "error: "); + } + mNumErrors++; } void warn(const DiagMessage& msg) override { diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h index 31fe298670ae..7ef18973d89b 100644 --- a/tools/aapt2/Resource.h +++ b/tools/aapt2/Resource.h @@ -78,6 +78,9 @@ struct ResourceName { ResourceType type; std::u16string entry; + ResourceName() = default; + ResourceName(const StringPiece16& p, ResourceType t, const StringPiece16& e); + bool isValid() const; bool operator<(const ResourceName& rhs) const; bool operator==(const ResourceName& rhs) const; @@ -226,6 +229,10 @@ inline ::std::ostream& operator<<(::std::ostream& out, const ResourceType& val) // ResourceName implementation. // +inline ResourceName::ResourceName(const StringPiece16& p, ResourceType t, const StringPiece16& e) : + package(p.toString()), type(t), entry(e.toString()) { +} + inline bool ResourceName::isValid() const { return !package.empty() && !entry.empty(); } 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<StringPiece16> 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<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 @@ 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<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 @@ std::unique_ptr<Item> 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<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 @@ bool ResourceParser::parsePrimitive(XmlPullParser* parser, const ResourceNameRef 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 @@ bool ResourceParser::parsePublic(XmlPullParser* parser, const StringPiece16& nam 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 @@ 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<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 @@ std::unique_ptr<Attribute> 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<Attribute> 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<Attribute> ResourceParser::parseAttrImpl(XmlPullParser* parser, } 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 @@ std::unique_ptr<Attribute> ResourceParser::parseAttrImpl(XmlPullParser* parser, } 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 @@ Maybe<Attribute::Symbol> 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<ResourceName> parseXmlAttributeName(StringPiece16 str) { @@ -604,7 +678,7 @@ static Maybe<ResourceName> 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<Style> style = util::make_unique<Style>(); @@ -660,12 +734,12 @@ bool ResourceParser::parseStyle(XmlPullParser* parser, const ResourceNameRef& re } 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 @@ bool ResourceParser::parseStyle(XmlPullParser* parser, const ResourceNameRef& re 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 @@ bool ResourceParser::parseArray(XmlPullParser* parser, const ResourceNameRef& re 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 @@ bool ResourceParser::parsePlural(XmlPullParser* parser, const ResourceNameRef& r 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 @@ bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser, 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 @@ bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser, if (error) { return false; } - return mTable->addResource(resourceName, mConfig, source, std::move(styleable), mDiag); + + outResource->value = std::move(styleable); + return true; } } // namespace aapt diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h index 514e55852407..5ccd47f2b6a7 100644 --- a/tools/aapt2/ResourceParser.h +++ b/tools/aapt2/ResourceParser.h @@ -31,13 +31,24 @@ namespace aapt { +struct ParsedResource; + +struct ResourceParserOptions { + /** + * Optional product name by which to filter resources. + * This is like a preprocessor definition in that we strip out resources + * that don't match before we compile them. + */ + Maybe<std::u16string> product; +}; + /* * Parses an XML file for resources and adds them to a ResourceTable. */ class ResourceParser { public: ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source, - const ConfigDescription& config); + const ConfigDescription& config, const ResourceParserOptions& options = {}); ResourceParser(const ResourceParser&) = delete; // No copy. @@ -62,25 +73,24 @@ private: std::unique_ptr<Item> parseXml(XmlPullParser* parser, uint32_t typeMask, bool allowRawValue); bool parseResources(XmlPullParser* parser); - bool parseString(XmlPullParser* parser, const ResourceNameRef& resourceName); - bool parseColor(XmlPullParser* parser, const ResourceNameRef& resourceName); - bool parsePrimitive(XmlPullParser* parser, const ResourceNameRef& resourceName); - bool parsePublic(XmlPullParser* parser, const StringPiece16& name); - bool parseAttr(XmlPullParser* parser, const ResourceNameRef& resourceName); - std::unique_ptr<Attribute> parseAttrImpl(XmlPullParser* parser, - ResourceName* resourceName, - bool weak); + bool parseString(XmlPullParser* parser, ParsedResource* outResource); + bool parseColor(XmlPullParser* parser, ParsedResource* outResource); + bool parsePrimitive(XmlPullParser* parser, ParsedResource* outResource); + bool parsePublic(XmlPullParser* parser, ParsedResource* outResource); + bool parseAttr(XmlPullParser* parser, ParsedResource* outResource); + bool parseAttrImpl(XmlPullParser* parser, ParsedResource* outResource, bool weak); Maybe<Attribute::Symbol> parseEnumOrFlagItem(XmlPullParser* parser, const StringPiece16& tag); - bool parseStyle(XmlPullParser* parser, const ResourceNameRef& resourceName); + bool parseStyle(XmlPullParser* parser, ParsedResource* outResource); bool parseStyleItem(XmlPullParser* parser, Style* style); - bool parseDeclareStyleable(XmlPullParser* parser, const ResourceNameRef& resourceName); - bool parseArray(XmlPullParser* parser, const ResourceNameRef& resourceName, uint32_t typeMask); - bool parsePlural(XmlPullParser* parser, const ResourceNameRef& resourceName); + bool parseDeclareStyleable(XmlPullParser* parser, ParsedResource* outResource); + bool parseArray(XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask); + bool parsePlural(XmlPullParser* parser, ParsedResource* outResource); IDiagnostics* mDiag; ResourceTable* mTable; Source mSource; ConfigDescription mConfig; + ResourceParserOptions mOptions; }; } // namespace aapt diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp index cb98afd18c5a..a7e9d390ef4a 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -48,10 +48,12 @@ struct ResourceParserTest : public ::testing::Test { mContext = test::ContextBuilder().build(); } - ::testing::AssertionResult testParse(const StringPiece& str) { + ::testing::AssertionResult testParse(const StringPiece& str, + Maybe<std::u16string> product = {}) { std::stringstream input(kXmlPreamble); input << "<resources>\n" << str << "\n</resources>" << std::endl; - ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, {}); + ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, {}, + ResourceParserOptions{ product }); XmlPullParser xmlParser(input); if (parser.parse(&xmlParser)) { return ::testing::AssertionSuccess(); @@ -314,6 +316,9 @@ TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) { std::string input = "<declare-styleable name=\"foo\">\n" " <attr name=\"bar\" />\n" " <attr name=\"bat\" format=\"string|reference\"/>\n" + " <attr name=\"baz\">\n" + " <enum name=\"foo\" value=\"1\"/>\n" + " </attr>\n" "</declare-styleable>"; ASSERT_TRUE(testParse(input)); @@ -325,9 +330,16 @@ TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) { ASSERT_NE(attr, nullptr); EXPECT_TRUE(attr->isWeak()); + attr = test::getValue<Attribute>(&mTable, u"@attr/baz"); + ASSERT_NE(attr, nullptr); + EXPECT_TRUE(attr->isWeak()); + EXPECT_EQ(1u, attr->symbols.size()); + + EXPECT_NE(nullptr, test::getValue<Id>(&mTable, u"@id/foo")); + Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo"); ASSERT_NE(styleable, nullptr); - ASSERT_EQ(2u, styleable->entries.size()); + ASSERT_EQ(3u, styleable->entries.size()); EXPECT_EQ(test::parseNameOrDie(u"@attr/bar"), styleable->entries[0].name.value()); EXPECT_EQ(test::parseNameOrDie(u"@attr/bat"), styleable->entries[1].name.value()); @@ -350,6 +362,14 @@ TEST_F(ResourceParserTest, ParseArray) { EXPECT_NE(nullptr, valueCast<BinaryPrimitive>(array->items[2].get())); } +TEST_F(ResourceParserTest, ParseStringArray) { + std::string input = "<string-array name=\"foo\">\n" + " <item>\"Werk\"</item>\n" + "</string-array>\n"; + ASSERT_TRUE(testParse(input)); + EXPECT_NE(nullptr, test::getValue<Array>(&mTable, u"@array/foo")); +} + TEST_F(ResourceParserTest, ParsePlural) { std::string input = "<plurals name=\"foo\">\n" " <item quantity=\"other\">apples</item>\n" @@ -385,4 +405,24 @@ TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) { ASSERT_NE(nullptr, id); } +TEST_F(ResourceParserTest, FilterProductsThatDontMatch) { + std::string input = "<string name=\"foo\" product=\"phone\">hi</string>\n" + "<string name=\"foo\" product=\"no-sdcard\">ho</string>\n" + "<string name=\"bar\" product=\"\">wee</string>\n" + "<string name=\"baz\">woo</string>\n"; + ASSERT_TRUE(testParse(input, std::u16string(u"no-sdcard"))); + + String* fooStr = test::getValue<String>(&mTable, u"@string/foo"); + ASSERT_NE(nullptr, fooStr); + EXPECT_EQ(StringPiece16(u"ho"), *fooStr->value); + + EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bar")); + EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/baz")); +} + +TEST_F(ResourceParserTest, FailWhenProductFilterStripsOutAllVersionsOfResource) { + std::string input = "<string name=\"foo\" product=\"tablet\">hello</string>\n"; + ASSERT_FALSE(testParse(input, std::u16string(u"phone"))); +} + } // namespace aapt diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index a1e7d36d91d9..e32fb5ee22ea 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -62,17 +62,17 @@ ResourceTablePackage* ResourceTable::findPackageById(uint8_t id) { return nullptr; } -ResourceTablePackage* ResourceTable::createPackage(const StringPiece16& name, uint8_t id) { +ResourceTablePackage* ResourceTable::createPackage(const StringPiece16& name, Maybe<uint8_t> id) { ResourceTablePackage* package = findOrCreatePackage(name); - if (!package->id) { + if (id && !package->id) { package->id = id; return package; } - if (package->id.value() == id) { - return package; + if (id && package->id && package->id.value() != id.value()) { + return nullptr; } - return nullptr; + return package; } ResourceTablePackage* ResourceTable::findOrCreatePackage(const StringPiece16& name) { diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index a00c14276aa4..60fed2f0e26b 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -176,9 +176,9 @@ public: bool markPublicAllowMangled(const ResourceNameRef& name, const ResourceId resId, const Source& source, IDiagnostics* diag); struct SearchResult { - ResourceTablePackage* package; - ResourceTableType* type; - ResourceEntry* entry; + ResourceTablePackage* package; + ResourceTableType* type; + ResourceEntry* entry; }; Maybe<SearchResult> findResource(const ResourceNameRef& name); @@ -208,7 +208,7 @@ public: ResourceTablePackage* findPackageById(uint8_t id); - ResourceTablePackage* createPackage(const StringPiece16& name, uint8_t id); + ResourceTablePackage* createPackage(const StringPiece16& name, Maybe<uint8_t> id = {}); private: ResourceTablePackage* findOrCreatePackage(const StringPiece16& name); diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp index 498bc9c162d3..0bc5dce13cbd 100644 --- a/tools/aapt2/compile/Compile.cpp +++ b/tools/aapt2/compile/Compile.cpp @@ -102,6 +102,7 @@ static Maybe<ResourcePathData> extractResourcePathData(const std::string& path, struct CompileOptions { std::string outputPath; + Maybe<std::u16string> product; bool verbose = false; }; @@ -121,8 +122,6 @@ static std::string buildIntermediateFilename(const std::string outDir, static bool compileTable(IAaptContext* context, const CompileOptions& options, const ResourcePathData& pathData, const std::string& outputPath) { ResourceTable table; - table.createPackage(u"", 0x7f); - { std::ifstream fin(pathData.source.path, std::ifstream::binary); if (!fin) { @@ -134,7 +133,7 @@ static bool compileTable(IAaptContext* context, const CompileOptions& options, // Parse the values file from XML. XmlPullParser xmlParser(fin); ResourceParser resParser(context->getDiagnostics(), &table, pathData.source, - pathData.config); + pathData.config, ResourceParserOptions{ options.product }); if (!resParser.parse(&xmlParser)) { return false; } @@ -142,6 +141,12 @@ static bool compileTable(IAaptContext* context, const CompileOptions& options, fin.close(); } + ResourceTablePackage* pkg = table.createPackage(context->getCompilationPackage()); + if (!pkg->id) { + // If no package ID was set while parsing (public identifiers), auto assign an ID. + pkg->id = context->getPackageId(); + } + // Assign IDs to prepare the table for flattening. IdAssigner idAssigner; if (!idAssigner.consume(context, &table)) { @@ -325,7 +330,7 @@ public: } uint8_t getPackageId() override { - return 0x7f; + return 0x0; } ISymbolTable* getExternalSymbols() override { @@ -340,13 +345,19 @@ public: int compile(const std::vector<StringPiece>& args) { CompileOptions options; + Maybe<std::string> product; Flags flags = Flags() .requiredFlag("-o", "Output path", &options.outputPath) + .optionalFlag("--product", "Product type to compile", &product) .optionalSwitch("-v", "Enables verbose logging", &options.verbose); if (!flags.parse("aapt2 compile", args, &std::cerr)) { return 1; } + if (product) { + options.product = util::utf8ToUtf16(product.value()); + } + CompileContext context; std::vector<ResourcePathData> inputData; diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp index 427ab18567bd..75cbd5d7baa1 100644 --- a/tools/aapt2/flatten/TableFlattener.cpp +++ b/tools/aapt2/flatten/TableFlattener.cpp @@ -23,9 +23,9 @@ #include "flatten/TableFlattener.h" #include "util/BigBuffer.h" +#include <base/macros.h> #include <type_traits> #include <numeric> -#include <utils/misc.h> using namespace android; @@ -59,20 +59,32 @@ struct FlatEntry { uint32_t sourceLine; }; -struct SymbolWriter { +class SymbolWriter { +public: struct Entry { StringPool::Ref name; size_t offset; }; - StringPool pool; std::vector<Entry> symbols; + explicit SymbolWriter(StringPool* pool) : mPool(pool) { + } + void addSymbol(const ResourceNameRef& name, size_t offset) { - symbols.push_back(Entry{ pool.makeRef(name.package.toString() + u":" + - toString(name.type).toString() + u"/" + - name.entry.toString()), offset }); + symbols.push_back(Entry{ mPool->makeRef(name.package.toString() + u":" + + toString(name.type).toString() + u"/" + + name.entry.toString()), offset }); + } + + void shiftAllOffsets(size_t offset) { + for (Entry& entry : symbols) { + entry.offset += offset; + } } + +private: + StringPool* mPool; }; struct MapFlattenVisitor : public RawValueVisitor { @@ -226,15 +238,59 @@ struct MapFlattenVisitor : public RawValueVisitor { } }; -struct PackageFlattener { +class PackageFlattener { +public: + PackageFlattener(IDiagnostics* diag, TableFlattenerOptions options, + ResourceTablePackage* package, SymbolWriter* symbolWriter, + StringPool* sourcePool) : + mDiag(diag), mOptions(options), mPackage(package), mSymbols(symbolWriter), + mSourcePool(sourcePool) { + } + + bool flattenPackage(BigBuffer* buffer) { + ChunkWriter pkgWriter(buffer); + ResTable_package* pkgHeader = pkgWriter.startChunk<ResTable_package>( + RES_TABLE_PACKAGE_TYPE); + pkgHeader->id = util::hostToDevice32(mPackage->id.value()); + + if (mPackage->name.size() >= arraysize(pkgHeader->name)) { + mDiag->error(DiagMessage() << + "package name '" << mPackage->name << "' is too long"); + return false; + } + + // Copy the package name in device endianness. + strcpy16_htod(pkgHeader->name, arraysize(pkgHeader->name), mPackage->name); + + // Serialize the types. We do this now so that our type and key strings + // are populated. We write those first. + BigBuffer typeBuffer(1024); + flattenTypes(&typeBuffer); + + pkgHeader->typeStrings = util::hostToDevice32(pkgWriter.size()); + StringPool::flattenUtf16(pkgWriter.getBuffer(), mTypePool); + + pkgHeader->keyStrings = util::hostToDevice32(pkgWriter.size()); + StringPool::flattenUtf16(pkgWriter.getBuffer(), mKeyPool); + + // Add the ResTable_package header/type/key strings to the offset. + mSymbols->shiftAllOffsets(pkgWriter.size()); + + // Append the types. + buffer->appendBuffer(std::move(typeBuffer)); + + pkgWriter.finish(); + return true; + } + +private: IDiagnostics* mDiag; TableFlattenerOptions mOptions; - ResourceTable* mTable; ResourceTablePackage* mPackage; - SymbolWriter mSymbols; StringPool mTypePool; StringPool mKeyPool; - StringPool mSourcePool; + SymbolWriter* mSymbols; + StringPool* mSourcePool; template <typename T> T* writeEntry(FlatEntry* entry, BigBuffer* buffer) { @@ -278,8 +334,8 @@ struct PackageFlattener { if (Reference* ref = valueCast<Reference>(entry->value)) { if (!ref->id) { assert(ref->name && "reference must have at least a name"); - mSymbols.addSymbol(ref->name.value(), - buffer->size() + offsetof(Res_value, data)); + mSymbols->addSymbol(ref->name.value(), + buffer->size() + offsetof(Res_value, data)); } } Res_value* outValue = buffer->nextBlock<Res_value>(); @@ -289,12 +345,12 @@ struct PackageFlattener { } else { const size_t beforeEntry = buffer->size(); ResTable_entry_ext* outEntry = writeEntry<ResTable_entry_ext>(entry, buffer); - MapFlattenVisitor visitor(&mSymbols, entry, buffer); + MapFlattenVisitor visitor(mSymbols, entry, buffer); entry->value->accept(&visitor); outEntry->count = util::hostToDevice32(visitor.mEntryCount); if (visitor.mParentName) { - mSymbols.addSymbol(visitor.mParentName.value(), - beforeEntry + offsetof(ResTable_entry_ext, parent)); + mSymbols->addSymbol(visitor.mParentName.value(), + beforeEntry + offsetof(ResTable_entry_ext, parent)); } else if (visitor.mParentIdent) { outEntry->parent.ident = util::hostToDevice32(visitor.mParentIdent.value()); } @@ -430,7 +486,7 @@ struct PackageFlattener { publicEntry->entryId = util::hostToDevice32(entry->id.value()); publicEntry->key.index = util::hostToDevice32(mKeyPool.makeRef( entry->name).getIndex()); - publicEntry->source.index = util::hostToDevice32(mSourcePool.makeRef( + publicEntry->source.index = util::hostToDevice32(mSourcePool->makeRef( util::utf8ToUtf16(entry->publicStatus.source.path)).getIndex()); if (entry->publicStatus.source.line) { publicEntry->sourceLine = util::hostToDevice32( @@ -487,7 +543,7 @@ struct PackageFlattener { for (auto& configValue : entry->values) { configToEntryListMap[configValue.config].push_back(FlatEntry{ entry, configValue.value.get(), (uint32_t) keyIndex, - (uint32_t)(mSourcePool.makeRef(util::utf8ToUtf16( + (uint32_t)(mSourcePool->makeRef(util::utf8ToUtf16( configValue.source.path)).getIndex()), (uint32_t)(configValue.source.line ? configValue.source.line.value() : 0) @@ -504,141 +560,113 @@ struct PackageFlattener { } return true; } +}; - bool flattenPackage(BigBuffer* buffer) { - // We must do this before writing the resources, since the string pool IDs may change. - mTable->stringPool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool { - int diff = a.context.priority - b.context.priority; - if (diff < 0) return true; - if (diff > 0) return false; - diff = a.context.config.compare(b.context.config); - if (diff < 0) return true; - if (diff > 0) return false; - return a.value < b.value; - }); - mTable->stringPool.prune(); - - const size_t beginningIndex = buffer->size(); +} // namespace - BigBuffer typeBuffer(1024); - if (!flattenTypes(&typeBuffer)) { +bool TableFlattener::consume(IAaptContext* context, ResourceTable* table) { + // We must do this before writing the resources, since the string pool IDs may change. + table->stringPool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool { + int diff = a.context.priority - b.context.priority; + if (diff < 0) return true; + if (diff > 0) return false; + diff = a.context.config.compare(b.context.config); + if (diff < 0) return true; + if (diff > 0) return false; + return a.value < b.value; + }); + table->stringPool.prune(); + + // Write the ResTable header. + ChunkWriter tableWriter(mBuffer); + ResTable_header* tableHeader = tableWriter.startChunk<ResTable_header>(RES_TABLE_TYPE); + tableHeader->packageCount = util::hostToDevice32(table->packages.size()); + + // Flatten the values string pool. + StringPool::flattenUtf8(tableWriter.getBuffer(), table->stringPool); + + // If we have a reference to a symbol that doesn't exist, we don't know its resource ID. + // We encode the name of the symbol along with the offset of where to include the resource ID + // once it is found. + StringPool symbolPool; + std::vector<SymbolWriter::Entry> symbolOffsets; + + // String pool holding the source paths of each value. + StringPool sourcePool; + + BigBuffer packageBuffer(1024); + + // Flatten each package. + for (auto& package : table->packages) { + const size_t beforePackageSize = packageBuffer.size(); + + // All packages will share a single global symbol pool. + SymbolWriter packageSymbolWriter(&symbolPool); + + PackageFlattener flattener(context->getDiagnostics(), mOptions, package.get(), + &packageSymbolWriter, &sourcePool); + if (!flattener.flattenPackage(&packageBuffer)) { return false; } - ChunkWriter tableWriter(buffer); - ResTable_header* tableHeader = tableWriter.startChunk<ResTable_header>(RES_TABLE_TYPE); - tableHeader->packageCount = util::hostToDevice32(1); + // The symbols are offset only from their own Package start. Offset them from the + // start of the packageBuffer. + packageSymbolWriter.shiftAllOffsets(beforePackageSize); + + // Extract all the symbols to offset + symbolOffsets.insert(symbolOffsets.end(), + std::make_move_iterator(packageSymbolWriter.symbols.begin()), + std::make_move_iterator(packageSymbolWriter.symbols.end())); + } - SymbolTable_entry* symbolEntryData = nullptr; - if (mOptions.useExtendedChunks && !mSymbols.symbols.empty()) { + SymbolTable_entry* symbolEntryData = nullptr; + if (mOptions.useExtendedChunks) { + if (!symbolOffsets.empty()) { // Sort the offsets so we can scan them linearly. - std::sort(mSymbols.symbols.begin(), mSymbols.symbols.end(), + std::sort(symbolOffsets.begin(), symbolOffsets.end(), [](const SymbolWriter::Entry& a, const SymbolWriter::Entry& b) -> bool { return a.offset < b.offset; }); + // Write the Symbol header. ChunkWriter symbolWriter(tableWriter.getBuffer()); SymbolTable_header* symbolHeader = symbolWriter.startChunk<SymbolTable_header>( RES_TABLE_SYMBOL_TABLE_TYPE); - symbolHeader->count = util::hostToDevice32(mSymbols.symbols.size()); + symbolHeader->count = util::hostToDevice32(symbolOffsets.size()); - symbolEntryData = symbolWriter.nextBlock<SymbolTable_entry>(mSymbols.symbols.size()); - StringPool::flattenUtf8(symbolWriter.getBuffer(), mSymbols.pool); + symbolEntryData = symbolWriter.nextBlock<SymbolTable_entry>(symbolOffsets.size()); + StringPool::flattenUtf8(symbolWriter.getBuffer(), symbolPool); symbolWriter.finish(); } - if (mOptions.useExtendedChunks && mSourcePool.size() > 0) { + if (sourcePool.size() > 0) { // Write out source pool. ChunkWriter srcWriter(tableWriter.getBuffer()); srcWriter.startChunk<ResChunk_header>(RES_TABLE_SOURCE_POOL_TYPE); - StringPool::flattenUtf8(srcWriter.getBuffer(), mSourcePool); + StringPool::flattenUtf8(srcWriter.getBuffer(), sourcePool); srcWriter.finish(); } - - StringPool::flattenUtf8(tableWriter.getBuffer(), mTable->stringPool); - - ChunkWriter pkgWriter(tableWriter.getBuffer()); - ResTable_package* pkgHeader = pkgWriter.startChunk<ResTable_package>( - RES_TABLE_PACKAGE_TYPE); - pkgHeader->id = util::hostToDevice32(mPackage->id.value()); - - if (mPackage->name.size() >= NELEM(pkgHeader->name)) { - mDiag->error(DiagMessage() << - "package name '" << mPackage->name << "' is too long"); - return false; - } - - strcpy16_htod(pkgHeader->name, NELEM(pkgHeader->name), mPackage->name); - - pkgHeader->typeStrings = util::hostToDevice32(pkgWriter.size()); - StringPool::flattenUtf16(pkgWriter.getBuffer(), mTypePool); - - pkgHeader->keyStrings = util::hostToDevice32(pkgWriter.size()); - StringPool::flattenUtf16(pkgWriter.getBuffer(), mKeyPool); - - // Actually write out the symbol entries if we have symbols. - if (symbolEntryData) { - for (auto& entry : mSymbols.symbols) { - symbolEntryData->stringIndex = util::hostToDevice32(entry.name.getIndex()); - - // The symbols were all calculated with the typeBuffer offset. We need to - // add the beginning of the output buffer. - symbolEntryData->offset = util::hostToDevice32( - (pkgWriter.getBuffer()->size() - beginningIndex) + entry.offset); - - symbolEntryData++; - } - } - - // Write out the types and entries. - pkgWriter.getBuffer()->appendBuffer(std::move(typeBuffer)); - - pkgWriter.finish(); - tableWriter.finish(); - return true; } -}; - -} // namespace -bool TableFlattener::consume(IAaptContext* context, ResourceTable* table) { - for (auto& package : table->packages) { - // Only support flattening one package. Since the StringPool is shared between packages - // in ResourceTable, we must fail if other packages are present, since their strings - // will be included in the final ResourceTable. - if (context->getCompilationPackage() != package->name) { - context->getDiagnostics()->error(DiagMessage() - << "resources for package '" << package->name - << "' can't be flattened when compiling package '" - << context->getCompilationPackage() << "'"); - return false; - } + const size_t beforePackagesSize = tableWriter.size(); - if (!package->id || package->id.value() != context->getPackageId()) { - context->getDiagnostics()->error(DiagMessage() - << "package '" << package->name << "' must have " - << "package id " - << std::hex << context->getPackageId() << std::dec); - return false; - } + // Finally merge all the packages into the main buffer. + tableWriter.getBuffer()->appendBuffer(std::move(packageBuffer)); - PackageFlattener flattener = { - context->getDiagnostics(), - mOptions, - table, - package.get() - }; + // Update the offsets to their final values. + if (symbolEntryData) { + for (SymbolWriter::Entry& entry : symbolOffsets) { + symbolEntryData->stringIndex = util::hostToDevice32(entry.name.getIndex()); - if (!flattener.flattenPackage(mBuffer)) { - return false; + // The symbols were all calculated with the packageBuffer offset. We need to + // add the beginning of the output buffer. + symbolEntryData->offset = util::hostToDevice32(entry.offset + beforePackagesSize); + symbolEntryData++; } - return true; } - context->getDiagnostics()->error(DiagMessage() - << "compilation package '" << context->getCompilationPackage() - << "' not found"); - return false; + tableWriter.finish(); + return true; } } // namespace aapt diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index 2b4c4d20b791..fa321a01d303 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -52,6 +52,7 @@ struct LinkOptions { bool staticLib = false; bool verbose = false; bool outputToDirectory = false; + Maybe<std::string> privateSymbols; }; struct LinkContext : public IAaptContext { @@ -400,7 +401,13 @@ struct LinkCommand { mContext.mNameMangler = util::make_unique<NameMangler>( NameManglerPolicy{ mContext.mCompilationPackage }); - mContext.mPackageId = 0x7f; + + if (mContext.mCompilationPackage == u"android") { + mContext.mPackageId = 0x01; + } else { + mContext.mPackageId = 0x7f; + } + mContext.mSymbols = createSymbolTableFromIncludePaths(); if (!mContext.mSymbols) { return 1; @@ -689,6 +696,9 @@ int link(const std::vector<StringPiece>& args) { "by -o", &options.outputToDirectory) .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib) + .optionalFlag("--private-symbols", "Package name to use when generating R.java for " + "private symbols. If not specified, public and private symbols will " + "use the application's package name", &options.privateSymbols) .optionalSwitch("-v", "Enables verbose logging", &options.verbose); if (!flags.parse("aapt2 link", args, &std::cerr)) { diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp index d5fd1fc49c85..0d63b976936e 100644 --- a/tools/aapt2/link/TableMerger.cpp +++ b/tools/aapt2/link/TableMerger.cpp @@ -39,7 +39,7 @@ bool TableMerger::merge(const Source& src, ResourceTable* table) { bool error = false; for (auto& package : table->packages) { // Warn of packages with an unrelated ID. - if (package->id && package->id.value() != desiredPackageId) { + if (package->id && package->id.value() != 0x0 && package->id.value() != desiredPackageId) { mContext->getDiagnostics()->warn(DiagMessage(src) << "ignoring package " << package->name); continue; diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h index b41c5683c77a..6fdaebba6897 100644 --- a/tools/aapt2/test/Common.h +++ b/tools/aapt2/test/Common.h @@ -18,6 +18,7 @@ #define AAPT_TEST_COMMON_H #include "ConfigDescription.h" +#include "Debug.h" #include "ResourceTable.h" #include "ResourceUtils.h" #include "ValueVisitor.h" diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp index 992a45cb34f4..ac91865e9e39 100644 --- a/tools/aapt2/unflatten/BinaryResourceParser.cpp +++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp @@ -27,7 +27,7 @@ #include <androidfw/ResourceTypes.h> #include <androidfw/TypeWrappers.h> -#include <utils/misc.h> +#include <base/macros.h> #include <map> #include <string> @@ -289,7 +289,7 @@ bool BinaryResourceParser::parsePackage(const ResChunk_header* chunk) { } // Extract the package name. - size_t len = strnlen16((const char16_t*) packageHeader->name, NELEM(packageHeader->name)); + size_t len = strnlen16((const char16_t*) packageHeader->name, arraysize(packageHeader->name)); std::u16string packageName; packageName.resize(len); for (size_t i = 0; i < len; i++) { @@ -416,13 +416,11 @@ bool BinaryResourceParser::parsePublic(const ResourceTablePackage* package, return false; } - const ResourceId resId = { - package->id.value(), header->typeId, util::deviceToHost16(entry->entryId) }; + const ResourceId resId(package->id.value(), header->typeId, + util::deviceToHost16(entry->entryId)); - const ResourceName name = { - package->name, - *parsedType, - util::getString(mKeyPool, entry->key.index).toString() }; + const ResourceName name(package->name, *parsedType, + util::getString(mKeyPool, entry->key.index).toString()); Source source; if (mSourcePool.getError() == NO_ERROR) { @@ -516,13 +514,11 @@ bool BinaryResourceParser::parseType(const ResourceTablePackage* package, continue; } - const ResourceName name = { - package->name, - *parsedType, - util::getString(mKeyPool, util::deviceToHost32(entry->key.index)).toString() }; + const ResourceName name(package->name, *parsedType, + util::getString(mKeyPool, + util::deviceToHost32(entry->key.index)).toString()); - const ResourceId resId = - { package->id.value(), type->id, static_cast<uint16_t>(it.index()) }; + const ResourceId resId(package->id.value(), type->id, static_cast<uint16_t>(it.index())); std::unique_ptr<Value> resourceValue; const ResTable_entry_source* sourceBlock = nullptr; @@ -598,7 +594,9 @@ std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& na StringPiece16 str = util::getString(mValuePool, data); const ResStringPool_span* spans = mValuePool.styleAt(data); - if (spans != nullptr) { + + // Check if the string has a valid style associated with it. + if (spans != nullptr && spans->name.index != ResStringPool_span::END) { StyleString styleStr = { str.toString() }; while (spans->name.index != ResStringPool_span::END) { styleStr.spans.push_back(Span{ @@ -662,8 +660,12 @@ std::unique_ptr<Value> BinaryResourceParser::parseMapEntry(const ResourceNameRef switch (name.type) { case ResourceType::kStyle: return parseStyle(name, config, map); + case ResourceType::kAttrPrivate: + // fallthrough case ResourceType::kAttr: return parseAttr(name, config, map); + case ResourceType::kIntegerArray: + // fallthrough case ResourceType::kArray: return parseArray(name, config, map); case ResourceType::kStyleable: @@ -671,6 +673,7 @@ std::unique_ptr<Value> BinaryResourceParser::parseMapEntry(const ResourceNameRef case ResourceType::kPlurals: return parsePlural(name, config, map); default: + assert(false && "unknown map type"); break; } return {}; |