Loading tools/aapt2/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -85,6 +85,7 @@ cc_library_host_static { "compile/Pseudolocalizer.cpp", "compile/XmlIdCollector.cpp", "configuration/ConfigurationParser.cpp", "filter/AbiFilter.cpp", "filter/ConfigFilter.cpp", "flatten/Archive.cpp", "flatten/TableFlattener.cpp", Loading tools/aapt2/LoadedApk.cpp +13 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,12 @@ std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context, bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options, IArchiveWriter* writer) { FilterChain empty; return WriteToArchive(context, options, &empty, writer); } bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options, FilterChain* filters, IArchiveWriter* writer) { std::set<std::string> referenced_resources; // List the files being referenced in the resource table. for (auto& pkg : table_->packages) { Loading Loading @@ -89,6 +95,13 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOption continue; } if (!filters->Keep(path)) { if (context->IsVerbose()) { context->GetDiagnostics()->Note(DiagMessage() << "Filtered '" << path << "' from APK."); } continue; } // The resource table needs to be re-serialized since it might have changed. if (path == "resources.arsc") { BigBuffer buffer(4096); Loading tools/aapt2/LoadedApk.h +9 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include "androidfw/StringPiece.h" #include "ResourceTable.h" #include "filter/Filter.h" #include "flatten/Archive.h" #include "flatten/TableFlattener.h" #include "io/ZipArchive.h" Loading Loading @@ -49,6 +50,14 @@ class LoadedApk { bool WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options, IArchiveWriter* writer); /** * Writes the APK on disk at the given path, while also removing the resource * files that are not referenced in the resource table. The provided filter * chain is applied to each entry in the APK file. */ bool WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options, FilterChain* filters, IArchiveWriter* writer); static std::unique_ptr<LoadedApk> LoadApkFromPath(IAaptContext* context, const android::StringPiece& path); Loading tools/aapt2/cmd/Optimize.cpp +83 −9 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include <memory> #include <vector> #include "android-base/stringprintf.h" #include "androidfw/StringPiece.h" #include "Diagnostics.h" Loading @@ -26,6 +27,8 @@ #include "SdkConstants.h" #include "ValueVisitor.h" #include "cmd/Util.h" #include "configuration/ConfigurationParser.h" #include "filter/AbiFilter.h" #include "flatten/TableFlattener.h" #include "flatten/XmlFlattener.h" #include "io/BigBufferInputStream.h" Loading @@ -33,14 +36,21 @@ #include "optimize/ResourceDeduper.h" #include "optimize/VersionCollapser.h" #include "split/TableSplitter.h" #include "util/Files.h" using android::StringPiece; using ::aapt::configuration::Abi; using ::aapt::configuration::Artifact; using ::aapt::configuration::Configuration; using ::android::StringPiece; using ::android::base::StringPrintf; namespace aapt { struct OptimizeOptions { // Path to the output APK. std::string output_path; 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; Loading @@ -55,6 +65,9 @@ struct OptimizeOptions { std::vector<SplitConstraints> split_constraints; TableFlattenerOptions table_flattener_options; // TODO: Come up with a better name for the Configuration struct. Maybe<Configuration> configuration; }; class OptimizeContext : public IAaptContext { Loading Loading @@ -175,11 +188,53 @@ class OptimizeCommand { ++split_constraints_iter; } if (options_.configuration && options_.output_dir) { Configuration& config = options_.configuration.value(); // For now, just write out the stripped APK since ABI splitting doesn't modify anything else. for (const Artifact& artifact : config.artifacts) { if (artifact.abi_group) { const std::string& group = artifact.abi_group.value(); auto abi_group = config.abi_groups.find(group); // TODO: Remove validation when configuration parser ensures referential integrity. if (abi_group == config.abi_groups.end()) { context_->GetDiagnostics()->Note( DiagMessage() << "could not find referenced ABI group '" << group << "'"); return 1; } FilterChain filters; filters.AddFilter(AbiFilter::FromAbiList(abi_group->second)); const std::string& path = apk->GetSource().path; const StringPiece ext = file::GetExtension(path); const std::string name = path.substr(0, path.rfind(ext.to_string())); // Name is hard coded for now since only one split dimension is supported. // TODO: Incorporate name generation into the configuration objects. const std::string file_name = StringPrintf("%s.%s%s", name.c_str(), group.c_str(), ext.data()); std::string out = options_.output_dir.value(); file::AppendPath(&out, file_name); std::unique_ptr<IArchiveWriter> writer = CreateZipFileArchiveWriter(context_->GetDiagnostics(), out); if (!apk->WriteToArchive(context_, options_.table_flattener_options, &filters, writer.get())) { return 1; } } } } if (options_.output_path) { std::unique_ptr<IArchiveWriter> writer = CreateZipFileArchiveWriter(context_->GetDiagnostics(), options_.output_path); CreateZipFileArchiveWriter(context_->GetDiagnostics(), options_.output_path.value()); if (!apk->WriteToArchive(context_, options_.table_flattener_options, writer.get())) { return 1; } } return 0; } Loading Loading @@ -293,13 +348,16 @@ bool ExtractAppDataFromManifest(OptimizeContext* context, LoadedApk* apk, int Optimize(const std::vector<StringPiece>& args) { OptimizeContext context; OptimizeOptions options; Maybe<std::string> config_path; Maybe<std::string> target_densities; std::vector<std::string> configs; std::vector<std::string> split_args; bool verbose = false; Flags flags = Flags() .RequiredFlag("-o", "Path to the output APK.", &options.output_path) .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) .OptionalFlag( "--target-densities", "Comma separated list of the screen densities that the APK will be optimized for.\n" Loading Loading @@ -369,6 +427,22 @@ int Optimize(const std::vector<StringPiece>& args) { } } if (config_path) { if (!options.output_dir) { context.GetDiagnostics()->Error( DiagMessage() << "Output directory is required when using a configuration file"); return 1; } std::string& path = config_path.value(); Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path); if (for_path) { options.configuration = for_path.value().WithDiagnostics(context.GetDiagnostics()).Parse(); } else { context.GetDiagnostics()->Error(DiagMessage() << "Could not parse config file " << path); return 1; } } if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) { return 1; } Loading tools/aapt2/configuration/ConfigurationParser.cpp +21 −11 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <algorithm> #include <functional> #include <map> #include <memory> #include <utility> Loading Loading @@ -56,15 +57,15 @@ using ::aapt::xml::XmlActionExecutorPolicy; using ::aapt::xml::XmlNodeAction; using ::android::base::ReadFileToString; const std::unordered_map<std::string, Abi> kAbiMap = { {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a}, {"x86", Abi::kX86}, {"x86_64", Abi::kX86_64}, {"mips", Abi::kMips}, {"mips64", Abi::kMips64}, {"universal", Abi::kUniversal}, const std::unordered_map<std::string, Abi> kStringToAbiMap = { {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a}, {"x86", Abi::kX86}, {"x86_64", Abi::kX86_64}, {"mips", Abi::kMips}, {"mips64", Abi::kMips64}, {"universal", Abi::kUniversal}, }; const std::map<Abi, std::string> kAbiToStringMap = { {Abi::kArmeV6, "armeabi"}, {Abi::kArmV7a, "armeabi-v7a"}, {Abi::kArm64V8a, "arm64-v8a"}, {Abi::kX86, "x86"}, {Abi::kX86_64, "x86_64"}, {Abi::kMips, "mips"}, {Abi::kMips64, "mips64"}, {Abi::kUniversal, "universal"}, }; constexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt"; Loading Loading @@ -102,7 +103,13 @@ class NamespaceVisitor : public xml::Visitor { } // namespace namespace configuration { const std::string& AbiToString(Abi abi) { return kAbiToStringMap.find(abi)->second; } } // namespace configuration /** Returns a ConfigurationParser for the file located at the provided path. */ Maybe<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) { Loading Loading @@ -175,6 +182,9 @@ Maybe<Configuration> ConfigurationParser::Parse() { return {}; } // TODO: Validate all references in the configuration are valid. It should be safe to assume from // this point on that any references from one section to another will be present. return {config}; } Loading @@ -201,7 +211,7 @@ ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ = DiagMessage() << "Unknown artifact attribute: " << attr.name << " = " << attr.value); } } config->artifacts[artifact.name] = artifact; config->artifacts.push_back(artifact); return true; }; Loading Loading @@ -236,7 +246,7 @@ ConfigurationParser::ActionHandler ConfigurationParser::abi_group_handler_ = for (auto& node : child->children) { xml::Text* t; if ((t = NodeCast<xml::Text>(node.get())) != nullptr) { group.push_back(kAbiMap.at(TrimWhitespace(t->text).to_string())); group.push_back(kStringToAbiMap.at(TrimWhitespace(t->text).to_string())); break; } } Loading Loading
tools/aapt2/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -85,6 +85,7 @@ cc_library_host_static { "compile/Pseudolocalizer.cpp", "compile/XmlIdCollector.cpp", "configuration/ConfigurationParser.cpp", "filter/AbiFilter.cpp", "filter/ConfigFilter.cpp", "flatten/Archive.cpp", "flatten/TableFlattener.cpp", Loading
tools/aapt2/LoadedApk.cpp +13 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,12 @@ std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context, bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options, IArchiveWriter* writer) { FilterChain empty; return WriteToArchive(context, options, &empty, writer); } bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options, FilterChain* filters, IArchiveWriter* writer) { std::set<std::string> referenced_resources; // List the files being referenced in the resource table. for (auto& pkg : table_->packages) { Loading Loading @@ -89,6 +95,13 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOption continue; } if (!filters->Keep(path)) { if (context->IsVerbose()) { context->GetDiagnostics()->Note(DiagMessage() << "Filtered '" << path << "' from APK."); } continue; } // The resource table needs to be re-serialized since it might have changed. if (path == "resources.arsc") { BigBuffer buffer(4096); Loading
tools/aapt2/LoadedApk.h +9 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include "androidfw/StringPiece.h" #include "ResourceTable.h" #include "filter/Filter.h" #include "flatten/Archive.h" #include "flatten/TableFlattener.h" #include "io/ZipArchive.h" Loading Loading @@ -49,6 +50,14 @@ class LoadedApk { bool WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options, IArchiveWriter* writer); /** * Writes the APK on disk at the given path, while also removing the resource * files that are not referenced in the resource table. The provided filter * chain is applied to each entry in the APK file. */ bool WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options, FilterChain* filters, IArchiveWriter* writer); static std::unique_ptr<LoadedApk> LoadApkFromPath(IAaptContext* context, const android::StringPiece& path); Loading
tools/aapt2/cmd/Optimize.cpp +83 −9 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include <memory> #include <vector> #include "android-base/stringprintf.h" #include "androidfw/StringPiece.h" #include "Diagnostics.h" Loading @@ -26,6 +27,8 @@ #include "SdkConstants.h" #include "ValueVisitor.h" #include "cmd/Util.h" #include "configuration/ConfigurationParser.h" #include "filter/AbiFilter.h" #include "flatten/TableFlattener.h" #include "flatten/XmlFlattener.h" #include "io/BigBufferInputStream.h" Loading @@ -33,14 +36,21 @@ #include "optimize/ResourceDeduper.h" #include "optimize/VersionCollapser.h" #include "split/TableSplitter.h" #include "util/Files.h" using android::StringPiece; using ::aapt::configuration::Abi; using ::aapt::configuration::Artifact; using ::aapt::configuration::Configuration; using ::android::StringPiece; using ::android::base::StringPrintf; namespace aapt { struct OptimizeOptions { // Path to the output APK. std::string output_path; 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; Loading @@ -55,6 +65,9 @@ struct OptimizeOptions { std::vector<SplitConstraints> split_constraints; TableFlattenerOptions table_flattener_options; // TODO: Come up with a better name for the Configuration struct. Maybe<Configuration> configuration; }; class OptimizeContext : public IAaptContext { Loading Loading @@ -175,11 +188,53 @@ class OptimizeCommand { ++split_constraints_iter; } if (options_.configuration && options_.output_dir) { Configuration& config = options_.configuration.value(); // For now, just write out the stripped APK since ABI splitting doesn't modify anything else. for (const Artifact& artifact : config.artifacts) { if (artifact.abi_group) { const std::string& group = artifact.abi_group.value(); auto abi_group = config.abi_groups.find(group); // TODO: Remove validation when configuration parser ensures referential integrity. if (abi_group == config.abi_groups.end()) { context_->GetDiagnostics()->Note( DiagMessage() << "could not find referenced ABI group '" << group << "'"); return 1; } FilterChain filters; filters.AddFilter(AbiFilter::FromAbiList(abi_group->second)); const std::string& path = apk->GetSource().path; const StringPiece ext = file::GetExtension(path); const std::string name = path.substr(0, path.rfind(ext.to_string())); // Name is hard coded for now since only one split dimension is supported. // TODO: Incorporate name generation into the configuration objects. const std::string file_name = StringPrintf("%s.%s%s", name.c_str(), group.c_str(), ext.data()); std::string out = options_.output_dir.value(); file::AppendPath(&out, file_name); std::unique_ptr<IArchiveWriter> writer = CreateZipFileArchiveWriter(context_->GetDiagnostics(), out); if (!apk->WriteToArchive(context_, options_.table_flattener_options, &filters, writer.get())) { return 1; } } } } if (options_.output_path) { std::unique_ptr<IArchiveWriter> writer = CreateZipFileArchiveWriter(context_->GetDiagnostics(), options_.output_path); CreateZipFileArchiveWriter(context_->GetDiagnostics(), options_.output_path.value()); if (!apk->WriteToArchive(context_, options_.table_flattener_options, writer.get())) { return 1; } } return 0; } Loading Loading @@ -293,13 +348,16 @@ bool ExtractAppDataFromManifest(OptimizeContext* context, LoadedApk* apk, int Optimize(const std::vector<StringPiece>& args) { OptimizeContext context; OptimizeOptions options; Maybe<std::string> config_path; Maybe<std::string> target_densities; std::vector<std::string> configs; std::vector<std::string> split_args; bool verbose = false; Flags flags = Flags() .RequiredFlag("-o", "Path to the output APK.", &options.output_path) .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) .OptionalFlag( "--target-densities", "Comma separated list of the screen densities that the APK will be optimized for.\n" Loading Loading @@ -369,6 +427,22 @@ int Optimize(const std::vector<StringPiece>& args) { } } if (config_path) { if (!options.output_dir) { context.GetDiagnostics()->Error( DiagMessage() << "Output directory is required when using a configuration file"); return 1; } std::string& path = config_path.value(); Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path); if (for_path) { options.configuration = for_path.value().WithDiagnostics(context.GetDiagnostics()).Parse(); } else { context.GetDiagnostics()->Error(DiagMessage() << "Could not parse config file " << path); return 1; } } if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) { return 1; } Loading
tools/aapt2/configuration/ConfigurationParser.cpp +21 −11 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <algorithm> #include <functional> #include <map> #include <memory> #include <utility> Loading Loading @@ -56,15 +57,15 @@ using ::aapt::xml::XmlActionExecutorPolicy; using ::aapt::xml::XmlNodeAction; using ::android::base::ReadFileToString; const std::unordered_map<std::string, Abi> kAbiMap = { {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a}, {"x86", Abi::kX86}, {"x86_64", Abi::kX86_64}, {"mips", Abi::kMips}, {"mips64", Abi::kMips64}, {"universal", Abi::kUniversal}, const std::unordered_map<std::string, Abi> kStringToAbiMap = { {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a}, {"x86", Abi::kX86}, {"x86_64", Abi::kX86_64}, {"mips", Abi::kMips}, {"mips64", Abi::kMips64}, {"universal", Abi::kUniversal}, }; const std::map<Abi, std::string> kAbiToStringMap = { {Abi::kArmeV6, "armeabi"}, {Abi::kArmV7a, "armeabi-v7a"}, {Abi::kArm64V8a, "arm64-v8a"}, {Abi::kX86, "x86"}, {Abi::kX86_64, "x86_64"}, {Abi::kMips, "mips"}, {Abi::kMips64, "mips64"}, {Abi::kUniversal, "universal"}, }; constexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt"; Loading Loading @@ -102,7 +103,13 @@ class NamespaceVisitor : public xml::Visitor { } // namespace namespace configuration { const std::string& AbiToString(Abi abi) { return kAbiToStringMap.find(abi)->second; } } // namespace configuration /** Returns a ConfigurationParser for the file located at the provided path. */ Maybe<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) { Loading Loading @@ -175,6 +182,9 @@ Maybe<Configuration> ConfigurationParser::Parse() { return {}; } // TODO: Validate all references in the configuration are valid. It should be safe to assume from // this point on that any references from one section to another will be present. return {config}; } Loading @@ -201,7 +211,7 @@ ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ = DiagMessage() << "Unknown artifact attribute: " << attr.name << " = " << attr.value); } } config->artifacts[artifact.name] = artifact; config->artifacts.push_back(artifact); return true; }; Loading Loading @@ -236,7 +246,7 @@ ConfigurationParser::ActionHandler ConfigurationParser::abi_group_handler_ = for (auto& node : child->children) { xml::Text* t; if ((t = NodeCast<xml::Text>(node.get())) != nullptr) { group.push_back(kAbiMap.at(TrimWhitespace(t->text).to_string())); group.push_back(kStringToAbiMap.at(TrimWhitespace(t->text).to_string())); break; } } Loading