13 #include "clang/Frontend/CompilerInvocation.h" 14 #include "clang/Tooling/ArgumentsAdjusters.h" 15 #include "clang/Tooling/CompilationDatabase.h" 16 #include "llvm/ADT/None.h" 17 #include "llvm/ADT/Optional.h" 18 #include "llvm/ADT/STLExtras.h" 19 #include "llvm/ADT/SmallString.h" 20 #include "llvm/Support/FileSystem.h" 21 #include "llvm/Support/Path.h" 30 void adjustArguments(tooling::CompileCommand &Cmd,
31 llvm::StringRef ResourceDir) {
32 tooling::ArgumentsAdjuster ArgsAdjuster = tooling::combineAdjusters(
35 tooling::getClangStripDependencyFileAdjuster(),
39 tooling::combineAdjusters(tooling::getStripPluginsAdjuster(),
40 tooling::getClangSyntaxOnlyAdjuster()));
42 Cmd.CommandLine = ArgsAdjuster(Cmd.CommandLine, Cmd.Filename);
45 if (!ResourceDir.empty())
46 Cmd.CommandLine.push_back((
"-resource-dir=" + ResourceDir).str());
49 std::string getStandardResourceDir() {
51 return CompilerInvocation::GetResourcesPath(
"clangd", (
void *)&Dummy);
58 for (
auto Path = llvm::sys::path::parent_path(FileName);
60 Path = llvm::sys::path::parent_path(
Path))
68 std::string ClangdExecutable =
69 llvm::sys::fs::getMainExecutable(
"clangd", (
void *)&Dummy);
70 SmallString<128> ClangPath;
71 ClangPath = llvm::sys::path::parent_path(ClangdExecutable);
72 llvm::sys::path::append(ClangPath,
"clang");
73 return ClangPath.str();
76 tooling::CompileCommand
82 auto FileExtension = llvm::sys::path::extension(File);
83 if (FileExtension.empty() || FileExtension ==
".h")
84 Argv.push_back(
"-xobjective-c++-header");
86 tooling::CompileCommand Cmd(llvm::sys::path::parent_path(File),
87 llvm::sys::path::filename(File), std::move(Argv),
89 Cmd.Heuristic =
"clangd fallback";
95 llvm::Optional<Path> CompileCommandsDir)
96 : CompileCommandsDir(std::move(CompileCommandsDir)) {}
101 llvm::Optional<tooling::CompileCommand>
103 CDBLookupRequest Req;
105 Req.ShouldBroadcast =
true;
107 auto Res = lookupCDB(Req);
109 log(
"Failed to find compilation database for {0}", File);
113 auto Candidates = Res->CDB->getCompileCommands(File);
114 if (!Candidates.empty())
115 return std::move(Candidates.front());
127 #if defined(_WIN32) || defined(__APPLE__) 135 #if defined(_WIN32) || defined(__APPLE__) 136 return A.equals_lower(B);
142 DirectoryBasedGlobalCompilationDatabase::CachedCDB &
143 DirectoryBasedGlobalCompilationDatabase::getCDBInDirLocked(
PathRef Dir)
const {
147 auto R = CompilationDatabases.try_emplace(
Key);
149 CachedCDB &
Entry = R.first->second;
150 std::string
Error =
"";
151 Entry.CDB = tooling::CompilationDatabase::loadFromDirectory(Dir, Error);
154 return R.first->second;
157 llvm::Optional<DirectoryBasedGlobalCompilationDatabase::CDBLookupResult>
158 DirectoryBasedGlobalCompilationDatabase::lookupCDB(
159 CDBLookupRequest Request)
const {
160 assert(llvm::sys::path::is_absolute(Request.FileName) &&
161 "path must be absolute");
163 bool ShouldBroadcast =
false;
167 std::lock_guard<std::mutex> Lock(Mutex);
168 CachedCDB *
Entry =
nullptr;
169 if (CompileCommandsDir) {
170 Entry = &getCDBInDirLocked(*CompileCommandsDir);
175 actOnAllParentDirectories(
removeDots(Request.FileName),
177 Entry = &getCDBInDirLocked(Path);
178 return Entry->CDB != nullptr;
182 if (!Entry || !Entry->CDB)
186 if (Request.ShouldBroadcast && !Entry->SentBroadcast) {
187 Entry->SentBroadcast =
true;
188 ShouldBroadcast =
true;
191 Result.CDB = Entry->CDB.get();
192 Result.PI.SourceRoot = Entry->Path;
198 broadcastCDB(Result);
202 void DirectoryBasedGlobalCompilationDatabase::broadcastCDB(
203 CDBLookupResult
Result)
const {
204 assert(Result.CDB &&
"Trying to broadcast an invalid CDB!");
206 std::vector<std::string> AllFiles = Result.CDB->getAllFiles();
209 if (CompileCommandsDir) {
210 assert(*CompileCommandsDir == Result.PI.SourceRoot &&
211 "Trying to broadcast a CDB outside of CompileCommandsDir!");
216 llvm::StringMap<bool> DirectoryHasCDB;
218 std::lock_guard<std::mutex> Lock(Mutex);
220 for (llvm::StringRef
File : AllFiles) {
222 auto It = DirectoryHasCDB.try_emplace(Path);
227 CachedCDB &
Entry = getCDBInDirLocked(Path);
228 It.first->second = Entry.CDB !=
nullptr;
229 return pathEqual(Path, Result.PI.SourceRoot);
234 std::vector<std::string> GovernedFiles;
235 for (llvm::StringRef
File : AllFiles) {
239 if (DirectoryHasCDB.lookup(Path)) {
240 if (
pathEqual(Path, Result.PI.SourceRoot))
253 llvm::Optional<ProjectInfo>
255 CDBLookupRequest Req;
257 Req.ShouldBroadcast =
false;
258 auto Res = lookupCDB(Req);
265 std::vector<std::string> FallbackFlags,
266 llvm::Optional<std::string> ResourceDir)
267 : Base(Base), ResourceDir(ResourceDir ? std::move(*ResourceDir)
268 : getStandardResourceDir()),
269 FallbackFlags(std::move(FallbackFlags)) {
271 BaseChanged = Base->
watch([
this](
const std::vector<std::string>
Changes) {
276 llvm::Optional<tooling::CompileCommand>
278 llvm::Optional<tooling::CompileCommand> Cmd;
280 std::lock_guard<std::mutex> Lock(Mutex);
282 if (It != Commands.end())
289 adjustArguments(*Cmd, ResourceDir);
296 std::lock_guard<std::mutex> Lock(Mutex);
297 Cmd.CommandLine.insert(Cmd.CommandLine.end(), FallbackFlags.begin(),
298 FallbackFlags.end());
299 adjustArguments(Cmd, ResourceDir);
304 PathRef File, llvm::Optional<tooling::CompileCommand> Cmd) {
310 std::unique_lock<std::mutex> Lock(Mutex);
312 Commands[CanonPath] = std::move(*Cmd);
314 Commands.erase(CanonPath);
321 std::lock_guard<std::mutex> Lock(Mutex);
323 if (It != Commands.end())
static std::string getFallbackClangPath()
virtual llvm::Optional< tooling::CompileCommand > getCompileCommand(PathRef File) const =0
If there are any known-good commands for building this file, returns one.
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Values in a Context are indexed by typed keys.
~DirectoryBasedGlobalCompilationDatabase() override
Documents should not be synced at all.
llvm::Optional< ProjectInfo > getProjectInfo(PathRef File) const override
Returns the path to first directory containing a compilation database in File's parents.
Provides compilation arguments used for parsing C and C++ files.
llvm::Optional< tooling::CompileCommand > getCompileCommand(PathRef File) const override
If there are any known-good commands for building this file, returns one.
static bool pathEqual(PathRef A, PathRef B)
llvm::Optional< ProjectInfo > getProjectInfo(PathRef File) const override
Finds the closest project to File.
void broadcast(const T &V)
llvm::unique_function< void()> Action
void log(const char *Fmt, Ts &&... Vals)
std::string Path
A typedef to represent a file path.
DirectoryBasedGlobalCompilationDatabase(llvm::Optional< Path > CompileCommandsDir)
tooling::CompileCommand getFallbackCommand(PathRef File) const override
Makes a guess at how to build a file.
void setCompileCommand(PathRef File, llvm::Optional< tooling::CompileCommand > CompilationCommand)
Sets or clears the compilation command for a particular file.
OverlayCDB(const GlobalCompilationDatabase *Base, std::vector< std::string > FallbackFlags={}, llvm::Optional< std::string > ResourceDir=llvm::None)
CommandChanged::Subscription watch(CommandChanged::Listener L) const
The callback is notified when files may have new compile commands.
static std::string maybeCaseFoldPath(PathRef Path)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
std::unique_ptr< GlobalCompilationDatabase > Base
Path removeDots(PathRef File)
Returns a version of File that doesn't contain dots and dot dots.
CommandChanged OnCommandChanged
llvm::Optional< tooling::CompileCommand > getCompileCommand(PathRef File) const override
Scans File's parents looking for compilation databases.
virtual llvm::Optional< ProjectInfo > getProjectInfo(PathRef File) const
Finds the closest project to File.
virtual tooling::CompileCommand getFallbackCommand(PathRef File) const
Makes a guess at how to build a file.