Merge "AAPT2: Add convert command"
This commit is contained in:
committed by
Android (Google) Code Review
commit
4ee829a707
@@ -16,6 +16,7 @@
|
||||
|
||||
toolSources = [
|
||||
"cmd/Compile.cpp",
|
||||
"cmd/Convert.cpp",
|
||||
"cmd/Diff.cpp",
|
||||
"cmd/Dump.cpp",
|
||||
"cmd/Link.cpp",
|
||||
|
||||
@@ -21,43 +21,139 @@
|
||||
#include "format/Archive.h"
|
||||
#include "format/binary/TableFlattener.h"
|
||||
#include "format/binary/XmlFlattener.h"
|
||||
#include "format/proto/ProtoDeserialize.h"
|
||||
#include "io/BigBufferStream.h"
|
||||
#include "io/Util.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
using ::aapt::io::IFile;
|
||||
using ::aapt::io::IFileCollection;
|
||||
using ::aapt::xml::XmlResource;
|
||||
using ::android::StringPiece;
|
||||
using ::std::unique_ptr;
|
||||
|
||||
namespace aapt {
|
||||
|
||||
using xml::XmlResource;
|
||||
|
||||
std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context,
|
||||
const android::StringPiece& path) {
|
||||
std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(const StringPiece& path, IDiagnostics* diag) {
|
||||
Source source(path);
|
||||
std::string error;
|
||||
std::unique_ptr<io::ZipFileCollection> apk = io::ZipFileCollection::Create(path, &error);
|
||||
if (!apk) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(source) << error);
|
||||
if (apk == nullptr) {
|
||||
diag->Error(DiagMessage(path) << "failed opening zip: " << error);
|
||||
return {};
|
||||
}
|
||||
|
||||
io::IFile* file = apk->FindFile("resources.arsc");
|
||||
if (!file) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(source) << "no resources.arsc found");
|
||||
if (apk->FindFile("resources.arsc") != nullptr) {
|
||||
return LoadBinaryApkFromFileCollection(source, std::move(apk), diag);
|
||||
} else if (apk->FindFile("resources.pb") != nullptr) {
|
||||
return LoadProtoApkFromFileCollection(source, std::move(apk), diag);
|
||||
}
|
||||
diag->Error(DiagMessage(path) << "no resource table found");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<LoadedApk> LoadedApk::LoadProtoApkFromFileCollection(
|
||||
const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) {
|
||||
io::IFile* table_file = collection->FindFile(kProtoResourceTablePath);
|
||||
if (table_file == nullptr) {
|
||||
diag->Error(DiagMessage(source) << "failed to find " << kProtoResourceTablePath);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<io::IData> data = file->OpenAsData();
|
||||
if (!data) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(source) << "could not open resources.arsc");
|
||||
std::unique_ptr<io::InputStream> in = table_file->OpenInputStream();
|
||||
if (in == nullptr) {
|
||||
diag->Error(DiagMessage(source) << "failed to open " << kProtoResourceTablePath);
|
||||
return {};
|
||||
}
|
||||
|
||||
pb::ResourceTable pb_table;
|
||||
io::ZeroCopyInputAdaptor adaptor(in.get());
|
||||
if (!pb_table.ParseFromZeroCopyStream(&adaptor)) {
|
||||
diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string error;
|
||||
std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
|
||||
if (!DeserializeTableFromPb(pb_table, collection.get(), table.get(), &error)) {
|
||||
diag->Error(DiagMessage(source)
|
||||
<< "failed to deserialize " << kProtoResourceTablePath << ": " << error);
|
||||
return {};
|
||||
}
|
||||
|
||||
io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath);
|
||||
if (manifest_file == nullptr) {
|
||||
diag->Error(DiagMessage(source) << "failed to find " << kAndroidManifestPath);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
|
||||
if (manifest_in == nullptr) {
|
||||
diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath);
|
||||
return {};
|
||||
}
|
||||
|
||||
pb::XmlNode pb_node;
|
||||
io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get());
|
||||
if (!pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) {
|
||||
diag->Error(DiagMessage(source) << "failed to read proto " << kAndroidManifestPath);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<xml::XmlResource> manifest = DeserializeXmlResourceFromPb(pb_node, &error);
|
||||
if (manifest == nullptr) {
|
||||
diag->Error(DiagMessage(source)
|
||||
<< "failed to deserialize proto " << kAndroidManifestPath << ": " << error);
|
||||
return {};
|
||||
}
|
||||
return util::make_unique<LoadedApk>(source, std::move(collection), std::move(table),
|
||||
std::move(manifest));
|
||||
}
|
||||
|
||||
std::unique_ptr<LoadedApk> LoadedApk::LoadBinaryApkFromFileCollection(
|
||||
const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) {
|
||||
io::IFile* table_file = collection->FindFile(kApkResourceTablePath);
|
||||
if (table_file == nullptr) {
|
||||
diag->Error(DiagMessage(source) << "failed to find " << kApkResourceTablePath);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<io::IData> data = table_file->OpenAsData();
|
||||
if (data == nullptr) {
|
||||
diag->Error(DiagMessage(source) << "failed to open " << kApkResourceTablePath);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
|
||||
BinaryResourceParser parser(context, table.get(), source, data->data(), data->size(), apk.get());
|
||||
BinaryResourceParser parser(diag, table.get(), source, data->data(), data->size(),
|
||||
collection.get());
|
||||
if (!parser.Parse()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
|
||||
io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath);
|
||||
if (manifest_file == nullptr) {
|
||||
diag->Error(DiagMessage(source) << "failed to find " << kAndroidManifestPath);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
|
||||
if (manifest_data == nullptr) {
|
||||
diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string error;
|
||||
std::unique_ptr<xml::XmlResource> manifest =
|
||||
xml::Inflate(manifest_data->data(), manifest_data->size(), &error);
|
||||
if (manifest == nullptr) {
|
||||
diag->Error(DiagMessage(source)
|
||||
<< "failed to parse binary " << kAndroidManifestPath << ": " << error);
|
||||
return {};
|
||||
}
|
||||
return util::make_unique<LoadedApk>(source, std::move(collection), std::move(table),
|
||||
std::move(manifest));
|
||||
}
|
||||
|
||||
bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
|
||||
@@ -148,26 +244,4 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<xml::XmlResource> LoadedApk::InflateManifest(IAaptContext* context) {
|
||||
IDiagnostics* diag = context->GetDiagnostics();
|
||||
|
||||
io::IFile* manifest_file = GetFileCollection()->FindFile("AndroidManifest.xml");
|
||||
if (manifest_file == nullptr) {
|
||||
diag->Error(DiagMessage(source_) << "no AndroidManifest.xml found");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
|
||||
if (manifest_data == nullptr) {
|
||||
diag->Error(DiagMessage(manifest_file->GetSource()) << "could not open AndroidManifest.xml");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<xml::XmlResource> manifest =
|
||||
xml::Inflate(manifest_data->data(), manifest_data->size(), diag, manifest_file->GetSource());
|
||||
if (manifest == nullptr) {
|
||||
diag->Error(DiagMessage() << "failed to read binary AndroidManifest.xml");
|
||||
}
|
||||
return manifest;
|
||||
}
|
||||
} // namespace aapt
|
||||
|
||||
@@ -29,20 +29,41 @@
|
||||
|
||||
namespace aapt {
|
||||
|
||||
constexpr static const char kApkResourceTablePath[] = "resources.arsc";
|
||||
constexpr static const char kProtoResourceTablePath[] = "resources.pb";
|
||||
constexpr static const char kAndroidManifestPath[] = "AndroidManifest.xml";
|
||||
|
||||
// Info about an APK loaded in memory.
|
||||
class LoadedApk {
|
||||
public:
|
||||
LoadedApk(
|
||||
const Source& source,
|
||||
std::unique_ptr<io::IFileCollection> apk,
|
||||
std::unique_ptr<ResourceTable> table)
|
||||
: source_(source), apk_(std::move(apk)), table_(std::move(table)) {}
|
||||
virtual ~LoadedApk() = default;
|
||||
// Loads both binary and proto APKs from disk.
|
||||
static std::unique_ptr<LoadedApk> LoadApkFromPath(const ::android::StringPiece& path,
|
||||
IDiagnostics* diag);
|
||||
|
||||
// Loads a proto APK from the given file collection.
|
||||
static std::unique_ptr<LoadedApk> LoadProtoApkFromFileCollection(
|
||||
const Source& source, std::unique_ptr<io::IFileCollection> collection, IDiagnostics* diag);
|
||||
|
||||
// Loads a binary APK from the given file collection.
|
||||
static std::unique_ptr<LoadedApk> LoadBinaryApkFromFileCollection(
|
||||
const Source& source, std::unique_ptr<io::IFileCollection> collection, IDiagnostics* diag);
|
||||
|
||||
LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk,
|
||||
std::unique_ptr<ResourceTable> table, std::unique_ptr<xml::XmlResource> manifest)
|
||||
: source_(source),
|
||||
apk_(std::move(apk)),
|
||||
table_(std::move(table)),
|
||||
manifest_(std::move(manifest)) {
|
||||
}
|
||||
|
||||
io::IFileCollection* GetFileCollection() {
|
||||
return apk_.get();
|
||||
}
|
||||
|
||||
const ResourceTable* GetResourceTable() const {
|
||||
return table_.get();
|
||||
}
|
||||
|
||||
ResourceTable* GetResourceTable() {
|
||||
return table_.get();
|
||||
}
|
||||
@@ -51,6 +72,10 @@ class LoadedApk {
|
||||
return source_;
|
||||
}
|
||||
|
||||
const xml::XmlResource* GetManifest() const {
|
||||
return manifest_.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the APK on disk at the given path, while also removing the resource
|
||||
* files that are not referenced in the resource table.
|
||||
@@ -71,11 +96,6 @@ class LoadedApk {
|
||||
const TableFlattenerOptions& options, FilterChain* filters,
|
||||
IArchiveWriter* writer, xml::XmlResource* manifest = nullptr);
|
||||
|
||||
/** Inflates the AndroidManifest.xml file from the APK. */
|
||||
std::unique_ptr<xml::XmlResource> InflateManifest(IAaptContext* context);
|
||||
|
||||
static std::unique_ptr<LoadedApk> LoadApkFromPath(IAaptContext* context,
|
||||
const android::StringPiece& path);
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(LoadedApk);
|
||||
@@ -83,6 +103,7 @@ class LoadedApk {
|
||||
Source source_;
|
||||
std::unique_ptr<io::IFileCollection> apk_;
|
||||
std::unique_ptr<ResourceTable> table_;
|
||||
std::unique_ptr<xml::XmlResource> manifest_;
|
||||
};
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -50,7 +50,7 @@ static void PrintVersion() {
|
||||
}
|
||||
|
||||
static void PrintUsage() {
|
||||
std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|version] ..." << std::endl;
|
||||
std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|convert|version] ..." << std::endl;
|
||||
}
|
||||
|
||||
extern int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics);
|
||||
@@ -58,6 +58,7 @@ extern int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics)
|
||||
extern int Dump(const std::vector<StringPiece>& args);
|
||||
extern int Diff(const std::vector<StringPiece>& args);
|
||||
extern int Optimize(const std::vector<StringPiece>& args);
|
||||
extern int Convert(const std::vector<StringPiece>& args);
|
||||
|
||||
static int ExecuteCommand(const StringPiece& command, const std::vector<StringPiece>& args,
|
||||
IDiagnostics* diagnostics) {
|
||||
@@ -71,6 +72,8 @@ static int ExecuteCommand(const StringPiece& command, const std::vector<StringPi
|
||||
return Diff(args);
|
||||
} else if (command == "optimize") {
|
||||
return Optimize(args);
|
||||
} else if (command == "convert") {
|
||||
return Convert(args);
|
||||
} else if (command == "version") {
|
||||
PrintVersion();
|
||||
return 0;
|
||||
|
||||
228
tools/aapt2/cmd/Convert.cpp
Normal file
228
tools/aapt2/cmd/Convert.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* 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 <vector>
|
||||
|
||||
#include "android-base/macros.h"
|
||||
#include "androidfw/StringPiece.h"
|
||||
|
||||
#include "Flags.h"
|
||||
#include "LoadedApk.h"
|
||||
#include "ValueVisitor.h"
|
||||
#include "cmd/Util.h"
|
||||
#include "format/binary/TableFlattener.h"
|
||||
#include "format/binary/XmlFlattener.h"
|
||||
#include "format/proto/ProtoDeserialize.h"
|
||||
#include "io/BigBufferStream.h"
|
||||
#include "io/Util.h"
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "process/SymbolTable.h"
|
||||
|
||||
using ::android::StringPiece;
|
||||
using ::std::unique_ptr;
|
||||
using ::std::vector;
|
||||
|
||||
namespace aapt {
|
||||
|
||||
static bool FlattenXml(IAaptContext* context, const xml::XmlResource& xml,
|
||||
const std::string& entry_path, bool utf16, IArchiveWriter* writer) {
|
||||
BigBuffer buffer(4096);
|
||||
XmlFlattenerOptions options = {};
|
||||
options.use_utf16 = utf16;
|
||||
XmlFlattener flattener(&buffer, options);
|
||||
if (!flattener.Consume(context, &xml)) {
|
||||
return false;
|
||||
}
|
||||
io::BigBufferInputStream input_stream(&buffer);
|
||||
return io::CopyInputStreamToArchive(context, &input_stream, entry_path, ArchiveEntry::kCompress,
|
||||
writer);
|
||||
}
|
||||
|
||||
bool ConvertProtoApkToBinaryApk(IAaptContext* context, unique_ptr<LoadedApk> apk,
|
||||
const TableFlattenerOptions& options, IArchiveWriter* writer) {
|
||||
if (!FlattenXml(context, *apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BigBuffer buffer(4096);
|
||||
TableFlattener table_flattener(options, &buffer);
|
||||
if (!table_flattener.Consume(context, apk->GetResourceTable())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
io::BigBufferInputStream input_stream(&buffer);
|
||||
if (!io::CopyInputStreamToArchive(context, &input_stream, kApkResourceTablePath,
|
||||
ArchiveEntry::kAlign, writer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& package : apk->GetResourceTable()->packages) {
|
||||
for (const auto& type : package->types) {
|
||||
for (const auto& entry : type->entries) {
|
||||
for (const auto& config_value : entry->values) {
|
||||
const FileReference* file = ValueCast<FileReference>(config_value->value.get());
|
||||
if (file != nullptr) {
|
||||
if (file->file == nullptr) {
|
||||
context->GetDiagnostics()->Warn(DiagMessage(apk->GetSource())
|
||||
<< "no file associated with " << *file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file->type == ResourceFile::Type::kProtoXml) {
|
||||
unique_ptr<io::InputStream> in = file->file->OpenInputStream();
|
||||
if (in == nullptr) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
|
||||
<< "failed to open file " << *file->path);
|
||||
return false;
|
||||
}
|
||||
|
||||
pb::XmlNode pb_node;
|
||||
io::ZeroCopyInputAdaptor adaptor(in.get());
|
||||
if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
|
||||
<< "failed to parse proto XML " << *file->path);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string error;
|
||||
unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error);
|
||||
if (xml == nullptr) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
|
||||
<< "failed to deserialize proto XML "
|
||||
<< *file->path << ": " << error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FlattenXml(context, *xml, *file->path, false /*utf16*/, writer)) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
|
||||
<< "failed to serialize XML " << *file->path);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!io::CopyFileToArchive(context, file->file, *file->path,
|
||||
file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u,
|
||||
writer)) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
|
||||
<< "failed to copy file " << *file->path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // file
|
||||
} // config_value
|
||||
} // entry
|
||||
} // type
|
||||
} // package
|
||||
return true;
|
||||
}
|
||||
|
||||
class Context : public IAaptContext {
|
||||
public:
|
||||
Context() : mangler_({}), symbols_(&mangler_) {
|
||||
}
|
||||
|
||||
PackageType GetPackageType() override {
|
||||
return PackageType::kApp;
|
||||
}
|
||||
|
||||
SymbolTable* GetExternalSymbols() override {
|
||||
return &symbols_;
|
||||
}
|
||||
|
||||
IDiagnostics* GetDiagnostics() override {
|
||||
return &diag_;
|
||||
}
|
||||
|
||||
const std::string& GetCompilationPackage() override {
|
||||
return package_;
|
||||
}
|
||||
|
||||
uint8_t GetPackageId() override {
|
||||
// Nothing should call this.
|
||||
UNIMPLEMENTED(FATAL) << "PackageID should not be necessary";
|
||||
return 0;
|
||||
}
|
||||
|
||||
NameMangler* GetNameMangler() override {
|
||||
UNIMPLEMENTED(FATAL);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool IsVerbose() override {
|
||||
return verbose_;
|
||||
}
|
||||
|
||||
int GetMinSdkVersion() override {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
bool verbose_ = false;
|
||||
std::string package_;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(Context);
|
||||
|
||||
NameMangler mangler_;
|
||||
SymbolTable symbols_;
|
||||
StdErrDiagnostics diag_;
|
||||
};
|
||||
|
||||
int Convert(const vector<StringPiece>& args) {
|
||||
Context context;
|
||||
std::string output_path;
|
||||
TableFlattenerOptions options;
|
||||
Flags flags =
|
||||
Flags()
|
||||
.RequiredFlag("-o", "Output path", &output_path)
|
||||
.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.use_sparse_entries)
|
||||
.OptionalSwitch("-v", "Enables verbose logging", &context.verbose_);
|
||||
if (!flags.Parse("aapt2 convert", args, &std::cerr)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (flags.GetArgs().size() != 1) {
|
||||
std::cerr << "must supply a single proto APK\n";
|
||||
flags.Usage("aapt2 convert", &std::cerr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const StringPiece& path = flags.GetArgs()[0];
|
||||
unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics());
|
||||
if (apk == nullptr) {
|
||||
context.GetDiagnostics()->Error(DiagMessage(path) << "failed to load APK");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Maybe<AppInfo> app_info =
|
||||
ExtractAppInfoFromBinaryManifest(*apk->GetManifest(), context.GetDiagnostics());
|
||||
if (!app_info) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
context.package_ = app_info.value().package;
|
||||
|
||||
unique_ptr<IArchiveWriter> writer =
|
||||
CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path);
|
||||
if (writer == nullptr) {
|
||||
return 1;
|
||||
}
|
||||
return ConvertProtoApkToBinaryApk(&context, std::move(apk), options, writer.get()) ? 0 : 1;
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
@@ -357,8 +357,9 @@ 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]);
|
||||
IDiagnostics* diag = context.GetDiagnostics();
|
||||
std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(flags.GetArgs()[0], diag);
|
||||
std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(flags.GetArgs()[1], diag);
|
||||
if (!apk_a || !apk_b) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DeserializeTableFromPb(pb_table, &table, &err)) {
|
||||
if (!DeserializeTableFromPb(pb_table, zip.get(), &table, &err)) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(file_path)
|
||||
<< "failed to parse table: " << err);
|
||||
return false;
|
||||
@@ -90,7 +90,8 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BinaryResourceParser parser(context, &table, Source(file_path), data->data(), data->size());
|
||||
BinaryResourceParser parser(context->GetDiagnostics(), &table, Source(file_path),
|
||||
data->data(), data->size());
|
||||
if (!parser.Parse()) {
|
||||
return false;
|
||||
}
|
||||
@@ -129,7 +130,7 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path) {
|
||||
|
||||
ResourceTable table;
|
||||
err.clear();
|
||||
if (!DeserializeTableFromPb(pb_table, &table, &err)) {
|
||||
if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &err)) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(file_path)
|
||||
<< "failed to parse table: " << err);
|
||||
continue;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "AppInfo.h"
|
||||
#include "Debug.h"
|
||||
#include "Flags.h"
|
||||
#include "LoadedApk.h"
|
||||
#include "Locale.h"
|
||||
#include "NameMangler.h"
|
||||
#include "ResourceUtils.h"
|
||||
@@ -39,7 +40,6 @@
|
||||
#include "filter/ConfigFilter.h"
|
||||
#include "format/Archive.h"
|
||||
#include "format/Container.h"
|
||||
#include "format/binary/BinaryResourceParser.h"
|
||||
#include "format/binary/TableFlattener.h"
|
||||
#include "format/binary/XmlFlattener.h"
|
||||
#include "format/proto/ProtoDeserialize.h"
|
||||
@@ -71,9 +71,6 @@ using ::android::base::StringPrintf;
|
||||
|
||||
namespace aapt {
|
||||
|
||||
constexpr static const char kApkResourceTablePath[] = "resources.arsc";
|
||||
constexpr static const char kProtoResourceTablePath[] = "resources.pb";
|
||||
|
||||
enum class OutputFormat {
|
||||
kApk,
|
||||
kProto,
|
||||
@@ -298,23 +295,6 @@ static bool FlattenXml(IAaptContext* context, const xml::XmlResource& xml_res,
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::unique_ptr<ResourceTable> LoadTableFromPb(const Source& source, const void* data,
|
||||
size_t len, IDiagnostics* diag) {
|
||||
pb::ResourceTable pb_table;
|
||||
if (!pb_table.ParseFromArray(data, len)) {
|
||||
diag->Error(DiagMessage(source) << "invalid compiled table");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
|
||||
std::string error;
|
||||
if (!DeserializeTableFromPb(pb_table, table.get(), &error)) {
|
||||
diag->Error(DiagMessage(source) << "invalid compiled table: " << error);
|
||||
return {};
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
// Inflates an XML file from the source path.
|
||||
static std::unique_ptr<xml::XmlResource> LoadXml(const std::string& path, IDiagnostics* diag) {
|
||||
FileInputStream fin(path);
|
||||
@@ -587,7 +567,7 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
|
||||
pb::XmlNode pb_xml_node;
|
||||
if (!pb_xml_node.ParseFromArray(data->data(), static_cast<int>(data->size()))) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(file->GetSource())
|
||||
<< "failed to parse proto xml");
|
||||
<< "failed to parse proto XML");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -595,13 +575,15 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
|
||||
file_op.xml_to_flatten = DeserializeXmlResourceFromPb(pb_xml_node, &error);
|
||||
if (file_op.xml_to_flatten == nullptr) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(file->GetSource())
|
||||
<< "failed to deserialize proto xml: " << error);
|
||||
<< "failed to deserialize proto XML: " << error);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
file_op.xml_to_flatten = xml::Inflate(data->data(), data->size(),
|
||||
context_->GetDiagnostics(), file->GetSource());
|
||||
std::string error_str;
|
||||
file_op.xml_to_flatten = xml::Inflate(data->data(), data->size(), &error_str);
|
||||
if (file_op.xml_to_flatten == nullptr) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(file->GetSource())
|
||||
<< "failed to parse binary XML: " << error_str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -748,22 +730,29 @@ class LinkCommand {
|
||||
file_collection_(util::make_unique<io::FileCollection>()) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SymbolTable that loads symbols from the various APKs and caches
|
||||
* the results for faster lookup.
|
||||
*/
|
||||
// Creates a SymbolTable that loads symbols from the various APKs.
|
||||
bool LoadSymbolsFromIncludePaths() {
|
||||
std::unique_ptr<AssetManagerSymbolSource> asset_source =
|
||||
util::make_unique<AssetManagerSymbolSource>();
|
||||
auto asset_source = util::make_unique<AssetManagerSymbolSource>();
|
||||
for (const std::string& path : options_.include_paths) {
|
||||
if (context_->IsVerbose()) {
|
||||
context_->GetDiagnostics()->Note(DiagMessage() << "including " << path);
|
||||
}
|
||||
|
||||
// First try to load the file as a static lib.
|
||||
std::string error_str;
|
||||
std::unique_ptr<ResourceTable> include_static = LoadStaticLibrary(path, &error_str);
|
||||
if (include_static) {
|
||||
std::string error;
|
||||
auto zip_collection = io::ZipFileCollection::Create(path, &error);
|
||||
if (zip_collection == nullptr) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage() << "failed to open APK: " << error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (zip_collection->FindFile(kProtoResourceTablePath) != nullptr) {
|
||||
// Load this as a static library include.
|
||||
std::unique_ptr<LoadedApk> static_apk = LoadedApk::LoadProtoApkFromFileCollection(
|
||||
Source(path), std::move(zip_collection), context_->GetDiagnostics());
|
||||
if (static_apk == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context_->GetPackageType() != PackageType::kStaticLib) {
|
||||
// Can't include static libraries when not building a static library (they have no IDs
|
||||
// assigned).
|
||||
@@ -772,13 +761,15 @@ class LinkCommand {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we are using --no-static-lib-packages, we need to rename the
|
||||
// package of this table to our compilation package.
|
||||
ResourceTable* table = static_apk->GetResourceTable();
|
||||
|
||||
// If we are using --no-static-lib-packages, we need to rename the package of this table to
|
||||
// our compilation package.
|
||||
if (options_.no_static_lib_packages) {
|
||||
// Since package names can differ, and multiple packages can exist in a ResourceTable,
|
||||
// we place the requirement that all static libraries are built with the package
|
||||
// ID 0x7f. So if one is not found, this is an error.
|
||||
if (ResourceTablePackage* pkg = include_static->FindPackageById(kAppPackageId)) {
|
||||
if (ResourceTablePackage* pkg = table->FindPackageById(kAppPackageId)) {
|
||||
pkg->name = context_->GetCompilationPackage();
|
||||
} else {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(path)
|
||||
@@ -788,19 +779,14 @@ class LinkCommand {
|
||||
}
|
||||
|
||||
context_->GetExternalSymbols()->AppendSource(
|
||||
util::make_unique<ResourceTableSymbolSource>(include_static.get()));
|
||||
|
||||
static_table_includes_.push_back(std::move(include_static));
|
||||
|
||||
} else if (!error_str.empty()) {
|
||||
// We had an error with reading, so fail.
|
||||
context_->GetDiagnostics()->Error(DiagMessage(path) << error_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!asset_source->AddAssetPath(path)) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(path) << "failed to load include path");
|
||||
return false;
|
||||
util::make_unique<ResourceTableSymbolSource>(table));
|
||||
static_library_includes_.push_back(std::move(static_apk));
|
||||
} else {
|
||||
if (!asset_source->AddAssetPath(path)) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage()
|
||||
<< "failed to load include path " << path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1194,46 +1180,18 @@ class LinkCommand {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<ResourceTable> LoadStaticLibrary(const std::string& input,
|
||||
std::string* out_error) {
|
||||
std::unique_ptr<io::ZipFileCollection> collection =
|
||||
io::ZipFileCollection::Create(input, out_error);
|
||||
if (!collection) {
|
||||
return {};
|
||||
}
|
||||
return LoadTablePbFromCollection(collection.get());
|
||||
}
|
||||
|
||||
std::unique_ptr<ResourceTable> LoadTablePbFromCollection(io::IFileCollection* collection) {
|
||||
io::IFile* file = collection->FindFile(kProtoResourceTablePath);
|
||||
if (!file) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<io::IData> data = file->OpenAsData();
|
||||
return LoadTableFromPb(file->GetSource(), data->data(), data->size(),
|
||||
context_->GetDiagnostics());
|
||||
}
|
||||
|
||||
bool MergeStaticLibrary(const std::string& input, bool override) {
|
||||
if (context_->IsVerbose()) {
|
||||
context_->GetDiagnostics()->Note(DiagMessage() << "merging static library " << input);
|
||||
}
|
||||
|
||||
std::string error_str;
|
||||
std::unique_ptr<io::ZipFileCollection> collection =
|
||||
io::ZipFileCollection::Create(input, &error_str);
|
||||
if (!collection) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(input) << error_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<ResourceTable> table = LoadTablePbFromCollection(collection.get());
|
||||
if (!table) {
|
||||
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(input, context_->GetDiagnostics());
|
||||
if (apk == nullptr) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(input) << "invalid static library");
|
||||
return false;
|
||||
}
|
||||
|
||||
ResourceTable* table = apk->GetResourceTable();
|
||||
ResourceTablePackage* pkg = table->FindPackageById(kAppPackageId);
|
||||
if (!pkg) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(input) << "static library has no package");
|
||||
@@ -1254,13 +1212,12 @@ class LinkCommand {
|
||||
// Clear the package name, so as to make the resources look like they are coming from the
|
||||
// local package.
|
||||
pkg->name = "";
|
||||
result = table_merger_->Merge(Source(input), table.get(), override, collection.get());
|
||||
result = table_merger_->Merge(Source(input), table, override);
|
||||
|
||||
} else {
|
||||
// This is the proper way to merge libraries, where the package name is
|
||||
// preserved and resource names are mangled.
|
||||
result =
|
||||
table_merger_->MergeAndMangle(Source(input), pkg->name, table.get(), collection.get());
|
||||
result = table_merger_->MergeAndMangle(Source(input), pkg->name, table);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
@@ -1268,31 +1225,10 @@ class LinkCommand {
|
||||
}
|
||||
|
||||
// Make sure to move the collection into the set of IFileCollections.
|
||||
collections_.push_back(std::move(collection));
|
||||
merged_apks_.push_back(std::move(apk));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MergeResourceTable(io::IFile* file, bool override) {
|
||||
if (context_->IsVerbose()) {
|
||||
context_->GetDiagnostics()->Note(DiagMessage() << "merging resource table "
|
||||
<< file->GetSource());
|
||||
}
|
||||
|
||||
std::unique_ptr<io::IData> data = file->OpenAsData();
|
||||
if (!data) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << "failed to open file");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<ResourceTable> table =
|
||||
LoadTableFromPb(file->GetSource(), data->data(), data->size(), context_->GetDiagnostics());
|
||||
if (!table) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return table_merger_->Merge(file->GetSource(), table.get(), override);
|
||||
}
|
||||
|
||||
bool MergeCompiledFile(const ResourceFile& compiled_file, io::IFile* file, bool override) {
|
||||
if (context_->IsVerbose()) {
|
||||
context_->GetDiagnostics()->Note(DiagMessage()
|
||||
@@ -1423,7 +1359,7 @@ class LinkCommand {
|
||||
|
||||
ResourceTable table;
|
||||
std::string error;
|
||||
if (!DeserializeTableFromPb(pb_table, &table, &error)) {
|
||||
if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &error)) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(src)
|
||||
<< "failed to deserialize resource table: " << error);
|
||||
return false;
|
||||
@@ -1868,13 +1804,15 @@ class LinkCommand {
|
||||
// A pointer to the FileCollection representing the filesystem (not archives).
|
||||
std::unique_ptr<io::FileCollection> file_collection_;
|
||||
|
||||
// A vector of IFileCollections. This is mainly here to keep ownership of the
|
||||
// A vector of IFileCollections. This is mainly here to retain ownership of the
|
||||
// collections.
|
||||
std::vector<std::unique_ptr<io::IFileCollection>> collections_;
|
||||
|
||||
// A vector of ResourceTables. This is here to retain ownership, so that the
|
||||
// SymbolTable can use these.
|
||||
std::vector<std::unique_ptr<ResourceTable>> static_table_includes_;
|
||||
// The set of merged APKs. This is mainly here to retain ownership of the APKs.
|
||||
std::vector<std::unique_ptr<LoadedApk>> merged_apks_;
|
||||
|
||||
// The set of included APKs (not merged). This is mainly here to retain ownership of the APKs.
|
||||
std::vector<std::unique_ptr<LoadedApk>> static_library_includes_;
|
||||
|
||||
// The set of shared libraries being used, mapping their assigned package ID to package name.
|
||||
std::map<size_t, std::string> shared_libs_;
|
||||
|
||||
@@ -281,15 +281,14 @@ class OptimizeCommand {
|
||||
OptimizeContext* context_;
|
||||
};
|
||||
|
||||
bool ExtractAppDataFromManifest(OptimizeContext* context, LoadedApk* apk,
|
||||
bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk,
|
||||
OptimizeOptions* out_options) {
|
||||
std::unique_ptr<xml::XmlResource> manifest = apk->InflateManifest(context);
|
||||
const xml::XmlResource* manifest = apk->GetManifest();
|
||||
if (manifest == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Maybe<AppInfo> app_info =
|
||||
ExtractAppInfoFromBinaryManifest(manifest.get(), context->GetDiagnostics());
|
||||
Maybe<AppInfo> app_info = ExtractAppInfoFromBinaryManifest(*manifest, context->GetDiagnostics());
|
||||
if (!app_info) {
|
||||
context->GetDiagnostics()->Error(DiagMessage()
|
||||
<< "failed to extract data from AndroidManifest.xml");
|
||||
@@ -355,7 +354,7 @@ int Optimize(const std::vector<StringPiece>& args) {
|
||||
}
|
||||
|
||||
const std::string& apk_path = flags.GetArgs()[0];
|
||||
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(&context, apk_path);
|
||||
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(apk_path, context.GetDiagnostics());
|
||||
if (!apk) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -214,9 +214,10 @@ std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
|
||||
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());
|
||||
static Maybe<std::string> ExtractCompiledString(const xml::Attribute& attr,
|
||||
std::string* out_error) {
|
||||
if (attr.compiled_value != nullptr) {
|
||||
const String* compiled_str = ValueCast<String>(attr.compiled_value.get());
|
||||
if (compiled_str != nullptr) {
|
||||
if (!compiled_str->value->empty()) {
|
||||
return *compiled_str->value;
|
||||
@@ -230,16 +231,16 @@ static Maybe<std::string> ExtractCompiledString(xml::Attribute* attr, std::strin
|
||||
}
|
||||
|
||||
// Fallback to the plain text value if there is one.
|
||||
if (!attr->value.empty()) {
|
||||
return attr->value;
|
||||
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());
|
||||
static Maybe<uint32_t> ExtractCompiledInt(const xml::Attribute& attr, std::string* out_error) {
|
||||
if (attr.compiled_value != nullptr) {
|
||||
const 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) {
|
||||
@@ -251,19 +252,19 @@ static Maybe<uint32_t> ExtractCompiledInt(xml::Attribute* attr, std::string* out
|
||||
}
|
||||
|
||||
// Fallback to the plain text value if there is one.
|
||||
Maybe<uint32_t> integer = ResourceUtils::ParseInt(attr->value);
|
||||
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";
|
||||
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());
|
||||
static Maybe<int> ExtractSdkVersion(const xml::Attribute& attr, std::string* out_error) {
|
||||
if (attr.compiled_value != nullptr) {
|
||||
const 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) {
|
||||
@@ -273,7 +274,7 @@ static Maybe<int> ExtractSdkVersion(xml::Attribute* attr, std::string* out_error
|
||||
return {};
|
||||
}
|
||||
|
||||
String* compiled_str = ValueCast<String>(attr->compiled_value.get());
|
||||
const 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) {
|
||||
@@ -288,19 +289,20 @@ static Maybe<int> ExtractSdkVersion(xml::Attribute* attr, std::string* out_error
|
||||
}
|
||||
|
||||
// Fallback to the plain text value if there is one.
|
||||
Maybe<int> sdk_version = ResourceUtils::ParseSdkVersion(attr->value);
|
||||
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";
|
||||
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) {
|
||||
Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
|
||||
IDiagnostics* diag) {
|
||||
// Make sure the first element is <manifest> with package attribute.
|
||||
xml::Element* manifest_el = xml_res->root.get();
|
||||
const xml::Element* manifest_el = xml_res.root.get();
|
||||
if (manifest_el == nullptr) {
|
||||
return {};
|
||||
}
|
||||
@@ -308,63 +310,63 @@ Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(xml::XmlResource* xml_res, IDiag
|
||||
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>");
|
||||
diag->Error(DiagMessage(xml_res.file.source) << "root tag must be <manifest>");
|
||||
return {};
|
||||
}
|
||||
|
||||
xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package");
|
||||
const xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package");
|
||||
if (!package_attr) {
|
||||
diag->Error(DiagMessage(xml_res->file.source) << "<manifest> must have a 'package' attribute");
|
||||
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);
|
||||
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))
|
||||
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 =
|
||||
if (const xml::Attribute* version_code_attr =
|
||||
manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) {
|
||||
Maybe<uint32_t> maybe_code = ExtractCompiledInt(version_code_attr, &error_msg);
|
||||
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))
|
||||
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 =
|
||||
if (const xml::Attribute* revision_code_attr =
|
||||
manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
|
||||
Maybe<uint32_t> maybe_code = ExtractCompiledInt(revision_code_attr, &error_msg);
|
||||
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))
|
||||
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 (const 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))
|
||||
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 =
|
||||
if (const xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
|
||||
if (const xml::Attribute* min_sdk =
|
||||
uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) {
|
||||
Maybe<int> maybe_sdk = ExtractSdkVersion(min_sdk, &error_msg);
|
||||
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))
|
||||
diag->Error(DiagMessage(xml_res.file.source.WithLine(uses_sdk_el->line_number))
|
||||
<< "invalid android:minSdkVersion: " << error_msg);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -57,7 +57,8 @@ 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);
|
||||
Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
|
||||
IDiagnostics* diag);
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
|
||||
@@ -73,29 +73,22 @@ class ReferenceIdToNameVisitor : public DescendingValueVisitor {
|
||||
|
||||
} // namespace
|
||||
|
||||
BinaryResourceParser::BinaryResourceParser(IAaptContext* context, ResourceTable* table,
|
||||
BinaryResourceParser::BinaryResourceParser(IDiagnostics* diag, 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),
|
||||
files_(files) {
|
||||
: diag_(diag), table_(table), source_(source), data_(data), data_len_(len), files_(files) {
|
||||
}
|
||||
|
||||
bool BinaryResourceParser::Parse() {
|
||||
ResChunkPullParser parser(data_, data_len_);
|
||||
|
||||
if (!ResChunkPullParser::IsGoodEvent(parser.Next())) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_)
|
||||
<< "corrupt resources.arsc: " << parser.error());
|
||||
diag_->Error(DiagMessage(source_) << "corrupt resources.arsc: " << parser.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parser.chunk()->type != android::RES_TABLE_TYPE) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_)
|
||||
<< StringPrintf("unknown chunk of type 0x%02x",
|
||||
diag_->Error(DiagMessage(source_) << StringPrintf("unknown chunk of type 0x%02x",
|
||||
static_cast<int>(parser.chunk()->type)));
|
||||
return false;
|
||||
}
|
||||
@@ -106,13 +99,12 @@ bool BinaryResourceParser::Parse() {
|
||||
|
||||
if (parser.Next() != ResChunkPullParser::Event::kEndDocument) {
|
||||
if (parser.event() == ResChunkPullParser::Event::kBadDocument) {
|
||||
context_->GetDiagnostics()->Warn(
|
||||
DiagMessage(source_) << "invalid chunk trailing RES_TABLE_TYPE: " << parser.error());
|
||||
diag_->Warn(DiagMessage(source_)
|
||||
<< "invalid chunk trailing RES_TABLE_TYPE: " << parser.error());
|
||||
} else {
|
||||
context_->GetDiagnostics()->Warn(
|
||||
DiagMessage(source_) << StringPrintf(
|
||||
"unexpected chunk of type 0x%02x trailing RES_TABLE_TYPE",
|
||||
static_cast<int>(parser.chunk()->type)));
|
||||
diag_->Warn(DiagMessage(source_)
|
||||
<< StringPrintf("unexpected chunk of type 0x%02x trailing RES_TABLE_TYPE",
|
||||
static_cast<int>(parser.chunk()->type)));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -122,7 +114,7 @@ bool BinaryResourceParser::Parse() {
|
||||
bool BinaryResourceParser::ParseTable(const ResChunk_header* chunk) {
|
||||
const ResTable_header* table_header = ConvertTo<ResTable_header>(chunk);
|
||||
if (!table_header) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_header chunk");
|
||||
diag_->Error(DiagMessage(source_) << "corrupt ResTable_header chunk");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -135,17 +127,15 @@ bool BinaryResourceParser::ParseTable(const ResChunk_header* chunk) {
|
||||
status_t err =
|
||||
value_pool_.setTo(parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
|
||||
if (err != NO_ERROR) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_)
|
||||
<< "corrupt string pool in ResTable: "
|
||||
<< value_pool_.getError());
|
||||
diag_->Error(DiagMessage(source_)
|
||||
<< "corrupt string pool in ResTable: " << value_pool_.getError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reserve some space for the strings we are going to add.
|
||||
table_->string_pool.HintWillAdd(value_pool_.size(), value_pool_.styleCount());
|
||||
} else {
|
||||
context_->GetDiagnostics()->Warn(DiagMessage(source_)
|
||||
<< "unexpected string pool in ResTable");
|
||||
diag_->Warn(DiagMessage(source_) << "unexpected string pool in ResTable");
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -156,16 +146,15 @@ bool BinaryResourceParser::ParseTable(const ResChunk_header* chunk) {
|
||||
break;
|
||||
|
||||
default:
|
||||
context_->GetDiagnostics()->Warn(
|
||||
DiagMessage(source_) << "unexpected chunk type "
|
||||
<< static_cast<int>(util::DeviceToHost16(parser.chunk()->type)));
|
||||
diag_->Warn(DiagMessage(source_)
|
||||
<< "unexpected chunk type "
|
||||
<< static_cast<int>(util::DeviceToHost16(parser.chunk()->type)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (parser.event() == ResChunkPullParser::Event::kBadDocument) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_)
|
||||
<< "corrupt resource table: " << parser.error());
|
||||
diag_->Error(DiagMessage(source_) << "corrupt resource table: " << parser.error());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -176,14 +165,13 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) {
|
||||
sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset);
|
||||
const ResTable_package* package_header = ConvertTo<ResTable_package, kMinPackageSize>(chunk);
|
||||
if (!package_header) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_package chunk");
|
||||
diag_->Error(DiagMessage(source_) << "corrupt ResTable_package chunk");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t package_id = util::DeviceToHost32(package_header->id);
|
||||
if (package_id > std::numeric_limits<uint8_t>::max()) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_)
|
||||
<< "package ID is too big (" << package_id << ")");
|
||||
diag_->Error(DiagMessage(source_) << "package ID is too big (" << package_id << ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -198,9 +186,8 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) {
|
||||
ResourceTablePackage* package =
|
||||
table_->CreatePackage(util::Utf16ToUtf8(package_name), static_cast<uint8_t>(package_id));
|
||||
if (!package) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_)
|
||||
<< "incompatible package '" << package_name << "' with ID "
|
||||
<< package_id);
|
||||
diag_->Error(DiagMessage(source_)
|
||||
<< "incompatible package '" << package_name << "' with ID " << package_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -218,8 +205,7 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) {
|
||||
status_t err =
|
||||
type_pool_.setTo(parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
|
||||
if (err != NO_ERROR) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_)
|
||||
<< "corrupt type string pool in "
|
||||
diag_->Error(DiagMessage(source_) << "corrupt type string pool in "
|
||||
<< "ResTable_package: " << type_pool_.getError());
|
||||
return false;
|
||||
}
|
||||
@@ -227,13 +213,12 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) {
|
||||
status_t err =
|
||||
key_pool_.setTo(parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
|
||||
if (err != NO_ERROR) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_)
|
||||
<< "corrupt key string pool in "
|
||||
diag_->Error(DiagMessage(source_) << "corrupt key string pool in "
|
||||
<< "ResTable_package: " << key_pool_.getError());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
context_->GetDiagnostics()->Warn(DiagMessage(source_) << "unexpected string pool");
|
||||
diag_->Warn(DiagMessage(source_) << "unexpected string pool");
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -256,16 +241,15 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) {
|
||||
break;
|
||||
|
||||
default:
|
||||
context_->GetDiagnostics()->Warn(
|
||||
DiagMessage(source_) << "unexpected chunk type "
|
||||
<< static_cast<int>(util::DeviceToHost16(parser.chunk()->type)));
|
||||
diag_->Warn(DiagMessage(source_)
|
||||
<< "unexpected chunk type "
|
||||
<< static_cast<int>(util::DeviceToHost16(parser.chunk()->type)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (parser.event() == ResChunkPullParser::Event::kBadDocument) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_)
|
||||
<< "corrupt ResTable_package: " << parser.error());
|
||||
diag_->Error(DiagMessage(source_) << "corrupt ResTable_package: " << parser.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -278,19 +262,18 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) {
|
||||
|
||||
bool BinaryResourceParser::ParseTypeSpec(const ResChunk_header* chunk) {
|
||||
if (type_pool_.getError() != NO_ERROR) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing type string pool");
|
||||
diag_->Error(DiagMessage(source_) << "missing type string pool");
|
||||
return false;
|
||||
}
|
||||
|
||||
const ResTable_typeSpec* type_spec = ConvertTo<ResTable_typeSpec>(chunk);
|
||||
if (!type_spec) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_typeSpec chunk");
|
||||
diag_->Error(DiagMessage(source_) << "corrupt ResTable_typeSpec chunk");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type_spec->id == 0) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_)
|
||||
<< "ResTable_typeSpec has invalid id: " << type_spec->id);
|
||||
diag_->Error(DiagMessage(source_) << "ResTable_typeSpec has invalid id: " << type_spec->id);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -299,12 +282,12 @@ bool BinaryResourceParser::ParseTypeSpec(const ResChunk_header* chunk) {
|
||||
bool BinaryResourceParser::ParseType(const ResourceTablePackage* package,
|
||||
const ResChunk_header* chunk) {
|
||||
if (type_pool_.getError() != NO_ERROR) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing type string pool");
|
||||
diag_->Error(DiagMessage(source_) << "missing type string pool");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key_pool_.getError() != NO_ERROR) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing key string pool");
|
||||
diag_->Error(DiagMessage(source_) << "missing key string pool");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -312,13 +295,12 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package,
|
||||
// a lot and has its own code to handle variable size.
|
||||
const ResTable_type* type = ConvertTo<ResTable_type, kResTableTypeMinSize>(chunk);
|
||||
if (!type) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_type chunk");
|
||||
diag_->Error(DiagMessage(source_) << "corrupt ResTable_type chunk");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type->id == 0) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_)
|
||||
<< "ResTable_type has invalid id: " << (int)type->id);
|
||||
diag_->Error(DiagMessage(source_) << "ResTable_type has invalid id: " << (int)type->id);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -329,9 +311,8 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package,
|
||||
|
||||
const ResourceType* parsed_type = ParseResourceType(type_str);
|
||||
if (!parsed_type) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_)
|
||||
<< "invalid type name '" << type_str << "' for type with ID "
|
||||
<< (int)type->id);
|
||||
diag_->Error(DiagMessage(source_)
|
||||
<< "invalid type name '" << type_str << "' for type with ID " << (int)type->id);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -360,14 +341,13 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package,
|
||||
}
|
||||
|
||||
if (!resource_value) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source_)
|
||||
<< "failed to parse value for resource " << name << " ("
|
||||
diag_->Error(DiagMessage(source_) << "failed to parse value for resource " << name << " ("
|
||||
<< res_id << ") with configuration '" << config << "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!table_->AddResourceAllowMangled(name, res_id, config, {}, std::move(resource_value),
|
||||
context_->GetDiagnostics())) {
|
||||
diag_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -375,7 +355,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, diag_)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -410,15 +390,14 @@ std::unique_ptr<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& na
|
||||
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) {
|
||||
if (files_ != 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()->Warn(DiagMessage()
|
||||
<< "resource " << name << " for config '" << config
|
||||
<< "' is a file reference to '" << *file_ref->path
|
||||
<< "' but no such path exists");
|
||||
diag_->Warn(DiagMessage() << "resource " << name << " for config '" << config
|
||||
<< "' is a file reference to '" << *file_ref->path
|
||||
<< "' but no such path exists");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -432,7 +411,7 @@ std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(const ResourceNameRef
|
||||
case ResourceType::kStyle:
|
||||
return ParseStyle(name, config, map);
|
||||
case ResourceType::kAttrPrivate:
|
||||
// fallthrough
|
||||
// fallthrough
|
||||
case ResourceType::kAttr:
|
||||
return ParseAttr(name, config, map);
|
||||
case ResourceType::kArray:
|
||||
@@ -445,8 +424,8 @@ std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(const ResourceNameRef
|
||||
// We can ignore the value here.
|
||||
return util::make_unique<Id>();
|
||||
default:
|
||||
context_->GetDiagnostics()->Error(DiagMessage() << "illegal map type '" << ToString(name.type)
|
||||
<< "' (" << (int)name.type << ")");
|
||||
diag_->Error(DiagMessage() << "illegal map type '" << ToString(name.type) << "' ("
|
||||
<< (int)name.type << ")");
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
|
||||
@@ -39,7 +39,7 @@ class BinaryResourceParser {
|
||||
public:
|
||||
// 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,
|
||||
BinaryResourceParser(IDiagnostics* diag, 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.
|
||||
@@ -80,7 +80,7 @@ class BinaryResourceParser {
|
||||
*/
|
||||
bool CollectMetaData(const android::ResTable_map& map_entry, Value* value);
|
||||
|
||||
IAaptContext* context_;
|
||||
IDiagnostics* diag_;
|
||||
ResourceTable* table_;
|
||||
|
||||
const Source source_;
|
||||
|
||||
@@ -568,14 +568,6 @@ bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) {
|
||||
ResTable_header* table_header = table_writer.StartChunk<ResTable_header>(RES_TABLE_TYPE);
|
||||
table_header->packageCount = util::HostToDevice32(table->packages.size());
|
||||
|
||||
// Write a self mapping entry for this package if the ID is non-standard (0x7f).
|
||||
if (context->GetPackageType() == PackageType::kApp) {
|
||||
const uint8_t package_id = context->GetPackageId();
|
||||
if (package_id != kFrameworkPackageId && package_id != kAppPackageId) {
|
||||
table->included_packages_[package_id] = context->GetCompilationPackage();
|
||||
}
|
||||
}
|
||||
|
||||
// Flatten the values string pool.
|
||||
StringPool::FlattenUtf8(table_writer.buffer(), table->string_pool);
|
||||
|
||||
@@ -583,6 +575,14 @@ bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) {
|
||||
|
||||
// Flatten each package.
|
||||
for (auto& package : table->packages) {
|
||||
if (context->GetPackageType() == PackageType::kApp) {
|
||||
// Write a self mapping entry for this package if the ID is non-standard (0x7f).
|
||||
const uint8_t package_id = package->id.value();
|
||||
if (package_id != kFrameworkPackageId && package_id != kAppPackageId) {
|
||||
table->included_packages_[package_id] = package->name;
|
||||
}
|
||||
}
|
||||
|
||||
PackageFlattener flattener(context, package.get(), &table->included_packages_,
|
||||
options_.use_sparse_entries);
|
||||
if (!flattener.FlattenPackage(&package_buffer)) {
|
||||
|
||||
@@ -71,7 +71,8 @@ class TableFlattenerTest : public ::testing::Test {
|
||||
return result;
|
||||
}
|
||||
|
||||
BinaryResourceParser parser(context, out_table, {}, content.data(), content.size());
|
||||
BinaryResourceParser parser(context->GetDiagnostics(), out_table, {}, content.data(),
|
||||
content.size());
|
||||
if (!parser.Parse()) {
|
||||
return ::testing::AssertionFailure() << "flattened ResTable is corrupt";
|
||||
}
|
||||
@@ -278,7 +279,7 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkO) {
|
||||
// Attempt to parse the sparse contents.
|
||||
|
||||
ResourceTable sparse_table;
|
||||
BinaryResourceParser parser(context.get(), &sparse_table, Source("test.arsc"),
|
||||
BinaryResourceParser parser(context->GetDiagnostics(), &sparse_table, Source("test.arsc"),
|
||||
sparse_contents.data(), sparse_contents.size());
|
||||
ASSERT_TRUE(parser.Parse());
|
||||
|
||||
|
||||
@@ -371,7 +371,8 @@ static SymbolState DeserializeVisibilityFromPb(const pb::SymbolStatus_Visibility
|
||||
}
|
||||
|
||||
static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStringPool& src_pool,
|
||||
ResourceTable* out_table, std::string* out_error) {
|
||||
io::IFileCollection* files, ResourceTable* out_table,
|
||||
std::string* out_error) {
|
||||
Maybe<uint8_t> id;
|
||||
if (pb_package.has_package_id()) {
|
||||
id = static_cast<uint8_t>(pb_package.package_id().id());
|
||||
@@ -444,7 +445,7 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr
|
||||
}
|
||||
|
||||
config_value->value = DeserializeValueFromPb(pb_config_value.value(), src_pool, config,
|
||||
&out_table->string_pool, out_error);
|
||||
&out_table->string_pool, files, out_error);
|
||||
if (config_value->value == nullptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -457,8 +458,8 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, ResourceTable* out_table,
|
||||
std::string* out_error) {
|
||||
bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, io::IFileCollection* files,
|
||||
ResourceTable* out_table, std::string* out_error) {
|
||||
// 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;
|
||||
@@ -474,7 +475,7 @@ bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, ResourceTable* ou
|
||||
}
|
||||
|
||||
for (const pb::Package& pb_package : pb_table.package()) {
|
||||
if (!DeserializePackageFromPb(pb_package, source_pool, out_table, out_error)) {
|
||||
if (!DeserializePackageFromPb(pb_package, source_pool, files, out_table, out_error)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -600,10 +601,11 @@ static size_t DeserializePluralEnumFromPb(const pb::Plural_Arity& arity) {
|
||||
std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
|
||||
const android::ResStringPool& src_pool,
|
||||
const ConfigDescription& config,
|
||||
StringPool* value_pool, std::string* out_error) {
|
||||
StringPool* value_pool, io::IFileCollection* files,
|
||||
std::string* out_error) {
|
||||
std::unique_ptr<Value> value;
|
||||
if (pb_value.has_item()) {
|
||||
value = DeserializeItemFromPb(pb_value.item(), src_pool, config, value_pool, out_error);
|
||||
value = DeserializeItemFromPb(pb_value.item(), src_pool, config, value_pool, files, out_error);
|
||||
if (value == nullptr) {
|
||||
return {};
|
||||
}
|
||||
@@ -651,8 +653,8 @@ std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
|
||||
return {};
|
||||
}
|
||||
DeserializeItemMetaDataFromPb(pb_entry, src_pool, &entry.key);
|
||||
entry.value =
|
||||
DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, out_error);
|
||||
entry.value = DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, files,
|
||||
out_error);
|
||||
if (entry.value == nullptr) {
|
||||
return {};
|
||||
}
|
||||
@@ -680,8 +682,8 @@ std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
|
||||
const pb::Array& pb_array = pb_compound_value.array();
|
||||
std::unique_ptr<Array> array = util::make_unique<Array>();
|
||||
for (const pb::Array_Element& pb_entry : pb_array.element()) {
|
||||
std::unique_ptr<Item> item =
|
||||
DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, out_error);
|
||||
std::unique_ptr<Item> item = DeserializeItemFromPb(pb_entry.item(), src_pool, config,
|
||||
value_pool, files, out_error);
|
||||
if (item == nullptr) {
|
||||
return {};
|
||||
}
|
||||
@@ -697,8 +699,8 @@ std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
|
||||
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
|
||||
for (const pb::Plural_Entry& pb_entry : pb_plural.entry()) {
|
||||
size_t plural_idx = DeserializePluralEnumFromPb(pb_entry.arity());
|
||||
plural->values[plural_idx] =
|
||||
DeserializeItemFromPb(pb_entry.item(), src_pool, config, value_pool, out_error);
|
||||
plural->values[plural_idx] = DeserializeItemFromPb(pb_entry.item(), src_pool, config,
|
||||
value_pool, files, out_error);
|
||||
if (!plural->values[plural_idx]) {
|
||||
return {};
|
||||
}
|
||||
@@ -727,7 +729,7 @@ std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
|
||||
std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
|
||||
const android::ResStringPool& src_pool,
|
||||
const ConfigDescription& config, StringPool* value_pool,
|
||||
std::string* out_error) {
|
||||
io::IFileCollection* files, std::string* out_error) {
|
||||
switch (pb_item.value_case()) {
|
||||
case pb::Item::kRef: {
|
||||
const pb::Reference& pb_ref = pb_item.ref();
|
||||
@@ -774,6 +776,9 @@ std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
|
||||
util::make_unique<FileReference>(value_pool->MakeRef(
|
||||
pb_file.path(), StringPool::Context(StringPool::Context::kHighPriority, config)));
|
||||
file_ref->type = DeserializeFileReferenceTypeFromPb(pb_file.type());
|
||||
if (files != nullptr) {
|
||||
file_ref->file = files->FindFile(*file_ref->path);
|
||||
}
|
||||
return std::move(file_ref);
|
||||
} break;
|
||||
|
||||
@@ -825,7 +830,7 @@ bool DeserializeXmlFromPb(const pb::XmlNode& pb_node, xml::Element* out_el, Stri
|
||||
}
|
||||
if (pb_attr.has_compiled_item()) {
|
||||
attr.compiled_value =
|
||||
DeserializeItemFromPb(pb_attr.compiled_item(), {}, {}, value_pool, out_error);
|
||||
DeserializeItemFromPb(pb_attr.compiled_item(), {}, {}, value_pool, nullptr, out_error);
|
||||
if (attr.compiled_value == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "Resources.pb.h"
|
||||
#include "ResourcesInternal.pb.h"
|
||||
#include "StringPool.h"
|
||||
#include "io/File.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
namespace aapt {
|
||||
@@ -34,12 +35,13 @@ namespace aapt {
|
||||
std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
|
||||
const android::ResStringPool& src_pool,
|
||||
const ConfigDescription& config,
|
||||
StringPool* value_pool, std::string* out_error);
|
||||
StringPool* value_pool, io::IFileCollection* files,
|
||||
std::string* out_error);
|
||||
|
||||
std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
|
||||
const android::ResStringPool& src_pool,
|
||||
const ConfigDescription& config, StringPool* value_pool,
|
||||
std::string* out_error);
|
||||
io::IFileCollection* files, std::string* out_error);
|
||||
|
||||
std::unique_ptr<xml::XmlResource> DeserializeXmlResourceFromPb(const pb::XmlNode& pb_node,
|
||||
std::string* out_error);
|
||||
@@ -50,8 +52,9 @@ bool DeserializeXmlFromPb(const pb::XmlNode& pb_node, xml::Element* out_el, Stri
|
||||
bool DeserializeConfigFromPb(const pb::Configuration& pb_config, ConfigDescription* out_config,
|
||||
std::string* out_error);
|
||||
|
||||
bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, ResourceTable* out_table,
|
||||
std::string* out_error);
|
||||
// Optional io::IFileCollection used to lookup references to files in the ResourceTable.
|
||||
bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, io::IFileCollection* files,
|
||||
ResourceTable* out_table, std::string* out_error);
|
||||
|
||||
bool DeserializeCompiledFileFromPb(const pb::internal::CompiledFile& pb_file,
|
||||
ResourceFile* out_file, std::string* out_error);
|
||||
|
||||
@@ -29,6 +29,12 @@ using ::testing::StrEq;
|
||||
|
||||
namespace aapt {
|
||||
|
||||
class MockFileCollection : public io::IFileCollection {
|
||||
public:
|
||||
MOCK_METHOD1(FindFile, io::IFile*(const StringPiece& path));
|
||||
MOCK_METHOD0(Iterator, std::unique_ptr<io::IFileCollectionIterator>());
|
||||
};
|
||||
|
||||
TEST(ProtoSerializeTest, SerializeSinglePackage) {
|
||||
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
|
||||
std::unique_ptr<ResourceTable> table =
|
||||
@@ -86,9 +92,14 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) {
|
||||
pb::ResourceTable pb_table;
|
||||
SerializeTableToPb(*table, &pb_table);
|
||||
|
||||
test::TestFile file_a("res/layout/main.xml");
|
||||
MockFileCollection files;
|
||||
EXPECT_CALL(files, FindFile(Eq("res/layout/main.xml")))
|
||||
.WillRepeatedly(::testing::Return(&file_a));
|
||||
|
||||
ResourceTable new_table;
|
||||
std::string error;
|
||||
ASSERT_TRUE(DeserializeTableFromPb(pb_table, &new_table, &error));
|
||||
ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
|
||||
EXPECT_THAT(error, IsEmpty());
|
||||
|
||||
Id* new_id = test::GetValue<Id>(&new_table, "com.app.a:id/foo");
|
||||
@@ -124,6 +135,11 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) {
|
||||
ASSERT_TRUE(actual_ref->id);
|
||||
EXPECT_THAT(*actual_ref, Eq(expected_ref));
|
||||
|
||||
FileReference* actual_file_ref =
|
||||
test::GetValue<FileReference>(&new_table, "com.app.a:layout/main");
|
||||
ASSERT_THAT(actual_file_ref, NotNull());
|
||||
EXPECT_THAT(actual_file_ref->file, Eq(&file_a));
|
||||
|
||||
StyledString* actual_styled_str =
|
||||
test::GetValue<StyledString>(&new_table, "com.app.a:string/styled");
|
||||
ASSERT_THAT(actual_styled_str, NotNull());
|
||||
|
||||
@@ -25,5 +25,4 @@ LOCAL_SRC_FILES := $(call all-java-files-under,src)
|
||||
LOCAL_STATIC_ANDROID_LIBRARIES := \
|
||||
AaptTestNamespace_LibOne \
|
||||
AaptTestNamespace_LibTwo
|
||||
LOCAL_AAPT_FLAGS := -v
|
||||
include $(BUILD_PACKAGE)
|
||||
|
||||
@@ -37,17 +37,14 @@ TableMerger::TableMerger(IAaptContext* context, ResourceTable* out_table,
|
||||
CHECK(master_package_ != nullptr) << "package name or ID already taken";
|
||||
}
|
||||
|
||||
bool TableMerger::Merge(const Source& src, ResourceTable* table, bool overlay,
|
||||
io::IFileCollection* collection) {
|
||||
bool TableMerger::Merge(const Source& src, ResourceTable* table, bool overlay) {
|
||||
// We allow adding new resources if this is not an overlay, or if the options allow overlays
|
||||
// to add new resources.
|
||||
return MergeImpl(src, table, collection, overlay,
|
||||
options_.auto_add_overlay || !overlay /*allow_new*/);
|
||||
return MergeImpl(src, table, overlay, options_.auto_add_overlay || !overlay /*allow_new*/);
|
||||
}
|
||||
|
||||
// This will merge packages with the same package name (or no package name).
|
||||
bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
|
||||
io::IFileCollection* collection, bool overlay, bool allow_new) {
|
||||
bool TableMerger::MergeImpl(const Source& src, ResourceTable* table, bool overlay, bool allow_new) {
|
||||
bool error = false;
|
||||
for (auto& package : table->packages) {
|
||||
// Only merge an empty package or the package we're building.
|
||||
@@ -55,37 +52,20 @@ bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
|
||||
// This is because at compile time it is unknown if the attributes are
|
||||
// simply uses of the attribute or definitions.
|
||||
if (package->name.empty() || context_->GetCompilationPackage() == package->name) {
|
||||
FileMergeCallback callback;
|
||||
if (collection) {
|
||||
callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
|
||||
FileReference* new_file, FileReference* old_file) -> bool {
|
||||
// The old file's path points inside the APK, so we can use it as is.
|
||||
io::IFile* f = collection->FindFile(*old_file->path);
|
||||
if (!f) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(src)
|
||||
<< "file '" << *old_file->path << "' not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
new_file->file = f;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
// Merge here. Once the entries are merged and mangled, any references to them are still
|
||||
// valid. This is because un-mangled references are mangled, then looked up at resolution
|
||||
// time. Also, when linking, we convert references with no package name to use the compilation
|
||||
// package name.
|
||||
error |=
|
||||
!DoMerge(src, table, package.get(), false /* mangle */, overlay, allow_new, callback);
|
||||
error |= !DoMerge(src, table, package.get(), false /*mangle*/, overlay, allow_new);
|
||||
}
|
||||
}
|
||||
return !error;
|
||||
}
|
||||
|
||||
// This will merge and mangle resources from a static library.
|
||||
// This will merge and mangle resources from a static library. It is assumed that all FileReferences
|
||||
// have correctly set their io::IFile*.
|
||||
bool TableMerger::MergeAndMangle(const Source& src, const StringPiece& package_name,
|
||||
ResourceTable* table, io::IFileCollection* collection) {
|
||||
ResourceTable* table) {
|
||||
bool error = false;
|
||||
for (auto& package : table->packages) {
|
||||
// Warn of packages with an unrelated ID.
|
||||
@@ -96,23 +76,7 @@ bool TableMerger::MergeAndMangle(const Source& src, const StringPiece& package_n
|
||||
|
||||
bool mangle = package_name != context_->GetCompilationPackage();
|
||||
merged_packages_.insert(package->name);
|
||||
|
||||
auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
|
||||
FileReference* new_file, FileReference* old_file) -> bool {
|
||||
// The old file's path points inside the APK, so we can use it as is.
|
||||
io::IFile* f = collection->FindFile(*old_file->path);
|
||||
if (!f) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(src)
|
||||
<< "file '" << *old_file->path << "' not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
new_file->file = f;
|
||||
return true;
|
||||
};
|
||||
|
||||
error |= !DoMerge(src, table, package.get(), mangle, false /*overlay*/, true /*allow_new*/,
|
||||
callback);
|
||||
error |= !DoMerge(src, table, package.get(), mangle, false /*overlay*/, true /*allow_new*/);
|
||||
}
|
||||
return !error;
|
||||
}
|
||||
@@ -187,7 +151,7 @@ static ResourceTable::CollisionResult ResolveMergeCollision(Value* existing, Val
|
||||
|
||||
static ResourceTable::CollisionResult MergeConfigValue(IAaptContext* context,
|
||||
const ResourceNameRef& res_name,
|
||||
const bool overlay,
|
||||
bool overlay,
|
||||
ResourceConfigValue* dst_config_value,
|
||||
ResourceConfigValue* src_config_value,
|
||||
StringPool* pool) {
|
||||
@@ -220,10 +184,8 @@ static ResourceTable::CollisionResult MergeConfigValue(IAaptContext* context,
|
||||
}
|
||||
|
||||
bool TableMerger::DoMerge(const Source& src, ResourceTable* src_table,
|
||||
ResourceTablePackage* src_package,
|
||||
const bool mangle_package, const bool overlay,
|
||||
const bool allow_new_resources,
|
||||
const FileMergeCallback& callback) {
|
||||
ResourceTablePackage* src_package, bool mangle_package, bool overlay,
|
||||
bool allow_new_resources) {
|
||||
bool error = false;
|
||||
|
||||
for (auto& src_type : src_package->types) {
|
||||
@@ -292,13 +254,6 @@ bool TableMerger::DoMerge(const Source& src, ResourceTable* src_table,
|
||||
} else {
|
||||
new_file_ref = std::unique_ptr<FileReference>(f->Clone(&master_table_->string_pool));
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
if (!callback(res_name, src_config_value->config, new_file_ref.get(), f)) {
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
dst_config_value->value = std::move(new_file_ref);
|
||||
|
||||
} else {
|
||||
@@ -343,8 +298,8 @@ bool TableMerger::MergeFile(const ResourceFile& file_desc, bool overlay, io::IFi
|
||||
->FindOrCreateValue(file_desc.config, {})
|
||||
->value = std::move(file_ref);
|
||||
|
||||
return DoMerge(file->GetSource(), &table, pkg, false /* mangle */, overlay /* overlay */,
|
||||
true /* allow_new */, {});
|
||||
return DoMerge(file->GetSource(), &table, pkg, false /*mangle*/, overlay /*overlay*/,
|
||||
true /*allow_new*/);
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -40,6 +40,9 @@ struct TableMergerOptions {
|
||||
// TableMerger takes resource tables and merges all packages within the tables that have the same
|
||||
// package ID.
|
||||
//
|
||||
// It is assumed that any FileReference values have their io::IFile pointer set to point to the
|
||||
// file they represent.
|
||||
//
|
||||
// If a package has a different name, all the entries in that table have their names mangled
|
||||
// to include the package name. This way there are no collisions. In order to do this correctly,
|
||||
// the TableMerger needs to also mangle any FileReference paths. Once these are mangled, the
|
||||
@@ -60,14 +63,11 @@ class TableMerger {
|
||||
|
||||
// Merges resources from the same or empty package. This is for local sources.
|
||||
// If overlay is true, the resources are treated as overlays.
|
||||
// An io::IFileCollection is optional and used to find the referenced Files and process them.
|
||||
bool Merge(const Source& src, ResourceTable* table, bool overlay,
|
||||
io::IFileCollection* collection = nullptr);
|
||||
bool Merge(const Source& src, ResourceTable* table, bool overlay);
|
||||
|
||||
// Merges resources from the given package, mangling the name. This is for static libraries.
|
||||
// An io::IFileCollection is needed in order to find the referenced Files and process them.
|
||||
bool MergeAndMangle(const Source& src, const android::StringPiece& package, ResourceTable* table,
|
||||
io::IFileCollection* collection);
|
||||
// All FileReference values must have their io::IFile set.
|
||||
bool MergeAndMangle(const Source& src, const android::StringPiece& package, ResourceTable* table);
|
||||
|
||||
// Merges a compiled file that belongs to this same or empty package.
|
||||
bool MergeFile(const ResourceFile& fileDesc, bool overlay, io::IFile* file);
|
||||
@@ -75,23 +75,16 @@ class TableMerger {
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(TableMerger);
|
||||
|
||||
using FileMergeCallback = std::function<bool(const ResourceNameRef&,
|
||||
const ConfigDescription& config,
|
||||
FileReference*, FileReference*)>;
|
||||
|
||||
IAaptContext* context_;
|
||||
ResourceTable* master_table_;
|
||||
TableMergerOptions options_;
|
||||
ResourceTablePackage* master_package_;
|
||||
std::set<std::string> merged_packages_;
|
||||
|
||||
bool MergeImpl(const Source& src, ResourceTable* src_table,
|
||||
io::IFileCollection* collection, bool overlay, bool allow_new);
|
||||
bool MergeImpl(const Source& src, ResourceTable* src_table, bool overlay, bool allow_new);
|
||||
|
||||
bool DoMerge(const Source& src, ResourceTable* src_table,
|
||||
ResourceTablePackage* src_package, const bool mangle_package,
|
||||
const bool overlay, const bool allow_new_resources,
|
||||
const FileMergeCallback& callback);
|
||||
bool DoMerge(const Source& src, ResourceTable* src_table, ResourceTablePackage* src_package,
|
||||
bool mangle_package, bool overlay, bool allow_new_resources);
|
||||
|
||||
std::unique_ptr<FileReference> CloneAndMangleFile(const std::string& package,
|
||||
const FileReference& value);
|
||||
|
||||
@@ -22,11 +22,12 @@
|
||||
|
||||
using ::aapt::test::ValueEq;
|
||||
using ::testing::Contains;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::UnorderedElementsAreArray;
|
||||
using ::testing::Pointee;
|
||||
using ::testing::Field;
|
||||
using ::testing::Eq;
|
||||
using ::testing::Field;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::Pointee;
|
||||
using ::testing::StrEq;
|
||||
using ::testing::UnorderedElementsAreArray;
|
||||
|
||||
namespace aapt {
|
||||
|
||||
@@ -67,10 +68,9 @@ TEST_F(TableMergerTest, SimpleMerge) {
|
||||
|
||||
ResourceTable final_table;
|
||||
TableMerger merger(context_.get(), &final_table, TableMergerOptions{});
|
||||
io::FileCollection collection;
|
||||
|
||||
ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
|
||||
ASSERT_TRUE(merger.MergeAndMangle({}, "com.app.b", table_b.get(), &collection));
|
||||
ASSERT_TRUE(merger.MergeAndMangle({}, "com.app.b", table_b.get()));
|
||||
|
||||
EXPECT_TRUE(merger.merged_packages().count("com.app.b") != 0);
|
||||
|
||||
@@ -122,32 +122,35 @@ TEST_F(TableMergerTest, MergeFileOverlay) {
|
||||
}
|
||||
|
||||
TEST_F(TableMergerTest, MergeFileReferences) {
|
||||
test::TestFile file_a("res/xml/file.xml");
|
||||
test::TestFile file_b("res/xml/file.xml");
|
||||
|
||||
std::unique_ptr<ResourceTable> table_a =
|
||||
test::ResourceTableBuilder()
|
||||
.SetPackageId("com.app.a", 0x7f)
|
||||
.AddFileReference("com.app.a:xml/file", "res/xml/file.xml")
|
||||
.AddFileReference("com.app.a:xml/file", "res/xml/file.xml", &file_a)
|
||||
.Build();
|
||||
std::unique_ptr<ResourceTable> table_b =
|
||||
test::ResourceTableBuilder()
|
||||
.SetPackageId("com.app.b", 0x7f)
|
||||
.AddFileReference("com.app.b:xml/file", "res/xml/file.xml")
|
||||
.AddFileReference("com.app.b:xml/file", "res/xml/file.xml", &file_b)
|
||||
.Build();
|
||||
|
||||
ResourceTable final_table;
|
||||
TableMerger merger(context_.get(), &final_table, TableMergerOptions{});
|
||||
io::FileCollection collection;
|
||||
collection.InsertFile("res/xml/file.xml");
|
||||
|
||||
ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
|
||||
ASSERT_TRUE(merger.MergeAndMangle({}, "com.app.b", table_b.get(), &collection));
|
||||
ASSERT_TRUE(merger.MergeAndMangle({}, "com.app.b", table_b.get()));
|
||||
|
||||
FileReference* f = test::GetValue<FileReference>(&final_table, "com.app.a:xml/file");
|
||||
ASSERT_THAT(f, NotNull());
|
||||
EXPECT_EQ(std::string("res/xml/file.xml"), *f->path);
|
||||
EXPECT_THAT(*f->path, StrEq("res/xml/file.xml"));
|
||||
EXPECT_THAT(f->file, Eq(&file_a));
|
||||
|
||||
f = test::GetValue<FileReference>(&final_table, "com.app.a:xml/com.app.b$file");
|
||||
ASSERT_THAT(f, NotNull());
|
||||
EXPECT_EQ(std::string("res/xml/com.app.b$file.xml"), *f->path);
|
||||
EXPECT_THAT(*f->path, StrEq("res/xml/com.app.b$file.xml"));
|
||||
EXPECT_THAT(f->file, Eq(&file_b));
|
||||
}
|
||||
|
||||
TEST_F(TableMergerTest, OverrideResourceWithOverlay) {
|
||||
|
||||
@@ -265,12 +265,14 @@ bool MultiApkGenerator::UpdateManifest(const Artifact& artifact,
|
||||
const PostProcessingConfiguration& config,
|
||||
std::unique_ptr<XmlResource>* updated_manifest,
|
||||
IDiagnostics* diag) {
|
||||
*updated_manifest = apk_->InflateManifest(context_);
|
||||
XmlResource* manifest = updated_manifest->get();
|
||||
if (manifest == nullptr) {
|
||||
const xml::XmlResource* apk_manifest = apk_->GetManifest();
|
||||
if (apk_manifest == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*updated_manifest = apk_manifest->Clone();
|
||||
XmlResource* manifest = updated_manifest->get();
|
||||
|
||||
// Make sure the first element is <manifest> with package attribute.
|
||||
xml::Element* manifest_el = manifest->root.get();
|
||||
if (manifest_el == nullptr) {
|
||||
|
||||
@@ -106,7 +106,7 @@ class MultiApkGeneratorTest : public ::testing::Test {
|
||||
TEST_F(MultiApkGeneratorTest, VersionFilterNewerVersion) {
|
||||
std::unique_ptr<ResourceTable> table = BuildTable();
|
||||
|
||||
LoadedApk apk = {{"test.apk"}, {}, std::move(table)};
|
||||
LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}};
|
||||
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(19).Build();
|
||||
PostProcessingConfiguration empty_config;
|
||||
TableFlattenerOptions table_flattener_options;
|
||||
@@ -147,7 +147,7 @@ TEST_F(MultiApkGeneratorTest, VersionFilterNewerVersion) {
|
||||
TEST_F(MultiApkGeneratorTest, VersionFilterOlderVersion) {
|
||||
std::unique_ptr<ResourceTable> table = BuildTable();
|
||||
|
||||
LoadedApk apk = {{"test.apk"}, {}, std::move(table)};
|
||||
LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}};
|
||||
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build();
|
||||
PostProcessingConfiguration empty_config;
|
||||
TableFlattenerOptions table_flattener_options;
|
||||
@@ -186,7 +186,7 @@ TEST_F(MultiApkGeneratorTest, VersionFilterOlderVersion) {
|
||||
TEST_F(MultiApkGeneratorTest, VersionFilterNoVersion) {
|
||||
std::unique_ptr<ResourceTable> table = BuildTable();
|
||||
|
||||
LoadedApk apk = {{"test.apk"}, {}, std::move(table)};
|
||||
LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}};
|
||||
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build();
|
||||
PostProcessingConfiguration empty_config;
|
||||
TableFlattenerOptions table_flattener_options;
|
||||
|
||||
@@ -78,21 +78,27 @@ ResourceTableBuilder& ResourceTableBuilder::AddString(const StringPiece& name, c
|
||||
}
|
||||
|
||||
ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name,
|
||||
const StringPiece& path) {
|
||||
return AddFileReference(name, {}, path);
|
||||
const StringPiece& path,
|
||||
io::IFile* file) {
|
||||
return AddFileReference(name, {}, path, file);
|
||||
}
|
||||
|
||||
ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name,
|
||||
const ResourceId& id,
|
||||
const StringPiece& path) {
|
||||
return AddValue(name, id, util::make_unique<FileReference>(table_->string_pool.MakeRef(path)));
|
||||
const StringPiece& path,
|
||||
io::IFile* file) {
|
||||
auto file_ref = util::make_unique<FileReference>(table_->string_pool.MakeRef(path));
|
||||
file_ref->file = file;
|
||||
return AddValue(name, id, std::move(file_ref));
|
||||
}
|
||||
|
||||
ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name,
|
||||
const StringPiece& path,
|
||||
const ConfigDescription& config) {
|
||||
return AddValue(name, config, {},
|
||||
util::make_unique<FileReference>(table_->string_pool.MakeRef(path)));
|
||||
const ConfigDescription& config,
|
||||
io::IFile* file) {
|
||||
auto file_ref = util::make_unique<FileReference>(table_->string_pool.MakeRef(path));
|
||||
file_ref->file = file;
|
||||
return AddValue(name, config, {}, std::move(file_ref));
|
||||
}
|
||||
|
||||
ResourceTableBuilder& ResourceTableBuilder::AddValue(const StringPiece& name,
|
||||
|
||||
@@ -52,12 +52,15 @@ class ResourceTableBuilder {
|
||||
ResourceTableBuilder& AddString(const android::StringPiece& name, const ResourceId& id,
|
||||
const ConfigDescription& config, const android::StringPiece& str);
|
||||
ResourceTableBuilder& AddFileReference(const android::StringPiece& name,
|
||||
const android::StringPiece& path);
|
||||
const android::StringPiece& path,
|
||||
io::IFile* file = nullptr);
|
||||
ResourceTableBuilder& AddFileReference(const android::StringPiece& name, const ResourceId& id,
|
||||
const android::StringPiece& path);
|
||||
const android::StringPiece& path,
|
||||
io::IFile* file = nullptr);
|
||||
ResourceTableBuilder& AddFileReference(const android::StringPiece& name,
|
||||
const android::StringPiece& path,
|
||||
const ConfigDescription& config);
|
||||
const ConfigDescription& config,
|
||||
io::IFile* file = nullptr);
|
||||
ResourceTableBuilder& AddValue(const android::StringPiece& name, std::unique_ptr<Value> value);
|
||||
ResourceTableBuilder& AddValue(const android::StringPiece& name, const ResourceId& id,
|
||||
std::unique_ptr<Value> value);
|
||||
|
||||
@@ -258,8 +258,7 @@ static void CopyAttributes(Element* el, android::ResXMLParser* parser, StringPoo
|
||||
}
|
||||
}
|
||||
|
||||
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 len, std::string* out_error) {
|
||||
// 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;
|
||||
@@ -270,7 +269,10 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos
|
||||
std::unique_ptr<Element> pending_element;
|
||||
|
||||
ResXMLTree tree;
|
||||
if (tree.setTo(data, data_len) != NO_ERROR) {
|
||||
if (tree.setTo(data, len) != NO_ERROR) {
|
||||
if (out_error != nullptr) {
|
||||
*out_error = "failed to initialize ResXMLTree";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -361,6 +363,27 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos
|
||||
return util::make_unique<XmlResource>(ResourceFile{}, std::move(string_pool), std::move(root));
|
||||
}
|
||||
|
||||
std::unique_ptr<XmlResource> XmlResource::Clone() const {
|
||||
std::unique_ptr<XmlResource> cloned = util::make_unique<XmlResource>(file);
|
||||
if (root != nullptr) {
|
||||
cloned->root = root->CloneElement([&](const xml::Element& src, xml::Element* dst) {
|
||||
dst->attributes.reserve(src.attributes.size());
|
||||
for (const xml::Attribute& attr : src.attributes) {
|
||||
xml::Attribute cloned_attr;
|
||||
cloned_attr.name = attr.name;
|
||||
cloned_attr.namespace_uri = attr.namespace_uri;
|
||||
cloned_attr.value = attr.value;
|
||||
cloned_attr.compiled_attribute = attr.compiled_attribute;
|
||||
if (attr.compiled_value != nullptr) {
|
||||
cloned_attr.compiled_value.reset(attr.compiled_value->Clone(&cloned->string_pool));
|
||||
}
|
||||
dst->attributes.push_back(std::move(cloned_attr));
|
||||
}
|
||||
});
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
Element* FindRootElement(Node* node) {
|
||||
if (node == nullptr) {
|
||||
return nullptr;
|
||||
@@ -383,12 +406,7 @@ void Element::InsertChild(size_t index, std::unique_ptr<Node> child) {
|
||||
}
|
||||
|
||||
Attribute* Element::FindAttribute(const StringPiece& ns, const StringPiece& name) {
|
||||
for (auto& attr : attributes) {
|
||||
if (ns == attr.namespace_uri && name == attr.name) {
|
||||
return &attr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
return const_cast<Attribute*>(static_cast<const Element*>(this)->FindAttribute(ns, name));
|
||||
}
|
||||
|
||||
const Attribute* Element::FindAttribute(const StringPiece& ns, const StringPiece& name) const {
|
||||
@@ -404,17 +422,29 @@ Element* Element::FindChild(const StringPiece& ns, const StringPiece& name) {
|
||||
return FindChildWithAttribute(ns, name, {}, {}, {});
|
||||
}
|
||||
|
||||
const Element* Element::FindChild(const StringPiece& ns, const StringPiece& name) const {
|
||||
return FindChildWithAttribute(ns, name, {}, {}, {});
|
||||
}
|
||||
|
||||
Element* Element::FindChildWithAttribute(const StringPiece& ns, const StringPiece& name,
|
||||
const StringPiece& attr_ns, const StringPiece& attr_name,
|
||||
const StringPiece& attr_value) {
|
||||
for (auto& child : children) {
|
||||
if (Element* el = NodeCast<Element>(child.get())) {
|
||||
return const_cast<Element*>(static_cast<const Element*>(this)->FindChildWithAttribute(
|
||||
ns, name, attr_ns, attr_name, attr_value));
|
||||
}
|
||||
|
||||
const Element* Element::FindChildWithAttribute(const StringPiece& ns, const StringPiece& name,
|
||||
const StringPiece& attr_ns,
|
||||
const StringPiece& attr_name,
|
||||
const StringPiece& attr_value) const {
|
||||
for (const auto& child : children) {
|
||||
if (const Element* el = NodeCast<Element>(child.get())) {
|
||||
if (ns == el->namespace_uri && name == el->name) {
|
||||
if (attr_ns.empty() && attr_name.empty()) {
|
||||
return el;
|
||||
}
|
||||
|
||||
Attribute* attr = el->FindAttribute(attr_ns, attr_name);
|
||||
const Attribute* attr = el->FindAttribute(attr_ns, attr_name);
|
||||
if (attr && attr_value == attr->value) {
|
||||
return el;
|
||||
}
|
||||
|
||||
@@ -100,11 +100,21 @@ class Element : public Node {
|
||||
Attribute* FindAttribute(const android::StringPiece& ns, const android::StringPiece& name);
|
||||
const Attribute* FindAttribute(const android::StringPiece& ns,
|
||||
const android::StringPiece& name) const;
|
||||
|
||||
Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name);
|
||||
const Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name) const;
|
||||
|
||||
Element* FindChildWithAttribute(const android::StringPiece& ns, const android::StringPiece& name,
|
||||
const android::StringPiece& attr_ns,
|
||||
const android::StringPiece& attr_name,
|
||||
const android::StringPiece& attr_value);
|
||||
|
||||
const Element* FindChildWithAttribute(const android::StringPiece& ns,
|
||||
const android::StringPiece& name,
|
||||
const android::StringPiece& attr_ns,
|
||||
const android::StringPiece& attr_name,
|
||||
const android::StringPiece& attr_value) const;
|
||||
|
||||
std::vector<Element*> GetChildElements();
|
||||
|
||||
// Due to overriding of subtypes not working with unique_ptr, define a convenience Clone method
|
||||
@@ -139,16 +149,16 @@ class XmlResource {
|
||||
StringPool string_pool;
|
||||
|
||||
std::unique_ptr<xml::Element> root;
|
||||
|
||||
std::unique_ptr<XmlResource> Clone() const;
|
||||
};
|
||||
|
||||
// Inflates an XML DOM from an InputStream, logging errors to the logger.
|
||||
// Returns the root node on success, or nullptr on failure.
|
||||
std::unique_ptr<XmlResource> Inflate(io::InputStream* 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);
|
||||
// Inflates an XML DOM from a binary ResXMLTree.
|
||||
std::unique_ptr<XmlResource> Inflate(const void* data, size_t len,
|
||||
std::string* out_error = nullptr);
|
||||
|
||||
Element* FindRootElement(Node* node);
|
||||
|
||||
|
||||
@@ -70,8 +70,7 @@ TEST(XmlDomTest, BinaryInflate) {
|
||||
ASSERT_TRUE(flattener.Consume(context.get(), doc.get()));
|
||||
|
||||
auto block = util::Copy(buffer);
|
||||
std::unique_ptr<XmlResource> new_doc =
|
||||
Inflate(block.get(), buffer.size(), context->GetDiagnostics(), Source("test.xml"));
|
||||
std::unique_ptr<XmlResource> new_doc = Inflate(block.get(), buffer.size(), nullptr);
|
||||
ASSERT_THAT(new_doc, NotNull());
|
||||
|
||||
EXPECT_THAT(new_doc->root->name, StrEq("Layout"));
|
||||
|
||||
Reference in New Issue
Block a user