[clang] [clang][InstallAPI] Add input file support to library (PR #81701)
Cyndy Ishida via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 20 07:59:31 PST 2024
https://github.com/cyndyishida updated https://github.com/llvm/llvm-project/pull/81701
>From 4210939acc22027077e509c13a6004d00f752011 Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Tue, 13 Feb 2024 18:22:23 -0800
Subject: [PATCH 1/2] [clang][InstallAPI] Add input file support to library
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.
---
clang/include/clang/InstallAPI/FileList.h | 42 ++++
clang/include/clang/InstallAPI/HeaderFile.h | 72 +++++++
clang/lib/ExtractAPI/CMakeLists.txt | 1 +
clang/lib/ExtractAPI/ExtractAPIConsumer.cpp | 7 +-
clang/lib/InstallAPI/CMakeLists.txt | 2 +
clang/lib/InstallAPI/FileList.cpp | 187 ++++++++++++++++++
clang/lib/InstallAPI/HeaderFile.cpp | 37 ++++
clang/unittests/CMakeLists.txt | 1 +
clang/unittests/InstallAPI/CMakeLists.txt | 11 ++
clang/unittests/InstallAPI/FileListTest.cpp | 147 ++++++++++++++
clang/unittests/InstallAPI/HeaderFileTest.cpp | 92 +++++++++
11 files changed, 595 insertions(+), 4 deletions(-)
create mode 100644 clang/include/clang/InstallAPI/FileList.h
create mode 100644 clang/include/clang/InstallAPI/HeaderFile.h
create mode 100644 clang/lib/InstallAPI/FileList.cpp
create mode 100644 clang/lib/InstallAPI/HeaderFile.cpp
create mode 100644 clang/unittests/InstallAPI/CMakeLists.txt
create mode 100644 clang/unittests/InstallAPI/FileListTest.cpp
create mode 100644 clang/unittests/InstallAPI/HeaderFileTest.cpp
diff --git a/clang/include/clang/InstallAPI/FileList.h b/clang/include/clang/InstallAPI/FileList.h
new file mode 100644
index 00000000000000..460af003b6a0ac
--- /dev/null
+++ b/clang/include/clang/InstallAPI/FileList.h
@@ -0,0 +1,42 @@
+//===- 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 {
+
+class FileListReader {
+public:
+ /// Decode JSON input and append header input into destination container.
+ /// Headers are loaded in the order they appear in the JSON input.
+ ///
+ /// \param InputBuffer JSON input data.
+ /// \param Destination Container to load headers into.
+ static llvm::Error
+ loadHeaders(std::unique_ptr<llvm::MemoryBuffer> InputBuffer,
+ HeaderSeq &Destination);
+
+ FileListReader() = delete;
+};
+
+} // 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..6ccd944f8b01be
--- /dev/null
+++ b/clang/include/clang/InstallAPI/HeaderFile.h
@@ -0,0 +1,72 @@
+//===- 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 {
+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) {}
+
+ static llvm::Regex getFrameworkIncludeRule();
+
+ bool operator==(const HeaderFile &Other) const {
+ return std::tie(Type, FullPath, IncludeName, Language) ==
+ std::tie(Other.Type, Other.FullPath, Other.IncludeName,
+ Other.Language);
+ }
+};
+
+/// 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/CMakeLists.txt b/clang/lib/ExtractAPI/CMakeLists.txt
index 2b6a5b7273f554..f508ffc862cb54 100644
--- a/clang/lib/ExtractAPI/CMakeLists.txt
+++ b/clang/lib/ExtractAPI/CMakeLists.txt
@@ -16,5 +16,6 @@ add_clang_library(clangExtractAPI
clangBasic
clangFrontend
clangIndex
+ clangInstallAPI
clangLex
)
diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
index fd62d841197d9f..275f49be22e15a 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,8 @@ 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::HeaderFile::getFrameworkIncludeRule().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..ae5a4a47c0bb55
--- /dev/null
+++ b/clang/lib/InstallAPI/FileList.cpp
@@ -0,0 +1,187 @@
+//===- 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;
+
+namespace {
+class 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;
+ HeaderSeq HeaderList;
+
+ Error parse(StringRef Input);
+};
+
+Expected<StringRef>
+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> 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> 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>
+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 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);
+
+ StringRef PathStr = *Path;
+ if (*Type == HeaderType::Project) {
+ HeaderList.emplace_back(
+ HeaderFile{PathStr, *Type, /*IncludeName=*/"", Language});
+ continue;
+ }
+ auto IncludeName = createIncludeHeaderName(PathStr);
+ HeaderList.emplace_back(PathStr, *Type,
+ IncludeName.has_value() ? IncludeName.value() : "",
+ Language);
+ }
+
+ return Error::success();
+}
+
+Error 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();
+}
+} // namespace
+
+llvm::Error
+FileListReader::loadHeaders(std::unique_ptr<MemoryBuffer> InputBuffer,
+ HeaderSeq &Destination) {
+ llvm::Error Err = Error::success();
+ Implementation Impl;
+ ErrorAsOutParameter ErrorAsOutParam(&Err);
+ Impl.InputBuffer = std::move(InputBuffer);
+
+ Err = Impl.parse(Impl.InputBuffer->getBuffer());
+ if (Err)
+ return Err;
+
+ Destination.reserve(Destination.size() + Impl.HeaderList.size());
+ llvm::move(Impl.HeaderList, std::back_inserter(Destination));
+
+ return Err;
+}
diff --git a/clang/lib/InstallAPI/HeaderFile.cpp b/clang/lib/InstallAPI/HeaderFile.cpp
new file mode 100644
index 00000000000000..c2d8372741ee07
--- /dev/null
+++ b/clang/lib/InstallAPI/HeaderFile.cpp
@@ -0,0 +1,37 @@
+//===- 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 {
+
+llvm::Regex HeaderFile::getFrameworkIncludeRule() {
+ return llvm::Regex("/(.+)\\.framework/(.+)?Headers/(.+)");
+}
+
+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;
+ HeaderFile::getFrameworkIncludeRule().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..4255001ff51f1c
--- /dev/null
+++ b/clang/unittests/InstallAPI/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_clang_unittest(InstallAPITests
+ HeaderFileTest.cpp
+ FileListTest.cpp
+ )
+
+clang_target_link_libraries(InstallAPITests
+ PRIVATE
+ clangInstallAPI
+ )
+
+target_link_libraries(InstallAPITests PRIVATE LLVMTestingSupport)
diff --git a/clang/unittests/InstallAPI/FileListTest.cpp b/clang/unittests/InstallAPI/FileListTest.cpp
new file mode 100644
index 00000000000000..711c7e9a6ab309
--- /dev/null
+++ b/clang/unittests/InstallAPI/FileListTest.cpp
@@ -0,0 +1,147 @@
+//===- 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 "clang/InstallAPI/HeaderFile.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang::installapi;
+
+namespace FileListTests {
+
+static inline void testValidFileList(std::string Input, HeaderSeq &Expected) {
+ auto InputBuf = MemoryBuffer::getMemBuffer(Input);
+ HeaderSeq Headers;
+ llvm::Error Err = FileListReader::loadHeaders(std::move(InputBuf), Headers);
+ EXPECT_FALSE(!!Err);
+ if (Err)
+ FAIL()
+ << "Unexpected error reading and testing FileListReader::loadHeaders";
+
+ EXPECT_EQ(Expected.size(), Headers.size());
+ EXPECT_EQ(Headers, Expected);
+}
+
+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"
+ }
+ ]
+ })";
+
+ HeaderSeq Expected = {
+ {"/tmp/dst/usr/include/foo.h", HeaderType::Public, "foo.h",
+ clang::Language::ObjC},
+ {"/tmp/dst/usr/local/include/bar.h", HeaderType::Private, "bar.h",
+ clang::Language::ObjCXX},
+ {"/tmp/src/baz.h", HeaderType::Project, "", std::nullopt}};
+
+ testValidFileList(Input, Expected);
+}
+
+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"
+ }
+ ]
+ })";
+
+ HeaderSeq Expected = {
+ {"/usr/include/foo.h", HeaderType::Public, "foo.h", std::nullopt},
+ {"/usr/local/include/bar.h", HeaderType::Private, "bar.h", std::nullopt},
+ };
+
+ testValidFileList(Input, Expected);
+}
+
+TEST(FileListReader, Version2) {
+ static const auto Input = R"({
+ "version" : "2",
+ "headers" : [
+ {
+ "type" : "public",
+ "path" : "/usr/include/foo.h"
+ },
+ {
+ "type" : "project",
+ "path" : "src/bar.h"
+ }
+ ]
+ })";
+ HeaderSeq Expected = {
+ {"/usr/include/foo.h", HeaderType::Public, "foo.h", std::nullopt},
+ {"src/bar.h", HeaderType::Project, "", std::nullopt},
+ };
+
+ testValidFileList(Input, Expected);
+}
+
+TEST(FileList, MissingVersion) {
+ static const char Input[] = R"({
+ "headers" : [
+ {
+ "type" : "public",
+ "path" : "/usr/include/foo.h"
+ },
+ {
+ "type" : "private",
+ "path" : "/usr/local/include/bar.h"
+ }
+ ]
+ })";
+ auto InputBuf = MemoryBuffer::getMemBuffer(Input);
+ HeaderSeq Headers;
+ llvm::Error Err = FileListReader::loadHeaders(std::move(InputBuf), Headers);
+ EXPECT_TRUE(!!Err);
+ EXPECT_STREQ("invalid input format: required field 'version' not specified\n",
+ toString(std::move(Err)).c_str());
+}
+
+TEST(FileList, InvalidTypes) {
+ static const char Input[] = R"({
+ "version" : "1",
+ "headers" : [
+ {
+ "type" : "project",
+ "path" : "/usr/include/foo.h"
+ }
+ ]
+ })";
+ auto InputBuf = MemoryBuffer::getMemBuffer(Input);
+ HeaderSeq Headers;
+ llvm::Error Err = FileListReader::loadHeaders(std::move(InputBuf), Headers);
+ EXPECT_TRUE(!!Err);
+ EXPECT_STREQ("invalid input format: unsupported header type\n",
+ toString(std::move(Err)).c_str());
+}
+} // namespace FileListTests
diff --git a/clang/unittests/InstallAPI/HeaderFileTest.cpp b/clang/unittests/InstallAPI/HeaderFileTest.cpp
new file mode 100644
index 00000000000000..985220c72937d4
--- /dev/null
+++ b/clang/unittests/InstallAPI/HeaderFileTest.cpp
@@ -0,0 +1,92 @@
+//===- unittests/InstallAPI/HeaderFile.cpp - HeaderFile Test --------------===//
+//
+// 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"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang::installapi;
+
+namespace HeaderFileTests {
+
+TEST(HeaderFile, FrameworkIncludes) {
+ const char *Path = "/System/Library/Frameworks/Foo.framework/Headers/Foo.h";
+ std::optional<std::string> IncludeName = createIncludeHeaderName(Path);
+ EXPECT_TRUE(IncludeName.has_value());
+ EXPECT_STREQ(IncludeName.value().c_str(), "Foo/Foo.h");
+
+ Path = "/System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/"
+ "Headers/SimpleBar.h";
+ IncludeName = createIncludeHeaderName(Path);
+ EXPECT_TRUE(IncludeName.has_value());
+ EXPECT_STREQ(IncludeName.value().c_str(), "Bar/SimpleBar.h");
+
+ Path = "/tmp/Foo.framework/Versions/A/Headers/SimpleFoo.h";
+ IncludeName = createIncludeHeaderName(Path);
+ EXPECT_TRUE(IncludeName.has_value());
+ EXPECT_STREQ(IncludeName.value().c_str(), "Foo/SimpleFoo.h");
+
+ Path = "/System/Library/PrivateFrameworks/Foo.framework/Headers/Foo.h";
+ IncludeName = createIncludeHeaderName(Path);
+ EXPECT_TRUE(IncludeName.has_value());
+ EXPECT_STREQ(IncludeName.value().c_str(), "Foo/Foo.h");
+
+ Path = "/AppleInternal/Developer/Library/Frameworks/"
+ "HelloFramework.framework/Headers/HelloFramework.h";
+ IncludeName = createIncludeHeaderName(Path);
+ EXPECT_TRUE(IncludeName.has_value());
+ EXPECT_STREQ(IncludeName.value().c_str(), "HelloFramework/HelloFramework.h");
+
+ Path = "/tmp/BuildProducts/Foo.framework/Versions/A/"
+ "PrivateHeaders/Foo+Private.h";
+ IncludeName = createIncludeHeaderName(Path);
+ EXPECT_TRUE(IncludeName.has_value());
+ EXPECT_STREQ(IncludeName.value().c_str(), "Foo/Foo+Private.h");
+
+ Path = "/Applications/Xcode.app/Contents/Developer/SDKS/MacOS.sdk/System/"
+ "Library/Frameworks/Foo.framework/PrivateHeaders/Foo_Private.h";
+ IncludeName = createIncludeHeaderName(Path);
+ EXPECT_TRUE(IncludeName.has_value());
+ EXPECT_STREQ(IncludeName.value().c_str(), "Foo/Foo_Private.h");
+
+ Path =
+ "/System/Library/PrivateFrameworks/Foo.framework/PrivateHeaders/Foo.hpp";
+ IncludeName = createIncludeHeaderName(Path);
+ EXPECT_TRUE(IncludeName.has_value());
+ EXPECT_STREQ(IncludeName.value().c_str(), "Foo/Foo.hpp");
+
+ Path = "/Applications/Xcode.app/Contents/Developer/SDKS/MacOS.sdk/System/"
+ "Library/Frameworks/Foo.framework/Headers/BarDir/Bar.h";
+ IncludeName = createIncludeHeaderName(Path);
+ EXPECT_TRUE(IncludeName.has_value());
+ EXPECT_STREQ(IncludeName.value().c_str(), "Foo/BarDir/Bar.h");
+}
+
+TEST(HeaderFile, DylibIncludes) {
+ const char *Path = "/usr/include/foo.h";
+ std::optional<std::string> IncludeName = createIncludeHeaderName(Path);
+ EXPECT_TRUE(IncludeName.has_value());
+ EXPECT_STREQ(IncludeName.value().c_str(), "foo.h");
+
+ Path = "/tmp/BuildProducts/usr/include/a/A.h";
+ IncludeName = createIncludeHeaderName(Path);
+ EXPECT_TRUE(IncludeName.has_value());
+ EXPECT_STREQ(IncludeName.value().c_str(), "a/A.h");
+
+ Path = "/Applications/Xcode.app/Contents/Developer/SDKS/MacOS.sdk/"
+ "usr/include/simd/types.h";
+ IncludeName = createIncludeHeaderName(Path);
+ EXPECT_TRUE(IncludeName.has_value());
+ EXPECT_STREQ(IncludeName.value().c_str(), "simd/types.h");
+
+ Path = "/usr/local/include/hidden/A.h";
+ IncludeName = createIncludeHeaderName(Path);
+ EXPECT_TRUE(IncludeName.has_value());
+ EXPECT_STREQ(IncludeName.value().c_str(), "hidden/A.h");
+}
+} // namespace HeaderFileTests
>From 28cc0d09d3d3da95481a34deb3255b33fb6607c0 Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Tue, 20 Feb 2024 07:58:41 -0800
Subject: [PATCH 2/2] Use TestingSupport Error checking wrappers
---
clang/unittests/InstallAPI/FileListTest.cpp | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/clang/unittests/InstallAPI/FileListTest.cpp b/clang/unittests/InstallAPI/FileListTest.cpp
index 711c7e9a6ab309..a5372e826ce203 100644
--- a/clang/unittests/InstallAPI/FileListTest.cpp
+++ b/clang/unittests/InstallAPI/FileListTest.cpp
@@ -10,6 +10,7 @@
#include "clang/InstallAPI/HeaderFile.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
using namespace llvm;
@@ -21,10 +22,7 @@ static inline void testValidFileList(std::string Input, HeaderSeq &Expected) {
auto InputBuf = MemoryBuffer::getMemBuffer(Input);
HeaderSeq Headers;
llvm::Error Err = FileListReader::loadHeaders(std::move(InputBuf), Headers);
- EXPECT_FALSE(!!Err);
- if (Err)
- FAIL()
- << "Unexpected error reading and testing FileListReader::loadHeaders";
+ ASSERT_THAT_ERROR(std::move(Err), Succeeded());
EXPECT_EQ(Expected.size(), Headers.size());
EXPECT_EQ(Headers, Expected);
@@ -122,9 +120,10 @@ TEST(FileList, MissingVersion) {
auto InputBuf = MemoryBuffer::getMemBuffer(Input);
HeaderSeq Headers;
llvm::Error Err = FileListReader::loadHeaders(std::move(InputBuf), Headers);
- EXPECT_TRUE(!!Err);
- EXPECT_STREQ("invalid input format: required field 'version' not specified\n",
- toString(std::move(Err)).c_str());
+ EXPECT_THAT_ERROR(
+ std::move(Err),
+ FailedWithMessage(
+ "invalid input format: required field 'version' not specified\n"));
}
TEST(FileList, InvalidTypes) {
@@ -140,8 +139,8 @@ TEST(FileList, InvalidTypes) {
auto InputBuf = MemoryBuffer::getMemBuffer(Input);
HeaderSeq Headers;
llvm::Error Err = FileListReader::loadHeaders(std::move(InputBuf), Headers);
- EXPECT_TRUE(!!Err);
- EXPECT_STREQ("invalid input format: unsupported header type\n",
- toString(std::move(Err)).c_str());
+ EXPECT_THAT_ERROR(
+ std::move(Err),
+ FailedWithMessage("invalid input format: unsupported header type\n"));
}
} // namespace FileListTests
More information about the cfe-commits
mailing list