public:
using xml::ConstVisitor::Visit;
- void Visit(const xml::Element* el) override {
- const size_t previous_size = prefix_.size();
+ XmlPrinter(Printer* printer) : printer_(printer) {
+ }
+ void Visit(const xml::Element* el) override {
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
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:
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);
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");
<< "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) {
if (!parser.Parse()) {
return false;
}
+ }
- printer.Println("Binary APK");
+ 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;
}
- Debug::PrintTable(table, 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();
}
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");
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;
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;
}
}