summaryrefslogtreecommitdiffstats
path: root/tools/aapt2/Main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/aapt2/Main.cpp')
-rw-r--r--tools/aapt2/Main.cpp344
1 files changed, 166 insertions, 178 deletions
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)) {