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

2024-03-25 Thread Cyndy Ishida via cfe-commits

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

Umbrella headers are a concept for Darwin-based libraries. They allow framework 
authors to control the order in which their headers should be parsed and allow 
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. There can be an umbrella header 
per access level. 

>From 9b25b0486d8f3c8409ee199a9f4695da7780e6cb Mon Sep 17 00:00:00 2001
From: Cyndy Ishida 
Date: Mon, 25 Mar 2024 17:12:14 -0400
Subject: [PATCH] [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;
 def warn_glob_did_not_match: Warning<"glob '%0' did not match any header 
file">, InGroup;
+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 00..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/SpecialPrivateU

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

2024-03-25 Thread via cfe-commits

llvmbot wrote:




@llvm/pr-subscribers-clang

Author: Cyndy Ishida (cyndyishida)


Changes

Umbrella headers are a concept for Darwin-based libraries. They allow framework 
authors to control the order in which their headers should be parsed and allow 
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. There can be an umbrella header 
per access level. 

---
Full diff: https://github.com/llvm/llvm-project/pull/86587.diff


7 Files Affected:

- (modified) clang/include/clang/Basic/DiagnosticInstallAPIKinds.td (+1) 
- (modified) clang/include/clang/InstallAPI/HeaderFile.h (+9-3) 
- (added) clang/test/InstallAPI/umbrella-headers.test (+59) 
- (modified) clang/tools/clang-installapi/InstallAPIOpts.td (+12) 
- (modified) clang/tools/clang-installapi/Options.cpp (+74) 
- (modified) clang/tools/clang-installapi/Options.h (+9) 
- (modified) llvm/include/llvm/TextAPI/Utils.h (+3) 


``diff
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;
 def warn_glob_did_not_match: Warning<"glob '%0' did not match any header 
file">, InGroup;
+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 00..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
+
+;--- 

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

2024-03-25 Thread via cfe-commits

github-actions[bot] wrote:



:white_check_mark: With the latest revision this PR passed the Python code 
formatter.

https://github.com/llvm/llvm-project/pull/86587
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-03-25 Thread via cfe-commits

github-actions[bot] wrote:



:white_check_mark: With the latest revision this PR passed the C/C++ code 
formatter.

https://github.com/llvm/llvm-project/pull/86587
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-03-25 Thread Juergen Ributzka via cfe-commits


@@ -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;

ributzka wrote:

Could the `HeaderType` enum be reshuffled so this arithmetic is not required?

https://github.com/llvm/llvm-project/pull/86587
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-03-25 Thread Juergen Ributzka via cfe-commits


@@ -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;

ributzka wrote:

Could a header ever be of an `unknown` type?

https://github.com/llvm/llvm-project/pull/86587
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-03-26 Thread Cyndy Ishida via cfe-commits

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 
Date: Mon, 25 Mar 2024 17:12:14 -0400
Subject: [PATCH 1/2] [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;
 def warn_glob_did_not_match: Warning<"glob '%0' did not match any header 
file">, InGroup;
+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 00..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-N

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

2024-03-26 Thread Cyndy Ishida via cfe-commits


@@ -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;

cyndyishida wrote:

No, added asserts.

https://github.com/llvm/llvm-project/pull/86587
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-03-26 Thread Cyndy Ishida via cfe-commits

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 
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;
 def warn_glob_did_not_match: Warning<"glob '%0' did not match any header 
file">, InGroup;
+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 00..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-N

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

2024-03-26 Thread Juergen Ributzka via cfe-commits


@@ -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;

ributzka wrote:

If the type can never be `unknown`, then would it be possible to completely 
remove it?

https://github.com/llvm/llvm-project/pull/86587
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-03-26 Thread Cyndy Ishida via cfe-commits


@@ -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;

cyndyishida wrote:

Oh sorry, I thought you meant for umbrella headers. In general, it's used as a 
default value. E.g. When non-input header files that are parsed through 
includes, cached then skipped over when picking up declarations during AST 
traversal. 
I think that's generally good to have a concept of null for enum values.

https://github.com/llvm/llvm-project/pull/86587
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-03-26 Thread Cyndy Ishida via cfe-commits

https://github.com/cyndyishida edited 
https://github.com/llvm/llvm-project/pull/86587
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-03-26 Thread Cyndy Ishida via cfe-commits

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 
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;
 def warn_glob_did_not_match: Warning<"glob '%0' did not match any header 
file">, InGroup;
+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 00..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-N

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

2024-03-27 Thread Juergen Ributzka via cfe-commits

https://github.com/ributzka approved this pull request.


https://github.com/llvm/llvm-project/pull/86587
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits