Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 627a086

Browse files
committedFeb 20, 2024·
[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.
1 parent d9f9775 commit 627a086

File tree

11 files changed

+592
-4
lines changed

11 files changed

+592
-4
lines changed
 
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//===- InstallAPI/FileList.h ------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
///
9+
/// The JSON file list parser is used to communicate input to InstallAPI.
10+
///
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_CLANG_INSTALLAPI_FILELIST_H
14+
#define LLVM_CLANG_INSTALLAPI_FILELIST_H
15+
16+
#include "clang/Basic/Diagnostic.h"
17+
#include "clang/Basic/FileManager.h"
18+
#include "clang/InstallAPI/HeaderFile.h"
19+
#include "llvm/Support/Error.h"
20+
#include "llvm/Support/MemoryBuffer.h"
21+
22+
namespace clang {
23+
namespace installapi {
24+
25+
class FileListReader {
26+
public:
27+
/// Decode JSON input and append header input into destination container.
28+
/// Headers are loaded in the order they appear in the JSON input.
29+
///
30+
/// \param InputBuffer JSON input data.
31+
/// \param Destination Container to load headers into.
32+
static llvm::Error
33+
loadHeaders(std::unique_ptr<llvm::MemoryBuffer> InputBuffer,
34+
HeaderSeq &Destination);
35+
36+
FileListReader() = delete;
37+
};
38+
39+
} // namespace installapi
40+
} // namespace clang
41+
42+
#endif // LLVM_CLANG_INSTALLAPI_FILELIST_H
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//===- InstallAPI/HeaderFile.h ----------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
///
9+
/// Representations of a library's headers for InstallAPI.
10+
///
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_CLANG_INSTALLAPI_HEADERFILE_H
14+
#define LLVM_CLANG_INSTALLAPI_HEADERFILE_H
15+
16+
#include "clang/Basic/LangStandard.h"
17+
#include "llvm/ADT/StringRef.h"
18+
#include "llvm/Support/Regex.h"
19+
#include <optional>
20+
#include <string>
21+
22+
namespace clang::installapi {
23+
enum class HeaderType {
24+
/// Represents declarations accessible to all clients.
25+
Public,
26+
/// Represents declarations accessible to a disclosed set of clients.
27+
Private,
28+
/// Represents declarations only accessible as implementation details to the
29+
/// input library.
30+
Project,
31+
};
32+
33+
class HeaderFile {
34+
/// Full input path to header.
35+
std::string FullPath;
36+
/// Access level of header.
37+
HeaderType Type;
38+
/// Expected way header will be included by clients.
39+
std::string IncludeName;
40+
/// Supported language mode for header.
41+
std::optional<clang::Language> Language;
42+
43+
public:
44+
HeaderFile(StringRef FullPath, HeaderType Type,
45+
StringRef IncludeName = StringRef(),
46+
std::optional<clang::Language> Language = std::nullopt)
47+
: FullPath(FullPath), Type(Type), IncludeName(IncludeName),
48+
Language(Language) {}
49+
50+
static llvm::Regex getFrameworkIncludeRule();
51+
52+
bool operator==(const HeaderFile &Other) const {
53+
return std::tie(Type, FullPath, IncludeName, Language) ==
54+
std::tie(Other.Type, Other.FullPath, Other.IncludeName,
55+
Other.Language);
56+
}
57+
};
58+
59+
/// Assemble expected way header will be included by clients.
60+
/// As in what maps inside the brackets of `#include <IncludeName.h>`
61+
/// For example,
62+
/// "/System/Library/Frameworks/Foo.framework/Headers/Foo.h" returns
63+
/// "Foo/Foo.h"
64+
///
65+
/// \param FullPath Path to the header file which includes the library
66+
/// structure.
67+
std::optional<std::string> createIncludeHeaderName(const StringRef FullPath);
68+
using HeaderSeq = std::vector<HeaderFile>;
69+
70+
} // namespace clang::installapi
71+
72+
#endif // LLVM_CLANG_INSTALLAPI_HEADERFILE_H

‎clang/lib/ExtractAPI/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ add_clang_library(clangExtractAPI
1616
clangBasic
1717
clangFrontend
1818
clangIndex
19+
clangInstallAPI
1920
clangLex
2021
)

‎clang/lib/ExtractAPI/ExtractAPIConsumer.cpp

+3-4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "clang/Frontend/CompilerInstance.h"
3131
#include "clang/Frontend/FrontendOptions.h"
3232
#include "clang/Frontend/MultiplexConsumer.h"
33+
#include "clang/InstallAPI/HeaderFile.h"
3334
#include "clang/Lex/MacroInfo.h"
3435
#include "clang/Lex/PPCallbacks.h"
3536
#include "clang/Lex/Preprocessor.h"
@@ -61,9 +62,6 @@ std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
6162
"CompilerInstance does not have a FileNamager!");
6263

