| //===--- GlobalCompilationDatabase.h -----------------------------*- C++-*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H |
| #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H |
| |
| #include "CompileCommands.h" |
| #include "support/Function.h" |
| #include "support/Path.h" |
| #include "support/ThreadsafeFS.h" |
| #include "clang/Tooling/ArgumentsAdjusters.h" |
| #include "clang/Tooling/CompilationDatabase.h" |
| #include "llvm/ADT/Optional.h" |
| #include "llvm/ADT/StringMap.h" |
| #include <memory> |
| #include <mutex> |
| #include <vector> |
| |
| namespace clang { |
| namespace clangd { |
| |
| class Logger; |
| |
| struct ProjectInfo { |
| // The directory in which the compilation database was discovered. |
| // Empty if directory-based compilation database discovery was not used. |
| std::string SourceRoot; |
| }; |
| |
| /// Provides compilation arguments used for parsing C and C++ files. |
| class GlobalCompilationDatabase { |
| public: |
| virtual ~GlobalCompilationDatabase() = default; |
| |
| /// If there are any known-good commands for building this file, returns one. |
| virtual llvm::Optional<tooling::CompileCommand> |
| getCompileCommand(PathRef File) const = 0; |
| |
| /// Finds the closest project to \p File. |
| virtual llvm::Optional<ProjectInfo> getProjectInfo(PathRef File) const { |
| return llvm::None; |
| } |
| |
| /// Makes a guess at how to build a file. |
| /// The default implementation just runs clang on the file. |
| /// Clangd should treat the results as unreliable. |
| virtual tooling::CompileCommand getFallbackCommand(PathRef File) const; |
| |
| /// If the CDB does any asynchronous work, wait for it to complete. |
| /// For use in tests. |
| virtual bool blockUntilIdle(Deadline D) const { return true; } |
| |
| using CommandChanged = Event<std::vector<std::string>>; |
| /// The callback is notified when files may have new compile commands. |
| /// The argument is a list of full file paths. |
| CommandChanged::Subscription watch(CommandChanged::Listener L) const { |
| return OnCommandChanged.observe(std::move(L)); |
| } |
| |
| protected: |
| mutable CommandChanged OnCommandChanged; |
| }; |
| |
| // Helper class for implementing GlobalCompilationDatabases that wrap others. |
| class DelegatingCDB : public GlobalCompilationDatabase { |
| public: |
| DelegatingCDB(const GlobalCompilationDatabase *Base); |
| DelegatingCDB(std::unique_ptr<GlobalCompilationDatabase> Base); |
| |
| llvm::Optional<tooling::CompileCommand> |
| getCompileCommand(PathRef File) const override; |
| |
| llvm::Optional<ProjectInfo> getProjectInfo(PathRef File) const override; |
| |
| tooling::CompileCommand getFallbackCommand(PathRef File) const override; |
| |
| bool blockUntilIdle(Deadline D) const override; |
| |
| private: |
| const GlobalCompilationDatabase *Base; |
| std::unique_ptr<GlobalCompilationDatabase> BaseOwner; |
| CommandChanged::Subscription BaseChanged; |
| }; |
| |
| /// Gets compile args from tooling::CompilationDatabases built for parent |
| /// directories. |
| class DirectoryBasedGlobalCompilationDatabase |
| : public GlobalCompilationDatabase { |
| public: |
| struct Options { |
| Options(const ThreadsafeFS &TFS) : TFS(TFS) {} |
| |
| const ThreadsafeFS &TFS; |
| // Frequency to check whether e.g. compile_commands.json has changed. |
| std::chrono::steady_clock::duration RevalidateAfter = |
| std::chrono::seconds(5); |
| // Frequency to check whether e.g. compile_commands.json has been created. |
| // (This is more expensive to check frequently, as we check many locations). |
| std::chrono::steady_clock::duration RevalidateMissingAfter = |
| std::chrono::seconds(30); |
| // Used to provide per-file configuration. |
| std::function<Context(llvm::StringRef)> ContextProvider; |
| // Only look for a compilation database in this one fixed directory. |
| // FIXME: fold this into config/context mechanism. |
| llvm::Optional<Path> CompileCommandsDir; |
| }; |
| |
| DirectoryBasedGlobalCompilationDatabase(const Options &Opts); |
| ~DirectoryBasedGlobalCompilationDatabase() override; |
| |
| /// Scans File's parents looking for compilation databases. |
| /// Any extra flags will be added. |
| /// Might trigger OnCommandChanged, if CDB wasn't broadcasted yet. |
| llvm::Optional<tooling::CompileCommand> |
| getCompileCommand(PathRef File) const override; |
| |
| /// Returns the path to first directory containing a compilation database in |
| /// \p File's parents. |
| llvm::Optional<ProjectInfo> getProjectInfo(PathRef File) const override; |
| |
| bool blockUntilIdle(Deadline Timeout) const override; |
| |
| private: |
| Options Opts; |
| |
| class DirectoryCache; |
| // Keyed by possibly-case-folded directory path. |
| // We can hand out pointers as they're stable and entries are never removed. |
| mutable llvm::StringMap<DirectoryCache> DirCaches; |
| mutable std::mutex DirCachesMutex; |
| |
| std::vector<DirectoryCache *> |
| getDirectoryCaches(llvm::ArrayRef<llvm::StringRef> Dirs) const; |
| |
| struct CDBLookupRequest { |
| PathRef FileName; |
| // Whether this lookup should trigger discovery of the CDB found. |
| bool ShouldBroadcast = false; |
| // Cached results newer than this are considered fresh and not checked |
| // against disk. |
| std::chrono::steady_clock::time_point FreshTime; |
| std::chrono::steady_clock::time_point FreshTimeMissing; |
| }; |
| struct CDBLookupResult { |
| std::shared_ptr<const tooling::CompilationDatabase> CDB; |
| ProjectInfo PI; |
| }; |
| llvm::Optional<CDBLookupResult> lookupCDB(CDBLookupRequest Request) const; |
| |
| class BroadcastThread; |
| std::unique_ptr<BroadcastThread> Broadcaster; |
| |
| // Performs broadcast on governed files. |
| void broadcastCDB(CDBLookupResult Res) const; |
| |
| // cache test calls lookupCDB directly to ensure valid/invalid times. |
| friend class DirectoryBasedGlobalCompilationDatabaseCacheTest; |
| }; |
| |
| /// Extracts system include search path from drivers matching QueryDriverGlobs |
| /// and adds them to the compile flags. Base may not be nullptr. |
| /// Returns Base when \p QueryDriverGlobs is empty. |
| std::unique_ptr<GlobalCompilationDatabase> |
| getQueryDriverDatabase(llvm::ArrayRef<std::string> QueryDriverGlobs, |
| std::unique_ptr<GlobalCompilationDatabase> Base); |
| |
| /// Wraps another compilation database, and supports overriding the commands |
| /// using an in-memory mapping. |
| class OverlayCDB : public DelegatingCDB { |
| public: |
| // Base may be null, in which case no entries are inherited. |
| // FallbackFlags are added to the fallback compile command. |
| // Adjuster is applied to all commands, fallback or not. |
| OverlayCDB(const GlobalCompilationDatabase *Base, |
| std::vector<std::string> FallbackFlags = {}, |
| tooling::ArgumentsAdjuster Adjuster = nullptr); |
| |
| llvm::Optional<tooling::CompileCommand> |
| getCompileCommand(PathRef File) const override; |
| tooling::CompileCommand getFallbackCommand(PathRef File) const override; |
| |
| /// Sets or clears the compilation command for a particular file. |
| void |
| setCompileCommand(PathRef File, |
| llvm::Optional<tooling::CompileCommand> CompilationCommand); |
| |
| private: |
| mutable std::mutex Mutex; |
| llvm::StringMap<tooling::CompileCommand> Commands; /* GUARDED_BY(Mut) */ |
| tooling::ArgumentsAdjuster ArgsAdjuster; |
| std::vector<std::string> FallbackFlags; |
| }; |
| |
| } // namespace clangd |
| } // namespace clang |
| |
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H |