Loading tools/aapt2/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ // toolSources = [ "cmd/Command.cpp", "cmd/Compile.cpp", "cmd/Convert.cpp", "cmd/Diff.cpp", Loading Loading @@ -124,7 +125,6 @@ cc_library_host_static { "ConfigDescription.cpp", "Debug.cpp", "DominatorTree.cpp", "Flags.cpp", "java/AnnotationProcessor.cpp", "java/ClassDefinition.cpp", "java/JavaClassGenerator.cpp", Loading tools/aapt2/Main.cpp +97 −83 Original line number Diff line number Diff line Loading @@ -29,6 +29,13 @@ #include "androidfw/StringPiece.h" #include "Diagnostics.h" #include "cmd/Command.h" #include "cmd/Compile.h" #include "cmd/Convert.h" #include "cmd/Diff.h" #include "cmd/Dump.h" #include "cmd/Link.h" #include "cmd/Optimize.h" #include "util/Files.h" #include "util/Util.h" Loading @@ -43,52 +50,66 @@ static const char* sMajorVersion = "2"; // Update minor version whenever a feature or flag is added. static const char* sMinorVersion = "19"; static void PrintVersion() { /** Prints the version information of AAPT2. */ class VersionCommand : public Command { public: explicit VersionCommand() : Command("version") { SetDescription("Prints the version of aapt."); } int Action(const std::vector<std::string>& /* args */) override { std::cerr << StringPrintf("Android Asset Packaging Tool (aapt) %s:%s", sMajorVersion, sMinorVersion) << std::endl; return 0; } }; static void PrintUsage() { std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|convert|version] ..." << std::endl; } extern int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics); 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) { if (command == "compile" || command == "c") { return Compile(args, diagnostics); } else if (command == "link" || command == "l") { return Link(args, diagnostics); } else if (command == "dump" || command == "d") { return Dump(args); } else if (command == "diff") { return Diff(args); } else if (command == "optimize") { return Optimize(args); } else if (command == "convert") { return Convert(args); } else if (command == "version") { PrintVersion(); return 0; /** The main entry point of AAPT. */ class MainCommand : public Command { public: explicit MainCommand(IDiagnostics* diagnostics) : Command("aapt2"), diagnostics_(diagnostics) { AddOptionalSubcommand(util::make_unique<CompileCommand>(diagnostics)); AddOptionalSubcommand(util::make_unique<LinkCommand>(diagnostics)); AddOptionalSubcommand(util::make_unique<DumpCommand>()); AddOptionalSubcommand(util::make_unique<DiffCommand>()); AddOptionalSubcommand(util::make_unique<OptimizeCommand>()); AddOptionalSubcommand(util::make_unique<ConvertCommand>()); AddOptionalSubcommand(util::make_unique<VersionCommand>()); } int Action(const std::vector<std::string>& args) override { if (args.size() == 0) { diagnostics_->Error(DiagMessage() << "no subcommand specified"); } else { diagnostics_->Error(DiagMessage() << "unknown subcommand '" << args[0] << "'"); } diagnostics->Error(DiagMessage() << "unknown command '" << command << "'"); Usage(&std::cerr); return -1; } static void RunDaemon(IDiagnostics* diagnostics) { private: IDiagnostics* diagnostics_; }; /* * Run in daemon mode. The first line of input is the command. This can be 'quit' which ends * the daemon mode. Each subsequent line is a single parameter to the command. The end of a * invocation is signaled by providing an empty line. At any point, an EOF signal or the * command 'quit' will end the daemon mode. */ class DaemonCommand : public Command { public: explicit DaemonCommand(IDiagnostics* diagnostics) : Command("daemon", "m"), diagnostics_(diagnostics) { SetDescription("Runs aapt in daemon mode. Each subsequent line is a single parameter to the\n" "command. The end of an invocation is signaled by providing an empty line."); } int Action(const std::vector<std::string>& /* args */) override { std::cout << "Ready" << std::endl; // Run in daemon mode. The first line of input is the command. This can be 'quit' which ends // the daemon mode. Each subsequent line is a single parameter to the command. The end of a // invocation is signaled by providing an empty line. At any point, an EOF signal or the // command 'quit' will end the daemon mode. while (true) { std::vector<std::string> raw_args; for (std::string line; std::getline(std::cin, line) && !line.empty();) { Loading @@ -104,53 +125,46 @@ static void RunDaemon(IDiagnostics* diagnostics) { continue; } // End the dameon if (raw_args[0] == "quit") { break; } std::vector<StringPiece> args; args.insert(args.end(), ++raw_args.begin(), raw_args.end()); int ret = ExecuteCommand(raw_args[0], args, diagnostics); if (ret != 0) { args.insert(args.end(), raw_args.begin(), raw_args.end()); if (MainCommand(diagnostics_).Execute(args, &std::cerr) != 0) { std::cerr << "Error" << std::endl; } std::cerr << "Done" << std::endl; } std::cout << "Exiting daemon" << std::endl; return 0; } private: IDiagnostics* diagnostics_; }; } // namespace aapt int MainImpl(int argc, char** argv) { if (argc < 2) { std::cerr << "no command specified\n"; aapt::PrintUsage(); if (argc < 1) { return -1; } argv += 1; argc -= 1; aapt::StdErrDiagnostics diagnostics; // Collect the arguments starting after the program name and command name. std::vector<StringPiece> args; for (int i = 1; i < argc; i++) { args.push_back(argv[i]); } const StringPiece command(argv[0]); if (command != "daemon" && command != "m") { // Single execution. const int result = aapt::ExecuteCommand(command, args, &diagnostics); if (result < 0) { aapt::PrintUsage(); } return result; } // Add the daemon subcommand here so it cannot be called while executing the daemon aapt::StdErrDiagnostics diagnostics; auto main_command = new aapt::MainCommand(&diagnostics); main_command->AddOptionalSubcommand(aapt::util::make_unique<aapt::DaemonCommand>(&diagnostics)); aapt::RunDaemon(&diagnostics); return 0; return main_command->Execute(args, &std::cerr); } int main(int argc, char** argv) { Loading tools/aapt2/Flags.cpp→tools/aapt2/cmd/Command.cpp +78 −45 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * Copyright (C) 2018 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. Loading @@ -14,7 +14,7 @@ * limitations under the License. */ #include "Flags.h" #include "Command.h" #include <iomanip> #include <iostream> Loading @@ -29,7 +29,7 @@ using android::StringPiece; namespace aapt { Flags& Flags::RequiredFlag(const StringPiece& name, void Command::AddRequiredFlag(const StringPiece& name, const StringPiece& description, std::string* value) { auto func = [value](const StringPiece& arg) -> bool { *value = arg.to_string(); Loading @@ -37,10 +37,9 @@ Flags& Flags::RequiredFlag(const StringPiece& name, }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false}); return *this; } Flags& Flags::RequiredFlagList(const StringPiece& name, void Command::AddRequiredFlagList(const StringPiece& name, const StringPiece& description, std::vector<std::string>* value) { auto func = [value](const StringPiece& arg) -> bool { Loading @@ -49,10 +48,9 @@ Flags& Flags::RequiredFlagList(const StringPiece& name, }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false}); return *this; } Flags& Flags::OptionalFlag(const StringPiece& name, void Command::AddOptionalFlag(const StringPiece& name, const StringPiece& description, Maybe<std::string>* value) { auto func = [value](const StringPiece& arg) -> bool { Loading @@ -61,10 +59,9 @@ Flags& Flags::OptionalFlag(const StringPiece& name, }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false}); return *this; } Flags& Flags::OptionalFlagList(const StringPiece& name, void Command::AddOptionalFlagList(const StringPiece& name, const StringPiece& description, std::vector<std::string>* value) { auto func = [value](const StringPiece& arg) -> bool { Loading @@ -73,10 +70,9 @@ Flags& Flags::OptionalFlagList(const StringPiece& name, }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false}); return *this; } Flags& Flags::OptionalFlagList(const StringPiece& name, void Command::AddOptionalFlagList(const StringPiece& name, const StringPiece& description, std::unordered_set<std::string>* value) { auto func = [value](const StringPiece& arg) -> bool { Loading @@ -85,10 +81,9 @@ Flags& Flags::OptionalFlagList(const StringPiece& name, }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false}); return *this; } Flags& Flags::OptionalSwitch(const StringPiece& name, void Command::AddOptionalSwitch(const StringPiece& name, const StringPiece& description, bool* value) { auto func = [value](const StringPiece& arg) -> bool { *value = true; Loading @@ -96,20 +91,51 @@ Flags& Flags::OptionalSwitch(const StringPiece& name, }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 0, false}); return *this; } void Flags::Usage(const StringPiece& command, std::ostream* out) { void Command::AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand) { subcommand->fullname_ = name_ + " " + subcommand->name_; subcommands_.push_back(std::move(subcommand)); } void Command::SetDescription(const android::StringPiece& description) { description_ = description.to_string(); } void Command::Usage(std::ostream* out) { constexpr size_t kWidth = 50; *out << command << " [options]"; *out << fullname_; if (!subcommands_.empty()) { *out << " [subcommand]"; } *out << " [options]"; for (const Flag& flag : flags_) { if (flag.required) { *out << " " << flag.name << " arg"; } } *out << " files...\n\nOptions:\n"; *out << " files...\n"; if (!subcommands_.empty()) { *out << "\nSubcommands:\n"; for (auto& subcommand : subcommands_) { std::string argline = subcommand->name_; // Split the description by newlines and write out the argument (which is // empty after the first line) followed by the description line. This will make sure // that multiline descriptions are still right justified and aligned. for (StringPiece line : util::Tokenize(subcommand->description_, '\n')) { *out << " " << std::setw(kWidth) << std::left << argline << line << "\n"; argline = " "; } } } *out << "\nOptions:\n"; for (const Flag& flag : flags_) { std::string argline = flag.name; Loading @@ -118,10 +144,8 @@ void Flags::Usage(const StringPiece& command, std::ostream* out) { } // Split the description by newlines and write out the argument (which is // empty after // the first line) followed by the description line. This will make sure // that multiline // descriptions are still right justified and aligned. // empty after the first line) followed by the description line. This will make sure // that multiline descriptions are still right justified and aligned. for (StringPiece line : util::Tokenize(flag.description, '\n')) { *out << " " << std::setw(kWidth) << std::left << argline << line << "\n"; argline = " "; Loading @@ -132,19 +156,29 @@ void Flags::Usage(const StringPiece& command, std::ostream* out) { out->flush(); } bool Flags::Parse(const StringPiece& command, const std::vector<StringPiece>& args, std::ostream* out_error) { int Command::Execute(const std::vector<android::StringPiece>& args, std::ostream* out_error) { std::vector<std::string> file_args; for (size_t i = 0; i < args.size(); i++) { StringPiece arg = args[i]; if (*(arg.data()) != '-') { args_.push_back(arg.to_string()); // Continue parsing as the sub command if the first argument matches one of the subcommands if (i == 0) { for (auto& subcommand : subcommands_) { if (arg == subcommand->name_ || arg==subcommand->short_name_) { return subcommand->Execute( std::vector<android::StringPiece>(args.begin() + 1, args.end()), out_error); } } } file_args.push_back(arg.to_string()); continue; } if (arg == "-h" || arg == "--help") { Usage(command, out_error); return false; Usage(out_error); return 1; } bool match = false; Loading @@ -154,7 +188,7 @@ bool Flags::Parse(const StringPiece& command, i++; if (i >= args.size()) { *out_error << flag.name << " missing argument.\n\n"; Usage(command, out_error); Usage(out_error); return false; } flag.action(args[i]); Loading @@ -169,21 +203,20 @@ bool Flags::Parse(const StringPiece& command, if (!match) { *out_error << "unknown option '" << arg << "'.\n\n"; Usage(command, out_error); return false; Usage(out_error); return 1; } } for (const Flag& flag : flags_) { if (flag.required && !flag.parsed) { *out_error << "missing required flag " << flag.name << "\n\n"; Usage(command, out_error); return false; Usage(out_error); return 1; } } return true; } const std::vector<std::string>& Flags::GetArgs() { return args_; } return Action(file_args); } } // namespace aapt tools/aapt2/Flags.h→tools/aapt2/cmd/Command.h +90 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * Copyright (C) 2018 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. Loading @@ -14,8 +14,8 @@ * limitations under the License. */ #ifndef AAPT_FLAGS_H #define AAPT_FLAGS_H #ifndef AAPT_COMMAND_H #define AAPT_COMMAND_H #include <functional> #include <ostream> Loading @@ -29,27 +29,42 @@ namespace aapt { class Flags { class Command { public: Flags& RequiredFlag(const android::StringPiece& name, const android::StringPiece& description, explicit Command(const android::StringPiece& name) : name_(name.to_string()), fullname_(name.to_string()) { } explicit Command(const android::StringPiece& name, const android::StringPiece& short_name) : name_(name.to_string()), short_name_(short_name.to_string()), fullname_(name.to_string()) {} virtual ~Command() = default; void AddRequiredFlag(const android::StringPiece& name, const android::StringPiece& description, std::string* value); Flags& RequiredFlagList(const android::StringPiece& name, const android::StringPiece& description, std::vector<std::string>* value); Flags& OptionalFlag(const android::StringPiece& name, const android::StringPiece& description, void AddRequiredFlagList(const android::StringPiece& name, const android::StringPiece& description, std::vector<std::string>* value); void AddOptionalFlag(const android::StringPiece& name, const android::StringPiece& description, Maybe<std::string>* value); Flags& OptionalFlagList(const android::StringPiece& name, const android::StringPiece& description, std::vector<std::string>* value); Flags& OptionalFlagList(const android::StringPiece& name, const android::StringPiece& description, std::unordered_set<std::string>* value); Flags& OptionalSwitch(const android::StringPiece& name, const android::StringPiece& description, void AddOptionalFlagList(const android::StringPiece& name, const android::StringPiece& description, std::vector<std::string>* value); void AddOptionalFlagList(const android::StringPiece& name, const android::StringPiece& description, std::unordered_set<std::string>* value); void AddOptionalSwitch(const android::StringPiece& name, const android::StringPiece& description, bool* value); void AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand); void SetDescription(const android::StringPiece& name); void Usage(const android::StringPiece& command, std::ostream* out); /** Prints the help menu of the command. */ void Usage(std::ostream* out); bool Parse(const android::StringPiece& command, const std::vector<android::StringPiece>& args, std::ostream* outError); /** * Parses the command line arguments, sets the flag variable values, and runs the action of * the command. If the arguments fail to parse to the command and its subcommands, then the action * will not be run and the usage will be printed instead. **/ int Execute(const std::vector<android::StringPiece>& args, std::ostream* outError); const std::vector<std::string>& GetArgs(); /** The action to preform when the command is executed. */ virtual int Action(const std::vector<std::string>& args) = 0; private: struct Flag { Loading @@ -62,10 +77,14 @@ class Flags { bool parsed; }; std::string description_; std::string name_; std::string short_name_; std::string fullname_; std::vector<Flag> flags_; std::vector<std::string> args_; std::vector<std::unique_ptr<Command>> subcommands_; }; } // namespace aapt #endif // AAPT_FLAGS_H #endif // AAPT_COMMAND_H tools/aapt2/cmd/Compile.cpp +27 −67 Original line number Diff line number Diff line Loading @@ -14,8 +14,9 @@ * limitations under the License. */ #include <dirent.h> #include "Compile.h" #include <dirent.h> #include <string> #include "android-base/errors.h" Loading @@ -27,7 +28,6 @@ #include "ConfigDescription.h" #include "Diagnostics.h" #include "Flags.h" #include "ResourceParser.h" #include "ResourceTable.h" #include "cmd/Util.h" Loading Loading @@ -121,17 +121,6 @@ static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path, extension.to_string(), config_str.to_string(), config}; } struct CompileOptions { std::string output_path; Maybe<std::string> res_dir; Maybe<std::string> generate_text_symbols_path; Maybe<Visibility::Level> visibility; bool pseudolocalize = false; bool no_png_crunch = false; bool legacy_mode = false; bool verbose = false; }; static std::string BuildIntermediateContainerFilename(const ResourcePathData& data) { std::stringstream name; name << data.resource_dir; Loading Loading @@ -712,50 +701,21 @@ class CompileContext : public IAaptContext { bool verbose_ = false; }; // Entry point for compilation phase. Parses arguments and dispatches to the correct steps. int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { CompileContext context(diagnostics); CompileOptions options; bool verbose = false; Maybe<std::string> visibility; Flags flags = Flags() .RequiredFlag("-o", "Output path", &options.output_path) .OptionalFlag("--dir", "Directory to scan for resources", &options.res_dir) .OptionalFlag("--output-text-symbols", "Generates a text file containing the resource symbols in the\n" "specified file", &options.generate_text_symbols_path) .OptionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales " "(en-XA and ar-XB)", &options.pseudolocalize) .OptionalSwitch("--no-crunch", "Disables PNG processing", &options.no_png_crunch) .OptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings", &options.legacy_mode) .OptionalSwitch("-v", "Enables verbose logging", &verbose) .OptionalFlag("--visibility", "Sets the visibility of the compiled resources to the specified\n" "level. Accepted levels: public, private, default", &visibility); if (!flags.Parse("aapt2 compile", args, &std::cerr)) { return 1; } context.SetVerbose(verbose); if (visibility) { if (visibility.value() == "public") { options.visibility = Visibility::Level::kPublic; } else if (visibility.value() == "private") { options.visibility = Visibility::Level::kPrivate; } else if (visibility.value() == "default") { options.visibility = Visibility::Level::kUndefined; int CompileCommand::Action(const std::vector<std::string>& args) { CompileContext context(diagnostic_); context.SetVerbose(options_.verbose); if (visibility_) { if (visibility_.value() == "public") { options_.visibility = Visibility::Level::kPublic; } else if (visibility_.value() == "private") { options_.visibility = Visibility::Level::kPrivate; } else if (visibility_.value() == "default") { options_.visibility = Visibility::Level::kUndefined; } else { context.GetDiagnostics()->Error( DiagMessage() << "Unrecognized visibility level passes to --visibility: '" << visibility.value() << "'. Accepted levels: public, private, default"); << visibility_.value() << "'. Accepted levels: public, private, default"); return 1; } } Loading @@ -763,25 +723,25 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { std::unique_ptr<IArchiveWriter> archive_writer; std::vector<ResourcePathData> input_data; if (options.res_dir) { if (!flags.GetArgs().empty()) { if (options_.res_dir) { if (!args.empty()) { // Can't have both files and a resource directory. context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified"); flags.Usage("aapt2 compile", &std::cerr); Usage(&std::cerr); return 1; } if (!LoadInputFilesFromDir(&context, options, &input_data)) { if (!LoadInputFilesFromDir(&context, options_, &input_data)) { return 1; } archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options.output_path); archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options_.output_path); } else { input_data.reserve(flags.GetArgs().size()); input_data.reserve(args.size()); // Collect data from the path for each input file. for (const std::string& arg : flags.GetArgs()) { for (const std::string& arg : args) { std::string error_str; if (Maybe<ResourcePathData> path_data = ExtractResourcePathData(arg, &error_str)) { input_data.push_back(std::move(path_data.value())); Loading @@ -791,7 +751,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { } } archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options.output_path); archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options_.output_path); } if (!archive_writer) { Loading @@ -800,7 +760,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { bool error = false; for (ResourcePathData& path_data : input_data) { if (options.verbose) { if (options_.verbose) { context.GetDiagnostics()->Note(DiagMessage(path_data.source) << "processing"); } Loading @@ -821,7 +781,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { if (*type != ResourceType::kRaw) { if (path_data.extension == "xml") { compile_func = &CompileXml; } else if ((!options.no_png_crunch && path_data.extension == "png") } else if ((!options_.no_png_crunch && path_data.extension == "png") || path_data.extension == "9.png") { compile_func = &CompilePng; } Loading @@ -835,7 +795,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { // Treat periods as a reserved character that should not be present in a file name // Legacy support for AAPT which did not reserve periods if (compile_func != &CompileFile && !options.legacy_mode if (compile_func != &CompileFile && !options_.legacy_mode && std::count(path_data.name.begin(), path_data.name.end(), '.') != 0) { error = true; context.GetDiagnostics()->Error(DiagMessage() << "resource file '" << path_data.source.path Loading @@ -846,7 +806,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { // Compile the file. const std::string out_path = BuildIntermediateContainerFilename(path_data); error |= !compile_func(&context, options, path_data, archive_writer.get(), out_path); error |= !compile_func(&context, options_, path_data, archive_writer.get(), out_path); } return error ? 1 : 0; } Loading Loading
tools/aapt2/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ // toolSources = [ "cmd/Command.cpp", "cmd/Compile.cpp", "cmd/Convert.cpp", "cmd/Diff.cpp", Loading Loading @@ -124,7 +125,6 @@ cc_library_host_static { "ConfigDescription.cpp", "Debug.cpp", "DominatorTree.cpp", "Flags.cpp", "java/AnnotationProcessor.cpp", "java/ClassDefinition.cpp", "java/JavaClassGenerator.cpp", Loading
tools/aapt2/Main.cpp +97 −83 Original line number Diff line number Diff line Loading @@ -29,6 +29,13 @@ #include "androidfw/StringPiece.h" #include "Diagnostics.h" #include "cmd/Command.h" #include "cmd/Compile.h" #include "cmd/Convert.h" #include "cmd/Diff.h" #include "cmd/Dump.h" #include "cmd/Link.h" #include "cmd/Optimize.h" #include "util/Files.h" #include "util/Util.h" Loading @@ -43,52 +50,66 @@ static const char* sMajorVersion = "2"; // Update minor version whenever a feature or flag is added. static const char* sMinorVersion = "19"; static void PrintVersion() { /** Prints the version information of AAPT2. */ class VersionCommand : public Command { public: explicit VersionCommand() : Command("version") { SetDescription("Prints the version of aapt."); } int Action(const std::vector<std::string>& /* args */) override { std::cerr << StringPrintf("Android Asset Packaging Tool (aapt) %s:%s", sMajorVersion, sMinorVersion) << std::endl; return 0; } }; static void PrintUsage() { std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|convert|version] ..." << std::endl; } extern int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics); 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) { if (command == "compile" || command == "c") { return Compile(args, diagnostics); } else if (command == "link" || command == "l") { return Link(args, diagnostics); } else if (command == "dump" || command == "d") { return Dump(args); } else if (command == "diff") { return Diff(args); } else if (command == "optimize") { return Optimize(args); } else if (command == "convert") { return Convert(args); } else if (command == "version") { PrintVersion(); return 0; /** The main entry point of AAPT. */ class MainCommand : public Command { public: explicit MainCommand(IDiagnostics* diagnostics) : Command("aapt2"), diagnostics_(diagnostics) { AddOptionalSubcommand(util::make_unique<CompileCommand>(diagnostics)); AddOptionalSubcommand(util::make_unique<LinkCommand>(diagnostics)); AddOptionalSubcommand(util::make_unique<DumpCommand>()); AddOptionalSubcommand(util::make_unique<DiffCommand>()); AddOptionalSubcommand(util::make_unique<OptimizeCommand>()); AddOptionalSubcommand(util::make_unique<ConvertCommand>()); AddOptionalSubcommand(util::make_unique<VersionCommand>()); } int Action(const std::vector<std::string>& args) override { if (args.size() == 0) { diagnostics_->Error(DiagMessage() << "no subcommand specified"); } else { diagnostics_->Error(DiagMessage() << "unknown subcommand '" << args[0] << "'"); } diagnostics->Error(DiagMessage() << "unknown command '" << command << "'"); Usage(&std::cerr); return -1; } static void RunDaemon(IDiagnostics* diagnostics) { private: IDiagnostics* diagnostics_; }; /* * Run in daemon mode. The first line of input is the command. This can be 'quit' which ends * the daemon mode. Each subsequent line is a single parameter to the command. The end of a * invocation is signaled by providing an empty line. At any point, an EOF signal or the * command 'quit' will end the daemon mode. */ class DaemonCommand : public Command { public: explicit DaemonCommand(IDiagnostics* diagnostics) : Command("daemon", "m"), diagnostics_(diagnostics) { SetDescription("Runs aapt in daemon mode. Each subsequent line is a single parameter to the\n" "command. The end of an invocation is signaled by providing an empty line."); } int Action(const std::vector<std::string>& /* args */) override { std::cout << "Ready" << std::endl; // Run in daemon mode. The first line of input is the command. This can be 'quit' which ends // the daemon mode. Each subsequent line is a single parameter to the command. The end of a // invocation is signaled by providing an empty line. At any point, an EOF signal or the // command 'quit' will end the daemon mode. while (true) { std::vector<std::string> raw_args; for (std::string line; std::getline(std::cin, line) && !line.empty();) { Loading @@ -104,53 +125,46 @@ static void RunDaemon(IDiagnostics* diagnostics) { continue; } // End the dameon if (raw_args[0] == "quit") { break; } std::vector<StringPiece> args; args.insert(args.end(), ++raw_args.begin(), raw_args.end()); int ret = ExecuteCommand(raw_args[0], args, diagnostics); if (ret != 0) { args.insert(args.end(), raw_args.begin(), raw_args.end()); if (MainCommand(diagnostics_).Execute(args, &std::cerr) != 0) { std::cerr << "Error" << std::endl; } std::cerr << "Done" << std::endl; } std::cout << "Exiting daemon" << std::endl; return 0; } private: IDiagnostics* diagnostics_; }; } // namespace aapt int MainImpl(int argc, char** argv) { if (argc < 2) { std::cerr << "no command specified\n"; aapt::PrintUsage(); if (argc < 1) { return -1; } argv += 1; argc -= 1; aapt::StdErrDiagnostics diagnostics; // Collect the arguments starting after the program name and command name. std::vector<StringPiece> args; for (int i = 1; i < argc; i++) { args.push_back(argv[i]); } const StringPiece command(argv[0]); if (command != "daemon" && command != "m") { // Single execution. const int result = aapt::ExecuteCommand(command, args, &diagnostics); if (result < 0) { aapt::PrintUsage(); } return result; } // Add the daemon subcommand here so it cannot be called while executing the daemon aapt::StdErrDiagnostics diagnostics; auto main_command = new aapt::MainCommand(&diagnostics); main_command->AddOptionalSubcommand(aapt::util::make_unique<aapt::DaemonCommand>(&diagnostics)); aapt::RunDaemon(&diagnostics); return 0; return main_command->Execute(args, &std::cerr); } int main(int argc, char** argv) { Loading
tools/aapt2/Flags.cpp→tools/aapt2/cmd/Command.cpp +78 −45 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * Copyright (C) 2018 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. Loading @@ -14,7 +14,7 @@ * limitations under the License. */ #include "Flags.h" #include "Command.h" #include <iomanip> #include <iostream> Loading @@ -29,7 +29,7 @@ using android::StringPiece; namespace aapt { Flags& Flags::RequiredFlag(const StringPiece& name, void Command::AddRequiredFlag(const StringPiece& name, const StringPiece& description, std::string* value) { auto func = [value](const StringPiece& arg) -> bool { *value = arg.to_string(); Loading @@ -37,10 +37,9 @@ Flags& Flags::RequiredFlag(const StringPiece& name, }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false}); return *this; } Flags& Flags::RequiredFlagList(const StringPiece& name, void Command::AddRequiredFlagList(const StringPiece& name, const StringPiece& description, std::vector<std::string>* value) { auto func = [value](const StringPiece& arg) -> bool { Loading @@ -49,10 +48,9 @@ Flags& Flags::RequiredFlagList(const StringPiece& name, }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false}); return *this; } Flags& Flags::OptionalFlag(const StringPiece& name, void Command::AddOptionalFlag(const StringPiece& name, const StringPiece& description, Maybe<std::string>* value) { auto func = [value](const StringPiece& arg) -> bool { Loading @@ -61,10 +59,9 @@ Flags& Flags::OptionalFlag(const StringPiece& name, }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false}); return *this; } Flags& Flags::OptionalFlagList(const StringPiece& name, void Command::AddOptionalFlagList(const StringPiece& name, const StringPiece& description, std::vector<std::string>* value) { auto func = [value](const StringPiece& arg) -> bool { Loading @@ -73,10 +70,9 @@ Flags& Flags::OptionalFlagList(const StringPiece& name, }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false}); return *this; } Flags& Flags::OptionalFlagList(const StringPiece& name, void Command::AddOptionalFlagList(const StringPiece& name, const StringPiece& description, std::unordered_set<std::string>* value) { auto func = [value](const StringPiece& arg) -> bool { Loading @@ -85,10 +81,9 @@ Flags& Flags::OptionalFlagList(const StringPiece& name, }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false}); return *this; } Flags& Flags::OptionalSwitch(const StringPiece& name, void Command::AddOptionalSwitch(const StringPiece& name, const StringPiece& description, bool* value) { auto func = [value](const StringPiece& arg) -> bool { *value = true; Loading @@ -96,20 +91,51 @@ Flags& Flags::OptionalSwitch(const StringPiece& name, }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 0, false}); return *this; } void Flags::Usage(const StringPiece& command, std::ostream* out) { void Command::AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand) { subcommand->fullname_ = name_ + " " + subcommand->name_; subcommands_.push_back(std::move(subcommand)); } void Command::SetDescription(const android::StringPiece& description) { description_ = description.to_string(); } void Command::Usage(std::ostream* out) { constexpr size_t kWidth = 50; *out << command << " [options]"; *out << fullname_; if (!subcommands_.empty()) { *out << " [subcommand]"; } *out << " [options]"; for (const Flag& flag : flags_) { if (flag.required) { *out << " " << flag.name << " arg"; } } *out << " files...\n\nOptions:\n"; *out << " files...\n"; if (!subcommands_.empty()) { *out << "\nSubcommands:\n"; for (auto& subcommand : subcommands_) { std::string argline = subcommand->name_; // Split the description by newlines and write out the argument (which is // empty after the first line) followed by the description line. This will make sure // that multiline descriptions are still right justified and aligned. for (StringPiece line : util::Tokenize(subcommand->description_, '\n')) { *out << " " << std::setw(kWidth) << std::left << argline << line << "\n"; argline = " "; } } } *out << "\nOptions:\n"; for (const Flag& flag : flags_) { std::string argline = flag.name; Loading @@ -118,10 +144,8 @@ void Flags::Usage(const StringPiece& command, std::ostream* out) { } // Split the description by newlines and write out the argument (which is // empty after // the first line) followed by the description line. This will make sure // that multiline // descriptions are still right justified and aligned. // empty after the first line) followed by the description line. This will make sure // that multiline descriptions are still right justified and aligned. for (StringPiece line : util::Tokenize(flag.description, '\n')) { *out << " " << std::setw(kWidth) << std::left << argline << line << "\n"; argline = " "; Loading @@ -132,19 +156,29 @@ void Flags::Usage(const StringPiece& command, std::ostream* out) { out->flush(); } bool Flags::Parse(const StringPiece& command, const std::vector<StringPiece>& args, std::ostream* out_error) { int Command::Execute(const std::vector<android::StringPiece>& args, std::ostream* out_error) { std::vector<std::string> file_args; for (size_t i = 0; i < args.size(); i++) { StringPiece arg = args[i]; if (*(arg.data()) != '-') { args_.push_back(arg.to_string()); // Continue parsing as the sub command if the first argument matches one of the subcommands if (i == 0) { for (auto& subcommand : subcommands_) { if (arg == subcommand->name_ || arg==subcommand->short_name_) { return subcommand->Execute( std::vector<android::StringPiece>(args.begin() + 1, args.end()), out_error); } } } file_args.push_back(arg.to_string()); continue; } if (arg == "-h" || arg == "--help") { Usage(command, out_error); return false; Usage(out_error); return 1; } bool match = false; Loading @@ -154,7 +188,7 @@ bool Flags::Parse(const StringPiece& command, i++; if (i >= args.size()) { *out_error << flag.name << " missing argument.\n\n"; Usage(command, out_error); Usage(out_error); return false; } flag.action(args[i]); Loading @@ -169,21 +203,20 @@ bool Flags::Parse(const StringPiece& command, if (!match) { *out_error << "unknown option '" << arg << "'.\n\n"; Usage(command, out_error); return false; Usage(out_error); return 1; } } for (const Flag& flag : flags_) { if (flag.required && !flag.parsed) { *out_error << "missing required flag " << flag.name << "\n\n"; Usage(command, out_error); return false; Usage(out_error); return 1; } } return true; } const std::vector<std::string>& Flags::GetArgs() { return args_; } return Action(file_args); } } // namespace aapt
tools/aapt2/Flags.h→tools/aapt2/cmd/Command.h +90 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * Copyright (C) 2018 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. Loading @@ -14,8 +14,8 @@ * limitations under the License. */ #ifndef AAPT_FLAGS_H #define AAPT_FLAGS_H #ifndef AAPT_COMMAND_H #define AAPT_COMMAND_H #include <functional> #include <ostream> Loading @@ -29,27 +29,42 @@ namespace aapt { class Flags { class Command { public: Flags& RequiredFlag(const android::StringPiece& name, const android::StringPiece& description, explicit Command(const android::StringPiece& name) : name_(name.to_string()), fullname_(name.to_string()) { } explicit Command(const android::StringPiece& name, const android::StringPiece& short_name) : name_(name.to_string()), short_name_(short_name.to_string()), fullname_(name.to_string()) {} virtual ~Command() = default; void AddRequiredFlag(const android::StringPiece& name, const android::StringPiece& description, std::string* value); Flags& RequiredFlagList(const android::StringPiece& name, const android::StringPiece& description, std::vector<std::string>* value); Flags& OptionalFlag(const android::StringPiece& name, const android::StringPiece& description, void AddRequiredFlagList(const android::StringPiece& name, const android::StringPiece& description, std::vector<std::string>* value); void AddOptionalFlag(const android::StringPiece& name, const android::StringPiece& description, Maybe<std::string>* value); Flags& OptionalFlagList(const android::StringPiece& name, const android::StringPiece& description, std::vector<std::string>* value); Flags& OptionalFlagList(const android::StringPiece& name, const android::StringPiece& description, std::unordered_set<std::string>* value); Flags& OptionalSwitch(const android::StringPiece& name, const android::StringPiece& description, void AddOptionalFlagList(const android::StringPiece& name, const android::StringPiece& description, std::vector<std::string>* value); void AddOptionalFlagList(const android::StringPiece& name, const android::StringPiece& description, std::unordered_set<std::string>* value); void AddOptionalSwitch(const android::StringPiece& name, const android::StringPiece& description, bool* value); void AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand); void SetDescription(const android::StringPiece& name); void Usage(const android::StringPiece& command, std::ostream* out); /** Prints the help menu of the command. */ void Usage(std::ostream* out); bool Parse(const android::StringPiece& command, const std::vector<android::StringPiece>& args, std::ostream* outError); /** * Parses the command line arguments, sets the flag variable values, and runs the action of * the command. If the arguments fail to parse to the command and its subcommands, then the action * will not be run and the usage will be printed instead. **/ int Execute(const std::vector<android::StringPiece>& args, std::ostream* outError); const std::vector<std::string>& GetArgs(); /** The action to preform when the command is executed. */ virtual int Action(const std::vector<std::string>& args) = 0; private: struct Flag { Loading @@ -62,10 +77,14 @@ class Flags { bool parsed; }; std::string description_; std::string name_; std::string short_name_; std::string fullname_; std::vector<Flag> flags_; std::vector<std::string> args_; std::vector<std::unique_ptr<Command>> subcommands_; }; } // namespace aapt #endif // AAPT_FLAGS_H #endif // AAPT_COMMAND_H
tools/aapt2/cmd/Compile.cpp +27 −67 Original line number Diff line number Diff line Loading @@ -14,8 +14,9 @@ * limitations under the License. */ #include <dirent.h> #include "Compile.h" #include <dirent.h> #include <string> #include "android-base/errors.h" Loading @@ -27,7 +28,6 @@ #include "ConfigDescription.h" #include "Diagnostics.h" #include "Flags.h" #include "ResourceParser.h" #include "ResourceTable.h" #include "cmd/Util.h" Loading Loading @@ -121,17 +121,6 @@ static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path, extension.to_string(), config_str.to_string(), config}; } struct CompileOptions { std::string output_path; Maybe<std::string> res_dir; Maybe<std::string> generate_text_symbols_path; Maybe<Visibility::Level> visibility; bool pseudolocalize = false; bool no_png_crunch = false; bool legacy_mode = false; bool verbose = false; }; static std::string BuildIntermediateContainerFilename(const ResourcePathData& data) { std::stringstream name; name << data.resource_dir; Loading Loading @@ -712,50 +701,21 @@ class CompileContext : public IAaptContext { bool verbose_ = false; }; // Entry point for compilation phase. Parses arguments and dispatches to the correct steps. int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { CompileContext context(diagnostics); CompileOptions options; bool verbose = false; Maybe<std::string> visibility; Flags flags = Flags() .RequiredFlag("-o", "Output path", &options.output_path) .OptionalFlag("--dir", "Directory to scan for resources", &options.res_dir) .OptionalFlag("--output-text-symbols", "Generates a text file containing the resource symbols in the\n" "specified file", &options.generate_text_symbols_path) .OptionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales " "(en-XA and ar-XB)", &options.pseudolocalize) .OptionalSwitch("--no-crunch", "Disables PNG processing", &options.no_png_crunch) .OptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings", &options.legacy_mode) .OptionalSwitch("-v", "Enables verbose logging", &verbose) .OptionalFlag("--visibility", "Sets the visibility of the compiled resources to the specified\n" "level. Accepted levels: public, private, default", &visibility); if (!flags.Parse("aapt2 compile", args, &std::cerr)) { return 1; } context.SetVerbose(verbose); if (visibility) { if (visibility.value() == "public") { options.visibility = Visibility::Level::kPublic; } else if (visibility.value() == "private") { options.visibility = Visibility::Level::kPrivate; } else if (visibility.value() == "default") { options.visibility = Visibility::Level::kUndefined; int CompileCommand::Action(const std::vector<std::string>& args) { CompileContext context(diagnostic_); context.SetVerbose(options_.verbose); if (visibility_) { if (visibility_.value() == "public") { options_.visibility = Visibility::Level::kPublic; } else if (visibility_.value() == "private") { options_.visibility = Visibility::Level::kPrivate; } else if (visibility_.value() == "default") { options_.visibility = Visibility::Level::kUndefined; } else { context.GetDiagnostics()->Error( DiagMessage() << "Unrecognized visibility level passes to --visibility: '" << visibility.value() << "'. Accepted levels: public, private, default"); << visibility_.value() << "'. Accepted levels: public, private, default"); return 1; } } Loading @@ -763,25 +723,25 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { std::unique_ptr<IArchiveWriter> archive_writer; std::vector<ResourcePathData> input_data; if (options.res_dir) { if (!flags.GetArgs().empty()) { if (options_.res_dir) { if (!args.empty()) { // Can't have both files and a resource directory. context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified"); flags.Usage("aapt2 compile", &std::cerr); Usage(&std::cerr); return 1; } if (!LoadInputFilesFromDir(&context, options, &input_data)) { if (!LoadInputFilesFromDir(&context, options_, &input_data)) { return 1; } archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options.output_path); archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options_.output_path); } else { input_data.reserve(flags.GetArgs().size()); input_data.reserve(args.size()); // Collect data from the path for each input file. for (const std::string& arg : flags.GetArgs()) { for (const std::string& arg : args) { std::string error_str; if (Maybe<ResourcePathData> path_data = ExtractResourcePathData(arg, &error_str)) { input_data.push_back(std::move(path_data.value())); Loading @@ -791,7 +751,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { } } archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options.output_path); archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options_.output_path); } if (!archive_writer) { Loading @@ -800,7 +760,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { bool error = false; for (ResourcePathData& path_data : input_data) { if (options.verbose) { if (options_.verbose) { context.GetDiagnostics()->Note(DiagMessage(path_data.source) << "processing"); } Loading @@ -821,7 +781,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { if (*type != ResourceType::kRaw) { if (path_data.extension == "xml") { compile_func = &CompileXml; } else if ((!options.no_png_crunch && path_data.extension == "png") } else if ((!options_.no_png_crunch && path_data.extension == "png") || path_data.extension == "9.png") { compile_func = &CompilePng; } Loading @@ -835,7 +795,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { // Treat periods as a reserved character that should not be present in a file name // Legacy support for AAPT which did not reserve periods if (compile_func != &CompileFile && !options.legacy_mode if (compile_func != &CompileFile && !options_.legacy_mode && std::count(path_data.name.begin(), path_data.name.end(), '.') != 0) { error = true; context.GetDiagnostics()->Error(DiagMessage() << "resource file '" << path_data.source.path Loading @@ -846,7 +806,7 @@ int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { // Compile the file. const std::string out_path = BuildIntermediateContainerFilename(path_data); error |= !compile_func(&context, options, path_data, archive_writer.get(), out_path); error |= !compile_func(&context, options_, path_data, archive_writer.get(), out_path); } return error ? 1 : 0; } Loading