[llvm] [readtapi] Add support for stubify-ing directories (PR #76885)

Samuel Marks via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 5 15:13:58 PST 2024


================
@@ -167,26 +197,237 @@ static bool handleMergeAction(const Context &Ctx) {
   return handleWriteAction(Ctx, std::move(Out));
 }
 
+static void stubifyImpl(std::unique_ptr<InterfaceFile> IF, Context &Ctx) {
+  // TODO: Add inlining and magic merge support.
+  if (Ctx.OutStream == nullptr) {
+    std::error_code EC;
+    SmallString<PATH_MAX> OutputLoc = IF->getPath();
+    replace_extension(OutputLoc, ".tbd");
+    Ctx.OutStream = std::make_unique<llvm::raw_fd_stream>(OutputLoc, EC);
+    if (EC)
+      reportError("opening file '" + OutputLoc + ": " + EC.message());
+  }
+
+  handleWriteAction(Ctx, std::move(IF));
+  // Clear out output stream after file has been written incase more files are
+  // stubifed.
+  Ctx.OutStream = nullptr;
+}
+
+static void stubifyDirectory(const StringRef InputPath, Context &Ctx) {
+  assert(InputPath.back() != '/' && "Unexpected / at end of input path.");
+  StringMap<std::vector<SymLink>> SymLinks;
+  StringMap<std::unique_ptr<InterfaceFile>> Dylibs;
+  StringMap<std::string> OriginalNames;
+  std::set<std::pair<std::string, bool>> LibsToDelete;
+
+  std::error_code EC;
+  for (sys::fs::recursive_directory_iterator IT(InputPath, EC), IE; IT != IE;
+       IT.increment(EC)) {
+    if (EC == std::errc::no_such_file_or_directory) {
+      reportWarning(IT->path() + ": " + EC.message());
+      continue;
+    }
+    if (EC)
+      reportError(IT->path() + ": " + EC.message());
+
+    // Skip header directories (include/Headers/PrivateHeaders) and module
+    // files.
+    StringRef Path = IT->path();
+    if (Path.ends_with("/include") || Path.ends_with("/Headers") ||
+        Path.ends_with("/PrivateHeaders") || Path.ends_with("/Modules") ||
+        Path.ends_with(".map") || Path.ends_with(".modulemap")) {
+      IT.no_push();
+      continue;
+    }
+
+    // Check if the entry is a symlink. We don't follow symlinks but we record
+    // their content.
+    bool IsSymLink;
+    if (auto EC = sys::fs::is_symlink_file(Path, IsSymLink))
+      reportError(Path + ": " + EC.message());
+
+    if (IsSymLink) {
+      IT.no_push();
+
+      bool ShouldSkip;
+      auto SymLinkEC = shouldSkipSymLink(Path, ShouldSkip);
+
+      // If symlink is broken, for some reason, we should continue
+      // trying to repair it before quitting.
+      if (!SymLinkEC && ShouldSkip)
+        continue;
+
+      if (Ctx.StubOpt.DeletePrivate &&
+          isPrivateLibrary(Path.drop_front(InputPath.size()), true)) {
+        LibsToDelete.emplace(Path, false);
+        continue;
+      }
+
+      SmallString<PATH_MAX> SymPath;
+      if (auto EC = read_link(Path, SymPath))
+        reportError("cannot read '" + Path + "' :" + EC.message());
+
+      // Sometimes there are broken symlinks that are absolute paths, which are
+      // invalid during build time, but would be correct during runtime. In the
+      // case of an absolute path we should check first if the path exists with
+      // the known locations as prefix.
+      SmallString<PATH_MAX> LinkSrc = Path;
+      SmallString<PATH_MAX> LinkTarget;
+      if (sys::path::is_absolute(SymPath)) {
+        LinkTarget = InputPath;
+        sys::path::append(LinkTarget, SymPath);
+
+        // TODO: Investigate supporting a file manager for file system accesses.
+        if (sys::fs::exists(LinkTarget)) {
+          // Convert the absolute path to an relative path.
+          if (auto ec = MachO::make_relative(LinkSrc, LinkTarget, SymPath))
+            reportError(LinkTarget + ": " + EC.message());
+        } else if (!sys::fs::exists(SymPath)) {
+          reportWarning("ignoring broken symlink: " + Path);
+          continue;
+        } else {
+          LinkTarget = SymPath;
+        }
+      } else {
+        LinkTarget = LinkSrc;
+        sys::path::remove_filename(LinkTarget);
+        sys::path::append(LinkTarget, SymPath);
+      }
+
+      // For Apple SDKs, the symlink src is guaranteed to be a canonical path
+      // because we don't follow symlinks when scanning. The symlink target is
+      // constructed from the symlink path and needs to be canonicalized.
+      if (auto ec = sys::fs::real_path(Twine(LinkTarget), LinkTarget)) {
+        reportWarning(LinkTarget + ": " + ec.message());
+        continue;
+      }
+
+      auto itr = SymLinks.insert({LinkTarget.c_str(), std::vector<SymLink>()});
+      itr.first->second.emplace_back(LinkSrc.str(), std::string(SymPath.str()));
+
+      continue;
+    }
+
+    bool IsDirectory = false;
+    if (auto EC = sys::fs::is_directory(Path, IsDirectory))
+      reportError(Path + ": " + EC.message());
+    if (IsDirectory)
+      continue;
+
+    if (Ctx.StubOpt.DeletePrivate &&
+        isPrivateLibrary(Path.drop_front(InputPath.size()))) {
+      IT.no_push();
+      LibsToDelete.emplace(Path, false);
+      continue;
+    }
+    auto IF = getInterfaceFile(Path);
+    if (Ctx.StubOpt.TraceLibs)
+      errs() << Path << "\n";
+
+    // Normalize path for map lookup by removing the extension.
+    SmallString<PATH_MAX> NormalizedPath(Path);
+    replace_extension(NormalizedPath, "");
+
+    if ((IF->getFileType() == FileType::MachO_DynamicLibrary) ||
+        (IF->getFileType() == FileType::MachO_DynamicLibrary_Stub)) {
+      OriginalNames[NormalizedPath.c_str()] = IF->getPath();
+
+      // Don't add this MachO dynamic library because we already have a
+      // text-based stub recorded for this path.
+      if (Dylibs.count(NormalizedPath.c_str()))
+        continue;
+    }
+
+    Dylibs[NormalizedPath.c_str()] = std::move(IF);
+  }
+
+  for (auto &Lib : Dylibs) {
+    auto &Dylib = Lib.second;
+    // Get the original file name.
+    SmallString<PATH_MAX> NormalizedPath(Dylib->getPath());
----------------
SamuelMarks wrote:

@cyndyishida It can technically only be known at runtime on Windows and can be > 32,766 https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation. Furthermore on POSIX you might want to actually query the file - https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html

[`SmallVector`](https://llvm.org/doxygen/SmallVector_8h.html); which [`SmallString`](https://llvm.org/doxygen/classllvm_1_1SmallString.html) uses internally; exposes a [`reserve`](https://llvm.org/doxygen/classllvm_1_1SmallVectorImpl.html#a499ea32ca1b8d16cedfe01d1e5b08f29) method which you can make `PATH_MAX`, `MAX_PATH`; so that it has reasonable default allocations but can grow.

https://github.com/llvm/llvm-project/pull/76885


More information about the llvm-commits mailing list