Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang][InstallAPI] Add input file support to library #81701

Merged
merged 3 commits into from
Feb 20, 2024

Conversation

cyndyishida
Copy link
Member

This patch adds support for expected InstallAPI inputs. InstallAPI accepts a well defined filelist of headers and how those headers represent a single library.

InstallAPI captures header files to determine linkable symbols to then compare against what was compiled in a binary dylib and generate TBD files.

@llvmbot llvmbot added the clang Clang issues not falling into any other category label Feb 14, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Feb 14, 2024

@llvm/pr-subscribers-clang

Author: Cyndy Ishida (cyndyishida)

Changes

This patch adds support for expected InstallAPI inputs. InstallAPI accepts a well defined filelist of headers and how those headers represent a single library.

InstallAPI captures header files to determine linkable symbols to then compare against what was compiled in a binary dylib and generate TBD files.


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

10 Files Affected:

  • (added) clang/include/clang/InstallAPI/FileList.h (+80)
  • (added) clang/include/clang/InstallAPI/HeaderFile.h (+69)
  • (modified) clang/lib/ExtractAPI/ExtractAPIConsumer.cpp (+2-4)
  • (modified) clang/lib/InstallAPI/CMakeLists.txt (+2)
  • (added) clang/lib/InstallAPI/FileList.cpp (+215)
  • (added) clang/lib/InstallAPI/HeaderFile.cpp (+32)
  • (modified) clang/unittests/CMakeLists.txt (+1)
  • (added) clang/unittests/InstallAPI/CMakeLists.txt (+9)
  • (added) clang/unittests/InstallAPI/FileListTest.cpp (+173)
  • (added) clang/unittests/InstallAPI/HeaderFileTest.cpp (+89)
diff --git a/clang/include/clang/InstallAPI/FileList.h b/clang/include/clang/InstallAPI/FileList.h
new file mode 100644
index 00000000000000..5639388514b11d
--- /dev/null
+++ b/clang/include/clang/InstallAPI/FileList.h
@@ -0,0 +1,80 @@
+//===- InstallAPI/FileList.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 JSON file list parser is used to communicate input to InstallAPI.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INSTALLAPI_FILELIST_H
+#define LLVM_CLANG_INSTALLAPI_FILELIST_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/InstallAPI/HeaderFile.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+namespace clang {
+namespace installapi {
+
+/// Abstract Interface for reading FileList JSON Input.
+class FileListReader {
+  class Implementation;
+
+  Implementation &Impl;
+
+  FileListReader(std::unique_ptr<llvm::MemoryBuffer> InputBuffer,
+                 llvm::Error &Err);
+
+public:
+  static llvm::Expected<std::unique_ptr<FileListReader>>
+  get(std::unique_ptr<llvm::MemoryBuffer> InputBuffer);
+
+  ~FileListReader();
+
+  FileListReader(const FileListReader &) = delete;
+  FileListReader &operator=(const FileListReader &) = delete;
+
+  int getVersion() const;
+
+  struct HeaderInfo {
+    HeaderType Type;
+    std::string Path;
+    std::optional<clang::Language> Language;
+  };
+
+  /// Visitor used when walking the contents of the file list.
+  class Visitor {
+  public:
+    virtual ~Visitor();
+
+    virtual void visitHeaderFile(HeaderInfo &header) = 0;
+  };
+
+  /// Visit the contents of the header list file, passing each entity to the
+  /// given visitor. It visits in the same order as they appear in the json
+  /// file.
+  void visit(Visitor &visitor);
+};
+
+class FileListVisitor final : public FileListReader::Visitor {
+  FileManager &FM;
+  DiagnosticsEngine &Diag;
+  HeaderSeq &HeaderFiles;
+
+public:
+  FileListVisitor(FileManager &FM, DiagnosticsEngine &Diag,
+                  HeaderSeq &HeaderFiles)
+      : FM(FM), Diag(Diag), HeaderFiles(HeaderFiles) {}
+
+  void visitHeaderFile(FileListReader::HeaderInfo &Header) override;
+};
+} // namespace installapi
+} // namespace clang
+
+#endif // LLVM_CLANG_INSTALLAPI_FILELIST_H
diff --git a/clang/include/clang/InstallAPI/HeaderFile.h b/clang/include/clang/InstallAPI/HeaderFile.h
new file mode 100644
index 00000000000000..7aea045b50c363
--- /dev/null
+++ b/clang/include/clang/InstallAPI/HeaderFile.h
@@ -0,0 +1,69 @@
+//===- InstallAPI/HeaderFile.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
+//
+//===----------------------------------------------------------------------===//
+///
+/// Representations of a library's headers for InstallAPI.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INSTALLAPI_HEADERFILE_H
+#define LLVM_CLANG_INSTALLAPI_HEADERFILE_H
+
+#include "clang/Basic/LangStandard.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Regex.h"
+#include <optional>
+#include <string>
+
+namespace clang::installapi {
+
+const llvm::Regex DarwinFwkHeaderRule("/(.+)\\.framework/(.+)?Headers/(.+)");
+
+enum class HeaderType {
+  /// Represents declarations accessible to all clients.
+  Public,
+  /// Represents declarations accessible to a disclosed set of clients.
+  Private,
+  /// Represents declarations only accessible as implementation details to the
+  /// input library.
+  Project,
+};
+
+class HeaderFile {
+  /// Full input path to header.
+  std::string FullPath;
+  /// Access level of header.
+  HeaderType Type;
+  /// Expected way header will be included by clients.
+  std::string IncludeName;
+  /// Supported language mode for header.
+  std::optional<clang::Language> Language;
+
+public:
+  HeaderFile(StringRef FullPath, HeaderType Type,
+             StringRef IncludeName = StringRef(),
+             std::optional<clang::Language> Language = std::nullopt)
+      : FullPath(FullPath), Type(Type), IncludeName(IncludeName),
+        Language(Language) {}
+
+  HeaderType getType() const { return Type; }
+};
+
+/// Assemble expected way header will be included by clients.
+/// As in what maps inside the brackets of `#include <IncludeName.h>`
+/// For example,
+/// "/System/Library/Frameworks/Foo.framework/Headers/Foo.h" returns
+/// "Foo/Foo.h"
+///
+/// \param FullPath Path to the header file which includes the library
+/// structure.
+std::optional<std::string> createIncludeHeaderName(const StringRef FullPath);
+using HeaderSeq = std::vector<HeaderFile>;
+
+} // namespace clang::installapi
+
+#endif // LLVM_CLANG_INSTALLAPI_HEADERFILE_H
diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
index fd62d841197d9f..f73b0adec76553 100644
--- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -30,6 +30,7 @@
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendOptions.h"
 #include "clang/Frontend/MultiplexConsumer.h"
