[clang] [llvm] [InstallAPI] Add *umbrella-header options (PR #86587)

Cyndy Ishida via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 26 21:46:52 PDT 2024


https://github.com/cyndyishida updated https://github.com/llvm/llvm-project/pull/86587

>From 9b25b0486d8f3c8409ee199a9f4695da7780e6cb Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Mon, 25 Mar 2024 17:12:14 -0400
Subject: [PATCH 1/3] [InstallAPI] Add *umbrella-header options

Umbrella headers are a concept for darwin based libraries. They allow
framework authors control the order of which their headers should be
parsed and allows clients to access available headers by including a
single header.

InstallAPI will attempt to find the umbrella based on the name of the
framework. Users can also specify this explicitly by using command line
options specifying the umbrella header by file path.
---
 .../clang/Basic/DiagnosticInstallAPIKinds.td  |  1 +
 clang/include/clang/InstallAPI/HeaderFile.h   | 12 ++-
 clang/test/InstallAPI/umbrella-headers.test   | 59 +++++++++++++++
 .../tools/clang-installapi/InstallAPIOpts.td  | 12 +++
 clang/tools/clang-installapi/Options.cpp      | 74 +++++++++++++++++++
 clang/tools/clang-installapi/Options.h        |  9 +++
 llvm/include/llvm/TextAPI/Utils.h             |  3 +
 7 files changed, 167 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/InstallAPI/umbrella-headers.test

diff --git a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
index 27df731fa28627..d710688fc1fe20 100644
--- a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
+++ b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
@@ -18,6 +18,7 @@ def err_no_output_file: Error<"no output file specified">;
 def err_no_such_header_file : Error<"no such %select{public|private|project}1 header file: '%0'">;
 def warn_no_such_excluded_header_file : Warning<"no such excluded %select{public|private}0 header file: '%1'">, InGroup<InstallAPIViolation>;
 def warn_glob_did_not_match: Warning<"glob '%0' did not match any header file">, InGroup<InstallAPIViolation>;
+def err_no_such_umbrella_header_file : Error<"no such %select{public|private|project}1 umbrella header file: '%0'">;
 } // end of command line category.
 
 let CategoryName = "Verification" in {
diff --git a/clang/include/clang/InstallAPI/HeaderFile.h b/clang/include/clang/InstallAPI/HeaderFile.h
index 235b4da3add840..332cd415b39ae4 100644
--- a/clang/include/clang/InstallAPI/HeaderFile.h
+++ b/clang/include/clang/InstallAPI/HeaderFile.h
@@ -62,6 +62,8 @@ class HeaderFile {
   bool Excluded{false};
   /// Add header file to processing.
   bool Extra{false};
+  /// Specify that header file is the umbrella header for library.
+  bool Umbrella{false};
 
 public:
   HeaderFile() = delete;
@@ -79,17 +81,21 @@ class HeaderFile {
 
   void setExtra(bool V = true) { Extra = V; }
   void setExcluded(bool V = true) { Excluded = V; }
+  void setUmbrellaHeader(bool V = true) { Umbrella = V; }
   bool isExtra() const { return Extra; }
   bool isExcluded() const { return Excluded; }
+  bool isUmbrellaHeader() const { return Umbrella; }
 
   bool useIncludeName() const {
     return Type != HeaderType::Project && !IncludeName.empty();
   }
 
   bool operator==(const HeaderFile &Other) const {
-    return std::tie(Type, FullPath, IncludeName, Language, Excluded, Extra) ==
-           std::tie(Other.Type, Other.FullPath, Other.IncludeName,
-                    Other.Language, Other.Excluded, Other.Extra);
+    return std::tie(Type, FullPath, IncludeName, Language, Excluded, Extra,
+                    Umbrella) == std::tie(Other.Type, Other.FullPath,
+                                          Other.IncludeName, Other.Language,
+                                          Other.Excluded, Other.Extra,
+                                          Other.Umbrella);
   }
 };
 
diff --git a/clang/test/InstallAPI/umbrella-headers.test b/clang/test/InstallAPI/umbrella-headers.test
new file mode 100644
index 00000000000000..b91d3df25b38ec
--- /dev/null
+++ b/clang/test/InstallAPI/umbrella-headers.test
@@ -0,0 +1,59 @@
+; RUN: rm -rf %t
+; RUN: split-file %s %t
+; RUN: sed -e "s|DSTROOT|%/t|g" %t/inputs.json.in > %t/inputs.json
+
+// Try umbrella header flags with different spellings.
+; RUN: clang-installapi --target=arm64-apple-macosx13 \
+; RUN:  -install_name /System/Library/Frameworks/Umbrella2.framework/Versions/A/Umbrella2 \
+; RUN: -ObjC -F%t/Frameworks/ %t/inputs.json \
+; RUN: --public-umbrella-header=%t/Frameworks/Umbrella2.framework/Headers/SpecialUmbrella.h \
+; RUN: -private-umbrella-header \
+; RUN: %t/Frameworks/Umbrella2.framework/PrivateHeaders/SpecialPrivateUmbrella.h \
+; RUN: -o %t/output.tbd 2>&1 | FileCheck -allow-empty %s
+
+; RUN: clang-installapi --target=arm64-apple-macosx13 \
+; RUN: -install_name /System/Library/Frameworks/Umbrella2.framework/Versions/A/Umbrella2 \
+; RUN: -ObjC -F%t/Frameworks/ %t/inputs.json \
+; RUN: --public-umbrella-header=SpecialUmbrella.h \
+; RUN: --private-umbrella-header=SpecialPrivateUmbrella.h \
+; RUN: -o %t/output.tbd 2>&1 | FileCheck -allow-empty %s
+
+; CHECK-NOT: error
+; CHECK-NOT: warning
+
+;--- Frameworks/Umbrella2.framework/Headers/SpecialUmbrella.h
+#define PUBLIC_UMBRELLA_HEADER_FIRST
+
+;--- Frameworks/Umbrella2.framework/Headers/AAA.h
+#ifndef PUBLIC_UMBRELLA_HEADER_FIRST
+#error "Public umbrella header was not included first!"
+#endif
+
+;--- Frameworks/Umbrella2.framework/PrivateHeaders/AAA_Private.h
+#ifndef PRIVATE_UMBRELLA_HEADER_FIRST
+#error "Private umbrella header was not included first!"
+#endif
+
+;--- Frameworks/Umbrella2.framework/PrivateHeaders/SpecialPrivateUmbrella.h
+#define PRIVATE_UMBRELLA_HEADER_FIRST
+
+;--- inputs.json.in
+{
+  "headers": [ {
+    "path" : "DSTROOT/Frameworks/Umbrella2.framework/Headers/AAA.h",
+    "type" : "public"
+  }, 
+  {
+    "path" : "DSTROOT/Frameworks/Umbrella2.framework/Headers/SpecialUmbrella.h",
+    "type" : "public"
+  },
+  {
+    "path" : "DSTROOT/Frameworks/Umbrella2.framework/PrivateHeaders/AAA_Private.h",
+    "type" : "private"
+  },
+  {
+    "path" : "DSTROOT/Frameworks/Umbrella2.framework/PrivateHeaders/SpecialPrivateUmbrella.h",
+    "type" : "private"
+  }],
+  "version": "3"
+}
diff --git a/clang/tools/clang-installapi/InstallAPIOpts.td b/clang/tools/clang-installapi/InstallAPIOpts.td
index ab9e1fe7f2f949..71532c9cf24d17 100644
--- a/clang/tools/clang-installapi/InstallAPIOpts.td
+++ b/clang/tools/clang-installapi/InstallAPIOpts.td
@@ -61,3 +61,15 @@ def exclude_private_header : Separate<["-"], "exclude-private-header">,
   HelpText<"Exclude private header from parsing">;
 def exclude_private_header_EQ : Joined<["--"], "exclude-private-header=">,
   Alias<exclude_private_header>;
+def public_umbrella_header : Separate<["-"], "public-umbrella-header">,
+  MetaVarName<"<path>">, HelpText<"Specify the public umbrella header location">;
+def public_umbrella_header_EQ : Joined<["--"], "public-umbrella-header=">,
+  Alias<public_umbrella_header>;
+def private_umbrella_header : Separate<["-"], "private-umbrella-header">,
+  MetaVarName<"<path>">, HelpText<"Specify the private umbrella header location">;
+def private_umbrella_header_EQ : Joined<["--"], "private-umbrella-header=">,
+  Alias<private_umbrella_header>;
+def project_umbrella_header : Separate<["-"], "project-umbrella-header">,
+  MetaVarName<"<path>">, HelpText<"Specify the project umbrella header location">;
+def project_umbrella_header_EQ : Joined<["--"], "project-umbrella-header=">,
+  Alias<project_umbrella_header>;
diff --git a/clang/tools/clang-installapi/Options.cpp b/clang/tools/clang-installapi/Options.cpp
index 4f79c62724a62d..dcc1924ec4d924 100644
--- a/clang/tools/clang-installapi/Options.cpp
+++ b/clang/tools/clang-installapi/Options.cpp
@@ -270,6 +270,16 @@ Options::processAndFilterOutInstallAPIOptions(ArrayRef<const char *> Args) {
                                  OPT_exclude_project_header))
     return {};
 
+  // Handle umbrella headers.
+  if (const Arg *A = ParsedArgs.getLastArg(OPT_public_umbrella_header))
+    DriverOpts.PublicUmbrellaHeader = A->getValue();
+
+  if (const Arg *A = ParsedArgs.getLastArg(OPT_private_umbrella_header))
+    DriverOpts.PrivateUmbrellaHeader = A->getValue();
+
+  if (const Arg *A = ParsedArgs.getLastArg(OPT_project_umbrella_header))
+    DriverOpts.ProjectUmbrellaHeader = A->getValue();
+
   /// Any unclaimed arguments should be forwarded to the clang driver.
   std::vector<const char *> ClangDriverArgs(ParsedArgs.size());
   for (const Arg *A : ParsedArgs) {
@@ -323,6 +333,15 @@ Options::Options(DiagnosticsEngine &Diag, FileManager *FM,
   }
 }
 
+static const Regex Rule("(.+)/(.+)\\.framework/");
+static StringRef getFrameworkNameFromInstallName(StringRef InstallName) {
+  SmallVector<StringRef, 3> Match;
+  Rule.match(InstallName, &Match);
+  if (Match.empty())
+    return "";
+  return Match.back();
+}
+
 InstallAPIContext Options::createContext() {
   InstallAPIContext Ctx;
   Ctx.FM = FM;
@@ -339,6 +358,11 @@ InstallAPIContext Options::createContext() {
   Ctx.OutputLoc = DriverOpts.OutputPath;
   Ctx.LangMode = FEOpts.LangMode;
 
+  // Attempt to find umbrella headers by capturing framework name.
+  StringRef FrameworkName;
+  if (!LinkerOpts.IsDylib)
+    FrameworkName = getFrameworkNameFromInstallName(LinkerOpts.InstallName);
+
   // Process inputs.
   for (const std::string &ListPath : DriverOpts.FileLists) {
     auto Buffer = FM->getBufferForFile(ListPath);
@@ -424,6 +448,56 @@ InstallAPIContext Options::createContext() {
     if (!Glob->didMatch())
       Diags->Report(diag::warn_glob_did_not_match) << Glob->str();
 
+  // Mark any explicit or inferred umbrella headers. If one exists, move
+  // that to the beginning of the input headers.
+  auto MarkandMoveUmbrellaInHeaders = [&](Regex &Regex,
+                                          HeaderType Type) -> bool {
+    auto It = find_if(Ctx.InputHeaders, [&Regex, Type](const HeaderFile &H) {
+      return (H.getType() == Type) && Regex.match(H.getPath());
+    });
+
+    if (It == Ctx.InputHeaders.end())
+      return false;
+    It->setUmbrellaHeader();
+
+    // Because there can be an umbrella header per header type,
+    // find the first non umbrella header to swap position with.
+    auto BeginPos = find_if(Ctx.InputHeaders, [](const HeaderFile &H) {
+      return !H.isUmbrellaHeader();
+    });
+    if (BeginPos != Ctx.InputHeaders.end() && BeginPos < It)
+      std::swap(*BeginPos, *It);
+    return true;
+  };
+
+  auto FindUmbrellaHeader = [&](StringRef HeaderPath, HeaderType Type) -> bool {
+    if (!HeaderPath.empty()) {
+      auto EscapedString = Regex::escape(HeaderPath);
+      Regex UmbrellaRegex(EscapedString);
+      if (!MarkandMoveUmbrellaInHeaders(UmbrellaRegex, Type)) {
+        Diags->Report(diag::err_no_such_umbrella_header_file)
+            << HeaderPath << (unsigned)Type - 1;
+        return false;
+      }
+    } else if (!FrameworkName.empty() && (Type != HeaderType::Project)) {
+      auto UmbrellaName = "/" + Regex::escape(FrameworkName);
+      if (Type == HeaderType::Public)
+        UmbrellaName += "\\.h";
+      else
+        UmbrellaName += "[_]?Private\\.h";
+      Regex UmbrellaRegex(UmbrellaName);
+      MarkandMoveUmbrellaInHeaders(UmbrellaRegex, Type);
+    }
+    return true;
+  };
+  if (!FindUmbrellaHeader(DriverOpts.PublicUmbrellaHeader,
+                          HeaderType::Public) ||
+      !FindUmbrellaHeader(DriverOpts.PrivateUmbrellaHeader,
+                          HeaderType::Private) ||
+      !FindUmbrellaHeader(DriverOpts.ProjectUmbrellaHeader,
+                          HeaderType::Project))
+    return Ctx;
+
   // Parse binary dylib and initialize verifier.
   if (DriverOpts.DylibToVerify.empty()) {
     Ctx.Verifier = std::make_unique<DylibVerifier>();
diff --git a/clang/tools/clang-installapi/Options.h b/clang/tools/clang-installapi/Options.h
index c18309f693701e..3671e4c8274bd3 100644
--- a/clang/tools/clang-installapi/Options.h
+++ b/clang/tools/clang-installapi/Options.h
@@ -31,6 +31,15 @@ struct DriverOptions {
   /// \brief Path to input file lists (JSON).
   llvm::MachO::PathSeq FileLists;
 
+  /// \brief Path to public umbrella header.
+  std::string PublicUmbrellaHeader;
+
+  /// \brief Path to private umbrella header.
+  std::string PrivateUmbrellaHeader;
+
+  /// \brief Path to project umbrella header.
+  std::string ProjectUmbrellaHeader;
+
   /// \brief Paths of extra public headers.
   PathSeq ExtraPublicHeaders;
 
diff --git a/llvm/include/llvm/TextAPI/Utils.h b/llvm/include/llvm/TextAPI/Utils.h
index 319e0abce25b03..97219272e96535 100644
--- a/llvm/include/llvm/TextAPI/Utils.h
+++ b/llvm/include/llvm/TextAPI/Utils.h
@@ -75,5 +75,8 @@ bool isPrivateLibrary(StringRef Path, bool IsSymLink = false);
 /// \return The equivalent regex rule.
 llvm::Expected<llvm::Regex> createRegexFromGlob(llvm::StringRef Glob);
 
+/// Infer the name of a framework by it's install name.
+StringRef getFrameworkNameFromInstallName(StringRef InstallName);
+
 } // namespace llvm::MachO
 #endif // LLVM_TEXTAPI_UTILS_H

>From 2938b2062893f1a23fc97a861f172606c2649216 Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Tue, 26 Mar 2024 09:16:50 -0400
Subject: [PATCH 2/3] Reorder HeaderType enum

* add failure test too
---
 clang/include/clang/InstallAPI/HeaderFile.h | 4 ++--
 clang/test/InstallAPI/umbrella-headers.test | 9 +++++++++
 clang/tools/clang-installapi/Options.cpp    | 7 ++++---
 3 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/InstallAPI/HeaderFile.h b/clang/include/clang/InstallAPI/HeaderFile.h
index 332cd415b39ae4..c67503d4ad49e9 100644
--- a/clang/include/clang/InstallAPI/HeaderFile.h
+++ b/clang/include/clang/InstallAPI/HeaderFile.h
@@ -24,8 +24,6 @@
 
 namespace clang::installapi {
 enum class HeaderType {
-  /// Unset or unknown type.
-  Unknown,
   /// Represents declarations accessible to all clients.
   Public,
   /// Represents declarations accessible to a disclosed set of clients.
@@ -33,6 +31,8 @@ enum class HeaderType {
   /// Represents declarations only accessible as implementation details to the
   /// input library.
   Project,
+  /// Unset or unknown type.
+  Unknown,
 };
 
 inline StringRef getName(const HeaderType T) {
diff --git a/clang/test/InstallAPI/umbrella-headers.test b/clang/test/InstallAPI/umbrella-headers.test
index b91d3df25b38ec..59bad628e6a878 100644
--- a/clang/test/InstallAPI/umbrella-headers.test
+++ b/clang/test/InstallAPI/umbrella-headers.test
@@ -18,6 +18,15 @@
 ; RUN: --private-umbrella-header=SpecialPrivateUmbrella.h \
 ; RUN: -o %t/output.tbd 2>&1 | FileCheck -allow-empty %s
 
+// Try missing umbrella header argument.
+; RUN: not clang-installapi --target=arm64-apple-macosx13 \
+; RUN: -install_name /System/Library/Frameworks/Umbrella2.framework/Versions/A/Umbrella2 \
+; RUN: -ObjC -F%t/Frameworks/ %t/inputs.json \
+; RUN: --public-umbrella-header=DoesntExistUmbrella.h \
+; RUN: -o %t/output.tbd 2>&1 | FileCheck %s -check-prefix=ERR
+
+; ERR: error: no such public umbrella header file: 'DoesntExistUmbrella.h'
+
 ; CHECK-NOT: error
 ; CHECK-NOT: warning
 
diff --git a/clang/tools/clang-installapi/Options.cpp b/clang/tools/clang-installapi/Options.cpp
index dcc1924ec4d924..5b9581f52454f8 100644
--- a/clang/tools/clang-installapi/Options.cpp
+++ b/clang/tools/clang-installapi/Options.cpp
@@ -381,8 +381,7 @@ InstallAPIContext Options::createContext() {
     assert(Type != HeaderType::Unknown && "Missing header type.");
     for (const StringRef Path : Headers) {
       if (!FM->getOptionalFileRef(Path)) {
-        Diags->Report(diag::err_no_such_header_file)
-            << Path << (unsigned)Type - 1;
+        Diags->Report(diag::err_no_such_header_file) << Path << (unsigned)Type;
         return false;
       }
       SmallString<PATH_MAX> FullPath(Path);
@@ -406,6 +405,7 @@ InstallAPIContext Options::createContext() {
   std::vector<std::unique_ptr<HeaderGlob>> ExcludedHeaderGlobs;
   std::set<FileEntryRef> ExcludedHeaderFiles;
   auto ParseGlobs = [&](const PathSeq &Paths, HeaderType Type) {
+    assert(Type != HeaderType::Unknown && "Missing header type.");
     for (const StringRef Path : Paths) {
       auto Glob = HeaderGlob::create(Path, Type);
       if (Glob)
@@ -471,12 +471,13 @@ InstallAPIContext Options::createContext() {
   };
 
   auto FindUmbrellaHeader = [&](StringRef HeaderPath, HeaderType Type) -> bool {
+    assert(Type != HeaderType::Unknown && "Missing header type.");
     if (!HeaderPath.empty()) {
       auto EscapedString = Regex::escape(HeaderPath);
       Regex UmbrellaRegex(EscapedString);
       if (!MarkandMoveUmbrellaInHeaders(UmbrellaRegex, Type)) {
         Diags->Report(diag::err_no_such_umbrella_header_file)
-            << HeaderPath << (unsigned)Type - 1;
+            << HeaderPath << (unsigned)Type;
         return false;
       }
     } else if (!FrameworkName.empty() && (Type != HeaderType::Project)) {

>From 2c79987a645535272631fc68fd7b3bfe03d2db3d Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Tue, 26 Mar 2024 15:44:13 -0400
Subject: [PATCH 3/3] Rework test

The regex/glob input logic doesn't quite work well with windows. Mark
the test as unsupported for now.
Additonally, improve the error message for missing umbrella headers to
report that its missing as general input.
---
 .../clang/Basic/DiagnosticInstallAPIKinds.td  |  2 +-
 .../Umbrella/Umbrella.framework/Headers/AAA.h |  3 ++
 .../Headers/SpecialUmbrella.h                 |  1 +
 .../PrivateHeaders/AAA_Private.h              |  3 ++
 .../PrivateHeaders/SpecialPrivateUmbrella.h   |  1 +
 .../InstallAPI/umbrella-headers-unix.test     | 40 +++++++++++++++++
 clang/test/InstallAPI/umbrella-headers.test   | 44 +++++--------------
 clang/tools/clang-installapi/Options.cpp      |  2 +-
 8 files changed, 62 insertions(+), 34 deletions(-)
 create mode 100644 clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/Headers/AAA.h
 create mode 100644 clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/Headers/SpecialUmbrella.h
 create mode 100644 clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/PrivateHeaders/AAA_Private.h
 create mode 100644 clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/PrivateHeaders/SpecialPrivateUmbrella.h
 create mode 100644 clang/test/InstallAPI/umbrella-headers-unix.test

diff --git a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
index d710688fc1fe20..e3263fe9ccb9d4 100644
--- a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
+++ b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
@@ -18,7 +18,7 @@ def err_no_output_file: Error<"no output file specified">;
 def err_no_such_header_file : Error<"no such %select{public|private|project}1 header file: '%0'">;
 def warn_no_such_excluded_header_file : Warning<"no such excluded %select{public|private}0 header file: '%1'">, InGroup<InstallAPIViolation>;
 def warn_glob_did_not_match: Warning<"glob '%0' did not match any header file">, InGroup<InstallAPIViolation>;
-def err_no_such_umbrella_header_file : Error<"no such %select{public|private|project}1 umbrella header file: '%0'">;
+def err_no_such_umbrella_header_file : Error<"%select{public|private|project}1 umbrella header file not found in input: '%0'">;
 } // end of command line category.
 
 let CategoryName = "Verification" in {
diff --git a/clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/Headers/AAA.h b/clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/Headers/AAA.h
new file mode 100644
index 00000000000000..993d5d4abadb8e
--- /dev/null
+++ b/clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/Headers/AAA.h
@@ -0,0 +1,3 @@
+#ifndef PUBLIC_UMBRELLA_HEADER_FIRST
+#error "Public umbrella header was not included first!"
+#endif
diff --git a/clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/Headers/SpecialUmbrella.h b/clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/Headers/SpecialUmbrella.h
new file mode 100644
index 00000000000000..2599ff14ae1723
--- /dev/null
+++ b/clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/Headers/SpecialUmbrella.h
@@ -0,0 +1 @@
+#define PUBLIC_UMBRELLA_HEADER_FIRST
diff --git a/clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/PrivateHeaders/AAA_Private.h b/clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/PrivateHeaders/AAA_Private.h
new file mode 100644
index 00000000000000..557209bfeb8699
--- /dev/null
+++ b/clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/PrivateHeaders/AAA_Private.h
@@ -0,0 +1,3 @@
+#ifndef PRIVATE_UMBRELLA_HEADER_FIRST
+#error "Private umbrella header was not included first!"
+#endif
diff --git a/clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/PrivateHeaders/SpecialPrivateUmbrella.h b/clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/PrivateHeaders/SpecialPrivateUmbrella.h
new file mode 100644
index 00000000000000..fd5b49b943161a
--- /dev/null
+++ b/clang/test/InstallAPI/Inputs/Umbrella/Umbrella.framework/PrivateHeaders/SpecialPrivateUmbrella.h
@@ -0,0 +1 @@
+#define PRIVATE_UMBRELLA_HEADER_FIRST
diff --git a/clang/test/InstallAPI/umbrella-headers-unix.test b/clang/test/InstallAPI/umbrella-headers-unix.test
new file mode 100644
index 00000000000000..46118779896cf1
--- /dev/null
+++ b/clang/test/InstallAPI/umbrella-headers-unix.test
@@ -0,0 +1,40 @@
+// UNSUPPORTED: system-windows
+
+; RUN: rm -rf %t
+; RUN: split-file %s %t
+; RUN: sed -e "s|DSTROOT|%/t|g" %t/inputs.json.in > %t/inputs.json
+; RUN: mkdir %t/Frameworks/
+; RUN: cp -r %S/Inputs/Umbrella/Umbrella.framework %t/Frameworks/
+
+// Only validate path based input that rely on regex matching on unix based file systems.
+; RUN: clang-installapi --target=arm64-apple-macosx13 \
+; RUN:  -install_name /System/Library/Frameworks/Umbrella2.framework/Versions/A/Umbrella \
+; RUN: -ObjC -F%t/Frameworks/ %t/inputs.json \
+; RUN: --public-umbrella-header=%t/Frameworks/Umbrella.framework/Headers/SpecialUmbrella.h \
+; RUN: -private-umbrella-header \
+; RUN: %t/Frameworks/Umbrella.framework/PrivateHeaders/SpecialPrivateUmbrella.h \
+; RUN: -o %t/output.tbd 2>&1 | FileCheck -allow-empty %s
+
+; CHECK-NOT: error
+; CHECK-NOT: warning
+
+;--- inputs.json.in
+{
+  "headers": [ {
+    "path" : "DSTROOT/Frameworks/Umbrella.framework/Headers/AAA.h",
+    "type" : "public"
+  }, 
+  {
+    "path" : "DSTROOT/Frameworks/Umbrella.framework/Headers/SpecialUmbrella.h",
+    "type" : "public"
+  },
+  {
+    "path" : "DSTROOT/Frameworks/Umbrella.framework/PrivateHeaders/AAA_Private.h",
+    "type" : "private"
+  },
+  {
+    "path" : "DSTROOT/Frameworks/Umbrella.framework/PrivateHeaders/SpecialPrivateUmbrella.h",
+    "type" : "private"
+  }],
+  "version": "3"
+}
diff --git a/clang/test/InstallAPI/umbrella-headers.test b/clang/test/InstallAPI/umbrella-headers.test
index 59bad628e6a878..ce9c50608c4119 100644
--- a/clang/test/InstallAPI/umbrella-headers.test
+++ b/clang/test/InstallAPI/umbrella-headers.test
@@ -1,18 +1,11 @@
 ; RUN: rm -rf %t
 ; RUN: split-file %s %t
 ; RUN: sed -e "s|DSTROOT|%/t|g" %t/inputs.json.in > %t/inputs.json
+; RUN: cp -r %S/Inputs/Umbrella/Umbrella.framework %t/Frameworks/
 
-// Try umbrella header flags with different spellings.
+// Check base filename matches.
 ; RUN: clang-installapi --target=arm64-apple-macosx13 \
-; RUN:  -install_name /System/Library/Frameworks/Umbrella2.framework/Versions/A/Umbrella2 \
-; RUN: -ObjC -F%t/Frameworks/ %t/inputs.json \
-; RUN: --public-umbrella-header=%t/Frameworks/Umbrella2.framework/Headers/SpecialUmbrella.h \
-; RUN: -private-umbrella-header \
-; RUN: %t/Frameworks/Umbrella2.framework/PrivateHeaders/SpecialPrivateUmbrella.h \
-; RUN: -o %t/output.tbd 2>&1 | FileCheck -allow-empty %s
-
-; RUN: clang-installapi --target=arm64-apple-macosx13 \
-; RUN: -install_name /System/Library/Frameworks/Umbrella2.framework/Versions/A/Umbrella2 \
+; RUN: -install_name /System/Library/Frameworks/Umbrella.framework/Versions/A/Umbrella \
 ; RUN: -ObjC -F%t/Frameworks/ %t/inputs.json \
 ; RUN: --public-umbrella-header=SpecialUmbrella.h \
 ; RUN: --private-umbrella-header=SpecialPrivateUmbrella.h \
@@ -20,48 +13,35 @@
 
 // Try missing umbrella header argument.
 ; RUN: not clang-installapi --target=arm64-apple-macosx13 \
-; RUN: -install_name /System/Library/Frameworks/Umbrella2.framework/Versions/A/Umbrella2 \
+; RUN: -install_name /System/Library/Frameworks/Umbrella.framework/Versions/A/Umbrella \
 ; RUN: -ObjC -F%t/Frameworks/ %t/inputs.json \
-; RUN: --public-umbrella-header=DoesntExistUmbrella.h \
+; RUN: --public-umbrella-header=Ignore.h \
 ; RUN: -o %t/output.tbd 2>&1 | FileCheck %s -check-prefix=ERR
 
-; ERR: error: no such public umbrella header file: 'DoesntExistUmbrella.h'
+; ERR: error: public umbrella header file not found in input: 'Ignore.h'
 
 ; CHECK-NOT: error
 ; CHECK-NOT: warning
 
-;--- Frameworks/Umbrella2.framework/Headers/SpecialUmbrella.h
-#define PUBLIC_UMBRELLA_HEADER_FIRST
-
-;--- Frameworks/Umbrella2.framework/Headers/AAA.h
-#ifndef PUBLIC_UMBRELLA_HEADER_FIRST
-#error "Public umbrella header was not included first!"
-#endif
-
-;--- Frameworks/Umbrella2.framework/PrivateHeaders/AAA_Private.h
-#ifndef PRIVATE_UMBRELLA_HEADER_FIRST
-#error "Private umbrella header was not included first!"
-#endif
-
-;--- Frameworks/Umbrella2.framework/PrivateHeaders/SpecialPrivateUmbrella.h
-#define PRIVATE_UMBRELLA_HEADER_FIRST
+;--- Frameworks/Umbrella.framework/Headers/Ignore.h
+#error "This header should be ignored"
 
 ;--- inputs.json.in
 {
   "headers": [ {
-    "path" : "DSTROOT/Frameworks/Umbrella2.framework/Headers/AAA.h",
+    "path" : "DSTROOT/Frameworks/Umbrella.framework/Headers/AAA.h",
     "type" : "public"
   }, 
   {
-    "path" : "DSTROOT/Frameworks/Umbrella2.framework/Headers/SpecialUmbrella.h",
+    "path" : "DSTROOT/Frameworks/Umbrella.framework/Headers/SpecialUmbrella.h",
     "type" : "public"
   },
   {
-    "path" : "DSTROOT/Frameworks/Umbrella2.framework/PrivateHeaders/AAA_Private.h",
+    "path" : "DSTROOT/Frameworks/Umbrella.framework/PrivateHeaders/AAA_Private.h",
     "type" : "private"
   },
   {
-    "path" : "DSTROOT/Frameworks/Umbrella2.framework/PrivateHeaders/SpecialPrivateUmbrella.h",
+    "path" : "DSTROOT/Frameworks/Umbrella.framework/PrivateHeaders/SpecialPrivateUmbrella.h",
     "type" : "private"
   }],
   "version": "3"
diff --git a/clang/tools/clang-installapi/Options.cpp b/clang/tools/clang-installapi/Options.cpp
index 5b9581f52454f8..8e4a1b019fd816 100644
--- a/clang/tools/clang-installapi/Options.cpp
+++ b/clang/tools/clang-installapi/Options.cpp
@@ -450,7 +450,7 @@ InstallAPIContext Options::createContext() {
 
   // Mark any explicit or inferred umbrella headers. If one exists, move
   // that to the beginning of the input headers.
-  auto MarkandMoveUmbrellaInHeaders = [&](Regex &Regex,
+  auto MarkandMoveUmbrellaInHeaders = [&](llvm::Regex &Regex,
                                           HeaderType Type) -> bool {
     auto It = find_if(Ctx.InputHeaders, [&Regex, Type](const HeaderFile &H) {
       return (H.getType() == Type) && Regex.match(H.getPath());



More information about the cfe-commits mailing list