Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d14c826c authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I0face862,If2d091e5,I4e18e63f

* changes:
  AAPT2: Sort artifacts based on the Play Store rules.
  AAPT2: Allow empty group definitions
  AAPT2: Get list of multi-APK artifacts without APK file
parents 1f661c30 78c43d7b
Loading
Loading
Loading
Loading
+35 −34
Original line number Diff line number Diff line
@@ -377,44 +377,10 @@ int Optimize(const std::vector<StringPiece>& args) {
  }

  const std::string& apk_path = flags.GetArgs()[0];
  std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(apk_path, context.GetDiagnostics());
  if (!apk) {
    return 1;
  }

  context.SetVerbose(verbose);
  IDiagnostics* diag = context.GetDiagnostics();

  if (target_densities) {
    // Parse the target screen densities.
    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());
    }
  }

  std::unique_ptr<IConfigFilter> filter;
  if (!configs.empty()) {
    filter = ParseConfigFilterParameters(configs, diag);
    if (filter == nullptr) {
      return 1;
    }
    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())) {
      return 1;
    }
  }

  if (config_path) {
    std::string& path = config_path.value();
    Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
@@ -456,6 +422,41 @@ int Optimize(const std::vector<StringPiece>& args) {
    return 1;
  }

  std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(apk_path, context.GetDiagnostics());
  if (!apk) {
    return 1;
  }

  if (target_densities) {
    // Parse the target screen densities.
    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());
    }
  }

  std::unique_ptr<IConfigFilter> filter;
  if (!configs.empty()) {
    filter = ParseConfigFilterParameters(configs, diag);
    if (filter == nullptr) {
      return 1;
    }
    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())) {
      return 1;
    }
  }

  if (options.table_flattener_options.collapse_key_stringpool) {
    if (whitelist_path) {
      std::string& path = whitelist_path.value();
+184 −147
Original line number Diff line number Diff line
@@ -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<std::string>& name, const Group<T>& 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<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
                                                        IDiagnostics* diag) {
  StringInputStream in(contents);
  std::unique_ptr<xml::XmlResource> 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<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
                                       const std::string& apk_name,
@@ -302,11 +249,11 @@ Maybe<OutputArtifact> 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<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,

namespace configuration {

/** Returns the binary reprasentation of the XML configuration. */
Maybe<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
                                                        const std::string& config_path,
                                                        IDiagnostics* diag) {
  StringInputStream in(contents);
  std::unique_ptr<xml::XmlResource> 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<size_t>(abi));
}
@@ -383,7 +388,7 @@ Maybe<std::string> 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> 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<std::vector<OutputArtifact>> ConfigurationParser::Parse(
    const android::StringPiece& apk_path) {
  Maybe<PostProcessingConfiguration> maybe_config = ExtractConfiguration(contents_, diag_);
  Maybe<PostProcessingConfiguration> 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<ConfiguredArtifact> 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<OutputArtifact> 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<OutputArtifact> 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<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") {
      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,9 +509,19 @@ 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.
  if (root_element->GetChildElements().empty()) {
    auto abi = kStringToAbiMap.find(label);
    if (abi == kStringToAbiMap.end()) {
      return false;
    }
    group.push_back(abi->second);
    return true;
  }

  for (auto* child : root_element->GetChildElements()) {
    if (child->name != "abi") {
      diag->Error(DiagMessage() << "Unexpected element in ABI group: " << child->name);
@@ -534,7 +530,13 @@ bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_eleme
      for (auto& node : child->children) {
        xml::Text* t;
        if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
          group.push_back(kStringToAbiMap.at(TrimWhitespace(t->text).to_string()));
          auto abi = kStringToAbiMap.find(TrimWhitespace(t->text).to_string());
          if (abi != kStringToAbiMap.end()) {
            group.push_back(abi->second);
          } else {
            diag->Error(DiagMessage() << "Could not parse ABI value: " << t->text);
            valid = false;
          }
          break;
        }
      }
@@ -551,9 +553,28 @@ 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.
  if (root_element->GetChildElements().empty()) {
    ConfigDescription config_descriptor;
    bool parsed = ConfigDescription::Parse(label, &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 {
      diag->Error(DiagMessage()
                      << "Could not parse config descriptor for empty screen-density-group: "
                      << label);
      valid = false;
    }

    return valid;
  }

  for (auto* child : root_element->GetChildElements()) {
    if (child->name != "screen-density") {
      diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
@@ -592,9 +613,28 @@ 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.
  if (root_element->GetChildElements().empty()) {
    ConfigDescription config_descriptor;
    bool parsed = ConfigDescription::Parse(label, &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 empty screen-density-group: "
                      << label);
      valid = false;
    }

    return valid;
  }

  for (auto* child : root_element->GetChildElements()) {
    if (child->name != "locale") {
      diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
@@ -626,44 +666,48 @@ bool LocaleGroupTagHandler(PostProcessingConfiguration* config, Element* root_el
  return valid;
};

bool AndroidSdkGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
bool AndroidSdkTagHandler(PostProcessingConfiguration* config, Element* root_element,
                          IDiagnostics* diag) {
  std::string label = GetLabel(root_element, diag);
  if (label.empty()) {
    return false;
  }

  AndroidSdk entry = AndroidSdk::ForMinSdk(-1);
  bool valid = true;
  bool found = false;

  for (auto* child : root_element->GetChildElements()) {
    if (child->name != "android-sdk") {
      diag->Error(DiagMessage() << "Unexpected root_element in ABI group: " << child->name);
      valid = false;
    } else {
      AndroidSdk entry;
      for (const auto& attr : child->attributes) {
        Maybe<int>* target = nullptr;
        if (attr.name == "minSdkVersion") {
          target = &entry.min_sdk_version;
  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<int> version = ResourceUtils::ParseSdkVersion(attr.value);
      if (version) {
        valid_attr = true;
        entry.min_sdk_version = version.value();
      }
    } else if (attr.name == "targetSdkVersion") {
          target = &entry.target_sdk_version;
      Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
      if (version) {
        valid_attr = true;
        entry.target_sdk_version = version;
      }
    } else if (attr.name == "maxSdkVersion") {
          target = &entry.max_sdk_version;
        } else {
          diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value);
          continue;
      Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
      if (version) {
        valid_attr = true;
        entry.max_sdk_version = version;
      }
    }

        *target = ResourceUtils::ParseSdkVersion(attr.value);
        if (!*target) {
    if (!valid_attr) {
      diag->Error(DiagMessage() << "Invalid attribute: " << attr.name << " = " << attr.value);
      valid = false;
    }
  }

  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 : child->GetChildElements()) {
  for (auto node : root_element->GetChildElements()) {
    if (node->name == "manifest") {
      if (entry.manifest) {
        diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
@@ -673,14 +717,7 @@ bool AndroidSdkGroupTagHandler(PostProcessingConfiguration* config, Element* roo
    }
  }

      config->android_sdk_groups[label] = entry;
      if (found) {
        valid = false;
      }
      found = true;
    }
  }

  config->android_sdks[entry.label] = entry;
  return valid;
};

@@ -691,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;
@@ -734,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()) {
+14 −7
Original line number Diff line number Diff line
@@ -71,7 +71,8 @@ struct AndroidManifest {
};

struct AndroidSdk {
  Maybe<int> min_sdk_version;
  std::string label;
  int min_sdk_version;  // min_sdk_version is mandatory if splitting by SDK.
  Maybe<int> target_sdk_version;
  Maybe<int> max_sdk_version;
  Maybe<AndroidManifest> manifest;
@@ -113,15 +114,19 @@ struct OutputArtifact {
  Maybe<AndroidSdk> android_sdk;
  std::vector<DeviceFeature> features;
  std::vector<GlTexture> 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<ConfigurationParser> 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_;
};
+133 −23

File changed.

Preview size limit exceeded, changes collapsed.

+323 −177

File changed.

Preview size limit exceeded, changes collapsed.

Loading