CppInterOp
C++ Language Interoperability Layer
Loading...
Searching...
No Matches
Paths.cpp
Go to the documentation of this file.
1//--------------------------------------------------------------------*- C++ -*-
2// CLING - the C++ LLVM-based InterpreterG :)
3// author:
4//
5// This file is dual-licensed: you can choose to license it under the University
6// of Illinois Open Source License or the GNU Lesser General Public License. See
7// LICENSE.TXT for details.
8//------------------------------------------------------------------------------
9
10#include "Paths.h"
11#include "Compatibility.h"
12
13#include "clang/Basic/FileManager.h"
14#include "clang/Lex/HeaderSearchOptions.h"
15
16#include "llvm/Support/Debug.h"
17#include "llvm/Support/DynamicLibrary.h"
18#include "llvm/Support/ErrorHandling.h"
19#include "llvm/Support/FileSystem.h"
20#include "llvm/Support/Path.h"
21
22#if defined(LLVM_ON_UNIX)
23#include <dlfcn.h>
24#endif
25
26namespace CppInternal {
27namespace utils {
28
29namespace platform {
30#if defined(LLVM_ON_UNIX)
31const char* const kEnvDelim = ":";
32#elif defined(_WIN32)
33const char* const kEnvDelim = ";";
34#else
35#error "Unknown platform (environmental delimiter)"
36#endif
37
38#if defined(LLVM_ON_UNIX)
39bool Popen(const std::string& Cmd, llvm::SmallVectorImpl<char>& Buf, bool RdE) {
40 if (FILE* PF = ::popen(RdE ? (Cmd + " 2>&1").c_str() : Cmd.c_str(), "r")) {
41 Buf.resize(0);
42 const size_t Chunk = Buf.capacity_in_bytes();
43 while (true) {
44 const size_t Len = Buf.size();
45 Buf.resize(Len + Chunk);
46 const size_t R = ::fread(&Buf[Len], sizeof(char), Chunk, PF);
47 if (R < Chunk) {
48 Buf.resize(Len + R);
49 break;
50 }
51 }
52 ::pclose(PF);
53 return !Buf.empty();
54 }
55 return false;
56}
57#endif
58
59bool GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
60#if defined(__APPLE__) || defined(__CYGWIN__)
61 Paths.push_back("/usr/local/lib/");
62 Paths.push_back("/usr/X11R6/lib/");
63 Paths.push_back("/usr/lib/");
64 Paths.push_back("/lib/");
65
66#ifndef __APPLE__
67 Paths.push_back("/lib/x86_64-linux-gnu/");
68 Paths.push_back("/usr/local/lib64/");
69 Paths.push_back("/usr/lib64/");
70 Paths.push_back("/lib64/");
71#endif
72#elif defined(LLVM_ON_UNIX)
73 llvm::SmallString<1024> Buf;
74 platform::Popen("LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls", Buf, true);
75 const llvm::StringRef Result = Buf.str();
76
77 const std::size_t NPos = std::string::npos;
78 const std::size_t LD = Result.find("(LD_LIBRARY_PATH)");
79 std::size_t From = Result.find("search path=", LD == NPos ? 0 : LD);
80 if (From != NPos) {
81 std::size_t To = Result.find("(system search path)", From);
82 if (To != NPos) {
83 From += 12;
84 while (To > From && isspace(Result[To - 1]))
85 --To;
86 std::string SysPath = Result.substr(From, To - From).str();
87 SysPath.erase(std::remove_if(SysPath.begin(), SysPath.end(), ::isspace),
88 SysPath.end());
89
90 llvm::SmallVector<llvm::StringRef, 10> CurPaths;
91 SplitPaths(SysPath, CurPaths);
92 for (const auto& Path : CurPaths)
93 Paths.push_back(Path.str());
94 }
95 }
96#endif
97 return true;
98}
99
100std::string NormalizePath(const std::string& Path) {
101
102 llvm::SmallString<256> Buffer;
103 std::error_code EC = llvm::sys::fs::real_path(Path, Buffer, true);
104 if (EC)
105 return std::string();
106 return std::string(Buffer.str());
107}
108
109#if defined(LLVM_ON_UNIX)
110static void DLErr(std::string* Err) {
111 if (!Err)
112 return;
113 if (const char* DyLibError = ::dlerror())
114 *Err = DyLibError;
115}
116
117void* DLOpen(const std::string& Path, std::string* Err /* = nullptr */) {
118 void* Lib = ::dlopen(Path.c_str(), RTLD_LAZY | RTLD_GLOBAL);
119 DLErr(Err);
120 return Lib;
121}
122
123void DLClose(void* Lib, std::string* Err /* = nullptr*/) {
124 ::dlclose(Lib);
125 DLErr(Err);
126}
127#elif defined(_WIN32)
128
129void* DLOpen(const std::string& Path, std::string* Err /* = nullptr */) {
130 auto lib = llvm::sys::DynamicLibrary::getLibrary(Path.c_str(), Err);
131 return lib.getOSSpecificHandle();
132}
133
134void DLClose(void* Lib, std::string* Err /* = nullptr*/) {
135 auto dl = llvm::sys::DynamicLibrary(Lib);
136 llvm::sys::DynamicLibrary::closeLibrary(dl);
137 if (Err) {
138 *Err = std::string();
139 }
140}
141#endif
142
143} // namespace platform
144
145using namespace llvm;
146using namespace clang;
147
148// Adapted from clang/lib/Frontend/CompilerInvocation.cpp
149
150void CopyIncludePaths(const clang::HeaderSearchOptions& Opts,
151 llvm::SmallVectorImpl<std::string>& incpaths,
152 bool withSystem, bool withFlags) {
153 if (withFlags && Opts.Sysroot != "/") {
154 incpaths.push_back("-isysroot");
155 incpaths.push_back(Opts.Sysroot);
156 }
157
158 /// User specified include entries.
159 for (unsigned i = 0, e = Opts.UserEntries.size(); i != e; ++i) {
160 const HeaderSearchOptions::Entry& E = Opts.UserEntries[i];
161 if (E.IsFramework && E.Group != frontend::Angled &&
162 E.Group != frontend::System)
163 llvm::report_fatal_error("Invalid option set!");
164 switch (E.Group) {
165 case frontend::After:
166 if (withFlags)
167 incpaths.push_back("-idirafter");
168 break;
169
170 case frontend::Quoted:
171 if (withFlags)
172 incpaths.push_back("-iquote");
173 break;
174
175 case frontend::System:
176 if (!withSystem)
177 continue;
178 if (withFlags)
179 incpaths.push_back("-isystem");
180 break;
181
182 case frontend::CSystem:
183 if (!withSystem)
184 continue;
185 if (withFlags)
186 incpaths.push_back("-c-isystem");
187 break;
188
189 case frontend::ExternCSystem:
190 if (!withSystem)
191 continue;
192 if (withFlags)
193 incpaths.push_back("-extern-c-isystem");
194 break;
195
196 case frontend::CXXSystem:
197 if (!withSystem)
198 continue;
199 if (withFlags)
200 incpaths.push_back("-cxx-isystem");
201 break;
202
203 case frontend::ObjCSystem:
204 if (!withSystem)
205 continue;
206 if (withFlags)
207 incpaths.push_back("-objc-isystem");
208 break;
209
210 case frontend::ObjCXXSystem:
211 if (!withSystem)
212 continue;
213 if (withFlags)
214 incpaths.push_back("-objcxx-isystem");
215 break;
216
217 case frontend::Angled:
218 if (withFlags)
219 incpaths.push_back(E.IsFramework ? "-F" : "-I");
220 break;
221 }
222 incpaths.push_back(E.Path);
223 }
224
225 if (withSystem && !Opts.ResourceDir.empty()) {
226 if (withFlags)
227 incpaths.push_back("-resource-dir");
228 incpaths.push_back(Opts.ResourceDir);
229 }
230 if (withSystem && withFlags && !Opts.ModuleCachePath.empty()) {
231 incpaths.push_back("-fmodule-cache-path");
232 incpaths.push_back(Opts.ModuleCachePath);
233 }
234 if (withSystem && withFlags && !Opts.UseStandardSystemIncludes)
235 incpaths.push_back("-nostdinc");
236 if (withSystem && withFlags && !Opts.UseStandardCXXIncludes)
237 incpaths.push_back("-nostdinc++");
238 if (withSystem && withFlags && Opts.UseLibcxx)
239 incpaths.push_back("-stdlib=libc++");
240 if (withSystem && withFlags && Opts.Verbose)
241 incpaths.push_back("-v");
242}
243
244void LogNonExistentDirectory(llvm::StringRef Path) {
245#define DEBUG_TYPE "LogNonExistentDirectory"
246 LLVM_DEBUG(dbgs() << " ignoring nonexistent directory \"" << Path << "\"\n");
247#undef DEBUG_TYPE
248}
249
250bool SplitPaths(llvm::StringRef PathStr,
251 llvm::SmallVectorImpl<llvm::StringRef>& Paths, SplitMode Mode,
252 llvm::StringRef Delim, bool Verbose) {
253#define DEBUG_TYPE "SplitPths"
254
255 assert(Delim.size() && "Splitting without a delimiter");
256
257#if defined(_WIN32)
258 // Support using a ':' delimiter on Windows.
259 const bool WindowsColon = (Delim == ":");
260#endif
261
262 bool AllExisted = true;
263 for (std::pair<llvm::StringRef, llvm::StringRef> Split = PathStr.split(Delim);
264 !Split.second.empty(); Split = PathStr.split(Delim)) {
265
266 if (!Split.first.empty()) {
267 bool Exists = llvm::sys::fs::is_directory(Split.first);
268
269#if defined(_WIN32)
270 // Because drive letters will have a colon we have to make sure the split
271 // occurs at a colon not followed by a path separator.
272 if (!Exists && WindowsColon && Split.first.size() == 1) {
273 // Both clang and cl.exe support '\' and '/' path separators.
274 if (Split.second.front() == '\\' || Split.second.front() == '/') {
275 const std::pair<llvm::StringRef, llvm::StringRef> Tmp =
276 Split.second.split(Delim);
277 // Split.first = 'C', but we want 'C:', so Tmp.first.size()+2
278 Split.first =
279 llvm::StringRef(Split.first.data(), Tmp.first.size() + 2);
280 Split.second = Tmp.second;
281 Exists = llvm::sys::fs::is_directory(Split.first);
282 }
283 }
284#endif
285
286 AllExisted = AllExisted && Exists;
287
288 if (!Exists) {
289 if (Mode == kFailNonExistent) {
290 if (Verbose) {
291 // Exiting early, but still log all non-existent paths that we have
292 LogNonExistentDirectory(Split.first);
293 while (!Split.second.empty()) {
294 Split = PathStr.split(Delim);
295 if (llvm::sys::fs::is_directory(Split.first)) {
296 LLVM_DEBUG(dbgs() << " ignoring directory that exists \""
297 << Split.first << "\"\n");
298 } else
299 LogNonExistentDirectory(Split.first);
300 Split = Split.second.split(Delim);
301 }
302 if (!llvm::sys::fs::is_directory(Split.first))
303 LogNonExistentDirectory(Split.first);
304 }
305 return false;
306 } else if (Mode == kAllowNonExistent)
307 Paths.push_back(Split.first);
308 else if (Verbose)
309 LogNonExistentDirectory(Split.first);
310 } else
311 Paths.push_back(Split.first);
312 }
313
314 PathStr = Split.second;
315 }
316
317 // Trim trailing sep in case of A:B:C:D:
318 if (!PathStr.empty() && PathStr.ends_with(Delim))
319 PathStr = PathStr.substr(0, PathStr.size() - Delim.size());
320
321 if (!PathStr.empty()) {
322 if (!llvm::sys::fs::is_directory(PathStr)) {
323 AllExisted = false;
324 if (Mode == kAllowNonExistent)
325 Paths.push_back(PathStr);
326 else if (Verbose)
328 } else
329 Paths.push_back(PathStr);
330 }
331
332 return AllExisted;
333
334#undef DEBUG_TYPE
335}
336
338 llvm::StringRef PathStr, clang::HeaderSearchOptions& HOpts,
339 const char* Delim /* = CppInternal::utils::platform::kEnvDelim */) {
340#define DEBUG_TYPE "AddIncludePaths"
341
342 llvm::SmallVector<llvm::StringRef, 10> Paths;
343 if (Delim && *Delim)
344 SplitPaths(PathStr, Paths, kAllowNonExistent, Delim, HOpts.Verbose);
345 else
346 Paths.push_back(PathStr);
347
348 // Avoid duplicates
349 llvm::SmallVector<llvm::StringRef, 10> PathsChecked;
350 for (llvm::StringRef Path : Paths) {
351 bool Exists = false;
352 for (const clang::HeaderSearchOptions::Entry& E : HOpts.UserEntries) {
353 if ((Exists = E.Path == Path))
354 break;
355 }
356 if (!Exists)
357 PathsChecked.push_back(Path);
358 }
359
360 const bool IsFramework = false;
361 const bool IsSysRootRelative = true;
362 for (llvm::StringRef Path : PathsChecked)
363 HOpts.AddPath(Path, clang::frontend::Angled, IsFramework,
364 IsSysRootRelative);
365
366 if (HOpts.Verbose) {
367 LLVM_DEBUG(dbgs() << "Added include paths:\n");
368#ifndef NDEBUG
369 for (llvm::StringRef Path : PathsChecked)
370 LLVM_DEBUG(dbgs() << " " << Path << "\n");
371#endif
372 }
373#undef DEBUG_TYPE
374}
375
376} // namespace utils
377} // namespace CppInternal
void * DLOpen(const std::string &Path, std::string *Err=nullptr)
Open a handle to a shared library.
std::string NormalizePath(const std::string &Path)
Returns a normalized version of the given Path.
Definition Paths.cpp:100
const char *const kEnvDelim
Platform specific delimiter for splitting environment variables.
bool GetSystemLibraryPaths(llvm::SmallVectorImpl< std::string > &Paths)
Definition Paths.cpp:59
void DLClose(void *Lib, std::string *Err=nullptr)
Close a handle to a shared library.
@ kAllowNonExistent
Add all paths whether they exist or not.
Definition Paths.h:65
@ kFailNonExistent
Fail on any non-existent paths.
Definition Paths.h:64
void CopyIncludePaths(const clang::HeaderSearchOptions &Opts, llvm::SmallVectorImpl< std::string > &incpaths, bool withSystem, bool withFlags)
Copies the current include paths into the HeaderSearchOptions.
Definition Paths.cpp:150
bool SplitPaths(llvm::StringRef PathStr, llvm::SmallVectorImpl< llvm::StringRef > &Paths, SplitMode Mode, llvm::StringRef Delim, bool Verbose)
Collect the constituent paths from a PATH string.
Definition Paths.cpp:250
void AddIncludePaths(llvm::StringRef PathStr, clang::HeaderSearchOptions &HOpts, const char *Delim)
Adds multiple include paths separated by a delimiter into the given HeaderSearchOptions.
Definition Paths.cpp:337
void LogNonExistentDirectory(llvm::StringRef Path)
Write to cling::errs that directory does not exist in a format matching what 'clang -v' would do.
Definition Paths.cpp:244
Definition Paths.h:18