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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H 10 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H 11 12 #include "CompileCommands.h" 13 #include "support/Function.h" 14 #include "support/Path.h" 15 #include "clang/Tooling/ArgumentsAdjusters.h" 16 #include "clang/Tooling/CompilationDatabase.h" 17 #include "llvm/ADT/Optional.h" 18 #include "llvm/ADT/StringMap.h" 19 #include <memory> 20 #include <mutex> 21 #include <vector> 22 23 namespace clang { 24 namespace clangd { 25 26 class Logger; 27 28 struct ProjectInfo { 29 // The directory in which the compilation database was discovered. 30 // Empty if directory-based compilation database discovery was not used. 31 std::string SourceRoot; 32 }; 33 34 /// Provides compilation arguments used for parsing C and C++ files. 35 class GlobalCompilationDatabase { 36 public: 37 virtual ~GlobalCompilationDatabase() = default; 38 39 /// If there are any known-good commands for building this file, returns one. 40 virtual llvm::Optional<tooling::CompileCommand> 41 getCompileCommand(PathRef File) const = 0; 42 43 /// Finds the closest project to \p File. getProjectInfo(PathRef File)44 virtual llvm::Optional<ProjectInfo> getProjectInfo(PathRef File) const { 45 return llvm::None; 46 } 47 48 /// Makes a guess at how to build a file. 49 /// The default implementation just runs clang on the file. 50 /// Clangd should treat the results as unreliable. 51 virtual tooling::CompileCommand getFallbackCommand(PathRef File) const; 52 53 using CommandChanged = Event<std::vector<std::string>>; 54 /// The callback is notified when files may have new compile commands. 55 /// The argument is a list of full file paths. watch(CommandChanged::Listener L)56 CommandChanged::Subscription watch(CommandChanged::Listener L) const { 57 return OnCommandChanged.observe(std::move(L)); 58 } 59 60 protected: 61 mutable CommandChanged OnCommandChanged; 62 }; 63 64 /// Gets compile args from tooling::CompilationDatabases built for parent 65 /// directories. 66 class DirectoryBasedGlobalCompilationDatabase 67 : public GlobalCompilationDatabase { 68 public: 69 DirectoryBasedGlobalCompilationDatabase( 70 llvm::Optional<Path> CompileCommandsDir); 71 ~DirectoryBasedGlobalCompilationDatabase() override; 72 73 /// Scans File's parents looking for compilation databases. 74 /// Any extra flags will be added. 75 /// Might trigger OnCommandChanged, if CDB wasn't broadcasted yet. 76 llvm::Optional<tooling::CompileCommand> 77 getCompileCommand(PathRef File) const override; 78 79 /// Returns the path to first directory containing a compilation database in 80 /// \p File's parents. 81 llvm::Optional<ProjectInfo> getProjectInfo(PathRef File) const override; 82 83 private: 84 /// Caches compilation databases loaded from directories. 85 struct CachedCDB { 86 std::string Path; // Not case-folded. 87 std::unique_ptr<clang::tooling::CompilationDatabase> CDB = nullptr; 88 bool SentBroadcast = false; 89 }; 90 CachedCDB &getCDBInDirLocked(PathRef File) const; 91 92 struct CDBLookupRequest { 93 PathRef FileName; 94 // Whether this lookup should trigger discovery of the CDB found. 95 bool ShouldBroadcast = false; 96 }; 97 struct CDBLookupResult { 98 tooling::CompilationDatabase *CDB = nullptr; 99 ProjectInfo PI; 100 }; 101 llvm::Optional<CDBLookupResult> lookupCDB(CDBLookupRequest Request) const; 102 103 // Performs broadcast on governed files. 104 void broadcastCDB(CDBLookupResult Res) const; 105 106 mutable std::mutex Mutex; 107 // Keyed by possibly-case-folded directory path. 108 mutable llvm::StringMap<CachedCDB> CompilationDatabases; 109 110 /// Used for command argument pointing to folder where compile_commands.json 111 /// is located. 112 llvm::Optional<Path> CompileCommandsDir; 113 }; 114 115 /// Extracts system include search path from drivers matching QueryDriverGlobs 116 /// and adds them to the compile flags. Base may not be nullptr. 117 /// Returns Base when \p QueryDriverGlobs is empty. 118 std::unique_ptr<GlobalCompilationDatabase> 119 getQueryDriverDatabase(llvm::ArrayRef<std::string> QueryDriverGlobs, 120 std::unique_ptr<GlobalCompilationDatabase> Base); 121 122 123 /// Wraps another compilation database, and supports overriding the commands 124 /// using an in-memory mapping. 125 class OverlayCDB : public GlobalCompilationDatabase { 126 public: 127 // Base may be null, in which case no entries are inherited. 128 // FallbackFlags are added to the fallback compile command. 129 // Adjuster is applied to all commands, fallback or not. 130 OverlayCDB(const GlobalCompilationDatabase *Base, 131 std::vector<std::string> FallbackFlags = {}, 132 tooling::ArgumentsAdjuster Adjuster = nullptr); 133 134 llvm::Optional<tooling::CompileCommand> 135 getCompileCommand(PathRef File) const override; 136 tooling::CompileCommand getFallbackCommand(PathRef File) const override; 137 llvm::Optional<ProjectInfo> getProjectInfo(PathRef File) const override; 138 139 /// Sets or clears the compilation command for a particular file. 140 void 141 setCompileCommand(PathRef File, 142 llvm::Optional<tooling::CompileCommand> CompilationCommand); 143 144 private: 145 mutable std::mutex Mutex; 146 llvm::StringMap<tooling::CompileCommand> Commands; /* GUARDED_BY(Mut) */ 147 const GlobalCompilationDatabase *Base; 148 tooling::ArgumentsAdjuster ArgsAdjuster; 149 std::vector<std::string> FallbackFlags; 150 CommandChanged::Subscription BaseChanged; 151 }; 152 153 } // namespace clangd 154 } // namespace clang 155 156 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H 157