6364
using namespace llvm::sys;
64-
// Matches framework include patterns
65-
const llvm::Regex Rule("/(.+)\\.framework/(.+)?Headers/(.+)");
66-
6765
const auto &FS = CI.getVirtualFileSystem();
6866

6967
SmallString<128> FilePath(File.begin(), File.end());
@@ -147,7 +145,8 @@ std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
147145
// include name `<Framework/Header.h>`
148146
if (Entry.IsFramework) {
149147
SmallVector<StringRef, 4> Matches;
150-
Rule.match(File, &Matches);
148+
clang::installapi::HeaderFile::getFrameworkIncludeRule().match(
149+
File, &Matches);
151150
// Returned matches are always in stable order.
152151
if (Matches.size() != 4)
153152
return std::nullopt;

‎clang/lib/InstallAPI/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ set(LLVM_LINK_COMPONENTS
55

66
add_clang_library(clangInstallAPI
77
Context.cpp
8+
FileList.cpp
9+
HeaderFile.cpp
810

911
LINK_LIBS
1012
clangAST

‎clang/lib/InstallAPI/FileList.cpp

+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
//===- FileList.cpp ---------------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "clang/InstallAPI/FileList.h"
10+
#include "clang/Basic/DiagnosticFrontend.h"
11+
#include "clang/InstallAPI/FileList.h"
12+
#include "llvm/ADT/StringSwitch.h"
13+
#include "llvm/Support/Error.h"
14+
#include "llvm/Support/JSON.h"
15+
#include "llvm/TextAPI/TextAPIError.h"
16+
#include <optional>
17+
18+
// clang-format off
19+
/*
20+
InstallAPI JSON Input Format specification.
21+
22+
{
23+
"headers" : [ # Required: Key must exist.
24+
{ # Optional: May contain 0 or more header inputs.
25+
"path" : "/usr/include/mach-o/dlfn.h", # Required: Path should point to destination
26+
# location where applicable.
27+
"type" : "public", # Required: Maps to HeaderType for header.
28+
"language": "c++" # Optional: Language mode for header.
29+
}
30+
],
31+
"version" : "3" # Required: Version 3 supports language mode
32+
& project header input.
33+
}
34+
*/
35+
// clang-format on
36+
37+
using namespace llvm;
38+
using namespace llvm::json;
39+
using namespace llvm::MachO;
40+
using namespace clang::installapi;
41+
42+
namespace {
43+
class Implementation {
44+
private:
45+
Expected<StringRef> parseString(const Object *Obj, StringRef Key,
46+
StringRef Error);
47+
Expected<StringRef> parsePath(const Object *Obj);
48+
Expected<HeaderType> parseType(const Object *Obj);
49+
std::optional<clang::Language> parseLanguage(const Object *Obj);
50+
Error parseHeaders(Array &Headers);
51+
52+
public:
53+
std::unique_ptr<MemoryBuffer> InputBuffer;
54+
unsigned Version;
55+
HeaderSeq HeaderList;
56+
57+
Error parse(StringRef Input);
58+
};
59+
60+
Expected<StringRef>
61+
Implementation::parseString(const Object *Obj, StringRef Key, StringRef Error) {
62+
auto Str = Obj->getString(Key);
63+
if (!Str)
64+
return make_error<StringError>(Error, inconvertibleErrorCode());
65+
return *Str;
66+
}
67+
68+
Expected<HeaderType> Implementation::parseType(const Object *Obj) {
69+
auto TypeStr =
70+
parseString(Obj, "type", "required field 'type' not specified");
71+
if (!TypeStr)
72+
return TypeStr.takeError();
73+
74+
if (*TypeStr == "public")
75+
return HeaderType::Public;
76+
else if (*TypeStr == "private")
77+
return HeaderType::Private;
78+
else if (*TypeStr == "project" && Version >= 2)
79+
return HeaderType::Project;
80+
81+
return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
82+
"unsupported header type");
83+
}
84+
85+
Expected<StringRef> Implementation::parsePath(const Object *Obj) {
86+
auto Path = parseString(Obj, "path", "required field 'path' not specified");
87+
if (!Path)
88+
return Path.takeError();
89+
90+
return *Path;
91+
}
92+
93+
std::optional<clang::Language>
94+
Implementation::parseLanguage(const Object *Obj) {
95+
auto Language = Obj->getString("language");
96+
if (!Language)
97+
return std::nullopt;
98+
99+
return StringSwitch<clang::Language>(*Language)
100+
.Case("c", clang::Language::C)
101+
.Case("c++", clang::Language::CXX)
102+
.Case("objective-c", clang::Language::ObjC)
103+
.Case("objective-c++", clang::Language::ObjCXX)
104+
.Default(clang::Language::Unknown);
105+
}
106+
107+
Error Implementation::parseHeaders(Array &Headers) {
108+
for (const auto &H : Headers) {
109+
auto *Obj = H.getAsObject();
110+
if (!Obj)
111+
return make_error<StringError>("expect a JSON object",
112+
inconvertibleErrorCode());
113+
auto Type = parseType(Obj);
114+
if (!Type)
115+
return Type.takeError();
116+
auto Path = parsePath(Obj);
117+
if (!Path)
118+
return Path.takeError();
119+
auto Language = parseLanguage(Obj);
120+
121+
StringRef PathStr = *Path;
122+
if (*Type == HeaderType::Project) {
123+
HeaderList.emplace_back(
124+
HeaderFile{PathStr, *Type, /*IncludeName=*/"", Language});
125+
continue;
126+
}
127+
auto IncludeName = createIncludeHeaderName(PathStr);
128+
HeaderList.emplace_back(PathStr, *Type,
129+
IncludeName.has_value() ? IncludeName.value() : "",
130+
Language);
131+
}
132+
133+
return Error::success();
134+
}
135+
136+
Error Implementation::parse(StringRef Input) {
137+
auto Val = json::parse(Input);
138+
if (!Val)
139+
return Val.takeError();
140+
141+
auto *Root = Val->getAsObject();
142+
if (!Root)
143+
return make_error<StringError>("not a JSON object",
144+
inconvertibleErrorCode());
145+
146+
auto VersionStr = Root->getString("version");
147+
if (!VersionStr)
148+
return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
149+
"required field 'version' not specified");
150+
if (VersionStr->getAsInteger(10, Version))
151+
return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
152+
"invalid version number");
153+
154+
if (Version < 1 || Version > 3)
155+
return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat,
156+
"unsupported version");
157+
158+
// Not specifying any header files should be atypical, but valid.
159+
auto Headers = Root->getArray("headers");
160+
if (!Headers)
161+
return Error::success();
162+
163+
Error Err = parseHeaders(*Headers);
164+
if (Err)
165+
return Err;
166+
167+
return Error::success();
168+
}
169+
} // namespace
170+
171+
llvm::Error
172+
FileListReader::loadHeaders(std::unique_ptr<MemoryBuffer> InputBuffer,
173+
HeaderSeq &Destination) {
174+
llvm::Error Err = Error::success();
175+
Implementation Impl;
176+
ErrorAsOutParameter ErrorAsOutParam(&Err);
177+
Impl.InputBuffer = std::move(InputBuffer);
178+
179+
Err = Impl.parse(Impl.InputBuffer->getBuffer());
180+
if (Err)
181+
return Err;
182+
183+
Destination.reserve(Destination.size() + Impl.HeaderList.size());
184+
llvm::move(Impl.HeaderList, std::back_inserter(Destination));
185+
186+
return Err;
187+
}

