Add header-abi-linker -symbol-tag-policy

The command line option supports the policies to filter llndk, apex
and systemapi.

Usage:
`-api 34 -include-symbol-tag llndk=202404 -symbol-tag-policy MatchTagOnly`
includes the symbols tagged with "llndk=202404", "introduced=34", or
"llndk=202404 introduced=35".

`-api 34 -include-symbol-tag apex -symbol-tag-policy MatchTagAndApi`
includes the symbols tagged with "apex" or "introduced=34", but
excludes those tagged with "apex introduced=35".

Test: LD_LIBRARY_PATH=out/soong/dist/lib64 \
      out/soong/host/linux-x86/nativetest64/header-checker-unittests/header-checker-unittests
Bug: 314010764
Change-Id: I6517e902e2b648bfefedb914caf800cd47408c37
diff --git a/vndk/tools/header-checker/src/linker/header_abi_linker.cpp b/vndk/tools/header-checker/src/linker/header_abi_linker.cpp
index a126777..3ca3b07 100644
--- a/vndk/tools/header-checker/src/linker/header_abi_linker.cpp
+++ b/vndk/tools/header-checker/src/linker/header_abi_linker.cpp
@@ -37,6 +37,7 @@
 
 
 using namespace header_checker;
+using header_checker::repr::ModeTagPolicy;
 using header_checker::repr::TextFormatIR;
 using header_checker::utils::CollectAllExportedHeaders;
 using header_checker::utils::HideIrrelevantCommandLineOptions;
@@ -102,6 +103,18 @@
                    "API levels."),
     llvm::cl::Optional, llvm::cl::cat(header_linker_category));
 
+static llvm::cl::opt<ModeTagPolicy> symbol_tag_policy(
+    "symbol-tag-policy",
+    llvm::cl::desc("Specify how to match -include-symbol-tag."),
+    llvm::cl::values(clEnumValN(ModeTagPolicy::MatchTagAndApi, "MatchTagAndApi",
+                                "If a symbol has mode tags, match both the "
+                                "mode tags and the \"introduced\" tag."),
+                     clEnumValN(ModeTagPolicy::MatchTagOnly, "MatchTagOnly",
+                                "If a symbol has mode tags, match the mode "
+                                "tags and ignore the \"introduced\" tag.")),
+    llvm::cl::init(ModeTagPolicy::MatchTagAndApi),
+    llvm::cl::cat(header_linker_category));
+
 static llvm::cl::opt<std::string> arch(
     "arch", llvm::cl::desc("<arch>"), llvm::cl::Optional,
     llvm::cl::cat(header_linker_category));
@@ -496,6 +509,7 @@
       return false;
     }
   }
+  parser.SetModeTagPolicy(symbol_tag_policy);
 
   return true;
 }
