diff options
author | 2017-04-03 18:12:45 -0700 | |
---|---|---|
committer | 2017-04-10 11:59:18 -0700 | |
commit | d0f492db038c6210c1138865d816bfb134376538 (patch) | |
tree | 1d4ed362037758111a28ecb453d527403d5d6815 | |
parent | 0015a153536c51cdd66dce5e9f08d19eb517fab8 (diff) |
AAPT2: Share split functionality between link and optimize
Generating splits should be possible to do from the optimize command.
This means that a lot of infrastructure around split APKs can be
shared by both the optimize and link phase.
Bug: 35925830
Change-Id: Ia88b9e4bff300a56353b2f7a4a2547c8eb43a299
Test: manual
25 files changed, 1513 insertions, 1071 deletions
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index ef3797c4a277..b460258248af 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -15,11 +15,12 @@ // toolSources = [ - "compile/Compile.cpp", - "diff/Diff.cpp", - "dump/Dump.cpp", - "link/Link.cpp", - "optimize/Optimize.cpp", + "cmd/Compile.cpp", + "cmd/Diff.cpp", + "cmd/Dump.cpp", + "cmd/Link.cpp", + "cmd/Optimize.cpp", + "cmd/Util.cpp", ] cc_defaults { @@ -90,7 +91,7 @@ cc_library_host_static { "io/BigBufferStreams.cpp", "io/File.cpp", "io/FileSystem.cpp", - "io/Io.cpp", + "io/Util.cpp", "io/ZipArchive.cpp", "link/AutoVersioner.cpp", "link/ManifestFixer.cpp", diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h index 9db21aadb242..d6f599520d71 100644 --- a/tools/aapt2/AppInfo.h +++ b/tools/aapt2/AppInfo.h @@ -29,7 +29,7 @@ struct AppInfo { std::string package; // The app's minimum SDK version, if it is defined. - Maybe<std::string> min_sdk_version; + Maybe<int> min_sdk_version; // The app's version code, if it is defined. Maybe<uint32_t> version_code; diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp index b855f8f80c58..8a8f8be205e7 100644 --- a/tools/aapt2/LoadedApk.cpp +++ b/tools/aapt2/LoadedApk.cpp @@ -21,6 +21,7 @@ #include "flatten/Archive.h" #include "flatten/TableFlattener.h" #include "io/BigBufferInputStream.h" +#include "io/Util.h" namespace aapt { @@ -47,11 +48,10 @@ std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context, } std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>(); - BinaryResourceParser parser(context, table.get(), source, data->data(), data->size()); + BinaryResourceParser parser(context, table.get(), source, data->data(), data->size(), apk.get()); if (!parser.Parse()) { return {}; } - return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table)); } @@ -100,20 +100,16 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOption } io::BigBufferInputStream input_stream(&buffer); - if (!writer->WriteFile(path, ArchiveEntry::kAlign, &input_stream)) { - context->GetDiagnostics()->Error(DiagMessage() - << "Error when writing file '" << path << "' in APK."); + if (!io::CopyInputStreamToArchive(context, &input_stream, path, ArchiveEntry::kAlign, + writer)) { return false; } - continue; - } - std::unique_ptr<io::IData> data = file->OpenAsData(); - uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u; - if (!writer->WriteFile(path, compression_flags, data.get())) { - context->GetDiagnostics()->Error(DiagMessage() - << "Error when writing file '" << path << "' in APK."); - return false; + } else { + uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u; + if (!io::CopyFileToArchive(context, file, path, compression_flags, writer)) { + return false; + } } } return true; diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index 36c1de6a4f26..87fda16ee956 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -25,7 +25,7 @@ namespace aapt { static const char* sMajorVersion = "2"; // Update minor version whenever a feature or flag is added. -static const char* sMinorVersion = "12"; +static const char* sMinorVersion = "13"; int PrintVersion() { std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "." diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index ca6738b16911..1bb7d9beee45 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -675,5 +675,65 @@ std::string BuildResourceFileName(const ResourceFile& res_file, return out.str(); } +std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const ConfigDescription& config, + const android::ResStringPool& src_pool, + const android::Res_value& res_value, + StringPool* dst_pool) { + if (type == ResourceType::kId) { + return util::make_unique<Id>(); + } + + const uint32_t data = util::DeviceToHost32(res_value.data); + switch (res_value.dataType) { + case android::Res_value::TYPE_STRING: { + const std::string str = util::GetString(src_pool, data); + const android::ResStringPool_span* spans = src_pool.styleAt(data); + + // Check if the string has a valid style associated with it. + if (spans != nullptr && spans->name.index != android::ResStringPool_span::END) { + StyleString style_str = {str}; + while (spans->name.index != android::ResStringPool_span::END) { + style_str.spans.push_back(Span{util::GetString(src_pool, spans->name.index), + spans->firstChar, spans->lastChar}); + spans++; + } + return util::make_unique<StyledString>(dst_pool->MakeRef( + style_str, StringPool::Context(StringPool::Context::kStylePriority, config))); + } else { + if (type != ResourceType::kString && util::StartsWith(str, "res/")) { + // This must be a FileReference. + return util::make_unique<FileReference>(dst_pool->MakeRef( + str, StringPool::Context(StringPool::Context::kHighPriority, config))); + } + + // There are no styles associated with this string, so treat it as a simple string. + return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config))); + } + } break; + + case android::Res_value::TYPE_REFERENCE: + case android::Res_value::TYPE_ATTRIBUTE: + case android::Res_value::TYPE_DYNAMIC_REFERENCE: + case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE: { + Reference::Type ref_type = Reference::Type::kResource; + if (res_value.dataType == android::Res_value::TYPE_ATTRIBUTE || + res_value.dataType == android::Res_value::TYPE_DYNAMIC_ATTRIBUTE) { + ref_type = Reference::Type::kAttribute; + } + + if (data == 0) { + // A reference of 0, must be the magic @null reference. + return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_REFERENCE, 0u); + } + + // This is a normal reference. + return util::make_unique<Reference>(data, ref_type); + } break; + } + + // Treat this as a raw binary primitive. + return util::make_unique<BinaryPrimitive>(res_value); +} + } // namespace ResourceUtils } // namespace aapt diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h index 59b78f4b3f33..48922b72fefa 100644 --- a/tools/aapt2/ResourceUtils.h +++ b/tools/aapt2/ResourceUtils.h @@ -20,11 +20,13 @@ #include <functional> #include <memory> +#include "androidfw/ResourceTypes.h" #include "androidfw/StringPiece.h" #include "NameMangler.h" #include "Resource.h" #include "ResourceValues.h" +#include "StringPool.h" namespace aapt { namespace ResourceUtils { @@ -200,6 +202,13 @@ uint32_t AndroidTypeToAttributeTypeMask(uint16_t type); std::string BuildResourceFileName(const ResourceFile& res_file, const NameMangler* mangler = nullptr); +// Parses the binary form of a resource value. `type` is used as a hint to know when a value is +// an ID versus a False boolean value, etc. `config` is for sorting strings in the string pool. +std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const ConfigDescription& config, + const android::ResStringPool& src_pool, + const android::Res_value& res_value, + StringPool* dst_pool); + } // namespace ResourceUtils } // namespace aapt diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h index a626d375b625..d1232a29b5aa 100644 --- a/tools/aapt2/StringPool.h +++ b/tools/aapt2/StringPool.h @@ -23,6 +23,7 @@ #include <unordered_map> #include <vector> +#include "android-base/macros.h" #include "androidfw/StringPiece.h" #include "ConfigDescription.h" @@ -148,7 +149,8 @@ class StringPool { static bool FlattenUtf16(BigBuffer* out, const StringPool& pool); StringPool() = default; - StringPool(const StringPool&) = delete; + StringPool(StringPool&&) = default; + StringPool& operator=(StringPool&&) = default; /** * Adds a string to the pool, unless it already exists. Returns @@ -208,6 +210,8 @@ class StringPool { void Prune(); private: + DISALLOW_COPY_AND_ASSIGN(StringPool); + friend const_iterator begin(const StringPool& pool); friend const_iterator end(const StringPool& pool); diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/cmd/Compile.cpp index 1fe30f0b1478..578a8fb423dc 100644 --- a/tools/aapt2/compile/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -38,6 +38,7 @@ #include "flatten/Archive.h" #include "flatten/XmlFlattener.h" #include "io/BigBufferOutputStream.h" +#include "io/Util.h" #include "proto/ProtoSerialize.h" #include "util/Files.h" #include "util/Maybe.h" @@ -138,9 +139,8 @@ static bool IsHidden(const StringPiece& filename) { /** * Walks the res directory structure, looking for resource files. */ -static bool LoadInputFilesFromDir( - IAaptContext* context, const CompileOptions& options, - std::vector<ResourcePathData>* out_path_data) { +static bool LoadInputFilesFromDir(IAaptContext* context, const CompileOptions& options, + std::vector<ResourcePathData>* out_path_data) { const std::string& root_dir = options.res_dir.value(); std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir); if (!d) { @@ -190,8 +190,7 @@ static bool LoadInputFilesFromDir( } static bool CompileTable(IAaptContext* context, const CompileOptions& options, - const ResourcePathData& path_data, - IArchiveWriter* writer, + const ResourcePathData& path_data, IArchiveWriter* writer, const std::string& output_path) { ResourceTable table; { @@ -210,11 +209,9 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options, // If the filename includes donottranslate, then the default translatable is // false. - parser_options.translatable = - path_data.name.find("donottranslate") == std::string::npos; + parser_options.translatable = path_data.name.find("donottranslate") == std::string::npos; - ResourceParser res_parser(context->GetDiagnostics(), &table, - path_data.source, path_data.config, + ResourceParser res_parser(context->GetDiagnostics(), &table, path_data.source, path_data.config, parser_options); if (!res_parser.Parse(&xml_parser)) { return false; @@ -273,10 +270,8 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options, return true; } -static bool WriteHeaderAndBufferToWriter(const StringPiece& output_path, - const ResourceFile& file, - const BigBuffer& buffer, - IArchiveWriter* writer, +static bool WriteHeaderAndBufferToWriter(const StringPiece& output_path, const ResourceFile& file, + const BigBuffer& buffer, IArchiveWriter* writer, IDiagnostics* diag) { // Start the entry so we can write the header. if (!writer->StartEntry(output_path, 0)) { @@ -312,10 +307,8 @@ static bool WriteHeaderAndBufferToWriter(const StringPiece& output_path, return true; } -static bool WriteHeaderAndMmapToWriter(const StringPiece& output_path, - const ResourceFile& file, - const android::FileMap& map, - IArchiveWriter* writer, +static bool WriteHeaderAndMmapToWriter(const StringPiece& output_path, const ResourceFile& file, + const android::FileMap& map, IArchiveWriter* writer, IDiagnostics* diag) { // Start the entry so we can write the header. if (!writer->StartEntry(output_path, 0)) { @@ -334,8 +327,7 @@ static bool WriteHeaderAndMmapToWriter(const StringPiece& output_path, // Number of CompiledFiles. output_stream.WriteLittleEndian32(1); - std::unique_ptr<pb::CompiledFile> compiled_file = - SerializeCompiledFileToPb(file); + std::unique_ptr<pb::CompiledFile> compiled_file = SerializeCompiledFileToPb(file); output_stream.WriteCompiledFile(compiled_file.get()); output_stream.WriteData(map.getDataPtr(), map.getDataLength()); @@ -352,10 +344,8 @@ static bool WriteHeaderAndMmapToWriter(const StringPiece& output_path, return true; } -static bool FlattenXmlToOutStream(IAaptContext* context, - const StringPiece& output_path, - xml::XmlResource* xmlres, - CompiledFileOutputStream* out) { +static bool FlattenXmlToOutStream(IAaptContext* context, const StringPiece& output_path, + xml::XmlResource* xmlres, CompiledFileOutputStream* out) { BigBuffer buffer(1024); XmlFlattenerOptions xml_flattener_options; xml_flattener_options.keep_raw_values = true; @@ -376,8 +366,8 @@ static bool FlattenXmlToOutStream(IAaptContext* context, } static bool CompileXml(IAaptContext* context, const CompileOptions& options, - const ResourcePathData& path_data, - IArchiveWriter* writer, const std::string& output_path) { + const ResourcePathData& path_data, IArchiveWriter* writer, + const std::string& output_path) { if (context->IsVerbose()) { context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling XML"); } @@ -400,8 +390,7 @@ static bool CompileXml(IAaptContext* context, const CompileOptions& options, return false; } - xmlres->file.name = ResourceName( - {}, *ParseResourceType(path_data.resource_dir), path_data.name); + xmlres->file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name); xmlres->file.config = path_data.config; xmlres->file.source = path_data.source; @@ -419,8 +408,7 @@ static bool CompileXml(IAaptContext* context, const CompileOptions& options, // Start the entry so we can write the header. if (!writer->StartEntry(output_path, 0)) { - context->GetDiagnostics()->Error(DiagMessage(output_path) - << "failed to open file"); + context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to open file"); return false; } @@ -439,48 +427,42 @@ static bool CompileXml(IAaptContext* context, const CompileOptions& options, // Number of CompiledFiles. output_stream.WriteLittleEndian32(1 + inline_documents.size()); - if (!FlattenXmlToOutStream(context, output_path, xmlres.get(), - &output_stream)) { + if (!FlattenXmlToOutStream(context, output_path, xmlres.get(), &output_stream)) { return false; } for (auto& inline_xml_doc : inline_documents) { - if (!FlattenXmlToOutStream(context, output_path, inline_xml_doc.get(), - &output_stream)) { + if (!FlattenXmlToOutStream(context, output_path, inline_xml_doc.get(), &output_stream)) { return false; } } } if (!writer->FinishEntry()) { - context->GetDiagnostics()->Error(DiagMessage(output_path) - << "failed to finish writing data"); + context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to finish writing data"); return false; } return true; } static bool CompilePng(IAaptContext* context, const CompileOptions& options, - const ResourcePathData& path_data, - IArchiveWriter* writer, const std::string& output_path) { + const ResourcePathData& path_data, IArchiveWriter* writer, + const std::string& output_path) { if (context->IsVerbose()) { - context->GetDiagnostics()->Note(DiagMessage(path_data.source) - << "compiling PNG"); + context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling PNG"); } BigBuffer buffer(4096); ResourceFile res_file; - res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), - path_data.name); + res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name); res_file.config = path_data.config; res_file.source = path_data.source; { std::string content; if (!android::base::ReadFileToString(path_data.source.path, &content)) { - context->GetDiagnostics()->Error( - DiagMessage(path_data.source) - << android::base::SystemErrorCodeToString(errno)); + context->GetDiagnostics()->Error(DiagMessage(path_data.source) + << android::base::SystemErrorCodeToString(errno)); return false; } @@ -517,8 +499,8 @@ static bool CompilePng(IAaptContext* context, const CompileOptions& options, } if (context->IsVerbose()) { - context->GetDiagnostics()->Note(DiagMessage(path_data.source) - << "9-patch: " << *nine_patch); + context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "9-patch: " + << *nine_patch); } } @@ -572,26 +554,22 @@ static bool CompilePng(IAaptContext* context, const CompileOptions& options, } static bool CompileFile(IAaptContext* context, const CompileOptions& options, - const ResourcePathData& path_data, - IArchiveWriter* writer, + const ResourcePathData& path_data, IArchiveWriter* writer, const std::string& output_path) { if (context->IsVerbose()) { - context->GetDiagnostics()->Note(DiagMessage(path_data.source) - << "compiling file"); + context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling file"); } BigBuffer buffer(256); ResourceFile res_file; - res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), - path_data.name); + res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name); res_file.config = path_data.config; res_file.source = path_data.source; std::string error_str; Maybe<android::FileMap> f = file::MmapPath(path_data.source.path, &error_str); if (!f) { - context->GetDiagnostics()->Error(DiagMessage(path_data.source) - << error_str); + context->GetDiagnostics()->Error(DiagMessage(path_data.source) << error_str); return false; } @@ -604,11 +582,17 @@ static bool CompileFile(IAaptContext* context, const CompileOptions& options, class CompileContext : public IAaptContext { public: - void SetVerbose(bool val) { verbose_ = val; } + void SetVerbose(bool val) { + verbose_ = val; + } - bool IsVerbose() override { return verbose_; } + bool IsVerbose() override { + return verbose_; + } - IDiagnostics* GetDiagnostics() override { return &diagnostics_; } + IDiagnostics* GetDiagnostics() override { + return &diagnostics_; + } NameMangler* GetNameMangler() override { abort(); @@ -620,14 +604,18 @@ class CompileContext : public IAaptContext { return empty; } - uint8_t GetPackageId() override { return 0x0; } + uint8_t GetPackageId() override { + return 0x0; + } SymbolTable* GetExternalSymbols() override { abort(); return nullptr; } - int GetMinSdkVersion() override { return 0; } + int GetMinSdkVersion() override { + return 0; + } private: StdErrDiagnostics diagnostics_; @@ -646,16 +634,13 @@ int Compile(const std::vector<StringPiece>& args) { Flags flags = Flags() .RequiredFlag("-o", "Output path", &options.output_path) - .OptionalFlag("--dir", "Directory to scan for resources", - &options.res_dir) + .OptionalFlag("--dir", "Directory to scan for resources", &options.res_dir) .OptionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales " "(en-XA and ar-XB)", &options.pseudolocalize) - .OptionalSwitch( - "--legacy", - "Treat errors that used to be valid in AAPT as warnings", - &options.legacy_mode) + .OptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings", + &options.legacy_mode) .OptionalSwitch("-v", "Enables verbose logging", &verbose); if (!flags.Parse("aapt2 compile", args, &std::cerr)) { return 1; @@ -669,8 +654,7 @@ int Compile(const std::vector<StringPiece>& args) { if (options.res_dir) { if (!flags.GetArgs().empty()) { // Can't have both files and a resource directory. - context.GetDiagnostics()->Error(DiagMessage() - << "files given but --dir specified"); + context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified"); flags.Usage("aapt2 compile", &std::cerr); return 1; } @@ -679,8 +663,7 @@ int Compile(const std::vector<StringPiece>& args) { return 1; } - archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), - options.output_path); + archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options.output_path); } else { input_data.reserve(flags.GetArgs().size()); @@ -688,18 +671,15 @@ int Compile(const std::vector<StringPiece>& args) { // Collect data from the path for each input file. for (const std::string& arg : flags.GetArgs()) { std::string error_str; - if (Maybe<ResourcePathData> path_data = - ExtractResourcePathData(arg, &error_str)) { + if (Maybe<ResourcePathData> path_data = ExtractResourcePathData(arg, &error_str)) { input_data.push_back(std::move(path_data.value())); } else { - context.GetDiagnostics()->Error(DiagMessage() << error_str << " (" - << arg << ")"); + context.GetDiagnostics()->Error(DiagMessage() << error_str << " (" << arg << ")"); return 1; } } - archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), - options.output_path); + archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options.output_path); } if (!archive_writer) { @@ -709,8 +689,7 @@ int Compile(const std::vector<StringPiece>& args) { bool error = false; for (ResourcePathData& path_data : input_data) { if (options.verbose) { - context.GetDiagnostics()->Note(DiagMessage(path_data.source) - << "processing"); + context.GetDiagnostics()->Note(DiagMessage(path_data.source) << "processing"); } if (path_data.resource_dir == "values") { @@ -718,42 +697,35 @@ int Compile(const std::vector<StringPiece>& args) { path_data.extension = "arsc"; const std::string output_filename = BuildIntermediateFilename(path_data); - if (!CompileTable(&context, options, path_data, archive_writer.get(), - output_filename)) { + if (!CompileTable(&context, options, path_data, archive_writer.get(), output_filename)) { error = true; } } else { const std::string output_filename = BuildIntermediateFilename(path_data); - if (const ResourceType* type = - ParseResourceType(path_data.resource_dir)) { + if (const ResourceType* type = ParseResourceType(path_data.resource_dir)) { if (*type != ResourceType::kRaw) { if (path_data.extension == "xml") { - if (!CompileXml(&context, options, path_data, archive_writer.get(), - output_filename)) { + if (!CompileXml(&context, options, path_data, archive_writer.get(), output_filename)) { error = true; } - } else if (path_data.extension == "png" || - path_data.extension == "9.png") { - if (!CompilePng(&context, options, path_data, archive_writer.get(), - output_filename)) { + } else if (path_data.extension == "png" || path_data.extension == "9.png") { + if (!CompilePng(&context, options, path_data, archive_writer.get(), output_filename)) { error = true; } } else { - if (!CompileFile(&context, options, path_data, archive_writer.get(), - output_filename)) { + if (!CompileFile(&context, options, path_data, archive_writer.get(), output_filename)) { error = true; } } } else { - if (!CompileFile(&context, options, path_data, archive_writer.get(), - output_filename)) { + if (!CompileFile(&context, options, path_data, archive_writer.get(), output_filename)) { error = true; } } } else { - context.GetDiagnostics()->Error( - DiagMessage() << "invalid file path '" << path_data.source << "'"); + context.GetDiagnostics()->Error(DiagMessage() << "invalid file path '" << path_data.source + << "'"); error = true; } } diff --git a/tools/aapt2/diff/Diff.cpp b/tools/aapt2/cmd/Diff.cpp index dacf8d9f1e96..fdc89b2b24bd 100644 --- a/tools/aapt2/diff/Diff.cpp +++ b/tools/aapt2/cmd/Diff.cpp @@ -28,21 +28,36 @@ namespace aapt { class DiffContext : public IAaptContext { public: - DiffContext() : name_mangler_({}), symbol_table_(&name_mangler_) {} + DiffContext() : name_mangler_({}), symbol_table_(&name_mangler_) { + } - const std::string& GetCompilationPackage() override { return empty_; } + const std::string& GetCompilationPackage() override { + return empty_; + } - uint8_t GetPackageId() override { return 0x0; } + uint8_t GetPackageId() override { + return 0x0; + } - IDiagnostics* GetDiagnostics() override { return &diagnostics_; } + IDiagnostics* GetDiagnostics() override { + return &diagnostics_; + } - NameMangler* GetNameMangler() override { return &name_mangler_; } + NameMangler* GetNameMangler() override { + return &name_mangler_; + } - SymbolTable* GetExternalSymbols() override { return &symbol_table_; } + SymbolTable* GetExternalSymbols() override { + return &symbol_table_; + } - bool IsVerbose() override { return false; } + bool IsVerbose() override { + return false; + } - int GetMinSdkVersion() override { return 0; } + int GetMinSdkVersion() override { + return 0; + } private: std::string empty_; @@ -55,34 +70,31 @@ static void EmitDiffLine(const Source& source, const StringPiece& message) { std::cerr << source << ": " << message << "\n"; } -static bool IsSymbolVisibilityDifferent(const Symbol& symbol_a, - const Symbol& symbol_b) { +static bool IsSymbolVisibilityDifferent(const Symbol& symbol_a, const Symbol& symbol_b) { return symbol_a.state != symbol_b.state; } template <typename Id> -static bool IsIdDiff(const Symbol& symbol_a, const Maybe<Id>& id_a, - const Symbol& symbol_b, const Maybe<Id>& id_b) { - if (symbol_a.state == SymbolState::kPublic || - symbol_b.state == SymbolState::kPublic) { +static bool IsIdDiff(const Symbol& symbol_a, const Maybe<Id>& id_a, const Symbol& symbol_b, + const Maybe<Id>& id_b) { + if (symbol_a.state == SymbolState::kPublic || symbol_b.state == SymbolState::kPublic) { return id_a != id_b; } return false; } -static bool EmitResourceConfigValueDiff( - IAaptContext* context, LoadedApk* apk_a, ResourceTablePackage* pkg_a, - ResourceTableType* type_a, ResourceEntry* entry_a, - ResourceConfigValue* config_value_a, LoadedApk* apk_b, - ResourceTablePackage* pkg_b, ResourceTableType* type_b, - ResourceEntry* entry_b, ResourceConfigValue* config_value_b) { +static bool EmitResourceConfigValueDiff(IAaptContext* context, LoadedApk* apk_a, + ResourceTablePackage* pkg_a, ResourceTableType* type_a, + ResourceEntry* entry_a, ResourceConfigValue* config_value_a, + LoadedApk* apk_b, ResourceTablePackage* pkg_b, + ResourceTableType* type_b, ResourceEntry* entry_b, + ResourceConfigValue* config_value_b) { Value* value_a = config_value_a->value.get(); Value* value_b = config_value_b->value.get(); if (!value_a->Equals(value_b)) { std::stringstream str_stream; - str_stream << "value " << pkg_a->name << ":" << type_a->type << "/" - << entry_a->name << " config=" << config_value_a->config - << " does not match:\n"; + str_stream << "value " << pkg_a->name << ":" << type_a->type << "/" << entry_a->name + << " config=" << config_value_a->config << " does not match:\n"; value_a->Print(&str_stream); str_stream << "\n vs \n"; value_b->Print(&str_stream); @@ -93,37 +105,33 @@ static bool EmitResourceConfigValueDiff( } static bool EmitResourceEntryDiff(IAaptContext* context, LoadedApk* apk_a, - ResourceTablePackage* pkg_a, - ResourceTableType* type_a, + ResourceTablePackage* pkg_a, ResourceTableType* type_a, ResourceEntry* entry_a, LoadedApk* apk_b, - ResourceTablePackage* pkg_b, - ResourceTableType* type_b, + ResourceTablePackage* pkg_b, ResourceTableType* type_b, ResourceEntry* entry_b) { bool diff = false; for (std::unique_ptr<ResourceConfigValue>& config_value_a : entry_a->values) { - ResourceConfigValue* config_value_b = - entry_b->FindValue(config_value_a->config); + ResourceConfigValue* config_value_b = entry_b->FindValue(config_value_a->config); if (!config_value_b) { std::stringstream str_stream; - str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/" - << entry_a->name << " config=" << config_value_a->config; + str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/" << entry_a->name + << " config=" << config_value_a->config; EmitDiffLine(apk_b->GetSource(), str_stream.str()); diff = true; } else { - diff |= EmitResourceConfigValueDiff( - context, apk_a, pkg_a, type_a, entry_a, config_value_a.get(), apk_b, - pkg_b, type_b, entry_b, config_value_b); + diff |= + EmitResourceConfigValueDiff(context, apk_a, pkg_a, type_a, entry_a, config_value_a.get(), + apk_b, pkg_b, type_b, entry_b, config_value_b); } } // Check for any newly added config values. for (std::unique_ptr<ResourceConfigValue>& config_value_b : entry_b->values) { - ResourceConfigValue* config_value_a = - entry_a->FindValue(config_value_b->config); + ResourceConfigValue* config_value_a = entry_a->FindValue(config_value_b->config); if (!config_value_a) { std::stringstream str_stream; - str_stream << "new config " << pkg_b->name << ":" << type_b->type << "/" - << entry_b->name << " config=" << config_value_b->config; + str_stream << "new config " << pkg_b->name << ":" << type_b->type << "/" << entry_b->name + << " config=" << config_value_b->config; EmitDiffLine(apk_b->GetSource(), str_stream.str()); diff = true; } @@ -132,22 +140,19 @@ static bool EmitResourceEntryDiff(IAaptContext* context, LoadedApk* apk_a, } static bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a, - ResourceTablePackage* pkg_a, - ResourceTableType* type_a, LoadedApk* apk_b, - ResourceTablePackage* pkg_b, + ResourceTablePackage* pkg_a, ResourceTableType* type_a, + LoadedApk* apk_b, ResourceTablePackage* pkg_b, ResourceTableType* type_b) { bool diff = false; for (std::unique_ptr<ResourceEntry>& entry_a : type_a->entries) { ResourceEntry* entry_b = type_b->FindEntry(entry_a->name); if (!entry_b) { std::stringstream str_stream; - str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/" - << entry_a->name; + str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/" << entry_a->name; EmitDiffLine(apk_b->GetSource(), str_stream.str()); diff = true; } else { - if (IsSymbolVisibilityDifferent(entry_a->symbol_status, - entry_b->symbol_status)) { + if (IsSymbolVisibilityDifferent(entry_a->symbol_status, entry_b->symbol_status)) { std::stringstream str_stream; str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name << " has different visibility ("; @@ -165,8 +170,8 @@ static bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a, str_stream << ")"; EmitDiffLine(apk_b->GetSource(), str_stream.str()); diff = true; - } else if (IsIdDiff(entry_a->symbol_status, entry_a->id, - entry_b->symbol_status, entry_b->id)) { + } else if (IsIdDiff(entry_a->symbol_status, entry_a->id, entry_b->symbol_status, + entry_b->id)) { std::stringstream str_stream; str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name << " has different public ID ("; @@ -185,9 +190,8 @@ static bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a, EmitDiffLine(apk_b->GetSource(), str_stream.str()); diff = true; } - diff |= - EmitResourceEntryDiff(context, apk_a, pkg_a, type_a, entry_a.get(), - apk_b, pkg_b, type_b, entry_b); + diff |= EmitResourceEntryDiff(context, apk_a, pkg_a, type_a, entry_a.get(), apk_b, pkg_b, + type_b, entry_b); } } @@ -196,8 +200,7 @@ static bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a, ResourceEntry* entry_a = type_a->FindEntry(entry_b->name); if (!entry_a) { std::stringstream str_stream; - str_stream << "new entry " << pkg_b->name << ":" << type_b->type << "/" - << entry_b->name; + str_stream << "new entry " << pkg_b->name << ":" << type_b->type << "/" << entry_b->name; EmitDiffLine(apk_b->GetSource(), str_stream.str()); diff = true; } @@ -206,8 +209,7 @@ static bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a, } static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a, - ResourceTablePackage* pkg_a, - LoadedApk* apk_b, + ResourceTablePackage* pkg_a, LoadedApk* apk_b, ResourceTablePackage* pkg_b) { bool diff = false; for (std::unique_ptr<ResourceTableType>& type_a : pkg_a->types) { @@ -218,11 +220,9 @@ static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a, EmitDiffLine(apk_a->GetSource(), str_stream.str()); diff = true; } else { - if (IsSymbolVisibilityDifferent(type_a->symbol_status, - type_b->symbol_status)) { + if (IsSymbolVisibilityDifferent(type_a->symbol_status, type_b->symbol_status)) { std::stringstream str_stream; - str_stream << pkg_a->name << ":" << type_a->type - << " has different visibility ("; + str_stream << pkg_a->name << ":" << type_a->type << " has different visibility ("; if (type_b->symbol_status.state == SymbolState::kPublic) { str_stream << "PUBLIC"; } else { @@ -237,11 +237,9 @@ static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a, str_stream << ")"; EmitDiffLine(apk_b->GetSource(), str_stream.str()); diff = true; - } else if (IsIdDiff(type_a->symbol_status, type_a->id, - type_b->symbol_status, type_b->id)) { + } else if (IsIdDiff(type_a->symbol_status, type_a->id, type_b->symbol_status, type_b->id)) { std::stringstream str_stream; - str_stream << pkg_a->name << ":" << type_a->type - << " has different public ID ("; + str_stream << pkg_a->name << ":" << type_a->type << " has different public ID ("; if (type_b->id) { str_stream << "0x" << std::hex << type_b->id.value(); } else { @@ -257,8 +255,7 @@ static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a, EmitDiffLine(apk_b->GetSource(), str_stream.str()); diff = true; } - diff |= EmitResourceTypeDiff(context, apk_a, pkg_a, type_a.get(), apk_b, - pkg_b, type_b); + diff |= EmitResourceTypeDiff(context, apk_a, pkg_a, type_a.get(), apk_b, pkg_b, type_b); } } @@ -275,8 +272,7 @@ static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a, return diff; } -static bool EmitResourceTableDiff(IAaptContext* context, LoadedApk* apk_a, - LoadedApk* apk_b) { +static bool EmitResourceTableDiff(IAaptContext* context, LoadedApk* apk_a, LoadedApk* apk_b) { ResourceTable* table_a = apk_a->GetResourceTable(); ResourceTable* table_b = apk_b->GetResourceTable(); @@ -307,8 +303,7 @@ static bool EmitResourceTableDiff(IAaptContext* context, LoadedApk* apk_a, EmitDiffLine(apk_b->GetSource(), str_stream.str()); diff = true; } - diff |= - EmitResourcePackageDiff(context, apk_a, pkg_a.get(), apk_b, pkg_b); + diff |= EmitResourcePackageDiff(context, apk_a, pkg_a.get(), apk_b, pkg_b); } } @@ -357,10 +352,8 @@ int Diff(const std::vector<StringPiece>& args) { return 1; } - std::unique_ptr<LoadedApk> apk_a = - LoadedApk::LoadApkFromPath(&context, flags.GetArgs()[0]); - std::unique_ptr<LoadedApk> apk_b = - LoadedApk::LoadApkFromPath(&context, flags.GetArgs()[1]); + std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(&context, flags.GetArgs()[0]); + std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(&context, flags.GetArgs()[1]); if (!apk_a || !apk_b) { return 1; } diff --git a/tools/aapt2/dump/Dump.cpp b/tools/aapt2/cmd/Dump.cpp index bcede72037b2..1bbfb28a7870 100644 --- a/tools/aapt2/dump/Dump.cpp +++ b/tools/aapt2/cmd/Dump.cpp @@ -31,13 +31,12 @@ using android::StringPiece; namespace aapt { -void DumpCompiledFile(const pb::CompiledFile& pb_file, const void* data, - size_t len, const Source& source, IAaptContext* context) { +void DumpCompiledFile(const pb::CompiledFile& pb_file, const void* data, size_t len, + const Source& source, IAaptContext* context) { std::unique_ptr<ResourceFile> file = DeserializeCompiledFileFromPb(pb_file, source, context->GetDiagnostics()); if (!file) { - context->GetDiagnostics()->Warn(DiagMessage() - << "failed to read compiled file"); + context->GetDiagnostics()->Warn(DiagMessage() << "failed to read compiled file"); return; } @@ -50,27 +49,24 @@ void TryDumpFile(IAaptContext* context, const std::string& file_path) { std::unique_ptr<ResourceTable> table; std::string err; - std::unique_ptr<io::ZipFileCollection> zip = - io::ZipFileCollection::Create(file_path, &err); + std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err); if (zip) { io::IFile* file = zip->FindFile("resources.arsc.flat"); if (file) { std::unique_ptr<io::IData> data = file->OpenAsData(); if (!data) { - context->GetDiagnostics()->Error( - DiagMessage(file_path) << "failed to open resources.arsc.flat"); + context->GetDiagnostics()->Error(DiagMessage(file_path) + << "failed to open resources.arsc.flat"); return; } pb::ResourceTable pb_table; if (!pb_table.ParseFromArray(data->data(), data->size())) { - context->GetDiagnostics()->Error(DiagMessage(file_path) - << "invalid resources.arsc.flat"); + context->GetDiagnostics()->Error(DiagMessage(file_path) << "invalid resources.arsc.flat"); return; } - table = DeserializeTableFromPb(pb_table, Source(file_path), - context->GetDiagnostics()); + table = DeserializeTableFromPb(pb_table, Source(file_path), context->GetDiagnostics()); if (!table) { return; } @@ -87,8 +83,8 @@ void TryDumpFile(IAaptContext* context, const std::string& file_path) { } table = util::make_unique<ResourceTable>(); - BinaryResourceParser parser(context, table.get(), Source(file_path), - data->data(), data->size()); + BinaryResourceParser parser(context, table.get(), Source(file_path), data->data(), + data->size()); if (!parser.Parse()) { return; } @@ -107,16 +103,13 @@ void TryDumpFile(IAaptContext* context, const std::string& file_path) { // Try as a compiled table. pb::ResourceTable pb_table; - if (pb_table.ParseFromArray(file_map->getDataPtr(), - file_map->getDataLength())) { - table = DeserializeTableFromPb(pb_table, Source(file_path), - context->GetDiagnostics()); + if (pb_table.ParseFromArray(file_map->getDataPtr(), file_map->getDataLength())) { + table = DeserializeTableFromPb(pb_table, Source(file_path), context->GetDiagnostics()); } if (!table) { // Try as a compiled file. - CompiledFileInputStream input(file_map->getDataPtr(), - file_map->getDataLength()); + CompiledFileInputStream input(file_map->getDataPtr(), file_map->getDataLength()); uint32_t num_files = 0; if (!input.ReadLittleEndian32(&num_files)) { @@ -126,20 +119,17 @@ void TryDumpFile(IAaptContext* context, const std::string& file_path) { for (uint32_t i = 0; i < num_files; i++) { pb::CompiledFile compiled_file; if (!input.ReadCompiledFile(&compiled_file)) { - context->GetDiagnostics()->Warn(DiagMessage() - << "failed to read compiled file"); + context->GetDiagnostics()->Warn(DiagMessage() << "failed to read compiled file"); return; } uint64_t offset, len; if (!input.ReadDataMetaData(&offset, &len)) { - context->GetDiagnostics()->Warn(DiagMessage() - << "failed to read meta data"); + context->GetDiagnostics()->Warn(DiagMessage() << "failed to read meta data"); return; } - const void* data = - static_cast<const uint8_t*>(file_map->getDataPtr()) + offset; + const void* data = static_cast<const uint8_t*>(file_map->getDataPtr()) + offset; DumpCompiledFile(compiled_file, data, len, Source(file_path), context); } } @@ -154,7 +144,9 @@ void TryDumpFile(IAaptContext* context, const std::string& file_path) { class DumpContext : public IAaptContext { public: - IDiagnostics* GetDiagnostics() override { return &diagnostics_; } + IDiagnostics* GetDiagnostics() override { + return &diagnostics_; + } NameMangler* GetNameMangler() override { abort(); @@ -166,18 +158,26 @@ class DumpContext : public IAaptContext { return empty; } - uint8_t GetPackageId() override { return 0; } + uint8_t GetPackageId() override { + return 0; + } SymbolTable* GetExternalSymbols() override { abort(); return nullptr; } - bool IsVerbose() override { return verbose_; } + bool IsVerbose() override { + return verbose_; + } - void SetVerbose(bool val) { verbose_ = val; } + void SetVerbose(bool val) { + verbose_ = val; + } - int GetMinSdkVersion() override { return 0; } + int GetMinSdkVersion() override { + return 0; + } private: StdErrDiagnostics diagnostics_; @@ -189,8 +189,7 @@ class DumpContext : public IAaptContext { */ int Dump(const std::vector<StringPiece>& args) { bool verbose = false; - Flags flags = - Flags().OptionalSwitch("-v", "increase verbosity of output", &verbose); + Flags flags = Flags().OptionalSwitch("-v", "increase verbosity of output", &verbose); if (!flags.Parse("aapt2 dump", args, &std::cerr)) { return 1; } diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/cmd/Link.cpp index 407eb5266785..6e0809e62450 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -33,6 +33,7 @@ #include "Locale.h" #include "NameMangler.h" #include "ResourceUtils.h" +#include "cmd/Util.h" #include "compile/IdAssigner.h" #include "filter/ConfigFilter.h" #include "flatten/Archive.h" @@ -40,6 +41,7 @@ #include "flatten/XmlFlattener.h" #include "io/BigBufferInputStream.h" #include "io/FileSystem.h" +#include "io/Util.h" #include "io/ZipArchive.h" #include "java/JavaClassGenerator.h" #include "java/ManifestClassGenerator.h" @@ -60,7 +62,6 @@ using android::StringPiece; using android::base::StringPrintf; -using ::google::protobuf::io::CopyingOutputStreamAdaptor; namespace aapt { @@ -125,11 +126,16 @@ struct LinkOptions { class LinkContext : public IAaptContext { public: - LinkContext() : name_mangler_({}), symbols_(&name_mangler_) {} + LinkContext() : name_mangler_({}), symbols_(&name_mangler_) { + } - IDiagnostics* GetDiagnostics() override { return &diagnostics_; } + IDiagnostics* GetDiagnostics() override { + return &diagnostics_; + } - NameMangler* GetNameMangler() override { return &name_mangler_; } + NameMangler* GetNameMangler() override { + return &name_mangler_; + } void SetNameManglerPolicy(const NameManglerPolicy& policy) { name_mangler_ = NameMangler(policy); @@ -143,19 +149,33 @@ class LinkContext : public IAaptContext { compilation_package_ = package_name.to_string(); } - uint8_t GetPackageId() override { return package_id_; } + uint8_t GetPackageId() override { + return package_id_; + } - void SetPackageId(uint8_t id) { package_id_ = id; } + void SetPackageId(uint8_t id) { + package_id_ = id; + } - SymbolTable* GetExternalSymbols() override { return &symbols_; } + SymbolTable* GetExternalSymbols() override { + return &symbols_; + } - bool IsVerbose() override { return verbose_; } + bool IsVerbose() override { + return verbose_; + } - void SetVerbose(bool val) { verbose_ = val; } + void SetVerbose(bool val) { + verbose_ = val; + } - int GetMinSdkVersion() override { return min_sdk_version_; } + int GetMinSdkVersion() override { + return min_sdk_version_; + } - void SetMinSdkVersion(int minSdk) { min_sdk_version_ = minSdk; } + void SetMinSdkVersion(int minSdk) { + min_sdk_version_ = minSdk; + } private: DISALLOW_COPY_AND_ASSIGN(LinkContext); @@ -169,63 +189,9 @@ class LinkContext : public IAaptContext { int min_sdk_version_ = 0; }; -static bool CopyInputStreamToArchive(io::InputStream* in, const std::string& out_path, - uint32_t compression_flags, IArchiveWriter* writer, - IAaptContext* context) { - if (context->IsVerbose()) { - context->GetDiagnostics()->Note(DiagMessage() << "writing " << out_path << " to archive"); - } - - if (!writer->WriteFile(out_path, compression_flags, in)) { - context->GetDiagnostics()->Error(DiagMessage() << "failed to write " << out_path - << " to archive: " << writer->GetError()); - return false; - } - return true; -} - -static bool CopyFileToArchive(io::IFile* file, const std::string& out_path, - uint32_t compression_flags, - IArchiveWriter* writer, IAaptContext* context) { - std::unique_ptr<io::IData> data = file->OpenAsData(); - if (!data) { - context->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << "failed to open file"); - return false; - } - return CopyInputStreamToArchive(data.get(), out_path, compression_flags, writer, context); -} - -static bool CopyProtoToArchive(::google::protobuf::MessageLite* proto_msg, - const std::string& out_path, uint32_t compression_flags, - IArchiveWriter* writer, IAaptContext* context) { - if (context->IsVerbose()) { - context->GetDiagnostics()->Note(DiagMessage() << "writing " << out_path << " to archive"); - } - - if (writer->StartEntry(out_path, compression_flags)) { - // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->FinishEntry(). - { - // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface. - ::google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer); - if (!proto_msg->SerializeToZeroCopyStream(&adaptor)) { - context->GetDiagnostics()->Error(DiagMessage() - << "failed to write " << out_path << " to archive"); - return false; - } - } - - if (writer->FinishEntry()) { - return true; - } - } - context->GetDiagnostics()->Error(DiagMessage() << "failed to write " << out_path - << " to archive: " << writer->GetError()); - return false; -} - static bool FlattenXml(xml::XmlResource* xml_res, const StringPiece& path, - Maybe<size_t> max_sdk_level, bool keep_raw_values, - IArchiveWriter* writer, IAaptContext* context) { + Maybe<size_t> max_sdk_level, bool keep_raw_values, IArchiveWriter* writer, + IAaptContext* context) { BigBuffer buffer(1024); XmlFlattenerOptions options = {}; options.keep_raw_values = keep_raw_values; @@ -239,21 +205,18 @@ static bool FlattenXml(xml::XmlResource* xml_res, const StringPiece& path, DiagMessage msg; msg << "writing " << path << " to archive"; if (max_sdk_level) { - msg << " maxSdkLevel=" << max_sdk_level.value() - << " keepRawValues=" << keep_raw_values; + msg << " maxSdkLevel=" << max_sdk_level.value() << " keepRawValues=" << keep_raw_values; } context->GetDiagnostics()->Note(msg); } io::BigBufferInputStream input_stream(&buffer); - return CopyInputStreamToArchive(&input_stream, path.to_string(), ArchiveEntry::kCompress, writer, - context); + return io::CopyInputStreamToArchive(context, &input_stream, path.to_string(), + ArchiveEntry::kCompress, writer); } -static std::unique_ptr<ResourceTable> LoadTableFromPb(const Source& source, - const void* data, - size_t len, - IDiagnostics* diag) { +static std::unique_ptr<ResourceTable> LoadTableFromPb(const Source& source, const void* data, + size_t len, IDiagnostics* diag) { pb::ResourceTable pb_table; if (!pb_table.ParseFromArray(data, len)) { diag->Error(DiagMessage(source) << "invalid compiled table"); @@ -270,8 +233,7 @@ static std::unique_ptr<ResourceTable> LoadTableFromPb(const Source& source, /** * Inflates an XML file from the source path. */ -static std::unique_ptr<xml::XmlResource> LoadXml(const std::string& path, - IDiagnostics* diag) { +static std::unique_ptr<xml::XmlResource> LoadXml(const std::string& path, IDiagnostics* diag) { std::ifstream fin(path, std::ifstream::binary); if (!fin) { diag->Error(DiagMessage(path) << strerror(errno)); @@ -293,9 +255,10 @@ struct ResourceFileFlattenerOptions { class ResourceFileFlattener { public: - ResourceFileFlattener(const ResourceFileFlattenerOptions& options, - IAaptContext* context, proguard::KeepSet* keep_set) - : options_(options), context_(context), keep_set_(keep_set) {} + ResourceFileFlattener(const ResourceFileFlattenerOptions& options, IAaptContext* context, + proguard::KeepSet* keep_set) + : options_(options), context_(context), keep_set_(keep_set) { + } bool Flatten(ResourceTable* table, IArchiveWriter* archive_writer); @@ -341,25 +304,15 @@ uint32_t ResourceFileFlattener::GetCompressionFlags(const StringPiece& str) { } static bool IsTransitionElement(const std::string& name) { - return - name == "fade" || - name == "changeBounds" || - name == "slide" || - name == "explode" || - name == "changeImageTransform" || - name == "changeTransform" || - name == "changeClipBounds" || - name == "autoTransition" || - name == "recolor" || - name == "changeScroll" || - name == "transitionSet" || - name == "transition" || - name == "transitionManager"; + return name == "fade" || name == "changeBounds" || name == "slide" || name == "explode" || + name == "changeImageTransform" || name == "changeTransform" || + name == "changeClipBounds" || name == "autoTransition" || name == "recolor" || + name == "changeScroll" || name == "transitionSet" || name == "transition" || + name == "transitionManager"; } -bool ResourceFileFlattener::LinkAndVersionXmlFile( - ResourceTable* table, FileOperation* file_op, - std::queue<FileOperation>* out_file_op_queue) { +bool ResourceFileFlattener::LinkAndVersionXmlFile(ResourceTable* table, FileOperation* file_op, + std::queue<FileOperation>* out_file_op_queue) { xml::XmlResource* doc = file_op->xml_to_flatten.get(); const Source& src = doc->file.source; @@ -372,8 +325,7 @@ bool ResourceFileFlattener::LinkAndVersionXmlFile( return false; } - if (options_.update_proguard_spec && - !proguard::CollectProguardRules(src, doc, keep_set_)) { + if (options_.update_proguard_spec && !proguard::CollectProguardRules(src, doc, keep_set_)) { return false; } @@ -415,8 +367,7 @@ bool ResourceFileFlattener::LinkAndVersionXmlFile( const int min_sdk_version = context_->GetMinSdkVersion(); for (int sdk_level : xml_linker.sdk_levels()) { if (sdk_level > min_sdk_version && sdk_level > config.sdkVersion) { - if (!ShouldGenerateVersionedResource(file_op->entry, config, - sdk_level)) { + if (!ShouldGenerateVersionedResource(file_op->entry, config, sdk_level)) { // If we shouldn't generate a versioned resource, stop checking. break; } @@ -425,24 +376,22 @@ bool ResourceFileFlattener::LinkAndVersionXmlFile( versioned_file_desc.config.sdkVersion = (uint16_t)sdk_level; FileOperation new_file_op; - new_file_op.xml_to_flatten = util::make_unique<xml::XmlResource>( - versioned_file_desc, doc->root->Clone()); + new_file_op.xml_to_flatten = + util::make_unique<xml::XmlResource>(versioned_file_desc, doc->root->Clone()); new_file_op.config = versioned_file_desc.config; new_file_op.entry = file_op->entry; - new_file_op.dst_path = ResourceUtils::BuildResourceFileName( - versioned_file_desc, context_->GetNameMangler()); + new_file_op.dst_path = + ResourceUtils::BuildResourceFileName(versioned_file_desc, context_->GetNameMangler()); if (context_->IsVerbose()) { - context_->GetDiagnostics()->Note( - DiagMessage(versioned_file_desc.source) - << "auto-versioning resource from config '" << config << "' -> '" - << versioned_file_desc.config << "'"); + context_->GetDiagnostics()->Note(DiagMessage(versioned_file_desc.source) + << "auto-versioning resource from config '" << config + << "' -> '" << versioned_file_desc.config << "'"); } bool added = table->AddFileReferenceAllowMangled( - versioned_file_desc.name, versioned_file_desc.config, - versioned_file_desc.source, new_file_op.dst_path, nullptr, - context_->GetDiagnostics()); + versioned_file_desc.name, versioned_file_desc.config, versioned_file_desc.source, + new_file_op.dst_path, nullptr, context_->GetDiagnostics()); if (!added) { return false; } @@ -460,11 +409,9 @@ bool ResourceFileFlattener::LinkAndVersionXmlFile( * will * corrupt the iteration order. */ -bool ResourceFileFlattener::Flatten(ResourceTable* table, - IArchiveWriter* archive_writer) { +bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archive_writer) { bool error = false; - std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> - config_sorted_files; + std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> config_sorted_files; for (auto& pkg : table->packages) { for (auto& type : pkg->types) { @@ -476,8 +423,7 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, // Populate the queue with all files in the ResourceTable. for (auto& entry : type->entries) { for (auto& config_value : entry->values) { - FileReference* file_ref = - ValueCast<FileReference>(config_value->value.get()); + FileReference* file_ref = ValueCast<FileReference>(config_value->value.get()); if (!file_ref) { continue; } @@ -496,8 +442,7 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, const StringPiece src_path = file->GetSource().path; if (type->type != ResourceType::kRaw && - (util::EndsWith(src_path, ".xml.flat") || - util::EndsWith(src_path, ".xml"))) { + (util::EndsWith(src_path, ".xml.flat") || util::EndsWith(src_path, ".xml"))) { std::unique_ptr<io::IData> data = file->OpenAsData(); if (!data) { context_->GetDiagnostics()->Error(DiagMessage(file->GetSource()) @@ -505,9 +450,8 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, return false; } - file_op.xml_to_flatten = - xml::Inflate(data->data(), data->size(), - context_->GetDiagnostics(), file->GetSource()); + file_op.xml_to_flatten = xml::Inflate(data->data(), data->size(), + context_->GetDiagnostics(), file->GetSource()); if (!file_op.xml_to_flatten) { return false; @@ -515,8 +459,7 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, file_op.xml_to_flatten->file.config = config_value->config; file_op.xml_to_flatten->file.source = file_ref->GetSource(); - file_op.xml_to_flatten->file.name = - ResourceName(pkg->name, type->type, entry->name); + file_op.xml_to_flatten->file.name = ResourceName(pkg->name, type->type, entry->name); // Enqueue the XML files to be processed. file_operations.push(std::move(file_op)); @@ -528,8 +471,8 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, // then creating a StringPiece from the copy, which would cause us // to end up referencing garbage in the map. const StringPiece entry_name(entry->name); - config_sorted_files[std::make_pair( - config_value->config, entry_name)] = std::move(file_op); + config_sorted_files[std::make_pair(config_value->config, entry_name)] = + std::move(file_op); } } } @@ -548,8 +491,7 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, // creating a StringPiece from the copy, which would cause us to end up // referencing garbage in the map. const StringPiece entry_name(file_op.entry->name); - config_sorted_files[std::make_pair(file_op.config, entry_name)] = - std::move(file_op); + config_sorted_files[std::make_pair(file_op.config, entry_name)] = std::move(file_op); } if (error) { @@ -564,21 +506,19 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, if (file_op.xml_to_flatten) { Maybe<size_t> max_sdk_level; if (!options_.no_auto_version && !file_op.skip_version) { - max_sdk_level = - std::max<size_t>(std::max<size_t>(config.sdkVersion, 1u), - context_->GetMinSdkVersion()); + max_sdk_level = std::max<size_t>(std::max<size_t>(config.sdkVersion, 1u), + context_->GetMinSdkVersion()); } - bool result = FlattenXml( - file_op.xml_to_flatten.get(), file_op.dst_path, max_sdk_level, - options_.keep_raw_values, archive_writer, context_); + bool result = FlattenXml(file_op.xml_to_flatten.get(), file_op.dst_path, max_sdk_level, + options_.keep_raw_values, archive_writer, context_); if (!result) { error = true; } } else { - bool result = CopyFileToArchive( - file_op.file_to_copy, file_op.dst_path, - GetCompressionFlags(file_op.dst_path), archive_writer, context_); + bool result = + io::CopyFileToArchive(context_, file_op.file_to_copy, file_op.dst_path, + GetCompressionFlags(file_op.dst_path), archive_writer); if (!result) { error = true; } @@ -589,10 +529,9 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, return !error; } -static bool WriteStableIdMapToPath( - IDiagnostics* diag, - const std::unordered_map<ResourceName, ResourceId>& id_map, - const std::string& id_map_path) { +static bool WriteStableIdMapToPath(IDiagnostics* diag, + const std::unordered_map<ResourceName, ResourceId>& id_map, + const std::string& id_map_path) { std::ofstream fout(id_map_path, std::ofstream::binary); if (!fout) { diag->Error(DiagMessage(id_map_path) << strerror(errno)); @@ -606,18 +545,16 @@ static bool WriteStableIdMapToPath( } if (!fout) { - diag->Error(DiagMessage(id_map_path) - << "failed writing to file: " - << android::base::SystemErrorCodeToString(errno)); + diag->Error(DiagMessage(id_map_path) << "failed writing to file: " + << android::base::SystemErrorCodeToString(errno)); return false; } return true; } -static bool LoadStableIdMap( - IDiagnostics* diag, const std::string& path, - std::unordered_map<ResourceName, ResourceId>* out_id_map) { +static bool LoadStableIdMap(IDiagnostics* diag, const std::string& path, + std::unordered_map<ResourceName, ResourceId>* out_id_map) { std::string content; if (!android::base::ReadFileToString(path, &content)) { diag->Error(DiagMessage(path) << "failed reading stable ID file"); @@ -643,20 +580,19 @@ static bool LoadStableIdMap( StringPiece res_name_str = util::TrimWhitespace(line.substr(0, std::distance(line.begin(), iter))); if (!ResourceUtils::ParseResourceName(res_name_str, &name)) { - diag->Error(DiagMessage(Source(path, line_no)) - << "invalid resource name '" << res_name_str << "'"); + diag->Error(DiagMessage(Source(path, line_no)) << "invalid resource name '" << res_name_str + << "'"); return false; } const size_t res_id_start_idx = std::distance(line.begin(), iter) + 1; const size_t res_id_str_len = line.size() - res_id_start_idx; - StringPiece res_id_str = - util::TrimWhitespace(line.substr(res_id_start_idx, res_id_str_len)); + StringPiece res_id_str = util::TrimWhitespace(line.substr(res_id_start_idx, res_id_str_len)); Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(res_id_str); if (!maybe_id) { - diag->Error(DiagMessage(Source(path, line_no)) << "invalid resource ID '" - << res_id_str << "'"); + diag->Error(DiagMessage(Source(path, line_no)) << "invalid resource ID '" << res_id_str + << "'"); return false; } @@ -665,38 +601,14 @@ static bool LoadStableIdMap( return true; } -static bool ParseSplitParameter(const StringPiece& arg, IDiagnostics* diag, - std::string* out_path, - SplitConstraints* out_split) { - std::vector<std::string> parts = util::Split(arg, ':'); - if (parts.size() != 2) { - diag->Error(DiagMessage() << "invalid split parameter '" << arg << "'"); - diag->Note( - DiagMessage() - << "should be --split path/to/output.apk:<config>[,<config>...]"); - return false; - } - *out_path = parts[0]; - std::vector<ConfigDescription> configs; - for (const StringPiece& config_str : util::Tokenize(parts[1], ',')) { - configs.push_back({}); - if (!ConfigDescription::Parse(config_str, &configs.back())) { - diag->Error(DiagMessage() << "invalid config '" << config_str - << "' in split parameter '" << arg << "'"); - return false; - } - } - out_split->configs.insert(configs.begin(), configs.end()); - return true; -} - class LinkCommand { public: LinkCommand(LinkContext* context, const LinkOptions& options) : options_(options), context_(context), final_table_(), - file_collection_(util::make_unique<io::FileCollection>()) {} + file_collection_(util::make_unique<io::FileCollection>()) { + } /** * Creates a SymbolTable that loads symbols from the various APKs and caches @@ -707,8 +619,7 @@ class LinkCommand { util::make_unique<AssetManagerSymbolSource>(); for (const std::string& path : options_.include_paths) { if (context_->IsVerbose()) { - context_->GetDiagnostics()->Note(DiagMessage(path) - << "loading include path"); + context_->GetDiagnostics()->Note(DiagMessage(path) << "loading include path"); } // First try to load the file as a static lib. @@ -820,7 +731,7 @@ class LinkCommand { if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) { if (xml::Attribute* min_sdk = uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) { - app_info.min_sdk_version = min_sdk->value; + app_info.min_sdk_version = ResourceUtils::ParseSdkVersion(min_sdk->value); } } return app_info; @@ -832,8 +743,7 @@ class LinkCommand { * stripped, or there is an error and false is returned. */ bool VerifyNoExternalPackages() { - auto is_ext_package_func = - [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool { + auto is_ext_package_func = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool { return context_->GetCompilationPackage() != pkg->name || !pkg->id || pkg->id.value() != context_->GetPackageId(); }; @@ -849,17 +759,16 @@ class LinkCommand { for (const auto& config_value : entry->values) { // Special case the occurrence of an ID that is being generated // for the 'android' package. This is due to legacy reasons. - if (ValueCast<Id>(config_value->value.get()) && - package->name == "android") { - context_->GetDiagnostics()->Warn( - DiagMessage(config_value->value->GetSource()) - << "generated id '" << res_name - << "' for external package '" << package->name << "'"); + if (ValueCast<Id>(config_value->value.get()) && package->name == "android") { + context_->GetDiagnostics()->Warn(DiagMessage(config_value->value->GetSource()) + << "generated id '" << res_name + << "' for external package '" << package->name + << "'"); } else { - context_->GetDiagnostics()->Error( - DiagMessage(config_value->value->GetSource()) - << "defined resource '" << res_name - << "' for external package '" << package->name << "'"); + context_->GetDiagnostics()->Error(DiagMessage(config_value->value->GetSource()) + << "defined resource '" << res_name + << "' for external package '" << package->name + << "'"); error = true; } } @@ -868,9 +777,8 @@ class LinkCommand { } } - auto new_end_iter = - std::remove_if(final_table_.packages.begin(), - final_table_.packages.end(), is_ext_package_func); + auto new_end_iter = std::remove_if(final_table_.packages.begin(), final_table_.packages.end(), + is_ext_package_func); final_table_.packages.erase(new_end_iter, final_table_.packages.end()); return !error; } @@ -919,17 +827,16 @@ class LinkCommand { } io::BigBufferInputStream input_stream(&buffer); - return CopyInputStreamToArchive(&input_stream, "resources.arsc", ArchiveEntry::kAlign, writer, - context_); + return io::CopyInputStreamToArchive(context_, &input_stream, "resources.arsc", + ArchiveEntry::kAlign, writer); } bool FlattenTableToPb(ResourceTable* table, IArchiveWriter* writer) { std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(table); - return CopyProtoToArchive(pb_table.get(), "resources.arsc.flat", 0, writer, context_); + return io::CopyProtoToArchive(context_, pb_table.get(), "resources.arsc.flat", 0, writer); } - bool WriteJavaFile(ResourceTable* table, - const StringPiece& package_name_to_generate, + bool WriteJavaFile(ResourceTable* table, const StringPiece& package_name_to_generate, const StringPiece& out_package, const JavaClassGeneratorOptions& java_options) { if (!options_.generate_java_class_path) { @@ -939,8 +846,8 @@ class LinkCommand { std::string out_path = options_.generate_java_class_path.value(); file::AppendPath(&out_path, file::PackageToPath(out_package)); if (!file::mkdirs(out_path)) { - context_->GetDiagnostics()->Error( - DiagMessage() << "failed to create directory '" << out_path << "'"); + context_->GetDiagnostics()->Error(DiagMessage() << "failed to create directory '" << out_path + << "'"); return false; } @@ -948,9 +855,9 @@ class LinkCommand { std::ofstream fout(out_path, std::ofstream::binary); if (!fout) { - context_->GetDiagnostics()->Error( - DiagMessage() << "failed writing to '" << out_path << "': " - << android::base::SystemErrorCodeToString(errno)); + context_->GetDiagnostics()->Error(DiagMessage() + << "failed writing to '" << out_path + << "': " << android::base::SystemErrorCodeToString(errno)); return false; } @@ -961,9 +868,9 @@ class LinkCommand { } if (!fout) { - context_->GetDiagnostics()->Error( - DiagMessage() << "failed writing to '" << out_path << "': " - << android::base::SystemErrorCodeToString(errno)); + context_->GetDiagnostics()->Error(DiagMessage() + << "failed writing to '" << out_path + << "': " << android::base::SystemErrorCodeToString(errno)); } return true; } @@ -999,8 +906,8 @@ class LinkCommand { file::AppendPath(&out_path, file::PackageToPath(package_utf8)); if (!file::mkdirs(out_path)) { - context_->GetDiagnostics()->Error( - DiagMessage() << "failed to create directory '" << out_path << "'"); + context_->GetDiagnostics()->Error(DiagMessage() << "failed to create directory '" << out_path + << "'"); return false; } @@ -1008,24 +915,22 @@ class LinkCommand { std::ofstream fout(out_path, std::ofstream::binary); if (!fout) { - context_->GetDiagnostics()->Error( - DiagMessage() << "failed writing to '" << out_path << "': " - << android::base::SystemErrorCodeToString(errno)); + context_->GetDiagnostics()->Error(DiagMessage() + << "failed writing to '" << out_path + << "': " << android::base::SystemErrorCodeToString(errno)); return false; } - if (!ClassDefinition::WriteJavaFile(manifest_class.get(), package_utf8, - true, &fout)) { - context_->GetDiagnostics()->Error( - DiagMessage() << "failed writing to '" << out_path << "': " - << android::base::SystemErrorCodeToString(errno)); + if (!ClassDefinition::WriteJavaFile(manifest_class.get(), package_utf8, true, &fout)) { + context_->GetDiagnostics()->Error(DiagMessage() + << "failed writing to '" << out_path + << "': " << android::base::SystemErrorCodeToString(errno)); return false; } return true; } - bool WriteProguardFile(const Maybe<std::string>& out, - const proguard::KeepSet& keep_set) { + bool WriteProguardFile(const Maybe<std::string>& out, const proguard::KeepSet& keep_set) { if (!out) { return true; } @@ -1033,17 +938,17 @@ class LinkCommand { const std::string& out_path = out.value(); std::ofstream fout(out_path, std::ofstream::binary); if (!fout) { - context_->GetDiagnostics()->Error( - DiagMessage() << "failed to open '" << out_path << "': " - << android::base::SystemErrorCodeToString(errno)); + context_->GetDiagnostics()->Error(DiagMessage() + << "failed to open '" << out_path + << "': " << android::base::SystemErrorCodeToString(errno)); return false; } proguard::WriteKeepSet(&fout, keep_set); if (!fout) { - context_->GetDiagnostics()->Error( - DiagMessage() << "failed writing to '" << out_path << "': " - << android::base::SystemErrorCodeToString(errno)); + context_->GetDiagnostics()->Error(DiagMessage() + << "failed writing to '" << out_path + << "': " << android::base::SystemErrorCodeToString(errno)); return false; } return true; @@ -1059,8 +964,7 @@ class LinkCommand { return LoadTablePbFromCollection(collection.get()); } - std::unique_ptr<ResourceTable> LoadTablePbFromCollection( - io::IFileCollection* collection) { + std::unique_ptr<ResourceTable> LoadTablePbFromCollection(io::IFileCollection* collection) { io::IFile* file = collection->FindFile("resources.arsc.flat"); if (!file) { return {}; @@ -1073,8 +977,7 @@ class LinkCommand { bool MergeStaticLibrary(const std::string& input, bool override) { if (context_->IsVerbose()) { - context_->GetDiagnostics()->Note(DiagMessage() - << "merging static library " << input); + context_->GetDiagnostics()->Note(DiagMessage() << "merging static library " << input); } std::string error_str; @@ -1118,8 +1021,8 @@ class LinkCommand { } else { // This is the proper way to merge libraries, where the package name is // preserved and resource names are mangled. - result = table_merger_->MergeAndMangle(Source(input), pkg->name, - table.get(), collection.get()); + result = + table_merger_->MergeAndMangle(Source(input), pkg->name, table.get(), collection.get()); } if (!result) { @@ -1133,20 +1036,18 @@ class LinkCommand { bool MergeResourceTable(io::IFile* file, bool override) { if (context_->IsVerbose()) { - context_->GetDiagnostics()->Note( - DiagMessage() << "merging resource table " << file->GetSource()); + context_->GetDiagnostics()->Note(DiagMessage() << "merging resource table " + << file->GetSource()); } std::unique_ptr<io::IData> data = file->OpenAsData(); if (!data) { - context_->GetDiagnostics()->Error(DiagMessage(file->GetSource()) - << "failed to open file"); + context_->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << "failed to open file"); return false; } std::unique_ptr<ResourceTable> table = - LoadTableFromPb(file->GetSource(), data->data(), data->size(), - context_->GetDiagnostics()); + LoadTableFromPb(file->GetSource(), data->data(), data->size(), context_->GetDiagnostics()); if (!table) { return false; } @@ -1160,12 +1061,11 @@ class LinkCommand { return result; } - bool MergeCompiledFile(io::IFile* file, ResourceFile* file_desc, - bool override) { + bool MergeCompiledFile(io::IFile* file, ResourceFile* file_desc, bool override) { if (context_->IsVerbose()) { - context_->GetDiagnostics()->Note( - DiagMessage() << "merging '" << file_desc->name - << "' from compiled file " << file->GetSource()); + context_->GetDiagnostics()->Note(DiagMessage() << "merging '" << file_desc->name + << "' from compiled file " + << file->GetSource()); } bool result = false; @@ -1196,8 +1096,8 @@ class LinkCommand { std::unique_ptr<Id> id = util::make_unique<Id>(); id->SetSource(file_desc->source.WithLine(exported_symbol.line)); bool result = final_table_.AddResourceAllowMangled( - res_name, ConfigDescription::DefaultConfig(), std::string(), - std::move(id), context_->GetDiagnostics()); + res_name, ConfigDescription::DefaultConfig(), std::string(), std::move(id), + context_->GetDiagnostics()); if (!result) { return false; } @@ -1216,8 +1116,7 @@ class LinkCommand { */ bool MergeArchive(const std::string& input, bool override) { if (context_->IsVerbose()) { - context_->GetDiagnostics()->Note(DiagMessage() << "merging archive " - << input); + context_->GetDiagnostics()->Note(DiagMessage() << "merging archive " << input); } std::string error_str; @@ -1296,49 +1195,43 @@ class LinkCommand { CompiledFileInputStream input_stream(data->data(), data->size()); uint32_t num_files = 0; if (!input_stream.ReadLittleEndian32(&num_files)) { - context_->GetDiagnostics()->Error(DiagMessage(src) - << "failed read num files"); + context_->GetDiagnostics()->Error(DiagMessage(src) << "failed read num files"); return false; } for (uint32_t i = 0; i < num_files; i++) { pb::CompiledFile compiled_file; if (!input_stream.ReadCompiledFile(&compiled_file)) { - context_->GetDiagnostics()->Error( - DiagMessage(src) << "failed to read compiled file header"); + context_->GetDiagnostics()->Error(DiagMessage(src) + << "failed to read compiled file header"); return false; } uint64_t offset, len; if (!input_stream.ReadDataMetaData(&offset, &len)) { - context_->GetDiagnostics()->Error(DiagMessage(src) - << "failed to read data meta data"); + context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to read data meta data"); return false; } - std::unique_ptr<ResourceFile> resource_file = - DeserializeCompiledFileFromPb(compiled_file, file->GetSource(), - context_->GetDiagnostics()); + std::unique_ptr<ResourceFile> resource_file = DeserializeCompiledFileFromPb( + compiled_file, file->GetSource(), context_->GetDiagnostics()); if (!resource_file) { return false; } - if (!MergeCompiledFile(file->CreateFileSegment(offset, len), - resource_file.get(), override)) { + if (!MergeCompiledFile(file->CreateFileSegment(offset, len), resource_file.get(), + override)) { return false; } } return true; - } else if (util::EndsWith(src.path, ".xml") || - util::EndsWith(src.path, ".png")) { + } else if (util::EndsWith(src.path, ".xml") || util::EndsWith(src.path, ".png")) { // Since AAPT compiles these file types and appends .flat to them, seeing // their raw extensions is a sign that they weren't compiled. - const StringPiece file_type = - util::EndsWith(src.path, ".xml") ? "XML" : "PNG"; - context_->GetDiagnostics()->Error(DiagMessage(src) - << "uncompiled " << file_type - << " file passed as argument. Must be " - "compiled first into .flat file."); + const StringPiece file_type = util::EndsWith(src.path, ".xml") ? "XML" : "PNG"; + context_->GetDiagnostics()->Error(DiagMessage(src) << "uncompiled " << file_type + << " file passed as argument. Must be " + "compiled first into .flat file."); return false; } @@ -1348,51 +1241,6 @@ class LinkCommand { return true; } - std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info, - const SplitConstraints& constraints) { - std::unique_ptr<xml::XmlResource> doc = util::make_unique<xml::XmlResource>(); - - std::unique_ptr<xml::Namespace> namespace_android = util::make_unique<xml::Namespace>(); - namespace_android->namespace_uri = xml::kSchemaAndroid; - namespace_android->namespace_prefix = "android"; - - std::unique_ptr<xml::Element> manifest_el = util::make_unique<xml::Element>(); - manifest_el->name = "manifest"; - manifest_el->attributes.push_back(xml::Attribute{"", "package", app_info.package}); - - if (app_info.version_code) { - manifest_el->attributes.push_back(xml::Attribute{ - xml::kSchemaAndroid, "versionCode", std::to_string(app_info.version_code.value())}); - } - - if (app_info.revision_code) { - manifest_el->attributes.push_back(xml::Attribute{ - xml::kSchemaAndroid, "revisionCode", std::to_string(app_info.revision_code.value())}); - } - - std::stringstream split_name; - if (app_info.split_name) { - split_name << app_info.split_name.value() << "."; - } - split_name << "config." << util::Joiner(constraints.configs, "_"); - - manifest_el->attributes.push_back(xml::Attribute{"", "split", split_name.str()}); - - if (app_info.split_name) { - manifest_el->attributes.push_back( - xml::Attribute{"", "configForSplit", app_info.split_name.value()}); - } - - std::unique_ptr<xml::Element> application_el = util::make_unique<xml::Element>(); - application_el->name = "application"; - application_el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, "hasCode", "false"}); - - manifest_el->AppendChild(std::move(application_el)); - namespace_android->AppendChild(std::move(manifest_el)); - doc->root = std::move(namespace_android); - return doc; - } - bool CopyAssetsDirsToApk(IArchiveWriter* writer) { std::map<std::string, std::unique_ptr<io::RegularFile>> merged_assets; for (const std::string& assets_dir : options_.assets_dirs) { @@ -1425,8 +1273,8 @@ class LinkCommand { compression_flags = 0u; } - if (!CopyFileToArchive(entry.second.get(), entry.first, compression_flags, writer, - context_)) { + if (!io::CopyFileToArchive(context_, entry.second.get(), entry.first, compression_flags, + writer)) { return false; } } @@ -1437,21 +1285,19 @@ class LinkCommand { * Writes the AndroidManifest, ResourceTable, and all XML files referenced by * the ResourceTable to the IArchiveWriter. */ - bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, - xml::XmlResource* manifest, ResourceTable* table) { + bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest, + ResourceTable* table) { const bool keep_raw_values = options_.package_type == PackageType::kStaticLib; - bool result = FlattenXml(manifest, "AndroidManifest.xml", {}, - keep_raw_values, writer, context_); + bool result = + FlattenXml(manifest, "AndroidManifest.xml", {}, keep_raw_values, writer, context_); if (!result) { return false; } ResourceFileFlattenerOptions file_flattener_options; file_flattener_options.keep_raw_values = keep_raw_values; - file_flattener_options.do_not_compress_anything = - options_.do_not_compress_anything; - file_flattener_options.extensions_to_not_compress = - options_.extensions_to_not_compress; + file_flattener_options.do_not_compress_anything = options_.do_not_compress_anything; + file_flattener_options.extensions_to_not_compress = options_.extensions_to_not_compress; file_flattener_options.no_auto_version = options_.no_auto_version; file_flattener_options.no_version_vectors = options_.no_version_vectors; file_flattener_options.no_version_transitions = options_.no_version_transitions; @@ -1487,10 +1333,9 @@ class LinkCommand { return 1; } - // First extract the Package name without modifying it (via - // --rename-manifest-package). - if (Maybe<AppInfo> maybe_app_info = ExtractAppInfoFromManifest( - manifest_xml.get(), context_->GetDiagnostics())) { + // First extract the Package name without modifying it (via --rename-manifest-package). + if (Maybe<AppInfo> maybe_app_info = + ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics())) { const AppInfo& app_info = maybe_app_info.value(); context_->SetCompilationPackage(app_info.package); } @@ -1500,22 +1345,16 @@ class LinkCommand { return 1; } - Maybe<AppInfo> maybe_app_info = ExtractAppInfoFromManifest( - manifest_xml.get(), context_->GetDiagnostics()); + Maybe<AppInfo> maybe_app_info = + ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics()); if (!maybe_app_info) { return 1; } const AppInfo& app_info = maybe_app_info.value(); - if (app_info.min_sdk_version) { - if (Maybe<int> maybe_min_sdk_version = ResourceUtils::ParseSdkVersion( - app_info.min_sdk_version.value())) { - context_->SetMinSdkVersion(maybe_min_sdk_version.value()); - } - } + context_->SetMinSdkVersion(app_info.min_sdk_version.value_or_default(0)); - context_->SetNameManglerPolicy( - NameManglerPolicy{context_->GetCompilationPackage()}); + context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()}); // Override the package ID when it is "android". if (context_->GetCompilationPackage() == "android") { @@ -1535,8 +1374,7 @@ class LinkCommand { TableMergerOptions table_merger_options; table_merger_options.auto_add_overlay = options_.auto_add_overlay; - table_merger_ = util::make_unique<TableMerger>(context_, &final_table_, - table_merger_options); + table_merger_ = util::make_unique<TableMerger>(context_, &final_table_, table_merger_options); if (context_->IsVerbose()) { context_->GetDiagnostics()->Note(DiagMessage() @@ -1547,16 +1385,14 @@ class LinkCommand { for (const std::string& input : input_files) { if (!MergePath(input, false)) { - context_->GetDiagnostics()->Error(DiagMessage() - << "failed parsing input"); + context_->GetDiagnostics()->Error(DiagMessage() << "failed parsing input"); return 1; } } for (const std::string& input : options_.overlay_files) { if (!MergePath(input, true)) { - context_->GetDiagnostics()->Error(DiagMessage() - << "failed parsing overlays"); + context_->GetDiagnostics()->Error(DiagMessage() << "failed parsing overlays"); return 1; } } @@ -1568,8 +1404,7 @@ class LinkCommand { if (options_.package_type != PackageType::kStaticLib) { PrivateAttributeMover mover; if (!mover.Consume(context_, &final_table_)) { - context_->GetDiagnostics()->Error( - DiagMessage() << "failed moving private attributes"); + context_->GetDiagnostics()->Error(DiagMessage() << "failed moving private attributes"); return 1; } @@ -1587,14 +1422,13 @@ class LinkCommand { for (auto& entry : type->entries) { ResourceName name(package->name, type->type, entry->name); // The IDs are guaranteed to exist. - options_.stable_id_map[std::move(name)] = ResourceId( - package->id.value(), type->id.value(), entry->id.value()); + options_.stable_id_map[std::move(name)] = + ResourceId(package->id.value(), type->id.value(), entry->id.value()); } } } - if (!WriteStableIdMapToPath(context_->GetDiagnostics(), - options_.stable_id_map, + if (!WriteStableIdMapToPath(context_->GetDiagnostics(), options_.stable_id_map, options_.resource_id_map_path.value())) { return 1; } @@ -1609,8 +1443,8 @@ class LinkCommand { } // Add the names to mangle based on our source merge earlier. - context_->SetNameManglerPolicy(NameManglerPolicy{ - context_->GetCompilationPackage(), table_merger_->merged_packages()}); + context_->SetNameManglerPolicy( + NameManglerPolicy{context_->GetCompilationPackage(), table_merger_->merged_packages()}); // Add our table to the symbol table. context_->GetExternalSymbols()->PrependSource( @@ -1618,8 +1452,7 @@ class LinkCommand { ReferenceLinker linker; if (!linker.Consume(context_, &final_table_)) { - context_->GetDiagnostics()->Error(DiagMessage() - << "failed linking references"); + context_->GetDiagnostics()->Error(DiagMessage() << "failed linking references"); return 1; } @@ -1639,17 +1472,16 @@ class LinkCommand { if (!options_.no_auto_version) { AutoVersioner versioner; if (!versioner.Consume(context_, &final_table_)) { - context_->GetDiagnostics()->Error(DiagMessage() - << "failed versioning styles"); + context_->GetDiagnostics()->Error(DiagMessage() << "failed versioning styles"); return 1; } } if (options_.package_type != PackageType::kStaticLib && context_->GetMinSdkVersion() > 0) { if (context_->IsVerbose()) { - context_->GetDiagnostics()->Note( - DiagMessage() << "collapsing resource versions for minimum SDK " - << context_->GetMinSdkVersion()); + context_->GetDiagnostics()->Note(DiagMessage() + << "collapsing resource versions for minimum SDK " + << context_->GetMinSdkVersion()); } VersionCollapser collapser; @@ -1676,27 +1508,12 @@ class LinkCommand { << "can't strip resources when building static library"); } } else { - // Adjust the SplitConstraints so that their SDK version is stripped if it - // is less - // than or equal to the minSdk. Otherwise the resources that have had - // their SDK version - // stripped due to minSdk won't ever match. - std::vector<SplitConstraints> adjusted_constraints_list; - adjusted_constraints_list.reserve(options_.split_constraints.size()); - for (const SplitConstraints& constraints : options_.split_constraints) { - SplitConstraints adjusted_constraints; - for (const ConfigDescription& config : constraints.configs) { - if (config.sdkVersion <= context_->GetMinSdkVersion()) { - adjusted_constraints.configs.insert(config.CopyWithoutSdkVersion()); - } else { - adjusted_constraints.configs.insert(config); - } - } - adjusted_constraints_list.push_back(std::move(adjusted_constraints)); - } + // Adjust the SplitConstraints so that their SDK version is stripped if it is less than or + // equal to the minSdk. + options_.split_constraints = + AdjustSplitConstraintsForMinSdk(context_->GetMinSdkVersion(), options_.split_constraints); - TableSplitter table_splitter(adjusted_constraints_list, - options_.table_splitter_options); + TableSplitter table_splitter(options_.split_constraints, options_.table_splitter_options); if (!table_splitter.VerifySplitConstraints(context_)) { return 1; } @@ -1704,21 +1521,18 @@ class LinkCommand { // Now we need to write out the Split APKs. auto path_iter = options_.split_paths.begin(); - auto split_constraints_iter = adjusted_constraints_list.begin(); - for (std::unique_ptr<ResourceTable>& split_table : - table_splitter.splits()) { + auto split_constraints_iter = options_.split_constraints.begin(); + for (std::unique_ptr<ResourceTable>& split_table : table_splitter.splits()) { if (context_->IsVerbose()) { - context_->GetDiagnostics()->Note( - DiagMessage(*path_iter) - << "generating split with configurations '" - << util::Joiner(split_constraints_iter->configs, ", ") << "'"); + context_->GetDiagnostics()->Note(DiagMessage(*path_iter) + << "generating split with configurations '" + << util::Joiner(split_constraints_iter->configs, ", ") + << "'"); } - std::unique_ptr<IArchiveWriter> archive_writer = - MakeArchiveWriter(*path_iter); + std::unique_ptr<IArchiveWriter> archive_writer = MakeArchiveWriter(*path_iter); if (!archive_writer) { - context_->GetDiagnostics()->Error(DiagMessage() - << "failed to create archive"); + context_->GetDiagnostics()->Error(DiagMessage() << "failed to create archive"); return 1; } @@ -1728,13 +1542,13 @@ class LinkCommand { XmlReferenceLinker linker; if (!linker.Consume(context_, split_manifest.get())) { - context_->GetDiagnostics()->Error( - DiagMessage() << "failed to create Split AndroidManifest.xml"); + context_->GetDiagnostics()->Error(DiagMessage() + << "failed to create Split AndroidManifest.xml"); return 1; } - if (!WriteApk(archive_writer.get(), &proguard_keep_set, - split_manifest.get(), split_table.get())) { + if (!WriteApk(archive_writer.get(), &proguard_keep_set, split_manifest.get(), + split_table.get())) { return 1; } @@ -1925,10 +1739,10 @@ int Link(const std::vector<StringPiece>& args) { std::vector<std::string> split_args; Flags flags = Flags() - .RequiredFlag("-o", "Output path", &options.output_path) - .RequiredFlag("--manifest", "Path to the Android manifest to build", + .RequiredFlag("-o", "Output path.", &options.output_path) + .RequiredFlag("--manifest", "Path to the Android manifest to build.", &options.manifest_path) - .OptionalFlagList("-I", "Adds an Android APK to link against", &options.include_paths) + .OptionalFlagList("-I", "Adds an Android APK to link against.", &options.include_paths) .OptionalFlagList("-A", "An assets directory to include in the APK. These are unprocessed.", &options.assets_dirs) @@ -1940,24 +1754,23 @@ int Link(const std::vector<StringPiece>& args) { "Specify the package ID to use for this app. Must be greater or equal to\n" "0x7f and can't be used with --static-lib or --shared-lib.", &package_id) - .OptionalFlag("--java", "Directory in which to generate R.java", + .OptionalFlag("--java", "Directory in which to generate R.java.", &options.generate_java_class_path) - .OptionalFlag("--proguard", "Output file for generated Proguard rules", + .OptionalFlag("--proguard", "Output file for generated Proguard rules.", &options.generate_proguard_rules_path) .OptionalFlag("--proguard-main-dex", - "Output file for generated Proguard rules for the main dex", + "Output file for generated Proguard rules for the main dex.", &options.generate_main_dex_proguard_rules_path) - .OptionalSwitch("--no-auto-version", "Disables automatic style and layout SDK versioning", + .OptionalSwitch("--no-auto-version", + "Disables automatic style and layout SDK versioning.", &options.no_auto_version) .OptionalSwitch("--no-version-vectors", - "Disables automatic versioning of vector drawables. " - "Use this only\n" - "when building with vector drawable support library", + "Disables automatic versioning of vector drawables. Use this only\n" + "when building with vector drawable support library.", &options.no_version_vectors) .OptionalSwitch("--no-version-transitions", - "Disables automatic versioning of transition resources. " - "Use this only\n" - "when building with transition support library", + "Disables automatic versioning of transition resources. Use this only\n" + "when building with transition support library.", &options.no_version_transitions) .OptionalSwitch("--no-resource-deduping", "Disables automatic deduping of resources with\n" @@ -1967,100 +1780,90 @@ int Link(const std::vector<StringPiece>& args) { "Enables encoding sparse entries using a binary search tree.\n" "This decreases APK size at the cost of resource retrieval performance.", &options.table_flattener_options.use_sparse_entries) - .OptionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01", + .OptionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01.", &legacy_x_flag) - .OptionalSwitch("-z", "Require localization of strings marked 'suggested'", + .OptionalSwitch("-z", "Require localization of strings marked 'suggested'.", &require_localization) .OptionalFlagList("-c", "Comma separated list of configurations to include. The default\n" - "is all configurations", + "is all configurations.", &configs) .OptionalFlag("--preferred-density", "Selects the closest matching density and strips out all others.", &preferred_density) .OptionalFlag("--product", "Comma separated list of product names to keep", &product_list) .OptionalSwitch("--output-to-dir", - "Outputs the APK contents to a directory specified " - "by -o", + "Outputs the APK contents to a directory specified by -o.", &options.output_to_directory) .OptionalSwitch("--no-xml-namespaces", - "Removes XML namespace prefix and URI " - "information from AndroidManifest.xml\nand XML " - "binaries in res/*.", + "Removes XML namespace prefix and URI information from\n" + "AndroidManifest.xml and XML binaries in res/*.", &options.no_xml_namespaces) .OptionalFlag("--min-sdk-version", - "Default minimum SDK version to use for " - "AndroidManifest.xml", + "Default minimum SDK version to use for AndroidManifest.xml.", &options.manifest_fixer_options.min_sdk_version_default) .OptionalFlag("--target-sdk-version", - "Default target SDK version to use for " - "AndroidManifest.xml", + "Default target SDK version to use for AndroidManifest.xml.", &options.manifest_fixer_options.target_sdk_version_default) .OptionalFlag("--version-code", - "Version code (integer) to inject into the " - "AndroidManifest.xml if none is present", + "Version code (integer) to inject into the AndroidManifest.xml if none is\n" + "present.", &options.manifest_fixer_options.version_code_default) .OptionalFlag("--version-name", - "Version name to inject into the AndroidManifest.xml " - "if none is present", + "Version name to inject into the AndroidManifest.xml if none is present.", &options.manifest_fixer_options.version_name_default) - .OptionalSwitch("--shared-lib", "Generates a shared Android runtime library", &shared_lib) - .OptionalSwitch("--static-lib", "Generate a static Android library", &static_lib) + .OptionalSwitch("--shared-lib", "Generates a shared Android runtime library.", + &shared_lib) + .OptionalSwitch("--static-lib", "Generate a static Android library.", &static_lib) .OptionalSwitch("--no-static-lib-packages", - "Merge all library resources under the app's package", + "Merge all library resources under the app's package.", &options.no_static_lib_packages) .OptionalSwitch("--non-final-ids", - "Generates R.java without the final modifier.\n" - "This is implied when --static-lib is specified.", + "Generates R.java without the final modifier. This is implied when\n" + "--static-lib is specified.", &options.generate_non_final_ids) .OptionalFlag("--stable-ids", "File containing a list of name to ID mapping.", &stable_id_file_path) .OptionalFlag("--emit-ids", - "Emit a file at the given path with a list of name to ID\n" - "mappings, suitable for use with --stable-ids.", + "Emit a file at the given path with a list of name to ID mappings,\n" + "suitable for use with --stable-ids.", &options.resource_id_map_path) .OptionalFlag("--private-symbols", - "Package name to use when generating R.java for " - "private symbols.\n" - "If not specified, public and private symbols will use " - "the application's " - "package name", + "Package name to use when generating R.java for private symbols.\n" + "If not specified, public and private symbols will use the application's\n" + "package name.", &options.private_symbols) - .OptionalFlag("--custom-package", "Custom Java package under which to generate R.java", + .OptionalFlag("--custom-package", "Custom Java package under which to generate R.java.", &options.custom_java_package) .OptionalFlagList("--extra-packages", - "Generate the same R.java but with different " - "package names", + "Generate the same R.java but with different package names.", &extra_java_packages) .OptionalFlagList("--add-javadoc-annotation", - "Adds a JavaDoc annotation to all " - "generated Java classes", + "Adds a JavaDoc annotation to all generated Java classes.", &options.javadoc_annotations) .OptionalSwitch("--auto-add-overlay", - "Allows the addition of new resources in " - "overlays without <add-resource> tags", + "Allows the addition of new resources in overlays without\n" + "<add-resource> tags.", &options.auto_add_overlay) - .OptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml", + .OptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml.", &options.manifest_fixer_options.rename_manifest_package) .OptionalFlag("--rename-instrumentation-target-package", - "Changes the name of the target package for instrumentation. " - "Most useful " - "when used\nin conjunction with --rename-manifest-package", + "Changes the name of the target package for instrumentation. Most useful\n" + "when used in conjunction with --rename-manifest-package.", &options.manifest_fixer_options.rename_instrumentation_target_package) - .OptionalFlagList("-0", "File extensions not to compress", + .OptionalFlagList("-0", "File extensions not to compress.", &options.extensions_to_not_compress) .OptionalFlagList("--split", - "Split resources matching a set of configs out to a " - "Split APK.\nSyntax: path/to/output.apk:<config>[,<config>[...]]", + "Split resources matching a set of configs out to a Split APK.\n" + "Syntax: path/to/output.apk:<config>[,<config>[...]].", &split_args) - .OptionalSwitch("-v", "Enables verbose logging", &verbose); + .OptionalSwitch("-v", "Enables verbose logging.", &verbose); if (!flags.Parse("aapt2 link", args, &std::cerr)) { return 1; } - // Expand all argument-files passed into the command line. These start with - // '@'. + // Expand all argument-files passed into the command line. These start with '@'. std::vector<std::string> arg_list; for (const std::string& arg : flags.GetArgs()) { if (util::StartsWith(arg, "@")) { @@ -2150,55 +1953,32 @@ int Link(const std::vector<StringPiece>& args) { } } - AxisConfigFilter filter; + std::unique_ptr<IConfigFilter> filter; if (!configs.empty()) { - for (const std::string& config_arg : configs) { - for (const StringPiece& config_str : util::Tokenize(config_arg, ',')) { - ConfigDescription config; - LocaleValue lv; - if (lv.InitFromFilterString(config_str)) { - lv.WriteTo(&config); - } else if (!ConfigDescription::Parse(config_str, &config)) { - context.GetDiagnostics()->Error(DiagMessage() << "invalid config '" - << config_str - << "' for -c option"); - return 1; - } - - if (config.density != 0) { - context.GetDiagnostics()->Warn(DiagMessage() << "ignoring density '" - << config - << "' for -c option"); - } else { - filter.AddConfig(config); - } - } + filter = ParseConfigFilterParameters(configs, context.GetDiagnostics()); + if (filter == nullptr) { + return 1; } - options.table_splitter_options.config_filter = &filter; + options.table_splitter_options.config_filter = filter.get(); } if (preferred_density) { - ConfigDescription preferred_density_config; - if (!ConfigDescription::Parse(preferred_density.value(), - &preferred_density_config)) { - context.GetDiagnostics()->Error( - DiagMessage() << "invalid density '" << preferred_density.value() - << "' for --preferred-density option"); + Maybe<uint16_t> density = + ParseTargetDensityParameter(preferred_density.value(), context.GetDiagnostics()); + if (!density) { return 1; } + options.table_splitter_options.preferred_densities.push_back(density.value()); + } - // Clear the version that can be automatically added. - preferred_density_config.sdkVersion = 0; - - if (preferred_density_config.diff(ConfigDescription::DefaultConfig()) != - ConfigDescription::CONFIG_DENSITY) { - context.GetDiagnostics()->Error( - DiagMessage() << "invalid preferred density '" - << preferred_density.value() << "'. " - << "Preferred density must only be a density value"); + // Parse the split parameters. + for (const std::string& split_arg : split_args) { + options.split_paths.push_back({}); + options.split_constraints.push_back({}); + if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options.split_paths.back(), + &options.split_constraints.back())) { return 1; } - options.table_splitter_options.preferred_densities.push_back(preferred_density_config.density); } if (options.package_type != PackageType::kStaticLib && stable_id_file_path) { @@ -2215,17 +1995,6 @@ int Link(const std::vector<StringPiece>& args) { ".imy", ".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"}); - // Parse the split parameters. - for (const std::string& split_arg : split_args) { - options.split_paths.push_back({}); - options.split_constraints.push_back({}); - if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), - &options.split_paths.back(), - &options.split_constraints.back())) { - return 1; - } - } - // Turn off auto versioning for static-libs. if (options.package_type == PackageType::kStaticLib) { options.no_auto_version = true; diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp new file mode 100644 index 000000000000..8f8e0c8e4f6d --- /dev/null +++ b/tools/aapt2/cmd/Optimize.cpp @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2017 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 <memory> +#include <vector> + +#include "androidfw/StringPiece.h" + +#include "Diagnostics.h" +#include "Flags.h" +#include "LoadedApk.h" +#include "ResourceUtils.h" +#include "SdkConstants.h" +#include "ValueVisitor.h" +#include "cmd/Util.h" +#include "flatten/TableFlattener.h" +#include "flatten/XmlFlattener.h" +#include "io/BigBufferInputStream.h" +#include "io/Util.h" +#include "optimize/ResourceDeduper.h" +#include "optimize/VersionCollapser.h" +#include "split/TableSplitter.h" + +using android::StringPiece; + +namespace aapt { + +struct OptimizeOptions { + // Path to the output APK. + std::string output_path; + + // Details of the app extracted from the AndroidManifest.xml + AppInfo app_info; + + // Split APK options. + TableSplitterOptions table_splitter_options; + + // List of output split paths. These are in the same order as `split_constraints`. + std::vector<std::string> split_paths; + + // List of SplitConstraints governing what resources go into each split. Ordered by `split_paths`. + std::vector<SplitConstraints> split_constraints; + + TableFlattenerOptions table_flattener_options; +}; + +class OptimizeContext : public IAaptContext { + public: + IDiagnostics* GetDiagnostics() override { + return &diagnostics_; + } + + NameMangler* GetNameMangler() override { + UNIMPLEMENTED(FATAL); + return nullptr; + } + + const std::string& GetCompilationPackage() override { + static std::string empty; + return empty; + } + + uint8_t GetPackageId() override { + return 0; + } + + SymbolTable* GetExternalSymbols() override { + UNIMPLEMENTED(FATAL); + return nullptr; + } + + bool IsVerbose() override { + return verbose_; + } + + void SetVerbose(bool val) { + verbose_ = val; + } + + void SetMinSdkVersion(int sdk_version) { + sdk_version_ = sdk_version; + } + + int GetMinSdkVersion() override { + return sdk_version_; + } + + private: + StdErrDiagnostics diagnostics_; + bool verbose_ = false; + int sdk_version_ = 0; +}; + +class OptimizeCommand { + public: + OptimizeCommand(OptimizeContext* context, const OptimizeOptions& options) + : options_(options), context_(context) { + } + + int Run(std::unique_ptr<LoadedApk> apk) { + if (context_->IsVerbose()) { + context_->GetDiagnostics()->Note(DiagMessage() << "Optimizing APK..."); + } + + VersionCollapser collapser; + if (!collapser.Consume(context_, apk->GetResourceTable())) { + return 1; + } + + ResourceDeduper deduper; + if (!deduper.Consume(context_, apk->GetResourceTable())) { + context_->GetDiagnostics()->Error(DiagMessage() << "failed deduping resources"); + return 1; + } + + // Adjust the SplitConstraints so that their SDK version is stripped if it is less than or + // equal to the minSdk. + options_.split_constraints = + AdjustSplitConstraintsForMinSdk(context_->GetMinSdkVersion(), options_.split_constraints); + + // Stripping the APK using the TableSplitter. The resource table is modified in place in the + // LoadedApk. + TableSplitter splitter(options_.split_constraints, options_.table_splitter_options); + if (!splitter.VerifySplitConstraints(context_)) { + return 1; + } + splitter.SplitTable(apk->GetResourceTable()); + + auto path_iter = options_.split_paths.begin(); + auto split_constraints_iter = options_.split_constraints.begin(); + for (std::unique_ptr<ResourceTable>& split_table : splitter.splits()) { + if (context_->IsVerbose()) { + context_->GetDiagnostics()->Note( + DiagMessage(*path_iter) << "generating split with configurations '" + << util::Joiner(split_constraints_iter->configs, ", ") << "'"); + } + + // Generate an AndroidManifest.xml for each split. + std::unique_ptr<xml::XmlResource> split_manifest = + GenerateSplitManifest(options_.app_info, *split_constraints_iter); + std::unique_ptr<IArchiveWriter> split_writer = + CreateZipFileArchiveWriter(context_->GetDiagnostics(), *path_iter); + if (!split_writer) { + return 1; + } + + if (!WriteSplitApk(split_table.get(), split_manifest.get(), split_writer.get())) { + return 1; + } + + ++path_iter; + ++split_constraints_iter; + } + + std::unique_ptr<IArchiveWriter> writer = + CreateZipFileArchiveWriter(context_->GetDiagnostics(), options_.output_path); + if (!apk->WriteToArchive(context_, options_.table_flattener_options, writer.get())) { + return 1; + } + + return 0; + } + + private: + bool WriteSplitApk(ResourceTable* table, xml::XmlResource* manifest, IArchiveWriter* writer) { + BigBuffer manifest_buffer(4096); + XmlFlattener xml_flattener(&manifest_buffer, {}); + if (!xml_flattener.Consume(context_, manifest)) { + return false; + } + + io::BigBufferInputStream manifest_buffer_in(&manifest_buffer); + if (!io::CopyInputStreamToArchive(context_, &manifest_buffer_in, "AndroidManifest.xml", + ArchiveEntry::kCompress, writer)) { + return false; + } + + std::map<std::pair<ConfigDescription, StringPiece>, FileReference*> config_sorted_files; + for (auto& pkg : table->packages) { + for (auto& type : pkg->types) { + // Sort by config and name, so that we get better locality in the zip file. + config_sorted_files.clear(); + + for (auto& entry : type->entries) { + for (auto& config_value : entry->values) { + FileReference* file_ref = ValueCast<FileReference>(config_value->value.get()); + if (file_ref == nullptr) { + continue; + } + + if (file_ref->file == nullptr) { + ResourceNameRef name(pkg->name, type->type, entry->name); + context_->GetDiagnostics()->Error(DiagMessage(file_ref->GetSource()) + << "file for resource " << name << " with config '" + << config_value->config << "' not found"); + return false; + } + + const StringPiece entry_name = entry->name; + config_sorted_files[std::make_pair(config_value->config, entry_name)] = file_ref; + } + } + + for (auto& entry : config_sorted_files) { + FileReference* file_ref = entry.second; + uint32_t compression_flags = + file_ref->file->WasCompressed() ? ArchiveEntry::kCompress : 0u; + if (!io::CopyFileToArchive(context_, file_ref->file, *file_ref->path, compression_flags, + writer)) { + return false; + } + } + } + } + + BigBuffer table_buffer(4096); + TableFlattener table_flattener(options_.table_flattener_options, &table_buffer); + if (!table_flattener.Consume(context_, table)) { + return false; + } + + io::BigBufferInputStream table_buffer_in(&table_buffer); + if (!io::CopyInputStreamToArchive(context_, &table_buffer_in, "resources.arsc", + ArchiveEntry::kAlign, writer)) { + return false; + } + return true; + } + + OptimizeOptions options_; + OptimizeContext* context_; +}; + +bool ExtractAppDataFromManifest(OptimizeContext* context, LoadedApk* apk, + OptimizeOptions* out_options) { + io::IFile* manifest_file = apk->GetFileCollection()->FindFile("AndroidManifest.xml"); + if (manifest_file == nullptr) { + context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) + << "missing AndroidManifest.xml"); + return false; + } + + std::unique_ptr<io::IData> data = manifest_file->OpenAsData(); + if (data == nullptr) { + context->GetDiagnostics()->Error(DiagMessage(manifest_file->GetSource()) + << "failed to open file"); + return false; + } + + std::unique_ptr<xml::XmlResource> manifest = xml::Inflate( + data->data(), data->size(), context->GetDiagnostics(), manifest_file->GetSource()); + if (manifest == nullptr) { + context->GetDiagnostics()->Error(DiagMessage() << "failed to read binary AndroidManifest.xml"); + return false; + } + + Maybe<AppInfo> app_info = + ExtractAppInfoFromBinaryManifest(manifest.get(), context->GetDiagnostics()); + if (!app_info) { + context->GetDiagnostics()->Error(DiagMessage() + << "failed to extract data from AndroidManifest.xml"); + return false; + } + + out_options->app_info = std::move(app_info.value()); + context->SetMinSdkVersion(out_options->app_info.min_sdk_version.value_or_default(0)); + return true; +} + +int Optimize(const std::vector<StringPiece>& args) { + OptimizeContext context; + OptimizeOptions options; + Maybe<std::string> target_densities; + std::vector<std::string> configs; + std::vector<std::string> split_args; + bool verbose = false; + Flags flags = + Flags() + .RequiredFlag("-o", "Path to the output APK.", &options.output_path) + .OptionalFlag( + "--target-densities", + "Comma separated list of the screen densities that the APK will be optimized for.\n" + "All the resources that would be unused on devices of the given densities will be \n" + "removed from the APK.", + &target_densities) + .OptionalFlagList("-c", + "Comma separated list of configurations to include. The default\n" + "is all configurations.", + &configs) + .OptionalFlagList("--split", + "Split resources matching a set of configs out to a " + "Split APK.\nSyntax: path/to/output.apk:<config>[,<config>[...]].", + &split_args) + .OptionalSwitch("--enable-sparse-encoding", + "Enables encoding sparse entries using a binary search tree.\n" + "This decreases APK size at the cost of resource retrieval performance.", + &options.table_flattener_options.use_sparse_entries) + .OptionalSwitch("-v", "Enables verbose logging", &verbose); + + if (!flags.Parse("aapt2 optimize", args, &std::cerr)) { + return 1; + } + + if (flags.GetArgs().size() != 1u) { + std::cerr << "must have one APK as argument.\n\n"; + flags.Usage("aapt2 optimize", &std::cerr); + return 1; + } + + std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(&context, flags.GetArgs()[0]); + if (!apk) { + return 1; + } + + context.SetVerbose(verbose); + + if (target_densities) { + // Parse the target screen densities. + for (const StringPiece& config_str : util::Tokenize(target_densities.value(), ',')) { + Maybe<uint16_t> target_density = + ParseTargetDensityParameter(config_str, context.GetDiagnostics()); + if (!target_density) { + return 1; + } + options.table_splitter_options.preferred_densities.push_back(target_density.value()); + } + } + + std::unique_ptr<IConfigFilter> filter; + if (!configs.empty()) { + filter = ParseConfigFilterParameters(configs, context.GetDiagnostics()); + if (filter == nullptr) { + return 1; + } + options.table_splitter_options.config_filter = filter.get(); + } + + // Parse the split parameters. + for (const std::string& split_arg : split_args) { + options.split_paths.push_back({}); + options.split_constraints.push_back({}); + if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options.split_paths.back(), + &options.split_constraints.back())) { + return 1; + } + } + + if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) { + return 1; + } + + OptimizeCommand cmd(&context, options); + return cmd.Run(std::move(apk)); +} + +} // namespace aapt diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp new file mode 100644 index 000000000000..fd94bbc0a1c3 --- /dev/null +++ b/tools/aapt2/cmd/Util.cpp @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2017 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 "cmd/Util.h" + +#include <vector> + +#include "android-base/logging.h" + +#include "ConfigDescription.h" +#include "Locale.h" +#include "ResourceUtils.h" +#include "ValueVisitor.h" +#include "split/TableSplitter.h" +#include "util/Maybe.h" +#include "util/Util.h" + +using android::StringPiece; + +namespace aapt { + +Maybe<uint16_t> ParseTargetDensityParameter(const StringPiece& arg, IDiagnostics* diag) { + ConfigDescription preferred_density_config; + if (!ConfigDescription::Parse(arg, &preferred_density_config)) { + diag->Error(DiagMessage() << "invalid density '" << arg << "' for --preferred-density option"); + return {}; + } + + // Clear the version that can be automatically added. + preferred_density_config.sdkVersion = 0; + + if (preferred_density_config.diff(ConfigDescription::DefaultConfig()) != + ConfigDescription::CONFIG_DENSITY) { + diag->Error(DiagMessage() << "invalid preferred density '" << arg << "'. " + << "Preferred density must only be a density value"); + return {}; + } + return preferred_density_config.density; +} + +bool ParseSplitParameter(const StringPiece& arg, IDiagnostics* diag, std::string* out_path, + SplitConstraints* out_split) { + CHECK(diag != nullptr); + CHECK(out_path != nullptr); + CHECK(out_split != nullptr); + + std::vector<std::string> parts = util::Split(arg, ':'); + if (parts.size() != 2) { + diag->Error(DiagMessage() << "invalid split parameter '" << arg << "'"); + diag->Note(DiagMessage() << "should be --split path/to/output.apk:<config>[,<config>...]"); + return false; + } + + *out_path = parts[0]; + std::vector<ConfigDescription> configs; + for (const StringPiece& config_str : util::Tokenize(parts[1], ',')) { + ConfigDescription config; + if (!ConfigDescription::Parse(config_str, &config)) { + diag->Error(DiagMessage() << "invalid config '" << config_str << "' in split parameter '" + << arg << "'"); + return false; + } + out_split->configs.insert(config); + } + return true; +} + +std::unique_ptr<IConfigFilter> ParseConfigFilterParameters(const std::vector<std::string>& args, + IDiagnostics* diag) { + std::unique_ptr<AxisConfigFilter> filter = util::make_unique<AxisConfigFilter>(); + for (const std::string& config_arg : args) { + for (const StringPiece& config_str : util::Tokenize(config_arg, ',')) { + ConfigDescription config; + LocaleValue lv; + if (lv.InitFromFilterString(config_str)) { + lv.WriteTo(&config); + } else if (!ConfigDescription::Parse(config_str, &config)) { + diag->Error(DiagMessage() << "invalid config '" << config_str << "' for -c option"); + return {}; + } + + if (config.density != 0) { + diag->Warn(DiagMessage() << "ignoring density '" << config << "' for -c option"); + } else { + filter->AddConfig(config); + } + } + } + return std::move(filter); +} + +// Adjust the SplitConstraints so that their SDK version is stripped if it +// is less than or equal to the minSdk. Otherwise the resources that have had +// their SDK version stripped due to minSdk won't ever match. +std::vector<SplitConstraints> AdjustSplitConstraintsForMinSdk( + int min_sdk, const std::vector<SplitConstraints>& split_constraints) { + std::vector<SplitConstraints> adjusted_constraints; + adjusted_constraints.reserve(split_constraints.size()); + for (const SplitConstraints& constraints : split_constraints) { + SplitConstraints constraint; + for (const ConfigDescription& config : constraints.configs) { + if (config.sdkVersion <= min_sdk) { + constraint.configs.insert(config.CopyWithoutSdkVersion()); + } else { + constraint.configs.insert(config); + } + } + adjusted_constraints.push_back(std::move(constraint)); + } + return adjusted_constraints; +} + +static xml::AaptAttribute CreateAttributeWithId(const ResourceId& id) { + return xml::AaptAttribute{id, Attribute(true)}; +} + +std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info, + const SplitConstraints& constraints) { + const ResourceId kVersionCode(0x0101021b); + const ResourceId kRevisionCode(0x010104d5); + const ResourceId kHasCode(0x0101000c); + + std::unique_ptr<xml::XmlResource> doc = util::make_unique<xml::XmlResource>(); + + std::unique_ptr<xml::Namespace> namespace_android = util::make_unique<xml::Namespace>(); + namespace_android->namespace_uri = xml::kSchemaAndroid; + namespace_android->namespace_prefix = "android"; + + std::unique_ptr<xml::Element> manifest_el = util::make_unique<xml::Element>(); + manifest_el->name = "manifest"; + manifest_el->attributes.push_back(xml::Attribute{"", "package", app_info.package}); + + if (app_info.version_code) { + const uint32_t version_code = app_info.version_code.value(); + manifest_el->attributes.push_back(xml::Attribute{ + xml::kSchemaAndroid, "versionCode", std::to_string(version_code), + CreateAttributeWithId(kVersionCode), + util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, version_code)}); + } + + if (app_info.revision_code) { + const uint32_t revision_code = app_info.revision_code.value(); + manifest_el->attributes.push_back(xml::Attribute{ + xml::kSchemaAndroid, "revisionCode", std::to_string(revision_code), + CreateAttributeWithId(kRevisionCode), + util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, revision_code)}); + } + + std::stringstream split_name; + if (app_info.split_name) { + split_name << app_info.split_name.value() << "."; + } + split_name << "config." << util::Joiner(constraints.configs, "_"); + + manifest_el->attributes.push_back(xml::Attribute{"", "split", split_name.str()}); + + if (app_info.split_name) { + manifest_el->attributes.push_back( + xml::Attribute{"", "configForSplit", app_info.split_name.value()}); + } + + std::unique_ptr<xml::Element> application_el = util::make_unique<xml::Element>(); + application_el->name = "application"; + application_el->attributes.push_back( + xml::Attribute{xml::kSchemaAndroid, "hasCode", "false", CreateAttributeWithId(kHasCode), + util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN, 0u)}); + + manifest_el->AppendChild(std::move(application_el)); + namespace_android->AppendChild(std::move(manifest_el)); + doc->root = std::move(namespace_android); + return doc; +} + +static Maybe<std::string> ExtractCompiledString(xml::Attribute* attr, std::string* out_error) { + if (attr->compiled_value != nullptr) { + String* compiled_str = ValueCast<String>(attr->compiled_value.get()); + if (compiled_str != nullptr) { + if (!compiled_str->value->empty()) { + return *compiled_str->value; + } else { + *out_error = "compiled value is an empty string"; + return {}; + } + } + *out_error = "compiled value is not a string"; + return {}; + } + + // Fallback to the plain text value if there is one. + if (!attr->value.empty()) { + return attr->value; + } + *out_error = "value is an empty string"; + return {}; +} + +static Maybe<uint32_t> ExtractCompiledInt(xml::Attribute* attr, std::string* out_error) { + if (attr->compiled_value != nullptr) { + BinaryPrimitive* compiled_prim = ValueCast<BinaryPrimitive>(attr->compiled_value.get()); + if (compiled_prim != nullptr) { + if (compiled_prim->value.dataType >= android::Res_value::TYPE_FIRST_INT && + compiled_prim->value.dataType <= android::Res_value::TYPE_LAST_INT) { + return compiled_prim->value.data; + } + } + *out_error = "compiled value is not an integer"; + return {}; + } + + // Fallback to the plain text value if there is one. + Maybe<uint32_t> integer = ResourceUtils::ParseInt(attr->value); + if (integer) { + return integer; + } + std::stringstream error_msg; + error_msg << "'" << attr->value << "' is not a valid integer"; + *out_error = error_msg.str(); + return {}; +} + +static Maybe<int> ExtractSdkVersion(xml::Attribute* attr, std::string* out_error) { + if (attr->compiled_value != nullptr) { + BinaryPrimitive* compiled_prim = ValueCast<BinaryPrimitive>(attr->compiled_value.get()); + if (compiled_prim != nullptr) { + if (compiled_prim->value.dataType >= android::Res_value::TYPE_FIRST_INT && + compiled_prim->value.dataType <= android::Res_value::TYPE_LAST_INT) { + return compiled_prim->value.data; + } + *out_error = "compiled value is not an integer or string"; + return {}; + } + + String* compiled_str = ValueCast<String>(attr->compiled_value.get()); + if (compiled_str != nullptr) { + Maybe<int> sdk_version = ResourceUtils::ParseSdkVersion(*compiled_str->value); + if (sdk_version) { + return sdk_version; + } + + *out_error = "compiled string value is not a valid SDK version"; + return {}; + } + *out_error = "compiled value is not an integer or string"; + return {}; + } + + // Fallback to the plain text value if there is one. + Maybe<int> sdk_version = ResourceUtils::ParseSdkVersion(attr->value); + if (sdk_version) { + return sdk_version; + } + std::stringstream error_msg; + error_msg << "'" << attr->value << "' is not a valid SDK version"; + *out_error = error_msg.str(); + return {}; +} + +Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(xml::XmlResource* xml_res, IDiagnostics* diag) { + // Make sure the first element is <manifest> with package attribute. + xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get()); + if (manifest_el == nullptr) { + return {}; + } + + AppInfo app_info; + + if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") { + diag->Error(DiagMessage(xml_res->file.source) << "root tag must be <manifest>"); + return {}; + } + + xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package"); + if (!package_attr) { + diag->Error(DiagMessage(xml_res->file.source) << "<manifest> must have a 'package' attribute"); + return {}; + } + + std::string error_msg; + Maybe<std::string> maybe_package = ExtractCompiledString(package_attr, &error_msg); + if (!maybe_package) { + diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number)) + << "invalid package name: " << error_msg); + return {}; + } + app_info.package = maybe_package.value(); + + if (xml::Attribute* version_code_attr = + manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) { + Maybe<uint32_t> maybe_code = ExtractCompiledInt(version_code_attr, &error_msg); + if (!maybe_code) { + diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number)) + << "invalid android:versionCode: " << error_msg); + return {}; + } + app_info.version_code = maybe_code.value(); + } + + if (xml::Attribute* revision_code_attr = + manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) { + Maybe<uint32_t> maybe_code = ExtractCompiledInt(revision_code_attr, &error_msg); + if (!maybe_code) { + diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number)) + << "invalid android:revisionCode: " << error_msg); + return {}; + } + app_info.revision_code = maybe_code.value(); + } + + if (xml::Attribute* split_name_attr = manifest_el->FindAttribute({}, "split")) { + Maybe<std::string> maybe_split_name = ExtractCompiledString(split_name_attr, &error_msg); + if (!maybe_split_name) { + diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number)) + << "invalid split name: " << error_msg); + return {}; + } + app_info.split_name = maybe_split_name.value(); + } + + if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) { + if (xml::Attribute* min_sdk = + uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) { + Maybe<int> maybe_sdk = ExtractSdkVersion(min_sdk, &error_msg); + if (!maybe_sdk) { + diag->Error(DiagMessage(xml_res->file.source.WithLine(uses_sdk_el->line_number)) + << "invalid android:minSdkVersion: " << error_msg); + return {}; + } + app_info.min_sdk_version = maybe_sdk.value(); + } + } + return app_info; +} + +} // namespace aapt diff --git a/tools/aapt2/cmd/Util.h b/tools/aapt2/cmd/Util.h new file mode 100644 index 000000000000..fd9b39c67380 --- /dev/null +++ b/tools/aapt2/cmd/Util.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 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_SPLIT_UTIL_H +#define AAPT_SPLIT_UTIL_H + +#include "androidfw/StringPiece.h" + +#include "AppInfo.h" +#include "Diagnostics.h" +#include "SdkConstants.h" +#include "filter/ConfigFilter.h" +#include "split/TableSplitter.h" +#include "util/Maybe.h" +#include "xml/XmlDom.h" + +namespace aapt { + +// Parses a configuration density (ex. hdpi, xxhdpi, 234dpi, anydpi, etc). +// Returns Nothing and logs a human friendly error message if the string was not legal. +Maybe<uint16_t> ParseTargetDensityParameter(const android::StringPiece& arg, IDiagnostics* diag); + +// Parses a string of the form 'path/to/output.apk:<config>[,<config>...]' and fills in +// `out_path` with the path and `out_split` with the set of ConfigDescriptions. +// Returns false and logs a human friendly error message if the string was not legal. +bool ParseSplitParameter(const android::StringPiece& arg, IDiagnostics* diag, std::string* out_path, + SplitConstraints* out_split); + +// Parses a set of config filter strings of the form 'en,fr-rFR' and returns an IConfigFilter. +// Returns nullptr and logs a human friendly error message if the string was not legal. +std::unique_ptr<IConfigFilter> ParseConfigFilterParameters(const std::vector<std::string>& args, + IDiagnostics* diag); + +// Adjust the SplitConstraints so that their SDK version is stripped if it +// is less than or equal to the min_sdk. Otherwise the resources that have had +// their SDK version stripped due to min_sdk won't ever match. +std::vector<SplitConstraints> AdjustSplitConstraintsForMinSdk( + int min_sdk, const std::vector<SplitConstraints>& split_constraints); + +// Generates a split AndroidManifest.xml given the split constraints and app info. The resulting +// XmlResource does not need to be linked via XmlReferenceLinker. +// This will never fail/return nullptr. +std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info, + const SplitConstraints& constraints); + +// Extracts relevant info from the AndroidManifest.xml. +Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(xml::XmlResource* xml_res, IDiagnostics* diag); + +} // namespace aapt + +#endif /* AAPT_SPLIT_UTIL_H */ diff --git a/tools/aapt2/io/Io.cpp b/tools/aapt2/io/Io.cpp deleted file mode 100644 index f5c5737cb149..000000000000 --- a/tools/aapt2/io/Io.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2016 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 "io/Io.h" - -#include <cstring> - -namespace aapt { -namespace io { - -bool Copy(OutputStream* out, InputStream* in) { - const void* in_buffer; - size_t in_len; - while (in->Next(&in_buffer, &in_len)) { - void* out_buffer; - size_t out_len; - if (!out->Next(&out_buffer, &out_len)) { - return !out->HadError(); - } - - const size_t bytes_to_copy = in_len < out_len ? in_len : out_len; - memcpy(out_buffer, in_buffer, bytes_to_copy); - out->BackUp(out_len - bytes_to_copy); - in->BackUp(in_len - bytes_to_copy); - } - return !in->HadError(); -} - -} // namespace io -} // namespace aapt diff --git a/tools/aapt2/io/Io.h b/tools/aapt2/io/Io.h index 2a34d4dd442d..a656740b35da 100644 --- a/tools/aapt2/io/Io.h +++ b/tools/aapt2/io/Io.h @@ -87,10 +87,6 @@ class OutputStream { virtual bool HadError() const = 0; }; -// Copies the data from in to out. Returns false if there was an error. -// If there was an error, check the individual streams' HadError/GetError methods. -bool Copy(OutputStream* out, InputStream* in); - } // namespace io } // namespace aapt diff --git a/tools/aapt2/io/Util.cpp b/tools/aapt2/io/Util.cpp new file mode 100644 index 000000000000..15114e8dbf54 --- /dev/null +++ b/tools/aapt2/io/Util.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 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 "io/Util.h" + +#include "google/protobuf/io/zero_copy_stream_impl_lite.h" + +namespace aapt { +namespace io { + +bool CopyInputStreamToArchive(IAaptContext* context, InputStream* in, const std::string& out_path, + uint32_t compression_flags, IArchiveWriter* writer) { + if (context->IsVerbose()) { + context->GetDiagnostics()->Note(DiagMessage() << "writing " << out_path << " to archive"); + } + + if (!writer->WriteFile(out_path, compression_flags, in)) { + context->GetDiagnostics()->Error(DiagMessage() << "failed to write " << out_path + << " to archive: " << writer->GetError()); + return false; + } + return true; +} + +bool CopyFileToArchive(IAaptContext* context, io::IFile* file, const std::string& out_path, + uint32_t compression_flags, IArchiveWriter* writer) { + std::unique_ptr<io::IData> data = file->OpenAsData(); + if (!data) { + context->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << "failed to open file"); + return false; + } + return CopyInputStreamToArchive(context, data.get(), out_path, compression_flags, writer); +} + +bool CopyProtoToArchive(IAaptContext* context, ::google::protobuf::MessageLite* proto_msg, + const std::string& out_path, uint32_t compression_flags, + IArchiveWriter* writer) { + if (context->IsVerbose()) { + context->GetDiagnostics()->Note(DiagMessage() << "writing " << out_path << " to archive"); + } + + if (writer->StartEntry(out_path, compression_flags)) { + // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->FinishEntry(). + { + // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface. + ::google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer); + if (!proto_msg->SerializeToZeroCopyStream(&adaptor)) { + context->GetDiagnostics()->Error(DiagMessage() << "failed to write " << out_path + << " to archive"); + return false; + } + } + + if (writer->FinishEntry()) { + return true; + } + } + context->GetDiagnostics()->Error(DiagMessage() << "failed to write " << out_path + << " to archive: " << writer->GetError()); + return false; +} + +bool Copy(OutputStream* out, InputStream* in) { + const void* in_buffer; + size_t in_len; + while (in->Next(&in_buffer, &in_len)) { + void* out_buffer; + size_t out_len; + if (!out->Next(&out_buffer, &out_len)) { + return !out->HadError(); + } + + const size_t bytes_to_copy = in_len < out_len ? in_len : out_len; + memcpy(out_buffer, in_buffer, bytes_to_copy); + out->BackUp(out_len - bytes_to_copy); + in->BackUp(in_len - bytes_to_copy); + } + return !in->HadError(); +} + +} // namespace io +} // namespace aapt diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h new file mode 100644 index 000000000000..ec1ddb8a07aa --- /dev/null +++ b/tools/aapt2/io/Util.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 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_IO_UTIL_H +#define AAPT_IO_UTIL_H + +#include <string> + +#include "google/protobuf/message_lite.h" + +#include "flatten/Archive.h" +#include "io/File.h" +#include "io/Io.h" +#include "process/IResourceTableConsumer.h" + +namespace aapt { +namespace io { + +bool CopyInputStreamToArchive(IAaptContext* context, InputStream* in, const std::string& out_path, + uint32_t compression_flags, IArchiveWriter* writer); + +bool CopyFileToArchive(IAaptContext* context, IFile* file, const std::string& out_path, + uint32_t compression_flags, IArchiveWriter* writer); + +bool CopyProtoToArchive(IAaptContext* context, ::google::protobuf::MessageLite* proto_msg, + const std::string& out_path, uint32_t compression_flags, + IArchiveWriter* writer); + +// Copies the data from in to out. Returns false if there was an error. +// If there was an error, check the individual streams' HadError/GetError methods. +bool Copy(OutputStream* out, InputStream* in); + +} // namespace io +} // namespace aapt + +#endif /* AAPT_IO_UTIL_H */ diff --git a/tools/aapt2/optimize/Optimize.cpp b/tools/aapt2/optimize/Optimize.cpp deleted file mode 100644 index 96159622e653..000000000000 --- a/tools/aapt2/optimize/Optimize.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2017 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 <memory> -#include <vector> - -#include "androidfw/StringPiece.h" - -#include "Diagnostics.h" -#include "Flags.h" -#include "LoadedApk.h" -#include "SdkConstants.h" -#include "flatten/TableFlattener.h" -#include "optimize/ResourceDeduper.h" -#include "optimize/VersionCollapser.h" -#include "split/TableSplitter.h" - -using android::StringPiece; - -namespace aapt { - -struct OptimizeOptions { - // Path to the output APK. - std::string output_path; - - // List of screen density configurations the APK will be optimized for. - std::vector<ConfigDescription> target_configs; - - TableFlattenerOptions table_flattener_options; -}; - -class OptimizeContext : public IAaptContext { - public: - IDiagnostics* GetDiagnostics() override { return &diagnostics_; } - - NameMangler* GetNameMangler() override { - abort(); - return nullptr; - } - - const std::string& GetCompilationPackage() override { - static std::string empty; - return empty; - } - - uint8_t GetPackageId() override { return 0; } - - SymbolTable* GetExternalSymbols() override { - abort(); - return nullptr; - } - - bool IsVerbose() override { return verbose_; } - - void SetVerbose(bool val) { verbose_ = val; } - - void SetMinSdkVersion(int sdk_version) { sdk_version_ = sdk_version; } - - int GetMinSdkVersion() override { return sdk_version_; } - - private: - StdErrDiagnostics diagnostics_; - bool verbose_ = false; - int sdk_version_ = 0; -}; - -class OptimizeCommand { - public: - OptimizeCommand(OptimizeContext* context, const OptimizeOptions& options) - : options_(options), - context_(context) {} - - int Run(std::unique_ptr<LoadedApk> apk) { - if (context_->IsVerbose()) { - context_->GetDiagnostics()->Note(DiagMessage() << "Optimizing APK..."); - } - - VersionCollapser collapser; - if (!collapser.Consume(context_, apk->GetResourceTable())) { - return 1; - } - - ResourceDeduper deduper; - if (!deduper.Consume(context_, apk->GetResourceTable())) { - context_->GetDiagnostics()->Error(DiagMessage() << "failed deduping resources"); - return 1; - } - - // Stripping the APK using the TableSplitter with no splits and the target - // densities as the preferred densities. The resource table is modified in - // place in the LoadedApk. - TableSplitterOptions splitter_options; - for (auto& config : options_.target_configs) { - splitter_options.preferred_densities.push_back(config.density); - } - std::vector<SplitConstraints> splits; - TableSplitter splitter(splits, splitter_options); - splitter.SplitTable(apk->GetResourceTable()); - - std::unique_ptr<IArchiveWriter> writer = - CreateZipFileArchiveWriter(context_->GetDiagnostics(), options_.output_path); - if (!apk->WriteToArchive(context_, options_.table_flattener_options, writer.get())) { - return 1; - } - - return 0; - } - - private: - OptimizeOptions options_; - OptimizeContext* context_; -}; - -int Optimize(const std::vector<StringPiece>& args) { - OptimizeContext context; - OptimizeOptions options; - Maybe<std::string> target_densities; - bool verbose = false; - Flags flags = - Flags() - .RequiredFlag("-o", "Path to the output APK.", &options.output_path) - .OptionalFlag( - "--target-densities", - "Comma separated list of the screen densities that the APK will " - "be optimized for. All the resources that would be unused on " - "devices of the given densities will be removed from the APK.", - &target_densities) - .OptionalSwitch("--enable-sparse-encoding", - "Enables encoding sparse entries using a binary search tree.\n" - "This decreases APK size at the cost of resource retrieval performance.", - &options.table_flattener_options.use_sparse_entries) - .OptionalSwitch("-v", "Enables verbose logging", &verbose); - - if (!flags.Parse("aapt2 optimize", args, &std::cerr)) { - return 1; - } - - if (flags.GetArgs().size() != 1u) { - std::cerr << "must have one APK as argument.\n\n"; - flags.Usage("aapt2 optimize", &std::cerr); - return 1; - } - - std::unique_ptr<LoadedApk> apk = - LoadedApk::LoadApkFromPath(&context, flags.GetArgs()[0]); - if (!apk) { - return 1; - } - - if (verbose) { - context.SetVerbose(verbose); - } - - if (target_densities) { - // Parse the target screen densities. - for (const StringPiece& config_str : util::Tokenize(target_densities.value(), ',')) { - ConfigDescription config; - if (!ConfigDescription::Parse(config_str, &config) || config.density == 0) { - context.GetDiagnostics()->Error( - DiagMessage() << "invalid density '" << config_str - << "' for --target-densities option"); - return 1; - } - - // Clear the version that can be automatically added. - config.sdkVersion = 0; - - if (config.diff(ConfigDescription::DefaultConfig()) != - ConfigDescription::CONFIG_DENSITY) { - context.GetDiagnostics()->Error( - DiagMessage() << "invalid density '" << config_str - << "' for --target-densities option. Must be only a " - << "density value."); - return 1; - } - - options.target_configs.push_back(config); - } - } - - // TODO(adamlesinski): Read manfiest and set the proper minSdkVersion. - // context.SetMinSdkVersion(SDK_O); - - OptimizeCommand cmd(&context, options); - return cmd.Run(std::move(apk)); -} - -} // namespace aapt diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md index 2e674bddce7e..daf1ebc20461 100644 --- a/tools/aapt2/readme.md +++ b/tools/aapt2/readme.md @@ -1,5 +1,10 @@ # Android Asset Packaging Tool 2.0 (AAPT2) release notes +## Version 2.13 +### `aapt2 optimize ...` +- aapt2 optimize can now split a binary APK with the same --split parameters as the link + phase. + ## Version 2.12 ### `aapt2 optimize ...` - aapt2 optimize now understands map (complex) values under the type `id`. It ignores their diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp index 35bf618c1635..20a45312993d 100644 --- a/tools/aapt2/unflatten/BinaryResourceParser.cpp +++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp @@ -73,15 +73,16 @@ class ReferenceIdToNameVisitor : public ValueVisitor { } // namespace -BinaryResourceParser::BinaryResourceParser(IAaptContext* context, - ResourceTable* table, - const Source& source, - const void* data, size_t len) +BinaryResourceParser::BinaryResourceParser(IAaptContext* context, ResourceTable* table, + const Source& source, const void* data, size_t len, + io::IFileCollection* files) : context_(context), table_(table), source_(source), data_(data), - data_len_(len) {} + data_len_(len), + files_(files) { +} bool BinaryResourceParser::Parse() { ResChunkPullParser parser(data_, data_len_); @@ -359,16 +360,14 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package, std::unique_ptr<Value> resource_value; if (entry->flags & ResTable_entry::FLAG_COMPLEX) { - const ResTable_map_entry* mapEntry = - static_cast<const ResTable_map_entry*>(entry); + const ResTable_map_entry* mapEntry = static_cast<const ResTable_map_entry*>(entry); // TODO(adamlesinski): Check that the entry count is valid. resource_value = ParseMapEntry(name, config, mapEntry); } else { const Res_value* value = - (const Res_value*)((const uint8_t*)entry + - util::DeviceToHost32(entry->size)); - resource_value = ParseValue(name, config, value, entry->flags); + (const Res_value*)((const uint8_t*)entry + util::DeviceToHost32(entry->size)); + resource_value = ParseValue(name, config, *value); } if (!resource_value) { @@ -388,8 +387,7 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package, Symbol symbol; symbol.state = SymbolState::kPublic; symbol.source = source_.WithLine(0); - if (!table_->SetSymbolStateAllowMangled(name, res_id, symbol, - context_->GetDiagnostics())) { + if (!table_->SetSymbolStateAllowMangled(name, res_id, symbol, context_->GetDiagnostics())) { return false; } } @@ -419,70 +417,25 @@ bool BinaryResourceParser::ParseLibrary(const ResChunk_header* chunk) { return true; } -std::unique_ptr<Item> BinaryResourceParser::ParseValue( - const ResourceNameRef& name, const ConfigDescription& config, - const Res_value* value, uint16_t flags) { - if (name.type == ResourceType::kId) { - return util::make_unique<Id>(); - } - - const uint32_t data = util::DeviceToHost32(value->data); - - if (value->dataType == Res_value::TYPE_STRING) { - const std::string str = util::GetString(value_pool_, data); - - const ResStringPool_span* spans = value_pool_.styleAt(data); - - // Check if the string has a valid style associated with it. - if (spans != nullptr && spans->name.index != ResStringPool_span::END) { - StyleString style_str = {str}; - while (spans->name.index != ResStringPool_span::END) { - style_str.spans.push_back( - Span{util::GetString(value_pool_, spans->name.index), - spans->firstChar, spans->lastChar}); - spans++; - } - return util::make_unique<StyledString>(table_->string_pool.MakeRef( - style_str, - StringPool::Context(StringPool::Context::kStylePriority, config))); - } else { - if (name.type != ResourceType::kString && util::StartsWith(str, "res/")) { - // This must be a FileReference. - return util::make_unique<FileReference>(table_->string_pool.MakeRef( - str, - StringPool::Context(StringPool::Context::kHighPriority, config))); +std::unique_ptr<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& name, + const ConfigDescription& config, + const android::Res_value& value) { + std::unique_ptr<Item> item = ResourceUtils::ParseBinaryResValue(name.type, config, value_pool_, + value, &table_->string_pool); + if (files_ != nullptr && item != nullptr) { + FileReference* file_ref = ValueCast<FileReference>(item.get()); + if (file_ref != nullptr) { + file_ref->file = files_->FindFile(*file_ref->path); + if (file_ref->file == nullptr) { + context_->GetDiagnostics()->Error(DiagMessage() << "resource " << name << " for config '" + << config << "' is a file reference to '" + << *file_ref->path + << "' but no such path exists"); + return {}; } - - // There are no styles associated with this string, so treat it as - // a simple string. - return util::make_unique<String>( - table_->string_pool.MakeRef(str, StringPool::Context(config))); - } - } - - if (value->dataType == Res_value::TYPE_REFERENCE || - value->dataType == Res_value::TYPE_ATTRIBUTE || - value->dataType == Res_value::TYPE_DYNAMIC_REFERENCE || - value->dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE) { - Reference::Type type = Reference::Type::kResource; - if (value->dataType == Res_value::TYPE_ATTRIBUTE || - value->dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE) { - type = Reference::Type::kAttribute; } - - if (data == 0) { - // A reference of 0, must be the magic @null reference. - Res_value null_type = {}; - null_type.dataType = Res_value::TYPE_REFERENCE; - return util::make_unique<BinaryPrimitive>(null_type); - } - - // This is a normal reference. - return util::make_unique<Reference>(data, type); } - - // Treat this as a raw binary primitive. - return util::make_unique<BinaryPrimitive>(*value); + return item; } std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry( @@ -528,7 +481,7 @@ std::unique_ptr<Style> BinaryResourceParser::ParseStyle( Style::Entry style_entry; style_entry.key = Reference(util::DeviceToHost32(map_entry.name.ident)); - style_entry.value = ParseValue(name, config, &map_entry.value, 0); + style_entry.value = ParseValue(name, config, map_entry.value); if (!style_entry.value) { return {}; } @@ -586,7 +539,7 @@ std::unique_ptr<Array> BinaryResourceParser::ParseArray( const ResTable_map_entry* map) { std::unique_ptr<Array> array = util::make_unique<Array>(); for (const ResTable_map& map_entry : map) { - array->items.push_back(ParseValue(name, config, &map_entry.value, 0)); + array->items.push_back(ParseValue(name, config, map_entry.value)); } return array; } @@ -596,7 +549,7 @@ std::unique_ptr<Plural> BinaryResourceParser::ParsePlural( const ResTable_map_entry* map) { std::unique_ptr<Plural> plural = util::make_unique<Plural>(); for (const ResTable_map& map_entry : map) { - std::unique_ptr<Item> item = ParseValue(name, config, &map_entry.value, 0); + std::unique_ptr<Item> item = ParseValue(name, config, map_entry.value); if (!item) { return {}; } diff --git a/tools/aapt2/unflatten/BinaryResourceParser.h b/tools/aapt2/unflatten/BinaryResourceParser.h index e3dd80212f12..c41ada09e551 100644 --- a/tools/aapt2/unflatten/BinaryResourceParser.h +++ b/tools/aapt2/unflatten/BinaryResourceParser.h @@ -45,8 +45,8 @@ class BinaryResourceParser { * Creates a parser, which will read `len` bytes from `data`, and * add any resources parsed to `table`. `source` is for logging purposes. */ - BinaryResourceParser(IAaptContext* context, ResourceTable* table, - const Source& source, const void* data, size_t data_len); + BinaryResourceParser(IAaptContext* context, ResourceTable* table, const Source& source, + const void* data, size_t data_len, io::IFileCollection* files = nullptr); /* * Parses the binary resource table and returns true if successful. @@ -63,10 +63,8 @@ class BinaryResourceParser { const android::ResChunk_header* chunk); bool ParseLibrary(const android::ResChunk_header* chunk); - std::unique_ptr<Item> ParseValue(const ResourceNameRef& name, - const ConfigDescription& config, - const android::Res_value* value, - uint16_t flags); + std::unique_ptr<Item> ParseValue(const ResourceNameRef& name, const ConfigDescription& config, + const android::Res_value& value); std::unique_ptr<Value> ParseMapEntry(const ResourceNameRef& name, const ConfigDescription& config, @@ -104,6 +102,9 @@ class BinaryResourceParser { const void* data_; const size_t data_len_; + // Optional file collection from which to create io::IFile objects. + io::IFileCollection* files_; + // The standard value string pool for resource values. android::ResStringPool value_pool_; diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h index 7210d21f64ab..30b9af61d896 100644 --- a/tools/aapt2/util/Util.h +++ b/tools/aapt2/util/Util.h @@ -99,8 +99,7 @@ Maybe<std::string> GetFullyQualifiedClassName(const android::StringPiece& packag const android::StringPiece& class_name); /** - * Makes a std::unique_ptr<> with the template parameter inferred by the - * compiler. + * Makes a std::unique_ptr<> with the template parameter inferred by the compiler. * This will be present in C++14 and can be removed then. */ template <typename T, class... Args> diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp index d9ea1bcf3766..4a278f632a4a 100644 --- a/tools/aapt2/xml/XmlDom.cpp +++ b/tools/aapt2/xml/XmlDom.cpp @@ -25,6 +25,7 @@ #include "android-base/logging.h" +#include "ResourceUtils.h" #include "XmlPullParser.h" #include "util/Util.h" @@ -193,16 +194,14 @@ static void XMLCALL CommentDataHandler(void* user_data, const char* comment) { stack->pending_comment += comment; } -std::unique_ptr<XmlResource> Inflate(std::istream* in, IDiagnostics* diag, - const Source& source) { +std::unique_ptr<XmlResource> Inflate(std::istream* in, IDiagnostics* diag, const Source& source) { Stack stack; XML_Parser parser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep); XML_SetUserData(parser, &stack); XML_UseParserAsHandlerArg(parser); XML_SetElementHandler(parser, StartElementHandler, EndElementHandler); - XML_SetNamespaceDeclHandler(parser, StartNamespaceHandler, - EndNamespaceHandler); + XML_SetNamespaceDeclHandler(parser, StartNamespaceHandler, EndNamespaceHandler); XML_SetCharacterDataHandler(parser, CharacterDataHandler); XML_SetCommentHandler(parser, CommentDataHandler); @@ -215,8 +214,7 @@ std::unique_ptr<XmlResource> Inflate(std::istream* in, IDiagnostics* diag, break; } - if (XML_Parse(parser, buffer, in->gcount(), in->eof()) == - XML_STATUS_ERROR) { + if (XML_Parse(parser, buffer, in->gcount(), in->eof()) == XML_STATUS_ERROR) { stack.root = {}; diag->Error(DiagMessage(source.WithLine(XML_GetCurrentLineNumber(parser))) << XML_ErrorString(XML_GetErrorCode(parser))); @@ -226,13 +224,12 @@ std::unique_ptr<XmlResource> Inflate(std::istream* in, IDiagnostics* diag, XML_ParserFree(parser); if (stack.root) { - return util::make_unique<XmlResource>(ResourceFile{{}, {}, source}, - std::move(stack.root)); + return util::make_unique<XmlResource>(ResourceFile{{}, {}, source}, std::move(stack.root)); } return {}; } -static void CopyAttributes(Element* el, android::ResXMLParser* parser) { +static void CopyAttributes(Element* el, android::ResXMLParser* parser, StringPool* out_pool) { const size_t attr_count = parser->getAttributeCount(); if (attr_count > 0) { el->attributes.reserve(attr_count); @@ -253,18 +250,26 @@ static void CopyAttributes(Element* el, android::ResXMLParser* parser) { if (str16) { attr.value = util::Utf16ToUtf8(StringPiece16(str16, len)); } + + android::Res_value res_value; + if (parser->getAttributeValue(i, &res_value) > 0) { + attr.compiled_value = ResourceUtils::ParseBinaryResValue( + ResourceType::kAnim, {}, parser->getStrings(), res_value, out_pool); + } + el->attributes.push_back(std::move(attr)); } } } -std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, - IDiagnostics* diag, const Source& source) { +std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnostics* diag, + const Source& source) { // We import the android namespace because on Windows NO_ERROR is a macro, not // an enum, which // causes errors when qualifying it with android:: using namespace android; + StringPool string_pool; std::unique_ptr<Node> root; std::stack<Node*> node_stack; @@ -307,7 +312,7 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, node->name = util::Utf16ToUtf8(StringPiece16(str16, len)); } - CopyAttributes(node.get(), &tree); + CopyAttributes(node.get(), &tree, &string_pool); new_node = std::move(node); break; @@ -352,7 +357,7 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, } } } - return util::make_unique<XmlResource>(ResourceFile{}, std::move(root)); + return util::make_unique<XmlResource>(ResourceFile{}, std::move(root), std::move(string_pool)); } std::unique_ptr<Node> Namespace::Clone() { diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h index 90cdfb6932b1..f1d0953c5494 100644 --- a/tools/aapt2/xml/XmlDom.h +++ b/tools/aapt2/xml/XmlDom.h @@ -129,21 +129,21 @@ class XmlResource { public: ResourceFile file; std::unique_ptr<xml::Node> root; + StringPool string_pool; }; /** * Inflates an XML DOM from a text stream, logging errors to the logger. * Returns the root node on success, or nullptr on failure. */ -std::unique_ptr<XmlResource> Inflate(std::istream* in, IDiagnostics* diag, - const Source& source); +std::unique_ptr<XmlResource> Inflate(std::istream* in, IDiagnostics* diag, const Source& source); /** * Inflates an XML DOM from a binary ResXMLTree, logging errors to the logger. * Returns the root node on success, or nullptr on failure. */ -std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, - IDiagnostics* diag, const Source& source); +std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnostics* diag, + const Source& source); Element* FindRootElement(XmlResource* doc); Element* FindRootElement(Node* node); |