[clang][InstallAPI] Add input file support to library (#81701)
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.
This commit is contained in:
42
clang/include/clang/InstallAPI/FileList.h
Normal file
42
clang/include/clang/InstallAPI/FileList.h
Normal file
@@ -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
|
||||
72
clang/include/clang/InstallAPI/HeaderFile.h
Normal file
72
clang/include/clang/InstallAPI/HeaderFile.h
Normal file
@@ -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
|
||||
@@ -16,5 +16,6 @@ add_clang_library(clangExtractAPI
|
||||
clangBasic
|
||||
clangFrontend
|
||||
clangIndex
|
||||
clangInstallAPI
|
||||
clangLex
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -5,6 +5,8 @@ set(LLVM_LINK_COMPONENTS
|
||||
|
||||
add_clang_library(clangInstallAPI
|
||||
Context.cpp
|
||||
FileList.cpp
|
||||
HeaderFile.cpp
|
||||
|
||||
LINK_LIBS
|
||||
clangAST
|
||||
|
||||
184
clang/lib/InstallAPI/FileList.cpp
Normal file
184
clang/lib/InstallAPI/FileList.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
//===- 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) {
|
||||
Implementation Impl;
|
||||
Impl.InputBuffer = std::move(InputBuffer);
|
||||
|
||||
if (llvm::Error Err = Impl.parse(Impl.InputBuffer->getBuffer()))
|
||||
return Err;
|
||||
|
||||
Destination.reserve(Destination.size() + Impl.HeaderList.size());
|
||||
llvm::move(Impl.HeaderList, std::back_inserter(Destination));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
37
clang/lib/InstallAPI/HeaderFile.cpp
Normal file
37
clang/lib/InstallAPI/HeaderFile.cpp
Normal file
@@ -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
|
||||
@@ -51,5 +51,6 @@ endif()
|
||||
add_subdirectory(DirectoryWatcher)
|
||||
add_subdirectory(Rename)
|
||||
add_subdirectory(Index)
|
||||
add_subdirectory(InstallAPI)
|
||||
add_subdirectory(Serialization)
|
||||
add_subdirectory(Support)
|
||||
|
||||
11
clang/unittests/InstallAPI/CMakeLists.txt
Normal file
11
clang/unittests/InstallAPI/CMakeLists.txt
Normal file
@@ -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)
|
||||
146
clang/unittests/InstallAPI/FileListTest.cpp
Normal file
146
clang/unittests/InstallAPI/FileListTest.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
//===- 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 "llvm/Testing/Support/Error.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);
|
||||
ASSERT_THAT_ERROR(std::move(Err), Succeeded());
|
||||
|
||||
EXPECT_EQ(Expected.size(), Headers.size());
|
||||
EXPECT_EQ(Headers, Expected);
|
||||
}
|
||||
|
||||
TEST(FileList, 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(FileList, 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_THAT_ERROR(
|
||||
std::move(Err),
|
||||
FailedWithMessage(
|
||||
"invalid input format: required field 'version' not specified\n"));
|
||||
}
|
||||
|
||||
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_THAT_ERROR(
|
||||
std::move(Err),
|
||||
FailedWithMessage("invalid input format: unsupported header type\n"));
|
||||
}
|
||||
} // namespace FileListTests
|
||||
92
clang/unittests/InstallAPI/HeaderFileTest.cpp
Normal file
92
clang/unittests/InstallAPI/HeaderFileTest.cpp
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user