+#include "clang/InstallAPI/HeaderFile.h"
 #include "clang/Lex/MacroInfo.h"
 #include "clang/Lex/PPCallbacks.h"
 #include "clang/Lex/Preprocessor.h"
@@ -61,9 +62,6 @@ std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
          "CompilerInstance does not have a FileNamager!");
 
   using namespace llvm::sys;
-  // Matches framework include patterns
-  const llvm::Regex Rule("/(.+)\\.framework/(.+)?Headers/(.+)");
-
   const auto &FS = CI.getVirtualFileSystem();
 
   SmallString<128> FilePath(File.begin(), File.end());
@@ -147,7 +145,7 @@ std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
       // include name `<Framework/Header.h>`
       if (Entry.IsFramework) {
         SmallVector<StringRef, 4> Matches;
-        Rule.match(File, &Matches);
+        clang::installapi::DarwinFwkHeaderRule.match(File, &Matches);
         // Returned matches are always in stable order.
         if (Matches.size() != 4)
           return std::nullopt;
diff --git a/clang/lib/InstallAPI/CMakeLists.txt b/clang/lib/InstallAPI/CMakeLists.txt
index 1476b737c5e61c..6c9cb4b559f67d 100644
--- a/clang/lib/InstallAPI/CMakeLists.txt
+++ b/clang/lib/InstallAPI/CMakeLists.txt
@@ -5,6 +5,8 @@ set(LLVM_LINK_COMPONENTS
 
 add_clang_library(clangInstallAPI
   Context.cpp
+  FileList.cpp
+  HeaderFile.cpp
 
   LINK_LIBS
   clangAST
diff --git a/clang/lib/InstallAPI/FileList.cpp b/clang/lib/InstallAPI/FileList.cpp
new file mode 100644
index 00000000000000..6bf6048778c96c
--- /dev/null
+++ b/clang/lib/InstallAPI/FileList.cpp
@@ -0,0 +1,215 @@
+//===- FileList.cpp ---------------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/InstallAPI/FileList.h"
+#include "clang/Basic/DiagnosticFrontend.h"
+#include "clang/InstallAPI/FileList.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/TextAPI/TextAPIError.h"
+#include <optional>
+
+// clang-format off
+/*
+InstallAPI JSON Input Format specification.
+
+{
+  "headers" : [                              # Required: Key must exist.
+    {                                        # Optional: May contain 0 or more header inputs.
+      "path" : "/usr/include/mach-o/dlfn.h", # Required: Path should point to destination 
+                                             #           location where applicable.
+      "type" : "public",                     # Required: Maps to HeaderType for header.
+      "language": "c++"                      # Optional: Language mode for header.
+    }
+  ],
+  "version" : "3"                            # Required: Version 3 supports language mode 
+                                                         & project header input.
+}
+*/
+// clang-format on
+
+using namespace llvm;
+using namespace llvm::json;
+using namespace llvm::MachO;
+using namespace clang::installapi;
+
+class FileListReader::Implementation {
+private:
+  Expected<StringRef> parseString(const Object *Obj, StringRef Key,
+                                  StringRef Error);
+  Expected<StringRef> parsePath(const Object *Obj);
+  Expected<HeaderType> parseType(const Object *Obj);
+  std::optional<clang::Language> parseLanguage(const Object *Obj);
+  Error parseHeaders(Array &Headers);
+
+public:
+  std::unique_ptr<MemoryBuffer> InputBuffer;
+  unsigned Version;
+  std::vector<HeaderInfo> HeaderList;
+
+  Error parse(StringRef Input);
+};
+
+Expected<StringRef>
+FileListReader::Implementation::parseString(const Object *Obj, StringRef Key,
+                                            StringRef Error) {
+  auto Str = Obj->getString(Key);
+  if (!Str)
+    return make_error<StringError>(Error, inconvertibleErrorCode());
+  return *Str;
+}
+
+Expected<HeaderType>
+FileListReader::Implementation::parseType(const Object *Obj) {
+  auto TypeStr =
+      parseString(Obj, "type", "required field 'type' not specified");
+  if (!TypeStr)
+    return TypeStr.takeError();
+
+  if (*TypeStr == "public")
+    return HeaderType::Public;
+  else if (*TypeStr == "private")
+    return HeaderType::Private;
+  else if (*TypeStr == "project" && Version >= 2)
+    return HeaderType::Project;
+
+  return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
+                                  "unsupported header type");
+}
+
+Expected<StringRef>
+FileListReader::Implementation::parsePath(const Object *Obj) {
+  auto Path = parseString(Obj, "path", "required field 'path' not specified");
+  if (!Path)
+    return Path.takeError();
+
+  return *Path;
+}
+
+std::optional<clang::Language>
+FileListReader::Implementation::parseLanguage(const Object *Obj) {
+  auto Language = Obj->getString("language");
+  if (!Language)
+    return std::nullopt;
+
+  return StringSwitch<clang::Language>(*Language)
+      .Case("c", clang::Language::C)
+      .Case("c++", clang::Language::CXX)
+      .Case("objective-c", clang::Language::ObjC)
+      .Case("objective-c++", clang::Language::ObjCXX)
+      .Default(clang::Language::Unknown);
+}
+
+Error FileListReader::Implementation::parseHeaders(Array &Headers) {
+  for (const auto &H : Headers) {
+    auto *Obj = H.getAsObject();
+    if (!Obj)
+      return make_error<StringError>("expect a JSON object",
+                                     inconvertibleErrorCode());
+    auto Type = parseType(Obj);
+    if (!Type)
+      return Type.takeError();
+    auto Path = parsePath(Obj);
+    if (!Path)
+      return Path.takeError();
+    auto Language = parseLanguage(Obj);
+
+    HeaderList.emplace_back(HeaderInfo{*Type, std::string(*Path), Language});
+  }
+
+  return Error::success();
+}
+
+Error FileListReader::Implementation::parse(StringRef Input) {
+  auto Val = json::parse(Input);
+  if (!Val)
+    return Val.takeError();
+
+  auto *Root = Val->getAsObject();
+  if (!Root)
+    return make_error<StringError>("not a JSON object",
+                                   inconvertibleErrorCode());
+
+  auto VersionStr = Root->getString("version");
+  if (!VersionStr)
+    return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
+                                    "required field 'version' not specified");
+  if (VersionStr->getAsInteger(10, Version))
+    return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
+                                    "invalid version number");
+
+  if (Version < 1 || Version > 3)
+    return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
+                                    "unsupported version");
+
+  // Not specifying any header files should be atypical, but valid.
+  auto Headers = Root->getArray("headers");
+  if (!Headers)
+    return Error::success();
+
+  Error Err = parseHeaders(*Headers);
+  if (Err)
+    return Err;
+
+  return Error::success();
+}
+
+FileListReader::FileListReader(std::unique_ptr<MemoryBuffer> InputBuffer,
+                               Error &Error)
+    : Impl(*new FileListReader::Implementation()) {
+  ErrorAsOutParameter ErrorAsOutParam(&Error);
+  Impl.InputBuffer = std::move(InputBuffer);
+
+  Error = Impl.parse(Impl.InputBuffer->getBuffer());
+}
+
+Expected<std::unique_ptr<FileListReader>>
+FileListReader::get(std::unique_ptr<MemoryBuffer> InputBuffer) {
+  Error Error = Error::success();
+  std::unique_ptr<FileListReader> Reader(
+      new FileListReader(std::move(InputBuffer), Error));
+  if (Error)
+    return std::move(Error);
+
+  return Reader;
+}
+
+FileListReader::~FileListReader() { delete &Impl; }
+
+int FileListReader::getVersion() const { return Impl.Version; }
+
+void FileListReader::visit(Visitor &Visitor) {
+  for (auto &File : Impl.HeaderList)
+    Visitor.visitHeaderFile(File);
+}
+
+FileListReader::Visitor::~Visitor() {}
+
+void FileListReader::Visitor::visitHeaderFile(HeaderInfo &Header) {}
+
+void FileListVisitor::visitHeaderFile(FileListReader::HeaderInfo &Header) {
+  llvm::vfs::Status Result;
+  if (FM.getNoncachedStatValue(Header.Path, Result) || !Result.exists()) {
+    Diag.Report(diag::err_fe_error_opening) << Header.Path;
+    return;
+  }
+
+  // Track full paths for project headers, as they are looked up via
+  // quote includes.
+  if (Header.Type == HeaderType::Project) {
+    HeaderFiles.emplace_back(Header.Path, Header.Type,
+                             /*IncludeName*/ "", Header.Language);
+    return;
+  }
+
+  auto IncludeName = createIncludeHeaderName(Header.Path);
+  HeaderFiles.emplace_back(Header.Path, Header.Type,
+                           IncludeName.has_value() ? IncludeName.value() : "",
+                           Header.Language);
+}
diff --git a/clang/lib/InstallAPI/HeaderFile.cpp b/clang/lib/InstallAPI/HeaderFile.cpp
new file mode 100644
index 00000000000000..78a85688ab53b4
--- /dev/null
+++ b/clang/lib/InstallAPI/HeaderFile.cpp
@@ -0,0 +1,32 @@
+//===- HeaderFile.cpp ------------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/InstallAPI/HeaderFile.h"
+
+using namespace llvm;
+namespace clang::installapi {
+std::optional<std::string> createIncludeHeaderName(const StringRef FullPath) {
+  // Headers in usr(/local)*/include.
+  std::string Pattern = "/include/";
+  auto PathPrefix = FullPath.find(Pattern);
+  if (PathPrefix != StringRef::npos) {
+    PathPrefix += Pattern.size();
+    return FullPath.drop_front(PathPrefix).str();
+  }
+
+  // Framework Headers.
+  SmallVector<StringRef, 4> Matches;
+  DarwinFwkHeaderRule.match(FullPath, &Matches);
+  // Returned matches are always in stable order.
+  if (Matches.size() != 4)
+    return std::nullopt;
+
+  return Matches[1].drop_front(Matches[1].rfind('/') + 1).str() + "/" +
+         Matches[3].str();
+}
+} // namespace clang::installapi
diff --git a/clang/unittests/CMakeLists.txt b/clang/unittests/CMakeLists.txt
index f4e4f585bdd800..37ca3107b54774 100644
--- a/clang/unittests/CMakeLists.txt
+++ b/clang/unittests/CMakeLists.txt
@@ -51,5 +51,6 @@ endif()
 add_subdirectory(DirectoryWatcher)
 add_subdirectory(Rename)
 add_subdirectory(Index)
