diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp index 655268bc73f9f..eabeb47fccec4 100644 --- a/tools/aapt2/configuration/ConfigurationParser.cpp +++ b/tools/aapt2/configuration/ConfigurationParser.cpp @@ -49,13 +49,15 @@ using ::aapt::configuration::AndroidSdk; using ::aapt::configuration::ConfiguredArtifact; using ::aapt::configuration::DeviceFeature; using ::aapt::configuration::Entry; +using ::aapt::configuration::ExtractConfiguration; using ::aapt::configuration::GlTexture; using ::aapt::configuration::Group; using ::aapt::configuration::Locale; +using ::aapt::configuration::OrderedEntry; using ::aapt::configuration::OutputArtifact; using ::aapt::configuration::PostProcessingConfiguration; using ::aapt::configuration::handler::AbiGroupTagHandler; -using ::aapt::configuration::handler::AndroidSdkGroupTagHandler; +using ::aapt::configuration::handler::AndroidSdkTagHandler; using ::aapt::configuration::handler::ArtifactFormatTagHandler; using ::aapt::configuration::handler::ArtifactTagHandler; using ::aapt::configuration::handler::DeviceFeatureGroupTagHandler; @@ -130,7 +132,7 @@ bool CopyXmlReferences(const Maybe& name, const Group& groups, return false; } - for (const T& item : group->second) { + for (const T& item : group->second.entry) { target->push_back(item); } return true; @@ -188,61 +190,6 @@ xml::XmlNodeAction::ActionFuncWithDiag Bind(configuration::PostProcessingConfigu }; } -/** Returns the binary reprasentation of the XML configuration. */ -Maybe ExtractConfiguration(const std::string& contents, - IDiagnostics* diag) { - StringInputStream in(contents); - std::unique_ptr doc = xml::Inflate(&in, diag, Source("config.xml")); - if (!doc) { - return {}; - } - - // Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace. - Element* root = doc->root.get(); - if (root == nullptr) { - diag->Error(DiagMessage() << "Could not find the root element in the XML document"); - return {}; - } - - std::string& xml_ns = root->namespace_uri; - if (!xml_ns.empty()) { - if (xml_ns != kAaptXmlNs) { - diag->Error(DiagMessage() << "Unknown namespace found on root element: " << xml_ns); - return {}; - } - - xml_ns.clear(); - NamespaceVisitor visitor; - root->Accept(&visitor); - } - - XmlActionExecutor executor; - XmlNodeAction& root_action = executor["post-process"]; - XmlNodeAction& artifacts_action = root_action["artifacts"]; - XmlNodeAction& groups_action = root_action["groups"]; - - PostProcessingConfiguration config; - - // Parse the artifact elements. - artifacts_action["artifact"].Action(Bind(&config, ArtifactTagHandler)); - artifacts_action["artifact-format"].Action(Bind(&config, ArtifactFormatTagHandler)); - - // Parse the different configuration groups. - groups_action["abi-group"].Action(Bind(&config, AbiGroupTagHandler)); - groups_action["screen-density-group"].Action(Bind(&config, ScreenDensityGroupTagHandler)); - groups_action["locale-group"].Action(Bind(&config, LocaleGroupTagHandler)); - groups_action["android-sdk-group"].Action(Bind(&config, AndroidSdkGroupTagHandler)); - groups_action["gl-texture-group"].Action(Bind(&config, GlTextureGroupTagHandler)); - groups_action["device-feature-group"].Action(Bind(&config, DeviceFeatureGroupTagHandler)); - - if (!executor.Execute(XmlActionExecutorPolicy::kNone, diag, doc.get())) { - diag->Error(DiagMessage() << "Could not process XML document"); - return {}; - } - - return {config}; -} - /** Converts a ConfiguredArtifact into an OutputArtifact. */ Maybe ToOutputArtifact(const ConfiguredArtifact& artifact, const std::string& apk_name, @@ -302,11 +249,11 @@ Maybe ToOutputArtifact(const ConfiguredArtifact& artifact, has_errors = true; } - if (artifact.android_sdk_group) { - auto entry = config.android_sdk_groups.find(artifact.android_sdk_group.value()); - if (entry == config.android_sdk_groups.end()) { + if (artifact.android_sdk) { + auto entry = config.android_sdks.find(artifact.android_sdk.value()); + if (entry == config.android_sdks.end()) { src_diag.Error(DiagMessage() << "Could not lookup required Android SDK version: " - << artifact.android_sdk_group.value()); + << artifact.android_sdk.value()); has_errors = true; } else { output_artifact.android_sdk = {entry->second}; @@ -323,6 +270,64 @@ Maybe ToOutputArtifact(const ConfiguredArtifact& artifact, namespace configuration { +/** Returns the binary reprasentation of the XML configuration. */ +Maybe ExtractConfiguration(const std::string& contents, + const std::string& config_path, + IDiagnostics* diag) { + StringInputStream in(contents); + std::unique_ptr doc = xml::Inflate(&in, diag, Source(config_path)); + if (!doc) { + return {}; + } + + // Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace. + Element* root = doc->root.get(); + if (root == nullptr) { + diag->Error(DiagMessage() << "Could not find the root element in the XML document"); + return {}; + } + + std::string& xml_ns = root->namespace_uri; + if (!xml_ns.empty()) { + if (xml_ns != kAaptXmlNs) { + diag->Error(DiagMessage() << "Unknown namespace found on root element: " << xml_ns); + return {}; + } + + xml_ns.clear(); + NamespaceVisitor visitor; + root->Accept(&visitor); + } + + XmlActionExecutor executor; + XmlNodeAction& root_action = executor["post-process"]; + XmlNodeAction& artifacts_action = root_action["artifacts"]; + + PostProcessingConfiguration config; + + // Parse the artifact elements. + artifacts_action["artifact"].Action(Bind(&config, ArtifactTagHandler)); + artifacts_action["artifact-format"].Action(Bind(&config, ArtifactFormatTagHandler)); + + // Parse the different configuration groups. + root_action["abi-groups"]["abi-group"].Action(Bind(&config, AbiGroupTagHandler)); + root_action["screen-density-groups"]["screen-density-group"].Action( + Bind(&config, ScreenDensityGroupTagHandler)); + root_action["locale-groups"]["locale-group"].Action(Bind(&config, LocaleGroupTagHandler)); + root_action["android-sdks"]["android-sdk"].Action(Bind(&config, AndroidSdkTagHandler)); + root_action["gl-texture-groups"]["gl-texture-group"].Action( + Bind(&config, GlTextureGroupTagHandler)); + root_action["device-feature-groups"]["device-feature-group"].Action( + Bind(&config, DeviceFeatureGroupTagHandler)); + + if (!executor.Execute(XmlActionExecutorPolicy::kNone, diag, doc.get())) { + diag->Error(DiagMessage() << "Could not process XML document"); + return {}; + } + + return {config}; +} + const StringPiece& AbiToString(Abi abi) { return kAbiToStringMap.at(static_cast(abi)); } @@ -383,7 +388,7 @@ Maybe ConfiguredArtifact::ToArtifactName(const StringPiece& format, return {}; } - if (!ReplacePlaceholder("${sdk}", android_sdk_group, &result, diag)) { + if (!ReplacePlaceholder("${sdk}", android_sdk, &result, diag)) { return {}; } @@ -414,47 +419,37 @@ Maybe ConfigurationParser::ForPath(const std::string& path) if (!ReadFileToString(path, &contents, true)) { return {}; } - return ConfigurationParser(contents); + return ConfigurationParser(contents, path); } -ConfigurationParser::ConfigurationParser(std::string contents) - : contents_(std::move(contents)), - diag_(&noop_) { +ConfigurationParser::ConfigurationParser(std::string contents, const std::string& config_path) + : contents_(std::move(contents)), config_path_(config_path), diag_(&noop_) { } Maybe> ConfigurationParser::Parse( const android::StringPiece& apk_path) { - Maybe maybe_config = ExtractConfiguration(contents_, diag_); + Maybe maybe_config = + ExtractConfiguration(contents_, config_path_, diag_); if (!maybe_config) { return {}; } - const PostProcessingConfiguration& config = maybe_config.value(); - - // TODO: Automatically arrange artifacts so that they match Play Store multi-APK requirements. - // see: https://developer.android.com/google/play/publishing/multiple-apks.html - // - // For now, make sure the version codes are unique. - std::vector artifacts = config.artifacts; - std::sort(artifacts.begin(), artifacts.end()); - if (std::adjacent_find(artifacts.begin(), artifacts.end()) != artifacts.end()) { - diag_->Error(DiagMessage() << "Configuration has duplicate versions"); - return {}; - } - - const std::string& apk_name = file::GetFilename(apk_path).to_string(); - const StringPiece ext = file::GetExtension(apk_name); - const std::string base_name = apk_name.substr(0, apk_name.size() - ext.size()); // Convert from a parsed configuration to a list of artifacts for processing. + const std::string& apk_name = file::GetFilename(apk_path).to_string(); std::vector output_artifacts; bool has_errors = false; - for (const ConfiguredArtifact& artifact : artifacts) { + PostProcessingConfiguration& config = maybe_config.value(); + config.SortArtifacts(); + + int version = 1; + for (const ConfiguredArtifact& artifact : config.artifacts) { Maybe output_artifact = ToOutputArtifact(artifact, apk_name, config, diag_); if (!output_artifact) { // Defer return an error condition so that all errors are reported. has_errors = true; } else { + output_artifact.value().version = version++; output_artifacts.push_back(std::move(output_artifact.value())); } } @@ -470,24 +465,18 @@ namespace handler { bool ArtifactTagHandler(PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) { - // This will be incremented later so the first version will always be different to the base APK. - int current_version = (config->artifacts.empty()) ? 0 : config->artifacts.back().version; - ConfiguredArtifact artifact{}; - Maybe version; for (const auto& attr : root_element->attributes) { if (attr.name == "name") { artifact.name = attr.value; - } else if (attr.name == "version") { - version = std::stoi(attr.value); } else if (attr.name == "abi-group") { artifact.abi_group = {attr.value}; } else if (attr.name == "screen-density-group") { artifact.screen_density_group = {attr.value}; } else if (attr.name == "locale-group") { artifact.locale_group = {attr.value}; - } else if (attr.name == "android-sdk-group") { - artifact.android_sdk_group = {attr.value}; + } else if (attr.name == "android-sdk") { + artifact.android_sdk = {attr.value}; } else if (attr.name == "gl-texture-group") { artifact.gl_texture_group = {attr.value}; } else if (attr.name == "device-feature-group") { @@ -497,9 +486,6 @@ bool ArtifactTagHandler(PostProcessingConfiguration* config, Element* root_eleme << attr.value); } } - - artifact.version = (version) ? version.value() : current_version + 1; - config->artifacts.push_back(artifact); return true; }; @@ -523,7 +509,7 @@ bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_eleme return false; } - auto& group = config->abi_groups[label]; + auto& group = GetOrCreateGroup(label, &config->abi_groups); bool valid = true; // Special case for empty abi-group tag. Label will be used as the ABI. @@ -567,7 +553,7 @@ bool ScreenDensityGroupTagHandler(PostProcessingConfiguration* config, Element* return false; } - auto& group = config->screen_density_groups[label]; + auto& group = GetOrCreateGroup(label, &config->screen_density_groups); bool valid = true; // Special case for empty screen-density-group tag. Label will be used as the screen density. @@ -627,7 +613,7 @@ bool LocaleGroupTagHandler(PostProcessingConfiguration* config, Element* root_el return false; } - auto& group = config->locale_groups[label]; + auto& group = GetOrCreateGroup(label, &config->locale_groups); bool valid = true; // Special case to auto insert a locale for an empty group. Label will be used for locale. @@ -680,61 +666,58 @@ bool LocaleGroupTagHandler(PostProcessingConfiguration* config, Element* root_el return valid; }; -bool AndroidSdkGroupTagHandler(PostProcessingConfiguration* config, Element* root_element, - IDiagnostics* diag) { - std::string label = GetLabel(root_element, diag); - if (label.empty()) { - return false; - } - +bool AndroidSdkTagHandler(PostProcessingConfiguration* config, Element* root_element, + IDiagnostics* diag) { + AndroidSdk entry = AndroidSdk::ForMinSdk(-1); bool valid = true; - bool found = false; + for (const auto& attr : root_element->attributes) { + bool valid_attr = false; + if (attr.name == "label") { + entry.label = attr.value; + valid_attr = true; + } else if (attr.name == "minSdkVersion") { + Maybe version = ResourceUtils::ParseSdkVersion(attr.value); + if (version) { + valid_attr = true; + entry.min_sdk_version = version.value(); + } + } else if (attr.name == "targetSdkVersion") { + Maybe version = ResourceUtils::ParseSdkVersion(attr.value); + if (version) { + valid_attr = true; + entry.target_sdk_version = version; + } + } else if (attr.name == "maxSdkVersion") { + Maybe version = ResourceUtils::ParseSdkVersion(attr.value); + if (version) { + valid_attr = true; + entry.max_sdk_version = version; + } + } - for (auto* child : root_element->GetChildElements()) { - if (child->name != "android-sdk") { - diag->Error(DiagMessage() << "Unexpected root_element in ABI group: " << child->name); + if (!valid_attr) { + diag->Error(DiagMessage() << "Invalid attribute: " << attr.name << " = " << attr.value); valid = false; - } else { - AndroidSdk entry; - for (const auto& attr : child->attributes) { - Maybe* target = nullptr; - if (attr.name == "minSdkVersion") { - target = &entry.min_sdk_version; - } else if (attr.name == "targetSdkVersion") { - target = &entry.target_sdk_version; - } else if (attr.name == "maxSdkVersion") { - target = &entry.max_sdk_version; - } else { - diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value); - continue; - } - - *target = ResourceUtils::ParseSdkVersion(attr.value); - if (!*target) { - diag->Error(DiagMessage() << "Invalid attribute: " << attr.name << " = " << attr.value); - valid = false; - } - } - - // TODO: Fill in the manifest details when they are finalised. - for (auto node : child->GetChildElements()) { - if (node->name == "manifest") { - if (entry.manifest) { - diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates."); - continue; - } - entry.manifest = {AndroidManifest()}; - } - } - - config->android_sdk_groups[label] = entry; - if (found) { - valid = false; - } - found = true; } } + if (entry.min_sdk_version == -1) { + diag->Error(DiagMessage() << "android-sdk is missing minSdkVersion attribute"); + valid = false; + } + + // TODO: Fill in the manifest details when they are finalised. + for (auto node : root_element->GetChildElements()) { + if (node->name == "manifest") { + if (entry.manifest) { + diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates."); + continue; + } + entry.manifest = {AndroidManifest()}; + } + } + + config->android_sdks[entry.label] = entry; return valid; }; @@ -745,7 +728,7 @@ bool GlTextureGroupTagHandler(PostProcessingConfiguration* config, Element* root return false; } - auto& group = config->gl_texture_groups[label]; + auto& group = GetOrCreateGroup(label, &config->gl_texture_groups); bool valid = true; GlTexture result; @@ -788,7 +771,7 @@ bool DeviceFeatureGroupTagHandler(PostProcessingConfiguration* config, Element* return false; } - auto& group = config->device_feature_groups[label]; + auto& group = GetOrCreateGroup(label, &config->device_feature_groups); bool valid = true; for (auto* child : root_element->GetChildElements()) { diff --git a/tools/aapt2/configuration/ConfigurationParser.h b/tools/aapt2/configuration/ConfigurationParser.h index ca5891025c92c..7f1d4453f9b36 100644 --- a/tools/aapt2/configuration/ConfigurationParser.h +++ b/tools/aapt2/configuration/ConfigurationParser.h @@ -71,7 +71,8 @@ struct AndroidManifest { }; struct AndroidSdk { - Maybe min_sdk_version; + std::string label; + int min_sdk_version; // min_sdk_version is mandatory if splitting by SDK. Maybe target_sdk_version; Maybe max_sdk_version; Maybe manifest; @@ -113,15 +114,19 @@ struct OutputArtifact { Maybe android_sdk; std::vector features; std::vector textures; + + inline int GetMinSdk(int default_value = -1) const { + if (!android_sdk) { + return default_value; + } + return android_sdk.value().min_sdk_version; + } }; } // namespace configuration // Forward declaration of classes used in the API. struct IDiagnostics; -namespace xml { -class Element; -} /** * XML configuration file parser for the split and optimize commands. @@ -133,8 +138,8 @@ class ConfigurationParser { static Maybe ForPath(const std::string& path); /** Returns a ConfigurationParser for the configuration in the provided file contents. */ - static ConfigurationParser ForContents(const std::string& contents) { - ConfigurationParser parser{contents}; + static ConfigurationParser ForContents(const std::string& contents, const std::string& path) { + ConfigurationParser parser{contents, path}; return parser; } @@ -156,7 +161,7 @@ class ConfigurationParser { * diagnostics context. The default diagnostics context can be overridden with a call to * WithDiagnostics(IDiagnostics *). */ - explicit ConfigurationParser(std::string contents); + ConfigurationParser(std::string contents, const std::string& config_path); /** Returns the current diagnostics context to any subclasses. */ IDiagnostics* diagnostics() { @@ -166,6 +171,8 @@ class ConfigurationParser { private: /** The contents of the configuration file to parse. */ const std::string contents_; + /** Path to the input configuration. */ + const std::string config_path_; /** The diagnostics context to send messages to. */ IDiagnostics* diag_; }; diff --git a/tools/aapt2/configuration/ConfigurationParser.internal.h b/tools/aapt2/configuration/ConfigurationParser.internal.h index 7657ebd70a8bc..a583057427e60 100644 --- a/tools/aapt2/configuration/ConfigurationParser.internal.h +++ b/tools/aapt2/configuration/ConfigurationParser.internal.h @@ -17,35 +17,105 @@ #ifndef AAPT2_CONFIGURATIONPARSER_INTERNAL_H #define AAPT2_CONFIGURATIONPARSER_INTERNAL_H +#include "configuration/ConfigurationParser.h" + +#include +#include + namespace aapt { + +// Forward declaration of classes used in the API. +namespace xml { +class Element; +} + namespace configuration { +template +struct OrderedEntry { + size_t order; + std::vector entry; +}; + /** A mapping of group labels to group of configuration items. */ template -using Group = std::unordered_map>; +using Group = std::unordered_map>; /** A mapping of group label to a single configuration item. */ template using Entry = std::unordered_map; +/** Retrieves an entry from the provided Group, creating a new instance if one does not exist. */ +template +std::vector& GetOrCreateGroup(std::string label, Group* group) { + OrderedEntry& entry = (*group)[label]; + // If this is a new entry, set the order. + if (entry.order == 0) { + entry.order = group->size(); + } + return entry.entry; +} + +/** + * A ComparisonChain is a grouping of comparisons to perform when sorting groups that have a well + * defined order of precedence. Comparisons are only made if none of the previous comparisons had a + * definite result. A comparison has a result if at least one of the items has an entry for that + * value and that they are not equal. + */ +class ComparisonChain { + public: + /** + * Adds a new comparison of items in a group to the chain. The new comparison is only used if we + * have not been able to determine the sort order with the previous comparisons. + */ + template + ComparisonChain& Add(const Group& groups, const Maybe& lhs, + const Maybe& rhs) { + return Add(GetGroupOrder(groups, lhs), GetGroupOrder(groups, rhs)); + } + + /** + * Adds a new comparison to the chain. The new comparison is only used if we have not been able to + * determine the sort order with the previous comparisons. + */ + ComparisonChain& Add(int lhs, int rhs) { + if (!has_result_) { + has_result_ = (lhs != rhs); + result_ = (lhs < rhs); + } + return *this; + } + + /** Returns true if the left hand side should come before the right hand side. */ + bool Compare() { + return result_; + } + + private: + template + inline size_t GetGroupOrder(const Group& groups, const Maybe& label) { + if (!label) { + return std::numeric_limits::max(); + } + return groups.at(label.value()).order; + } + + bool has_result_ = false; + bool result_ = false; +}; + /** Output artifact configuration options. */ struct ConfiguredArtifact { /** Name to use for output of processing foo.apk -> foo..apk. */ Maybe name; - /** - * Value to add to the base Android manifest versionCode. If it is not present in the - * configuration file, it is set to the previous artifact + 1. If the first artifact does not have - * a value, artifacts are a 1 based index. - */ - int version; /** If present, uses the ABI group with this name. */ Maybe abi_group; /** If present, uses the screen density group with this name. */ Maybe screen_density_group; /** If present, uses the locale group with this name. */ Maybe locale_group; - /** If present, uses the Android SDK group with this name. */ - Maybe android_sdk_group; + /** If present, uses the Android SDK with this name. */ + Maybe android_sdk; /** If present, uses the device feature group with this name. */ Maybe device_feature_group; /** If present, uses the OpenGL texture group with this name. */ @@ -57,31 +127,71 @@ struct ConfiguredArtifact { /** Convert an artifact name template into a name string based on configuration contents. */ Maybe Name(const android::StringPiece& apk_name, IDiagnostics* diag) const; - - bool operator<(const ConfiguredArtifact& rhs) const { - // TODO(safarmer): Order by play store multi-APK requirements. - return version < rhs.version; - } - - bool operator==(const ConfiguredArtifact& rhs) const { - return version == rhs.version; - } }; /** AAPT2 XML configuration file binary representation. */ struct PostProcessingConfiguration { - // TODO: Support named artifacts? std::vector artifacts; Maybe artifact_format; Group abi_groups; Group screen_density_groups; Group locale_groups; - Entry android_sdk_groups; Group device_feature_groups; Group gl_texture_groups; + Entry android_sdks; + + /** + * Sorts the configured artifacts based on the ordering of the groups in the configuration file. + * The only exception to this rule is Android SDK versions. Larger SDK versions will have a larger + * versionCode to ensure users get the correct APK when they upgrade their OS. + */ + void SortArtifacts() { + std::sort(artifacts.begin(), artifacts.end(), *this); + } + + /** Comparator that ensures artifacts are in the preferred order for versionCode rewriting. */ + bool operator()(const ConfiguredArtifact& lhs, const ConfiguredArtifact& rhs) { + // Split dimensions are added in the order of precedence. Items higher in the list result in + // higher version codes. + return ComparisonChain() + // All splits with a minSdkVersion specified must be last to ensure the application will be + // updated if a user upgrades the version of Android on their device. + .Add(GetMinSdk(lhs), GetMinSdk(rhs)) + // ABI version is important, especially on x86 phones where they may begin to run in ARM + // emulation mode on newer Android versions. This allows us to ensure that the x86 version + // is installed on these devices rather than ARM. + .Add(abi_groups, lhs.abi_group, rhs.abi_group) + // The rest are in arbitrary order based on estimated usage. + .Add(screen_density_groups, lhs.screen_density_group, rhs.screen_density_group) + .Add(locale_groups, lhs.locale_group, rhs.locale_group) + .Add(gl_texture_groups, lhs.gl_texture_group, rhs.gl_texture_group) + .Add(device_feature_groups, lhs.device_feature_group, rhs.device_feature_group) + .Compare(); + } + + private: + /** + * Returns the min_sdk_version from the provided artifact or 0 if none is present. This allows + * artifacts that have an Android SDK version to have a higher versionCode than those that do not. + */ + inline int GetMinSdk(const ConfiguredArtifact& artifact) { + if (!artifact.android_sdk) { + return 0; + } + const auto& entry = android_sdks.find(artifact.android_sdk.value()); + if (entry == android_sdks.end()) { + return 0; + } + return entry->second.min_sdk_version; + } }; +/** Parses the provided XML document returning the post processing configuration. */ +Maybe ExtractConfiguration(const std::string& contents, + const std::string& config_path, + IDiagnostics* diag); + namespace handler { /** Handler for tags. */ @@ -104,9 +214,9 @@ bool ScreenDensityGroupTagHandler(configuration::PostProcessingConfiguration* co bool LocaleGroupTagHandler(configuration::PostProcessingConfiguration* config, xml::Element* element, IDiagnostics* diag); -/** Handler for tags. */ -bool AndroidSdkGroupTagHandler(configuration::PostProcessingConfiguration* config, - xml::Element* element, IDiagnostics* diag); +/** Handler for tags. */ +bool AndroidSdkTagHandler(configuration::PostProcessingConfiguration* config, xml::Element* element, + IDiagnostics* diag); /** Handler for tags. */ bool GlTextureGroupTagHandler(configuration::PostProcessingConfiguration* config, diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp index da00511fd192f..0329846a5bb59 100644 --- a/tools/aapt2/configuration/ConfigurationParser_test.cpp +++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp @@ -30,11 +30,33 @@ namespace aapt { namespace configuration { void PrintTo(const AndroidSdk& sdk, std::ostream* os) { - *os << "SDK: min=" << sdk.min_sdk_version.value_or_default(-1) + *os << "SDK: min=" << sdk.min_sdk_version << ", target=" << sdk.target_sdk_version.value_or_default(-1) << ", max=" << sdk.max_sdk_version.value_or_default(-1); } +bool operator==(const ConfiguredArtifact& lhs, const ConfiguredArtifact& rhs) { + return lhs.name == rhs.name && lhs.abi_group == rhs.abi_group && + lhs.screen_density_group == rhs.screen_density_group && + lhs.locale_group == rhs.locale_group && lhs.android_sdk == rhs.android_sdk && + lhs.device_feature_group == rhs.device_feature_group && + lhs.gl_texture_group == rhs.gl_texture_group; +} + +std::ostream& operator<<(std::ostream& out, const Maybe& value) { + PrintTo(value, &out); + return out; +} + +void PrintTo(const ConfiguredArtifact& artifact, std::ostream* os) { + *os << "\n{" + << "\n name: " << artifact.name << "\n sdk: " << artifact.android_sdk + << "\n abi: " << artifact.abi_group << "\n density: " << artifact.screen_density_group + << "\n locale: " << artifact.locale_group + << "\n features: " << artifact.device_feature_group + << "\n textures: " << artifact.gl_texture_group << "\n}\n"; +} + namespace handler { namespace { @@ -44,6 +66,7 @@ using ::aapt::configuration::AndroidManifest; using ::aapt::configuration::AndroidSdk; using ::aapt::configuration::ConfiguredArtifact; using ::aapt::configuration::DeviceFeature; +using ::aapt::configuration::ExtractConfiguration; using ::aapt::configuration::GlTexture; using ::aapt::configuration::Locale; using ::aapt::configuration::PostProcessingConfiguration; @@ -52,11 +75,13 @@ using ::aapt::xml::NodeCast; using ::android::ResTable_config; using ::android::base::StringPrintf; using ::testing::ElementsAre; +using ::testing::Eq; using ::testing::SizeIs; +using ::testing::StrEq; constexpr const char* kValidConfig = R"( - + armeabi-v7a arm64-v8a @@ -65,6 +90,8 @@ constexpr const char* kValidConfig = R"( x86 mips + + xhdpi xxhdpi @@ -78,6 +105,8 @@ constexpr const char* kValidConfig = R"( xxhdpi xxxhdpi + + en es @@ -89,25 +118,30 @@ constexpr const char* kValidConfig = R"( es-rMX fr-rCA - - - - - - - + + + + + + + + + assets/dxt1/* + + android.hardware.audio.low_latency - + ${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release @@ -117,7 +151,7 @@ constexpr const char* kValidConfig = R"( abi-group="arm" screen-density-group="large" locale-group="europe" - android-sdk-group="v19" + android-sdk="v19" gl-texture-group="dxt1" device-feature-group="low-latency"/> abi-group="other" screen-density-group="alldpi" locale-group="north-america" - android-sdk-group="v19" + android-sdk="v19" gl-texture-group="dxt1" device-feature-group="low-latency"/> @@ -134,7 +168,8 @@ constexpr const char* kValidConfig = R"( class ConfigurationParserTest : public ConfigurationParser, public ::testing::Test { public: - ConfigurationParserTest() : ConfigurationParser("") {} + ConfigurationParserTest() : ConfigurationParser("", "config.xml") { + } protected: StdErrDiagnostics diag_; @@ -145,8 +180,31 @@ TEST_F(ConfigurationParserTest, ForPath_NoFile) { EXPECT_FALSE(result); } +TEST_F(ConfigurationParserTest, ExtractConfiguration) { + Maybe maybe_config = + ExtractConfiguration(kValidConfig, "dummy.xml", &diag_); + + PostProcessingConfiguration config = maybe_config.value(); + + auto& arm = config.abi_groups["arm"]; + auto& other = config.abi_groups["other"]; + EXPECT_EQ(arm.order, 1ul); + EXPECT_EQ(other.order, 2ul); + + auto& large = config.screen_density_groups["large"]; + auto& alldpi = config.screen_density_groups["alldpi"]; + EXPECT_EQ(large.order, 1ul); + EXPECT_EQ(alldpi.order, 2ul); + + auto& north_america = config.locale_groups["north-america"]; + auto& europe = config.locale_groups["europe"]; + // Checked in reverse to make sure access order does not matter. + EXPECT_EQ(north_america.order, 2ul); + EXPECT_EQ(europe.order, 1ul); +} + TEST_F(ConfigurationParserTest, ValidateFile) { - auto parser = ConfigurationParser::ForContents(kValidConfig).WithDiagnostics(&diag_); + auto parser = ConfigurationParser::ForContents(kValidConfig, "conf.xml").WithDiagnostics(&diag_); auto result = parser.Parse("test.apk"); ASSERT_TRUE(result); const std::vector& value = result.value(); @@ -154,6 +212,7 @@ TEST_F(ConfigurationParserTest, ValidateFile) { const OutputArtifact& a1 = value[0]; EXPECT_EQ(a1.name, "art1.apk"); + EXPECT_EQ(a1.version, 1); EXPECT_THAT(a1.abis, ElementsAre(Abi::kArmV7a, Abi::kArm64V8a)); EXPECT_THAT(a1.screen_densities, ElementsAre(test::ParseConfigOrDie("xhdpi").CopyWithoutSdkVersion(), @@ -161,12 +220,15 @@ TEST_F(ConfigurationParserTest, ValidateFile) { test::ParseConfigOrDie("xxxhdpi").CopyWithoutSdkVersion())); EXPECT_THAT(a1.locales, ElementsAre(test::ParseConfigOrDie("en"), test::ParseConfigOrDie("es"), test::ParseConfigOrDie("fr"), test::ParseConfigOrDie("de"))); - EXPECT_EQ(a1.android_sdk.value().min_sdk_version.value(), 19l); + ASSERT_TRUE(a1.android_sdk); + ASSERT_TRUE(a1.android_sdk.value().min_sdk_version); + EXPECT_EQ(a1.android_sdk.value().min_sdk_version, 19l); EXPECT_THAT(a1.textures, SizeIs(1ul)); EXPECT_THAT(a1.features, SizeIs(1ul)); const OutputArtifact& a2 = value[1]; EXPECT_EQ(a2.name, "art2.apk"); + EXPECT_EQ(a2.version, 2); EXPECT_THAT(a2.abis, ElementsAre(Abi::kX86, Abi::kMips)); EXPECT_THAT(a2.screen_densities, ElementsAre(test::ParseConfigOrDie("ldpi").CopyWithoutSdkVersion(), @@ -178,124 +240,138 @@ TEST_F(ConfigurationParserTest, ValidateFile) { EXPECT_THAT(a2.locales, ElementsAre(test::ParseConfigOrDie("en"), test::ParseConfigOrDie("es-rMX"), test::ParseConfigOrDie("fr-rCA"))); - EXPECT_EQ(a2.android_sdk.value().min_sdk_version.value(), 19l); + ASSERT_TRUE(a2.android_sdk); + ASSERT_TRUE(a2.android_sdk.value().min_sdk_version); + EXPECT_EQ(a2.android_sdk.value().min_sdk_version, 19l); EXPECT_THAT(a2.textures, SizeIs(1ul)); EXPECT_THAT(a2.features, SizeIs(1ul)); } +TEST_F(ConfigurationParserTest, ConfiguredArtifactOrdering) { + // Create a base builder with the configuration groups but no artifacts to allow it to be copied. + test::PostProcessingConfigurationBuilder base_builder = test::PostProcessingConfigurationBuilder() + .AddAbiGroup("arm") + .AddAbiGroup("arm64") + .AddAndroidSdk("v23", 23) + .AddAndroidSdk("v19", 19); + + { + // Test version ordering. + ConfiguredArtifact v23; + v23.android_sdk = {"v23"}; + ConfiguredArtifact v19; + v19.android_sdk = {"v19"}; + + test::PostProcessingConfigurationBuilder builder = base_builder; + PostProcessingConfiguration config = builder.AddArtifact(v23).AddArtifact(v19).Build(); + + config.SortArtifacts(); + ASSERT_THAT(config.artifacts, SizeIs(2)); + EXPECT_THAT(config.artifacts[0], Eq(v19)); + EXPECT_THAT(config.artifacts[1], Eq(v23)); + } + + { + // Test ABI ordering. + ConfiguredArtifact arm; + arm.android_sdk = {"v19"}; + arm.abi_group = {"arm"}; + ConfiguredArtifact arm64; + arm64.android_sdk = {"v19"}; + arm64.abi_group = {"arm64"}; + + test::PostProcessingConfigurationBuilder builder = base_builder; + PostProcessingConfiguration config = builder.AddArtifact(arm64).AddArtifact(arm).Build(); + + config.SortArtifacts(); + ASSERT_THAT(config.artifacts, SizeIs(2)); + EXPECT_THAT(config.artifacts[0], Eq(arm)); + EXPECT_THAT(config.artifacts[1], Eq(arm64)); + } + + { + // Test Android SDK has precedence over ABI. + ConfiguredArtifact arm; + arm.android_sdk = {"v23"}; + arm.abi_group = {"arm"}; + ConfiguredArtifact arm64; + arm64.android_sdk = {"v19"}; + arm64.abi_group = {"arm64"}; + + test::PostProcessingConfigurationBuilder builder = base_builder; + PostProcessingConfiguration config = builder.AddArtifact(arm64).AddArtifact(arm).Build(); + + config.SortArtifacts(); + ASSERT_THAT(config.artifacts, SizeIs(2)); + EXPECT_THAT(config.artifacts[0], Eq(arm64)); + EXPECT_THAT(config.artifacts[1], Eq(arm)); + } + + { + // Test version is better than ABI. + ConfiguredArtifact arm; + arm.abi_group = {"arm"}; + ConfiguredArtifact v19; + v19.android_sdk = {"v19"}; + + test::PostProcessingConfigurationBuilder builder = base_builder; + PostProcessingConfiguration config = builder.AddArtifact(v19).AddArtifact(arm).Build(); + + config.SortArtifacts(); + ASSERT_THAT(config.artifacts, SizeIs(2)); + EXPECT_THAT(config.artifacts[0], Eq(arm)); + EXPECT_THAT(config.artifacts[1], Eq(v19)); + } + + { + // Test version is sorted higher than no version. + ConfiguredArtifact arm; + arm.abi_group = {"arm"}; + ConfiguredArtifact v19; + v19.abi_group = {"arm"}; + v19.android_sdk = {"v19"}; + + test::PostProcessingConfigurationBuilder builder = base_builder; + PostProcessingConfiguration config = builder.AddArtifact(v19).AddArtifact(arm).Build(); + + config.SortArtifacts(); + ASSERT_THAT(config.artifacts, SizeIs(2)); + EXPECT_THAT(config.artifacts[0], Eq(arm)); + EXPECT_THAT(config.artifacts[1], Eq(v19)); + } +} + TEST_F(ConfigurationParserTest, InvalidNamespace) { constexpr const char* invalid_ns = R"( - )"; + )"; - auto result = ConfigurationParser::ForContents(invalid_ns).Parse("test.apk"); + auto result = ConfigurationParser::ForContents(invalid_ns, "config.xml").Parse("test.apk"); ASSERT_FALSE(result); } TEST_F(ConfigurationParserTest, ArtifactAction) { PostProcessingConfiguration config; - { - const auto doc = test::BuildXmlDom(R"xml( + const auto doc = test::BuildXmlDom(R"xml( )xml"); - ASSERT_TRUE(ArtifactTagHandler(&config, NodeCast(doc->root.get()), &diag_)); + ASSERT_TRUE(ArtifactTagHandler(&config, NodeCast(doc->root.get()), &diag_)); - EXPECT_THAT(config.artifacts, SizeIs(1ul)); + EXPECT_THAT(config.artifacts, SizeIs(1ul)); - auto& artifact = config.artifacts.back(); - EXPECT_FALSE(artifact.name); // TODO: make this fail. - EXPECT_EQ(1, artifact.version); - EXPECT_EQ("arm", artifact.abi_group.value()); - EXPECT_EQ("large", artifact.screen_density_group.value()); - EXPECT_EQ("europe", artifact.locale_group.value()); - EXPECT_EQ("v19", artifact.android_sdk_group.value()); - EXPECT_EQ("dxt1", artifact.gl_texture_group.value()); - EXPECT_EQ("low-latency", artifact.device_feature_group.value()); - } - - { - // Perform a second action to ensure we get 2 artifacts. - const auto doc = test::BuildXmlDom(R"xml( - )xml"); - - ASSERT_TRUE(ArtifactTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_)); - EXPECT_THAT(config.artifacts, SizeIs(2ul)); - EXPECT_EQ(2, config.artifacts.back().version); - } - - { - // Perform a third action with a set version code. - const auto doc = test::BuildXmlDom(R"xml( - )xml"); - - ASSERT_TRUE(ArtifactTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_)); - EXPECT_THAT(config.artifacts, SizeIs(3ul)); - EXPECT_EQ(5, config.artifacts.back().version); - } - - { - // Perform a fourth action to ensure the version code still increments. - const auto doc = test::BuildXmlDom(R"xml( - )xml"); - - ASSERT_TRUE(ArtifactTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_)); - EXPECT_THAT(config.artifacts, SizeIs(4ul)); - EXPECT_EQ(6, config.artifacts.back().version); - } -} - -TEST_F(ConfigurationParserTest, DuplicateArtifactVersion) { - static constexpr const char* configuration = R"xml( - > - - - ${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release - - - - - )xml"; - auto result = ConfigurationParser::ForContents(configuration).Parse("test.apk"); - ASSERT_FALSE(result); + auto& artifact = config.artifacts.back(); + EXPECT_FALSE(artifact.name); // TODO: make this fail. + EXPECT_EQ("arm", artifact.abi_group.value()); + EXPECT_EQ("large", artifact.screen_density_group.value()); + EXPECT_EQ("europe", artifact.locale_group.value()); + EXPECT_EQ("v19", artifact.android_sdk.value()); + EXPECT_EQ("dxt1", artifact.gl_texture_group.value()); + EXPECT_EQ("low-latency", artifact.device_feature_group.value()); } TEST_F(ConfigurationParserTest, ArtifactFormatAction) { @@ -334,7 +410,7 @@ TEST_F(ConfigurationParserTest, AbiGroupAction) { EXPECT_THAT(config.abi_groups, SizeIs(1ul)); ASSERT_EQ(1u, config.abi_groups.count("arm")); - auto& out = config.abi_groups["arm"]; + auto& out = config.abi_groups["arm"].entry; ASSERT_THAT(out, ElementsAre(Abi::kArmV7a, Abi::kArm64V8a)); } @@ -350,7 +426,7 @@ TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup) { EXPECT_THAT(config.abi_groups, SizeIs(1ul)); ASSERT_EQ(1u, config.abi_groups.count("arm64-v8a")); - auto& out = config.abi_groups["arm64-v8a"]; + auto& out = config.abi_groups["arm64-v8a"].entry; ASSERT_THAT(out, ElementsAre(Abi::kArm64V8a)); } @@ -390,7 +466,7 @@ TEST_F(ConfigurationParserTest, ScreenDensityGroupAction) { ConfigDescription xxxhdpi; xxxhdpi.density = ResTable_config::DENSITY_XXXHIGH; - auto& out = config.screen_density_groups["large"]; + auto& out = config.screen_density_groups["large"].entry; ASSERT_THAT(out, ElementsAre(xhdpi, xxhdpi, xxxhdpi)); } @@ -409,7 +485,7 @@ TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup) { ConfigDescription xhdpi; xhdpi.density = ResTable_config::DENSITY_XHIGH; - auto& out = config.screen_density_groups["xhdpi"]; + auto& out = config.screen_density_groups["xhdpi"].entry; ASSERT_THAT(out, ElementsAre(xhdpi)); } @@ -441,7 +517,7 @@ TEST_F(ConfigurationParserTest, LocaleGroupAction) { ASSERT_EQ(1ul, config.locale_groups.size()); ASSERT_EQ(1u, config.locale_groups.count("europe")); - const auto& out = config.locale_groups["europe"]; + const auto& out = config.locale_groups["europe"].entry; ConfigDescription en = test::ParseConfigOrDie("en"); ConfigDescription es = test::ParseConfigOrDie("es"); @@ -463,7 +539,7 @@ TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup) { ASSERT_EQ(1ul, config.locale_groups.size()); ASSERT_EQ(1u, config.locale_groups.count("en")); - const auto& out = config.locale_groups["en"]; + const auto& out = config.locale_groups["en"].entry; ConfigDescription en = test::ParseConfigOrDie("en"); @@ -482,27 +558,25 @@ TEST_F(ConfigurationParserTest, LocaleGroupAction_InvalidEmtpyGroup) { TEST_F(ConfigurationParserTest, AndroidSdkGroupAction) { static constexpr const char* xml = R"xml( - - - - )xml"; + )xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; - bool ok = AndroidSdkGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); + bool ok = AndroidSdkTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); - ASSERT_EQ(1ul, config.android_sdk_groups.size()); - ASSERT_EQ(1u, config.android_sdk_groups.count("v19")); + ASSERT_EQ(1ul, config.android_sdks.size()); + ASSERT_EQ(1u, config.android_sdks.count("v19")); - auto& out = config.android_sdk_groups["v19"]; + auto& out = config.android_sdks["v19"]; AndroidSdk sdk; sdk.min_sdk_version = 19; @@ -515,98 +589,86 @@ TEST_F(ConfigurationParserTest, AndroidSdkGroupAction) { TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_SingleVersion) { { - static constexpr const char* xml = R"xml( - - - )xml"; - + const char* xml = ""; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; - bool ok = AndroidSdkGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); + bool ok = AndroidSdkTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); - ASSERT_EQ(1ul, config.android_sdk_groups.size()); - ASSERT_EQ(1u, config.android_sdk_groups.count("v19")); + ASSERT_EQ(1ul, config.android_sdks.size()); + ASSERT_EQ(1u, config.android_sdks.count("v19")); - auto& out = config.android_sdk_groups["v19"]; - EXPECT_EQ(19, out.min_sdk_version.value()); + auto& out = config.android_sdks["v19"]; + EXPECT_EQ(19, out.min_sdk_version); EXPECT_FALSE(out.max_sdk_version); EXPECT_FALSE(out.target_sdk_version); } { - static constexpr const char* xml = R"xml( - - - )xml"; - + const char* xml = + ""; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; - bool ok = AndroidSdkGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); + bool ok = AndroidSdkTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); - ASSERT_EQ(1ul, config.android_sdk_groups.size()); - ASSERT_EQ(1u, config.android_sdk_groups.count("v19")); + ASSERT_EQ(1ul, config.android_sdks.size()); + ASSERT_EQ(1u, config.android_sdks.count("v19")); - auto& out = config.android_sdk_groups["v19"]; + auto& out = config.android_sdks["v19"]; EXPECT_EQ(19, out.max_sdk_version.value()); - EXPECT_FALSE(out.min_sdk_version); + EXPECT_EQ(19, out.min_sdk_version); EXPECT_FALSE(out.target_sdk_version); } { - static constexpr const char* xml = R"xml( - - - )xml"; - + const char* xml = + ""; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; - bool ok = AndroidSdkGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); + bool ok = AndroidSdkTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); - ASSERT_EQ(1ul, config.android_sdk_groups.size()); - ASSERT_EQ(1u, config.android_sdk_groups.count("v19")); + ASSERT_EQ(1ul, config.android_sdks.size()); + ASSERT_EQ(1u, config.android_sdks.count("v19")); - auto& out = config.android_sdk_groups["v19"]; + auto& out = config.android_sdks["v19"]; EXPECT_EQ(19, out.target_sdk_version.value()); - EXPECT_FALSE(out.min_sdk_version); + EXPECT_EQ(19, out.min_sdk_version); EXPECT_FALSE(out.max_sdk_version); } } TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_InvalidVersion) { static constexpr const char* xml = R"xml( - - - - - - - )xml"; + + + + + )xml"; auto doc = test::BuildXmlDom(xml); PostProcessingConfiguration config; - bool ok = AndroidSdkGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); + bool ok = AndroidSdkTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_FALSE(ok); } TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) { static constexpr const char* xml = R"xml( - - - )xml"; + )xml"; const auto& dev_sdk = GetDevelopmentSdkCodeNameAndVersion(); const char* codename = dev_sdk.first.data(); @@ -615,13 +677,13 @@ TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) { auto doc = test::BuildXmlDom(StringPrintf(xml, codename, codename)); PostProcessingConfiguration config; - bool ok = AndroidSdkGroupTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); + bool ok = AndroidSdkTagHandler(&config, NodeCast(doc.get()->root.get()), &diag_); ASSERT_TRUE(ok); - ASSERT_EQ(1ul, config.android_sdk_groups.size()); - ASSERT_EQ(1u, config.android_sdk_groups.count("P")); + ASSERT_EQ(1ul, config.android_sdks.size()); + ASSERT_EQ(1u, config.android_sdks.count("P")); - auto& out = config.android_sdk_groups["P"]; + auto& out = config.android_sdks["P"]; AndroidSdk sdk; sdk.min_sdk_version = 25; @@ -651,7 +713,7 @@ TEST_F(ConfigurationParserTest, GlTextureGroupAction) { EXPECT_THAT(config.gl_texture_groups, SizeIs(1ul)); ASSERT_EQ(1u, config.gl_texture_groups.count("dxt1")); - auto& out = config.gl_texture_groups["dxt1"]; + auto& out = config.gl_texture_groups["dxt1"].entry; GlTexture texture{ std::string("GL_EXT_texture_compression_dxt1"), @@ -680,7 +742,7 @@ TEST_F(ConfigurationParserTest, DeviceFeatureGroupAction) { EXPECT_THAT(config.device_feature_groups, SizeIs(1ul)); ASSERT_EQ(1u, config.device_feature_groups.count("low-latency")); - auto& out = config.device_feature_groups["low-latency"]; + auto& out = config.device_feature_groups["low-latency"].entry; DeviceFeature low_latency = "android.hardware.audio.low_latency"; DeviceFeature pro = "android.hardware.audio.pro"; @@ -734,7 +796,7 @@ TEST(ArtifactTest, Complex) { artifact.device_feature_group = {"df1"}; artifact.gl_texture_group = {"glx1"}; artifact.locale_group = {"en-AU"}; - artifact.android_sdk_group = {"v26"}; + artifact.android_sdk = {"v26"}; { auto result = artifact.ToArtifactName( diff --git a/tools/aapt2/configuration/aapt2.xsd b/tools/aapt2/configuration/aapt2.xsd index 134153a017f87..fb2f49bae7b7a 100644 --- a/tools/aapt2/configuration/aapt2.xsd +++ b/tools/aapt2/configuration/aapt2.xsd @@ -8,22 +8,52 @@ - + + + + + + - + + + + + + + + + + + + + + + + + - + + + + + + + + + + @@ -38,8 +68,6 @@ - - @@ -52,7 +80,7 @@ - + @@ -66,14 +94,14 @@ - + - + @@ -93,7 +121,7 @@ - + @@ -108,20 +136,14 @@ - - - - - - - - + + @@ -135,7 +157,7 @@ - + diff --git a/tools/aapt2/configuration/example/config.xml b/tools/aapt2/configuration/example/config.xml index ce31e61b2d62e..d8aba09e836d8 100644 --- a/tools/aapt2/configuration/example/config.xml +++ b/tools/aapt2/configuration/example/config.xml @@ -1,70 +1,5 @@ - - - armeabi-v7a - arm64-v8a - - - - x86 - mips - - - - xhdpi - xxhdpi - xxxhdpi - - - - ldpi - mdpi - hdpi - xhdpi - xxhdpi - xxxhdpi - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - assets/dxt1/* - - - - - android.hardware.audio.low_latency - - - ${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release @@ -87,4 +22,79 @@ device-feature-group="low-latency"/> + + + + + + + + + + + + armeabi-v7a + arm64-v8a + + + + x86 + mips + + + + + + xhdpi + xxhdpi + xxxhdpi + + + + ldpi + mdpi + hdpi + xhdpi + xxhdpi + xxxhdpi + + + + + + + + + + + + + + + + + + + + + + + + + + assets/dxt1/* + + + + + + + android.hardware.audio.low_latency + + + diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp index 16898d6f3e030..991faadcafc44 100644 --- a/tools/aapt2/optimize/MultiApkGenerator.cpp +++ b/tools/aapt2/optimize/MultiApkGenerator.cpp @@ -120,8 +120,6 @@ MultiApkGenerator::MultiApkGenerator(LoadedApk* apk, IAaptContext* context) } bool MultiApkGenerator::FromBaseApk(const MultiApkGeneratorOptions& options) { - // TODO(safarmer): Handle APK version codes for the generated APKs. - std::unordered_set artifacts_to_keep = options.kept_artifacts; std::unordered_set filtered_artifacts; std::unordered_set kept_artifacts; @@ -237,8 +235,8 @@ std::unique_ptr MultiApkGenerator::FilterTable(IAaptContext* cont splits.config_filter = &axis_filter; } - if (artifact.android_sdk && artifact.android_sdk.value().min_sdk_version) { - wrapped_context.SetMinSdkVersion(artifact.android_sdk.value().min_sdk_version.value()); + if (artifact.android_sdk) { + wrapped_context.SetMinSdkVersion(artifact.android_sdk.value().min_sdk_version); } std::unique_ptr table = old_table.Clone(); @@ -301,7 +299,7 @@ bool MultiApkGenerator::UpdateManifest(const OutputArtifact& artifact, if (xml::Attribute* min_sdk_attr = uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) { // Populate with a pre-compiles attribute to we don't need to relink etc. - const std::string& min_sdk_str = std::to_string(android_sdk.min_sdk_version.value()); + const std::string& min_sdk_str = std::to_string(android_sdk.min_sdk_version); min_sdk_attr->compiled_value = ResourceUtils::TryParseInt(min_sdk_str); } else { // There was no minSdkVersion. This is strange since at this point we should have been diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp index 88897a8afac33..84d03cfb0283c 100644 --- a/tools/aapt2/test/Builders.cpp +++ b/tools/aapt2/test/Builders.cpp @@ -26,6 +26,7 @@ using ::aapt::configuration::Abi; using ::aapt::configuration::AndroidSdk; using ::aapt::configuration::ConfiguredArtifact; +using ::aapt::configuration::GetOrCreateGroup; using ::aapt::io::StringInputStream; using ::android::StringPiece; @@ -226,6 +227,11 @@ ArtifactBuilder& ArtifactBuilder::SetName(const std::string& name) { return *this; } +ArtifactBuilder& ArtifactBuilder::SetVersion(int version) { + artifact_.version = version; + return *this; +} + ArtifactBuilder& ArtifactBuilder::AddAbi(configuration::Abi abi) { artifact_.abis.push_back(abi); return *this; @@ -250,5 +256,54 @@ configuration::OutputArtifact ArtifactBuilder::Build() { return artifact_; } +PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::AddAbiGroup( + const std::string& label, std::vector abis) { + return AddGroup(label, &config_.abi_groups, std::move(abis)); +} + +PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::AddDensityGroup( + const std::string& label, std::vector densities) { + std::vector configs; + for (const auto& density : densities) { + configs.push_back(test::ParseConfigOrDie(density)); + } + return AddGroup(label, &config_.screen_density_groups, configs); +} + +PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::AddLocaleGroup( + const std::string& label, std::vector locales) { + std::vector configs; + for (const auto& locale : locales) { + configs.push_back(test::ParseConfigOrDie(locale)); + } + return AddGroup(label, &config_.locale_groups, configs); +} + +PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::AddDeviceFeatureGroup( + const std::string& label) { + return AddGroup(label, &config_.device_feature_groups); +} + +PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::AddGlTextureGroup( + const std::string& label) { + return AddGroup(label, &config_.gl_texture_groups); +} + +PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::AddAndroidSdk( + std::string label, int min_sdk) { + config_.android_sdks[label] = AndroidSdk::ForMinSdk(min_sdk); + return *this; +} + +PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::AddArtifact( + configuration::ConfiguredArtifact artifact) { + config_.artifacts.push_back(std::move(artifact)); + return *this; +} + +configuration::PostProcessingConfiguration PostProcessingConfigurationBuilder::Build() { + return config_; +} + } // namespace test } // namespace aapt diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h index 2f83b78ffb2ab..72faa55392e41 100644 --- a/tools/aapt2/test/Builders.h +++ b/tools/aapt2/test/Builders.h @@ -160,6 +160,7 @@ class ArtifactBuilder { ArtifactBuilder() = default; ArtifactBuilder& SetName(const std::string& name); + ArtifactBuilder& SetVersion(int version); ArtifactBuilder& AddAbi(configuration::Abi abi); ArtifactBuilder& AddDensity(const ConfigDescription& density); ArtifactBuilder& AddLocale(const ConfigDescription& locale); @@ -167,9 +168,41 @@ class ArtifactBuilder { configuration::OutputArtifact Build(); private: + DISALLOW_COPY_AND_ASSIGN(ArtifactBuilder); + configuration::OutputArtifact artifact_; }; +class PostProcessingConfigurationBuilder { + public: + PostProcessingConfigurationBuilder() = default; + + PostProcessingConfigurationBuilder& AddAbiGroup(const std::string& label, + std::vector abis = {}); + PostProcessingConfigurationBuilder& AddDensityGroup(const std::string& label, + std::vector densities = {}); + PostProcessingConfigurationBuilder& AddLocaleGroup(const std::string& label, + std::vector locales = {}); + PostProcessingConfigurationBuilder& AddDeviceFeatureGroup(const std::string& label); + PostProcessingConfigurationBuilder& AddGlTextureGroup(const std::string& label); + PostProcessingConfigurationBuilder& AddAndroidSdk(std::string label, int min_sdk); + PostProcessingConfigurationBuilder& AddArtifact(configuration::ConfiguredArtifact artrifact); + + configuration::PostProcessingConfiguration Build(); + + private: + template + inline PostProcessingConfigurationBuilder& AddGroup(const std::string& label, + configuration::Group* group, + std::vector to_add = {}) { + auto& values = GetOrCreateGroup(label, group); + values.insert(std::begin(values), std::begin(to_add), std::end(to_add)); + return *this; + } + + configuration::PostProcessingConfiguration config_; +}; + } // namespace test } // namespace aapt