diff --git a/vndk/tools/header-checker/src/repr/symbol/version_script_parser.cpp b/vndk/tools/header-checker/src/repr/symbol/version_script_parser.cpp
index 931064b..2fc9bf4 100644
--- a/vndk/tools/header-checker/src/repr/symbol/version_script_parser.cpp
+++ b/vndk/tools/header-checker/src/repr/symbol/version_script_parser.cpp
@@ -185,6 +185,10 @@
 
 
 bool VersionScriptParser::MatchModeTags(const ParsedTags &tags) {
+  if (included_mode_tags_.empty()) {
+    // Include all tags if the user does not specify the option.
+    return true;
+  }
   for (const auto &mode_tag : tags.mode_tags_) {
     auto included_mode_tag = included_mode_tags_.find(mode_tag.first);
     if (included_mode_tag != included_mode_tags_.end() &&
@@ -216,11 +220,18 @@
     return false;
   }
 
-  if (!included_mode_tags_.empty() && !tags.mode_tags_.empty()) {
-    return MatchModeTags(tags);
+  if (tags.mode_tags_.empty()) {
+    return MatchIntroducedTags(tags);
   }
 
-  return MatchIntroducedTags(tags);
+  switch (mode_tag_policy_) {
+    case MatchTagAndApi:
+      return MatchModeTags(tags) && MatchIntroducedTags(tags);
+    case MatchTagOnly:
+      return MatchModeTags(tags);
+  }
+  // Unreachable
+  return false;
 }
 
 
diff --git a/vndk/tools/header-checker/src/repr/symbol/version_script_parser.h b/vndk/tools/header-checker/src/repr/symbol/version_script_parser.h
index 0f468b5..818450a 100644
--- a/vndk/tools/header-checker/src/repr/symbol/version_script_parser.h
+++ b/vndk/tools/header-checker/src/repr/symbol/version_script_parser.h
@@ -28,6 +28,11 @@
 namespace repr {
 
 
+enum ModeTagPolicy {
+  MatchTagAndApi,
+  MatchTagOnly,
+};
+
 class VersionScriptParser {
  private:
   // This comparison function allows finding elements by string_view.
@@ -89,6 +94,8 @@
   // Returns whether the argument is valid.
   bool AddModeTag(std::string_view mode_tag);
 
+  void SetModeTagPolicy(ModeTagPolicy policy) { mode_tag_policy_ = policy; }
+
   void SetErrorHandler(std::unique_ptr<ErrorHandler> error_handler) {
     error_handler_ = std::move(error_handler);
   }
@@ -133,6 +140,7 @@
   utils::StringSet excluded_symbol_versions_;
   utils::StringSet excluded_symbol_tags_;
   ModeTagLevelMap included_mode_tags_;
+  ModeTagPolicy mode_tag_policy_;
 
   std::istream *stream_;
   int line_no_;
diff --git a/vndk/tools/header-checker/src/repr/symbol/version_script_parser_test.cpp b/vndk/tools/header-checker/src/repr/symbol/version_script_parser_test.cpp
index 41955f3..ed079c8 100644
--- a/vndk/tools/header-checker/src/repr/symbol/version_script_parser_test.cpp
+++ b/vndk/tools/header-checker/src/repr/symbol/version_script_parser_test.cpp
@@ -363,14 +363,11 @@
         always;  # unknown
         api34;  # introduced=34
         api35;  # introduced=35
-        api35_llndk202404;  # introduced=35 llndk=202404
+        llndk202404;  # llndk=202404
+        llndk202504;  # llndk=202504
         systemapi;  # systemapi
         systemapi_llndk;  # systemapi llndk
     };
-    LIBEX_2.0 {  # introduced=36
-        api36_llndk202504;
-        api36_llndk202504;  # llndk=202504
-    };
   )TESTDATA";
 
   {
@@ -384,9 +381,8 @@
 
     const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
 
-    EXPECT_THAT(funcs,
-                ElementsAre(Key("always"), Key("api34"),
-                            Key("api35_llndk202404"), Key("systemapi_llndk")));
+    EXPECT_THAT(funcs, ElementsAre(Key("always"), Key("api34"),
+                                   Key("llndk202404"), Key("systemapi_llndk")));
   }
 
   {
@@ -400,10 +396,9 @@
 
     const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
 
-    EXPECT_THAT(
-        funcs,
-        ElementsAre(Key("always"), Key("api34"), Key("api35_llndk202404"),
-                    Key("api36_llndk202504"), Key("systemapi_llndk")));
+    EXPECT_THAT(funcs,
+                ElementsAre(Key("always"), Key("api34"), Key("llndk202404"),
+                            Key("llndk202504"), Key("systemapi_llndk")));
   }
 
   // Include all mode tags
@@ -417,10 +412,9 @@
 
     const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
 
-    EXPECT_THAT(funcs,
-                ElementsAre(Key("always"), Key("api34"), Key("api35"),
-                            Key("api35_llndk202404"), Key("api36_llndk202504"),
-                            Key("systemapi"), Key("systemapi_llndk")));
+    EXPECT_THAT(funcs, ElementsAre(Key("always"), Key("api34"), Key("api35"),
+                                   Key("llndk202404"), Key("llndk202504"),
+                                   Key("systemapi"), Key("systemapi_llndk")));
   }
 
   // Exclude all mode tags
@@ -435,8 +429,65 @@
 
     const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
 
-    EXPECT_THAT(funcs, ElementsAre(Key("always"), Key("api34"), Key("api35"),
-                                   Key("api36_llndk202504")));
+    EXPECT_THAT(funcs, ElementsAre(Key("always"), Key("api34"), Key("api35")));
+  }
+}
+
+
+TEST(VersionScriptParserTest, SetModeTagPolicy) {
+  static const char testdata[] = R"TESTDATA(
+    LIBEX_1.0 {  # introduced=36
+        api36;
+        api36_llndk202504;
+        api36_llndk202504;  # llndk=202504
+        llndk202504;  # llndk=202504
+    };
+  )TESTDATA";
+
+  {
+    VersionScriptParser parser;
+    parser.SetApiLevel(35);
+    parser.AddModeTag("llndk=202504");
+    parser.SetModeTagPolicy(ModeTagPolicy::MatchTagAndApi);
+
+    std::istringstream stream(testdata);
+    std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
+    ASSERT_TRUE(result);
+
+    const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
+
+    EXPECT_TRUE(funcs.empty());
+  }
+
+  {
+    VersionScriptParser parser;
+    parser.SetApiLevel(35);
+    parser.AddModeTag("llndk=202504");
+    parser.SetModeTagPolicy(ModeTagPolicy::MatchTagOnly);
+
+    std::istringstream stream(testdata);
+    std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
+    ASSERT_TRUE(result);
+
+    const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
+
+    EXPECT_THAT(funcs,
+                ElementsAre(Key("api36_llndk202504"), Key("llndk202504")));
+  }
+
+  {
+    VersionScriptParser parser;
+    parser.SetApiLevel(36);
+    parser.SetModeTagPolicy(ModeTagPolicy::MatchTagAndApi);
+
+    std::istringstream stream(testdata);
+    std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
+    ASSERT_TRUE(result);
+
+    const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
+
+    EXPECT_THAT(funcs, ElementsAre(Key("api36"), Key("api36_llndk202504"),
+                                   Key("llndk202504")));
   }
 }