Dump XML files in aapt2 dump
Test: manual Change-Id: Id7f656c1385f6d1fec8eaa993c8e2b36bb1ae5b0
This commit is contained in:
@@ -414,59 +414,70 @@ class XmlPrinter : public xml::ConstVisitor {
|
||||
public:
|
||||
using xml::ConstVisitor::Visit;
|
||||
|
||||
XmlPrinter(Printer* printer) : printer_(printer) {
|
||||
}
|
||||
|
||||
void Visit(const xml::Element* el) override {
|
||||
const size_t previous_size = prefix_.size();
|
||||
|
||||
for (const xml::NamespaceDecl& decl : el->namespace_decls) {
|
||||
std::cerr << prefix_ << "N: " << decl.prefix << "=" << decl.uri
|
||||
<< " (line=" << decl.line_number << ")\n";
|
||||
prefix_ += " ";
|
||||
printer_->Println(StringPrintf("N: %s=%s (line=%zu)", decl.prefix.c_str(), decl.uri.c_str(),
|
||||
decl.line_number));
|
||||
printer_->Indent();
|
||||
}
|
||||
|
||||
std::cerr << prefix_ << "E: ";
|
||||
printer_->Print("E: ");
|
||||
if (!el->namespace_uri.empty()) {
|
||||
std::cerr << el->namespace_uri << ":";
|
||||
printer_->Print(el->namespace_uri);
|
||||
printer_->Print(":");
|
||||
}
|
||||
std::cerr << el->name << " (line=" << el->line_number << ")\n";
|
||||
printer_->Println(StringPrintf("%s (line=%zu)", el->name.c_str(), el->line_number));
|
||||
printer_->Indent();
|
||||
|
||||
for (const xml::Attribute& attr : el->attributes) {
|
||||
std::cerr << prefix_ << " A: ";
|
||||
printer_->Print("A: ");
|
||||
if (!attr.namespace_uri.empty()) {
|
||||
std::cerr << attr.namespace_uri << ":";
|
||||
printer_->Print(attr.namespace_uri);
|
||||
printer_->Print(":");
|
||||
}
|
||||
std::cerr << attr.name;
|
||||
printer_->Print(attr.name);
|
||||
|
||||
if (attr.compiled_attribute) {
|
||||
std::cerr << "(" << attr.compiled_attribute.value().id.value_or_default(ResourceId(0x0))
|
||||
<< ")";
|
||||
printer_->Print("(");
|
||||
printer_->Print(
|
||||
attr.compiled_attribute.value().id.value_or_default(ResourceId(0)).to_string());
|
||||
printer_->Print(")");
|
||||
}
|
||||
std::cerr << "=";
|
||||
printer_->Print("=");
|
||||
if (attr.compiled_value != nullptr) {
|
||||
std::cerr << *attr.compiled_value;
|
||||
attr.compiled_value->PrettyPrint(printer_);
|
||||
} else {
|
||||
std::cerr << attr.value;
|
||||
printer_->Print(attr.value);
|
||||
}
|
||||
std::cerr << "\n";
|
||||
printer_->Println();
|
||||
}
|
||||
|
||||
prefix_ += " ";
|
||||
printer_->Indent();
|
||||
xml::ConstVisitor::Visit(el);
|
||||
prefix_.resize(previous_size);
|
||||
printer_->Undent();
|
||||
printer_->Undent();
|
||||
|
||||
for (size_t i = 0; i < el->namespace_decls.size(); i++) {
|
||||
printer_->Undent();
|
||||
}
|
||||
}
|
||||
|
||||
void Visit(const xml::Text* text) override {
|
||||
std::cerr << prefix_ << "T: '" << text->text << "'\n";
|
||||
printer_->Println(StringPrintf("T: '%s'", text->text.c_str()));
|
||||
}
|
||||
|
||||
private:
|
||||
std::string prefix_;
|
||||
Printer* printer_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void Debug::DumpXml(const xml::XmlResource& doc) {
|
||||
XmlPrinter printer;
|
||||
doc.root->Accept(&printer);
|
||||
void Debug::DumpXml(const xml::XmlResource& doc, Printer* printer) {
|
||||
XmlPrinter xml_visitor(printer);
|
||||
doc.root->Accept(&xml_visitor);
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -37,7 +37,7 @@ struct Debug {
|
||||
text::Printer* printer);
|
||||
static void PrintStyleGraph(ResourceTable* table, const ResourceName& target_style);
|
||||
static void DumpHex(const void* data, size_t len);
|
||||
static void DumpXml(const xml::XmlResource& doc);
|
||||
static void DumpXml(const xml::XmlResource& doc, text::Printer* printer);
|
||||
};
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -38,6 +38,13 @@ using ::android::base::StringPrintf;
|
||||
|
||||
namespace aapt {
|
||||
|
||||
struct DumpOptions {
|
||||
DebugPrintTableOptions print_options;
|
||||
|
||||
// The path to a file within an APK to dump.
|
||||
Maybe<std::string> file_to_dump_path;
|
||||
};
|
||||
|
||||
static const char* ResourceFileTypeToString(const ResourceFile::Type& type) {
|
||||
switch (type) {
|
||||
case ResourceFile::Type::kPng:
|
||||
@@ -69,8 +76,52 @@ static void DumpCompiledFile(const ResourceFile& file, const Source& source, off
|
||||
printer->Println(StringPrintf("Data: offset=%" PRIi64 " length=%zd", offset, len));
|
||||
}
|
||||
|
||||
static bool DumpXmlFile(IAaptContext* context, io::IFile* file, bool proto,
|
||||
text::Printer* printer) {
|
||||
std::unique_ptr<xml::XmlResource> doc;
|
||||
if (proto) {
|
||||
std::unique_ptr<io::InputStream> in = file->OpenInputStream();
|
||||
if (in == nullptr) {
|
||||
context->GetDiagnostics()->Error(DiagMessage() << "failed to open file");
|
||||
return false;
|
||||
}
|
||||
|
||||
io::ZeroCopyInputAdaptor adaptor(in.get());
|
||||
pb::XmlNode pb_node;
|
||||
if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
|
||||
context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as proto XML");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string err;
|
||||
doc = DeserializeXmlResourceFromPb(pb_node, &err);
|
||||
if (doc == nullptr) {
|
||||
context->GetDiagnostics()->Error(DiagMessage() << "failed to deserialize proto XML");
|
||||
return false;
|
||||
}
|
||||
printer->Println("Proto XML");
|
||||
} else {
|
||||
std::unique_ptr<io::IData> data = file->OpenAsData();
|
||||
if (data == nullptr) {
|
||||
context->GetDiagnostics()->Error(DiagMessage() << "failed to open file");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string err;
|
||||
doc = xml::Inflate(data->data(), data->size(), &err);
|
||||
if (doc == nullptr) {
|
||||
context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as binary XML");
|
||||
return false;
|
||||
}
|
||||
printer->Println("Binary XML");
|
||||
}
|
||||
|
||||
Debug::DumpXml(*doc, printer);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
|
||||
const DebugPrintTableOptions& print_options) {
|
||||
const DumpOptions& options) {
|
||||
// Use a smaller buffer so that there is less latency for dumping to stdout.
|
||||
constexpr size_t kStdOutBufferSize = 1024u;
|
||||
io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
|
||||
@@ -80,7 +131,10 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
|
||||
std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err);
|
||||
if (zip) {
|
||||
ResourceTable table;
|
||||
bool proto = false;
|
||||
if (io::IFile* file = zip->FindFile("resources.pb")) {
|
||||
proto = true;
|
||||
|
||||
std::unique_ptr<io::IData> data = file->OpenAsData();
|
||||
if (data == nullptr) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.pb");
|
||||
@@ -98,8 +152,6 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
|
||||
<< "failed to parse table: " << err);
|
||||
return false;
|
||||
}
|
||||
|
||||
printer.Println("Proto APK");
|
||||
} else if (io::IFile* file = zip->FindFile("resources.arsc")) {
|
||||
std::unique_ptr<io::IData> data = file->OpenAsData();
|
||||
if (!data) {
|
||||
@@ -112,12 +164,26 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
|
||||
if (!parser.Parse()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
printer.Println("Binary APK");
|
||||
}
|
||||
|
||||
Debug::PrintTable(table, print_options, &printer);
|
||||
return true;
|
||||
if (!options.file_to_dump_path) {
|
||||
if (proto) {
|
||||
printer.Println("Proto APK");
|
||||
} else {
|
||||
printer.Println("Binary APK");
|
||||
}
|
||||
Debug::PrintTable(table, options.print_options, &printer);
|
||||
return true;
|
||||
}
|
||||
|
||||
io::IFile* file = zip->FindFile(options.file_to_dump_path.value());
|
||||
if (file == nullptr) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(file_path)
|
||||
<< "file '" << options.file_to_dump_path.value()
|
||||
<< "' not found in APK");
|
||||
return false;
|
||||
}
|
||||
return DumpXmlFile(context, file, proto, &printer);
|
||||
}
|
||||
|
||||
err.clear();
|
||||
@@ -159,7 +225,7 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
|
||||
}
|
||||
|
||||
printer.Indent();
|
||||
Debug::PrintTable(table, print_options, &printer);
|
||||
Debug::PrintTable(table, options.print_options, &printer);
|
||||
printer.Undent();
|
||||
} else if (entry->Type() == ContainerEntryType::kResFile) {
|
||||
printer.Println("kResFile");
|
||||
@@ -243,10 +309,13 @@ class DumpContext : public IAaptContext {
|
||||
int Dump(const std::vector<StringPiece>& args) {
|
||||
bool verbose = false;
|
||||
bool no_values = false;
|
||||
DumpOptions options;
|
||||
Flags flags = Flags()
|
||||
.OptionalSwitch("--no-values",
|
||||
"Suppresses output of values when displaying resource tables.",
|
||||
&no_values)
|
||||
.OptionalFlag("--file", "Dumps the specified file from the APK passed as arg.",
|
||||
&options.file_to_dump_path)
|
||||
.OptionalSwitch("-v", "increase verbosity of output", &verbose);
|
||||
if (!flags.Parse("aapt2 dump", args, &std::cerr)) {
|
||||
return 1;
|
||||
@@ -255,11 +324,10 @@ int Dump(const std::vector<StringPiece>& args) {
|
||||
DumpContext context;
|
||||
context.SetVerbose(verbose);
|
||||
|
||||
DebugPrintTableOptions dump_table_options;
|
||||
dump_table_options.show_sources = true;
|
||||
dump_table_options.show_values = !no_values;
|
||||
options.print_options.show_sources = true;
|
||||
options.print_options.show_values = !no_values;
|
||||
for (const std::string& arg : flags.GetArgs()) {
|
||||
if (!TryDumpFile(&context, arg, dump_table_options)) {
|
||||
if (!TryDumpFile(&context, arg, options)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user