summaryrefslogtreecommitdiff
path: root/tools/aapt2
diff options
context:
space:
mode:
author Adam Lesinski <adamlesinski@google.com> 2015-10-13 11:37:10 -0700
committer Adam Lesinski <adamlesinski@google.com> 2015-10-16 15:50:58 -0700
commit9ba47d813075fcb05c5e1532c137c93b394631cb (patch)
tree1eb9f7adc315ab8ad640249f8ac1888951520f3f /tools/aapt2
parent072c5bdff77e354bdf333c0c1d460cdd1c2e76ae (diff)
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
Diffstat (limited to 'tools/aapt2')
-rw-r--r--tools/aapt2/Diagnostics.h13
-rw-r--r--tools/aapt2/Resource.h7
-rw-r--r--tools/aapt2/ResourceParser.cpp306
-rw-r--r--tools/aapt2/ResourceParser.h36
-rw-r--r--tools/aapt2/ResourceParser_test.cpp46
-rw-r--r--tools/aapt2/ResourceTable.cpp10
-rw-r--r--tools/aapt2/ResourceTable.h8
-rw-r--r--tools/aapt2/compile/Compile.cpp19
-rw-r--r--tools/aapt2/flatten/TableFlattener.cpp270
-rw-r--r--tools/aapt2/link/Link.cpp12
-rw-r--r--tools/aapt2/link/TableMerger.cpp2
-rw-r--r--tools/aapt2/test/Common.h1
-rw-r--r--tools/aapt2/unflatten/BinaryResourceParser.cpp33
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 {};