blob: 8e78fedf44bb7c6c674d30435257b44c8d932641 [file] [log] [blame]
Kirill Bobyrev8e35f1e2018-08-14 16:03:32 +00001//===--- GlobalCompilationDatabase.cpp ---------------------------*- C++-*-===//
Ilya Biryukov38d79772017-05-16 09:38:59 +00002//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Ilya Biryukov38d79772017-05-16 09:38:59 +00006//
Kirill Bobyrev8e35f1e2018-08-14 16:03:32 +00007//===----------------------------------------------------------------------===//
Ilya Biryukov38d79772017-05-16 09:38:59 +00008
9#include "GlobalCompilationDatabase.h"
Kadir Cetinkaya6d53adf2019-07-18 16:13:23 +000010#include "FS.h"
Ilya Biryukov0c1ca6b2017-10-02 15:13:20 +000011#include "Logger.h"
Kadir Cetinkayaad549352019-07-11 09:54:31 +000012#include "Path.h"
Kadir Cetinkayabe6b35d2019-01-22 09:10:20 +000013#include "clang/Frontend/CompilerInvocation.h"
14#include "clang/Tooling/ArgumentsAdjusters.h"
Ilya Biryukov38d79772017-05-16 09:38:59 +000015#include "clang/Tooling/CompilationDatabase.h"
Kadir Cetinkayaad549352019-07-11 09:54:31 +000016#include "llvm/ADT/None.h"
Kadir Cetinkayabe6b35d2019-01-22 09:10:20 +000017#include "llvm/ADT/Optional.h"
Kadir Cetinkayaad549352019-07-11 09:54:31 +000018#include "llvm/ADT/STLExtras.h"
Kadir Cetinkaya6d53adf2019-07-18 16:13:23 +000019#include "llvm/ADT/SmallString.h"
Ilya Biryukov38d79772017-05-16 09:38:59 +000020#include "llvm/Support/FileSystem.h"
Sam McCall88bccde2019-11-29 19:37:48 +010021#include "llvm/Support/FileUtilities.h"
Ilya Biryukov38d79772017-05-16 09:38:59 +000022#include "llvm/Support/Path.h"
Sam McCall88bccde2019-11-29 19:37:48 +010023#include "llvm/Support/Program.h"
Kadir Cetinkayaad549352019-07-11 09:54:31 +000024#include <string>
25#include <tuple>
26#include <vector>
Ilya Biryukov38d79772017-05-16 09:38:59 +000027
Krasimir Georgievc2a16a32017-07-06 08:44:54 +000028namespace clang {
29namespace clangd {
Kadir Cetinkayabe6b35d2019-01-22 09:10:20 +000030namespace {
31
Sam McCall88bccde2019-11-29 19:37:48 +010032// Query apple's `xcrun` launcher, which is the source of truth for "how should"
33// clang be invoked on this system.
34llvm::Optional<std::string> queryXcrun(llvm::ArrayRef<llvm::StringRef> Argv) {
35 auto Xcrun = llvm::sys::findProgramByName("xcrun");
36 if (!Xcrun) {
37 log("Couldn't find xcrun. Hopefully you have a non-apple toolchain...");
38 return llvm::None;
39 }
40 llvm::SmallString<64> OutFile;
41 llvm::sys::fs::createTemporaryFile("clangd-xcrun", "", OutFile);
42 llvm::FileRemover OutRemover(OutFile);
43 llvm::Optional<llvm::StringRef> Redirects[3] = {
44 /*stdin=*/{""}, /*stdout=*/{OutFile}, /*stderr=*/{""}};
45 vlog("Invoking {0} to find clang installation", *Xcrun);
46 int Ret = llvm::sys::ExecuteAndWait(*Xcrun, Argv,
47 /*Env=*/llvm::None, Redirects,
48 /*SecondsToWait=*/10);
49 if (Ret != 0) {
50 log("xcrun exists but failed with code {0}. "
51 "If you have a non-apple toolchain, this is OK. "
52 "Otherwise, try xcode-select --install.",
53 Ret);
54 return llvm::None;
55 }
56
57 auto Buf = llvm::MemoryBuffer::getFile(OutFile);
58 if (!Buf) {
59 log("Can't read xcrun output: {0}", Buf.getError().message());
60 return llvm::None;
61 }
62 StringRef Path = Buf->get()->getBuffer().trim();
63 if (Path.empty()) {
64 log("xcrun produced no output");
65 return llvm::None;
66 }
67 return Path.str();
68}
69
70// On Mac, `which clang` is /usr/bin/clang. It runs `xcrun clang`, which knows
71// where the real clang is kept. We need to do the same thing,
72// because cc1 (not the driver!) will find libc++ relative to argv[0].
73llvm::Optional<std::string> queryMacClangPath() {
74#ifndef __APPLE__
75 return llvm::None;
76#endif
77
78 return queryXcrun({"xcrun", "--find", "clang"});
79}
80
81// Resolve symlinks if possible.
82std::string resolve(std::string Path) {
83 llvm::SmallString<128> Resolved;
84 if (llvm::sys::fs::real_path(Path, Resolved))
85 return Path; // On error;
86 return Resolved.str();
87}
88
89// Get a plausible full `clang` path.
90// This is used in the fallback compile command, or when the CDB returns a
91// generic driver with no path.
92llvm::StringRef getFallbackClangPath() {
93 static const std::string &MemoizedFallbackPath = [&]() -> std::string {
94 // The driver and/or cc1 sometimes depend on the binary name to compute
95 // useful things like the standard library location.
96 // We need to emulate what clang on this system is likely to see.
97 // cc1 in particular looks at the "real path" of the running process, and
98 // so if /usr/bin/clang is a symlink, it sees the resolved path.
99 // clangd doesn't have that luxury, so we resolve symlinks ourselves.
100
101 // /usr/bin/clang on a mac is a program that redirects to the right clang.
102 // We resolve it as if it were a symlink.
103 if (auto MacClang = queryMacClangPath())
104 return resolve(std::move(*MacClang));
105 // On other platforms, just look for compilers on the PATH.
106 for (const char* Name : {"clang", "gcc", "cc"})
107 if (auto PathCC = llvm::sys::findProgramByName(Name))
108 return resolve(std::move(*PathCC));
109 // Fallback: a nonexistent 'clang' binary next to clangd.
110 static int Dummy;
111 std::string ClangdExecutable =
112 llvm::sys::fs::getMainExecutable("clangd", (void *)&Dummy);
113 SmallString<128> ClangPath;
114 ClangPath = llvm::sys::path::parent_path(ClangdExecutable);
115 llvm::sys::path::append(ClangPath, "clang");
116 return ClangPath.str();
117 }();
118 return MemoizedFallbackPath;
119}
120
121// On mac, /usr/bin/clang sets SDKROOT and then invokes the real clang.
122// The effect of this is to set -isysroot correctly. We do the same.
123const std::string *getMacSysroot() {
124#ifndef __APPLE__
125 return nullptr;
126#endif
127
128 // SDKROOT overridden in environment, respect it. Driver will set isysroot.
129 if (::getenv("SDKROOT"))
130 return nullptr;
131 static const llvm::Optional<std::string> &Sysroot =
132 queryXcrun({"xcrun", "--show-sdk-path"});
133 return Sysroot ? Sysroot.getPointer() : nullptr;
134}
135
136// Transform a command into the form we want to send to the driver.
137// The command was originally either from the CDB or is the fallback command,
138// and may have been modified by OverlayCDB.
Kadir Cetinkayabe6b35d2019-01-22 09:10:20 +0000139void adjustArguments(tooling::CompileCommand &Cmd,
140 llvm::StringRef ResourceDir) {
Kadir Cetinkaya65944ab2019-03-08 08:38:25 +0000141 tooling::ArgumentsAdjuster ArgsAdjuster = tooling::combineAdjusters(
142 // clangd should not write files to disk, including dependency files
143 // requested on the command line.
144 tooling::getClangStripDependencyFileAdjuster(),
145 // Strip plugin related command line arguments. Clangd does
146 // not support plugins currently. Therefore it breaks if
147 // compiler tries to load plugins.
148 tooling::combineAdjusters(tooling::getStripPluginsAdjuster(),
149 tooling::getClangSyntaxOnlyAdjuster()));
150
151 Cmd.CommandLine = ArgsAdjuster(Cmd.CommandLine, Cmd.Filename);
Sam McCall88bccde2019-11-29 19:37:48 +0100152 // Check whether the flag exists, either as -flag or -flag=*
153 auto Has = [&](llvm::StringRef Flag) {
154 for (llvm::StringRef Arg : Cmd.CommandLine) {
155 if (Arg.consume_front(Flag) && (Arg.empty() || Arg[0] == '='))
156 return true;
157 }
158 return false;
159 };
Kadir Cetinkayabe6b35d2019-01-22 09:10:20 +0000160 // Inject the resource dir.
Sam McCall88bccde2019-11-29 19:37:48 +0100161 if (!ResourceDir.empty() && !Has("-resource-dir"))
Kadir Cetinkayabe6b35d2019-01-22 09:10:20 +0000162 Cmd.CommandLine.push_back(("-resource-dir=" + ResourceDir).str());
Sam McCall88bccde2019-11-29 19:37:48 +0100163 if (!Has("-isysroot"))
164 if (const std::string *MacSysroot = getMacSysroot()) {
165 Cmd.CommandLine.push_back("-isysroot");
166 Cmd.CommandLine.push_back(*MacSysroot);
167 }
168
169 // If the driver is a generic name like "g++" with no path, add a clang path.
170 // This makes it easier for us to find the standard libraries on mac.
171 if (!Cmd.CommandLine.empty()) {
172 std::string &Driver = Cmd.CommandLine.front();
173 if (Driver == "clang" || Driver == "clang++" || Driver == "gcc" ||
174 Driver == "g++" || Driver == "cc" || Driver == "c++") {
175 llvm::SmallString<128> QualifiedDriver =
176 llvm::sys::path::parent_path(getFallbackClangPath());
177 llvm::sys::path::append(QualifiedDriver, Driver);
178 Driver = QualifiedDriver.str();
179 }
180 }
Kadir Cetinkayabe6b35d2019-01-22 09:10:20 +0000181}
182
183std::string getStandardResourceDir() {
184 static int Dummy; // Just an address in this process.
185 return CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy);
186}
187
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000188// Runs the given action on all parent directories of filename, starting from
189// deepest directory and going up to root. Stops whenever action succeeds.
190void actOnAllParentDirectories(PathRef FileName,
191 llvm::function_ref<bool(PathRef)> Action) {
192 for (auto Path = llvm::sys::path::parent_path(FileName);
193 !Path.empty() && !Action(Path);
194 Path = llvm::sys::path::parent_path(Path))
195 ;
196}
197
Kadir Cetinkayabe6b35d2019-01-22 09:10:20 +0000198} // namespace
Krasimir Georgievc2a16a32017-07-06 08:44:54 +0000199
Sam McCallecbeab02017-12-04 10:08:45 +0000200tooling::CompileCommand
201GlobalCompilationDatabase::getFallbackCommand(PathRef File) const {
Sam McCall88bccde2019-11-29 19:37:48 +0100202 std::vector<std::string> Argv = {"clang"};
Haojian Wu8ddf31b2019-06-18 11:54:17 +0000203 // Clang treats .h files as C by default and files without extension as linker
204 // input, resulting in unhelpful diagnostics.
Sam McCall690dcf12018-04-20 11:35:17 +0000205 // Parsing as Objective C++ is friendly to more cases.
Haojian Wu8ddf31b2019-06-18 11:54:17 +0000206 auto FileExtension = llvm::sys::path::extension(File);
207 if (FileExtension.empty() || FileExtension == ".h")
Sam McCall690dcf12018-04-20 11:35:17 +0000208 Argv.push_back("-xobjective-c++-header");
209 Argv.push_back(File);
Eric Liu9ef03dd2019-04-15 12:32:28 +0000210 tooling::CompileCommand Cmd(llvm::sys::path::parent_path(File),
211 llvm::sys::path::filename(File), std::move(Argv),
212 /*Output=*/"");
213 Cmd.Heuristic = "clangd fallback";
214 return Cmd;
Krasimir Georgievc2a16a32017-07-06 08:44:54 +0000215}
Ilya Biryukov38d79772017-05-16 09:38:59 +0000216
Ilya Biryukove5128f72017-09-20 07:24:15 +0000217DirectoryBasedGlobalCompilationDatabase::
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000218 DirectoryBasedGlobalCompilationDatabase(
219 llvm::Optional<Path> CompileCommandsDir)
Ilya Biryukov940901e2017-12-13 12:51:22 +0000220 : CompileCommandsDir(std::move(CompileCommandsDir)) {}
Ilya Biryukove5128f72017-09-20 07:24:15 +0000221
Sam McCall690dcf12018-04-20 11:35:17 +0000222DirectoryBasedGlobalCompilationDatabase::
223 ~DirectoryBasedGlobalCompilationDatabase() = default;
224
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000225llvm::Optional<tooling::CompileCommand>
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000226DirectoryBasedGlobalCompilationDatabase::getCompileCommand(PathRef File) const {
227 CDBLookupRequest Req;
228 Req.FileName = File;
229 Req.ShouldBroadcast = true;
230
231 auto Res = lookupCDB(Req);
232 if (!Res) {
Sam McCallbed58852018-07-11 10:35:11 +0000233 log("Failed to find compilation database for {0}", File);
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000234 return llvm::None;
Krasimir Georgievc2a16a32017-07-06 08:44:54 +0000235 }
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000236
237 auto Candidates = Res->CDB->getCompileCommands(File);
238 if (!Candidates.empty())
239 return std::move(Candidates.front());
240
Sam McCallc008af62018-10-20 15:30:37 +0000241 return None;
Sam McCallecbeab02017-12-04 10:08:45 +0000242}
Krasimir Georgievc2a16a32017-07-06 08:44:54 +0000243
Sam McCall7ee08672019-07-26 14:07:11 +0000244// For platforms where paths are case-insensitive (but case-preserving),
245// we need to do case-insensitive comparisons and use lowercase keys.
246// FIXME: Make Path a real class with desired semantics instead.
247// This class is not the only place this problem exists.
248// FIXME: Mac filesystems default to case-insensitive, but may be sensitive.
249
250static std::string maybeCaseFoldPath(PathRef Path) {
251#if defined(_WIN32) || defined(__APPLE__)
252 return Path.lower();
253#else
254 return Path;
255#endif
256}
257
258static bool pathEqual(PathRef A, PathRef B) {
259#if defined(_WIN32) || defined(__APPLE__)
260 return A.equals_lower(B);
261#else
262 return A == B;
263#endif
264}
265
266DirectoryBasedGlobalCompilationDatabase::CachedCDB &
Sam McCallc02ba722017-12-22 09:47:34 +0000267DirectoryBasedGlobalCompilationDatabase::getCDBInDirLocked(PathRef Dir) const {
268 // FIXME(ibiryukov): Invalidate cached compilation databases on changes
Sam McCall7ee08672019-07-26 14:07:11 +0000269 // FIXME(sammccall): this function hot, avoid copying key when hitting cache.
270 auto Key = maybeCaseFoldPath(Dir);
271 auto R = CompilationDatabases.try_emplace(Key);
272 if (R.second) { // Cache miss, try to load CDB.
273 CachedCDB &Entry = R.first->second;
274 std::string Error = "";
275 Entry.CDB = tooling::CompilationDatabase::loadFromDirectory(Dir, Error);
276 Entry.Path = Dir;
277 }
278 return R.first->second;
Sam McCallc02ba722017-12-22 09:47:34 +0000279}
Ilya Biryukov38d79772017-05-16 09:38:59 +0000280
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000281llvm::Optional<DirectoryBasedGlobalCompilationDatabase::CDBLookupResult>
282DirectoryBasedGlobalCompilationDatabase::lookupCDB(
283 CDBLookupRequest Request) const {
284 assert(llvm::sys::path::is_absolute(Request.FileName) &&
Ilya Biryukov38d79772017-05-16 09:38:59 +0000285 "path must be absolute");
286
Sam McCall7ee08672019-07-26 14:07:11 +0000287 bool ShouldBroadcast = false;
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000288 CDBLookupResult Result;
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000289
290 {
291 std::lock_guard<std::mutex> Lock(Mutex);
Sam McCall7ee08672019-07-26 14:07:11 +0000292 CachedCDB *Entry = nullptr;
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000293 if (CompileCommandsDir) {
Sam McCall7ee08672019-07-26 14:07:11 +0000294 Entry = &getCDBInDirLocked(*CompileCommandsDir);
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000295 } else {
Kadir Cetinkaya6d53adf2019-07-18 16:13:23 +0000296 // Traverse the canonical version to prevent false positives. i.e.:
297 // src/build/../a.cc can detect a CDB in /src/build if not canonicalized.
Sam McCall7ee08672019-07-26 14:07:11 +0000298 // FIXME(sammccall): this loop is hot, use a union-find-like structure.
Kadir Cetinkaya6d53adf2019-07-18 16:13:23 +0000299 actOnAllParentDirectories(removeDots(Request.FileName),
Sam McCall7ee08672019-07-26 14:07:11 +0000300 [&](PathRef Path) {
301 Entry = &getCDBInDirLocked(Path);
302 return Entry->CDB != nullptr;
Kadir Cetinkaya6d53adf2019-07-18 16:13:23 +0000303 });
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000304 }
305
Sam McCall7ee08672019-07-26 14:07:11 +0000306 if (!Entry || !Entry->CDB)
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000307 return llvm::None;
308
309 // Mark CDB as broadcasted to make sure discovery is performed once.
Sam McCall7ee08672019-07-26 14:07:11 +0000310 if (Request.ShouldBroadcast && !Entry->SentBroadcast) {
311 Entry->SentBroadcast = true;
312 ShouldBroadcast = true;
313 }
314
315 Result.CDB = Entry->CDB.get();
316 Result.PI.SourceRoot = Entry->Path;
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000317 }
318
319 // FIXME: Maybe make the following part async, since this can block retrieval
320 // of compile commands.
Sam McCall7ee08672019-07-26 14:07:11 +0000321 if (ShouldBroadcast)
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000322 broadcastCDB(Result);
323 return Result;
324}
325
326void DirectoryBasedGlobalCompilationDatabase::broadcastCDB(
327 CDBLookupResult Result) const {
328 assert(Result.CDB && "Trying to broadcast an invalid CDB!");
329
330 std::vector<std::string> AllFiles = Result.CDB->getAllFiles();
331 // We assume CDB in CompileCommandsDir owns all of its entries, since we don't
332 // perform any search in parent paths whenever it is set.
Sam McCall2bebc3d2018-11-20 10:56:03 +0000333 if (CompileCommandsDir) {
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000334 assert(*CompileCommandsDir == Result.PI.SourceRoot &&
335 "Trying to broadcast a CDB outside of CompileCommandsDir!");
336 OnCommandChanged.broadcast(std::move(AllFiles));
337 return;
338 }
339
340 llvm::StringMap<bool> DirectoryHasCDB;
341 {
342 std::lock_guard<std::mutex> Lock(Mutex);
343 // Uniquify all parent directories of all files.
344 for (llvm::StringRef File : AllFiles) {
345 actOnAllParentDirectories(File, [&](PathRef Path) {
346 auto It = DirectoryHasCDB.try_emplace(Path);
347 // Already seen this path, and all of its parents.
348 if (!It.second)
349 return true;
350
Sam McCall7ee08672019-07-26 14:07:11 +0000351 CachedCDB &Entry = getCDBInDirLocked(Path);
352 It.first->second = Entry.CDB != nullptr;
353 return pathEqual(Path, Result.PI.SourceRoot);
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000354 });
Sam McCall2bebc3d2018-11-20 10:56:03 +0000355 }
356 }
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000357
358 std::vector<std::string> GovernedFiles;
359 for (llvm::StringRef File : AllFiles) {
360 // A file is governed by this CDB if lookup for the file would find it.
361 // Independent of whether it has an entry for that file or not.
362 actOnAllParentDirectories(File, [&](PathRef Path) {
363 if (DirectoryHasCDB.lookup(Path)) {
Sam McCall7ee08672019-07-26 14:07:11 +0000364 if (pathEqual(Path, Result.PI.SourceRoot))
Kadir Cetinkaya6d53adf2019-07-18 16:13:23 +0000365 // Make sure listeners always get a canonical path for the file.
366 GovernedFiles.push_back(removeDots(File));
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000367 // Stop as soon as we hit a CDB.
368 return true;
369 }
370 return false;
371 });
372 }
373
374 OnCommandChanged.broadcast(std::move(GovernedFiles));
375}
376
377llvm::Optional<ProjectInfo>
378DirectoryBasedGlobalCompilationDatabase::getProjectInfo(PathRef File) const {
379 CDBLookupRequest Req;
380 Req.FileName = File;
381 Req.ShouldBroadcast = false;
382 auto Res = lookupCDB(Req);
383 if (!Res)
384 return llvm::None;
385 return Res->PI;
Sam McCall2bebc3d2018-11-20 10:56:03 +0000386}
387
388OverlayCDB::OverlayCDB(const GlobalCompilationDatabase *Base,
Kadir Cetinkayabe6b35d2019-01-22 09:10:20 +0000389 std::vector<std::string> FallbackFlags,
390 llvm::Optional<std::string> ResourceDir)
391 : Base(Base), ResourceDir(ResourceDir ? std::move(*ResourceDir)
392 : getStandardResourceDir()),
393 FallbackFlags(std::move(FallbackFlags)) {
Sam McCall2bebc3d2018-11-20 10:56:03 +0000394 if (Base)
395 BaseChanged = Base->watch([this](const std::vector<std::string> Changes) {
396 OnCommandChanged.broadcast(Changes);
397 });
Ilya Biryukov38d79772017-05-16 09:38:59 +0000398}
Krasimir Georgievc2a16a32017-07-06 08:44:54 +0000399
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000400llvm::Optional<tooling::CompileCommand>
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000401OverlayCDB::getCompileCommand(PathRef File) const {
Kadir Cetinkayabe6b35d2019-01-22 09:10:20 +0000402 llvm::Optional<tooling::CompileCommand> Cmd;
Sam McCallc55d09a2018-11-02 13:09:36 +0000403 {
404 std::lock_guard<std::mutex> Lock(Mutex);
Kadir Cetinkaya6d53adf2019-07-18 16:13:23 +0000405 auto It = Commands.find(removeDots(File));
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000406 if (It != Commands.end())
Kadir Cetinkayabe6b35d2019-01-22 09:10:20 +0000407 Cmd = It->second;
Sam McCallc55d09a2018-11-02 13:09:36 +0000408 }
Kadir Cetinkayabe6b35d2019-01-22 09:10:20 +0000409 if (!Cmd && Base)
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000410 Cmd = Base->getCompileCommand(File);
Kadir Cetinkayabe6b35d2019-01-22 09:10:20 +0000411 if (!Cmd)
412 return llvm::None;
413 adjustArguments(*Cmd, ResourceDir);
414 return Cmd;
Alex Lorenzf8087862018-08-01 17:39:29 +0000415}
416
Sam McCallc55d09a2018-11-02 13:09:36 +0000417tooling::CompileCommand OverlayCDB::getFallbackCommand(PathRef File) const {
418 auto Cmd = Base ? Base->getFallbackCommand(File)
419 : GlobalCompilationDatabase::getFallbackCommand(File);
420 std::lock_guard<std::mutex> Lock(Mutex);
421 Cmd.CommandLine.insert(Cmd.CommandLine.end(), FallbackFlags.begin(),
422 FallbackFlags.end());
Kadir Cetinkayaa7f9f422019-06-04 13:38:36 +0000423 adjustArguments(Cmd, ResourceDir);
Sam McCallc55d09a2018-11-02 13:09:36 +0000424 return Cmd;
425}
426
427void OverlayCDB::setCompileCommand(
428 PathRef File, llvm::Optional<tooling::CompileCommand> Cmd) {
Kadir Cetinkaya6d53adf2019-07-18 16:13:23 +0000429 // We store a canonical version internally to prevent mismatches between set
430 // and get compile commands. Also it assures clients listening to broadcasts
431 // doesn't receive different names for the same file.
432 std::string CanonPath = removeDots(File);
Sam McCall2bebc3d2018-11-20 10:56:03 +0000433 {
434 std::unique_lock<std::mutex> Lock(Mutex);
435 if (Cmd)
Kadir Cetinkaya6d53adf2019-07-18 16:13:23 +0000436 Commands[CanonPath] = std::move(*Cmd);
Sam McCall2bebc3d2018-11-20 10:56:03 +0000437 else
Kadir Cetinkaya6d53adf2019-07-18 16:13:23 +0000438 Commands.erase(CanonPath);
Sam McCall2bebc3d2018-11-20 10:56:03 +0000439 }
Kadir Cetinkaya6d53adf2019-07-18 16:13:23 +0000440 OnCommandChanged.broadcast({CanonPath});
Alex Lorenzf8087862018-08-01 17:39:29 +0000441}
442
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000443llvm::Optional<ProjectInfo> OverlayCDB::getProjectInfo(PathRef File) const {
444 {
445 std::lock_guard<std::mutex> Lock(Mutex);
Kadir Cetinkaya6d53adf2019-07-18 16:13:23 +0000446 auto It = Commands.find(removeDots(File));
Kadir Cetinkayaad549352019-07-11 09:54:31 +0000447 if (It != Commands.end())
448 return ProjectInfo{};
449 }
450 if (Base)
451 return Base->getProjectInfo(File);
452
453 return llvm::None;
454}
Krasimir Georgievc2a16a32017-07-06 08:44:54 +0000455} // namespace clangd
456} // namespace clang