Merge "AAPT2: Share split functionality between link and optimize" into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
3750fd24c6
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 << "."
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
369
tools/aapt2/cmd/Optimize.cpp
Normal file
369
tools/aapt2/cmd/Optimize.cpp
Normal file
@@ -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
|
||||
347
tools/aapt2/cmd/Util.cpp
Normal file
347
tools/aapt2/cmd/Util.cpp
Normal file
@@ -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
|
||||
64
tools/aapt2/cmd/Util.h
Normal file
64
tools/aapt2/cmd/Util.h
Normal file
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
95
tools/aapt2/io/Util.cpp
Normal file
95
tools/aapt2/io/Util.cpp
Normal file
@@ -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
|
||||
49
tools/aapt2/io/Util.h
Normal file
49
tools/aapt2/io/Util.h
Normal file
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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++;
|
||||
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 {};
|
||||
}
|
||||
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)));
|
||||
}
|
||||
|
||||
// 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 {};
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user