diff options
author | 2018-07-10 13:51:36 -0700 | |
---|---|---|
committer | 2018-07-13 14:26:06 -0700 | |
commit | 833a1a6c4a7b57431ae620fc83f4c17f6fbf6a06 (patch) | |
tree | acac93a67f807052f0b77700195377f904ed20fa | |
parent | 53208af20d501b892b9cc9971033a9f2e2d7cf35 (diff) |
AAPT2: Refactor flags into commands
Refactors the flag based command invocation into classes that make using
subcommands easier.
Test: manual tests of printing
Change-Id: Ic8df6af0be30db552e32150afebecbfeec7e1075
-rw-r--r-- | tools/aapt2/Android.bp | 2 | ||||
-rw-r--r-- | tools/aapt2/Flags.h | 71 | ||||
-rw-r--r-- | tools/aapt2/Main.cpp | 180 | ||||
-rw-r--r-- | tools/aapt2/cmd/Command.cpp (renamed from tools/aapt2/Flags.cpp) | 123 | ||||
-rw-r--r-- | tools/aapt2/cmd/Command.h | 90 | ||||
-rw-r--r-- | tools/aapt2/cmd/Compile.cpp | 94 | ||||
-rw-r--r-- | tools/aapt2/cmd/Compile.h | 58 | ||||
-rw-r--r-- | tools/aapt2/cmd/Compile_test.cpp | 5 | ||||
-rw-r--r-- | tools/aapt2/cmd/Convert.cpp | 50 | ||||
-rw-r--r-- | tools/aapt2/cmd/Convert.h | 54 | ||||
-rw-r--r-- | tools/aapt2/cmd/Diff.cpp | 18 | ||||
-rw-r--r-- | tools/aapt2/cmd/Diff.h | 35 | ||||
-rw-r--r-- | tools/aapt2/cmd/Dump.cpp | 38 | ||||
-rw-r--r-- | tools/aapt2/cmd/Dump.h | 54 | ||||
-rw-r--r-- | tools/aapt2/cmd/Link.cpp | 356 | ||||
-rw-r--r-- | tools/aapt2/cmd/Link.h | 282 | ||||
-rw-r--r-- | tools/aapt2/cmd/Optimize.cpp | 169 | ||||
-rw-r--r-- | tools/aapt2/cmd/Optimize.h | 124 | ||||
-rw-r--r-- | tools/aapt2/jni/aapt2_jni.cpp | 11 |
19 files changed, 1030 insertions, 784 deletions
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index 7a33de0262e2..2ecf25b751b6 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -15,6 +15,7 @@ // toolSources = [ + "cmd/Command.cpp", "cmd/Compile.cpp", "cmd/Convert.cpp", "cmd/Diff.cpp", @@ -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", diff --git a/tools/aapt2/Flags.h b/tools/aapt2/Flags.h deleted file mode 100644 index 3b3ae710dc7b..000000000000 --- a/tools/aapt2/Flags.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AAPT_FLAGS_H -#define AAPT_FLAGS_H - -#include <functional> -#include <ostream> -#include <string> -#include <unordered_set> -#include <vector> - -#include "androidfw/StringPiece.h" - -#include "util/Maybe.h" - -namespace aapt { - -class Flags { - public: - Flags& RequiredFlag(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, - 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, - bool* value); - - void Usage(const android::StringPiece& command, std::ostream* out); - - bool Parse(const android::StringPiece& command, const std::vector<android::StringPiece>& args, - std::ostream* outError); - - const std::vector<std::string>& GetArgs(); - - private: - struct Flag { - std::string name; - std::string description; - std::function<bool(const android::StringPiece& value)> action; - bool required; - size_t num_args; - - bool parsed; - }; - - std::vector<Flag> flags_; - std::vector<std::string> args_; -}; - -} // namespace aapt - -#endif // AAPT_FLAGS_H diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index 808b29cfd844..23903c9e05f3 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -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" @@ -43,114 +50,121 @@ static const char* sMajorVersion = "2"; // Update minor version whenever a feature or flag is added. static const char* sMinorVersion = "19"; -static void PrintVersion() { - std::cerr << StringPrintf("Android Asset Packaging Tool (aapt) %s:%s", sMajorVersion, - sMinorVersion) - << std::endl; -} - -static void PrintUsage() { - std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|convert|version] ..." << std::endl; -} +/** Prints the version information of AAPT2. */ +class VersionCommand : public Command { + public: + explicit VersionCommand() : Command("version") { + SetDescription("Prints the version of aapt."); + } -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(); + 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; } - diagnostics->Error(DiagMessage() << "unknown command '" << command << "'"); - return -1; -} +}; + +/** 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>()); + } -static void RunDaemon(IDiagnostics* diagnostics) { - 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();) { - raw_args.push_back(line); + 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] << "'"); } - if (!std::cin) { - break; - } + Usage(&std::cerr); + return -1; + } - // An empty command does nothing. - if (raw_args.empty()) { - continue; - } + private: + IDiagnostics* diagnostics_; +}; - if (raw_args[0] == "quit") { - break; - } +/* + * 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."); + } - 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) { - std::cerr << "Error" << std::endl; + int Action(const std::vector<std::string>& /* args */) override { + std::cout << "Ready" << std::endl; + + while (true) { + std::vector<std::string> raw_args; + for (std::string line; std::getline(std::cin, line) && !line.empty();) { + raw_args.push_back(line); + } + + if (!std::cin) { + break; + } + + // An empty command does nothing. + if (raw_args.empty()) { + continue; + } + + // End the dameon + if (raw_args[0] == "quit") { + break; + } + + std::vector<StringPiece> args; + 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::cerr << "Done" << std::endl; + std::cout << "Exiting daemon" << std::endl; + + return 0; } - std::cout << "Exiting daemon" << std::endl; -} + + 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) { diff --git a/tools/aapt2/Flags.cpp b/tools/aapt2/cmd/Command.cpp index 84977ab424cc..09411b95bcfe 100644 --- a/tools/aapt2/Flags.cpp +++ b/tools/aapt2/cmd/Command.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "Flags.h" +#include "Command.h" #include <iomanip> #include <iostream> @@ -29,87 +29,113 @@ using android::StringPiece; namespace aapt { -Flags& Flags::RequiredFlag(const StringPiece& name, - const StringPiece& description, std::string* value) { +void Command::AddRequiredFlag(const StringPiece& name, + const StringPiece& description, std::string* value) { auto func = [value](const StringPiece& arg) -> bool { *value = arg.to_string(); return true; }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false}); - return *this; } -Flags& Flags::RequiredFlagList(const StringPiece& name, - const StringPiece& description, - std::vector<std::string>* value) { +void Command::AddRequiredFlagList(const StringPiece& name, + const StringPiece& description, + std::vector<std::string>* value) { auto func = [value](const StringPiece& arg) -> bool { value->push_back(arg.to_string()); return true; }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false}); - return *this; } -Flags& Flags::OptionalFlag(const StringPiece& name, - const StringPiece& description, - Maybe<std::string>* value) { +void Command::AddOptionalFlag(const StringPiece& name, + const StringPiece& description, + Maybe<std::string>* value) { auto func = [value](const StringPiece& arg) -> bool { *value = arg.to_string(); return true; }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false}); - return *this; } -Flags& Flags::OptionalFlagList(const StringPiece& name, - const StringPiece& description, - std::vector<std::string>* value) { +void Command::AddOptionalFlagList(const StringPiece& name, + const StringPiece& description, + std::vector<std::string>* value) { auto func = [value](const StringPiece& arg) -> bool { value->push_back(arg.to_string()); return true; }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false}); - return *this; } -Flags& Flags::OptionalFlagList(const StringPiece& name, - const StringPiece& description, - std::unordered_set<std::string>* value) { +void Command::AddOptionalFlagList(const StringPiece& name, + const StringPiece& description, + std::unordered_set<std::string>* value) { auto func = [value](const StringPiece& arg) -> bool { value->insert(arg.to_string()); return true; }; flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false}); - return *this; } -Flags& Flags::OptionalSwitch(const StringPiece& name, - const StringPiece& description, bool* value) { +void Command::AddOptionalSwitch(const StringPiece& name, + const StringPiece& description, bool* value) { auto func = [value](const StringPiece& arg) -> bool { *value = true; return true; }; 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; @@ -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 = " "; @@ -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; @@ -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]); @@ -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 diff --git a/tools/aapt2/cmd/Command.h b/tools/aapt2/cmd/Command.h new file mode 100644 index 000000000000..71dc6fe48da1 --- /dev/null +++ b/tools/aapt2/cmd/Command.h @@ -0,0 +1,90 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AAPT_COMMAND_H +#define AAPT_COMMAND_H + +#include <functional> +#include <ostream> +#include <string> +#include <unordered_set> +#include <vector> + +#include "androidfw/StringPiece.h" + +#include "util/Maybe.h" + +namespace aapt { + +class Command { + public: + 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); + 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); + 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); + + /** Prints the help menu of the command. */ + void Usage(std::ostream* out); + + /** + * 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); + + /** The action to preform when the command is executed. */ + virtual int Action(const std::vector<std::string>& args) = 0; + + private: + struct Flag { + std::string name; + std::string description; + std::function<bool(const android::StringPiece& value)> action; + bool required; + size_t num_args; + + bool parsed; + }; + + std::string description_; + std::string name_; + std::string short_name_; + std::string fullname_; + std::vector<Flag> flags_; + std::vector<std::unique_ptr<Command>> subcommands_; +}; + +} // namespace aapt + +#endif // AAPT_COMMAND_H diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp index a17a0d3e7299..8c1fa9ac0226 100644 --- a/tools/aapt2/cmd/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -14,8 +14,9 @@ * limitations under the License. */ -#include <dirent.h> +#include "Compile.h" +#include <dirent.h> #include <string> #include "android-base/errors.h" @@ -27,7 +28,6 @@ #include "ConfigDescription.h" #include "Diagnostics.h" -#include "Flags.h" #include "ResourceParser.h" #include "ResourceTable.h" #include "cmd/Util.h" @@ -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; @@ -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; } } @@ -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())); @@ -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) { @@ -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"); } @@ -821,21 +781,21 @@ 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; } } } else { context.GetDiagnostics()->Error(DiagMessage() - << "invalid file path '" << path_data.source << "'"); + << "invalid file path '" << path_data.source << "'"); error = true; continue; } // 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 @@ -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; } diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h index d95cf1c22732..41519520fda1 100644 --- a/tools/aapt2/cmd/Compile.h +++ b/tools/aapt2/cmd/Compile.h @@ -1,13 +1,69 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #ifndef AAPT2_COMPILE_H #define AAPT2_COMPILE_H #include "androidfw/StringPiece.h" +#include "Command.h" #include "Diagnostics.h" +#include "ResourceTable.h" namespace aapt { - int Compile(const std::vector<android::StringPiece>& args, IDiagnostics* diagnostics); +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; +}; + +class CompileCommand : public Command { + public: + explicit CompileCommand(IDiagnostics* diagnostic) : Command("compile", "c"), + diagnostic_(diagnostic) { + SetDescription("Compiles resources to be linked into an apk."); + AddRequiredFlag("-o", "Output path", &options_.output_path); + AddOptionalFlag("--dir", "Directory to scan for resources", &options_.res_dir); + AddOptionalFlag("--output-text-symbols", + "Generates a text file containing the resource symbols in the\n" + "specified file", &options_.generate_text_symbols_path); + AddOptionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales " + "(en-XA and ar-XB)", &options_.pseudolocalize); + AddOptionalSwitch("--no-crunch", "Disables PNG processing", &options_.no_png_crunch); + AddOptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings", + &options_.legacy_mode); + AddOptionalSwitch("-v", "Enables verbose logging", &options_.verbose); + AddOptionalFlag("--visibility", + "Sets the visibility of the compiled resources to the specified\n" + "level. Accepted levels: public, private, default", &visibility_); + } + + int Action(const std::vector<std::string>& args) override; + + private: + IDiagnostics* diagnostic_; + CompileOptions options_; + Maybe<std::string> visibility_; +}; }// namespace aapt diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp index 212f2cf26e0d..d21addf4a081 100644 --- a/tools/aapt2/cmd/Compile_test.cpp +++ b/tools/aapt2/cmd/Compile_test.cpp @@ -23,7 +23,8 @@ namespace aapt { -int TestCompile(std::string path, std::string outDir, bool legacy, StdErrDiagnostics& diag) { +int TestCompile(const std::string& path, const std::string& outDir, bool legacy, + StdErrDiagnostics& diag) { std::vector<android::StringPiece> args; args.push_back(path); args.push_back("-o"); @@ -32,7 +33,7 @@ int TestCompile(std::string path, std::string outDir, bool legacy, StdErrDiagnos if (legacy) { args.push_back("--legacy"); } - return aapt::Compile(args, &diag); + return CompileCommand(&diag).Execute(args, &std::cerr); } TEST(CompilerTest, MultiplePeriods) { diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp index 3c8beaa19008..4b82eefa7d6f 100644 --- a/tools/aapt2/cmd/Convert.cpp +++ b/tools/aapt2/cmd/Convert.cpp @@ -14,12 +14,13 @@ * limitations under the License. */ +#include "Convert.h" + #include <vector> #include "android-base/macros.h" #include "androidfw/StringPiece.h" -#include "Flags.h" #include "LoadedApk.h" #include "ValueVisitor.h" #include "cmd/Util.h" @@ -325,37 +326,18 @@ class Context : public IAaptContext { StdErrDiagnostics diag_; }; -int Convert(const vector<StringPiece>& args) { - - static const char* kOutputFormatProto = "proto"; - static const char* kOutputFormatBinary = "binary"; +const char* ConvertCommand::kOutputFormatProto = "proto"; +const char* ConvertCommand::kOutputFormatBinary = "binary"; - Context context; - std::string output_path; - Maybe<std::string> output_format; - TableFlattenerOptions options; - Flags flags = - Flags() - .RequiredFlag("-o", "Output path", &output_path) - .OptionalFlag("--output-format", StringPrintf("Format of the output. Accepted values are " - "'%s' and '%s'. When not set, defaults to '%s'.", kOutputFormatProto, - kOutputFormatBinary, kOutputFormatBinary), &output_format) - .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) { +int ConvertCommand::Action(const std::vector<std::string>& args) { + if (args.size() != 1) { std::cerr << "must supply a single proto APK\n"; - flags.Usage("aapt2 convert", &std::cerr); + Usage(&std::cerr); return 1; } - const StringPiece& path = flags.GetArgs()[0]; + Context context; + const StringPiece& path = args[0]; unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics()); if (apk == nullptr) { context.GetDiagnostics()->Error(DiagMessage(path) << "failed to load APK"); @@ -371,24 +353,24 @@ int Convert(const vector<StringPiece>& args) { context.package_ = app_info.value().package; unique_ptr<IArchiveWriter> writer = - CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path); + CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path_); if (writer == nullptr) { return 1; } unique_ptr<IApkSerializer> serializer; - if (!output_format || output_format.value() == kOutputFormatBinary) { - serializer.reset(new BinaryApkSerializer(&context, apk->GetSource(), options)); - } else if (output_format.value() == kOutputFormatProto) { + if (!output_format_ || output_format_.value() == ConvertCommand::kOutputFormatBinary) { + + serializer.reset(new BinaryApkSerializer(&context, apk->GetSource(), options_)); + } else if (output_format_.value() == ConvertCommand::kOutputFormatProto) { serializer.reset(new ProtoApkSerializer(&context, apk->GetSource())); } else { context.GetDiagnostics()->Error(DiagMessage(path) - << "Invalid value for flag --output-format: " - << output_format.value()); + << "Invalid value for flag --output-format: " + << output_format_.value()); return 1; } - return ConvertApk(&context, std::move(apk), serializer.get(), writer.get()) ? 0 : 1; } diff --git a/tools/aapt2/cmd/Convert.h b/tools/aapt2/cmd/Convert.h new file mode 100644 index 000000000000..fcec23d16f78 --- /dev/null +++ b/tools/aapt2/cmd/Convert.h @@ -0,0 +1,54 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AAPT2_CONVERT_H +#define AAPT2_CONVERT_H + +#include "Command.h" +#include "format/binary/TableFlattener.h" + +namespace aapt { + +class ConvertCommand : public Command { + public: + explicit ConvertCommand() : Command("convert") { + SetDescription("Converts an apk between binary and proto formats."); + AddRequiredFlag("-o", "Output path", &output_path_); + AddOptionalFlag("--output-format", android::base::StringPrintf("Format of the output. " + "Accepted values are '%s' and '%s'. When not set, defaults to '%s'.", + kOutputFormatProto, kOutputFormatBinary, kOutputFormatBinary), &output_format_); + AddOptionalSwitch("--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); + AddOptionalSwitch("-v", "Enables verbose logging", &verbose_); + } + + int Action(const std::vector<std::string>& args) override; + + private: + const static char* kOutputFormatProto; + const static char* kOutputFormatBinary; + + TableFlattenerOptions options_; + std::string output_path_; + Maybe<std::string> output_format_; + bool verbose_ = false; +}; + +}// namespace aapt + +#endif //AAPT2_CONVERT_H diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp index 16c7bba33e10..7875a2bb7a86 100644 --- a/tools/aapt2/cmd/Diff.cpp +++ b/tools/aapt2/cmd/Diff.cpp @@ -14,9 +14,10 @@ * limitations under the License. */ +#include "Diff.h" + #include "android-base/macros.h" -#include "Flags.h" #include "LoadedApk.h" #include "ValueVisitor.h" #include "process/IResourceTableConsumer.h" @@ -348,23 +349,18 @@ static void ZeroOutAppReferences(ResourceTable* table) { VisitAllValuesInTable(table, &visitor); } -int Diff(const std::vector<StringPiece>& args) { +int DiffCommand::Action(const std::vector<std::string>& args) { DiffContext context; - Flags flags; - if (!flags.Parse("aapt2 diff", args, &std::cerr)) { - return 1; - } - - if (flags.GetArgs().size() != 2u) { + if (args.size() != 2u) { std::cerr << "must have two apks as arguments.\n\n"; - flags.Usage("aapt2 diff", &std::cerr); + Usage(&std::cerr); return 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); + std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(args[0], diag); + std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(args[1], diag); if (!apk_a || !apk_b) { return 1; } diff --git a/tools/aapt2/cmd/Diff.h b/tools/aapt2/cmd/Diff.h new file mode 100644 index 000000000000..c38888863be9 --- /dev/null +++ b/tools/aapt2/cmd/Diff.h @@ -0,0 +1,35 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AAPT2_DIFF_H +#define AAPT2_DIFF_H + +#include "Command.h" + +namespace aapt { + +class DiffCommand : public Command { + public: + explicit DiffCommand() : Command("diff") { + SetDescription("Prints the differences in resources of two apks."); + } + + int Action(const std::vector<std::string>& args) override; +}; + +}// namespace aapt + +#endif //AAPT2_DIFF_H diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp index fd133f3da175..717e757a1bba 100644 --- a/tools/aapt2/cmd/Dump.cpp +++ b/tools/aapt2/cmd/Dump.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "Dump.h" + #include <cinttypes> #include <vector> @@ -22,7 +24,6 @@ #include "Debug.h" #include "Diagnostics.h" -#include "Flags.h" #include "format/Container.h" #include "format/binary/BinaryResourceParser.h" #include "format/proto/ProtoDeserialize.h" @@ -38,13 +39,6 @@ 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: @@ -309,29 +303,13 @@ class DumpContext : public IAaptContext { } // namespace -// Entry point for dump command. -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; - } - +int DumpCommand::Action(const std::vector<std::string>& args) { DumpContext context; - context.SetVerbose(verbose); - - options.print_options.show_sources = true; - options.print_options.show_values = !no_values; - for (const std::string& arg : flags.GetArgs()) { - if (!TryDumpFile(&context, arg, options)) { + context.SetVerbose(verbose_); + options_.print_options.show_sources = true; + options_.print_options.show_values = !no_values_; + for (const std::string& arg : args) { + if (!TryDumpFile(&context, arg, options_)) { return 1; } } diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h new file mode 100644 index 000000000000..4893c8b76041 --- /dev/null +++ b/tools/aapt2/cmd/Dump.h @@ -0,0 +1,54 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AAPT2_DUMP_H +#define AAPT2_DUMP_H + +#include "Command.h" +#include "Debug.h" + +namespace aapt { + +struct DumpOptions { + DebugPrintTableOptions print_options; + + // The path to a file within an APK to dump. + Maybe<std::string> file_to_dump_path; +}; + +class DumpCommand : public Command { + public: + DumpCommand() : Command("dump", "d") { + SetDescription("Prints resource and manifest information."); + AddOptionalSwitch("--no-values", "Suppresses output of values when displaying resource tables.", + &no_values_); + AddOptionalFlag("--file", "Dumps the specified file from the APK passed as arg.", + &options_.file_to_dump_path); + AddOptionalSwitch("-v", "increase verbosity of output", &verbose_); + } + + int Action(const std::vector<std::string>& args) override; + + private: + DumpOptions options_; + + bool verbose_ = false; + bool no_values_ = false; +}; + +}// namespace aapt + +#endif //AAPT2_DUMP_H diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index 1a2da7fbd000..26770d1f281c 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "Link.h" + #include <sys/stat.h> #include <cinttypes> @@ -29,7 +31,6 @@ #include "AppInfo.h" #include "Debug.h" -#include "Flags.h" #include "LoadedApk.h" #include "Locale.h" #include "NameMangler.h" @@ -74,74 +75,6 @@ using ::android::base::StringPrintf; namespace aapt { -enum class OutputFormat { - kApk, - kProto, -}; - -struct LinkOptions { - std::string output_path; - std::string manifest_path; - std::vector<std::string> include_paths; - std::vector<std::string> overlay_files; - std::vector<std::string> assets_dirs; - bool output_to_directory = false; - bool auto_add_overlay = false; - OutputFormat output_format = OutputFormat::kApk; - - // Java/Proguard options. - Maybe<std::string> generate_java_class_path; - Maybe<std::string> custom_java_package; - std::set<std::string> extra_java_packages; - Maybe<std::string> generate_text_symbols_path; - Maybe<std::string> generate_proguard_rules_path; - Maybe<std::string> generate_main_dex_proguard_rules_path; - bool generate_conditional_proguard_rules = false; - bool generate_non_final_ids = false; - std::vector<std::string> javadoc_annotations; - Maybe<std::string> private_symbols; - - // Optimizations/features. - bool no_auto_version = false; - bool no_version_vectors = false; - bool no_version_transitions = false; - bool no_resource_deduping = false; - bool no_xml_namespaces = false; - bool do_not_compress_anything = false; - std::unordered_set<std::string> extensions_to_not_compress; - - // Static lib options. - bool no_static_lib_packages = false; - bool auto_namespace_static_lib = false; - - // AndroidManifest.xml massaging options. - ManifestFixerOptions manifest_fixer_options; - - // Products to use/filter on. - std::unordered_set<std::string> products; - - // Flattening options. - TableFlattenerOptions table_flattener_options; - - // Split APK options. - TableSplitterOptions table_splitter_options; - std::vector<SplitConstraints> split_constraints; - std::vector<std::string> split_paths; - - // Stable ID options. - std::unordered_map<ResourceName, ResourceId> stable_id_map; - Maybe<std::string> resource_id_map_path; - - // When 'true', allow reserved package IDs to be used for applications. Pre-O, the platform - // treats negative resource IDs [those with a package ID of 0x80 or higher] as invalid. - // In order to work around this limitation, we allow the use of traditionally reserved - // resource IDs [those between 0x02 and 0x7E]. - bool allow_reserved_package_id = false; - - // Whether we should fail on definitions of a resource with conflicting visibility. - bool strict_visibility = false; -}; - class LinkContext : public IAaptContext { public: LinkContext(IDiagnostics* diagnostics) @@ -785,9 +718,9 @@ static int32_t FindFrameworkAssetManagerCookie(const android::AssetManager& asse return table.getTableCookie(idx); } -class LinkCommand { +class Linker { public: - LinkCommand(LinkContext* context, const LinkOptions& options) + Linker(LinkContext* context, const LinkOptions& options) : options_(options), context_(context), final_table_(), @@ -2040,197 +1973,12 @@ class LinkCommand { Maybe<std::string> included_feature_base_; }; -int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { - LinkContext context(diagnostics); - LinkOptions options; - std::vector<std::string> overlay_arg_list; - std::vector<std::string> extra_java_packages; - Maybe<std::string> package_id; - std::vector<std::string> configs; - Maybe<std::string> preferred_density; - Maybe<std::string> product_list; - bool legacy_x_flag = false; - bool require_localization = false; - bool verbose = false; - bool shared_lib = false; - bool static_lib = false; - bool proto_format = false; - Maybe<std::string> stable_id_file_path; - std::vector<std::string> split_args; - Flags flags = - Flags() - .RequiredFlag("-o", "Output path.", &options.output_path) - .RequiredFlag("--manifest", "Path to the Android manifest to build.", - &options.manifest_path) - .OptionalFlagList("-I", "Adds an Android APK to link against.", &options.include_paths) - .OptionalFlagList("-A", - "An assets directory to include in the APK. These are unprocessed.", - &options.assets_dirs) - .OptionalFlagList("-R", - "Compilation unit to link, using `overlay` semantics.\n" - "The last conflicting resource given takes precedence.", - &overlay_arg_list) - .OptionalFlag("--package-id", - "Specify the package ID to use for this app. Must be greater or equal to\n" - "0x7f and can't be used with --static-lib or --shared-lib.", - &package_id) - .OptionalFlag("--java", "Directory in which to generate R.java.", - &options.generate_java_class_path) - .OptionalFlag("--proguard", "Output file for generated Proguard rules.", - &options.generate_proguard_rules_path) - .OptionalFlag("--proguard-main-dex", - "Output file for generated Proguard rules for the main dex.", - &options.generate_main_dex_proguard_rules_path) - .OptionalSwitch("--proguard-conditional-keep-rules", - "Generate conditional Proguard keep rules.", - &options.generate_conditional_proguard_rules) - .OptionalSwitch("--no-auto-version", - "Disables automatic style and layout SDK versioning.", - &options.no_auto_version) - .OptionalSwitch("--no-version-vectors", - "Disables automatic versioning of vector drawables. Use this only\n" - "when building with vector drawable support library.", - &options.no_version_vectors) - .OptionalSwitch("--no-version-transitions", - "Disables automatic versioning of transition resources. Use this only\n" - "when building with transition support library.", - &options.no_version_transitions) - .OptionalSwitch("--no-resource-deduping", - "Disables automatic deduping of resources with\n" - "identical values across compatible configurations.", - &options.no_resource_deduping) - .OptionalSwitch("--enable-sparse-encoding", - "Enables encoding sparse entries using a binary search tree.\n" - "This decreases APK size at the cost of resource retrieval performance.", - &options.table_flattener_options.use_sparse_entries) - .OptionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01.", - &legacy_x_flag) - .OptionalSwitch("-z", "Require localization of strings marked 'suggested'.", - &require_localization) - .OptionalFlagList("-c", - "Comma separated list of configurations to include. The default\n" - "is all configurations.", - &configs) - .OptionalFlag("--preferred-density", - "Selects the closest matching density and strips out all others.", - &preferred_density) - .OptionalFlag("--product", "Comma separated list of product names to keep", &product_list) - .OptionalSwitch("--output-to-dir", - "Outputs the APK contents to a directory specified by -o.", - &options.output_to_directory) - .OptionalSwitch("--no-xml-namespaces", - "Removes XML namespace prefix and URI information from\n" - "AndroidManifest.xml and XML binaries in res/*.", - &options.no_xml_namespaces) - .OptionalFlag("--min-sdk-version", - "Default minimum SDK version to use for AndroidManifest.xml.", - &options.manifest_fixer_options.min_sdk_version_default) - .OptionalFlag("--target-sdk-version", - "Default target SDK version to use for AndroidManifest.xml.", - &options.manifest_fixer_options.target_sdk_version_default) - .OptionalFlag("--version-code", - "Version code (integer) to inject into the AndroidManifest.xml if none is\n" - "present.", - &options.manifest_fixer_options.version_code_default) - .OptionalFlag("--version-name", - "Version name to inject into the AndroidManifest.xml if none is present.", - &options.manifest_fixer_options.version_name_default) - .OptionalSwitch("--replace-version", - "If --version-code and/or --version-name are specified, these\n" - "values will replace any value already in the manifest. By\n" - "default, nothing is changed if the manifest already defines\n" - "these attributes.", - &options.manifest_fixer_options.replace_version) - .OptionalFlag("--compile-sdk-version-code", - "Version code (integer) to inject into the AndroidManifest.xml if none is\n" - "present.", - &options.manifest_fixer_options.compile_sdk_version) - .OptionalFlag("--compile-sdk-version-name", - "Version name to inject into the AndroidManifest.xml if none is present.", - &options.manifest_fixer_options.compile_sdk_version_codename) - .OptionalSwitch("--shared-lib", "Generates a shared Android runtime library.", - &shared_lib) - .OptionalSwitch("--static-lib", "Generate a static Android library.", &static_lib) - .OptionalSwitch("--proto-format", - "Generates compiled resources in Protobuf format.\n" - "Suitable as input to the bundle tool for generating an App Bundle.", - &proto_format) - .OptionalSwitch("--no-static-lib-packages", - "Merge all library resources under the app's package.", - &options.no_static_lib_packages) - .OptionalSwitch("--auto-namespace-static-lib", - "Automatically namespace resource references when building a static\n" - "library.", - &options.auto_namespace_static_lib) - .OptionalSwitch("--non-final-ids", - "Generates R.java without the final modifier. This is implied when\n" - "--static-lib is specified.", - &options.generate_non_final_ids) - .OptionalFlag("--stable-ids", "File containing a list of name to ID mapping.", - &stable_id_file_path) - .OptionalFlag("--emit-ids", - "Emit a file at the given path with a list of name to ID mappings,\n" - "suitable for use with --stable-ids.", - &options.resource_id_map_path) - .OptionalFlag("--private-symbols", - "Package name to use when generating R.java for private symbols.\n" - "If not specified, public and private symbols will use the application's\n" - "package name.", - &options.private_symbols) - .OptionalFlag("--custom-package", "Custom Java package under which to generate R.java.", - &options.custom_java_package) - .OptionalFlagList("--extra-packages", - "Generate the same R.java but with different package names.", - &extra_java_packages) - .OptionalFlagList("--add-javadoc-annotation", - "Adds a JavaDoc annotation to all generated Java classes.", - &options.javadoc_annotations) - .OptionalFlag("--output-text-symbols", - "Generates a text file containing the resource symbols of the R class in\n" - "the specified folder.", - &options.generate_text_symbols_path) - .OptionalSwitch("--allow-reserved-package-id", - "Allows the use of a reserved package ID. This should on be used for\n" - "packages with a pre-O min-sdk\n", - &options.allow_reserved_package_id) - .OptionalSwitch("--auto-add-overlay", - "Allows the addition of new resources in overlays without\n" - "<add-resource> tags.", - &options.auto_add_overlay) - .OptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml.", - &options.manifest_fixer_options.rename_manifest_package) - .OptionalFlag("--rename-instrumentation-target-package", - "Changes the name of the target package for instrumentation. Most useful\n" - "when used in conjunction with --rename-manifest-package.", - &options.manifest_fixer_options.rename_instrumentation_target_package) - .OptionalFlagList("-0", "File extensions not to compress.", - &options.extensions_to_not_compress) - .OptionalSwitch("--no-compress", "Do not compress any resources.", - &options.do_not_compress_anything) - .OptionalSwitch("--warn-manifest-validation", - "Treat manifest validation errors as warnings.", - &options.manifest_fixer_options.warn_validation) - .OptionalFlagList("--split", - "Split resources matching a set of configs out to a Split APK.\n" - "Syntax: path/to/output.apk:<config>[,<config>[...]].\n" - "On Windows, use a semicolon ';' separator instead.", - &split_args) - .OptionalSwitch("-v", "Enables verbose logging.", &verbose) - .OptionalSwitch("--debug-mode", - "Inserts android:debuggable=\"true\" in to the application node of the\n" - "manifest, making the application debuggable even on production devices.", - &options.manifest_fixer_options.debug_mode) - .OptionalSwitch("--strict-visibility", - "Do not allow overlays with different visibility levels.", - &options.strict_visibility); - - if (!flags.Parse("aapt2 link", args, &std::cerr)) { - return 1; - } +int LinkCommand::Action(const std::vector<std::string>& args) { + LinkContext context(diag_); // Expand all argument-files passed into the command line. These start with '@'. std::vector<std::string> arg_list; - for (const std::string& arg : flags.GetArgs()) { + for (const std::string& arg : args) { if (util::StartsWith(arg, "@")) { const std::string path = arg.substr(1, arg.size() - 1); std::string error; @@ -2244,27 +1992,27 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { } // Expand all argument-files passed to -R. - for (const std::string& arg : overlay_arg_list) { + for (const std::string& arg : overlay_arg_list_) { if (util::StartsWith(arg, "@")) { const std::string path = arg.substr(1, arg.size() - 1); std::string error; - if (!file::AppendArgsFromFile(path, &options.overlay_files, &error)) { + if (!file::AppendArgsFromFile(path, &options_.overlay_files, &error)) { context.GetDiagnostics()->Error(DiagMessage(path) << error); return 1; } } else { - options.overlay_files.push_back(arg); + options_.overlay_files.push_back(arg); } } - if (verbose) { - context.SetVerbose(verbose); + if (verbose_) { + context.SetVerbose(verbose_); } - if (int{shared_lib} + int{static_lib} + int{proto_format} > 1) { + if (int{shared_lib_} + int{static_lib_} + int{proto_format_} > 1) { context.GetDiagnostics()->Error( DiagMessage() - << "only one of --shared-lib, --static-lib, or --proto_format can be defined"); + << "only one of --shared-lib, --static-lib, or --proto_format can be defined"); return 1; } @@ -2272,18 +2020,18 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { context.SetPackageType(PackageType::kApp); context.SetPackageId(kAppPackageId); - if (shared_lib) { + if (shared_lib_) { context.SetPackageType(PackageType::kSharedLib); context.SetPackageId(0x00); - } else if (static_lib) { + } else if (static_lib_) { context.SetPackageType(PackageType::kStaticLib); - options.output_format = OutputFormat::kProto; - } else if (proto_format) { - options.output_format = OutputFormat::kProto; + options_.output_format = OutputFormat::kProto; + } else if (proto_format_) { + options_.output_format = OutputFormat::kProto; } - if (options.auto_namespace_static_lib) { - if (!static_lib) { + if (options_.auto_namespace_static_lib) { + if (!static_lib_) { context.GetDiagnostics()->Error( DiagMessage() << "--auto-namespace-static-lib can only be used with --static-lib"); return 1; @@ -2291,16 +2039,16 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { context.SetAutoNamespace(true); } - if (package_id) { + if (package_id_) { if (context.GetPackageType() != PackageType::kApp) { context.GetDiagnostics()->Error( DiagMessage() << "can't specify --package-id when not building a regular app"); return 1; } - const Maybe<uint32_t> maybe_package_id_int = ResourceUtils::ParseInt(package_id.value()); + const Maybe<uint32_t> maybe_package_id_int = ResourceUtils::ParseInt(package_id_.value()); if (!maybe_package_id_int) { - context.GetDiagnostics()->Error(DiagMessage() << "package ID '" << package_id.value() + context.GetDiagnostics()->Error(DiagMessage() << "package ID '" << package_id_.value() << "' is not a valid integer"); return 1; } @@ -2308,7 +2056,7 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { const uint32_t package_id_int = maybe_package_id_int.value(); if (package_id_int > std::numeric_limits<uint8_t>::max() || package_id_int == kFrameworkPackageId - || (!options.allow_reserved_package_id && package_id_int < kAppPackageId)) { + || (!options_.allow_reserved_package_id && package_id_int < kAppPackageId)) { context.GetDiagnostics()->Error( DiagMessage() << StringPrintf( "invalid package ID 0x%02x. Must be in the range 0x7f-0xff.", package_id_int)); @@ -2318,71 +2066,71 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { } // Populate the set of extra packages for which to generate R.java. - for (std::string& extra_package : extra_java_packages) { + for (std::string& extra_package : extra_java_packages_) { // A given package can actually be a colon separated list of packages. for (StringPiece package : util::Split(extra_package, ':')) { - options.extra_java_packages.insert(package.to_string()); + options_.extra_java_packages.insert(package.to_string()); } } - if (product_list) { - for (StringPiece product : util::Tokenize(product_list.value(), ',')) { + if (product_list_) { + for (StringPiece product : util::Tokenize(product_list_.value(), ',')) { if (product != "" && product != "default") { - options.products.insert(product.to_string()); + options_.products.insert(product.to_string()); } } } std::unique_ptr<IConfigFilter> filter; - if (!configs.empty()) { - filter = ParseConfigFilterParameters(configs, context.GetDiagnostics()); + if (!configs_.empty()) { + filter = ParseConfigFilterParameters(configs_, context.GetDiagnostics()); if (filter == nullptr) { return 1; } - options.table_splitter_options.config_filter = filter.get(); + options_.table_splitter_options.config_filter = filter.get(); } - if (preferred_density) { + if (preferred_density_) { Maybe<uint16_t> density = - ParseTargetDensityParameter(preferred_density.value(), context.GetDiagnostics()); + ParseTargetDensityParameter(preferred_density_.value(), context.GetDiagnostics()); if (!density) { return 1; } - options.table_splitter_options.preferred_densities.push_back(density.value()); + options_.table_splitter_options.preferred_densities.push_back(density.value()); } // Parse the split parameters. - for (const std::string& split_arg : split_args) { - options.split_paths.push_back({}); - options.split_constraints.push_back({}); - if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options.split_paths.back(), - &options.split_constraints.back())) { + for (const std::string& split_arg : split_args_) { + options_.split_paths.push_back({}); + options_.split_constraints.push_back({}); + if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options_.split_paths.back(), + &options_.split_constraints.back())) { return 1; } } - if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path) { - if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path.value(), - &options.stable_id_map)) { + if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path_) { + if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path_.value(), + &options_.stable_id_map)) { return 1; } } // Populate some default no-compress extensions that are already compressed. - options.extensions_to_not_compress.insert( + options_.extensions_to_not_compress.insert( {".jpg", ".jpeg", ".png", ".gif", ".wav", ".mp2", ".mp3", ".ogg", - ".aac", ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", ".rtttl", - ".imy", ".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", - ".3gpp2", ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"}); + ".aac", ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", ".rtttl", + ".imy", ".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", + ".3gpp2", ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"}); // Turn off auto versioning for static-libs. if (context.GetPackageType() == PackageType::kStaticLib) { - options.no_auto_version = true; - options.no_version_vectors = true; - options.no_version_transitions = true; + options_.no_auto_version = true; + options_.no_version_vectors = true; + options_.no_version_transitions = true; } - LinkCommand cmd(&context, options); + Linker cmd(&context, options_); return cmd.Run(arg_list); } diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h new file mode 100644 index 000000000000..434475ee1be5 --- /dev/null +++ b/tools/aapt2/cmd/Link.h @@ -0,0 +1,282 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AAPT2_LINK_H +#define AAPT2_LINK_H + +#include "Command.h" +#include "Diagnostics.h" +#include "Resource.h" +#include "split/TableSplitter.h" +#include "format/binary/TableFlattener.h" +#include "link/ManifestFixer.h" + +namespace aapt { + +enum class OutputFormat { + kApk, + kProto, +}; + +struct LinkOptions { + std::string output_path; + std::string manifest_path; + std::vector<std::string> include_paths; + std::vector<std::string> overlay_files; + std::vector<std::string> assets_dirs; + bool output_to_directory = false; + bool auto_add_overlay = false; + OutputFormat output_format = OutputFormat::kApk; + + // Java/Proguard options. + Maybe<std::string> generate_java_class_path; + Maybe<std::string> custom_java_package; + std::set<std::string> extra_java_packages; + Maybe<std::string> generate_text_symbols_path; + Maybe<std::string> generate_proguard_rules_path; + Maybe<std::string> generate_main_dex_proguard_rules_path; + bool generate_conditional_proguard_rules = false; + bool generate_non_final_ids = false; + std::vector<std::string> javadoc_annotations; + Maybe<std::string> private_symbols; + + // Optimizations/features. + bool no_auto_version = false; + bool no_version_vectors = false; + bool no_version_transitions = false; + bool no_resource_deduping = false; + bool no_xml_namespaces = false; + bool do_not_compress_anything = false; + std::unordered_set<std::string> extensions_to_not_compress; + + // Static lib options. + bool no_static_lib_packages = false; + bool auto_namespace_static_lib = false; + + // AndroidManifest.xml massaging options. + ManifestFixerOptions manifest_fixer_options; + + // Products to use/filter on. + std::unordered_set<std::string> products; + + // Flattening options. + TableFlattenerOptions table_flattener_options; + + // Split APK options. + TableSplitterOptions table_splitter_options; + std::vector<SplitConstraints> split_constraints; + std::vector<std::string> split_paths; + + // Stable ID options. + std::unordered_map<ResourceName, ResourceId> stable_id_map; + Maybe<std::string> resource_id_map_path; + + // When 'true', allow reserved package IDs to be used for applications. Pre-O, the platform + // treats negative resource IDs [those with a package ID of 0x80 or higher] as invalid. + // In order to work around this limitation, we allow the use of traditionally reserved + // resource IDs [those between 0x02 and 0x7E]. + bool allow_reserved_package_id = false; + + // Whether we should fail on definitions of a resource with conflicting visibility. + bool strict_visibility = false; +}; + +class LinkCommand : public Command { + public: + explicit LinkCommand(IDiagnostics* diag) : Command("link", "l"), + diag_(diag) { + SetDescription("Links resources into an apk."); + AddRequiredFlag("-o", "Output path.", &options_.output_path); + AddRequiredFlag("--manifest", "Path to the Android manifest to build.", + &options_.manifest_path); + AddOptionalFlagList("-I", "Adds an Android APK to link against.", &options_.include_paths); + AddOptionalFlagList("-A", "An assets directory to include in the APK. These are unprocessed.", + &options_.assets_dirs); + AddOptionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n" + "The last conflicting resource given takes precedence.", &overlay_arg_list_); + AddOptionalFlag("--package-id", + "Specify the package ID to use for this app. Must be greater or equal to\n" + "0x7f and can't be used with --static-lib or --shared-lib.", &package_id_); + AddOptionalFlag("--java", "Directory in which to generate R.java.", + &options_.generate_java_class_path); + AddOptionalFlag("--proguard", "Output file for generated Proguard rules.", + &options_.generate_proguard_rules_path); + AddOptionalFlag("--proguard-main-dex", + "Output file for generated Proguard rules for the main dex.", + &options_.generate_main_dex_proguard_rules_path); + AddOptionalSwitch("--proguard-conditional-keep-rules", + "Generate conditional Proguard keep rules.", + &options_.generate_conditional_proguard_rules); + AddOptionalSwitch("--no-auto-version", "Disables automatic style and layout SDK versioning.", + &options_.no_auto_version); + AddOptionalSwitch("--no-version-vectors", + "Disables automatic versioning of vector drawables. Use this only\n" + "when building with vector drawable support library.", + &options_.no_version_vectors); + AddOptionalSwitch("--no-version-transitions", + "Disables automatic versioning of transition resources. Use this only\n" + "when building with transition support library.", + &options_.no_version_transitions); + AddOptionalSwitch("--no-resource-deduping", "Disables automatic deduping of resources with\n" + "identical values across compatible configurations.", + &options_.no_resource_deduping); + AddOptionalSwitch("--enable-sparse-encoding", + "This decreases APK size at the cost of resource retrieval performance.", + &options_.table_flattener_options.use_sparse_entries); + AddOptionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01.", + &legacy_x_flag_); + AddOptionalSwitch("-z", "Require localization of strings marked 'suggested'.", + &require_localization_); + AddOptionalFlagList("-c", + "Comma separated list of configurations to include. The default\n" + "is all configurations.", &configs_); + AddOptionalFlag("--preferred-density", + "Selects the closest matching density and strips out all others.", + &preferred_density_); + AddOptionalFlag("--product", "Comma separated list of product names to keep", &product_list_); + AddOptionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified by -o.", + &options_.output_to_directory); + AddOptionalSwitch("--no-xml-namespaces", "Removes XML namespace prefix and URI information\n" + "from AndroidManifest.xml and XML binaries in res/*.", + &options_.no_xml_namespaces); + AddOptionalFlag("--min-sdk-version", + "Default minimum SDK version to use for AndroidManifest.xml.", + &options_.manifest_fixer_options.min_sdk_version_default); + AddOptionalFlag("--target-sdk-version", + "Default target SDK version to use for AndroidManifest.xml.", + &options_.manifest_fixer_options.target_sdk_version_default); + AddOptionalFlag("--version-code", + "Version code (integer) to inject into the AndroidManifest.xml if none is\n" + "present.", + &options_.manifest_fixer_options.version_code_default); + AddOptionalFlag("--version-name", + "Version name to inject into the AndroidManifest.xml if none is present.", + &options_.manifest_fixer_options.version_name_default); + AddOptionalSwitch("--replace-version", + "If --version-code and/or --version-name are specified, these\n" + "values will replace any value already in the manifest. By\n" + "default, nothing is changed if the manifest already defines\n" + "these attributes.", + &options_.manifest_fixer_options.replace_version); + AddOptionalFlag("--compile-sdk-version-code", + "Version code (integer) to inject into the AndroidManifest.xml if none is\n" + "present.", + &options_.manifest_fixer_options.compile_sdk_version); + AddOptionalFlag("--compile-sdk-version-name", + "Version name to inject into the AndroidManifest.xml if none is present.", + &options_.manifest_fixer_options.compile_sdk_version_codename); + AddOptionalSwitch("--shared-lib", "Generates a shared Android runtime library.", + &shared_lib_); + AddOptionalSwitch("--static-lib", "Generate a static Android library.", &static_lib_); + AddOptionalSwitch("--proto-format", + "Generates compiled resources in Protobuf format.\n" + "Suitable as input to the bundle tool for generating an App Bundle.", + &proto_format_); + AddOptionalSwitch("--no-static-lib-packages", + "Merge all library resources under the app's package.", + &options_.no_static_lib_packages); + AddOptionalSwitch("--auto-namespace-static-lib", + "Automatically namespace resource references when building a static\n" + "library.", + &options_.auto_namespace_static_lib); + AddOptionalSwitch("--non-final-ids", + "Generates R.java without the final modifier. This is implied when\n" + "--static-lib is specified.", + &options_.generate_non_final_ids); + AddOptionalFlag("--stable-ids", "File containing a list of name to ID mapping.", + &stable_id_file_path_); + AddOptionalFlag("--emit-ids", + "Emit a file at the given path with a list of name to ID mappings,\n" + "suitable for use with --stable-ids.", + &options_.resource_id_map_path); + AddOptionalFlag("--private-symbols", + "Package name to use when generating R.java for private symbols.\n" + "If not specified, public and private symbols will use the application's\n" + "package name.", + &options_.private_symbols); + AddOptionalFlag("--custom-package", "Custom Java package under which to generate R.java.", + &options_.custom_java_package); + AddOptionalFlagList("--extra-packages", + "Generate the same R.java but with different package names.", + &extra_java_packages_); + AddOptionalFlagList("--add-javadoc-annotation", + "Adds a JavaDoc annotation to all generated Java classes.", + &options_.javadoc_annotations); + AddOptionalFlag("--output-text-symbols", + "Generates a text file containing the resource symbols of the R class in\n" + "the specified folder.", + &options_.generate_text_symbols_path); + AddOptionalSwitch("--allow-reserved-package-id", + "Allows the use of a reserved package ID. This should on be used for\n" + "packages with a pre-O min-sdk\n", + &options_.allow_reserved_package_id); + AddOptionalSwitch("--auto-add-overlay", + "Allows the addition of new resources in overlays without\n" + "<add-resource> tags.", + &options_.auto_add_overlay); + AddOptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml.", + &options_.manifest_fixer_options.rename_manifest_package); + AddOptionalFlag("--rename-instrumentation-target-package", + "Changes the name of the target package for instrumentation. Most useful\n" + "when used in conjunction with --rename-manifest-package.", + &options_.manifest_fixer_options.rename_instrumentation_target_package); + AddOptionalFlagList("-0", "File extensions not to compress.", + &options_.extensions_to_not_compress); + AddOptionalSwitch("--no-compress", "Do not compress any resources.", + &options_.do_not_compress_anything); + AddOptionalSwitch("--warn-manifest-validation", + "Treat manifest validation errors as warnings.", + &options_.manifest_fixer_options.warn_validation); + AddOptionalFlagList("--split", + "Split resources matching a set of configs out to a Split APK.\n" + "Syntax: path/to/output.apk:<config>[,<config>[...]].\n" + "On Windows, use a semicolon ';' separator instead.", + &split_args_); + AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_); + AddOptionalSwitch("--debug-mode", + "Inserts android:debuggable=\"true\" in to the application node of the\n" + "manifest, making the application debuggable even on production devices.", + &options_.manifest_fixer_options.debug_mode); + AddOptionalSwitch("--strict-visibility", + "Do not allow overlays with different visibility levels.", + &options_.strict_visibility); + } + + int Action(const std::vector<std::string>& args) override; + + private: + IDiagnostics* diag_; + LinkOptions options_; + + std::vector<std::string> overlay_arg_list_; + std::vector<std::string> extra_java_packages_; + Maybe<std::string> package_id_; + std::vector<std::string> configs_; + Maybe<std::string> preferred_density_; + Maybe<std::string> product_list_; + bool legacy_x_flag_ = false; + bool require_localization_ = false; + bool verbose_ = false; + bool shared_lib_ = false; + bool static_lib_ = false; + bool proto_format_ = false; + Maybe<std::string> stable_id_file_path_; + std::vector<std::string> split_args_; +}; + +}// namespace aapt + +#endif //AAPT2_LINK_H
\ No newline at end of file diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp index 4afa8f0a907d..b4cba8c2801c 100644 --- a/tools/aapt2/cmd/Optimize.cpp +++ b/tools/aapt2/cmd/Optimize.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "Optimize.h" + #include <memory> #include <vector> @@ -24,7 +26,6 @@ #include "androidfw/StringPiece.h" #include "Diagnostics.h" -#include "Flags.h" #include "LoadedApk.h" #include "ResourceUtils.h" #include "SdkConstants.h" @@ -54,36 +55,6 @@ using ::android::base::StringPrintf; namespace aapt { -struct OptimizeOptions { - // Path to the output APK. - Maybe<std::string> output_path; - // Path to the output APK directory for splits. - Maybe<std::string> output_dir; - - // Details of the app extracted from the AndroidManifest.xml - AppInfo app_info; - - // Blacklist of unused resources that should be removed from the apk. - std::unordered_set<ResourceName> resources_blacklist; - - // Split APK options. - TableSplitterOptions table_splitter_options; - - // List of output split paths. These are in the same order as `split_constraints`. - std::vector<std::string> split_paths; - - // List of SplitConstraints governing what resources go into each split. Ordered by `split_paths`. - std::vector<SplitConstraints> split_constraints; - - TableFlattenerOptions table_flattener_options; - - Maybe<std::vector<OutputArtifact>> apk_artifacts; - - // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts - // are kept and will be written as output. - std::unordered_set<std::string> kept_artifacts; -}; - class OptimizeContext : public IAaptContext { public: OptimizeContext() = default; @@ -145,9 +116,9 @@ class OptimizeContext : public IAaptContext { int sdk_version_ = 0; }; -class OptimizeCommand { +class Optimizer { public: - OptimizeCommand(OptimizeContext* context, const OptimizeOptions& options) + Optimizer(OptimizeContext* context, const OptimizeOptions& options) : options_(options), context_(context) { } @@ -378,82 +349,24 @@ bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk, return true; } -int Optimize(const std::vector<StringPiece>& args) { - OptimizeContext context; - OptimizeOptions options; - Maybe<std::string> config_path; - Maybe<std::string> whitelist_path; - Maybe<std::string> resources_config_path; - Maybe<std::string> target_densities; - std::vector<std::string> configs; - std::vector<std::string> split_args; - std::unordered_set<std::string> kept_artifacts; - bool verbose = false; - bool print_only = false; - Flags flags = - Flags() - .OptionalFlag("-o", "Path to the output APK.", &options.output_path) - .OptionalFlag("-d", "Path to the output directory (for splits).", &options.output_dir) - .OptionalFlag("-x", "Path to XML configuration file.", &config_path) - .OptionalSwitch("-p", "Print the multi APK artifacts and exit.", &print_only) - .OptionalFlag( - "--target-densities", - "Comma separated list of the screen densities that the APK will be optimized for.\n" - "All the resources that would be unused on devices of the given densities will be \n" - "removed from the APK.", - &target_densities) - .OptionalFlag("--whitelist-path", - "Path to the whitelist.cfg file containing whitelisted resources \n" - "whose names should not be altered in final resource tables.", - &whitelist_path) - .OptionalFlag("--resources-config-path", - "Path to the resources.cfg file containing the list of resources and \n" - "directives to each resource. \n" - "Format: type/resource_name#[directive][,directive]", - &resources_config_path) - .OptionalFlagList("-c", - "Comma separated list of configurations to include. The default\n" - "is all configurations.", - &configs) - .OptionalFlagList("--split", - "Split resources matching a set of configs out to a " - "Split APK.\nSyntax: path/to/output.apk;<config>[,<config>[...]].\n" - "On Windows, use a semicolon ';' separator instead.", - &split_args) - .OptionalFlagList("--keep-artifacts", - "Comma separated list of artifacts to keep. If none are specified,\n" - "all artifacts will be kept.", - &kept_artifacts) - .OptionalSwitch("--enable-sparse-encoding", - "Enables encoding sparse entries using a binary search tree.\n" - "This decreases APK size at the cost of resource retrieval performance.", - &options.table_flattener_options.use_sparse_entries) - .OptionalSwitch("--enable-resource-obfuscation", - "Enables obfuscation of key string pool to single value", - &options.table_flattener_options.collapse_key_stringpool) - .OptionalSwitch("-v", "Enables verbose logging", &verbose); - - if (!flags.Parse("aapt2 optimize", args, &std::cerr)) { - return 1; - } - - if (flags.GetArgs().size() != 1u) { +int OptimizeCommand::Action(const std::vector<std::string>& args) { + if (args.size() != 1u) { std::cerr << "must have one APK as argument.\n\n"; - flags.Usage("aapt2 optimize", &std::cerr); + Usage(&std::cerr); return 1; } - const std::string& apk_path = flags.GetArgs()[0]; - - context.SetVerbose(verbose); + const std::string& apk_path = args[0]; + OptimizeContext context; + context.SetVerbose(verbose_); IDiagnostics* diag = context.GetDiagnostics(); - if (config_path) { - std::string& path = config_path.value(); + if (config_path_) { + std::string& path = config_path_.value(); Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path); if (for_path) { - options.apk_artifacts = for_path.value().WithDiagnostics(diag).Parse(apk_path); - if (!options.apk_artifacts) { + options_.apk_artifacts = for_path.value().WithDiagnostics(diag).Parse(apk_path); + if (!options_.apk_artifacts) { diag->Error(DiagMessage() << "Failed to parse the output artifact list"); return 1; } @@ -463,28 +376,28 @@ int Optimize(const std::vector<StringPiece>& args) { return 1; } - if (print_only) { - for (const OutputArtifact& artifact : options.apk_artifacts.value()) { + if (print_only_) { + for (const OutputArtifact& artifact : options_.apk_artifacts.value()) { std::cout << artifact.name << std::endl; } return 0; } - if (!kept_artifacts.empty()) { - for (const std::string& artifact_str : kept_artifacts) { + if (!kept_artifacts_.empty()) { + for (const std::string& artifact_str : kept_artifacts_) { for (const StringPiece& artifact : util::Tokenize(artifact_str, ',')) { - options.kept_artifacts.insert(artifact.to_string()); + options_.kept_artifacts.insert(artifact.to_string()); } } } // Since we know that we are going to process the APK (not just print targets), make sure we // have somewhere to write them to. - if (!options.output_dir) { + if (!options_.output_dir) { diag->Error(DiagMessage() << "Output directory is required when using a configuration file"); return 1; } - } else if (print_only) { + } else if (print_only_) { diag->Error(DiagMessage() << "Asked to print artifacts without providing a configurations"); return 1; } @@ -494,57 +407,57 @@ int Optimize(const std::vector<StringPiece>& args) { return 1; } - if (target_densities) { + if (target_densities_) { // Parse the target screen densities. - for (const StringPiece& config_str : util::Tokenize(target_densities.value(), ',')) { + for (const StringPiece& config_str : util::Tokenize(target_densities_.value(), ',')) { Maybe<uint16_t> target_density = ParseTargetDensityParameter(config_str, diag); if (!target_density) { return 1; } - options.table_splitter_options.preferred_densities.push_back(target_density.value()); + options_.table_splitter_options.preferred_densities.push_back(target_density.value()); } } std::unique_ptr<IConfigFilter> filter; - if (!configs.empty()) { - filter = ParseConfigFilterParameters(configs, diag); + if (!configs_.empty()) { + filter = ParseConfigFilterParameters(configs_, diag); if (filter == nullptr) { return 1; } - options.table_splitter_options.config_filter = filter.get(); + options_.table_splitter_options.config_filter = filter.get(); } // Parse the split parameters. - for (const std::string& split_arg : split_args) { - options.split_paths.emplace_back(); - options.split_constraints.emplace_back(); - if (!ParseSplitParameter(split_arg, diag, &options.split_paths.back(), - &options.split_constraints.back())) { + for (const std::string& split_arg : split_args_) { + options_.split_paths.emplace_back(); + options_.split_constraints.emplace_back(); + if (!ParseSplitParameter(split_arg, diag, &options_.split_paths.back(), + &options_.split_constraints.back())) { return 1; } } - if (options.table_flattener_options.collapse_key_stringpool) { - if (whitelist_path) { - std::string& path = whitelist_path.value(); - if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options)) { + if (options_.table_flattener_options.collapse_key_stringpool) { + if (whitelist_path_) { + std::string& path = whitelist_path_.value(); + if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options_)) { return 1; } } } - if (resources_config_path) { - std::string& path = resources_config_path.value(); - if (!ExtractConfig(path, &context, &options)) { + if (resources_config_path_) { + std::string& path = resources_config_path_.value(); + if (!ExtractConfig(path, &context, &options_)) { return 1; } } - if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) { + if (!ExtractAppDataFromManifest(&context, apk.get(), &options_)) { return 1; } - OptimizeCommand cmd(&context, options); + Optimizer cmd(&context, options_); return cmd.Run(std::move(apk)); } diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h new file mode 100644 index 000000000000..43bc216382fa --- /dev/null +++ b/tools/aapt2/cmd/Optimize.h @@ -0,0 +1,124 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AAPT2_OPTIMIZE_H +#define AAPT2_OPTIMIZE_H + +#include "AppInfo.h" +#include "Command.h" +#include "configuration/ConfigurationParser.h" +#include "format/binary/TableFlattener.h" +#include "split/TableSplitter.h" + +namespace aapt { + +struct OptimizeOptions { + friend class OptimizeCommand; + + // Path to the output APK. + Maybe<std::string> output_path; + // Path to the output APK directory for splits. + Maybe<std::string> output_dir; + + // Details of the app extracted from the AndroidManifest.xml + AppInfo app_info; + + // Blacklist of unused resources that should be removed from the apk. + std::unordered_set<ResourceName> resources_blacklist; + + // Split APK options. + TableSplitterOptions table_splitter_options; + + // List of output split paths. These are in the same order as `split_constraints`. + std::vector<std::string> split_paths; + + // List of SplitConstraints governing what resources go into each split. Ordered by `split_paths`. + std::vector<SplitConstraints> split_constraints; + + TableFlattenerOptions table_flattener_options; + + Maybe<std::vector<aapt::configuration::OutputArtifact>> apk_artifacts; + + // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts + // are kept and will be written as output. + std::unordered_set<std::string> kept_artifacts; +}; + +class OptimizeCommand : public Command { + public: + explicit OptimizeCommand() : Command("optimize") { + SetDescription("Preforms resource optimizations on an apk."); + AddOptionalFlag("-o", "Path to the output APK.", &options_.output_path); + AddOptionalFlag("-d", "Path to the output directory (for splits).", &options_.output_dir); + AddOptionalFlag("-x", "Path to XML configuration file.", &config_path_); + AddOptionalSwitch("-p", "Print the multi APK artifacts and exit.", &print_only_); + AddOptionalFlag( + "--target-densities", + "Comma separated list of the screen densities that the APK will be optimized for.\n" + "All the resources that would be unused on devices of the given densities will be \n" + "removed from the APK.", + &target_densities_); + AddOptionalFlag("--whitelist-path", + "Path to the whitelist.cfg file containing whitelisted resources \n" + "whose names should not be altered in final resource tables.", + &whitelist_path_); + AddOptionalFlag("--resources-config-path", + "Path to the resources.cfg file containing the list of resources and \n" + "directives to each resource. \n" + "Format: type/resource_name#[directive][,directive]", + &resources_config_path_); + AddOptionalFlagList("-c", + "Comma separated list of configurations to include. The default\n" + "is all configurations.", + &configs_); + AddOptionalFlagList("--split", + "Split resources matching a set of configs out to a " + "Split APK.\nSyntax: path/to/output.apk;<config>[,<config>[...]].\n" + "On Windows, use a semicolon ';' separator instead.", + &split_args_); + AddOptionalFlagList("--keep-artifacts", + "Comma separated list of artifacts to keep. If none are specified,\n" + "all artifacts will be kept.", + &kept_artifacts_); + AddOptionalSwitch("--enable-sparse-encoding", + "Enables encoding sparse entries using a binary search tree.\n" + "This decreases APK size at the cost of resource retrieval performance.", + &options_.table_flattener_options.use_sparse_entries); + AddOptionalSwitch("--enable-resource-obfuscation", + "Enables obfuscation of key string pool to single value", + &options_.table_flattener_options.collapse_key_stringpool); + AddOptionalSwitch("-v", "Enables verbose logging", &verbose_); + } + + int Action(const std::vector<std::string>& args) override; + + private: + OptimizeOptions options_; + + Maybe<std::string> config_path_; + Maybe<std::string> whitelist_path_; + Maybe<std::string> resources_config_path_; + Maybe<std::string> target_densities_; + std::vector<std::string> configs_; + std::vector<std::string> split_args_; + std::unordered_set<std::string> kept_artifacts_; + bool print_only_ = false; + bool verbose_ = false; +}; + +}// namespace aapt + +#endif //AAPT2_OPTIMIZE_H
\ No newline at end of file diff --git a/tools/aapt2/jni/aapt2_jni.cpp b/tools/aapt2/jni/aapt2_jni.cpp index ad5ad4c336e5..ba9646f9aeb4 100644 --- a/tools/aapt2/jni/aapt2_jni.cpp +++ b/tools/aapt2/jni/aapt2_jni.cpp @@ -25,15 +25,12 @@ #include "ScopedUtfChars.h" #include "Diagnostics.h" +#include "cmd/Compile.h" +#include "cmd/Link.h" #include "util/Util.h" using android::StringPiece; -namespace aapt { -extern int Compile(const std::vector<StringPiece>& args, IDiagnostics* iDiagnostics); -extern int Link(const std::vector<StringPiece>& args, IDiagnostics* iDiagnostics); -} - /* * Converts a java List<String> into C++ vector<ScopedUtfChars>. */ @@ -126,7 +123,7 @@ JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeCompile( list_to_utfchars(env, arguments_obj); std::vector<StringPiece> compile_args = extract_pieces(compile_args_jni); JniDiagnostics diagnostics(env, diagnostics_obj); - return aapt::Compile(compile_args, &diagnostics); + return aapt::CompileCommand(&diagnostics).Execute(compile_args, &std::cerr); } JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeLink(JNIEnv* env, @@ -137,7 +134,7 @@ JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeLink(JNIEnv* list_to_utfchars(env, arguments_obj); std::vector<StringPiece> link_args = extract_pieces(link_args_jni); JniDiagnostics diagnostics(env, diagnostics_obj); - return aapt::Link(link_args, &diagnostics); + return aapt::LinkCommand(&diagnostics).Execute(link_args, &std::cerr); } JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2Jni_ping( |