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

Cyndy Ishida via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 25 12:22:02 PDT 2024


================
@@ -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<Library> Libraries) {
+  HeaderSeq Headers;
+  for (const Library &Lib : 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_MAX> 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 &Lib = 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<bool>(llvm::sys::path::extension(Path))
+      .Case(".framework", true)
+      .Default(false);
+}
+
+Library &
+DirectoryScanner::getOrCreateLibrary(StringRef Path,
+                                     std::vector<Library> &Libs) const {
+  if (Path.consume_front(RootPath) && Path.empty())
+    Path = "/";
+
+  auto LibIt =
+      find_if(Libs, [Path](const Library &L) { return L.getPath() == Path; });
+  if (LibIt != Libs.end())
+    return *LibIt;
+
+  Libs.emplace_back(Path);
+  return Libs.back();
+}
+
+Error DirectoryScanner::scanHeaders(StringRef Path, Library &Lib,
+                                    HeaderType Type, StringRef BasePath,
+                                    StringRef ParentPath) const {
+  std::error_code ec;
+  auto &FS = 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


More information about the cfe-commits mailing list