summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Adam Lesinski <adamlesinski@google.com> 2015-10-20 19:49:53 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2015-10-20 19:49:53 +0000
commit91bbbcf9a990d0dee20741143e0687d3b3d647f5 (patch)
treec9a345b491fb9f494ecf0ab927c23dad7a5499a5
parenta66682bceb5606a9f17b03b43402ddfaf8c77956 (diff)
parent9ba47d813075fcb05c5e1532c137c93b394631cb (diff)
Merge "Filter products during compile phase"
-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 {};