summaryrefslogtreecommitdiffstats
path: root/tools/aapt2
diff options
context:
space:
mode:
authorAdam Lesinski <adamlesinski@google.com>2015-05-04 17:40:56 -0700
committerAdam Lesinski <adamlesinski@google.com>2015-05-12 20:41:31 -0700
commit330edcdf1316ed599fe0eb16a64330821fd92f18 (patch)
tree8a9bddd59f53efc836b787565ba206a1d78383e3 /tools/aapt2
parenta4492b418d23e3bcdb077aab5d48eb24e932fc13 (diff)
downloadframeworks_base-330edcdf1316ed599fe0eb16a64330821fd92f18.zip
frameworks_base-330edcdf1316ed599fe0eb16a64330821fd92f18.tar.gz
frameworks_base-330edcdf1316ed599fe0eb16a64330821fd92f18.tar.bz2
AAPT2: Support static lib referencing static lib
When a static library A references static library B, and app C references both A and B, we get the following symbol merging, symbols from library B get imported twice. We must only check that symbol references to library B are valid when building library A. We should only merge all the symbols when building final app C. Change-Id: I23cba33b0901dcbb5328d9c9dfaa6a979c073c36
Diffstat (limited to 'tools/aapt2')
-rw-r--r--tools/aapt2/Android.mk1
-rw-r--r--tools/aapt2/BinaryResourceParser.cpp43
-rw-r--r--tools/aapt2/Debug.cpp181
-rw-r--r--tools/aapt2/Debug.h33
-rw-r--r--tools/aapt2/JavaClassGenerator.cpp13
-rw-r--r--tools/aapt2/JavaClassGenerator_test.cpp7
-rw-r--r--tools/aapt2/Linker.cpp149
-rw-r--r--tools/aapt2/Linker.h43
-rw-r--r--tools/aapt2/Linker_test.cpp3
-rw-r--r--tools/aapt2/Main.cpp344
-rw-r--r--tools/aapt2/MockResolver.h2
-rw-r--r--tools/aapt2/ResourceTable.cpp45
-rw-r--r--tools/aapt2/ResourceTable.h16
-rw-r--r--tools/aapt2/ResourceTableResolver.cpp158
-rw-r--r--tools/aapt2/ResourceTableResolver.h14
-rw-r--r--tools/aapt2/ResourceValues.cpp28
-rw-r--r--tools/aapt2/ResourceValues.h5
-rw-r--r--tools/aapt2/TableFlattener.cpp24
-rw-r--r--tools/aapt2/XmlFlattener.cpp93
-rw-r--r--tools/aapt2/XmlFlattener.h6
20 files changed, 751 insertions, 457 deletions
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 5ef4311..23d679d 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -30,6 +30,7 @@ sources := \
BinaryXmlPullParser.cpp \
BindingXmlPullParser.cpp \
ConfigDescription.cpp \
+ Debug.cpp \
Files.cpp \
Flag.cpp \
JavaClassGenerator.cpp \
diff --git a/tools/aapt2/BinaryResourceParser.cpp b/tools/aapt2/BinaryResourceParser.cpp
index d16f63b..bbd21fb 100644
--- a/tools/aapt2/BinaryResourceParser.cpp
+++ b/tools/aapt2/BinaryResourceParser.cpp
@@ -475,10 +475,17 @@ bool BinaryResourceParser::parsePublic(const ResChunk_header* chunk) {
source.line = entry->sourceLine;
}
- if (!mTable->markPublic(name, resId, source)) {
+ if (!mTable->markPublicAllowMangled(name, resId, source)) {
return false;
}
+ // Add this resource name->id mapping to the index so
+ // that we can resolve all ID references to name references.
+ auto cacheIter = mIdIndex.find(resId);
+ if (cacheIter == mIdIndex.end()) {
+ mIdIndex.insert({ resId, name });
+ }
+
entry++;
}
return true;
@@ -611,12 +618,12 @@ bool BinaryResourceParser::parseType(const ResChunk_header* chunk) {
source.line = sourceBlock->line;
}
- if (!mTable->addResource(name, config, source, std::move(resourceValue))) {
+ if (!mTable->addResourceAllowMangled(name, config, source, std::move(resourceValue))) {
return false;
}
if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) {
- if (!mTable->markPublic(name, resId, mSource.line(0))) {
+ if (!mTable->markPublicAllowMangled(name, resId, mSource.line(0))) {
return false;
}
}
@@ -635,6 +642,10 @@ std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& na
const ConfigDescription& config,
const Res_value* value,
uint16_t flags) {
+ if (name.type == ResourceType::kId) {
+ return util::make_unique<Id>();
+ }
+
if (value->dataType == Res_value::TYPE_STRING) {
StringPiece16 str = util::getString(mValuePool, value->data);
@@ -697,13 +708,6 @@ std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& na
StringPool::Context{ 1, config }));
}
- if (name.type == ResourceType::kId ||
- (value->dataType == Res_value::TYPE_NULL &&
- value->data == Res_value::DATA_NULL_UNDEFINED &&
- (flags & ResTable_entry::FLAG_WEAK) != 0)) {
- return util::make_unique<Id>();
- }
-
// Treat this as a raw binary primitive.
return util::make_unique<BinaryPrimitive>(*value);
}
@@ -789,10 +793,21 @@ std::unique_ptr<Attribute> BinaryResourceParser::parseAttr(const ResourceNameRef
continue;
}
- attr->symbols.push_back(Attribute::Symbol{
- Reference(mapEntry.name.ident),
- mapEntry.value.data
- });
+ Attribute::Symbol symbol;
+ symbol.value = mapEntry.value.data;
+ if (mapEntry.name.ident == 0) {
+ // The map entry's key (id) is not set. This must be
+ // a symbol reference, so resolve it.
+ ResourceNameRef symbolName;
+ bool result = getSymbol(&mapEntry.name.ident, &symbolName);
+ assert(result);
+ symbol.symbol.name = symbolName.toResourceName();
+ } else {
+ // The map entry's key (id) is a regular reference.
+ symbol.symbol.id = mapEntry.name.ident;
+ }
+
+ attr->symbols.push_back(std::move(symbol));
}
}
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
new file mode 100644
index 0000000..a7b9bba
--- /dev/null
+++ b/tools/aapt2/Debug.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Debug.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "Util.h"
+
+#include <algorithm>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <set>
+#include <vector>
+
+namespace aapt {
+
+struct PrintVisitor : ConstValueVisitor {
+ void visit(const Attribute& attr, ValueVisitorArgs&) override {
+ std::cout << "(attr) type=";
+ attr.printMask(std::cout);
+ static constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM |
+ android::ResTable_map::TYPE_FLAGS;
+ if (attr.typeMask & kMask) {
+ for (const auto& symbol : attr.symbols) {
+ std::cout << "\n "
+ << symbol.symbol.name.entry << " (" << symbol.symbol.id << ") = "
+ << symbol.value;
+ }
+ }
+ }
+
+ void visit(const Style& style, ValueVisitorArgs&) override {
+ std::cout << "(style)";
+ if (style.parent.name.isValid() || style.parent.id.isValid()) {
+ std::cout << " parent=";
+ if (style.parent.name.isValid()) {
+ std::cout << style.parent.name << " ";
+ }
+
+ if (style.parent.id.isValid()) {
+ std::cout << style.parent.id;
+ }
+ }
+
+ for (const auto& entry : style.entries) {
+ std::cout << "\n ";
+ if (entry.key.name.isValid()) {
+ std::cout << entry.key.name.package << ":" << entry.key.name.entry;
+ }
+
+ if (entry.key.id.isValid()) {
+ std::cout << "(" << entry.key.id << ")";
+ }
+
+ std::cout << "=" << *entry.value;
+ }
+ }
+
+ void visit(const Array& array, ValueVisitorArgs&) override {
+ array.print(std::cout);
+ }
+
+ void visit(const Plural& plural, ValueVisitorArgs&) override {
+ plural.print(std::cout);
+ }
+
+ void visit(const Styleable& styleable, ValueVisitorArgs&) override {
+ styleable.print(std::cout);
+ }
+
+ void visitItem(const Item& item, ValueVisitorArgs& args) override {
+ item.print(std::cout);
+ }
+};
+
+void Debug::printTable(const std::shared_ptr<ResourceTable>& table) {
+ std::cout << "Package name=" << table->getPackage();
+ if (table->getPackageId() != ResourceTable::kUnsetPackageId) {
+ std::cout << " id=" << std::hex << table->getPackageId() << std::dec;
+ }
+ std::cout << std::endl;
+
+ for (const auto& type : *table) {
+ std::cout << " type " << type->type;
+ if (type->typeId != ResourceTableType::kUnsetTypeId) {
+ std::cout << " id=" << std::hex << type->typeId << std::dec;
+ }
+ std::cout << " entryCount=" << type->entries.size() << std::endl;
+
+ std::vector<const ResourceEntry*> sortedEntries;
+ for (const auto& entry : type->entries) {
+ auto iter = std::lower_bound(sortedEntries.begin(), sortedEntries.end(), entry.get(),
+ [](const ResourceEntry* a, const ResourceEntry* b) -> bool {
+ return a->entryId < b->entryId;
+ });
+ sortedEntries.insert(iter, entry.get());
+ }
+
+ for (const ResourceEntry* entry : sortedEntries) {
+ ResourceId id = { table->getPackageId(), type->typeId, entry->entryId };
+ ResourceName name = { table->getPackage(), type->type, entry->name };
+ std::cout << " spec resource " << id << " " << name;
+ if (entry->publicStatus.isPublic) {
+ std::cout << " PUBLIC";
+ }
+ std::cout << std::endl;
+
+ PrintVisitor visitor;
+ for (const auto& value : entry->values) {
+ std::cout << " (" << value.config << ") ";
+ value.value->accept(visitor, {});
+ std::cout << std::endl;
+ }
+ }
+ }
+}
+
+static size_t getNodeIndex(const std::vector<ResourceName>& names, const ResourceName& name) {
+ auto iter = std::lower_bound(names.begin(), names.end(), name);
+ assert(iter != names.end() && *iter == name);
+ return std::distance(names.begin(), iter);
+}
+
+void Debug::printStyleGraph(const std::shared_ptr<ResourceTable>& table) {
+ std::vector<ResourceName> names;
+ std::map<ResourceName, std::set<ResourceName>> graph;
+
+ for (const auto& type : *table) {
+ for (const auto& entry : type->entries) {
+ ResourceName name = { table->getPackage(), type->type, entry->name };
+ for (const auto& value : entry->values) {
+ visitFunc<Style>(*value.value, [&](const Style& style) {
+ if (style.parent.name.isValid()) {
+ names.push_back(style.parent.name);
+ names.push_back(name);
+ graph[style.parent.name].insert(name);
+ }
+ });
+ }
+ }
+ }
+
+ std::sort(names.begin(), names.end());
+ auto it1 = std::unique(names.begin(), names.end());
+ names.resize(std::distance(names.begin(), it1));
+
+ std::cout << "digraph styles {\n";
+
+ for (const auto& name : names) {
+ std::cout << " node_" << getNodeIndex(names, name)
+ << " [label=\"" << name.entry << "\"];\n";
+ }
+
+ for (const auto& entry : graph) {
+ const ResourceName& parent = entry.first;
+ size_t parentNodeIndex = getNodeIndex(names, parent);
+
+ for (const auto& childName : entry.second) {
+ std::cout << "node_" << getNodeIndex(names, childName) << " -> "
+ << "node_" << parentNodeIndex << ";\n";
+ }
+ }
+
+ std::cout << "}" << std::endl;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
new file mode 100644
index 0000000..84ae40f
--- /dev/null
+++ b/tools/aapt2/Debug.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_DEBUG_H
+#define AAPT_DEBUG_H
+
+#include "ResourceTable.h"
+
+#include <memory>
+
+namespace aapt {
+
+struct Debug {
+ static void printTable(const std::shared_ptr<ResourceTable>& table);
+ static void printStyleGraph(const std::shared_ptr<ResourceTable>& table);
+};
+
+} // namespace aapt
+
+#endif // AAPT_DEBUG_H
diff --git a/tools/aapt2/JavaClassGenerator.cpp b/tools/aapt2/JavaClassGenerator.cpp
index 2bb0e65..e2ffe79 100644
--- a/tools/aapt2/JavaClassGenerator.cpp
+++ b/tools/aapt2/JavaClassGenerator.cpp
@@ -81,23 +81,28 @@ static std::u16string transform(const StringPiece16& symbol) {
}
struct GenArgs : ValueVisitorArgs {
- GenArgs(std::ostream* o, std::u16string* e) : out(o), entryName(e) {
+ GenArgs(std::ostream* o, const std::u16string* p, std::u16string* e) :
+ out(o), package(p), entryName(e) {
}
std::ostream* out;
+ const std::u16string* package;
std::u16string* entryName;
};
void JavaClassGenerator::visit(const Styleable& styleable, ValueVisitorArgs& a) {
const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
std::ostream* out = static_cast<GenArgs&>(a).out;
+ const std::u16string* package = static_cast<GenArgs&>(a).package;
std::u16string* entryName = static_cast<GenArgs&>(a).entryName;
// This must be sorted by resource ID.
std::vector<std::pair<ResourceId, ResourceNameRef>> sortedAttributes;
sortedAttributes.reserve(styleable.entries.size());
for (const auto& attr : styleable.entries) {
- assert(attr.id.isValid() && "no ID set for Styleable entry");
+ // If we are not encoding final attributes, the styleable entry may have no ID
+ // if we are building a static library.
+ assert((!mOptions.useFinal || attr.id.isValid()) && "no ID set for Styleable entry");
assert(attr.name.isValid() && "no name set for Styleable entry");
sortedAttributes.emplace_back(attr.id, attr.name);
}
@@ -129,7 +134,7 @@ void JavaClassGenerator::visit(const Styleable& styleable, ValueVisitorArgs& a)
// We may reference IDs from other packages, so prefix the entry name with
// the package.
const ResourceNameRef& itemName = sortedAttributes[i].second;
- if (itemName.package != mTable->getPackage()) {
+ if (itemName.package != *package) {
*out << "_" << transform(itemName.package);
}
*out << "_" << transform(itemName.entry) << " = " << i << ";" << std::endl;
@@ -172,7 +177,7 @@ bool JavaClassGenerator::generateType(const std::u16string& package, size_t pack
if (type.type == ResourceType::kStyleable) {
assert(!entry->values.empty());
- entry->values.front().value->accept(*this, GenArgs{ &out, &unmangledName });
+ entry->values.front().value->accept(*this, GenArgs{ &out, &package, &unmangledName });
} else {
out << " " << "public static" << finalModifier
<< " int " << transform(unmangledName) << " = " << id << ";" << std::endl;
diff --git a/tools/aapt2/JavaClassGenerator_test.cpp b/tools/aapt2/JavaClassGenerator_test.cpp
index d4341b6..b385ff4 100644
--- a/tools/aapt2/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/JavaClassGenerator_test.cpp
@@ -95,8 +95,9 @@ TEST_F(JavaClassGeneratorTest, EmitPackageMangledSymbols) {
SourceLine{ "lib.xml", 33 }, util::make_unique<Id>()));
ASSERT_TRUE(mTable->merge(std::move(table)));
- Linker linker(mTable, std::make_shared<MockResolver>(mTable,
- std::map<ResourceName, ResourceId>()));
+ Linker linker(mTable,
+ std::make_shared<MockResolver>(mTable, std::map<ResourceName, ResourceId>()),
+ {});
ASSERT_TRUE(linker.linkAndValidate());
JavaClassGenerator generator(mTable, {});
@@ -130,7 +131,7 @@ TEST_F(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
{ ResourceName{ u"com.lib", ResourceType::kAttr, u"bar" },
ResourceId{ 0x02, 0x01, 0x0000 } }}));
- Linker linker(mTable, resolver);
+ Linker linker(mTable, resolver, {});
ASSERT_TRUE(linker.linkAndValidate());
JavaClassGenerator generator(mTable, {});
diff --git a/tools/aapt2/Linker.cpp b/tools/aapt2/Linker.cpp
index a8b7a14..f3f04a5 100644
--- a/tools/aapt2/Linker.cpp
+++ b/tools/aapt2/Linker.cpp
@@ -40,8 +40,9 @@ namespace aapt {
Linker::Args::Args(const ResourceNameRef& r, const SourceLine& s) : referrer(r), source(s) {
}
-Linker::Linker(std::shared_ptr<ResourceTable> table, std::shared_ptr<IResolver> resolver) :
- mTable(table), mResolver(resolver), mError(false) {
+Linker::Linker(const std::shared_ptr<ResourceTable>& table,
+ const std::shared_ptr<IResolver>& resolver, const Options& options) :
+ mResolver(resolver), mTable(table), mOptions(options), mError(false) {
}
bool Linker::linkAndValidate() {
@@ -49,7 +50,7 @@ bool Linker::linkAndValidate() {
std::array<std::set<uint16_t>, 256> usedIds;
usedTypeIds.set(0);
- // First build the graph of references.
+ // Collect which resource IDs are already taken.
for (auto& type : *mTable) {
if (type->typeId != ResourceTableType::kUnsetTypeId) {
// The ID for this type has already been set. We
@@ -66,29 +67,10 @@ bool Linker::linkAndValidate() {
// later.
usedIds[type->typeId].insert(entry->entryId);
}
-
- if (entry->publicStatus.isPublic && entry->values.empty()) {
- // A public resource has no values. It will not be encoded
- // properly without a symbol table. This is a unresolved symbol.
- addUnresolvedSymbol(ResourceNameRef{
- mTable->getPackage(), type->type, entry->name },
- entry->publicStatus.source);
- } else {
- for (auto& valueConfig : entry->values) {
- // Dispatch to the right method of this linker
- // based on the value's type.
- valueConfig.value->accept(*this, Args{
- ResourceNameRef{ mTable->getPackage(), type->type, entry->name },
- valueConfig.source
- });
- }
- }
}
}
- /*
- * Assign resource IDs that are available.
- */
+ // Assign resource IDs that are available.
size_t nextTypeIndex = 0;
for (auto& type : *mTable) {
if (type->typeId == ResourceTableType::kUnsetTypeId) {
@@ -109,29 +91,32 @@ bool Linker::linkAndValidate() {
++nextEntryIter;
}
entry->entryId = nextIndex++;
+ }
+ }
+ }
- std::u16string unmangledPackage = mTable->getPackage();
- std::u16string unmangledName = entry->name;
- NameMangler::unmangle(&unmangledName, &unmangledPackage);
+ // Now do reference linking.
+ for (auto& type : *mTable) {
+ for (auto& entry : type->entries) {
+ if (entry->publicStatus.isPublic && entry->values.empty()) {
+ // A public resource has no values. It will not be encoded
+ // properly without a symbol table. This is a unresolved symbol.
+ addUnresolvedSymbol(ResourceNameRef{
+ mTable->getPackage(), type->type, entry->name },
+ entry->publicStatus.source);
+ continue;
+ }
- // Update callers of this resource with the right ID.
- auto callersIter = mGraph.find(ResourceNameRef{
- unmangledPackage,
- type->type,
- unmangledName
+ for (auto& valueConfig : entry->values) {
+ // Dispatch to the right method of this linker
+ // based on the value's type.
+ valueConfig.value->accept(*this, Args{
+ ResourceNameRef{ mTable->getPackage(), type->type, entry->name },
+ valueConfig.source
});
-
- if (callersIter != std::end(mGraph)) {
- for (Node& caller : callersIter->second) {
- caller.reference->id = ResourceId(mTable->getPackageId(),
- type->typeId,
- entry->entryId);
- }
- }
}
}
}
-
return !mError;
}
@@ -139,12 +124,48 @@ const Linker::ResourceNameToSourceMap& Linker::getUnresolvedReferences() const {
return mUnresolvedSymbols;
}
+void Linker::doResolveReference(Reference& reference, const SourceLine& source) {
+ Maybe<ResourceId> result = mResolver->findId(reference.name);
+ if (!result) {
+ addUnresolvedSymbol(reference.name, source);
+ return;
+ }
+ assert(result.value().isValid());
+
+ if (mOptions.linkResourceIds) {
+ reference.id = result.value();
+ } else {
+ reference.id = 0;
+ }
+}
+
+const Attribute* Linker::doResolveAttribute(Reference& attribute, const SourceLine& source) {
+ Maybe<IResolver::Entry> result = mResolver->findAttribute(attribute.name);
+ if (!result || !result.value().attr) {
+ addUnresolvedSymbol(attribute.name, source);
+ return nullptr;
+ }
+
+ const IResolver::Entry& entry = result.value();
+ assert(entry.id.isValid());
+
+ if (mOptions.linkResourceIds) {
+ attribute.id = entry.id;
+ } else {
+ attribute.id = 0;
+ }
+ return entry.attr;
+}
+
void Linker::visit(Reference& reference, ValueVisitorArgs& a) {
Args& args = static_cast<Args&>(a);
if (!reference.name.isValid()) {
// We can't have a completely bad reference.
- assert(reference.id.isValid());
+ if (!reference.id.isValid()) {
+ Logger::error() << "srsly? " << args.referrer << std::endl;
+ assert(reference.id.isValid());
+ }
// This reference has no name but has an ID.
// It is a really bad error to have no name and have the same
@@ -156,25 +177,7 @@ void Linker::visit(Reference& reference, ValueVisitorArgs& a) {
return;
}
- Maybe<ResourceId> result = mResolver->findId(reference.name);
- if (!result) {
- addUnresolvedSymbol(reference.name, args.source);
- return;
- }
-
- const ResourceId& id = result.value();
- if (id.isValid()) {
- reference.id = id;
- } else {
- // We need to update the ID when it is set, so add it
- // to the graph.
- mGraph[reference.name].push_back(Node{
- args.referrer,
- args.source.path,
- args.source.line,
- &reference
- });
- }
+ doResolveReference(reference, args.source);
// TODO(adamlesinski): Verify the referencedType is another reference
// or a compatible primitive.
@@ -192,7 +195,6 @@ void Linker::processAttributeValue(const ResourceNameRef& name, const SourceLine
// We should never get here. All references would have been
// parsed in the parser phase.
assert(false);
- //mTable->addResource(name, ConfigDescription{}, source, util::make_unique<Id>());
};
convertedValue = ResourceParser::parseItemForAttribute(*str.value, attr,
@@ -240,25 +242,10 @@ void Linker::visit(Style& style, ValueVisitorArgs& a) {
}
for (Style::Entry& styleEntry : style.entries) {
- Maybe<IResolver::Entry> result = mResolver->findAttribute(styleEntry.key.name);
- if (!result || !result.value().attr) {
- addUnresolvedSymbol(styleEntry.key.name, args.source);
- continue;
- }
-
- const IResolver::Entry& entry = result.value();
- if (entry.id.isValid()) {
- styleEntry.key.id = entry.id;
- } else {
- // Create a dependency for the style on this attribute.
- mGraph[styleEntry.key.name].push_back(Node{
- args.referrer,
- args.source.path,
- args.source.line,
- &styleEntry.key
- });
+ const Attribute* attr = doResolveAttribute(styleEntry.key, args.source);
+ if (attr) {
+ processAttributeValue(args.referrer, args.source, *attr, styleEntry.value);
}
- processAttributeValue(args.referrer, args.source, *entry.attr, styleEntry.value);
}
}
@@ -300,8 +287,4 @@ void Linker::addUnresolvedSymbol(const ResourceNameRef& name, const SourceLine&
mUnresolvedSymbols[name.toResourceName()].push_back(source);
}
-::std::ostream& operator<<(::std::ostream& out, const Linker::Node& node) {
- return out << node.name << "(" << node.source << ":" << node.line << ")";
-}
-
} // namespace aapt
diff --git a/tools/aapt2/Linker.h b/tools/aapt2/Linker.h
index 9db64ab..6f03515 100644
--- a/tools/aapt2/Linker.h
+++ b/tools/aapt2/Linker.h
@@ -50,14 +50,25 @@ namespace aapt {
*/
class Linker : ValueVisitor {
public:
+ struct Options {
+ /**
+ * Assign resource Ids to references when linking.
+ * When building a static library, set this to false.
+ */
+ bool linkResourceIds = true;
+ };
+
/**
* Create a Linker for the given resource table with the sources available in
* IResolver. IResolver should contain the ResourceTable as a source too.
*/
- Linker(std::shared_ptr<ResourceTable> table, std::shared_ptr<IResolver> resolver);
+ Linker(const std::shared_ptr<ResourceTable>& table,
+ const std::shared_ptr<IResolver>& resolver, const Options& options);
Linker(const Linker&) = delete;
+ virtual ~Linker() = default;
+
/**
* Entry point to the linker. Assigns resource IDs, follows references,
* and validates types. Returns true if all references to defined values
@@ -73,6 +84,12 @@ public:
using ResourceNameToSourceMap = std::map<ResourceName, std::vector<SourceLine>>;
const ResourceNameToSourceMap& getUnresolvedReferences() const;
+protected:
+ virtual void doResolveReference(Reference& reference, const SourceLine& source);
+ virtual const Attribute* doResolveAttribute(Reference& attribute, const SourceLine& source);
+
+ std::shared_ptr<IResolver> mResolver;
+
private:
struct Args : public ValueVisitorArgs {
Args(const ResourceNameRef& r, const SourceLine& s);
@@ -92,33 +109,13 @@ private:
void visit(Plural& plural, ValueVisitorArgs& args) override;
void processAttributeValue(const ResourceNameRef& name, const SourceLine& source,
- const Attribute& attr, std::unique_ptr<Item>& value);
+ const Attribute& attr, std::unique_ptr<Item>& value);
void addUnresolvedSymbol(const ResourceNameRef& name, const SourceLine& source);
- /**
- * Node of the resource table graph.
- */
- struct Node {
- // We use ResourceNameRef and StringPiece, which are safe so long as the ResourceTable
- // that defines the data isn't modified.
- ResourceNameRef name;
- StringPiece source;
- size_t line;
-
- // The reference object that points to name.
- Reference* reference;
-
- bool operator<(const Node& rhs) const;
- bool operator==(const Node& rhs) const;
- bool operator!=(const Node& rhs) const;
- };
- friend ::std::ostream& operator<<(::std::ostream&, const Node&);
-
std::shared_ptr<ResourceTable> mTable;
- std::shared_ptr<IResolver> mResolver;
- std::map<ResourceNameRef, std::vector<Node>> mGraph;
std::map<ResourceName, std::vector<SourceLine>> mUnresolvedSymbols;
+ Options mOptions;
bool mError;
};
diff --git a/tools/aapt2/Linker_test.cpp b/tools/aapt2/Linker_test.cpp
index 3c5b8b4..66c6717 100644
--- a/tools/aapt2/Linker_test.cpp
+++ b/tools/aapt2/Linker_test.cpp
@@ -32,7 +32,8 @@ struct LinkerTest : public ::testing::Test {
mTable->setPackage(u"android");
mTable->setPackageId(0x01);
mLinker = std::make_shared<Linker>(mTable, std::make_shared<ResourceTableResolver>(
- mTable, std::make_shared<android::AssetManager>()));
+ mTable, std::vector<std::shared_ptr<const android::AssetManager>>()),
+ Linker::Options{});
// Create a few attributes for use in the tests.
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 3377f07..f1b7777 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -19,6 +19,7 @@
#include "BinaryResourceParser.h"
#include "BinaryXmlPullParser.h"
#include "BindingXmlPullParser.h"
+#include "Debug.h"
#include "Files.h"
#include "Flag.h"
#include "JavaClassGenerator.h"
@@ -55,54 +56,6 @@ constexpr const char* kAaptVersionStr = "2.0-alpha";
using namespace aapt;
-void printTable(const ResourceTable& table) {
- std::cout << "ResourceTable package=" << table.getPackage();
- if (table.getPackageId() != ResourceTable::kUnsetPackageId) {
- std::cout << " id=" << std::hex << table.getPackageId() << std::dec;
- }
- std::cout << std::endl
- << "---------------------------------------------------------" << std::endl;
-
- for (const auto& type : table) {
- std::cout << "Type " << type->type;
- if (type->typeId != ResourceTableType::kUnsetTypeId) {
- std::cout << " [" << type->typeId << "]";
- }
- std::cout << " (" << type->entries.size() << " entries)" << std::endl;
- for (const auto& entry : type->entries) {
- std::cout << " " << entry->name;
- if (entry->entryId != ResourceEntry::kUnsetEntryId) {
- std::cout << " [" << entry->entryId << "]";
- }
- std::cout << " (" << entry->values.size() << " configurations)";
- if (entry->publicStatus.isPublic) {
- std::cout << " PUBLIC";
- }
- std::cout << std::endl;
- for (const auto& value : entry->values) {
- std::cout << " " << value.config << " (" << value.source << ") : ";
- value.value->print(std::cout);
- std::cout << std::endl;
- }
- }
- }
-}
-
-void printStringPool(const StringPool& pool) {
- std::cout << "String pool of length " << pool.size() << std::endl
- << "---------------------------------------------------------" << std::endl;
-
- size_t i = 0;
- for (const auto& entry : pool) {
- std::cout << "[" << i << "]: "
- << entry->value
- << " (Priority " << entry->context.priority
- << ", Config '" << entry->context.config << "')"
- << std::endl;
- i++;
- }
-}
-
/**
* Collect files from 'root', filtering out any files that do not
* match the FileFilter 'filter'.
@@ -144,29 +97,6 @@ bool walkTree(const Source& root, const FileFilter& filter,
return !error;
}
-bool loadResTable(android::ResTable* table, const Source& source) {
- std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary);
- if (!ifs) {
- Logger::error(source) << strerror(errno) << std::endl;
- return false;
- }
-
- std::streampos fsize = ifs.tellg();
- ifs.seekg(0, std::ios::end);
- fsize = ifs.tellg() - fsize;
- ifs.seekg(0, std::ios::beg);
-
- assert(fsize >= 0);
- size_t dataSize = static_cast<size_t>(fsize);
- char* buf = new char[dataSize];
- ifs.read(buf, dataSize);
-
- bool result = table->add(buf, dataSize, -1, true) == android::NO_ERROR;
-
- delete [] buf;
- return result;
-}
-
void versionStylesForCompat(const std::shared_ptr<ResourceTable>& table) {
for (auto& type : *table) {
if (type->type != ResourceType::kStyle) {
@@ -285,8 +215,6 @@ static BasicStringPiece<TChar> getExtension(const BasicStringPiece<TChar>& str)
return str.substr(offset, str.size() - offset);
}
-
-
std::string buildFileReference(const ResourceNameRef& name, const ConfigDescription& config,
const StringPiece& extension) {
std::stringstream path;
@@ -321,6 +249,8 @@ struct AaptOptions {
enum class Phase {
Link,
Compile,
+ Dump,
+ DumpStyleGraph,
};
enum class PackageType {
@@ -366,9 +296,8 @@ struct AaptOptions {
bool versionStylesAndLayouts = true;
};
-
bool compileXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
- const CompileItem& item, std::queue<CompileItem>* outQueue, ZipFile* outApk) {
+ const CompileItem& item, ZipFile* outApk) {
std::ifstream in(item.source.path, std::ifstream::binary);
if (!in) {
Logger::error(item.source) << strerror(errno) << std::endl;
@@ -382,6 +311,45 @@ bool compileXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>
XmlFlattener::Options xmlOptions;
xmlOptions.defaultPackage = table->getPackage();
+ xmlOptions.keepRawValues = true;
+
+ std::shared_ptr<XmlPullParser> parser = std::make_shared<SourceXmlPullParser>(in);
+
+ Maybe<size_t> minStrippedSdk = flattener.flatten(item.source, parser, &outBuffer,
+ xmlOptions);
+ if (!minStrippedSdk) {
+ return false;
+ }
+
+ // Write the resulting compiled XML file to the output APK.
+ if (outApk->add(outBuffer, buildFileReference(item).data(), ZipEntry::kCompressStored,
+ nullptr) != android::NO_ERROR) {
+ Logger::error(options.output) << "failed to write compiled '" << item.source
+ << "' to apk." << std::endl;
+ return false;
+ }
+ return true;
+}
+
+bool linkXml(const AaptOptions& options, const std::shared_ptr<IResolver>& resolver,
+ const LinkItem& item, const void* data, size_t dataLen, ZipFile* outApk,
+ std::queue<LinkItem>* outQueue) {
+ std::shared_ptr<android::ResXMLTree> tree = std::make_shared<android::ResXMLTree>();
+ if (tree->setTo(data, dataLen, false) != android::NO_ERROR) {
+ return false;
+ }
+
+ std::shared_ptr<XmlPullParser> parser = std::make_shared<BinaryXmlPullParser>(tree);
+
+ BigBuffer outBuffer(1024);
+ XmlFlattener flattener({}, resolver);
+
+ XmlFlattener::Options xmlOptions;
+ xmlOptions.defaultPackage = item.originalPackage;
+
+ if (options.packageType == AaptOptions::PackageType::StaticLibrary) {
+ xmlOptions.keepRawValues = true;
+ }
if (options.versionStylesAndLayouts) {
// We strip attributes that do not belong in this version of the resource.
@@ -390,14 +358,14 @@ bool compileXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>
}
std::shared_ptr<BindingXmlPullParser> binding;
- std::shared_ptr<XmlPullParser> parser = std::make_shared<SourceXmlPullParser>(in);
if (item.name.type == ResourceType::kLayout) {
// Layouts may have defined bindings, so we need to make sure they get processed.
binding = std::make_shared<BindingXmlPullParser>(parser);
parser = binding;
}
- Maybe<size_t> minStrippedSdk = flattener.flatten(item.source, parser, &outBuffer, xmlOptions);
+ Maybe<size_t> minStrippedSdk = flattener.flatten(item.source, parser, &outBuffer,
+ xmlOptions);
if (!minStrippedSdk) {
return false;
}
@@ -405,16 +373,15 @@ bool compileXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>
if (minStrippedSdk.value() > 0) {
// Something was stripped, so let's generate a new file
// with the version of the smallest SDK version stripped.
- CompileItem newWork = item;
+ LinkItem newWork = item;
newWork.config.sdkVersion = minStrippedSdk.value();
outQueue->push(newWork);
}
- // Write the resulting compiled XML file to the output APK.
- if (outApk->add(outBuffer, buildFileReference(item).data(), ZipEntry::kCompressStored,
+ if (outApk->add(outBuffer, buildFileReference(item).data(), ZipEntry::kCompressDeflated,
nullptr) != android::NO_ERROR) {
- Logger::error(options.output) << "failed to write compiled '" << item.source << "' to apk."
- << std::endl;
+ Logger::error(options.output) << "failed to write linked file '"
+ << buildFileReference(item) << "' to apk." << std::endl;
return false;
}
@@ -444,33 +411,6 @@ bool compileXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>
return true;
}
-bool linkXml(const AaptOptions& options, const std::shared_ptr<IResolver>& resolver,
- const LinkItem& item, const void* data, size_t dataLen, ZipFile* outApk) {
- std::shared_ptr<android::ResXMLTree> tree = std::make_shared<android::ResXMLTree>();
- if (tree->setTo(data, dataLen, false) != android::NO_ERROR) {
- return false;
- }
-
- std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<BinaryXmlPullParser>(tree);
-
- BigBuffer outBuffer(1024);
- XmlFlattener flattener({}, resolver);
-
- XmlFlattener::Options xmlOptions;
- xmlOptions.defaultPackage = item.originalPackage;
- if (!flattener.flatten(item.source, xmlParser, &outBuffer, xmlOptions)) {
- return false;
- }
-
- if (outApk->add(outBuffer, buildFileReference(item).data(), ZipEntry::kCompressDeflated,
- nullptr) != android::NO_ERROR) {
- Logger::error(options.output) << "failed to write linked file '" << item.source
- << "' to apk." << std::endl;
- return false;
- }
- return true;
-}
-
bool compilePng(const AaptOptions& options, const CompileItem& item, ZipFile* outApk) {
std::ifstream in(item.source.path, std::ifstream::binary);
if (!in) {
@@ -505,8 +445,8 @@ bool copyFile(const AaptOptions& options, const CompileItem& item, ZipFile* outA
return true;
}
-bool compileManifest(const AaptOptions& options,
- const std::shared_ptr<ResourceTableResolver>& resolver, ZipFile* outApk) {
+bool compileManifest(const AaptOptions& options, const std::shared_ptr<IResolver>& resolver,
+ const android::ResTable& table, ZipFile* outApk) {
if (options.verbose) {
Logger::note(options.manifest) << "compiling AndroidManifest.xml." << std::endl;
}
@@ -534,7 +474,7 @@ bool compileManifest(const AaptOptions& options,
return false;
}
- ManifestValidator validator(resolver->getResTable());
+ ManifestValidator validator(table);
if (!validator.validate(options.manifest, &tree)) {
return false;
}
@@ -690,7 +630,7 @@ struct StaticLibraryData {
};
bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outTable,
- const std::shared_ptr<ResourceTableResolver>& resolver) {
+ const std::shared_ptr<IResolver>& resolver) {
std::map<std::shared_ptr<ResourceTable>, StaticLibraryData> apkFiles;
std::unordered_set<std::u16string> linkedPackages;
@@ -744,9 +684,18 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
}
}
+ // Version all styles referencing attributes outside of their specified SDK version.
+ if (options.versionStylesAndLayouts) {
+ versionStylesForCompat(outTable);
+ }
+
{
// Now that everything is merged, let's link it.
- Linker linker(outTable, resolver);
+ Linker::Options linkerOptions;
+ if (options.packageType == AaptOptions::PackageType::StaticLibrary) {
+ linkerOptions.linkResourceIds = false;
+ }
+ Linker linker(outTable, resolver, linkerOptions);
if (!linker.linkAndValidate()) {
return false;
}
@@ -771,7 +720,8 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
return false;
}
- if (!compileManifest(options, resolver, &outApk)) {
+ android::ResTable binTable;
+ if (!compileManifest(options, resolver, binTable, &outApk)) {
return false;
}
@@ -791,7 +741,7 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
assert(uncompressedData);
if (!linkXml(options, resolver, item, uncompressedData, entry->getUncompressedLen(),
- &outApk)) {
+ &outApk, &linkQueue)) {
Logger::error(options.output) << "failed to link '" << item.originalPath << "'."
<< std::endl;
return false;
@@ -864,8 +814,8 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
// Flatten the resource table.
TableFlattener::Options flattenerOptions;
- if (options.packageType == AaptOptions::PackageType::StaticLibrary) {
- flattenerOptions.useExtendedChunks = true;
+ if (options.packageType != AaptOptions::PackageType::StaticLibrary) {
+ flattenerOptions.useExtendedChunks = false;
}
if (!writeResourceTable(options, outTable, flattenerOptions, &outApk)) {
@@ -920,12 +870,6 @@ bool compile(const AaptOptions& options, const std::shared_ptr<ResourceTable>& t
if (error) {
return false;
}
-
- // Version all styles referencing attributes outside of their specified SDK version.
- if (options.versionStylesAndLayouts) {
- versionStylesForCompat(table);
- }
-
// Open the output APK file for writing.
ZipFile outApk;
if (outApk.open(options.output.path.data(), kOpenFlags) != android::NO_ERROR) {
@@ -941,7 +885,7 @@ bool compile(const AaptOptions& options, const std::shared_ptr<ResourceTable>& t
error |= !addFileReference(table, item);
if (item.extension == "xml") {
- error |= !compileXml(options, table, item, &compileQueue, &outApk);
+ error |= !compileXml(options, table, item, &outApk);
} else if (item.extension == "png" || item.extension == "9.png") {
error |= !compilePng(options, item, &outApk);
} else {
@@ -954,7 +898,7 @@ bool compile(const AaptOptions& options, const std::shared_ptr<ResourceTable>& t
}
// Link and assign resource IDs.
- Linker linker(table, resolver);
+ Linker linker(table, resolver, {});
if (!linker.linkAndValidate()) {
return false;
}
@@ -984,6 +928,7 @@ static void printCommandsAndDie() {
std::cerr << "The following commands are supported:" << std::endl << std::endl;
std::cerr << "compile compiles a subset of resources" << std::endl;
std::cerr << "link links together compiled resources and libraries" << std::endl;
+ std::cerr << "dump dumps resource contents to to standard out" << std::endl;
std::cerr << std::endl;
std::cerr << "run aapt2 with one of the commands and the -h flag for extra details."
<< std::endl;
@@ -1009,48 +954,56 @@ static AaptOptions prepareArgs(int argc, char** argv) {
options.phase = AaptOptions::Phase::Link;
} else if (command == "compile") {
options.phase = AaptOptions::Phase::Compile;
+ } else if (command == "dump") {
+ options.phase = AaptOptions::Phase::Dump;
+ } else if (command == "dump-style-graph") {
+ options.phase = AaptOptions::Phase::DumpStyleGraph;
} else {
std::cerr << "invalid command '" << command << "'." << std::endl << std::endl;
printCommandsAndDie();
}
bool isStaticLib = false;
- if (options.phase == AaptOptions::Phase::Compile) {
- flag::requiredFlag("--package", "Android package name",
- [&options](const StringPiece& arg) {
- options.appInfo.package = util::utf8ToUtf16(arg);
- });
- flag::optionalFlag("--binding", "Output directory for binding XML files",
- [&options](const StringPiece& arg) {
- options.bindingOutput = Source{ arg.toString() };
- });
- flag::optionalSwitch("--no-version", "Disables automatic style and layout versioning",
- false, &options.versionStylesAndLayouts);
-
- } else if (options.phase == AaptOptions::Phase::Link) {
- flag::requiredFlag("--manifest", "AndroidManifest.xml of your app",
- [&options](const StringPiece& arg) {
- options.manifest = Source{ arg.toString() };
- });
-
- flag::optionalFlag("-I", "add an Android APK to link against",
- [&options](const StringPiece& arg) {
- options.libraries.push_back(Source{ arg.toString() });
- });
+ if (options.phase == AaptOptions::Phase::Compile ||
+ options.phase == AaptOptions::Phase::Link) {
+ if (options.phase == AaptOptions::Phase::Compile) {
+ flag::requiredFlag("--package", "Android package name",
+ [&options](const StringPiece& arg) {
+ options.appInfo.package = util::utf8ToUtf16(arg);
+ });
+ } else if (options.phase == AaptOptions::Phase::Link) {
+ flag::requiredFlag("--manifest", "AndroidManifest.xml of your app",
+ [&options](const StringPiece& arg) {
+ options.manifest = Source{ arg.toString() };
+ });
+
+ flag::optionalFlag("-I", "add an Android APK to link against",
+ [&options](const StringPiece& arg) {
+ options.libraries.push_back(Source{ arg.toString() });
+ });
+
+ flag::optionalFlag("--java", "directory in which to generate R.java",
+ [&options](const StringPiece& arg) {
+ options.generateJavaClass = Source{ arg.toString() };
+ });
+
+ flag::optionalSwitch("--static-lib", "generate a static Android library", true,
+ &isStaticLib);
+
+ flag::optionalFlag("--binding", "Output directory for binding XML files",
+ [&options](const StringPiece& arg) {
+ options.bindingOutput = Source{ arg.toString() };
+ });
+ flag::optionalSwitch("--no-version", "Disables automatic style and layout versioning",
+ false, &options.versionStylesAndLayouts);
+ }
- flag::optionalFlag("--java", "directory in which to generate R.java",
- [&options](const StringPiece& arg) {
- options.generateJavaClass = Source{ arg.toString() };
- });
- flag::optionalSwitch("--static-lib", "generate a static Android library", true,
- &isStaticLib);
+ // Common flags for all steps.
+ flag::requiredFlag("-o", "Output path", [&options](const StringPiece& arg) {
+ options.output = Source{ arg.toString() };
+ });
}
- // Common flags for all steps.
- flag::requiredFlag("-o", "Output path", [&options](const StringPiece& arg) {
- options.output = Source{ arg.toString() };
- });
-
bool help = false;
flag::optionalSwitch("-v", "enables verbose logging", true, &options.verbose);
flag::optionalSwitch("-h", "displays this help menu", true, &help);
@@ -1078,10 +1031,56 @@ static AaptOptions prepareArgs(int argc, char** argv) {
return options;
}
+static bool doDump(const AaptOptions& options) {
+ for (const Source& source : options.input) {
+ std::unique_ptr<ZipFile> zipFile = util::make_unique<ZipFile>();
+ if (zipFile->open(source.path.data(), ZipFile::kOpenReadOnly) != android::NO_ERROR) {
+ Logger::error(source) << "failed to open: " << strerror(errno) << std::endl;
+ return false;
+ }
+
+ std::shared_ptr<ResourceTable> table = std::make_shared<ResourceTable>();
+ std::shared_ptr<ResourceTableResolver> resolver =
+ std::make_shared<ResourceTableResolver>(
+ table, std::vector<std::shared_ptr<const android::AssetManager>>());
+
+ ZipEntry* entry = zipFile->getEntryByName("resources.arsc");
+ if (!entry) {
+ Logger::error(source) << "missing 'resources.arsc'." << std::endl;
+ return false;
+ }
+
+ std::unique_ptr<void, DeleteMalloc> uncompressedData = std::unique_ptr<void, DeleteMalloc>(
+ zipFile->uncompress(entry));
+ assert(uncompressedData);
+
+ BinaryResourceParser parser(table, resolver, source, uncompressedData.get(),
+ entry->getUncompressedLen());
+ if (!parser.parse()) {
+ return false;
+ }
+
+ if (options.phase == AaptOptions::Phase::Dump) {
+ Debug::printTable(table);
+ } else if (options.phase == AaptOptions::Phase::DumpStyleGraph) {
+ Debug::printStyleGraph(table);
+ }
+ }
+ return true;
+}
+
int main(int argc, char** argv) {
Logger::setLog(std::make_shared<Log>(std::cerr, std::cerr));
AaptOptions options = prepareArgs(argc, argv);
+ if (options.phase == AaptOptions::Phase::Dump ||
+ options.phase == AaptOptions::Phase::DumpStyleGraph) {
+ if (!doDump(options)) {
+ return 1;
+ }
+ return 0;
+ }
+
// If we specified a manifest, go ahead and load the package name from the manifest.
if (!options.manifest.path.empty()) {
if (!loadAppInfo(options.manifest, &options.appInfo)) {
@@ -1105,37 +1104,26 @@ int main(int argc, char** argv) {
}
// Load the included libraries.
- std::shared_ptr<android::AssetManager> libraries = std::make_shared<android::AssetManager>();
+ std::vector<std::shared_ptr<const android::AssetManager>> sources;
for (const Source& source : options.libraries) {
- if (util::stringEndsWith<char>(source.path, ".arsc")) {
- // We'll process these last so as to avoid a cookie issue.
- continue;
- }
-
+ std::shared_ptr<android::AssetManager> assetManager =
+ std::make_shared<android::AssetManager>();
int32_t cookie;
- if (!libraries->addAssetPath(android::String8(source.path.data()), &cookie)) {
+ if (!assetManager->addAssetPath(android::String8(source.path.data()), &cookie)) {
Logger::error(source) << "failed to load library." << std::endl;
return false;
}
- }
- for (const Source& source : options.libraries) {
- if (!util::stringEndsWith<char>(source.path, ".arsc")) {
- // We've already processed this.
- continue;
- }
-
- // Dirty hack but there is no other way to get a
- // writeable ResTable.
- if (!loadResTable(const_cast<android::ResTable*>(&libraries->getResources(false)),
- source)) {
+ if (cookie == 0) {
+ Logger::error(source) << "failed to load library." << std::endl;
return false;
}
+ sources.push_back(assetManager);
}
// Make the resolver that will cache IDs for us.
std::shared_ptr<ResourceTableResolver> resolver = std::make_shared<ResourceTableResolver>(
- table, libraries);
+ table, sources);
if (options.phase == AaptOptions::Phase::Compile) {
if (!compile(options, table, resolver)) {
diff --git a/tools/aapt2/MockResolver.h b/tools/aapt2/MockResolver.h
index 48ff6a6..0c9b954 100644
--- a/tools/aapt2/MockResolver.h
+++ b/tools/aapt2/MockResolver.h
@@ -34,7 +34,7 @@ struct MockResolver : public IResolver {
MockResolver(const std::shared_ptr<ResourceTable>& table,
const std::map<ResourceName, ResourceId>& items) :
mResolver(std::make_shared<ResourceTableResolver>(
- table, std::make_shared<const android::AssetManager>())),
+ table, std::vector<std::shared_ptr<const android::AssetManager>>())),
mAttr(false, android::ResTable_map::TYPE_ANY), mItems(items) {
}
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 9468860..c93ecc7 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -42,6 +42,8 @@ static bool lessThanEntry(const std::unique_ptr<ResourceEntry>& lhs, const Strin
}
ResourceTable::ResourceTable() : mPackageId(kUnsetPackageId) {
+ // Make sure attrs always have type ID 1.
+ findOrCreateType(ResourceType::kAttr)->typeId = 1;
}
std::unique_ptr<ResourceTableType>& ResourceTable::findOrCreateType(ResourceType type) {
@@ -142,10 +144,30 @@ static int defaultCollisionHandler(const Value& existing, const Value& incoming)
}
static constexpr const char16_t* kValidNameChars = u"._-";
+static constexpr const char16_t* kValidNameMangledChars = u"._-$";
+
+bool ResourceTable::addResource(const ResourceNameRef& name, const ConfigDescription& config,
+ const SourceLine& source, std::unique_ptr<Value> value) {
+ return addResourceImpl(name, ResourceId{}, config, source, std::move(value), kValidNameChars);
+}
bool ResourceTable::addResource(const ResourceNameRef& name, const ResourceId resId,
- const ConfigDescription& config, const SourceLine& source,
- std::unique_ptr<Value> value) {
+ const ConfigDescription& config, const SourceLine& source,
+ std::unique_ptr<Value> value) {
+ return addResourceImpl(name, resId, config, source, std::move(value), kValidNameChars);
+}
+
+bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const SourceLine& source,
+ std::unique_ptr<Value> value) {
+ return addResourceImpl(name, ResourceId{}, config, source, std::move(value),
+ kValidNameMangledChars);
+}
+
+bool ResourceTable::addResourceImpl(const ResourceNameRef& name, const ResourceId resId,
+ const ConfigDescription& config, const SourceLine& source,
+ std::unique_ptr<Value> value, const char16_t* validChars) {
if (!name.package.empty() && name.package != mPackage) {
Logger::error(source)
<< "resource '"
@@ -157,7 +179,7 @@ bool ResourceTable::addResource(const ResourceNameRef& name, const ResourceId re
return false;
}
- auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, kValidNameChars);
+ auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
if (badCharIter != name.entry.end()) {
Logger::error(source)
<< "resource '"
@@ -233,13 +255,18 @@ bool ResourceTable::addResource(const ResourceNameRef& name, const ResourceId re
return true;
}
-bool ResourceTable::addResource(const ResourceNameRef& name, const ConfigDescription& config,
- const SourceLine& source, std::unique_ptr<Value> value) {
- return addResource(name, ResourceId{}, config, source, std::move(value));
-}
-
bool ResourceTable::markPublic(const ResourceNameRef& name, const ResourceId resId,
const SourceLine& source) {
+ return markPublicImpl(name, resId, source, kValidNameChars);
+}
+
+bool ResourceTable::markPublicAllowMangled(const ResourceNameRef& name, const ResourceId resId,
+ const SourceLine& source) {
+ return markPublicImpl(name, resId, source, kValidNameMangledChars);
+}
+
+bool ResourceTable::markPublicImpl(const ResourceNameRef& name, const ResourceId resId,
+ const SourceLine& source, const char16_t* validChars) {
if (!name.package.empty() && name.package != mPackage) {
Logger::error(source)
<< "resource '"
@@ -251,7 +278,7 @@ bool ResourceTable::markPublic(const ResourceNameRef& name, const ResourceId res
return false;
}
- auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, kValidNameChars);
+ auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
if (badCharIter != name.entry.end()) {
Logger::error(source)
<< "resource '"
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 94bacd8..706f56a 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -143,11 +143,21 @@ public:
bool addResource(const ResourceNameRef& name, const ConfigDescription& config,
const SourceLine& source, std::unique_ptr<Value> value);
+ /**
+ * Same as addResource, but doesn't verify the validity of the name. This is used
+ * when loading resources from an existing binary resource table that may have mangled
+ * names.
+ */
+ bool addResourceAllowMangled(const ResourceNameRef& name, const ConfigDescription& config,
+ const SourceLine& source, std::unique_ptr<Value> value);
+
bool addResource(const ResourceNameRef& name, const ResourceId resId,
const ConfigDescription& config, const SourceLine& source,
std::unique_ptr<Value> value);
bool markPublic(const ResourceNameRef& name, const ResourceId resId, const SourceLine& source);
+ bool markPublicAllowMangled(const ResourceNameRef& name, const ResourceId resId,
+ const SourceLine& source);
/*
* Merges the resources from `other` into this table, mangling the names of the resources
@@ -176,6 +186,12 @@ private:
std::unique_ptr<ResourceEntry>& findOrCreateEntry(std::unique_ptr<ResourceTableType>& type,
const StringPiece16& name);
+ bool addResourceImpl(const ResourceNameRef& name, const ResourceId resId,
+ const ConfigDescription& config, const SourceLine& source,
+ std::unique_ptr<Value> value, const char16_t* validChars);
+ bool markPublicImpl(const ResourceNameRef& name, const ResourceId resId,
+ const SourceLine& source, const char16_t* validChars);
+
std::u16string mPackage;
size_t mPackageId;
diff --git a/tools/aapt2/ResourceTableResolver.cpp b/tools/aapt2/ResourceTableResolver.cpp
index 0a9f521..910c2c0 100644
--- a/tools/aapt2/ResourceTableResolver.cpp
+++ b/tools/aapt2/ResourceTableResolver.cpp
@@ -31,13 +31,15 @@ namespace aapt {
ResourceTableResolver::ResourceTableResolver(
std::shared_ptr<const ResourceTable> table,
- std::shared_ptr<const android::AssetManager> sources) :
+ const std::vector<std::shared_ptr<const android::AssetManager>>& sources) :
mTable(table), mSources(sources) {
- const android::ResTable& resTable = mSources->getResources(false);
- const size_t packageCount = resTable.getBasePackageCount();
- for (size_t i = 0; i < packageCount; i++) {
- std::u16string packageName = resTable.getBasePackageName(i).string();
- mIncludedPackages.insert(std::move(packageName));
+ for (const auto& assetManager : mSources) {
+ const android::ResTable& resTable = assetManager->getResources(false);
+ const size_t packageCount = resTable.getBasePackageCount();
+ for (size_t i = 0; i < packageCount; i++) {
+ std::u16string packageName = resTable.getBasePackageName(i).string();
+ mIncludedPackages.insert(std::move(packageName));
+ }
}
}
@@ -99,20 +101,23 @@ Maybe<IResolver::Entry> ResourceTableResolver::findAttribute(const ResourceName&
}
Maybe<ResourceName> ResourceTableResolver::findName(ResourceId resId) {
- const android::ResTable& table = mSources->getResources(false);
+ for (const auto& assetManager : mSources) {
+ const android::ResTable& table = assetManager->getResources(false);
- android::ResTable::resource_name resourceName;
- if (!table.getResourceName(resId.id, false, &resourceName)) {
- return {};
- }
+ android::ResTable::resource_name resourceName;
+ if (!table.getResourceName(resId.id, false, &resourceName)) {
+ continue;
+ }
- const ResourceType* type = parseResourceType(StringPiece16(resourceName.type,
- resourceName.typeLen));
- assert(type);
- return ResourceName{
- { resourceName.package, resourceName.packageLen },
- *type,
- { resourceName.name, resourceName.nameLen } };
+ const ResourceType* type = parseResourceType(StringPiece16(resourceName.type,
+ resourceName.typeLen));
+ assert(type);
+ return ResourceName{
+ { resourceName.package, resourceName.packageLen },
+ *type,
+ { resourceName.name, resourceName.nameLen } };
+ }
+ return {};
}
/**
@@ -122,73 +127,76 @@ Maybe<ResourceName> ResourceTableResolver::findName(ResourceId resId) {
*/
const ResourceTableResolver::CacheEntry* ResourceTableResolver::buildCacheEntry(
const ResourceName& name) {
- const android::ResTable& table = mSources->getResources(false);
-
- const StringPiece16 type16 = toString(name.type);
- ResourceId resId {
- table.identifierForName(
- name.entry.data(), name.entry.size(),
- type16.data(), type16.size(),
- name.package.data(), name.package.size())
- };
-
- if (!resId.isValid()) {
- return nullptr;
- }
-
- CacheEntry& entry = mCache[name];
- entry.id = resId;
+ for (const auto& assetManager : mSources) {
+ const android::ResTable& table = assetManager->getResources(false);
+
+ const StringPiece16 type16 = toString(name.type);
+ ResourceId resId {
+ table.identifierForName(
+ name.entry.data(), name.entry.size(),
+ type16.data(), type16.size(),
+ name.package.data(), name.package.size())
+ };
+
+ if (!resId.isValid()) {
+ continue;
+ }
- //
- // Now check to see if this resource is an Attribute.
- //
+ CacheEntry& entry = mCache[name];
+ entry.id = resId;
- const android::ResTable::bag_entry* bagBegin;
- ssize_t bags = table.lockBag(resId.id, &bagBegin);
- if (bags < 1) {
- table.unlockBag(bagBegin);
- return &entry;
- }
+ //
+ // Now check to see if this resource is an Attribute.
+ //
- // Look for the ATTR_TYPE key in the bag and check the types it supports.
- uint32_t attrTypeMask = 0;
- for (ssize_t i = 0; i < bags; i++) {
- if (bagBegin[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
- attrTypeMask = bagBegin[i].map.value.data;
+ const android::ResTable::bag_entry* bagBegin;
+ ssize_t bags = table.lockBag(resId.id, &bagBegin);
+ if (bags < 1) {
+ table.unlockBag(bagBegin);
+ return &entry;
}
- }
-
- entry.attr = util::make_unique<Attribute>(false);
- if (attrTypeMask & android::ResTable_map::TYPE_ENUM ||
- attrTypeMask & android::ResTable_map::TYPE_FLAGS) {
+ // Look for the ATTR_TYPE key in the bag and check the types it supports.
+ uint32_t attrTypeMask = 0;
for (ssize_t i = 0; i < bags; i++) {
- if (Res_INTERNALID(bagBegin[i].map.name.ident)) {
- // Internal IDs are special keys, which are not enum/flag symbols, so skip.
- continue;
+ if (bagBegin[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
+ attrTypeMask = bagBegin[i].map.value.data;
}
+ }
- android::ResTable::resource_name symbolName;
- bool result = table.getResourceName(bagBegin[i].map.name.ident, false,
- &symbolName);
- assert(result);
- const ResourceType* type = parseResourceType(
- StringPiece16(symbolName.type, symbolName.typeLen));
- assert(type);
-
- entry.attr->symbols.push_back(Attribute::Symbol{
- Reference(ResourceNameRef(
- StringPiece16(symbolName.package, symbolName.packageLen),
- *type,
- StringPiece16(symbolName.name, symbolName.nameLen))),
- bagBegin[i].map.value.data
- });
+ entry.attr = util::make_unique<Attribute>(false);
+
+ if (attrTypeMask & android::ResTable_map::TYPE_ENUM ||
+ attrTypeMask & android::ResTable_map::TYPE_FLAGS) {
+ for (ssize_t i = 0; i < bags; i++) {
+ if (Res_INTERNALID(bagBegin[i].map.name.ident)) {
+ // Internal IDs are special keys, which are not enum/flag symbols, so skip.
+ continue;
+ }
+
+ android::ResTable::resource_name symbolName;
+ bool result = table.getResourceName(bagBegin[i].map.name.ident, false,
+ &symbolName);
+ assert(result);
+ const ResourceType* type = parseResourceType(
+ StringPiece16(symbolName.type, symbolName.typeLen));
+ assert(type);
+
+ entry.attr->symbols.push_back(Attribute::Symbol{
+ Reference(ResourceNameRef(
+ StringPiece16(symbolName.package, symbolName.packageLen),
+ *type,
+ StringPiece16(symbolName.name, symbolName.nameLen))),
+ bagBegin[i].map.value.data
+ });
+ }
}
- }
- entry.attr->typeMask |= attrTypeMask;
- table.unlockBag(bagBegin);
- return &entry;
+ entry.attr->typeMask |= attrTypeMask;
+ table.unlockBag(bagBegin);
+ return &entry;
+ }
+ return nullptr;
}
} // namespace aapt
diff --git a/tools/aapt2/ResourceTableResolver.h b/tools/aapt2/ResourceTableResolver.h
index c8e8ab7..8f6b0b5 100644
--- a/tools/aapt2/ResourceTableResolver.h
+++ b/tools/aapt2/ResourceTableResolver.h
@@ -24,7 +24,6 @@
#include "ResourceValues.h"
#include <androidfw/AssetManager.h>
-#include <androidfw/ResourceTypes.h>
#include <memory>
#include <vector>
#include <unordered_set>
@@ -40,8 +39,9 @@ public:
* Creates a resolver with a local ResourceTable and an AssetManager
* loaded with library packages.
*/
- ResourceTableResolver(std::shared_ptr<const ResourceTable> table,
- std::shared_ptr<const android::AssetManager> sources);
+ ResourceTableResolver(
+ std::shared_ptr<const ResourceTable> table,
+ const std::vector<std::shared_ptr<const android::AssetManager>>& sources);
ResourceTableResolver(const ResourceTableResolver&) = delete; // Not copyable.
@@ -51,8 +51,6 @@ public:
virtual Maybe<ResourceName> findName(ResourceId resId) override;
- const android::ResTable& getResTable() const;
-
private:
struct CacheEntry {
ResourceId id;
@@ -62,15 +60,11 @@ private:
const CacheEntry* buildCacheEntry(const ResourceName& name);
std::shared_ptr<const ResourceTable> mTable;
- std::shared_ptr<const android::AssetManager> mSources;
+ std::vector<std::shared_ptr<const android::AssetManager>> mSources;
std::map<ResourceName, CacheEntry> mCache;
std::unordered_set<std::u16string> mIncludedPackages;
};
-inline const android::ResTable& ResourceTableResolver::getResTable() const {
- return mSources->getResources(false);
-}
-
} // namespace aapt
#endif // AAPT_RESOURCE_TABLE_RESOLVER_H
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 2bf38e4..be0c3f3 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -101,8 +101,8 @@ bool Id::isWeak() const {
}
bool Id::flatten(android::Res_value& out) const {
- out.dataType = android::Res_value::TYPE_NULL;
- out.data = android::Res_value::DATA_NULL_UNDEFINED;
+ out.dataType = android::Res_value::TYPE_INT_BOOLEAN;
+ out.data = 0;
return true;
}
@@ -231,17 +231,15 @@ Attribute* Attribute::clone(StringPool* /*newPool*/) const {
return attr;
}
-void Attribute::print(std::ostream& out) const {
- out << "(attr)";
+void Attribute::printMask(std::ostream& out) const {
if (typeMask == android::ResTable_map::TYPE_ANY) {
- out << " any";
+ out << "any";
return;
}
bool set = false;
if ((typeMask & android::ResTable_map::TYPE_REFERENCE) != 0) {
if (!set) {
- out << " ";
set = true;
} else {
out << "|";
@@ -251,7 +249,6 @@ void Attribute::print(std::ostream& out) const {
if ((typeMask & android::ResTable_map::TYPE_STRING) != 0) {
if (!set) {
- out << " ";
set = true;
} else {
out << "|";
@@ -261,7 +258,6 @@ void Attribute::print(std::ostream& out) const {
if ((typeMask & android::ResTable_map::TYPE_INTEGER) != 0) {
if (!set) {
- out << " ";
set = true;
} else {
out << "|";
@@ -271,7 +267,6 @@ void Attribute::print(std::ostream& out) const {
if ((typeMask & android::ResTable_map::TYPE_BOOLEAN) != 0) {
if (!set) {
- out << " ";
set = true;
} else {
out << "|";
@@ -281,7 +276,6 @@ void Attribute::print(std::ostream& out) const {
if ((typeMask & android::ResTable_map::TYPE_COLOR) != 0) {
if (!set) {
- out << " ";
set = true;
} else {
out << "|";
@@ -291,7 +285,6 @@ void Attribute::print(std::ostream& out) const {
if ((typeMask & android::ResTable_map::TYPE_FLOAT) != 0) {
if (!set) {
- out << " ";
set = true;
} else {
out << "|";
@@ -301,7 +294,6 @@ void Attribute::print(std::ostream& out) const {
if ((typeMask & android::ResTable_map::TYPE_DIMENSION) != 0) {
if (!set) {
- out << " ";
set = true;
} else {
out << "|";
@@ -311,7 +303,6 @@ void Attribute::print(std::ostream& out) const {
if ((typeMask & android::ResTable_map::TYPE_FRACTION) != 0) {
if (!set) {
- out << " ";
set = true;
} else {
out << "|";
@@ -321,7 +312,6 @@ void Attribute::print(std::ostream& out) const {
if ((typeMask & android::ResTable_map::TYPE_ENUM) != 0) {
if (!set) {
- out << " ";
set = true;
} else {
out << "|";
@@ -331,13 +321,17 @@ void Attribute::print(std::ostream& out) const {
if ((typeMask & android::ResTable_map::TYPE_FLAGS) != 0) {
if (!set) {
- out << " ";
set = true;
} else {
out << "|";
}
out << "flags";
}
+}
+
+void Attribute::print(std::ostream& out) const {
+ out << "(attr) ";
+ printMask(out);
out << " ["
<< util::joiner(symbols.begin(), symbols.end(), ", ")
@@ -348,10 +342,6 @@ void Attribute::print(std::ostream& out) const {
}
}
-static ::std::ostream& operator<<(::std::ostream& out, const Attribute::Symbol& s) {
- return out << s.symbol.name.entry << "=" << s.value;
-}
-
Style::Style(bool weak) : weak(weak) {
}
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index f8ece6f..b448bd8 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -222,6 +222,7 @@ struct Attribute : public BaseValue<Attribute> {
bool isWeak() const override;
virtual Attribute* clone(StringPool* newPool) const override;
+ void printMask(std::ostream& out) const;
virtual void print(std::ostream& out) const override;
};
@@ -280,6 +281,10 @@ inline ::std::ostream& operator<<(::std::ostream& out, const Value& value) {
return out;
}
+inline ::std::ostream& operator<<(::std::ostream& out, const Attribute::Symbol& s) {
+ return out << s.symbol.name.entry << "=" << s.value;
+}
+
/**
* The argument object that gets passed through the value
* back to the ValueVisitor. Subclasses of ValueVisitor should
diff --git a/tools/aapt2/TableFlattener.cpp b/tools/aapt2/TableFlattener.cpp
index aa0f1d5..539c48f 100644
--- a/tools/aapt2/TableFlattener.cpp
+++ b/tools/aapt2/TableFlattener.cpp
@@ -79,6 +79,7 @@ public:
// Write the key.
if (!Res_INTERNALID(key.id.id) && !key.id.isValid()) {
+ assert(key.name.isValid());
mSymbols->push_back(std::make_pair(ResourceNameRef(key.name),
mOut->size() - sizeof(*outMapEntry)));
}
@@ -96,6 +97,23 @@ public:
outMapEntry->value.size = sizeof(outMapEntry->value);
}
+ void flattenValueOnly(const Item& value) {
+ mMap->count++;
+
+ android::ResTable_map* outMapEntry = mOut->nextBlock<android::ResTable_map>();
+
+ // Write the value.
+ value.flatten(outMapEntry->value);
+
+ if (outMapEntry->value.data == 0x0) {
+ visitFunc<Reference>(value, [&](const Reference& reference) {
+ mSymbols->push_back(std::make_pair(ResourceNameRef(reference.name),
+ mOut->size() - sizeof(outMapEntry->value.data)));
+ });
+ }
+ outMapEntry->value.size = sizeof(outMapEntry->value);
+ }
+
static bool compareStyleEntries(const Style::Entry* lhs, const Style::Entry* rhs) {
return lhs->key.id < rhs->key.id;
}
@@ -139,7 +157,7 @@ public:
void visit(const Array& array, ValueVisitorArgs&) override {
for (const auto& item : array.items) {
- flattenEntry({}, *item);
+ flattenValueOnly(*item);
}
}
@@ -334,6 +352,10 @@ bool TableFlattener::flatten(BigBuffer* out, const ResourceTable& table) {
spec->id = type->typeId;
spec->entryCount = type->entries.size();
+ if (type->entries.empty()) {
+ continue;
+ }
+
// Reserve space for the masks of each resource in this type. These
// show for which configuration axis the resource changes.
uint32_t* configMasks = typeBlock.nextBlock<uint32_t>(type->entries.size());
diff --git a/tools/aapt2/XmlFlattener.cpp b/tools/aapt2/XmlFlattener.cpp
index 650e624..97e4f68 100644
--- a/tools/aapt2/XmlFlattener.cpp
+++ b/tools/aapt2/XmlFlattener.cpp
@@ -300,6 +300,7 @@ Maybe<size_t> XmlFlattener::flatten(const Source& source,
elem->attributeCount = sortedAttributes.size();
// Flatten the sorted attributes.
+ uint16_t attributeIndex = 1;
for (auto entry : sortedAttributes) {
android::ResXMLTree_attribute* attr =
out.nextBlock<android::ResXMLTree_attribute>();
@@ -310,44 +311,64 @@ Maybe<size_t> XmlFlattener::flatten(const Source& source,
attr->ns.index = -1;
}
+ StringPool::Ref rawValueRef = pool.makeRef(entry.xmlAttr->value, lowPriority);
+
stringRefs.emplace_back(entry.nameRef, &attr->name);
- attr->rawValue.index = -1;
- StringPool::Ref rawValueRef = pool.makeRef(entry.xmlAttr->value, lowPriority);
+ if (options.keepRawValues) {
+ stringRefs.emplace_back(rawValueRef, &attr->rawValue);
+ } else {
+ attr->rawValue.index = -1;
+ }
- if (entry.attr) {
- std::unique_ptr<Item> value = ResourceParser::parseItemForAttribute(
- entry.xmlAttr->value, *entry.attr);
- if (value) {
- AttributeValueFlattener flattener(
- mResolver,
- &logger,
- &attr->typedValue,
- parser,
- &error,
- rawValueRef,
- &options.defaultPackage,
- &stringRefs);
- value->accept(flattener, {});
- } else if (!(entry.attr->typeMask & android::ResTable_map::TYPE_STRING)) {
- logger.error(parser->getLineNumber())
- << "'"
- << *rawValueRef
- << "' is not compatible with attribute "
- << *entry.attr
- << "."
- << std::endl;
- error = true;
- } else {
- attr->typedValue.dataType = android::Res_value::TYPE_STRING;
- stringRefs.emplace_back(rawValueRef, &attr->rawValue);
- stringRefs.emplace_back(rawValueRef,
- reinterpret_cast<android::ResStringPool_ref*>(
- &attr->typedValue.data));
+ // Assign the indices for specific attributes.
+ if (entry.xmlAttr->namespaceUri == kSchemaAndroid &&
+ entry.xmlAttr->name == u"id") {
+ elem->idIndex = attributeIndex;
+ } else if (entry.xmlAttr->namespaceUri.empty()) {
+ if (entry.xmlAttr->name == u"class") {
+ elem->classIndex = attributeIndex;
+ } else if (entry.xmlAttr->name == u"style") {
+ elem->styleIndex = attributeIndex;
}
+ }
+
+ std::unique_ptr<Item> value;
+ if (entry.attr) {
+ value = ResourceParser::parseItemForAttribute(entry.xmlAttr->value,
+ *entry.attr);
+ } else {
+ bool create = false;
+ value = ResourceParser::tryParseReference(entry.xmlAttr->value, &create);
+ }
+
+ if (mResolver && value) {
+ AttributeValueFlattener flattener(
+ mResolver,
+ &logger,
+ &attr->typedValue,
+ parser,
+ &error,
+ rawValueRef,
+ &options.defaultPackage,
+ &stringRefs);
+ value->accept(flattener, {});
+ } else if (!value && entry.attr &&
+ !(entry.attr->typeMask & android::ResTable_map::TYPE_STRING)) {
+ logger.error(parser->getLineNumber())
+ << "'"
+ << *rawValueRef
+ << "' is not compatible with attribute "
+ << *entry.attr
+ << "."
+ << std::endl;
+ error = true;
} else {
attr->typedValue.dataType = android::Res_value::TYPE_STRING;
- stringRefs.emplace_back(rawValueRef, &attr->rawValue);
+ if (!options.keepRawValues) {
+ // Don't set the string twice.
+ stringRefs.emplace_back(rawValueRef, &attr->rawValue);
+ }
stringRefs.emplace_back(rawValueRef,
reinterpret_cast<android::ResStringPool_ref*>(
&attr->typedValue.data));
@@ -440,6 +461,9 @@ Maybe<size_t> XmlFlattener::flatten(const Source& source,
header->header.type = android::RES_XML_TYPE;
header->header.headerSize = sizeof(*header);
+ // Flatten the StringPool.
+ StringPool::flattenUtf16(outBuffer, pool);
+
// Write the array of resource IDs, indexed by StringPool order.
const size_t beforeResIdMapIndex = outBuffer->size();
android::ResChunk_header* resIdMapChunk = outBuffer->nextBlock<android::ResChunk_header>();
@@ -458,10 +482,7 @@ Maybe<size_t> XmlFlattener::flatten(const Source& source,
}
resIdMapChunk->size = outBuffer->size() - beforeResIdMapIndex;
- // Flatten the StringPool.
- StringPool::flattenUtf16(outBuffer, pool);
-
- // Move the temporary BigBuffer into outBuffer->
+ // Move the temporary BigBuffer into outBuffer.
outBuffer->appendBuffer(std::move(out));
header->header.size = outBuffer->size() - beforeXmlTreeIndex;
diff --git a/tools/aapt2/XmlFlattener.h b/tools/aapt2/XmlFlattener.h
index 60a500e..2cfcc16 100644
--- a/tools/aapt2/XmlFlattener.h
+++ b/tools/aapt2/XmlFlattener.h
@@ -47,6 +47,12 @@ public:
* max SDK.
*/
Maybe<size_t> maxSdkAttribute;
+
+ /**
+ * Setting this to true will keep the raw string value of
+ * an attribute's value when it has been resolved.
+ */
+ bool keepRawValues = false;
};
/**