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