‎clang/lib/InstallAPI/HeaderFile.cpp

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===- HeaderFile.cpp ------------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "clang/InstallAPI/HeaderFile.h"
10+
11+
using namespace llvm;
12+
namespace clang::installapi {
13+
14+
llvm::Regex HeaderFile::getFrameworkIncludeRule() {
15+
return llvm::Regex("/(.+)\\.framework/(.+)?Headers/(.+)");
16+
}
17+
18+
std::optional<std::string> createIncludeHeaderName(const StringRef FullPath) {
19+
// Headers in usr(/local)*/include.
20+
std::string Pattern = "/include/";
21+
auto PathPrefix = FullPath.find(Pattern);
22+
if (PathPrefix != StringRef::npos) {
23+
PathPrefix += Pattern.size();
24+
return FullPath.drop_front(PathPrefix).str();
25+
}
26+
27+
// Framework Headers.
28+
SmallVector<StringRef, 4> Matches;
29+
HeaderFile::getFrameworkIncludeRule().match(FullPath, &Matches);
30+
// Returned matches are always in stable order.
31+
if (Matches.size() != 4)
32+
return std::nullopt;
33+
34+
return Matches[1].drop_front(Matches[1].rfind('/') + 1).str() + "/" +
35+
Matches[3].str();
36+
}
37+
} // namespace clang::installapi

