Loading tools/aapt2/BinaryResourceParser.cpp +7 −0 Original line number Diff line number Diff line Loading @@ -603,6 +603,13 @@ std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& na mTable->getValueStringPool().makeRef( styleStr, StringPool::Context{1, config})); } else { if (name.type != ResourceType::kString && util::stringStartsWith<char16_t>(str, u"res/")) { // This must be a FileReference. return util::make_unique<FileReference>(mTable->getValueStringPool().makeRef( str, StringPool::Context{ 0, config })); } // There are no styles associated with this string, so treat it as // a simple string. return util::make_unique<String>( Loading tools/aapt2/Main.cpp +201 −141 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include "Linker.h" #include "ManifestParser.h" #include "ManifestValidator.h" #include "NameMangler.h" #include "Png.h" #include "ResourceParser.h" #include "ResourceTable.h" Loading Loading @@ -266,22 +267,50 @@ struct CompileItem { struct LinkItem { Source source; std::string apkPath; ResourceName name; ConfigDescription config; std::string originalPath; ZipFile* apk; }; std::string buildFileReference(const CompileItem& item) { template <typename TChar> static BasicStringPiece<TChar> getExtension(const BasicStringPiece<TChar>& str) { auto iter = std::find(str.begin(), str.end(), static_cast<TChar>('.')); if (iter == str.end()) { return BasicStringPiece<TChar>(); } size_t offset = (iter - str.begin()) + 1; return str.substr(offset, str.size() - offset); } std::string buildFileReference(const ResourceNameRef& name, const ConfigDescription& config, const StringPiece& extension) { std::stringstream path; path << "res/" << item.name.type; if (item.config != ConfigDescription{}) { path << "-" << item.config; path << "res/" << name.type; if (config != ConfigDescription{}) { path << "-" << config; } path << "/" << util::utf16ToUtf8(name.entry); if (!extension.empty()) { path << "." << extension; } path << "/" << util::utf16ToUtf8(item.name.entry) + "." + item.extension; return path.str(); } std::string buildFileReference(const CompileItem& item) { return buildFileReference(item.name, item.config, item.extension); } std::string buildFileReference(const LinkItem& item) { return buildFileReference(item.name, item.config, getExtension<char>(item.originalPath)); } bool addFileReference(const std::shared_ptr<ResourceTable>& table, const CompileItem& item) { StringPool& pool = table->getValueStringPool(); StringPool::Ref ref = pool.makeRef(util::utf8ToUtf16(buildFileReference(item))); StringPool::Ref ref = pool.makeRef(util::utf8ToUtf16(buildFileReference(item)), StringPool::Context{ 0, item.config }); return table->addResource(item.name, item.config, item.source.line(0), util::make_unique<FileReference>(ref)); } Loading Loading @@ -418,8 +447,8 @@ bool linkXml(const AaptOptions& options, const std::shared_ptr<Resolver>& resolv return false; } if (outApk->add(outBuffer, item.apkPath.data(), ZipEntry::kCompressDeflated, nullptr) != android::NO_ERROR) { 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; Loading Loading @@ -502,109 +531,6 @@ bool compileManifest(const AaptOptions& options, const std::shared_ptr<Resolver> return true; } bool loadAppInfo(const Source& source, AppInfo* outInfo) { std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary); if (!ifs) { Logger::error(source) << strerror(errno) << std::endl; return false; } ManifestParser parser; std::shared_ptr<XmlPullParser> pullParser = std::make_shared<SourceXmlPullParser>(ifs); return parser.parse(source, pullParser, outInfo); } 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 << std::endl; std::cerr << "run aapt2 with one of the commands and the -h flag for extra details." << std::endl; exit(1); } static AaptOptions prepareArgs(int argc, char** argv) { if (argc < 2) { std::cerr << "no command specified." << std::endl << std::endl; printCommandsAndDie(); } const StringPiece command(argv[1]); argc -= 2; argv += 2; AaptOptions options; if (command == "--version" || command == "version") { std::cout << kAaptVersionStr << std::endl; exit(0); } else if (command == "link") { options.phase = AaptOptions::Phase::Link; } else if (command == "compile") { options.phase = AaptOptions::Phase::Compile; } else { std::cerr << "invalid command '" << command << "'." << std::endl << std::endl; printCommandsAndDie(); } 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() }); }); flag::optionalFlag("--java", "directory in which to generate R.java", [&options](const StringPiece& arg) { options.generateJavaClass = 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); // Build the command string for output (eg. "aapt2 compile"). std::string fullCommand = "aapt2"; fullCommand += " "; fullCommand += command.toString(); // Actually read the command line flags. flag::parse(argc, argv, fullCommand); if (help) { flag::usageAndDie(fullCommand); } // Copy all the remaining arguments. for (const std::string& arg : flag::getArgs()) { options.input.push_back(Source{ arg }); } return options; } static bool compileValues(const std::shared_ptr<ResourceTable>& table, const Source& source, const ConfigDescription& config) { std::ifstream in(source.path, std::ifstream::binary); Loading @@ -630,6 +556,7 @@ struct ResourcePathData { * [--/res/]type[-config]/name */ static Maybe<ResourcePathData> extractResourcePathData(const Source& source) { // TODO(adamlesinski): Use Windows path separator on windows. std::vector<std::string> parts = util::splitAndLowercase(source.path, '/'); if (parts.size() < 2) { Logger::error(source) << "bad resource path." << std::endl; Loading Loading @@ -695,6 +622,38 @@ bool writeResourceTable(const AaptOptions& options, const std::shared_ptr<Resour return true; } /** * For each FileReference in the table, adds a LinkItem to the link queue for processing. */ static void addApkFilesToLinkQueue(const std::u16string& package, const Source& source, const std::shared_ptr<ResourceTable>& table, const std::unique_ptr<ZipFile>& apk, std::queue<LinkItem>* outLinkQueue) { bool mangle = package != table->getPackage(); for (auto& type : *table) { for (auto& entry : type->entries) { ResourceName name = { package, type->type, entry->name }; if (mangle) { NameMangler::mangle(table->getPackage(), &name.entry); } for (auto& value : entry->values) { visitFunc<FileReference>(*value.value, [&](FileReference& ref) { std::string pathUtf8 = util::utf16ToUtf8(*ref.path); outLinkQueue->push(LinkItem{ source, name, value.config, pathUtf8, apk.get() }); // Now rewrite the file path. if (mangle) { ref.path = table->getValueStringPool().makeRef(util::utf8ToUtf16( buildFileReference(name, value.config, getExtension<char>(pathUtf8)))); } }); } } } } static constexpr int kOpenFlags = ZipFile::kOpenCreate | ZipFile::kOpenTruncate | ZipFile::kOpenReadWrite; Loading Loading @@ -740,9 +699,14 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT linkedPackages.insert(table->getPackage()); } std::queue<LinkItem> linkQueue; for (auto& p : apkFiles) { const std::shared_ptr<ResourceTable>& inTable = p.first; // Collect all FileReferences and add them to the queue for processing. addApkFilesToLinkQueue(options.appInfo.package, Source{}, inTable, p.second, &linkQueue); // Merge the tables. if (!outTable->merge(std::move(*inTable))) { return false; } Loading Loading @@ -779,42 +743,35 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT return false; } for (auto& p : apkFiles) { std::unique_ptr<ZipFile>& zipFile = p.second; // TODO(adamlesinski): Get list of files to read when processing config filter. for (; !linkQueue.empty(); linkQueue.pop()) { const LinkItem& item = linkQueue.front(); const int numEntries = zipFile->getNumEntries(); for (int i = 0; i < numEntries; i++) { ZipEntry* entry = zipFile->getEntryByIndex(i); assert(entry); StringPiece filename = entry->getFileName(); if (!util::stringStartsWith<char>(filename, "res/")) { continue; ZipEntry* entry = item.apk->getEntryByName(item.originalPath.data()); if (!entry) { Logger::error(item.source) << "failed to find '" << item.originalPath << "'." << std::endl; return false; } if (util::stringEndsWith<char>(filename, ".xml")) { void* uncompressedData = zipFile->uncompress(entry); if (util::stringEndsWith<char>(item.originalPath, ".xml")) { void* uncompressedData = item.apk->uncompress(entry); assert(uncompressedData); LinkItem item = { Source{ filename.toString() }, filename.toString() }; if (!linkXml(options, resolver, item, uncompressedData, entry->getUncompressedLen(), &outApk)) { Logger::error(options.output) << "failed to link '" << filename << "'." if (!linkXml(options, resolver, item, uncompressedData, entry->getUncompressedLen(), &outApk)) { Logger::error(options.output) << "failed to link '" << item.originalPath << "'." << std::endl; return false; } } else { if (outApk.add(zipFile.get(), entry, 0, nullptr) != android::NO_ERROR) { Logger::error(options.output) << "failed to copy '" << filename << "'." if (outApk.add(item.apk, entry, buildFileReference(item).data(), 0, nullptr) != android::NO_ERROR) { Logger::error(options.output) << "failed to copy '" << item.originalPath << "'." << std::endl; return false; } } } } // Generate the Java class file. if (options.generateJavaClass) { Loading Loading @@ -957,6 +914,109 @@ bool compile(const AaptOptions& options, const std::shared_ptr<ResourceTable>& t return true; } bool loadAppInfo(const Source& source, AppInfo* outInfo) { std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary); if (!ifs) { Logger::error(source) << strerror(errno) << std::endl; return false; } ManifestParser parser; std::shared_ptr<XmlPullParser> pullParser = std::make_shared<SourceXmlPullParser>(ifs); return parser.parse(source, pullParser, outInfo); } 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 << std::endl; std::cerr << "run aapt2 with one of the commands and the -h flag for extra details." << std::endl; exit(1); } static AaptOptions prepareArgs(int argc, char** argv) { if (argc < 2) { std::cerr << "no command specified." << std::endl << std::endl; printCommandsAndDie(); } const StringPiece command(argv[1]); argc -= 2; argv += 2; AaptOptions options; if (command == "--version" || command == "version") { std::cout << kAaptVersionStr << std::endl; exit(0); } else if (command == "link") { options.phase = AaptOptions::Phase::Link; } else if (command == "compile") { options.phase = AaptOptions::Phase::Compile; } else { std::cerr << "invalid command '" << command << "'." << std::endl << std::endl; printCommandsAndDie(); } 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() }); }); flag::optionalFlag("--java", "directory in which to generate R.java", [&options](const StringPiece& arg) { options.generateJavaClass = 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); // Build the command string for output (eg. "aapt2 compile"). std::string fullCommand = "aapt2"; fullCommand += " "; fullCommand += command.toString(); // Actually read the command line flags. flag::parse(argc, argv, fullCommand); if (help) { flag::usageAndDie(fullCommand); } // Copy all the remaining arguments. for (const std::string& arg : flag::getArgs()) { options.input.push_back(Source{ arg }); } return options; } int main(int argc, char** argv) { Logger::setLog(std::make_shared<Log>(std::cerr, std::cerr)); AaptOptions options = prepareArgs(argc, argv); Loading tools/aapt2/TableFlattener.cpp +44 −28 Original line number Diff line number Diff line Loading @@ -43,8 +43,7 @@ struct FlatEntry { */ class MapFlattener : public ConstValueVisitor { public: MapFlattener(BigBuffer* out, const FlatEntry& flatEntry, std::vector<std::pair<ResourceNameRef, uint32_t>>& symbols) : MapFlattener(BigBuffer* out, const FlatEntry& flatEntry, SymbolEntryVector* symbols) : mOut(out), mSymbols(symbols) { mMap = mOut->nextBlock<android::ResTable_map_entry>(); mMap->key.index = flatEntry.entryKey; Loading @@ -65,7 +64,7 @@ public: void flattenParent(const Reference& ref) { if (!ref.id.isValid()) { mSymbols.push_back({ mSymbols->push_back({ ResourceNameRef(ref.name), (mOut->size() - mMap->size) + sizeof(*mMap) - sizeof(android::ResTable_entry) }); Loading @@ -80,7 +79,7 @@ public: // Write the key. if (!Res_INTERNALID(key.id.id) && !key.id.isValid()) { mSymbols.push_back(std::make_pair(ResourceNameRef(key.name), mSymbols->push_back(std::make_pair(ResourceNameRef(key.name), mOut->size() - sizeof(*outMapEntry))); } outMapEntry->name.ident = key.id.id; Loading @@ -90,7 +89,7 @@ public: if (outMapEntry->value.data == 0x0) { visitFunc<Reference>(value, [&](const Reference& reference) { mSymbols.push_back(std::make_pair(ResourceNameRef(reference.name), mSymbols->push_back(std::make_pair(ResourceNameRef(reference.name), mOut->size() - sizeof(outMapEntry->value.data))); }); } Loading Loading @@ -188,16 +187,47 @@ public: private: BigBuffer* mOut; std::vector<std::pair<ResourceNameRef, uint32_t>>& mSymbols; SymbolEntryVector* mSymbols; android::ResTable_map_entry* mMap; }; /** * Flattens a value, with special handling for References. */ struct ValueFlattener : ConstValueVisitor { ValueFlattener(BigBuffer* out, SymbolEntryVector* symbols) : result(false), mOut(out), mOutValue(nullptr), mSymbols(symbols) { mOutValue = mOut->nextBlock<android::Res_value>(); } virtual void visit(const Reference& ref, ValueVisitorArgs& a) override { visitItem(ref, a); if (mOutValue->data == 0x0) { mSymbols->push_back({ ResourceNameRef(ref.name), mOut->size() - sizeof(mOutValue->data)}); } } virtual void visitItem(const Item& item, ValueVisitorArgs&) override { result = item.flatten(*mOutValue); mOutValue->size = sizeof(*mOutValue); } bool result; private: BigBuffer* mOut; android::Res_value* mOutValue; SymbolEntryVector* mSymbols; }; TableFlattener::TableFlattener(Options options) : mOptions(options) { } bool TableFlattener::flattenValue(BigBuffer* out, const FlatEntry& flatEntry, std::vector<std::pair<ResourceNameRef, uint32_t>>& symbolEntries) { SymbolEntryVector* symbols) { if (flatEntry.value.isItem()) { android::ResTable_entry* entry = out->nextBlock<android::ResTable_entry>(); Loading @@ -218,30 +248,16 @@ bool TableFlattener::flattenValue(BigBuffer* out, const FlatEntry& flatEntry, ResTable_entry_source* sourceBlock = out->nextBlock<ResTable_entry_source>(); sourceBlock->pathIndex = flatEntry.sourcePathKey; sourceBlock->line = flatEntry.sourceLine; entry->size += sizeof(*sourceBlock); } android::Res_value* outValue = out->nextBlock<android::Res_value>(); const Item& item = static_cast<const Item&>(flatEntry.value); if (!item.flatten(*outValue)) { return false; } if (outValue->data == 0x0) { visitFunc<Reference>(item, [&](const Reference& reference) { symbolEntries.push_back({ ResourceNameRef(reference.name), out->size() - sizeof(outValue->data) }); }); } outValue->size = sizeof(*outValue); return true; const Item* item = static_cast<const Item*>(&flatEntry.value); ValueFlattener flattener(out, symbols); item->accept(flattener, {}); return flattener.result; } MapFlattener flattener(out, flatEntry, symbolEntries); MapFlattener flattener(out, flatEntry, symbols); flatEntry.value.accept(flattener, {}); return true; } Loading @@ -263,7 +279,7 @@ bool TableFlattener::flatten(BigBuffer* out, const ResourceTable& table) { return false; } std::vector<std::pair<ResourceNameRef, uint32_t>> symbolEntries; SymbolEntryVector symbolEntries; StringPool typePool; StringPool keyPool; Loading Loading @@ -401,7 +417,7 @@ bool TableFlattener::flatten(BigBuffer* out, const ResourceTable& table) { for (const FlatEntry& flatEntry : entry.second) { assert(flatEntry.entry.entryId < type->entries.size()); indices[flatEntry.entry.entryId] = typeBlock.size() - entryStart; if (!flattenValue(&typeBlock, flatEntry, symbolEntries)) { if (!flattenValue(&typeBlock, flatEntry, &symbolEntries)) { Logger::error() << "failed to flatten resource '" << ResourceNameRef { Loading tools/aapt2/TableFlattener.h +3 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ namespace aapt { using SymbolEntryVector = std::vector<std::pair<ResourceNameRef, uint32_t>>; struct FlatEntry; /** Loading Loading @@ -49,8 +51,7 @@ struct TableFlattener { bool flatten(BigBuffer* out, const ResourceTable& table); private: bool flattenValue(BigBuffer* out, const FlatEntry& flatEntry, std::vector<std::pair<ResourceNameRef, uint32_t>>& symbolEntries); bool flattenValue(BigBuffer* out, const FlatEntry& flatEntry, SymbolEntryVector* symbols); Options mOptions; }; Loading tools/aapt2/ZipEntry.cpp +7 −1 Original line number Diff line number Diff line Loading @@ -144,9 +144,15 @@ void ZipEntry::initNew(const char* fileName, const char* comment) * Initializes the CDE and the LFH. */ status_t ZipEntry::initFromExternal(const ZipFile* /* pZipFile */, const ZipEntry* pEntry) const ZipEntry* pEntry, const char* storageName) { mCDE = pEntry->mCDE; if (storageName && *storageName != 0) { mCDE.mFileNameLength = strlen(storageName); mCDE.mFileName = new unsigned char[mCDE.mFileNameLength + 1]; strcpy((char*) mCDE.mFileName, storageName); } // Check whether we got all the memory needed. if ((mCDE.mFileNameLength > 0 && mCDE.mFileName == NULL) || (mCDE.mFileCommentLength > 0 && mCDE.mFileComment == NULL) || Loading Loading
tools/aapt2/BinaryResourceParser.cpp +7 −0 Original line number Diff line number Diff line Loading @@ -603,6 +603,13 @@ std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& na mTable->getValueStringPool().makeRef( styleStr, StringPool::Context{1, config})); } else { if (name.type != ResourceType::kString && util::stringStartsWith<char16_t>(str, u"res/")) { // This must be a FileReference. return util::make_unique<FileReference>(mTable->getValueStringPool().makeRef( str, StringPool::Context{ 0, config })); } // There are no styles associated with this string, so treat it as // a simple string. return util::make_unique<String>( Loading
tools/aapt2/Main.cpp +201 −141 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include "Linker.h" #include "ManifestParser.h" #include "ManifestValidator.h" #include "NameMangler.h" #include "Png.h" #include "ResourceParser.h" #include "ResourceTable.h" Loading Loading @@ -266,22 +267,50 @@ struct CompileItem { struct LinkItem { Source source; std::string apkPath; ResourceName name; ConfigDescription config; std::string originalPath; ZipFile* apk; }; std::string buildFileReference(const CompileItem& item) { template <typename TChar> static BasicStringPiece<TChar> getExtension(const BasicStringPiece<TChar>& str) { auto iter = std::find(str.begin(), str.end(), static_cast<TChar>('.')); if (iter == str.end()) { return BasicStringPiece<TChar>(); } size_t offset = (iter - str.begin()) + 1; return str.substr(offset, str.size() - offset); } std::string buildFileReference(const ResourceNameRef& name, const ConfigDescription& config, const StringPiece& extension) { std::stringstream path; path << "res/" << item.name.type; if (item.config != ConfigDescription{}) { path << "-" << item.config; path << "res/" << name.type; if (config != ConfigDescription{}) { path << "-" << config; } path << "/" << util::utf16ToUtf8(name.entry); if (!extension.empty()) { path << "." << extension; } path << "/" << util::utf16ToUtf8(item.name.entry) + "." + item.extension; return path.str(); } std::string buildFileReference(const CompileItem& item) { return buildFileReference(item.name, item.config, item.extension); } std::string buildFileReference(const LinkItem& item) { return buildFileReference(item.name, item.config, getExtension<char>(item.originalPath)); } bool addFileReference(const std::shared_ptr<ResourceTable>& table, const CompileItem& item) { StringPool& pool = table->getValueStringPool(); StringPool::Ref ref = pool.makeRef(util::utf8ToUtf16(buildFileReference(item))); StringPool::Ref ref = pool.makeRef(util::utf8ToUtf16(buildFileReference(item)), StringPool::Context{ 0, item.config }); return table->addResource(item.name, item.config, item.source.line(0), util::make_unique<FileReference>(ref)); } Loading Loading @@ -418,8 +447,8 @@ bool linkXml(const AaptOptions& options, const std::shared_ptr<Resolver>& resolv return false; } if (outApk->add(outBuffer, item.apkPath.data(), ZipEntry::kCompressDeflated, nullptr) != android::NO_ERROR) { 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; Loading Loading @@ -502,109 +531,6 @@ bool compileManifest(const AaptOptions& options, const std::shared_ptr<Resolver> return true; } bool loadAppInfo(const Source& source, AppInfo* outInfo) { std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary); if (!ifs) { Logger::error(source) << strerror(errno) << std::endl; return false; } ManifestParser parser; std::shared_ptr<XmlPullParser> pullParser = std::make_shared<SourceXmlPullParser>(ifs); return parser.parse(source, pullParser, outInfo); } 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 << std::endl; std::cerr << "run aapt2 with one of the commands and the -h flag for extra details." << std::endl; exit(1); } static AaptOptions prepareArgs(int argc, char** argv) { if (argc < 2) { std::cerr << "no command specified." << std::endl << std::endl; printCommandsAndDie(); } const StringPiece command(argv[1]); argc -= 2; argv += 2; AaptOptions options; if (command == "--version" || command == "version") { std::cout << kAaptVersionStr << std::endl; exit(0); } else if (command == "link") { options.phase = AaptOptions::Phase::Link; } else if (command == "compile") { options.phase = AaptOptions::Phase::Compile; } else { std::cerr << "invalid command '" << command << "'." << std::endl << std::endl; printCommandsAndDie(); } 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() }); }); flag::optionalFlag("--java", "directory in which to generate R.java", [&options](const StringPiece& arg) { options.generateJavaClass = 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); // Build the command string for output (eg. "aapt2 compile"). std::string fullCommand = "aapt2"; fullCommand += " "; fullCommand += command.toString(); // Actually read the command line flags. flag::parse(argc, argv, fullCommand); if (help) { flag::usageAndDie(fullCommand); } // Copy all the remaining arguments. for (const std::string& arg : flag::getArgs()) { options.input.push_back(Source{ arg }); } return options; } static bool compileValues(const std::shared_ptr<ResourceTable>& table, const Source& source, const ConfigDescription& config) { std::ifstream in(source.path, std::ifstream::binary); Loading @@ -630,6 +556,7 @@ struct ResourcePathData { * [--/res/]type[-config]/name */ static Maybe<ResourcePathData> extractResourcePathData(const Source& source) { // TODO(adamlesinski): Use Windows path separator on windows. std::vector<std::string> parts = util::splitAndLowercase(source.path, '/'); if (parts.size() < 2) { Logger::error(source) << "bad resource path." << std::endl; Loading Loading @@ -695,6 +622,38 @@ bool writeResourceTable(const AaptOptions& options, const std::shared_ptr<Resour return true; } /** * For each FileReference in the table, adds a LinkItem to the link queue for processing. */ static void addApkFilesToLinkQueue(const std::u16string& package, const Source& source, const std::shared_ptr<ResourceTable>& table, const std::unique_ptr<ZipFile>& apk, std::queue<LinkItem>* outLinkQueue) { bool mangle = package != table->getPackage(); for (auto& type : *table) { for (auto& entry : type->entries) { ResourceName name = { package, type->type, entry->name }; if (mangle) { NameMangler::mangle(table->getPackage(), &name.entry); } for (auto& value : entry->values) { visitFunc<FileReference>(*value.value, [&](FileReference& ref) { std::string pathUtf8 = util::utf16ToUtf8(*ref.path); outLinkQueue->push(LinkItem{ source, name, value.config, pathUtf8, apk.get() }); // Now rewrite the file path. if (mangle) { ref.path = table->getValueStringPool().makeRef(util::utf8ToUtf16( buildFileReference(name, value.config, getExtension<char>(pathUtf8)))); } }); } } } } static constexpr int kOpenFlags = ZipFile::kOpenCreate | ZipFile::kOpenTruncate | ZipFile::kOpenReadWrite; Loading Loading @@ -740,9 +699,14 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT linkedPackages.insert(table->getPackage()); } std::queue<LinkItem> linkQueue; for (auto& p : apkFiles) { const std::shared_ptr<ResourceTable>& inTable = p.first; // Collect all FileReferences and add them to the queue for processing. addApkFilesToLinkQueue(options.appInfo.package, Source{}, inTable, p.second, &linkQueue); // Merge the tables. if (!outTable->merge(std::move(*inTable))) { return false; } Loading Loading @@ -779,42 +743,35 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT return false; } for (auto& p : apkFiles) { std::unique_ptr<ZipFile>& zipFile = p.second; // TODO(adamlesinski): Get list of files to read when processing config filter. for (; !linkQueue.empty(); linkQueue.pop()) { const LinkItem& item = linkQueue.front(); const int numEntries = zipFile->getNumEntries(); for (int i = 0; i < numEntries; i++) { ZipEntry* entry = zipFile->getEntryByIndex(i); assert(entry); StringPiece filename = entry->getFileName(); if (!util::stringStartsWith<char>(filename, "res/")) { continue; ZipEntry* entry = item.apk->getEntryByName(item.originalPath.data()); if (!entry) { Logger::error(item.source) << "failed to find '" << item.originalPath << "'." << std::endl; return false; } if (util::stringEndsWith<char>(filename, ".xml")) { void* uncompressedData = zipFile->uncompress(entry); if (util::stringEndsWith<char>(item.originalPath, ".xml")) { void* uncompressedData = item.apk->uncompress(entry); assert(uncompressedData); LinkItem item = { Source{ filename.toString() }, filename.toString() }; if (!linkXml(options, resolver, item, uncompressedData, entry->getUncompressedLen(), &outApk)) { Logger::error(options.output) << "failed to link '" << filename << "'." if (!linkXml(options, resolver, item, uncompressedData, entry->getUncompressedLen(), &outApk)) { Logger::error(options.output) << "failed to link '" << item.originalPath << "'." << std::endl; return false; } } else { if (outApk.add(zipFile.get(), entry, 0, nullptr) != android::NO_ERROR) { Logger::error(options.output) << "failed to copy '" << filename << "'." if (outApk.add(item.apk, entry, buildFileReference(item).data(), 0, nullptr) != android::NO_ERROR) { Logger::error(options.output) << "failed to copy '" << item.originalPath << "'." << std::endl; return false; } } } } // Generate the Java class file. if (options.generateJavaClass) { Loading Loading @@ -957,6 +914,109 @@ bool compile(const AaptOptions& options, const std::shared_ptr<ResourceTable>& t return true; } bool loadAppInfo(const Source& source, AppInfo* outInfo) { std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary); if (!ifs) { Logger::error(source) << strerror(errno) << std::endl; return false; } ManifestParser parser; std::shared_ptr<XmlPullParser> pullParser = std::make_shared<SourceXmlPullParser>(ifs); return parser.parse(source, pullParser, outInfo); } 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 << std::endl; std::cerr << "run aapt2 with one of the commands and the -h flag for extra details." << std::endl; exit(1); } static AaptOptions prepareArgs(int argc, char** argv) { if (argc < 2) { std::cerr << "no command specified." << std::endl << std::endl; printCommandsAndDie(); } const StringPiece command(argv[1]); argc -= 2; argv += 2; AaptOptions options; if (command == "--version" || command == "version") { std::cout << kAaptVersionStr << std::endl; exit(0); } else if (command == "link") { options.phase = AaptOptions::Phase::Link; } else if (command == "compile") { options.phase = AaptOptions::Phase::Compile; } else { std::cerr << "invalid command '" << command << "'." << std::endl << std::endl; printCommandsAndDie(); } 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() }); }); flag::optionalFlag("--java", "directory in which to generate R.java", [&options](const StringPiece& arg) { options.generateJavaClass = 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); // Build the command string for output (eg. "aapt2 compile"). std::string fullCommand = "aapt2"; fullCommand += " "; fullCommand += command.toString(); // Actually read the command line flags. flag::parse(argc, argv, fullCommand); if (help) { flag::usageAndDie(fullCommand); } // Copy all the remaining arguments. for (const std::string& arg : flag::getArgs()) { options.input.push_back(Source{ arg }); } return options; } int main(int argc, char** argv) { Logger::setLog(std::make_shared<Log>(std::cerr, std::cerr)); AaptOptions options = prepareArgs(argc, argv); Loading
tools/aapt2/TableFlattener.cpp +44 −28 Original line number Diff line number Diff line Loading @@ -43,8 +43,7 @@ struct FlatEntry { */ class MapFlattener : public ConstValueVisitor { public: MapFlattener(BigBuffer* out, const FlatEntry& flatEntry, std::vector<std::pair<ResourceNameRef, uint32_t>>& symbols) : MapFlattener(BigBuffer* out, const FlatEntry& flatEntry, SymbolEntryVector* symbols) : mOut(out), mSymbols(symbols) { mMap = mOut->nextBlock<android::ResTable_map_entry>(); mMap->key.index = flatEntry.entryKey; Loading @@ -65,7 +64,7 @@ public: void flattenParent(const Reference& ref) { if (!ref.id.isValid()) { mSymbols.push_back({ mSymbols->push_back({ ResourceNameRef(ref.name), (mOut->size() - mMap->size) + sizeof(*mMap) - sizeof(android::ResTable_entry) }); Loading @@ -80,7 +79,7 @@ public: // Write the key. if (!Res_INTERNALID(key.id.id) && !key.id.isValid()) { mSymbols.push_back(std::make_pair(ResourceNameRef(key.name), mSymbols->push_back(std::make_pair(ResourceNameRef(key.name), mOut->size() - sizeof(*outMapEntry))); } outMapEntry->name.ident = key.id.id; Loading @@ -90,7 +89,7 @@ public: if (outMapEntry->value.data == 0x0) { visitFunc<Reference>(value, [&](const Reference& reference) { mSymbols.push_back(std::make_pair(ResourceNameRef(reference.name), mSymbols->push_back(std::make_pair(ResourceNameRef(reference.name), mOut->size() - sizeof(outMapEntry->value.data))); }); } Loading Loading @@ -188,16 +187,47 @@ public: private: BigBuffer* mOut; std::vector<std::pair<ResourceNameRef, uint32_t>>& mSymbols; SymbolEntryVector* mSymbols; android::ResTable_map_entry* mMap; }; /** * Flattens a value, with special handling for References. */ struct ValueFlattener : ConstValueVisitor { ValueFlattener(BigBuffer* out, SymbolEntryVector* symbols) : result(false), mOut(out), mOutValue(nullptr), mSymbols(symbols) { mOutValue = mOut->nextBlock<android::Res_value>(); } virtual void visit(const Reference& ref, ValueVisitorArgs& a) override { visitItem(ref, a); if (mOutValue->data == 0x0) { mSymbols->push_back({ ResourceNameRef(ref.name), mOut->size() - sizeof(mOutValue->data)}); } } virtual void visitItem(const Item& item, ValueVisitorArgs&) override { result = item.flatten(*mOutValue); mOutValue->size = sizeof(*mOutValue); } bool result; private: BigBuffer* mOut; android::Res_value* mOutValue; SymbolEntryVector* mSymbols; }; TableFlattener::TableFlattener(Options options) : mOptions(options) { } bool TableFlattener::flattenValue(BigBuffer* out, const FlatEntry& flatEntry, std::vector<std::pair<ResourceNameRef, uint32_t>>& symbolEntries) { SymbolEntryVector* symbols) { if (flatEntry.value.isItem()) { android::ResTable_entry* entry = out->nextBlock<android::ResTable_entry>(); Loading @@ -218,30 +248,16 @@ bool TableFlattener::flattenValue(BigBuffer* out, const FlatEntry& flatEntry, ResTable_entry_source* sourceBlock = out->nextBlock<ResTable_entry_source>(); sourceBlock->pathIndex = flatEntry.sourcePathKey; sourceBlock->line = flatEntry.sourceLine; entry->size += sizeof(*sourceBlock); } android::Res_value* outValue = out->nextBlock<android::Res_value>(); const Item& item = static_cast<const Item&>(flatEntry.value); if (!item.flatten(*outValue)) { return false; } if (outValue->data == 0x0) { visitFunc<Reference>(item, [&](const Reference& reference) { symbolEntries.push_back({ ResourceNameRef(reference.name), out->size() - sizeof(outValue->data) }); }); } outValue->size = sizeof(*outValue); return true; const Item* item = static_cast<const Item*>(&flatEntry.value); ValueFlattener flattener(out, symbols); item->accept(flattener, {}); return flattener.result; } MapFlattener flattener(out, flatEntry, symbolEntries); MapFlattener flattener(out, flatEntry, symbols); flatEntry.value.accept(flattener, {}); return true; } Loading @@ -263,7 +279,7 @@ bool TableFlattener::flatten(BigBuffer* out, const ResourceTable& table) { return false; } std::vector<std::pair<ResourceNameRef, uint32_t>> symbolEntries; SymbolEntryVector symbolEntries; StringPool typePool; StringPool keyPool; Loading Loading @@ -401,7 +417,7 @@ bool TableFlattener::flatten(BigBuffer* out, const ResourceTable& table) { for (const FlatEntry& flatEntry : entry.second) { assert(flatEntry.entry.entryId < type->entries.size()); indices[flatEntry.entry.entryId] = typeBlock.size() - entryStart; if (!flattenValue(&typeBlock, flatEntry, symbolEntries)) { if (!flattenValue(&typeBlock, flatEntry, &symbolEntries)) { Logger::error() << "failed to flatten resource '" << ResourceNameRef { Loading
tools/aapt2/TableFlattener.h +3 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ namespace aapt { using SymbolEntryVector = std::vector<std::pair<ResourceNameRef, uint32_t>>; struct FlatEntry; /** Loading Loading @@ -49,8 +51,7 @@ struct TableFlattener { bool flatten(BigBuffer* out, const ResourceTable& table); private: bool flattenValue(BigBuffer* out, const FlatEntry& flatEntry, std::vector<std::pair<ResourceNameRef, uint32_t>>& symbolEntries); bool flattenValue(BigBuffer* out, const FlatEntry& flatEntry, SymbolEntryVector* symbols); Options mOptions; }; Loading
tools/aapt2/ZipEntry.cpp +7 −1 Original line number Diff line number Diff line Loading @@ -144,9 +144,15 @@ void ZipEntry::initNew(const char* fileName, const char* comment) * Initializes the CDE and the LFH. */ status_t ZipEntry::initFromExternal(const ZipFile* /* pZipFile */, const ZipEntry* pEntry) const ZipEntry* pEntry, const char* storageName) { mCDE = pEntry->mCDE; if (storageName && *storageName != 0) { mCDE.mFileNameLength = strlen(storageName); mCDE.mFileName = new unsigned char[mCDE.mFileNameLength + 1]; strcpy((char*) mCDE.mFileName, storageName); } // Check whether we got all the memory needed. if ((mCDE.mFileNameLength > 0 && mCDE.mFileName == NULL) || (mCDE.mFileCommentLength > 0 && mCDE.mFileComment == NULL) || Loading