[clang] [InstallAPI] Pick up input headers by directory traversal (PR #94508)

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


@@ -0,0 +1,300 @@
+//===- DirectoryScanner.cpp 
---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/InstallAPI/DirectoryScanner.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/TextAPI/DylibReader.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+
+namespace clang::installapi {
+
+HeaderSeq DirectoryScanner::getHeaders(ArrayRef Libraries) {
+  HeaderSeq Headers;
+  for (const Library  : Libraries)
+llvm::append_range(Headers, Lib.Headers);
+  return Headers;
+}
+
+llvm::Error DirectoryScanner::scan(StringRef Directory) {
+  if (Mode == ScanMode::ScanFrameworks)
+return scanForFrameworks(Directory);
+
+  return scanForUnwrappedLibraries(Directory);
+}
+
+llvm::Error DirectoryScanner::scanForUnwrappedLibraries(StringRef Directory) {
+  // Check some known sub-directory locations.
+  auto GetDirectory = [&](const char *Sub) -> OptionalDirectoryEntryRef {
+SmallString Path(Directory);
+sys::path::append(Path, Sub);
+return FM.getOptionalDirectoryRef(Path);
+  };
+
+  auto DirPublic = GetDirectory("usr/include");
+  auto DirPrivate = GetDirectory("usr/local/include");
+  if (!DirPublic && !DirPrivate) {
+std::error_code ec = std::make_error_code(std::errc::not_a_directory);
+return createStringError(ec,
+ "cannot find any public (usr/include) or private "
+ "(usr/local/include) header directory");
+  }
+
+  Library  = getOrCreateLibrary(Directory, Libraries);
+  Lib.IsUnwrappedDylib = true;
+
+  if (DirPublic)
+if (Error Err = scanHeaders(DirPublic->getName(), Lib, HeaderType::Public,
+Directory))
+  return Err;
+
+  if (DirPrivate)
+if (Error Err = scanHeaders(DirPrivate->getName(), Lib, 
HeaderType::Private,
+Directory))
+  return Err;
+
+  return Error::success();
+}
+
+static bool isFramework(StringRef Path) {
+  while (Path.back() == '/')
+Path = Path.slice(0, Path.size() - 1);
+
+  return llvm::StringSwitch(llvm::sys::path::extension(Path))
+  .Case(".framework", true)
+  .Default(false);
+}
+
+Library &
+DirectoryScanner::getOrCreateLibrary(StringRef Path,
+ std::vector ) const {
+  if (Path.consume_front(RootPath) && Path.empty())
+Path = "/";
+
+  auto LibIt =
+  find_if(Libs, [Path](const Library ) { return L.getPath() == Path; });
+  if (LibIt != Libs.end())
+return *LibIt;
+
+  Libs.emplace_back(Path);
+  return Libs.back();
+}
+
+Error DirectoryScanner::scanHeaders(StringRef Path, Library ,
+HeaderType Type, StringRef BasePath,
+StringRef ParentPath) const {
+  std::error_code ec;
+  auto  = FM.getVirtualFileSystem();
+  PathSeq SubDirectories;
+  for (vfs::directory_iterator i = FS.dir_begin(Path, ec), ie; i != ie;
+   i.increment(ec)) {
+StringRef HeaderPath = i->path();
+if (ec)
+  return createStringError(ec, "unable to read: " + HeaderPath);
+
+if (sys::fs::is_symlink_file(HeaderPath))
+  continue;
+
+// Ignore tmp files from unifdef.
+const StringRef Filename = sys::path::filename(HeaderPath);
+if (Filename.starts_with("."))
+  continue;
+
+// If it is a directory, remember the subdirectory.
+if (FM.getOptionalDirectoryRef(HeaderPath))
+  SubDirectories.push_back(HeaderPath.str());
+
+if (!isHeaderFile(HeaderPath))
+  continue;
+
+// Skip files that do not exist. This usually happens for broken symlinks.
+if (FS.status(HeaderPath) == std::errc::no_such_file_or_directory)
+  continue;
+
+auto IncludeName = createIncludeHeaderName(HeaderPath);
+Lib.addHeaderFile(HeaderPath, Type,
+  IncludeName.has_value() ? IncludeName.value() : "");
+  }
+
+  // Go through the subdirectories.
+  // Sort the sub-directory first since different file systems might have
+  // different traverse order.
+  llvm::sort(SubDirectories);
+  if (ParentPath.empty())
+ParentPath = Path;
+  for (const StringRef Dir : SubDirectories)
+return scanHeaders(Dir, Lib, Type, BasePath, ParentPath);

cyndyishida wrote:

Thanks for reporting! fixed in: https://github.com/llvm/llvm-project/pull/100636

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


[clang] [InstallAPI] Pick up input headers by directory traversal (PR #94508)

2024-07-25 Thread Mike Rice via cfe-commits


@@ -0,0 +1,300 @@
+//===- DirectoryScanner.cpp 
---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#include "clang/InstallAPI/DirectoryScanner.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/TextAPI/DylibReader.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+
+namespace clang::installapi {
+
+HeaderSeq DirectoryScanner::getHeaders(ArrayRef Libraries) {
+  HeaderSeq Headers;
+  for (const Library  : Libraries)
+llvm::append_range(Headers, Lib.Headers);
+  return Headers;
+}
+
+llvm::Error DirectoryScanner::scan(StringRef Directory) {
+  if (Mode == ScanMode::ScanFrameworks)
+return scanForFrameworks(Directory);
+
+  return scanForUnwrappedLibraries(Directory);
+}
+
+llvm::Error DirectoryScanner::scanForUnwrappedLibraries(StringRef Directory) {
+  // Check some known sub-directory locations.
+  auto GetDirectory = [&](const char *Sub) -> OptionalDirectoryEntryRef {
+SmallString Path(Directory);
+sys::path::append(Path, Sub);
+return FM.getOptionalDirectoryRef(Path);
+  };
+
+  auto DirPublic = GetDirectory("usr/include");
+  auto DirPrivate = GetDirectory("usr/local/include");
+  if (!DirPublic && !DirPrivate) {
+std::error_code ec = std::make_error_code(std::errc::not_a_directory);
+return createStringError(ec,
+ "cannot find any public (usr/include) or private "
+ "(usr/local/include) header directory");
+  }
+
+  Library  = getOrCreateLibrary(Directory, Libraries);
+  Lib.IsUnwrappedDylib = true;
+
+  if (DirPublic)
+if (Error Err = scanHeaders(DirPublic->getName(), Lib, HeaderType::Public,
+Directory))
+  return Err;
+
+  if (DirPrivate)
+if (Error Err = scanHeaders(DirPrivate->getName(), Lib, 
HeaderType::Private,
+Directory))
+  return Err;
+
+  return Error::success();
+}
+
+static bool isFramework(StringRef Path) {
+  while (Path.back() == '/')
+Path = Path.slice(0, Path.size() - 1);
+
+  return llvm::StringSwitch(llvm::sys::path::extension(Path))
+  .Case(".framework", true)
+  .Default(false);
+}
+
+Library &
+DirectoryScanner::getOrCreateLibrary(StringRef Path,
+ std::vector ) const {
+  if (Path.consume_front(RootPath) && Path.empty())
+Path = "/";
+
+  auto LibIt =
+  find_if(Libs, [Path](const Library ) { return L.getPath() == Path; });
+  if (LibIt != Libs.end())
+return *LibIt;
+
+  Libs.emplace_back(Path);
+  return Libs.back();
+}
+
+Error DirectoryScanner::scanHeaders(StringRef Path, Library ,
+HeaderType Type, StringRef BasePath,
+StringRef ParentPath) const {
+  std::error_code ec;
+  auto  = FM.getVirtualFileSystem();
+  PathSeq SubDirectories;
+  for (vfs::directory_iterator i = FS.dir_begin(Path, ec), ie; i != ie;
+   i.increment(ec)) {
+StringRef HeaderPath = i->path();
+if (ec)
+  return createStringError(ec, "unable to read: " + HeaderPath);
+
+if (sys::fs::is_symlink_file(HeaderPath))
+  continue;
+
+// Ignore tmp files from unifdef.
+const StringRef Filename = sys::path::filename(HeaderPath);
+if (Filename.starts_with("."))
+  continue;
+
+// If it is a directory, remember the subdirectory.
+if (FM.getOptionalDirectoryRef(HeaderPath))
+  SubDirectories.push_back(HeaderPath.str());
+
+if (!isHeaderFile(HeaderPath))
+  continue;
+
+// Skip files that do not exist. This usually happens for broken symlinks.
+if (FS.status(HeaderPath) == std::errc::no_such_file_or_directory)
+  continue;
+
+auto IncludeName = createIncludeHeaderName(HeaderPath);
+Lib.addHeaderFile(HeaderPath, Type,
+  IncludeName.has_value() ? IncludeName.value() : "");
+  }
+
+  // Go through the subdirectories.
+  // Sort the sub-directory first since different file systems might have
+  // different traverse order.
+  llvm::sort(SubDirectories);
+  if (ParentPath.empty())
+ParentPath = Path;
+  for (const StringRef Dir : SubDirectories)
+return scanHeaders(Dir, Lib, Type, BasePath, ParentPath);

mikerice1969 wrote:

Hi @cyndyishida Our static verification run reports that this loop always exits 
on the first iteration (so it is not really a loop). Is that really what you 
want here? If you only want to scan the first item maybe something other than a 
loop makes sense?

https://github.com/llvm/llvm-project/pull/94508
___
cfe-commits mailing list
cfe-commits@lists.llvm.org

[clang] [InstallAPI] Pick up input headers by directory traversal (PR #94508)

2024-06-14 Thread Cyndy Ishida via cfe-commits

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


[clang] [InstallAPI] Pick up input headers by directory traversal (PR #94508)

2024-06-13 Thread Cyndy Ishida via cfe-commits

cyndyishida wrote:

ping 

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


[clang] [InstallAPI] Pick up input headers by directory traversal (PR #94508)

2024-06-13 Thread Zixu Wang via cfe-commits

https://github.com/zixu-w approved this pull request.


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


[clang] [InstallAPI] Pick up input headers by directory traversal (PR #94508)

2024-06-11 Thread Cyndy Ishida via cfe-commits

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

>From 95cc0c9a2b135706e80a5e73ef5e4257aa89984c Mon Sep 17 00:00:00 2001
From: Cyndy Ishida 
Date: Fri, 10 May 2024 09:19:22 -0700
Subject: [PATCH] [InstallAPI] Pick up input headers by directory traversal

Match TAPI behavior and allow input headers to be resolved via a passed
directory, which is expected to be a library sitting in a build
directory.
---
 .../clang/Basic/DiagnosticInstallAPIKinds.td  |   2 +
 .../clang/InstallAPI/DirectoryScanner.h   |  81 +
 clang/include/clang/InstallAPI/HeaderFile.h   |  13 +
 clang/include/clang/InstallAPI/Library.h  |  65 
 clang/include/clang/InstallAPI/MachO.h|   1 +
 clang/lib/InstallAPI/CMakeLists.txt   |   2 +
 clang/lib/InstallAPI/DirectoryScanner.cpp | 300 ++
 clang/lib/InstallAPI/Library.cpp  |  40 +++
 clang/test/InstallAPI/asm.test|   2 +-
 clang/test/InstallAPI/basic.test  |   4 +-
 clang/test/InstallAPI/binary-attributes.test  |   6 +-
 clang/test/InstallAPI/cpp.test|   4 +-
 clang/test/InstallAPI/diagnostics-dsym.test   |   4 +-
 .../InstallAPI/directory-scanning-dylib.test  |  57 
 .../directory-scanning-frameworks.test|  88 +
 clang/test/InstallAPI/functions.test  |   2 +-
 clang/test/InstallAPI/variables.test  |   2 +-
 clang/tools/clang-installapi/Options.cpp  |  51 ++-
 clang/tools/clang-installapi/Options.h|   3 +
 19 files changed, 703 insertions(+), 24 deletions(-)
 create mode 100644 clang/include/clang/InstallAPI/DirectoryScanner.h
 create mode 100644 clang/include/clang/InstallAPI/Library.h
 create mode 100644 clang/lib/InstallAPI/DirectoryScanner.cpp
 create mode 100644 clang/lib/InstallAPI/Library.cpp
 create mode 100644 clang/test/InstallAPI/directory-scanning-dylib.test
 create mode 100644 clang/test/InstallAPI/directory-scanning-frameworks.test

diff --git a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td 
b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
index cdf27247602f2..e10fa71011f30 100644
--- a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
+++ b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
@@ -26,6 +26,8 @@ def err_unsupported_environment : Error<"environment '%0' is 
not supported: '%1'
 def err_unsupported_os : Error<"os '%0' is not supported: '%1'">;
 def err_cannot_read_input_list : Error<"could not read %0 input list '%1': 
%2">;
 def err_invalid_label: Error<"label '%0' is reserved: use a different label 
name for -X">;
+def err_directory_scanning: Error<"could not read directory '%0': %1">;
+def err_more_than_one_library: Error<"more than one framework/dynamic library 
found">;
 } // end of command line category.
 
 let CategoryName = "Verification" in {
diff --git a/clang/include/clang/InstallAPI/DirectoryScanner.h 
b/clang/include/clang/InstallAPI/DirectoryScanner.h
new file mode 100644
index 0..803328982ec87
--- /dev/null
+++ b/clang/include/clang/InstallAPI/DirectoryScanner.h
@@ -0,0 +1,81 @@
+//===- InstallAPI/DirectoryScanner.h *- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+///
+/// The DirectoryScanner for collecting library files on the file system.
+///
+//===--===//
+#ifndef LLVM_CLANG_INSTALLAPI_DIRECTORYSCANNER_H
+#define LLVM_CLANG_INSTALLAPI_DIRECTORYSCANNER_H
+
+#include "clang/Basic/FileManager.h"
+#include "clang/InstallAPI/Library.h"
+
+namespace clang::installapi {
+
+enum ScanMode {
+  /// Scanning Framework directory.
+  ScanFrameworks,
+  /// Scanning Dylib directory.
+  ScanDylibs,
+};
+
+class DirectoryScanner {
+public:
+  DirectoryScanner(FileManager , ScanMode Mode = ScanMode::ScanFrameworks)
+  : FM(FM), Mode(Mode) {}
+
+  /// Scan for all input files throughout directory.
+  ///
+  /// \param Directory Path of input directory.
+  llvm::Error scan(StringRef Directory);
+
+  /// Take over ownership of stored libraries.
+  std::vector takeLibraries() { return std::move(Libraries); };
+
+  /// Get all the header files in libraries.
+  ///
+  /// \param Libraries Reference of collection of libraries.
+  static HeaderSeq getHeaders(ArrayRef Libraries);
+
+private:
+  /// Collect files for dylibs in usr/(local)/lib within directory.
+  llvm::Error scanForUnwrappedLibraries(StringRef Directory);
+
+  /// Collect files for any frameworks within directory.
+  llvm::Error scanForFrameworks(StringRef Directory);
+
+  /// Get a library from the libraries collection.
+  Library (StringRef Path, std::vector ) 
const;
+
+  /// Collect multiple frameworks from directory.

[clang] [InstallAPI] Pick up input headers by directory traversal (PR #94508)

2024-06-08 Thread Juergen Ributzka via cfe-commits


@@ -97,6 +97,14 @@ class HeaderFile {
   Other.Excluded, Other.Extra,
   Other.Umbrella);
   }
+
+  bool operator<(const HeaderFile ) const {

ributzka wrote:

Including the umbrella header first increases the likelihood of successfully 
parsing all the headers in the framework. Extra headers always go last.

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


[clang] [InstallAPI] Pick up input headers by directory traversal (PR #94508)

2024-06-06 Thread Cyndy Ishida via cfe-commits


@@ -97,6 +97,14 @@ class HeaderFile {
   Other.Excluded, Other.Extra,
   Other.Umbrella);
   }
+
+  bool operator<(const HeaderFile ) const {

cyndyishida wrote:

I could infer, but I don't actually know @ributzka do you remember?

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


[clang] [InstallAPI] Pick up input headers by directory traversal (PR #94508)

2024-06-06 Thread Zixu Wang via cfe-commits


@@ -97,6 +97,14 @@ class HeaderFile {
   Other.Excluded, Other.Extra,
   Other.Umbrella);
   }
+
+  bool operator<(const HeaderFile ) const {

zixu-w wrote:

What's the reasoning for the ordering? Might be easier to understand in the 
future with some comments.

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


[clang] [InstallAPI] Pick up input headers by directory traversal (PR #94508)

2024-06-06 Thread Cyndy Ishida via cfe-commits

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

>From c547d990aca29ecfe6f51d37c5c3f8712dfc5e44 Mon Sep 17 00:00:00 2001
From: Cyndy Ishida 
Date: Fri, 10 May 2024 09:19:22 -0700
Subject: [PATCH 1/2] [InstallAPI] Pick up input headers by directory traversal

Match TAPI behavior and allow input headers to be resolved via a passed
directory, which is expected to be a library sitting in a build
directory.
---
 .../clang/Basic/DiagnosticInstallAPIKinds.td  |   2 +
 .../clang/InstallAPI/DirectoryScanner.h   |  81 +
 clang/include/clang/InstallAPI/HeaderFile.h   |   8 +
 clang/include/clang/InstallAPI/Library.h  |  65 
 clang/include/clang/InstallAPI/MachO.h|   1 +
 clang/lib/InstallAPI/CMakeLists.txt   |   2 +
 clang/lib/InstallAPI/DirectoryScanner.cpp | 300 ++
 clang/lib/InstallAPI/Library.cpp  |  40 +++
 clang/test/InstallAPI/asm.test|   2 +-
 clang/test/InstallAPI/basic.test  |   4 +-
 clang/test/InstallAPI/binary-attributes.test  |   6 +-
 clang/test/InstallAPI/cpp.test|   4 +-
 clang/test/InstallAPI/diagnostics-dsym.test   |   4 +-
 .../InstallAPI/directory-scanning-dylib.test  |  57 
 .../directory-scanning-frameworks.test|  89 ++
 clang/test/InstallAPI/functions.test  |   2 +-
 clang/test/InstallAPI/variables.test  |   2 +-
 clang/tools/clang-installapi/Options.cpp  |  51 ++-
 clang/tools/clang-installapi/Options.h|   3 +
 19 files changed, 699 insertions(+), 24 deletions(-)
 create mode 100644 clang/include/clang/InstallAPI/DirectoryScanner.h
 create mode 100644 clang/include/clang/InstallAPI/Library.h
 create mode 100644 clang/lib/InstallAPI/DirectoryScanner.cpp
 create mode 100644 clang/lib/InstallAPI/Library.cpp
 create mode 100644 clang/test/InstallAPI/directory-scanning-dylib.test
 create mode 100644 clang/test/InstallAPI/directory-scanning-frameworks.test

diff --git a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td 
b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
index cdf27247602f2..e10fa71011f30 100644
--- a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
+++ b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
@@ -26,6 +26,8 @@ def err_unsupported_environment : Error<"environment '%0' is 
not supported: '%1'
 def err_unsupported_os : Error<"os '%0' is not supported: '%1'">;
 def err_cannot_read_input_list : Error<"could not read %0 input list '%1': 
%2">;
 def err_invalid_label: Error<"label '%0' is reserved: use a different label 
name for -X">;
+def err_directory_scanning: Error<"could not read directory '%0': %1">;
+def err_more_than_one_library: Error<"more than one framework/dynamic library 
found">;
 } // end of command line category.
 
 let CategoryName = "Verification" in {
diff --git a/clang/include/clang/InstallAPI/DirectoryScanner.h 
b/clang/include/clang/InstallAPI/DirectoryScanner.h
new file mode 100644
index 0..803328982ec87
--- /dev/null
+++ b/clang/include/clang/InstallAPI/DirectoryScanner.h
@@ -0,0 +1,81 @@
+//===- InstallAPI/DirectoryScanner.h *- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+///
+/// The DirectoryScanner for collecting library files on the file system.
+///
+//===--===//
+#ifndef LLVM_CLANG_INSTALLAPI_DIRECTORYSCANNER_H
+#define LLVM_CLANG_INSTALLAPI_DIRECTORYSCANNER_H
+
+#include "clang/Basic/FileManager.h"
+#include "clang/InstallAPI/Library.h"
+
+namespace clang::installapi {
+
+enum ScanMode {
+  /// Scanning Framework directory.
+  ScanFrameworks,
+  /// Scanning Dylib directory.
+  ScanDylibs,
+};
+
+class DirectoryScanner {
+public:
+  DirectoryScanner(FileManager , ScanMode Mode = ScanMode::ScanFrameworks)
+  : FM(FM), Mode(Mode) {}
+
+  /// Scan for all input files throughout directory.
+  ///
+  /// \param Directory Path of input directory.
+  llvm::Error scan(StringRef Directory);
+
+  /// Take over ownership of stored libraries.
+  std::vector takeLibraries() { return std::move(Libraries); };
+
+  /// Get all the header files in libraries.
+  ///
+  /// \param Libraries Reference of collection of libraries.
+  static HeaderSeq getHeaders(ArrayRef Libraries);
+
+private:
+  /// Collect files for dylibs in usr/(local)/lib within directory.
+  llvm::Error scanForUnwrappedLibraries(StringRef Directory);
+
+  /// Collect files for any frameworks within directory.
+  llvm::Error scanForFrameworks(StringRef Directory);
+
+  /// Get a library from the libraries collection.
+  Library (StringRef Path, std::vector ) 
const;
+
+  /// Collect multiple frameworks from 

[clang] [InstallAPI] Pick up input headers by directory traversal (PR #94508)

2024-06-05 Thread via cfe-commits

llvmbot wrote:




@llvm/pr-subscribers-clang

Author: Cyndy Ishida (cyndyishida)


Changes

Match TAPI behavior and allow input headers to be resolved via a passed 
directory, which is expected to be a library sitting in a build directory.

---

Patch is 34.57 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/94508.diff


19 Files Affected:

- (modified) clang/include/clang/Basic/DiagnosticInstallAPIKinds.td (+2) 
- (added) clang/include/clang/InstallAPI/DirectoryScanner.h (+81) 
- (modified) clang/include/clang/InstallAPI/HeaderFile.h (+8) 
- (added) clang/include/clang/InstallAPI/Library.h (+65) 
- (modified) clang/include/clang/InstallAPI/MachO.h (+1) 
- (modified) clang/lib/InstallAPI/CMakeLists.txt (+2) 
- (added) clang/lib/InstallAPI/DirectoryScanner.cpp (+300) 
- (added) clang/lib/InstallAPI/Library.cpp (+40) 
- (modified) clang/test/InstallAPI/asm.test (+1-1) 
- (modified) clang/test/InstallAPI/basic.test (+2-2) 
- (modified) clang/test/InstallAPI/binary-attributes.test (+4-2) 
- (modified) clang/test/InstallAPI/cpp.test (+2-2) 
- (modified) clang/test/InstallAPI/diagnostics-dsym.test (+2-2) 
- (added) clang/test/InstallAPI/directory-scanning-dylib.test (+57) 
- (added) clang/test/InstallAPI/directory-scanning-frameworks.test (+89) 
- (modified) clang/test/InstallAPI/functions.test (+1-1) 
- (modified) clang/test/InstallAPI/variables.test (+1-1) 
- (modified) clang/tools/clang-installapi/Options.cpp (+38-13) 
- (modified) clang/tools/clang-installapi/Options.h (+3) 


``diff
diff --git a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td 
b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
index cdf27247602f2..e10fa71011f30 100644
--- a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
+++ b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
@@ -26,6 +26,8 @@ def err_unsupported_environment : Error<"environment '%0' is 
not supported: '%1'
 def err_unsupported_os : Error<"os '%0' is not supported: '%1'">;
 def err_cannot_read_input_list : Error<"could not read %0 input list '%1': 
%2">;
 def err_invalid_label: Error<"label '%0' is reserved: use a different label 
name for -X">;
+def err_directory_scanning: Error<"could not read directory '%0': %1">;
+def err_more_than_one_library: Error<"more than one framework/dynamic library 
found">;
 } // end of command line category.
 
 let CategoryName = "Verification" in {
diff --git a/clang/include/clang/InstallAPI/DirectoryScanner.h 
b/clang/include/clang/InstallAPI/DirectoryScanner.h
new file mode 100644
index 0..803328982ec87
--- /dev/null
+++ b/clang/include/clang/InstallAPI/DirectoryScanner.h
@@ -0,0 +1,81 @@
+//===- InstallAPI/DirectoryScanner.h *- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+///
+/// The DirectoryScanner for collecting library files on the file system.
+///
+//===--===//
+#ifndef LLVM_CLANG_INSTALLAPI_DIRECTORYSCANNER_H
+#define LLVM_CLANG_INSTALLAPI_DIRECTORYSCANNER_H
+
+#include "clang/Basic/FileManager.h"
+#include "clang/InstallAPI/Library.h"
+
+namespace clang::installapi {
+
+enum ScanMode {
+  /// Scanning Framework directory.
+  ScanFrameworks,
+  /// Scanning Dylib directory.
+  ScanDylibs,
+};
+
+class DirectoryScanner {
+public:
+  DirectoryScanner(FileManager , ScanMode Mode = ScanMode::ScanFrameworks)
+  : FM(FM), Mode(Mode) {}
+
+  /// Scan for all input files throughout directory.
+  ///
+  /// \param Directory Path of input directory.
+  llvm::Error scan(StringRef Directory);
+
+  /// Take over ownership of stored libraries.
+  std::vector takeLibraries() { return std::move(Libraries); };
+
+  /// Get all the header files in libraries.
+  ///
+  /// \param Libraries Reference of collection of libraries.
+  static HeaderSeq getHeaders(ArrayRef Libraries);
+
+private:
+  /// Collect files for dylibs in usr/(local)/lib within directory.
+  llvm::Error scanForUnwrappedLibraries(StringRef Directory);
+
+  /// Collect files for any frameworks within directory.
+  llvm::Error scanForFrameworks(StringRef Directory);
+
+  /// Get a library from the libraries collection.
+  Library (StringRef Path, std::vector ) 
const;
+
+  /// Collect multiple frameworks from directory.
+  llvm::Error scanMultipleFrameworks(StringRef Directory,
+ std::vector ) const;
+  /// Collect files from nested frameworks.
+  llvm::Error scanSubFrameworksDirectory(StringRef Directory,
+ std::vector ) const;
+
+  /// Collect files from framework path.
+  llvm::Error scanFrameworkDirectory(StringRef Path, Library ) const;
+
+  /// Collect 

[clang] [InstallAPI] Pick up input headers by directory traversal (PR #94508)

2024-06-05 Thread Cyndy Ishida via cfe-commits

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

Match TAPI behavior and allow input headers to be resolved via a passed 
directory, which is expected to be a library sitting in a build directory.

>From c547d990aca29ecfe6f51d37c5c3f8712dfc5e44 Mon Sep 17 00:00:00 2001
From: Cyndy Ishida 
Date: Fri, 10 May 2024 09:19:22 -0700
Subject: [PATCH] [InstallAPI] Pick up input headers by directory traversal

Match TAPI behavior and allow input headers to be resolved via a passed
directory, which is expected to be a library sitting in a build
directory.
---
 .../clang/Basic/DiagnosticInstallAPIKinds.td  |   2 +
 .../clang/InstallAPI/DirectoryScanner.h   |  81 +
 clang/include/clang/InstallAPI/HeaderFile.h   |   8 +
 clang/include/clang/InstallAPI/Library.h  |  65 
 clang/include/clang/InstallAPI/MachO.h|   1 +
 clang/lib/InstallAPI/CMakeLists.txt   |   2 +
 clang/lib/InstallAPI/DirectoryScanner.cpp | 300 ++
 clang/lib/InstallAPI/Library.cpp  |  40 +++
 clang/test/InstallAPI/asm.test|   2 +-
 clang/test/InstallAPI/basic.test  |   4 +-
 clang/test/InstallAPI/binary-attributes.test  |   6 +-
 clang/test/InstallAPI/cpp.test|   4 +-
 clang/test/InstallAPI/diagnostics-dsym.test   |   4 +-
 .../InstallAPI/directory-scanning-dylib.test  |  57 
 .../directory-scanning-frameworks.test|  89 ++
 clang/test/InstallAPI/functions.test  |   2 +-
 clang/test/InstallAPI/variables.test  |   2 +-
 clang/tools/clang-installapi/Options.cpp  |  51 ++-
 clang/tools/clang-installapi/Options.h|   3 +
 19 files changed, 699 insertions(+), 24 deletions(-)
 create mode 100644 clang/include/clang/InstallAPI/DirectoryScanner.h
 create mode 100644 clang/include/clang/InstallAPI/Library.h
 create mode 100644 clang/lib/InstallAPI/DirectoryScanner.cpp
 create mode 100644 clang/lib/InstallAPI/Library.cpp
 create mode 100644 clang/test/InstallAPI/directory-scanning-dylib.test
 create mode 100644 clang/test/InstallAPI/directory-scanning-frameworks.test

diff --git a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td 
b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
index cdf27247602f2..e10fa71011f30 100644
--- a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
+++ b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
@@ -26,6 +26,8 @@ def err_unsupported_environment : Error<"environment '%0' is 
not supported: '%1'
 def err_unsupported_os : Error<"os '%0' is not supported: '%1'">;
 def err_cannot_read_input_list : Error<"could not read %0 input list '%1': 
%2">;
 def err_invalid_label: Error<"label '%0' is reserved: use a different label 
name for -X">;
+def err_directory_scanning: Error<"could not read directory '%0': %1">;
+def err_more_than_one_library: Error<"more than one framework/dynamic library 
found">;
 } // end of command line category.
 
 let CategoryName = "Verification" in {
diff --git a/clang/include/clang/InstallAPI/DirectoryScanner.h 
b/clang/include/clang/InstallAPI/DirectoryScanner.h
new file mode 100644
index 0..803328982ec87
--- /dev/null
+++ b/clang/include/clang/InstallAPI/DirectoryScanner.h
@@ -0,0 +1,81 @@
+//===- InstallAPI/DirectoryScanner.h *- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+///
+/// The DirectoryScanner for collecting library files on the file system.
+///
+//===--===//
+#ifndef LLVM_CLANG_INSTALLAPI_DIRECTORYSCANNER_H
+#define LLVM_CLANG_INSTALLAPI_DIRECTORYSCANNER_H
+
+#include "clang/Basic/FileManager.h"
+#include "clang/InstallAPI/Library.h"
+
+namespace clang::installapi {
+
+enum ScanMode {
+  /// Scanning Framework directory.
+  ScanFrameworks,
+  /// Scanning Dylib directory.
+  ScanDylibs,
+};
+
+class DirectoryScanner {
+public:
+  DirectoryScanner(FileManager , ScanMode Mode = ScanMode::ScanFrameworks)
+  : FM(FM), Mode(Mode) {}
+
+  /// Scan for all input files throughout directory.
+  ///
+  /// \param Directory Path of input directory.
+  llvm::Error scan(StringRef Directory);
+
+  /// Take over ownership of stored libraries.
+  std::vector takeLibraries() { return std::move(Libraries); };
+
+  /// Get all the header files in libraries.
+  ///
+  /// \param Libraries Reference of collection of libraries.
+  static HeaderSeq getHeaders(ArrayRef Libraries);
+
+private:
+  /// Collect files for dylibs in usr/(local)/lib within directory.
+  llvm::Error scanForUnwrappedLibraries(StringRef Directory);
+
+  /// Collect files for any frameworks within directory.
+  llvm::Error scanForFrameworks(StringRef Directory);
+
+