‎clang/unittests/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,6 @@ endif()
5151
add_subdirectory(DirectoryWatcher)
5252
add_subdirectory(Rename)
5353
add_subdirectory(Index)
54+
add_subdirectory(InstallAPI)
5455
add_subdirectory(Serialization)
5556
add_subdirectory(Support)
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
add_clang_unittest(InstallAPITests
2+
HeaderFileTest.cpp
3+
FileListTest.cpp
4+
)
5+
6+
clang_target_link_libraries(InstallAPITests
7+
PRIVATE
8+
clangInstallAPI
9+
)
10+
11+
target_link_libraries(InstallAPITests PRIVATE LLVMTestingSupport)
+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//===- unittests/InstallAPI/FileList.cpp - File List Tests ---------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "clang/InstallAPI/FileList.h"
10+
#include "clang/InstallAPI/HeaderFile.h"
11+
#include "llvm/ADT/StringRef.h"
12+
#include "llvm/Support/MemoryBuffer.h"
13+
#include "gtest/gtest.h"
14+
15+
using namespace llvm;
16+
using namespace clang::installapi;
17+
18+
namespace FileListTests {
19+
20+
static inline void testValidFileList(std::string Input, HeaderSeq &Expected) {
21+
auto InputBuf = MemoryBuffer::getMemBuffer(Input);
22+
HeaderSeq Headers;
23+
llvm::Error Err = FileListReader::loadHeaders(std::move(InputBuf), Headers);
24+
EXPECT_FALSE(!!Err);
25+
26+
EXPECT_EQ(Expected.size(), Headers.size());
27+
EXPECT_EQ(Headers, Expected);
28+
}
29+
30+
TEST(FileListReader, Version3) {
31+
static const char Input[] = R"({
32+
"version" : "3",
33+
"headers" : [
34+
{
35+
"type" : "public",
36+
"path" : "/tmp/dst/usr/include/foo.h",
37+
"language" : "objective-c"
38+
},
39+
{
40+
"type" : "private",
41+
"path" : "/tmp/dst/usr/local/include/bar.h",
42+
"language" : "objective-c++"
43+
},
44+
{
45+
"type" : "project",
46+
"path" : "/tmp/src/baz.h"
47+
}
48+
]
49+
})";
50+
51+
HeaderSeq Expected = {
52+
{"/tmp/dst/usr/include/foo.h", HeaderType::Public, "foo.h",
53+
clang::Language::ObjC},
54+
{"/tmp/dst/usr/local/include/bar.h", HeaderType::Private, "bar.h",
55+
clang::Language::ObjCXX},
56+
{"/tmp/src/baz.h", HeaderType::Project, "", std::nullopt}};
57+
58+
testValidFileList(Input, Expected);
59+
}
60+
61+
TEST(FileList, Version1) {
62+
static const char Input[] = R"({
63+
"version" : "1",
64+
"headers" : [
65+
{
66+
"type" : "public",
67+
"path" : "/usr/include/foo.h"
68+
},
69+
{
70+
"type" : "private",
71+
"path" : "/usr/local/include/bar.h"
72+
}
73+
]
74+
})";
75+
76+
HeaderSeq Expected = {
77+
{"/usr/include/foo.h", HeaderType::Public, "foo.h", std::nullopt},
78+
{"/usr/local/include/bar.h", HeaderType::Private, "bar.h", std::nullopt},
79+
};
80+
81+
testValidFileList(Input, Expected);
82+
}
83+
84+
TEST(FileListReader, Version2) {
85+
static const auto Input = R"({
86+
"version" : "2",
87+
"headers" : [
88+
{
89+
"type" : "public",
90+
"path" : "/usr/include/foo.h"
91+
},
92+
{
93+
"type" : "project",
94+
"path" : "src/bar.h"
95+
}
96+
]
97+
})";
98+
HeaderSeq Expected = {
99+
{"/usr/include/foo.h", HeaderType::Public, "foo.h", std::nullopt},
100+
{"src/bar.h", HeaderType::Project, "", std::nullopt},
101+
};
102+
103+
testValidFileList(Input, Expected);
104+
}
105+
106+
TEST(FileList, MissingVersion) {
107+
static const char Input[] = R"({
108+
"headers" : [
109+
{
110+
"type" : "public",
111+
"path" : "/usr/include/foo.h"
112+
},
113+
{
114+
"type" : "private",
115+
"path" : "/usr/local/include/bar.h"
116+
}
117+
]
118+
})";
119+
auto InputBuf = MemoryBuffer::getMemBuffer(Input);
120+
HeaderSeq Headers;
121+
llvm::Error Err = FileListReader::loadHeaders(std::move(InputBuf), Headers);
122+
EXPECT_TRUE(!!Err);
123+
EXPECT_STREQ("invalid input format: required field 'version' not specified\n",
124+
toString(std::move(Err)).c_str());
125+
}
126+
127+
TEST(FileList, InvalidTypes) {
128+
static const char Input[] = R"({
129+
"version" : "1",
130+
"headers" : [
131+
{
132+
"type" : "project",
133+
"path" : "/usr/include/foo.h"
134+
}
135+
]
136+
})";
137+
auto InputBuf = MemoryBuffer::getMemBuffer(Input);
138+
HeaderSeq Headers;
139+
llvm::Error Err = FileListReader::loadHeaders(std::move(InputBuf), Headers);
140+
EXPECT_TRUE(!!Err);
141+
EXPECT_STREQ("invalid input format: unsupported header type\n",
142+
toString(std::move(Err)).c_str());
143+
}
144+
} // namespace FileListTests
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//===- unittests/InstallAPI/HeaderFile.cpp - HeaderFile Test --------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "clang/InstallAPI/HeaderFile.h"
10+
#include "gtest/gtest.h"
11+
12+
using namespace llvm;
13+
using namespace clang::installapi;
14+
15+
namespace HeaderFileTests {
16+
17+
TEST(HeaderFile, FrameworkIncludes) {
18+
const char *Path = "/System/Library/Frameworks/Foo.framework/Headers/Foo.h";
19+
std::optional<std::string> IncludeName = createIncludeHeaderName(Path);
20+
EXPECT_TRUE(IncludeName.has_value());
21+
EXPECT_STREQ(IncludeName.value().c_str(), "Foo/Foo.h");
22+
23+
Path = "/System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/"
24+
"Headers/SimpleBar.h";
25+
IncludeName = createIncludeHeaderName(Path);
26+
EXPECT_TRUE(IncludeName.has_value());
27+
EXPECT_STREQ(IncludeName.value().c_str(), "Bar/SimpleBar.h");
28+
29+
Path = "/tmp/Foo.framework/Versions/A/Headers/SimpleFoo.h";
30+
IncludeName = createIncludeHeaderName(Path);
31+
EXPECT_TRUE(IncludeName.has_value());
32+
EXPECT_STREQ(IncludeName.value().c_str(), "Foo/SimpleFoo.h");
33+
34+
Path = "/System/Library/PrivateFrameworks/Foo.framework/Headers/Foo.h";
35+
IncludeName = createIncludeHeaderName(Path);
36+
EXPECT_TRUE(IncludeName.has_value());
37+
EXPECT_STREQ(IncludeName.value().c_str(), "Foo/Foo.h");
38+
39+
Path = "/AppleInternal/Developer/Library/Frameworks/"
40+
"HelloFramework.framework/Headers/HelloFramework.h";
41+
IncludeName = createIncludeHeaderName(Path);
42+
EXPECT_TRUE(IncludeName.has_value());
43+
EXPECT_STREQ(IncludeName.value().c_str(), "HelloFramework/HelloFramework.h");
44+
45+
Path = "/tmp/BuildProducts/Foo.framework/Versions/A/"
46+
"PrivateHeaders/Foo+Private.h";
47+
IncludeName = createIncludeHeaderName(Path);
48+
EXPECT_TRUE(IncludeName.has_value());
49+
EXPECT_STREQ(IncludeName.value().c_str(), "Foo/Foo+Private.h");
50+
51+
Path = "/Applications/Xcode.app/Contents/Developer/SDKS/MacOS.sdk/System/"
52+
"Library/Frameworks/Foo.framework/PrivateHeaders/Foo_Private.h";
53+
IncludeName = createIncludeHeaderName(Path);
54+
EXPECT_TRUE(IncludeName.has_value());
55+
EXPECT_STREQ(IncludeName.value().c_str(), "Foo/Foo_Private.h");
56+
57+
Path =
58+
"/System/Library/PrivateFrameworks/Foo.framework/PrivateHeaders/Foo.hpp";
59+
IncludeName = createIncludeHeaderName(Path);
60+
EXPECT_TRUE(IncludeName.has_value());
61+
EXPECT_STREQ(IncludeName.value().c_str(), "Foo/Foo.hpp");
62+
63+
Path = "/Applications/Xcode.app/Contents/Developer/SDKS/MacOS.sdk/System/"
64+
"Library/Frameworks/Foo.framework/Headers/BarDir/Bar.h";
65+
IncludeName = createIncludeHeaderName(Path);
66+
EXPECT_TRUE(IncludeName.has_value());
67+
EXPECT_STREQ(IncludeName.value().c_str(), "Foo/BarDir/Bar.h");
68+
}
69+
70+
TEST(HeaderFile, DylibIncludes) {
71+
const char *Path = "/usr/include/foo.h";
72+
std::optional<std::string> IncludeName = createIncludeHeaderName(Path);
73+
EXPECT_TRUE(IncludeName.has_value());
74+
EXPECT_STREQ(IncludeName.value().c_str(), "foo.h");
75+
76+
Path = "/tmp/BuildProducts/usr/include/a/A.h";
77+
IncludeName = createIncludeHeaderName(Path);
78+
EXPECT_TRUE(IncludeName.has_value());
79+
EXPECT_STREQ(IncludeName.value().c_str(), "a/A.h");
80+
81+
Path = "/Applications/Xcode.app/Contents/Developer/SDKS/MacOS.sdk/"
82+
"usr/include/simd/types.h";
83+
IncludeName = createIncludeHeaderName(Path);
84+
EXPECT_TRUE(IncludeName.has_value());
85+
EXPECT_STREQ(IncludeName.value().c_str(), "simd/types.h");
86+
87+
Path = "/usr/local/include/hidden/A.h";
88+
IncludeName = createIncludeHeaderName(Path);
89+
EXPECT_TRUE(IncludeName.has_value());
90+
EXPECT_STREQ(IncludeName.value().c_str(), "hidden/A.h");
91+
}
92+
} // namespace HeaderFileTests

0 commit comments

Comments
 (0)
Please sign in to comment.