diff options
Diffstat (limited to 'tools/aapt2/configuration/ConfigurationParser.cpp')
-rw-r--r-- | tools/aapt2/configuration/ConfigurationParser.cpp | 185 |
1 files changed, 154 insertions, 31 deletions
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp index bdccf8bcae3a..b99240f0a40a 100644 --- a/tools/aapt2/configuration/ConfigurationParser.cpp +++ b/tools/aapt2/configuration/ConfigurationParser.cpp @@ -27,9 +27,11 @@ #include "ConfigDescription.h" #include "Diagnostics.h" +#include "ResourceUtils.h" #include "io/File.h" #include "io/FileSystem.h" -#include "io/StringInputStream.h" +#include "io/StringStream.h" +#include "util/Files.h" #include "util/Maybe.h" #include "util/Util.h" #include "xml/XmlActionExecutor.h" @@ -58,6 +60,7 @@ using ::aapt::xml::XmlActionExecutor; using ::aapt::xml::XmlActionExecutorPolicy; using ::aapt::xml::XmlNodeAction; using ::android::base::ReadFileToString; +using ::android::StringPiece; const std::unordered_map<std::string, Abi> kStringToAbiMap = { {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a}, @@ -116,56 +119,141 @@ const std::string& AbiToString(Abi abi) { * success, or false if the either the placeholder is not found in the name, or the value is not * present and the placeholder was. */ -static bool ReplacePlaceholder(const std::string& placeholder, const Maybe<std::string>& value, +static bool ReplacePlaceholder(const StringPiece& placeholder, const Maybe<StringPiece>& value, std::string* name, IDiagnostics* diag) { - size_t offset = name->find(placeholder); - if (value) { - if (offset == std::string::npos) { + size_t offset = name->find(placeholder.data()); + bool found = (offset != std::string::npos); + + // Make sure the placeholder was present if the desired value is present. + if (!found) { + if (value) { diag->Error(DiagMessage() << "Missing placeholder for artifact: " << placeholder); return false; } - name->replace(offset, placeholder.length(), value.value()); return true; } + DCHECK(found) << "Missing return path for placeholder not found"; + // Make sure the placeholder was not present if the desired value was not present. - bool result = (offset == std::string::npos); - if (!result) { + if (!value) { diag->Error(DiagMessage() << "Placeholder present but no value for artifact: " << placeholder); + return false; + } + + name->replace(offset, placeholder.length(), value.value().data()); + + // Make sure there was only one instance of the placeholder. + if (name->find(placeholder.data()) != std::string::npos) { + diag->Error(DiagMessage() << "Placeholder present multiple times: " << placeholder); + return false; + } + return true; +} + +/** + * Returns the common artifact base name from a template string. + */ +Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, IDiagnostics* diag) { + const StringPiece ext = file::GetExtension(apk_name); + size_t end_index = apk_name.to_string().rfind(ext.to_string()); + const std::string base_name = + (end_index != std::string::npos) ? std::string{apk_name.begin(), end_index} : ""; + + // Base name is optional. + if (result.find("${basename}") != std::string::npos) { + Maybe<StringPiece> maybe_base_name = + base_name.empty() ? Maybe<StringPiece>{} : Maybe<StringPiece>{base_name}; + if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) { + return {}; + } } + + // Extension is optional. + if (result.find("${ext}") != std::string::npos) { + // Make sure we disregard the '.' in the extension when replacing the placeholder. + if (!ReplacePlaceholder("${ext}", {ext.substr(1)}, &result, diag)) { + return {}; + } + } else { + // If no extension is specified, and the name template does not end in the current extension, + // add the existing extension. + if (!util::EndsWith(result, ext)) { + result.append(ext.to_string()); + } + } + return result; } -Maybe<std::string> Artifact::ToArtifactName(const std::string& format, IDiagnostics* diag) const { - std::string result = format; +Maybe<std::string> Artifact::ToArtifactName(const StringPiece& format, const StringPiece& apk_name, + IDiagnostics* diag) const { + Maybe<std::string> base = ToBaseName(format.to_string(), apk_name, diag); + if (!base) { + return {}; + } + std::string result = std::move(base.value()); - if (!ReplacePlaceholder("{abi}", abi_group, &result, diag)) { + if (!ReplacePlaceholder("${abi}", abi_group, &result, diag)) { return {}; } - if (!ReplacePlaceholder("{density}", screen_density_group, &result, diag)) { + if (!ReplacePlaceholder("${density}", screen_density_group, &result, diag)) { return {}; } - if (!ReplacePlaceholder("{locale}", locale_group, &result, diag)) { + if (!ReplacePlaceholder("${locale}", locale_group, &result, diag)) { return {}; } - if (!ReplacePlaceholder("{sdk}", android_sdk_group, &result, diag)) { + if (!ReplacePlaceholder("${sdk}", android_sdk_group, &result, diag)) { return {}; } - if (!ReplacePlaceholder("{feature}", device_feature_group, &result, diag)) { + if (!ReplacePlaceholder("${feature}", device_feature_group, &result, diag)) { return {}; } - if (!ReplacePlaceholder("{gl}", gl_texture_group, &result, diag)) { + if (!ReplacePlaceholder("${gl}", gl_texture_group, &result, diag)) { return {}; } return result; } +Maybe<std::string> Artifact::Name(const StringPiece& apk_name, IDiagnostics* diag) const { + if (!name) { + return {}; + } + + return ToBaseName(name.value(), apk_name, diag); +} + +bool PostProcessingConfiguration::AllArtifactNames(const StringPiece& apk_name, + std::vector<std::string>* artifact_names, + IDiagnostics* diag) const { + for (const auto& artifact : artifacts) { + Maybe<std::string> name; + if (artifact.name) { + name = artifact.Name(apk_name, diag); + } else { + if (!artifact_format) { + diag->Error(DiagMessage() << "No global artifact template and an artifact name is missing"); + return false; + } + name = artifact.ToArtifactName(artifact_format.value(), apk_name, diag); + } + + if (!name) { + return false; + } + + artifact_names->push_back(std::move(name.value())); + } + + return true; +} + } // namespace configuration /** Returns a ConfigurationParser for the file located at the provided path. */ @@ -242,15 +330,32 @@ Maybe<PostProcessingConfiguration> ConfigurationParser::Parse() { // 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. + // 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<Artifact>& 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 {}; + } + return {config}; } ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ = [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool { + // 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; + Artifact artifact{}; + Maybe<int> 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") { @@ -268,6 +373,9 @@ ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ = << attr.value); } } + + artifact.version = (version) ? version.value() : current_version + 1; + config->artifacts.push_back(artifact); return true; }; @@ -333,7 +441,10 @@ ConfigurationParser::ActionHandler ConfigurationParser::screen_density_group_han if ((t = NodeCast<xml::Text>(node.get())) != nullptr) { ConfigDescription config_descriptor; const android::StringPiece& text = TrimWhitespace(t->text); - if (ConfigDescription::Parse(text, &config_descriptor)) { + bool parsed = ConfigDescription::Parse(text, &config_descriptor); + if (parsed && + (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) == + android::ResTable_config::CONFIG_DENSITY)) { // Copy the density with the minimum SDK version stripped out. group.push_back(config_descriptor.CopyWithoutSdkVersion()); } else { @@ -366,17 +477,25 @@ ConfigurationParser::ActionHandler ConfigurationParser::locale_group_handler_ = << child->name); valid = false; } else { - Locale entry; - for (const auto& attr : child->attributes) { - if (attr.name == "lang") { - entry.lang = {attr.value}; - } else if (attr.name == "region") { - entry.region = {attr.value}; - } else { - diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value); + for (auto& node : child->children) { + xml::Text* t; + if ((t = NodeCast<xml::Text>(node.get())) != nullptr) { + ConfigDescription config_descriptor; + const android::StringPiece& text = TrimWhitespace(t->text); + bool parsed = ConfigDescription::Parse(text, &config_descriptor); + if (parsed && + (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) == + android::ResTable_config::CONFIG_LOCALE)) { + // Copy the locale with the minimum SDK version stripped out. + group.push_back(config_descriptor.CopyWithoutSdkVersion()); + } else { + diag->Error(DiagMessage() + << "Could not parse config descriptor for screen-density: " << text); + valid = false; + } + break; } } - group.push_back(entry); } } @@ -390,8 +509,8 @@ ConfigurationParser::ActionHandler ConfigurationParser::android_sdk_group_handle return false; } - auto& group = config->android_sdk_groups[label]; bool valid = true; + bool found = false; for (auto* child : root_element->GetChildElements()) { if (child->name != "android-sdk") { @@ -401,11 +520,11 @@ ConfigurationParser::ActionHandler ConfigurationParser::android_sdk_group_handle AndroidSdk entry; for (const auto& attr : child->attributes) { if (attr.name == "minSdkVersion") { - entry.min_sdk_version = {attr.value}; + entry.min_sdk_version = ResourceUtils::ParseSdkVersion(attr.value); } else if (attr.name == "targetSdkVersion") { - entry.target_sdk_version = {attr.value}; + entry.target_sdk_version = ResourceUtils::ParseSdkVersion(attr.value); } else if (attr.name == "maxSdkVersion") { - entry.max_sdk_version = {attr.value}; + entry.max_sdk_version = ResourceUtils::ParseSdkVersion(attr.value); } else { diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value); } @@ -422,7 +541,11 @@ ConfigurationParser::ActionHandler ConfigurationParser::android_sdk_group_handle } } - group.push_back(entry); + config->android_sdk_groups[label] = entry; + if (found) { + valid = false; + } + found = true; } } |