Index: clang-tools-extra/trunk/clangd/ClangdLSPServer.h =================================================================== --- clang-tools-extra/trunk/clangd/ClangdLSPServer.h +++ clang-tools-extra/trunk/clangd/ClangdLSPServer.h @@ -27,9 +27,13 @@ /// dispatch and ClangdServer together. class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { public: + /// If \p CompileCommandsDir has a value, compile_commands.json will be + /// loaded only from \p CompileCommandsDir. Otherwise, clangd will look + /// for compile_commands.json in all parent directories of each file. ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount, bool SnippetCompletions, - llvm::Optional ResourceDir); + llvm::Optional ResourceDir, + llvm::Optional CompileCommandsDir); /// Run LSP server loop, receiving input for it from \p In. \p In must be /// opened in binary mode. Output will be written using Out variable passed to Index: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp =================================================================== --- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp +++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp @@ -196,8 +196,9 @@ ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount, bool SnippetCompletions, - llvm::Optional ResourceDir) - : Out(Out), CDB(/*Logger=*/Out), + llvm::Optional ResourceDir, + llvm::Optional CompileCommandsDir) + : Out(Out), CDB(/*Logger=*/Out, std::move(CompileCommandsDir)), Server(CDB, /*DiagConsumer=*/*this, FSProvider, AsyncThreadsCount, SnippetCompletions, /*Logger=*/Out, ResourceDir) {} Index: clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.h =================================================================== --- clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.h +++ clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.h @@ -47,7 +47,8 @@ class DirectoryBasedGlobalCompilationDatabase : public GlobalCompilationDatabase { public: - DirectoryBasedGlobalCompilationDatabase(clangd::Logger &Logger); + DirectoryBasedGlobalCompilationDatabase( + clangd::Logger &Logger, llvm::Optional CompileCommandsDir); std::vector getCompileCommands(PathRef File) override; @@ -56,6 +57,7 @@ private: tooling::CompilationDatabase *getCompilationDatabase(PathRef File); + tooling::CompilationDatabase *tryLoadDatabaseFromPath(PathRef File); std::mutex Mutex; /// Caches compilation databases loaded from directories(keys are @@ -67,6 +69,9 @@ llvm::StringMap> ExtraFlagsForFile; /// Used for logging. clangd::Logger &Logger; + /// Used for command argument pointing to folder where compile_commands.json + /// is located. + llvm::Optional CompileCommandsDir; }; } // namespace clangd } // namespace clang Index: clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.cpp =================================================================== --- clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.cpp +++ clang-tools-extra/trunk/clangd/GlobalCompilationDatabase.cpp @@ -8,10 +8,10 @@ //===---------------------------------------------------------------------===// #include "GlobalCompilationDatabase.h" +#include "Logger.h" #include "clang/Tooling/CompilationDatabase.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" -#include "Logger.h" namespace clang { namespace clangd { @@ -38,8 +38,9 @@ } DirectoryBasedGlobalCompilationDatabase:: - DirectoryBasedGlobalCompilationDatabase(clangd::Logger &Logger) - : Logger(Logger) {} + DirectoryBasedGlobalCompilationDatabase( + clangd::Logger &Logger, llvm::Optional CompileCommandsDir) + : Logger(Logger), CompileCommandsDir(std::move(CompileCommandsDir)) {} std::vector DirectoryBasedGlobalCompilationDatabase::getCompileCommands(PathRef File) { @@ -67,31 +68,50 @@ } tooling::CompilationDatabase * -DirectoryBasedGlobalCompilationDatabase::getCompilationDatabase(PathRef File) { - std::lock_guard Lock(Mutex); +DirectoryBasedGlobalCompilationDatabase::tryLoadDatabaseFromPath(PathRef File) { namespace path = llvm::sys::path; + auto CachedIt = CompilationDatabases.find(File); assert((path::is_absolute(File, path::Style::posix) || path::is_absolute(File, path::Style::windows)) && "path must be absolute"); - for (auto Path = path::parent_path(File); !Path.empty(); - Path = path::parent_path(Path)) { + if (CachedIt != CompilationDatabases.end()) + return CachedIt->second.get(); + std::string Error = ""; + auto CDB = tooling::CompilationDatabase::loadFromDirectory(File, Error); + if (CDB && Error.empty()) { + auto Result = CDB.get(); + CompilationDatabases.insert(std::make_pair(File, std::move(CDB))); + return Result; + } - auto CachedIt = CompilationDatabases.find(Path); - if (CachedIt != CompilationDatabases.end()) - return CachedIt->second.get(); + return nullptr; +} - std::string Error; - auto CDB = tooling::CompilationDatabase::loadFromDirectory(Path, Error); +tooling::CompilationDatabase * +DirectoryBasedGlobalCompilationDatabase::getCompilationDatabase(PathRef File) { + std::lock_guard Lock(Mutex); + + namespace path = llvm::sys::path; + if (CompileCommandsDir.hasValue()) { + tooling::CompilationDatabase *ReturnValue = + tryLoadDatabaseFromPath(CompileCommandsDir.getValue()); + if (ReturnValue == nullptr) + Logger.log("Failed to find compilation database for " + Twine(File) + + "in overriden directory " + CompileCommandsDir.getValue() + + "\n"); + return ReturnValue; + } + + for (auto Path = path::parent_path(File); !Path.empty(); + Path = path::parent_path(Path)) { + auto CDB = tryLoadDatabaseFromPath(Path); if (!CDB) continue; - // FIXME(ibiryukov): Invalidate cached compilation databases on changes - auto Result = CDB.get(); - CompilationDatabases.insert(std::make_pair(Path, std::move(CDB))); - return Result; + return CDB; } Logger.log("Failed to find compilation database for " + Twine(File) + "\n"); Index: clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp =================================================================== --- clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp +++ clang-tools-extra/trunk/clangd/tool/ClangdMain.cpp @@ -11,8 +11,8 @@ #include "JSONRPCDispatcher.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" #include "llvm/Support/Program.h" - #include #include #include @@ -21,6 +21,12 @@ using namespace clang; using namespace clang::clangd; +static llvm::cl::opt CompileCommandsDir( + "compile-commands-dir", + llvm::cl::desc("Specify a path to look for compile_commands.json. If path " + "is invalid, clangd will look in the current directory and " + "parent paths of each source file.")); + static llvm::cl::opt WorkerThreadsCount("j", llvm::cl::desc("Number of async workers used by clangd"), @@ -56,18 +62,37 @@ if (RunSynchronously) WorkerThreadsCount = 0; + /// Validate command line arguments. llvm::raw_ostream &Outs = llvm::outs(); llvm::raw_ostream &Logs = llvm::errs(); JSONOutput Out(Outs, Logs); - // Change stdin to binary to not lose \r\n on windows. - llvm::sys::ChangeStdinToBinary(); + // If --compile-commands-dir arg was invoked, check value and override default + // path. + namespace path = llvm::sys::path; + llvm::Optional CompileCommandsDirPath; + + if (CompileCommandsDir.empty()) { + CompileCommandsDirPath = llvm::None; + } else if (!llvm::sys::path::is_absolute(CompileCommandsDir) || + !llvm::sys::fs::exists(CompileCommandsDir)) { + llvm::errs() << "Path specified by --compile-commands-dir either does not " + "exist or is not an absolute " + "path. The argument will be ignored.\n"; + CompileCommandsDirPath = llvm::None; + } else { + CompileCommandsDirPath = CompileCommandsDir; + } llvm::Optional ResourceDirRef = None; if (!ResourceDir.empty()) ResourceDirRef = ResourceDir; + /// Change stdin to binary to not lose \r\n on windows. + llvm::sys::ChangeStdinToBinary(); + + /// Initialize and run ClangdLSPServer. ClangdLSPServer LSPServer(Out, WorkerThreadsCount, EnableSnippets, - ResourceDirRef); + ResourceDirRef, CompileCommandsDirPath); LSPServer.run(std::cin); }