Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/InstallAPI/DirectoryScanner.cpp
35232 views
1
//===- DirectoryScanner.cpp -----------------------------------------------===//
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/DirectoryScanner.h"
10
#include "llvm/ADT/StringRef.h"
11
#include "llvm/ADT/StringSwitch.h"
12
#include "llvm/TextAPI/DylibReader.h"
13
14
using namespace llvm;
15
using namespace llvm::MachO;
16
17
namespace clang::installapi {
18
19
HeaderSeq DirectoryScanner::getHeaders(ArrayRef<Library> Libraries) {
20
HeaderSeq Headers;
21
for (const Library &Lib : Libraries)
22
llvm::append_range(Headers, Lib.Headers);
23
return Headers;
24
}
25
26
llvm::Error DirectoryScanner::scan(StringRef Directory) {
27
if (Mode == ScanMode::ScanFrameworks)
28
return scanForFrameworks(Directory);
29
30
return scanForUnwrappedLibraries(Directory);
31
}
32
33
llvm::Error DirectoryScanner::scanForUnwrappedLibraries(StringRef Directory) {
34
// Check some known sub-directory locations.
35
auto GetDirectory = [&](const char *Sub) -> OptionalDirectoryEntryRef {
36
SmallString<PATH_MAX> Path(Directory);
37
sys::path::append(Path, Sub);
38
return FM.getOptionalDirectoryRef(Path);
39
};
40
41
auto DirPublic = GetDirectory("usr/include");
42
auto DirPrivate = GetDirectory("usr/local/include");
43
if (!DirPublic && !DirPrivate) {
44
std::error_code ec = std::make_error_code(std::errc::not_a_directory);
45
return createStringError(ec,
46
"cannot find any public (usr/include) or private "
47
"(usr/local/include) header directory");
48
}
49
50
Library &Lib = getOrCreateLibrary(Directory, Libraries);
51
Lib.IsUnwrappedDylib = true;
52
53
if (DirPublic)
54
if (Error Err = scanHeaders(DirPublic->getName(), Lib, HeaderType::Public,
55
Directory))
56
return Err;
57
58
if (DirPrivate)
59
if (Error Err = scanHeaders(DirPrivate->getName(), Lib, HeaderType::Private,
60
Directory))
61
return Err;
62
63
return Error::success();
64
}
65
66
static bool isFramework(StringRef Path) {
67
while (Path.back() == '/')
68
Path = Path.slice(0, Path.size() - 1);
69
70
return llvm::StringSwitch<bool>(llvm::sys::path::extension(Path))
71
.Case(".framework", true)
72
.Default(false);
73
}
74
75
Library &
76
DirectoryScanner::getOrCreateLibrary(StringRef Path,
77
std::vector<Library> &Libs) const {
78
if (Path.consume_front(RootPath) && Path.empty())
79
Path = "/";
80
81
auto LibIt =
82
find_if(Libs, [Path](const Library &L) { return L.getPath() == Path; });
83
if (LibIt != Libs.end())
84
return *LibIt;
85
86
Libs.emplace_back(Path);
87
return Libs.back();
88
}
89
90
Error DirectoryScanner::scanHeaders(StringRef Path, Library &Lib,
91
HeaderType Type, StringRef BasePath,
92
StringRef ParentPath) const {
93
std::error_code ec;
94
auto &FS = FM.getVirtualFileSystem();
95
PathSeq SubDirectories;
96
for (vfs::directory_iterator i = FS.dir_begin(Path, ec), ie; i != ie;
97
i.increment(ec)) {
98
StringRef HeaderPath = i->path();
99
if (ec)
100
return createStringError(ec, "unable to read: " + HeaderPath);
101
102
if (sys::fs::is_symlink_file(HeaderPath))
103
continue;
104
105
// Ignore tmp files from unifdef.
106
const StringRef Filename = sys::path::filename(HeaderPath);
107
if (Filename.starts_with("."))
108
continue;
109
110
// If it is a directory, remember the subdirectory.
111
if (FM.getOptionalDirectoryRef(HeaderPath))
112
SubDirectories.push_back(HeaderPath.str());
113
114
if (!isHeaderFile(HeaderPath))
115
continue;
116
117
// Skip files that do not exist. This usually happens for broken symlinks.
118
if (FS.status(HeaderPath) == std::errc::no_such_file_or_directory)
119
continue;
120
121
auto IncludeName = createIncludeHeaderName(HeaderPath);
122
Lib.addHeaderFile(HeaderPath, Type,
123
IncludeName.has_value() ? IncludeName.value() : "");
124
}
125
126
// Go through the subdirectories.
127
// Sort the sub-directory first since different file systems might have
128
// different traverse order.
129
llvm::sort(SubDirectories);
130
if (ParentPath.empty())
131
ParentPath = Path;
132
for (const StringRef Dir : SubDirectories)
133
return scanHeaders(Dir, Lib, Type, BasePath, ParentPath);
134
135
return Error::success();
136
}
137
138
llvm::Error
139
DirectoryScanner::scanMultipleFrameworks(StringRef Directory,
140
std::vector<Library> &Libs) const {
141
std::error_code ec;
142
auto &FS = FM.getVirtualFileSystem();
143
for (vfs::directory_iterator i = FS.dir_begin(Directory, ec), ie; i != ie;
144
i.increment(ec)) {
145
StringRef Curr = i->path();
146
147
// Skip files that do not exist. This usually happens for broken symlinks.
148
if (ec == std::errc::no_such_file_or_directory) {
149
ec.clear();
150
continue;
151
}
152
if (ec)
153
return createStringError(ec, Curr);
154
155
if (sys::fs::is_symlink_file(Curr))
156
continue;
157
158
if (isFramework(Curr)) {
159
if (!FM.getOptionalDirectoryRef(Curr))
160
continue;
161
Library &Framework = getOrCreateLibrary(Curr, Libs);
162
if (Error Err = scanFrameworkDirectory(Curr, Framework))
163
return Err;
164
}
165
}
166
167
return Error::success();
168
}
169
170
llvm::Error
171
DirectoryScanner::scanSubFrameworksDirectory(StringRef Directory,
172
std::vector<Library> &Libs) const {
173
if (FM.getOptionalDirectoryRef(Directory))
174
return scanMultipleFrameworks(Directory, Libs);
175
176
std::error_code ec = std::make_error_code(std::errc::not_a_directory);
177
return createStringError(ec, Directory);
178
}
179
180
/// FIXME: How to handle versions? For now scan them separately as independent
181
/// frameworks.
182
llvm::Error
183
DirectoryScanner::scanFrameworkVersionsDirectory(StringRef Path,
184
Library &Lib) const {
185
std::error_code ec;
186
auto &FS = FM.getVirtualFileSystem();
187
for (vfs::directory_iterator i = FS.dir_begin(Path, ec), ie; i != ie;
188
i.increment(ec)) {
189
const StringRef Curr = i->path();
190
191
// Skip files that do not exist. This usually happens for broken symlinks.
192
if (ec == std::errc::no_such_file_or_directory) {
193
ec.clear();
194
continue;
195
}
196
if (ec)
197
return createStringError(ec, Curr);
198
199
if (sys::fs::is_symlink_file(Curr))
200
continue;
201
202
// Each version should be a framework directory.
203
if (!FM.getOptionalDirectoryRef(Curr))
204
continue;
205
206
Library &VersionedFramework =
207
getOrCreateLibrary(Curr, Lib.FrameworkVersions);
208
if (Error Err = scanFrameworkDirectory(Curr, VersionedFramework))
209
return Err;
210
}
211
212
return Error::success();
213
}
214
215
llvm::Error DirectoryScanner::scanFrameworkDirectory(StringRef Path,
216
Library &Framework) const {
217
// If the framework is inside Kernel or IOKit, scan headers in the different
218
// directories separately.
219
Framework.IsUnwrappedDylib =
220
Path.contains("Kernel.framework") || Path.contains("IOKit.framework");
221
222
// Unfortunately we cannot identify symlinks in the VFS. We assume that if
223
// there is a Versions directory, then we have symlinks and directly proceed
224
// to the Versions folder.
225
std::error_code ec;
226
auto &FS = FM.getVirtualFileSystem();
227
228
for (vfs::directory_iterator i = FS.dir_begin(Path, ec), ie; i != ie;
229
i.increment(ec)) {
230
StringRef Curr = i->path();
231
// Skip files that do not exist. This usually happens for broken symlinks.
232
if (ec == std::errc::no_such_file_or_directory) {
233
ec.clear();
234
continue;
235
}
236
237
if (ec)
238
return createStringError(ec, Curr);
239
240
if (sys::fs::is_symlink_file(Curr))
241
continue;
242
243
StringRef FileName = sys::path::filename(Curr);
244
// Scan all "public" headers.
245
if (FileName.contains("Headers")) {
246
if (Error Err = scanHeaders(Curr, Framework, HeaderType::Public, Curr))
247
return Err;
248
continue;
249
}
250
// Scan all "private" headers.
251
if (FileName.contains("PrivateHeaders")) {
252
if (Error Err = scanHeaders(Curr, Framework, HeaderType::Private, Curr))
253
return Err;
254
continue;
255
}
256
// Scan sub frameworks.
257
if (FileName.contains("Frameworks")) {
258
if (Error Err = scanSubFrameworksDirectory(Curr, Framework.SubFrameworks))
259
return Err;
260
continue;
261
}
262
// Check for versioned frameworks.
263
if (FileName.contains("Versions")) {
264
if (Error Err = scanFrameworkVersionsDirectory(Curr, Framework))
265
return Err;
266
continue;
267
}
268
}
269
270
return Error::success();
271
}
272
273
llvm::Error DirectoryScanner::scanForFrameworks(StringRef Directory) {
274
RootPath = "";
275
276
// Expect a certain directory structure and naming convention to find
277
// frameworks.
278
static const char *SubDirectories[] = {"System/Library/Frameworks/",
279
"System/Library/PrivateFrameworks/"};
280
281
// Check if the directory is already a framework.
282
if (isFramework(Directory)) {
283
Library &Framework = getOrCreateLibrary(Directory, Libraries);
284
if (Error Err = scanFrameworkDirectory(Directory, Framework))
285
return Err;
286
return Error::success();
287
}
288
289
// Check known sub-directory locations.
290
for (const auto *SubDir : SubDirectories) {
291
SmallString<PATH_MAX> Path(Directory);
292
sys::path::append(Path, SubDir);
293
294
if (Error Err = scanMultipleFrameworks(Path, Libraries))
295
return Err;
296
}
297
298
return Error::success();
299
}
300
} // namespace clang::installapi
301
302