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

Commit e85b64e0 authored by Mark Punzalan's avatar Mark Punzalan Committed by Automerger Merge Worker
Browse files

Merge "[aapt2] Parse --feature-flags parameter values" into main am: 21bbfb87

parents 719febf8 21bbfb87
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -2513,6 +2513,28 @@ int LinkCommand::Action(const std::vector<std::string>& args) {
    }
  }

  // Parse the feature flag values. An argument that starts with '@' points to a file to read flag
  // values from.
  std::vector<std::string> all_feature_flags_args;
  for (const std::string& arg : feature_flags_args_) {
    if (util::StartsWith(arg, "@")) {
      const std::string path = arg.substr(1, arg.size() - 1);
      std::string error;
      if (!file::AppendArgsFromFile(path, &all_feature_flags_args, &error)) {
        context.GetDiagnostics()->Error(android::DiagMessage(path) << error);
        return 1;
      }
    } else {
      all_feature_flags_args.push_back(arg);
    }
  }

  for (const std::string& arg : all_feature_flags_args) {
    if (ParseFeatureFlagsParameter(arg, context.GetDiagnostics(), &options_.feature_flag_values)) {
      return 1;
    }
  }

  if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path_) {
    if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path_.value(),
        &options_.stable_id_map)) {
+7 −0
Original line number Diff line number Diff line
@@ -17,11 +17,17 @@
#ifndef AAPT2_LINK_H
#define AAPT2_LINK_H

#include <optional>
#include <regex>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "Command.h"
#include "Resource.h"
#include "androidfw/IDiagnostics.h"
#include "cmd/Util.h"
#include "format/binary/TableFlattener.h"
#include "format/proto/ProtoSerialize.h"
#include "link/ManifestFixer.h"
@@ -72,6 +78,7 @@ struct LinkOptions {
  bool use_sparse_encoding = false;
  std::unordered_set<std::string> extensions_to_not_compress;
  std::optional<std::regex> regex_to_not_compress;
  FeatureFlagValues feature_flag_values;

  // Static lib options.
  bool no_static_lib_packages = false;
+50 −0
Original line number Diff line number Diff line
@@ -113,6 +113,56 @@ std::unique_ptr<IConfigFilter> ParseConfigFilterParameters(const std::vector<std
  return std::move(filter);
}

bool ParseFeatureFlagsParameter(StringPiece arg, android::IDiagnostics* diag,
                                FeatureFlagValues* out_feature_flag_values) {
  if (arg.empty()) {
    return true;
  }

  for (StringPiece flag_and_value : util::Tokenize(arg, ',')) {
    std::vector<std::string> parts = util::Split(flag_and_value, '=');
    if (parts.empty()) {
      continue;
    }

    if (parts.size() > 2) {
      diag->Error(android::DiagMessage()
                  << "Invalid feature flag and optional value '" << flag_and_value
                  << "'. Must be in the format 'flag_name[=true|false]");
      return false;
    }

    StringPiece flag_name = util::TrimWhitespace(parts[0]);
    if (flag_name.empty()) {
      diag->Error(android::DiagMessage() << "No name given for one or more flags in: " << arg);
      return false;
    }

    std::optional<bool> flag_value = {};
    if (parts.size() == 2) {
      StringPiece str_flag_value = util::TrimWhitespace(parts[1]);
      if (!str_flag_value.empty()) {
        flag_value = ResourceUtils::ParseBool(parts[1]);
        if (!flag_value.has_value()) {
          diag->Error(android::DiagMessage() << "Invalid value for feature flag '" << flag_and_value
                                             << "'. Value must be 'true' or 'false'");
          return false;
        }
      }
    }

    if (auto [it, inserted] =
            out_feature_flag_values->try_emplace(std::string(flag_name), flag_value);
        !inserted) {
      // We are allowing the same flag to appear multiple times, last value wins.
      diag->Note(android::DiagMessage()
                 << "Value for feature flag '" << flag_name << "' was given more than once");
      it->second = flag_value;
    }
  }
  return true;
}

// Adjust the SplitConstraints so that their SDK version is stripped if it
// is less than or equal to the minSdk. Otherwise the resources that have had
// their SDK version stripped due to minSdk won't ever match.
+14 −0
Original line number Diff line number Diff line
@@ -17,8 +17,13 @@
#ifndef AAPT_SPLIT_UTIL_H
#define AAPT_SPLIT_UTIL_H

#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <regex>
#include <set>
#include <string>
#include <unordered_set>

#include "AppInfo.h"
@@ -32,6 +37,8 @@

namespace aapt {

using FeatureFlagValues = std::map<std::string, std::optional<bool>, std::less<>>;

// Parses a configuration density (ex. hdpi, xxhdpi, 234dpi, anydpi, etc).
// Returns Nothing and logs a human friendly error message if the string was not legal.
std::optional<uint16_t> ParseTargetDensityParameter(android::StringPiece arg,
@@ -48,6 +55,13 @@ bool ParseSplitParameter(android::StringPiece arg, android::IDiagnostics* diag,
std::unique_ptr<IConfigFilter> ParseConfigFilterParameters(const std::vector<std::string>& args,
                                                           android::IDiagnostics* diag);

// Parses a feature flags parameter, which can contain one or more pairs of flag names and optional
// values, and fills in `out_feature_flag_values` with the parsed values. The pairs in the argument
// are separated by ',' and the name is separated from the value by '=' if there is a value given.
// Example arg: "flag1=true,flag2=false,flag3=,flag4" where flag3 and flag4 have no given value.
bool ParseFeatureFlagsParameter(android::StringPiece arg, android::IDiagnostics* diag,
                                FeatureFlagValues* out_feature_flag_values);

// Adjust the SplitConstraints so that their SDK version is stripped if it
// is less than or equal to the min_sdk. Otherwise the resources that have had
// their SDK version stripped due to min_sdk won't ever match.
+46 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include "util/Files.h"

using ::android::ConfigDescription;
using testing::Pair;
using testing::UnorderedElementsAre;

namespace aapt {
@@ -354,6 +355,51 @@ TEST (UtilTest, ParseSplitParameters) {
  EXPECT_CONFIG_EQ(constraints, expected_configuration);
}

TEST(UtilTest, ParseFeatureFlagsParameter_Empty) {
  auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
  FeatureFlagValues feature_flag_values;
  ASSERT_TRUE(ParseFeatureFlagsParameter("", diagnostics, &feature_flag_values));
  EXPECT_TRUE(feature_flag_values.empty());
}

TEST(UtilTest, ParseFeatureFlagsParameter_TooManyParts) {
  auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
  FeatureFlagValues feature_flag_values;
  ASSERT_FALSE(ParseFeatureFlagsParameter("foo=bar=baz", diagnostics, &feature_flag_values));
}

TEST(UtilTest, ParseFeatureFlagsParameter_NoNameGiven) {
  auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
  FeatureFlagValues feature_flag_values;
  ASSERT_FALSE(ParseFeatureFlagsParameter("foo=true,=false", diagnostics, &feature_flag_values));
}

TEST(UtilTest, ParseFeatureFlagsParameter_InvalidValue) {
  auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
  FeatureFlagValues feature_flag_values;
  ASSERT_FALSE(ParseFeatureFlagsParameter("foo=true,bar=42", diagnostics, &feature_flag_values));
}

TEST(UtilTest, ParseFeatureFlagsParameter_DuplicateFlag) {
  auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
  FeatureFlagValues feature_flag_values;
  ASSERT_TRUE(
      ParseFeatureFlagsParameter("foo=true,bar=true,foo=false", diagnostics, &feature_flag_values));
  EXPECT_THAT(feature_flag_values, UnorderedElementsAre(Pair("foo", std::optional<bool>(false)),
                                                        Pair("bar", std::optional<bool>(true))));
}

TEST(UtilTest, ParseFeatureFlagsParameter_Valid) {
  auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
  FeatureFlagValues feature_flag_values;
  ASSERT_TRUE(ParseFeatureFlagsParameter("foo= true, bar =FALSE,baz=, quux", diagnostics,
                                         &feature_flag_values));
  EXPECT_THAT(feature_flag_values,
              UnorderedElementsAre(Pair("foo", std::optional<bool>(true)),
                                   Pair("bar", std::optional<bool>(false)),
                                   Pair("baz", std::nullopt), Pair("quux", std::nullopt)));
}

TEST (UtilTest, AdjustSplitConstraintsForMinSdk) {
  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();