226 lines · c
1//===--- GlobalCompilationDatabase.h -----------------------------*- C++-*-===//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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H10#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H11 12#include "ProjectModules.h"13#include "support/Function.h"14#include "support/Path.h"15#include "support/Threading.h"16#include "support/ThreadsafeFS.h"17#include "clang/Tooling/ArgumentsAdjusters.h"18#include "clang/Tooling/CompilationDatabase.h"19#include "llvm/ADT/FunctionExtras.h"20#include "llvm/ADT/StringMap.h"21#include <memory>22#include <mutex>23#include <optional>24#include <vector>25 26namespace clang {27namespace clangd {28 29struct ProjectInfo {30 // The directory in which the compilation database was discovered.31 // Empty if directory-based compilation database discovery was not used.32 std::string SourceRoot;33};34 35/// Provides compilation arguments used for parsing C and C++ files.36class GlobalCompilationDatabase {37public:38 virtual ~GlobalCompilationDatabase() = default;39 40 /// If there are any known-good commands for building this file, returns one.41 virtual std::optional<tooling::CompileCommand>42 getCompileCommand(PathRef File) const = 0;43 44 /// Finds the closest project to \p File.45 virtual std::optional<ProjectInfo> getProjectInfo(PathRef File) const {46 return std::nullopt;47 }48 49 /// Get the modules in the closest project to \p File50 virtual std::unique_ptr<ProjectModules>51 getProjectModules(PathRef File) const {52 return nullptr;53 }54 55 /// Makes a guess at how to build a file.56 /// The default implementation just runs clang on the file.57 /// Clangd should treat the results as unreliable.58 virtual tooling::CompileCommand getFallbackCommand(PathRef File) const;59 60 /// If the CDB does any asynchronous work, wait for it to complete.61 /// For use in tests.62 virtual bool blockUntilIdle(Deadline D) const { return true; }63 64 using CommandChanged = Event<std::vector<std::string>>;65 /// The callback is notified when files may have new compile commands.66 /// The argument is a list of full file paths.67 CommandChanged::Subscription watch(CommandChanged::Listener L) const {68 return OnCommandChanged.observe(std::move(L));69 }70 71protected:72 mutable CommandChanged OnCommandChanged;73};74 75// Helper class for implementing GlobalCompilationDatabases that wrap others.76class DelegatingCDB : public GlobalCompilationDatabase {77public:78 DelegatingCDB(const GlobalCompilationDatabase *Base);79 DelegatingCDB(std::unique_ptr<GlobalCompilationDatabase> Base);80 81 std::optional<tooling::CompileCommand>82 getCompileCommand(PathRef File) const override;83 84 std::optional<ProjectInfo> getProjectInfo(PathRef File) const override;85 86 std::unique_ptr<ProjectModules>87 getProjectModules(PathRef File) const override;88 89 tooling::CompileCommand getFallbackCommand(PathRef File) const override;90 91 bool blockUntilIdle(Deadline D) const override;92 93private:94 const GlobalCompilationDatabase *Base;95 std::unique_ptr<GlobalCompilationDatabase> BaseOwner;96 CommandChanged::Subscription BaseChanged;97};98 99/// Gets compile args from tooling::CompilationDatabases built for parent100/// directories.101class DirectoryBasedGlobalCompilationDatabase102 : public GlobalCompilationDatabase {103public:104 struct Options {105 Options(const ThreadsafeFS &TFS) : TFS(TFS) {}106 107 const ThreadsafeFS &TFS;108 // Frequency to check whether e.g. compile_commands.json has changed.109 std::chrono::steady_clock::duration RevalidateAfter =110 std::chrono::seconds(5);111 // Frequency to check whether e.g. compile_commands.json has been created.112 // (This is more expensive to check frequently, as we check many locations).113 std::chrono::steady_clock::duration RevalidateMissingAfter =114 std::chrono::seconds(30);115 // Used to provide per-file configuration.116 std::function<Context(llvm::StringRef)> ContextProvider;117 // Only look for a compilation database in this one fixed directory.118 // FIXME: fold this into config/context mechanism.119 std::optional<Path> CompileCommandsDir;120 };121 122 DirectoryBasedGlobalCompilationDatabase(const Options &Opts);123 ~DirectoryBasedGlobalCompilationDatabase() override;124 125 /// Scans File's parents looking for compilation databases.126 /// Any extra flags will be added.127 /// Might trigger OnCommandChanged, if CDB wasn't broadcasted yet.128 std::optional<tooling::CompileCommand>129 getCompileCommand(PathRef File) const override;130 131 /// Returns the path to first directory containing a compilation database in132 /// \p File's parents.133 std::optional<ProjectInfo> getProjectInfo(PathRef File) const override;134 135 std::unique_ptr<ProjectModules>136 getProjectModules(PathRef File) const override;137 138 bool blockUntilIdle(Deadline Timeout) const override;139 140private:141 Options Opts;142 143 class DirectoryCache;144 // Keyed by possibly-case-folded directory path.145 // We can hand out pointers as they're stable and entries are never removed.146 mutable llvm::StringMap<DirectoryCache> DirCaches;147 mutable std::mutex DirCachesMutex;148 149 std::vector<DirectoryCache *>150 getDirectoryCaches(llvm::ArrayRef<llvm::StringRef> Dirs) const;151 152 struct CDBLookupRequest {153 PathRef FileName;154 // Whether this lookup should trigger discovery of the CDB found.155 bool ShouldBroadcast = false;156 // Cached results newer than this are considered fresh and not checked157 // against disk.158 std::chrono::steady_clock::time_point FreshTime;159 std::chrono::steady_clock::time_point FreshTimeMissing;160 };161 struct CDBLookupResult {162 std::shared_ptr<const tooling::CompilationDatabase> CDB;163 ProjectInfo PI;164 };165 std::optional<CDBLookupResult> lookupCDB(CDBLookupRequest Request) const;166 167 class BroadcastThread;168 std::unique_ptr<BroadcastThread> Broadcaster;169 170 // Performs broadcast on governed files.171 void broadcastCDB(CDBLookupResult Res) const;172 173 // cache test calls lookupCDB directly to ensure valid/invalid times.174 friend class DirectoryBasedGlobalCompilationDatabaseCacheTest;175};176 177/// Extracts system include search path from drivers matching QueryDriverGlobs178/// and adds them to the compile flags.179/// Returns null when \p QueryDriverGlobs is empty.180using SystemIncludeExtractorFn = llvm::unique_function<void(181 tooling::CompileCommand &, llvm::StringRef) const>;182SystemIncludeExtractorFn183getSystemIncludeExtractor(llvm::ArrayRef<std::string> QueryDriverGlobs);184 185/// Wraps another compilation database, and supports overriding the commands186/// using an in-memory mapping.187class OverlayCDB : public DelegatingCDB {188public:189 // Makes adjustments to a tooling::CompileCommand which will be used to190 // process a file (possibly different from the one in the command).191 using CommandMangler = llvm::unique_function<void(tooling::CompileCommand &,192 StringRef File) const>;193 194 // Base may be null, in which case no entries are inherited.195 // FallbackFlags are added to the fallback compile command.196 // Adjuster is applied to all commands, fallback or not.197 OverlayCDB(const GlobalCompilationDatabase *Base,198 std::vector<std::string> FallbackFlags = {},199 CommandMangler Mangler = nullptr);200 201 std::optional<tooling::CompileCommand>202 getCompileCommand(PathRef File) const override;203 tooling::CompileCommand getFallbackCommand(PathRef File) const override;204 205 /// Sets or clears the compilation command for a particular file.206 /// Returns true if the command was changed (including insertion and removal),207 /// false if it was unchanged.208 bool209 setCompileCommand(PathRef File,210 std::optional<tooling::CompileCommand> CompilationCommand);211 212 std::unique_ptr<ProjectModules>213 getProjectModules(PathRef File) const override;214 215private:216 mutable std::mutex Mutex;217 llvm::StringMap<tooling::CompileCommand> Commands; /* GUARDED_BY(Mut) */218 CommandMangler Mangler;219 std::vector<std::string> FallbackFlags;220};221 222} // namespace clangd223} // namespace clang224 225#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H226