+add_subdirectory(InstallAPI)
 add_subdirectory(Serialization)
 add_subdirectory(Support)
diff --git a/clang/unittests/InstallAPI/CMakeLists.txt b/clang/unittests/InstallAPI/CMakeLists.txt
new file mode 100644
index 00000000000000..b70b7c136e64a6
--- /dev/null
+++ b/clang/unittests/InstallAPI/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_clang_unittest(InstallAPITests
+  HeaderFileTest.cpp
+  FileListTest.cpp
+  )
+
+clang_target_link_libraries(InstallAPITests
+  PRIVATE
+  clangInstallAPI 
+  )
diff --git a/clang/unittests/InstallAPI/FileListTest.cpp b/clang/unittests/InstallAPI/FileListTest.cpp
new file mode 100644
index 00000000000000..7e0830e44102ab
--- /dev/null
+++ b/clang/unittests/InstallAPI/FileListTest.cpp
@@ -0,0 +1,173 @@
+//===- unittests/InstallAPI/FileList.cpp - File List Tests ---------------===//
+//
+// 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/FileList.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "gtest/gtest.h"
+#include <map>
+
+using namespace llvm;
+using namespace clang::installapi;
+
+namespace {
+class TestVisitor : public FileListReader::Visitor {
+public:
+  std::map<StringRef, FileListReader::HeaderInfo> Headers;
+
+  void visitHeaderFile(FileListReader::HeaderInfo &Header) override {
+    StringRef Key = StringRef(Header.Path).rsplit("/").second;
+    Headers[Key] = Header;
+  }
+};
+} // namespace
+
+static bool operator==(const FileListReader::HeaderInfo &LHS,
+                       const FileListReader::HeaderInfo &RHS) {
+  return std::tie(LHS.Type, LHS.Path, LHS.Language) ==
+         std::tie(RHS.Type, RHS.Path, RHS.Language);
+}
+
+TEST(FileListReader, Version3) {
+  static const char Input[] = R"({
+    "version" : "3",
+    "headers" : [
+      {
+        "type" : "public",
+        "path" : "/tmp/dst/usr/include/foo.h",
+        "language" : "objective-c"
+      },
+      {
+        "type" : "private",
+        "path" : "/tmp/dst/usr/local/include/bar.h",
+        "language" : "objective-c++"
+      },
+      {
+        "type" : "project",
+        "path" : "/tmp/src/baz.h"
+      }
+    ]
+  })";
+  auto InputBuf = MemoryBuffer::getMemBuffer(Input);
+  auto Reader = FileListReader::get(std::move(InputBuf));
+  ASSERT_TRUE(!!Reader);
+  TestVisitor Visitor;
+  (*Reader)->visit(Visitor);
+  EXPECT_EQ(3U, Visitor.Headers.size());
+
+  FileListReader::HeaderInfo Foo{
+      HeaderType::Public, "/tmp/dst/usr/include/foo.h", clang::Language::ObjC};
+  EXPECT_TRUE(Foo == Visitor.Headers["foo.h"]);
+
+  FileListReader::HeaderInfo Bar{HeaderType::Private,
+                                 "/tmp/dst/usr/local/include/bar.h",
+                                 clang::Language::ObjCXX};
+  EXPECT_TRUE(Bar == Visitor.Headers["bar.h"]);
+
+  FileListReader::HeaderInfo Baz{HeaderType::Project, "/tmp/src/baz.h",
+                                 std::nullopt};
+  EXPECT_TRUE(Baz == Visitor.Headers["baz.h"]);
+}
+
+TEST(FileList, Version1) {
+  static const char Input[] = R"({
+    "version" : "1",
+    "headers" : [
+      {
+        "type" : "public",
+        "path" : "/usr/include/foo.h"
+      },
+      {
+        "type" : "private",
+        "path" : "/usr/local/include/bar.h"
+      }
+    ]
+  })";
+  auto InputBuf = MemoryBuffer::getMemBuffer(Input);
+  auto Reader = FileListReader::get(std::move(InputBuf));
+  ASSERT_TRUE(!!Reader);
+
+  TestVisitor Visitor;
+  (*Reader)->visit(Visitor);
+  EXPECT_EQ(2U, Visitor.Headers.size());
+
+  FileListReader::HeaderInfo Foo{HeaderType::Public, "/usr/include/foo.h",
+                                 std::nullopt};
+  EXPECT_TRUE(Foo == Visitor.Headers["foo.h"]);
+
+  FileListReader::HeaderInfo Bar{HeaderType::Private,
+                                 "/usr/local/include/bar.h", std::nullopt};
+  EXPECT_TRUE(Bar == Visitor.Headers["bar.h"]);
+}
+
+TEST(FileList, MissingVersion) {
+  static const char Input[] = R"({
+    "headers" : [
+      {
+        "type" : "public",
+        "path" : "/usr/include/foo.h"
...
[truncated]

Copy link
Collaborator

@ributzka ributzka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

This patch adds support for expected InstallAPI inputs.
InstallAPI  accepts a well defined filelist of headers and how those headers
represent a single library.

InstallAPI captures header files to determine linkable symbols
to then compare against what was compiled in a binary dylib and
generate TBD files.
@cyndyishida cyndyishida merged commit 4c6043d into llvm:main Feb 20, 2024
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants