diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -480,7 +480,8 @@ CompileCommandsDir = Dir; if (UseDirBasedCDB) { BaseCDB = std::make_unique( - CompileCommandsDir); + CompileCommandsDir, + Params.initializationOptions.compilationDatabaseMap); BaseCDB = getQueryDriverDatabase( llvm::makeArrayRef(ClangdServerOpts.QueryDriverGlobs), std::move(BaseCDB)); @@ -1270,7 +1271,8 @@ // clang-format on } -ClangdLSPServer::~ClangdLSPServer() { IsBeingDestroyed = true; +ClangdLSPServer::~ClangdLSPServer() { + IsBeingDestroyed = true; // Explicitly destroy ClangdServer first, blocking on threads it owns. // This ensures they don't access any other members. Server.reset(); diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.h b/clang-tools-extra/clangd/GlobalCompilationDatabase.h --- a/clang-tools-extra/clangd/GlobalCompilationDatabase.h +++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.h @@ -12,6 +12,7 @@ #include "CompileCommands.h" #include "Function.h" #include "Path.h" +#include "Protocol.h" #include "clang/Tooling/ArgumentsAdjusters.h" #include "clang/Tooling/CompilationDatabase.h" #include "llvm/ADT/Optional.h" @@ -67,7 +68,8 @@ : public GlobalCompilationDatabase { public: DirectoryBasedGlobalCompilationDatabase( - llvm::Optional CompileCommandsDir); + llvm::Optional CompileCommandsDir, + llvm::Optional CDBMap); ~DirectoryBasedGlobalCompilationDatabase() override; /// Scans File's parents looking for compilation databases. @@ -110,6 +112,8 @@ /// Used for command argument pointing to folder where compile_commands.json /// is located. llvm::Optional CompileCommandsDir; + + llvm::Optional CDBMap; }; /// Extracts system include search path from drivers matching QueryDriverGlobs @@ -119,7 +123,6 @@ getQueryDriverDatabase(llvm::ArrayRef QueryDriverGlobs, std::unique_ptr Base); - /// Wraps another compilation database, and supports overriding the commands /// using an in-memory mapping. class OverlayCDB : public GlobalCompilationDatabase { diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp --- a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp +++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp @@ -60,8 +60,10 @@ DirectoryBasedGlobalCompilationDatabase:: DirectoryBasedGlobalCompilationDatabase( - llvm::Optional CompileCommandsDir) - : CompileCommandsDir(std::move(CompileCommandsDir)) {} + llvm::Optional CompileCommandsDir, + llvm::Optional CDBMap) + : CompileCommandsDir(std::move(CompileCommandsDir)), + CDBMap(std::move(CDBMap)) {} DirectoryBasedGlobalCompilationDatabase:: ~DirectoryBasedGlobalCompilationDatabase() = default; @@ -136,6 +138,13 @@ CachedCDB *Entry = nullptr; if (CompileCommandsDir) { Entry = &getCDBInDirLocked(*CompileCommandsDir); + } else if (CDBMap) { + for (const auto &MapEntry : *CDBMap) { + if (StringRef(Request.FileName).startswith(MapEntry.sourceDir)) { + Entry = &getCDBInDirLocked(MapEntry.dbPath); + break; + } + } } else { // Traverse the canonical version to prevent false positives. i.e.: // src/build/../a.cc can detect a CDB in /src/build if not canonicalized. diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -23,6 +23,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOL_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOL_H +#include "Path.h" #include "URI.h" #include "index/SymbolID.h" #include "clang/Index/IndexSymbol.h" @@ -449,6 +450,14 @@ }; bool fromJSON(const llvm::json::Value &, ConfigurationSettings &); +struct CompilationDatabasePath { + Path sourceDir; + Path dbPath; +}; +bool fromJSON(const llvm::json::Value &, CompilationDatabasePath &); + +using CompilationDatabaseMap = std::vector; + /// Clangd extension: parameters configurable at `initialize` time. /// LSP defines this type as `any`. struct InitializationOptions { @@ -456,7 +465,12 @@ // also set through the initialize request (initializationOptions field). ConfigurationSettings ConfigSettings; - llvm::Optional compilationDatabasePath; + llvm::Optional compilationDatabasePath; + + // A map from source directories to directories containing compilation + // database files. Paths must be absolute. + llvm::Optional compilationDatabaseMap; + // Additional flags to be included in the "fallback command" used when // the compilation database doesn't describe an opened file. // The command used will be approximately `clang $FILE $fallbackFlags`. diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -940,6 +940,16 @@ return true; } +bool fromJSON(const llvm::json::Value &Params, CompilationDatabasePath &P) { + llvm::json::ObjectMapper O(Params); + if (!O) + return true; // 'any' type in LSP. + + O.map("sourceDir", P.sourceDir); + O.map("dbPath", P.dbPath); + return true; +} + bool fromJSON(const llvm::json::Value &Params, InitializationOptions &Opts) { llvm::json::ObjectMapper O(Params); if (!O) @@ -947,6 +957,7 @@ fromJSON(Params, Opts.ConfigSettings); O.map("compilationDatabasePath", Opts.compilationDatabasePath); + O.map("compilationDatabaseMap", Opts.compilationDatabaseMap); O.map("fallbackFlags", Opts.fallbackFlags); O.map("clangdFileStatus", Opts.FileStatus); return true; diff --git a/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp b/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp --- a/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp +++ b/clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp @@ -39,7 +39,7 @@ using ::testing::UnorderedElementsAre; TEST(GlobalCompilationDatabaseTest, FallbackCommand) { - DirectoryBasedGlobalCompilationDatabase DB(None); + DirectoryBasedGlobalCompilationDatabase DB(None, None); auto Cmd = DB.getFallbackCommand(testPath("foo/bar.cc")); EXPECT_EQ(Cmd.Directory, testPath("foo")); EXPECT_THAT(Cmd.CommandLine, ElementsAre("clang", testPath("foo/bar.cc"))); @@ -236,7 +236,7 @@ // Note that gen2.cc goes missing with our following model, not sure this // happens in practice though. { - DirectoryBasedGlobalCompilationDatabase DB(llvm::None); + DirectoryBasedGlobalCompilationDatabase DB(llvm::None, llvm::None); std::vector DiscoveredFiles; auto Sub = DB.watch([&DiscoveredFiles](const std::vector Changes) { @@ -258,7 +258,7 @@ // With a custom compile commands dir. { - DirectoryBasedGlobalCompilationDatabase DB(FS.Root.str().str()); + DirectoryBasedGlobalCompilationDatabase DB(FS.Root.str().str(), llvm::None); std::vector DiscoveredFiles; auto Sub = DB.watch([&DiscoveredFiles](const std::vector Changes) {