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

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

Merge "AAPT2: Multi APK generator by version"

parents e341d061 efe45392
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -194,8 +194,10 @@ class OptimizeCommand {

    if (options_.configuration && options_.output_dir) {
      MultiApkGenerator generator{apk.get(), context_};
      if (!generator.FromBaseApk(options_.output_dir.value(), options_.configuration.value(),
                                 options_.table_flattener_options)) {
      MultiApkGeneratorOptions generator_options = {options_.output_dir.value(),
                                                    options_.configuration.value(),
                                                    options_.table_flattener_options};
      if (!generator.FromBaseApk(generator_options)) {
        return 1;
      }
    }
+6 −2
Original line number Diff line number Diff line
@@ -488,8 +488,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") {
@@ -520,7 +520,11 @@ ConfigurationParser::ActionHandler ConfigurationParser::android_sdk_group_handle
        }
      }

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

+11 −1
Original line number Diff line number Diff line
@@ -33,6 +33,10 @@ namespace configuration {
template<class T>
using Group = std::unordered_map<std::string, std::vector<T>>;

/** A mapping of group label to a single configuration item. */
template <class T>
using Entry = std::unordered_map<std::string, T>;

/** Output artifact configuration options. */
struct Artifact {
  /** Name to use for output of processing foo.apk -> foo.<name>.apk. */
@@ -104,6 +108,12 @@ struct AndroidSdk {
  Maybe<std::string> max_sdk_version;
  Maybe<AndroidManifest> manifest;

  static AndroidSdk ForMinSdk(std::string min_sdk) {
    AndroidSdk sdk;
    sdk.min_sdk_version = {std::move(min_sdk)};
    return sdk;
  }

  inline friend bool operator==(const AndroidSdk& lhs, const AndroidSdk& rhs) {
    return lhs.min_sdk_version == rhs.min_sdk_version &&
        lhs.target_sdk_version == rhs.target_sdk_version &&
@@ -134,7 +144,7 @@ struct PostProcessingConfiguration {
  Group<Abi> abi_groups;
  Group<ConfigDescription> screen_density_groups;
  Group<ConfigDescription> locale_groups;
  Group<AndroidSdk> android_sdk_groups;
  Entry<AndroidSdk> android_sdk_groups;
  Group<DeviceFeature> device_feature_groups;
  Group<GlTexture> gl_texture_groups;

+32 −32
Original line number Diff line number Diff line
@@ -74,11 +74,11 @@ constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?>
      <locale>es-rMX</locale>
      <locale>fr-rCA</locale>
    </locale-group>
    <android-sdk-group label="19">
    <android-sdk-group label="v19">
      <android-sdk
          minSdkVersion="19"
          targetSdkVersion="24"
          maxSdkVersion="25">
          minSdkVersion="v19"
          targetSdkVersion="v24"
          maxSdkVersion="v25">
        <manifest>
          <!--- manifest additions here XSLT? TODO -->
        </manifest>
@@ -102,7 +102,7 @@ constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?>
        abi-group="arm"
        screen-density-group="large"
        locale-group="europe"
        android-sdk-group="19"
        android-sdk-group="v19"
        gl-texture-group="dxt1"
        device-feature-group="low-latency"/>
    <artifact
@@ -110,7 +110,7 @@ constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?>
        abi-group="other"
        screen-density-group="alldpi"
        locale-group="north-america"
        android-sdk-group="19"
        android-sdk-group="v19"
        gl-texture-group="dxt1"
        device-feature-group="low-latency"/>
  </artifacts>
@@ -155,7 +155,8 @@ TEST_F(ConfigurationParserTest, ValidateFile) {
  EXPECT_EQ(3ul, value.locale_groups["north-america"].size());

  EXPECT_EQ(1ul, value.android_sdk_groups.size());
  EXPECT_EQ(1ul, value.android_sdk_groups["19"].size());
  EXPECT_TRUE(value.android_sdk_groups["v19"].min_sdk_version);
  EXPECT_EQ("v19", value.android_sdk_groups["v19"].min_sdk_version.value());

  EXPECT_EQ(1ul, value.gl_texture_groups.size());
  EXPECT_EQ(1ul, value.gl_texture_groups["dxt1"].size());
@@ -178,7 +179,7 @@ TEST_F(ConfigurationParserTest, ArtifactAction) {
        abi-group="arm"
        screen-density-group="large"
        locale-group="europe"
        android-sdk-group="19"
        android-sdk-group="v19"
        gl-texture-group="dxt1"
        device-feature-group="low-latency"/>)xml";

@@ -195,7 +196,7 @@ TEST_F(ConfigurationParserTest, ArtifactAction) {
  EXPECT_EQ("arm", artifact.abi_group.value());
  EXPECT_EQ("large", artifact.screen_density_group.value());
  EXPECT_EQ("europe", artifact.locale_group.value());
  EXPECT_EQ("19", artifact.android_sdk_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());

@@ -205,7 +206,7 @@ TEST_F(ConfigurationParserTest, ArtifactAction) {
        abi-group="other"
        screen-density-group="large"
        locale-group="europe"
        android-sdk-group="19"
        android-sdk-group="v19"
        gl-texture-group="dxt1"
        device-feature-group="low-latency"/>)xml";
  doc = test::BuildXmlDom(second);
@@ -318,11 +319,11 @@ TEST_F(ConfigurationParserTest, LocaleGroupAction) {

TEST_F(ConfigurationParserTest, AndroidSdkGroupAction) {
  static constexpr const char* xml = R"xml(
    <android-sdk-group label="19">
    <android-sdk-group label="v19">
      <android-sdk
          minSdkVersion="19"
          targetSdkVersion="24"
          maxSdkVersion="25">
          minSdkVersion="v19"
          targetSdkVersion="v24"
          maxSdkVersion="v25">
        <manifest>
          <!--- manifest additions here XSLT? TODO -->
        </manifest>
@@ -336,18 +337,17 @@ TEST_F(ConfigurationParserTest, AndroidSdkGroupAction) {
  ASSERT_TRUE(ok);

  ASSERT_EQ(1ul, config.android_sdk_groups.size());
  ASSERT_EQ(1u, config.android_sdk_groups.count("19"));
  ASSERT_EQ(1u, config.android_sdk_groups.count("v19"));

  auto& out = config.android_sdk_groups["19"];
  auto& out = config.android_sdk_groups["v19"];

  AndroidSdk sdk;
  sdk.min_sdk_version = std::string("19");
  sdk.target_sdk_version = std::string("24");
  sdk.max_sdk_version = std::string("25");
  sdk.min_sdk_version = std::string("v19");
  sdk.target_sdk_version = std::string("v24");
  sdk.max_sdk_version = std::string("v25");
  sdk.manifest = AndroidManifest();

  ASSERT_EQ(1ul, out.size());
  ASSERT_EQ(sdk, out[0]);
  ASSERT_EQ(sdk, out);
}

TEST_F(ConfigurationParserTest, GlTextureGroupAction) {
@@ -454,41 +454,41 @@ TEST(ArtifactTest, Complex) {
  artifact.device_feature_group = {"df1"};
  artifact.gl_texture_group = {"glx1"};
  artifact.locale_group = {"en-AU"};
  artifact.android_sdk_group = {"26"};
  artifact.android_sdk_group = {"v26"};

  {
    auto result = artifact.ToArtifactName(
        "app.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}.apk", "", &diag);
        "app.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.apk", "", &diag);
    ASSERT_TRUE(result);
    EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
    EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk");
  }

  {
    auto result = artifact.ToArtifactName(
        "app.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}.apk", "app.apk", &diag);
        "app.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.apk", "app.apk", &diag);
    ASSERT_TRUE(result);
    EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
    EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk");
  }

  {
    auto result = artifact.ToArtifactName(
        "${basename}.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}.apk", "app.apk", &diag);
        "${basename}.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.apk", "app.apk", &diag);
    ASSERT_TRUE(result);
    EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
    EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk");
  }

  {
    auto result = artifact.ToArtifactName(
        "app.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}.${ext}", "app.apk", &diag);
        "app.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}.${ext}", "app.apk", &diag);
    ASSERT_TRUE(result);
    EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
    EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk");
  }

  {
    auto result = artifact.ToArtifactName(
        "${basename}.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}", "app.apk", &diag);
        "${basename}.${density}_${locale}_${feature}_${gl}.${sdk}.${abi}", "app.apk", &diag);
    ASSERT_TRUE(result);
    EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
    EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.v26.mips64.apk");
  }
}

+160 −53
Original line number Diff line number Diff line
@@ -26,25 +26,77 @@
#include "filter/AbiFilter.h"
#include "filter/Filter.h"
#include "flatten/Archive.h"
#include "optimize/VersionCollapser.h"
#include "process/IResourceTableConsumer.h"
#include "split/TableSplitter.h"
#include "util/Files.h"

namespace aapt {

using ::aapt::configuration::AndroidSdk;
using ::aapt::configuration::Artifact;
using ::aapt::configuration::PostProcessingConfiguration;
using ::android::StringPiece;

/**
 * Context wrapper that allows the min Android SDK value to be overridden.
 */
class ContextWrapper : public IAaptContext {
 public:
  explicit ContextWrapper(IAaptContext* context)
      : context_(context), min_sdk_(context_->GetMinSdkVersion()) {
  }

  PackageType GetPackageType() override {
    return context_->GetPackageType();
  }

  SymbolTable* GetExternalSymbols() override {
    return context_->GetExternalSymbols();
  }

  IDiagnostics* GetDiagnostics() override {
    return context_->GetDiagnostics();
  }

  const std::string& GetCompilationPackage() override {
    return context_->GetCompilationPackage();
  }

  uint8_t GetPackageId() override {
    return context_->GetPackageId();
  }

  NameMangler* GetNameMangler() override {
    return context_->GetNameMangler();
  }

  bool IsVerbose() override {
    return context_->IsVerbose();
  }

  int GetMinSdkVersion() override {
    return min_sdk_;
  }

  void SetMinSdkVersion(int min_sdk) {
    min_sdk_ = min_sdk;
  }

 private:
  IAaptContext* context_;

  int min_sdk_ = -1;
};

MultiApkGenerator::MultiApkGenerator(LoadedApk* apk, IAaptContext* context)
    : apk_(apk), context_(context) {
}

bool MultiApkGenerator::FromBaseApk(const std::string& out_dir,
                                    const PostProcessingConfiguration& config,
                                    const TableFlattenerOptions& table_flattener_options) {
bool MultiApkGenerator::FromBaseApk(const MultiApkGeneratorOptions& options) {
  // TODO(safarmer): Handle APK version codes for the generated APKs.
  IDiagnostics* diag = context_->GetDiagnostics();
  const PostProcessingConfiguration& config = options.config;

  const std::string& apk_name = file::GetFilename(apk_->GetSource().path).to_string();
  const StringPiece ext = file::GetExtension(apk_name);
@@ -53,8 +105,6 @@ bool MultiApkGenerator::FromBaseApk(const std::string& out_dir,
  // For now, just write out the stripped APK since ABI splitting doesn't modify anything else.
  for (const Artifact& artifact : config.artifacts) {
    FilterChain filters;
    TableSplitterOptions splits;
    AxisConfigFilter axis_filter;

    if (!artifact.name && !config.artifact_format) {
      diag->Error(
@@ -71,16 +121,57 @@ bool MultiApkGenerator::FromBaseApk(const std::string& out_dir,
      return false;
    }

    std::unique_ptr<ResourceTable> table =
        FilterTable(artifact, config, *apk_->GetResourceTable(), &filters);
    if (!table) {
      return false;
    }

    std::string out = options.out_dir;
    if (!file::mkdirs(out)) {
      context_->GetDiagnostics()->Warn(DiagMessage() << "could not create out dir: " << out);
    }
    file::AppendPath(&out, artifact_name.value());

    if (context_->IsVerbose()) {
      context_->GetDiagnostics()->Note(DiagMessage() << "Generating split: " << out);
    }

    std::unique_ptr<IArchiveWriter> writer =
        CreateZipFileArchiveWriter(context_->GetDiagnostics(), out);

    if (context_->IsVerbose()) {
      diag->Note(DiagMessage() << "Writing output: " << out);
    }

    if (!apk_->WriteToArchive(context_, table.get(), options.table_flattener_options, &filters,
                              writer.get())) {
      return false;
    }
  }

  return true;
}

std::unique_ptr<ResourceTable> MultiApkGenerator::FilterTable(
    const configuration::Artifact& artifact,
    const configuration::PostProcessingConfiguration& config, const ResourceTable& old_table,
    FilterChain* filters) {
  TableSplitterOptions splits;
  AxisConfigFilter axis_filter;
  ContextWrapper wrappedContext{context_};

  if (artifact.abi_group) {
    const std::string& group_name = artifact.abi_group.value();

    auto group = config.abi_groups.find(group_name);
    // TODO: Remove validation when configuration parser ensures referential integrity.
    if (group == config.abi_groups.end()) {
        diag->Error(DiagMessage() << "could not find referenced ABI group '" << group_name << "'");
        return false;
      context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced ABI group '"
                                                      << group_name << "'");
      return {};
    }
      filters.AddFilter(AbiFilter::FromAbiList(group->second));
    filters->AddFilter(AbiFilter::FromAbiList(group->second));
  }

  if (artifact.screen_density_group) {
@@ -89,14 +180,15 @@ bool MultiApkGenerator::FromBaseApk(const std::string& out_dir,
    auto group = config.screen_density_groups.find(group_name);
    // TODO: Remove validation when configuration parser ensures referential integrity.
    if (group == config.screen_density_groups.end()) {
        diag->Error(DiagMessage() << "could not find referenced group '" << group_name << "'");
        return false;
      context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced group '"
                                                      << group_name << "'");
      return {};
    }

    const std::vector<ConfigDescription>& densities = group->second;
      std::for_each(densities.begin(), densities.end(), [&](const ConfigDescription& c) {
        splits.preferred_densities.push_back(c.density);
      });
    for(const auto& density_config : densities) {
      splits.preferred_densities.push_back(density_config.density);
    }
  }

  if (artifact.locale_group) {
@@ -104,41 +196,56 @@ bool MultiApkGenerator::FromBaseApk(const std::string& out_dir,
    auto group = config.locale_groups.find(group_name);
    // TODO: Remove validation when configuration parser ensures referential integrity.
    if (group == config.locale_groups.end()) {
        diag->Error(DiagMessage() << "could not find referenced group '" << group_name << "'");
        return false;
      context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced group '"
                                                      << group_name << "'");
      return {};
    }

    const std::vector<ConfigDescription>& locales = group->second;
      std::for_each(locales.begin(), locales.end(),
                    [&](const ConfigDescription& c) { axis_filter.AddConfig(c); });
    for (const auto& locale : locales) {
      axis_filter.AddConfig(locale);
    }
    splits.config_filter = &axis_filter;
  }

    std::unique_ptr<ResourceTable> table = apk_->GetResourceTable()->Clone();


    TableSplitter splitter{{}, splits};
    splitter.SplitTable(table.get());

    std::string out = out_dir;
    if (!file::mkdirs(out)) {
      context_->GetDiagnostics()->Warn(DiagMessage() << "could not create out dir: " << out);
  if (artifact.android_sdk_group) {
    const std::string& group_name = artifact.android_sdk_group.value();
    auto group = config.android_sdk_groups.find(group_name);
    // TODO: Remove validation when configuration parser ensures referential integrity.
    if (group == config.android_sdk_groups.end()) {
      context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced group '"
                                                      << group_name << "'");
      return {};
    }
    file::AppendPath(&out, artifact_name.value());

    std::unique_ptr<IArchiveWriter> writer = CreateZipFileArchiveWriter(diag, out);
    const AndroidSdk& sdk = group->second;
    if (!sdk.min_sdk_version) {
      context_->GetDiagnostics()->Error(DiagMessage()
                                        << "skipping SDK version. No min SDK: " << group_name);
      return {};
    }

    if (context_->IsVerbose()) {
      diag->Note(DiagMessage() << "Writing output: " << out);
    ConfigDescription c;
    const std::string& version = sdk.min_sdk_version.value();
    if (!ConfigDescription::Parse(version, &c)) {
      context_->GetDiagnostics()->Error(DiagMessage() << "could not parse min SDK: " << version);
      return {};
    }

    if (!apk_->WriteToArchive(context_, table.get(), table_flattener_options, &filters,
                              writer.get())) {
      return false;
    wrappedContext.SetMinSdkVersion(c.sdkVersion);
  }

  std::unique_ptr<ResourceTable> table = old_table.Clone();

  VersionCollapser collapser;
  if (!collapser.Consume(context_, table.get())) {
    context_->GetDiagnostics()->Error(DiagMessage() << "Failed to strip versioned resources");
    return {};
  }

  return true;
  TableSplitter splitter{{}, splits};
  splitter.SplitTable(table.get());
  return table;
}

}  // namespace aapt
Loading