From 15398001d1a70be7b97e033e01284a4bad58b0b9 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Thu, 20 Dec 2018 20:53:50 -0800 Subject: [PATCH 01/10] Handle file deletion and register workspace/didChangeWatchedFiles * In the "initialized" callback, send client/registerCapability with DidChangeWatchedFilesRegistrationOptions * In workspace/didChangeWatchedFiles callback, call pipeline::Index * In pipeline::Index, add a `deleted` status --- src/indexer.cc | 8 +-- src/indexer.hh | 4 +- src/message_handler.cc | 1 + src/message_handler.hh | 1 + src/messages/initialize.cc | 29 ++++++++ src/messages/textDocument_did.cc | 6 +- src/messages/workspace.cc | 24 +++++-- src/pipeline.cc | 113 +++++++++++++++++++------------ src/pipeline.hh | 10 ++- src/project.cc | 25 ++++--- src/query.cc | 3 +- src/sema_manager.cc | 11 ++- src/serializer.cc | 6 +- 13 files changed, 160 insertions(+), 81 deletions(-) diff --git a/src/indexer.cc b/src/indexer.cc index 584dd20a..83c8cbc2 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -82,8 +82,7 @@ struct IndexParam { if (!vfs.Stamp(path, it->second.mtime, 1)) return; - it->second.db = std::make_unique(File.getUniqueID(), path, - it->second.content); + it->second.db = std::make_unique(path, it->second.content); } } @@ -1186,9 +1185,8 @@ public: const int IndexFile::kMajorVersion = 19; const int IndexFile::kMinorVersion = 1; -IndexFile::IndexFile(llvm::sys::fs::UniqueID UniqueID, const std::string &path, - const std::string &contents) - : UniqueID(UniqueID), path(path), file_contents(contents) {} +IndexFile::IndexFile(const std::string &path, const std::string &contents) + : path(path), file_contents(contents) {} IndexFunc &IndexFile::ToFunc(Usr usr) { auto [it, inserted] = usr2func.try_emplace(usr); diff --git a/src/indexer.hh b/src/indexer.hh index 88598510..191f8074 100644 --- a/src/indexer.hh +++ b/src/indexer.hh @@ -290,7 +290,6 @@ struct IndexFile { // files accepted by newer ccls. static const int kMinorVersion; - llvm::sys::fs::UniqueID UniqueID; std::string path; std::vector args; // This is unfortunately time_t as used by clang::FileEntry @@ -320,8 +319,7 @@ struct IndexFile { // File contents at the time of index. Not serialized. std::string file_contents; - IndexFile(llvm::sys::fs::UniqueID UniqueID, const std::string &path, - const std::string &contents); + IndexFile(const std::string &path, const std::string &contents); IndexFunc &ToFunc(Usr usr); IndexType &ToType(Usr usr); diff --git a/src/message_handler.cc b/src/message_handler.cc index 99e8197c..b88f777a 100644 --- a/src/message_handler.cc +++ b/src/message_handler.cc @@ -181,6 +181,7 @@ MessageHandler::MessageHandler() { Bind("$ccls/vars", &MessageHandler::ccls_vars); Bind("exit", &MessageHandler::exit); Bind("initialize", &MessageHandler::initialize); + Bind("initialized", &MessageHandler::initialized); Bind("shutdown", &MessageHandler::shutdown); Bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction); Bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens); diff --git a/src/message_handler.hh b/src/message_handler.hh index 7063fe1c..b0a3747c 100644 --- a/src/message_handler.hh +++ b/src/message_handler.hh @@ -250,6 +250,7 @@ private: void ccls_vars(JsonReader &, ReplyOnce &); void exit(EmptyParam &); void initialize(JsonReader &, ReplyOnce &); + void initialized(EmptyParam &); void shutdown(EmptyParam &, ReplyOnce &); void textDocument_codeAction(CodeActionParam &, ReplyOnce &); void textDocument_codeLens(TextDocumentParam &, ReplyOnce &); diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index 10db5208..bbafa9d2 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -42,6 +42,8 @@ namespace { enum class TextDocumentSyncKind { None = 0, Full = 1, Incremental = 2 }; REFLECT_UNDERLYING(TextDocumentSyncKind) +bool didChangeWatchedFiles; + struct ServerCap { struct SaveOptions { bool includeText = false; @@ -239,6 +241,24 @@ struct InitializeResult { }; REFLECT_STRUCT(InitializeResult, capabilities); +struct FileSystemWatcher { + std::string globPattern = "**/*"; +}; +struct DidChangeWatchedFilesRegistration { + std::string id = "didChangeWatchedFiles"; + std::string method = "workspace/didChangeWatchedFiles"; + struct Option { + std::vector watchers = {{}}; + } registerOptions; +}; +struct RegistrationParam { + std::vector registrations = {{}}; +}; +REFLECT_STRUCT(FileSystemWatcher, globPattern); +REFLECT_STRUCT(DidChangeWatchedFilesRegistration::Option, watchers); +REFLECT_STRUCT(DidChangeWatchedFilesRegistration, id, method, registerOptions); +REFLECT_STRUCT(RegistrationParam, registrations); + void *Indexer(void *arg_) { MessageHandler *h; int idx; @@ -298,6 +318,8 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) { capabilities.textDocument.definition.linkSupport; g_config->client.snippetSupport &= capabilities.textDocument.completion.completionItem.snippetSupport; + didChangeWatchedFiles = + capabilities.workspace.didChangeWatchedFiles.dynamicRegistration; // Ensure there is a resource directory. if (g_config->clang.resourceDir.empty()) @@ -370,6 +392,13 @@ void StandaloneInitialize(MessageHandler &handler, const std::string &root) { Initialize(&handler, param, reply); } +void MessageHandler::initialized(EmptyParam &) { + if (didChangeWatchedFiles) { + RegistrationParam param; + pipeline::Request("client/registerCapability", param); + } +} + void MessageHandler::shutdown(EmptyParam &, ReplyOnce &reply) { reply(JsonNull{}); } diff --git a/src/messages/textDocument_did.cc b/src/messages/textDocument_did.cc index 2dc91629..b00b3c7d 100644 --- a/src/messages/textDocument_did.cc +++ b/src/messages/textDocument_did.cc @@ -25,7 +25,7 @@ void MessageHandler::textDocument_didChange(TextDocumentDidChangeParam ¶m) { std::string path = param.textDocument.uri.GetPath(); wfiles->OnChange(param); if (g_config->index.onChange) - pipeline::Index(path, {}, IndexMode::OnChange); + pipeline::Index(path, {}, IndexMode::OnChange, true); manager->OnView(path); if (g_config->diagnostics.onChange >= 0) manager->ScheduleDiag(path, g_config->diagnostics.onChange); @@ -56,14 +56,14 @@ void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam ¶m) { std::pair lang = lookupExtension(path); if ((lang.first != LanguageId::Unknown && !lang.second) || !pipeline::pending_index_requests) - pipeline::Index(path, {}, IndexMode::Normal); + pipeline::Index(path, {}, IndexMode::Normal, false); manager->OnView(path); } void MessageHandler::textDocument_didSave(TextDocumentParam ¶m) { const std::string &path = param.textDocument.uri.GetPath(); - pipeline::Index(path, {}, IndexMode::Normal); + pipeline::Index(path, {}, IndexMode::Normal, false); manager->OnSave(path); } } // namespace ccls diff --git a/src/messages/workspace.cc b/src/messages/workspace.cc index 5b049958..5435d98f 100644 --- a/src/messages/workspace.cc +++ b/src/messages/workspace.cc @@ -23,11 +23,13 @@ limitations under the License. #include #include +#include #include #include #include #include +using namespace llvm; namespace ccls { REFLECT_STRUCT(SymbolInformation, name, kind, location, containerName); @@ -44,20 +46,30 @@ void MessageHandler::workspace_didChangeWatchedFiles( DidChangeWatchedFilesParam ¶m) { for (auto &event : param.changes) { std::string path = event.uri.GetPath(); + if ((g_config->cacheDirectory.size() && + StringRef(path).startswith(g_config->cacheDirectory)) || + lookupExtension(path).first == LanguageId::Unknown) + return; + for (std::string cur = path; cur.size(); cur = sys::path::parent_path(cur)) + if (cur[0] == '.') + return; + IndexMode mode = wfiles->GetFile(path) ? IndexMode::Normal : IndexMode::NonInteractive; switch (event.type) { case FileChangeType::Created: case FileChangeType::Changed: { - pipeline::Index(path, {}, mode); - if (mode == IndexMode::Normal) - manager->OnSave(path); - else - manager->OnClose(path); + pipeline::Index(path, {}, mode, true); + if (event.type == FileChangeType::Changed) { + if (mode == IndexMode::Normal) + manager->OnSave(path); + else + manager->OnClose(path); + } break; } case FileChangeType::Deleted: - pipeline::Index(path, {}, mode); + pipeline::Index(path, {}, mode, false); manager->OnClose(path); break; } diff --git a/src/pipeline.cc b/src/pipeline.cc index e03158b7..e55f9a40 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -77,16 +77,16 @@ void StandaloneInitialize(MessageHandler &, const std::string &root); namespace pipeline { std::atomic quit; -std::atomic loaded_ts = ATOMIC_VAR_INIT(0), - pending_index_requests = ATOMIC_VAR_INIT(0); +std::atomic loaded_ts{0}, pending_index_requests{0}, request_id{0}; int64_t tick = 0; namespace { -struct Index_Request { +struct IndexRequest { std::string path; std::vector args; IndexMode mode; + bool must_exist = false; RequestId id; int64_t ts = tick++; }; @@ -99,7 +99,7 @@ MultiQueueWaiter *main_waiter; MultiQueueWaiter *indexer_waiter; MultiQueueWaiter *stdout_waiter; ThreadedQueue *on_request; -ThreadedQueue *index_request; +ThreadedQueue *index_request; ThreadedQueue *on_indexed; ThreadedQueue *for_stdout; @@ -184,7 +184,7 @@ std::mutex &GetFileMutex(const std::string &path) { bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, Project *project, VFS *vfs, const GroupMatch &matcher) { - std::optional opt_request = index_request->TryPopFront(); + std::optional opt_request = index_request->TryPopFront(); if (!opt_request) return false; auto &request = *opt_request; @@ -206,23 +206,33 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, return false; } - Project::Entry entry = project->FindEntry(request.path, true); + // must_exist is currently unused. + Project::Entry entry = project->FindEntry(request.path, false); + if (request.must_exist && entry.filename.empty()) + return true; if (request.args.size()) entry.args = request.args; std::string path_to_index = entry.filename; std::unique_ptr prev; + bool deleted = false; + int reparse = 0; std::optional write_time = LastWriteTime(path_to_index); - if (!write_time) - return true; - int reparse = vfs->Stamp(path_to_index, *write_time, 0); - if (request.path != path_to_index) { - std::optional mtime1 = LastWriteTime(request.path); - if (!mtime1) - return true; - if (vfs->Stamp(request.path, *mtime1, 0)) - reparse = 2; + if (!write_time) { + deleted = true; + } else { + reparse = vfs->Stamp(path_to_index, *write_time, 0); + if (request.path != path_to_index) { + std::optional mtime1 = LastWriteTime(request.path); + if (!mtime1) + deleted = true; + else if (vfs->Stamp(request.path, *mtime1, 0)) + reparse = 2; + } } + if (deleted) + reparse = 2; + if (g_config->index.onChange) { reparse = 2; std::lock_guard lock(vfs->mutex); @@ -306,27 +316,34 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, for (auto &arg : entry.args) (line += ' ') += arg; } - LOG_S(INFO) << "parse " << path_to_index << line; + LOG_S(INFO) << (deleted ? "delete " : "parse ") << path_to_index << line; } - std::vector> remapped; - if (g_config->index.onChange) { - std::string content = wfiles->GetContent(path_to_index); - if (content.size()) - remapped.emplace_back(path_to_index, content); - } - bool ok; - auto indexes = idx::Index(completion, wfiles, vfs, entry.directory, - path_to_index, entry.args, remapped, ok); - - if (!ok) { - if (request.id.Valid()) { - ResponseError err; - err.code = ErrorCode::InternalError; - err.message = "failed to index " + path_to_index; - pipeline::ReplyError(request.id, err); + std::vector> indexes; + if (deleted) { + indexes.push_back(std::make_unique(request.path, "")); + if (request.path != path_to_index) + indexes.push_back(std::make_unique(path_to_index, "")); + } else { + std::vector> remapped; + if (g_config->index.onChange) { + std::string content = wfiles->GetContent(path_to_index); + if (content.size()) + remapped.emplace_back(path_to_index, content); + } + bool ok; + indexes = idx::Index(completion, wfiles, vfs, entry.directory, + path_to_index, entry.args, remapped, ok); + + if (!ok) { + if (request.id.Valid()) { + ResponseError err; + err.code = ErrorCode::InternalError; + err.message = "failed to index " + path_to_index; + pipeline::ReplyError(request.id, err); + } + return true; } - return true; } for (std::unique_ptr &curr : indexes) { @@ -336,8 +353,9 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, continue; } - LOG_IF_S(INFO, loud) << "store index for " << path << " (delta: " << !!prev - << ")"; + if (!deleted) + LOG_IF_S(INFO, loud) << "store index for " << path + << " (delta: " << !!prev << ")"; { std::lock_guard lock(GetFileMutex(path)); if (vfs->Loaded(path)) @@ -351,9 +369,14 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, std::string().swap(it.first->second.index.file_contents); } else { std::string cache_path = GetCachePath(path); - WriteToFile(cache_path, curr->file_contents); - WriteToFile(AppendSerializationFormat(cache_path), - Serialize(g_config->cacheFormat, *curr)); + if (deleted) { + (void)sys::fs::remove(cache_path); + (void)sys::fs::remove(AppendSerializationFormat(cache_path)); + } else { + WriteToFile(cache_path, curr->file_contents); + WriteToFile(AppendSerializationFormat(cache_path), + Serialize(g_config->cacheFormat, *curr)); + } } on_indexed->PushBack(IndexUpdate::CreateDelta(prev.get(), curr.get()), request.mode != IndexMode::NonInteractive); @@ -404,7 +427,7 @@ void Init() { on_indexed = new ThreadedQueue(main_waiter); indexer_waiter = new MultiQueueWaiter; - index_request = new ThreadedQueue(indexer_waiter); + index_request = new ThreadedQueue(indexer_waiter); stdout_waiter = new MultiQueueWaiter; for_stdout = new ThreadedQueue(stdout_waiter); @@ -637,9 +660,10 @@ void Standalone(const std::string &root) { } void Index(const std::string &path, const std::vector &args, - IndexMode mode, RequestId id) { + IndexMode mode, bool must_exist, RequestId id) { pending_index_requests++; - index_request->PushBack({path, args, mode, id}, mode != IndexMode::NonInteractive); + index_request->PushBack({path, args, mode, must_exist, id}, + mode != IndexMode::NonInteractive); } std::optional LoadIndexedContent(const std::string &path) { @@ -653,7 +677,8 @@ std::optional LoadIndexedContent(const std::string &path) { return ReadContent(GetCachePath(path)); } -void Notify(const char *method, const std::function &fn) { +void NotifyOrRequest(const char *method, bool request, + const std::function &fn) { rapidjson::StringBuffer output; rapidjson::Writer w(output); w.StartObject(); @@ -661,6 +686,10 @@ void Notify(const char *method, const std::function &fn) { w.String("2.0"); w.Key("method"); w.String(method); + if (request) { + w.Key("id"); + w.Int64(request_id.fetch_add(1, std::memory_order_relaxed)); + } w.Key("params"); JsonWriter writer(&w); fn(writer); diff --git a/src/pipeline.hh b/src/pipeline.hh index da6b44bc..c61c6dc2 100644 --- a/src/pipeline.hh +++ b/src/pipeline.hh @@ -51,13 +51,17 @@ void MainLoop(); void Standalone(const std::string &root); void Index(const std::string &path, const std::vector &args, - IndexMode mode, RequestId id = {}); + IndexMode mode, bool must_exist, RequestId id = {}); std::optional LoadIndexedContent(const std::string& path); -void Notify(const char *method, const std::function &fn); +void NotifyOrRequest(const char *method, bool request, + const std::function &fn); template void Notify(const char *method, T &result) { - Notify(method, [&](JsonWriter &w) { Reflect(w, result); }); + NotifyOrRequest(method, false, [&](JsonWriter &w) { Reflect(w, result); }); +} +template void Request(const char *method, T &result) { + NotifyOrRequest(method, true, [&](JsonWriter &w) { Reflect(w, result); }); } void Reply(RequestId id, const std::function &fn); diff --git a/src/project.cc b/src/project.cc index 3305a662..9346af54 100644 --- a/src/project.cc +++ b/src/project.cc @@ -424,7 +424,7 @@ void Project::Load(const std::string &root) { } Project::Entry Project::FindEntry(const std::string &path, - bool can_be_inferred) { + bool must_exist) { Project::Folder *best_folder = nullptr; const Entry *best = nullptr; std::shared_lock lock(mtx); @@ -432,11 +432,12 @@ Project::Entry Project::FindEntry(const std::string &path, auto it = folder.path2entry_index.find(path); if (it != folder.path2entry_index.end()) { Project::Entry &entry = folder.entries[it->second]; - if (can_be_inferred || entry.filename == path) + if (!must_exist || entry.filename == path) return entry; } } + bool exists = false; std::string dir; const std::vector *extra = nullptr; Project::Entry ret; @@ -444,11 +445,12 @@ Project::Entry Project::FindEntry(const std::string &path, if (StringRef(path).startswith(root)) for (auto &[dir1, args] : folder.dot_ccls) if (StringRef(path).startswith(dir1)) { - if (AppendToCDB(args)) { - dir = dir1; - extra = &args; + dir = dir1; + extra = &args; + if (AppendToCDB(args)) goto out; - } + exists = true; + ret.root = ret.directory = root; ret.filename = path; if (args.empty()) { @@ -464,6 +466,8 @@ Project::Entry Project::FindEntry(const std::string &path, return ret; } out: + if (must_exist && !exists) + return ret; if (!best) { int best_score = INT_MIN; @@ -526,9 +530,10 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) { if (match.Matches(entry.filename, &reason) && match_i.Matches(entry.filename, &reason)) { bool interactive = wfiles->GetFile(entry.filename) != nullptr; - pipeline::Index( - entry.filename, entry.args, - interactive ? IndexMode::Normal : IndexMode::NonInteractive, id); + pipeline::Index(entry.filename, entry.args, + interactive ? IndexMode::Normal + : IndexMode::NonInteractive, + false, id); } else { LOG_V(1) << "[" << i << "/" << folder.entries.size() << "]: " << reason << "; skip " << entry.filename; @@ -541,6 +546,6 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) { pipeline::loaded_ts = pipeline::tick; // Dummy request to indicate that project is loaded and // trigger refreshing semantic highlight for all working files. - pipeline::Index("", {}, IndexMode::NonInteractive); + pipeline::Index("", {}, IndexMode::NonInteractive, false); } } // namespace ccls diff --git a/src/query.cc b/src/query.cc index aa305a3e..6a2d1b5b 100644 --- a/src/query.cc +++ b/src/query.cc @@ -83,8 +83,7 @@ bool TryReplaceDef(llvm::SmallVectorImpl &def_list, Q &&def) { IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) { IndexUpdate r; - static IndexFile empty(llvm::sys::fs::UniqueID(0, 0), current->path, - ""); + static IndexFile empty(current->path, ""); if (previous) r.prev_lid2path = std::move(previous->lid2path); else diff --git a/src/sema_manager.cc b/src/sema_manager.cc index 3d969b36..89388ee0 100644 --- a/src/sema_manager.cc +++ b/src/sema_manager.cc @@ -302,7 +302,7 @@ public: std::unique_ptr BuildCompilerInstance( Session &session, std::unique_ptr CI, IntrusiveRefCntPtr FS, DiagnosticConsumer &DC, - const PreambleData *preamble, const std::string &path, + const PreambleData *preamble, const std::string &main, std::unique_ptr &Buf) { if (preamble) { #if LLVM_VERSION_MAJOR >= 7 @@ -311,7 +311,7 @@ std::unique_ptr BuildCompilerInstance( preamble->Preamble.AddImplicitPreamble(*CI, FS, Buf.get()); #endif } else { - CI->getPreprocessorOpts().addRemappedFile(path, Buf.get()); + CI->getPreprocessorOpts().addRemappedFile(main, Buf.get()); } auto Clang = std::make_unique(session.PCH); @@ -328,6 +328,11 @@ std::unique_ptr BuildCompilerInstance( Clang->createFileManager(); Clang->setSourceManager(new SourceManager(Clang->getDiagnostics(), Clang->getFileManager(), true)); + auto &IS = Clang->getFrontendOpts().Inputs; + if (IS.size()) { + assert(IS[0].isFile()); + IS[0] = FrontendInputFile(main, IS[0].getKind(), IS[0].isSystem()); + } return Clang; } @@ -702,7 +707,7 @@ SemaManager::EnsureSession(const std::string &path, bool *created) { std::shared_ptr session = sessions.Get(path); if (!session) { session = std::make_shared( - project_->FindEntry(path, false), wfiles, PCH); + project_->FindEntry(path, true), wfiles, PCH); std::string line; if (LOG_V_ENABLED(1)) { line = "\n "; diff --git a/src/serializer.cc b/src/serializer.cc index c754f83a..0dec4710 100644 --- a/src/serializer.cc +++ b/src/serializer.cc @@ -482,8 +482,7 @@ Deserialize(SerializeFormat format, const std::string &path, if (major != IndexFile::kMajorVersion || minor != IndexFile::kMinorVersion) throw std::invalid_argument("Invalid version"); - file = std::make_unique(sys::fs::UniqueID(0, 0), path, - file_content); + file = std::make_unique(path, file_content); ReflectFile(reader, *file); } catch (std::invalid_argument &e) { LOG_S(INFO) << "failed to deserialize '" << path << "': " << e.what(); @@ -506,8 +505,7 @@ Deserialize(SerializeFormat format, const std::string &path, if (reader.HasParseError()) return nullptr; - file = std::make_unique(sys::fs::UniqueID(0, 0), path, - file_content); + file = std::make_unique(path, file_content); JsonReader json_reader{&reader}; try { ReflectFile(json_reader, *file); From 5c5505656285eae26809ea37a2b4c21bcd8bfef6 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sun, 23 Dec 2018 21:22:51 -0800 Subject: [PATCH 02/10] Adjust FrontendOpts.Inputs[0] for inferred files --- src/clang_tu.cc | 5 ++++- src/clang_tu.hh | 3 ++- src/indexer.cc | 53 ++++++++++++++------------------------------- src/pipeline.cc | 8 +++++-- src/project.cc | 21 +++++++++--------- src/project.hh | 2 +- src/sema_manager.cc | 8 +++---- 7 files changed, 43 insertions(+), 57 deletions(-) diff --git a/src/clang_tu.cc b/src/clang_tu.cc index 4092372b..a2d982b4 100644 --- a/src/clang_tu.cc +++ b/src/clang_tu.cc @@ -93,7 +93,7 @@ Range FromTokenRangeDefaulted(const SourceManager &SM, const LangOptions &Lang, } std::unique_ptr -BuildCompilerInvocation(std::vector args, +BuildCompilerInvocation(const std::string &main, std::vector args, IntrusiveRefCntPtr VFS) { std::string save = "-resource-dir=" + g_config->clang.resourceDir; args.push_back(save.c_str()); @@ -106,6 +106,9 @@ BuildCompilerInvocation(std::vector args, CI->getDiagnosticOpts().IgnoreWarnings = true; CI->getFrontendOpts().DisableFree = false; CI->getLangOpts()->SpellChecking = false; + auto &IS = CI->getFrontendOpts().Inputs; + if (IS.size()) + IS[0] = FrontendInputFile(main, IS[0].getKind(), IS[0].isSystem()); } return CI; } diff --git a/src/clang_tu.hh b/src/clang_tu.hh index 2bfa2365..05096c1e 100644 --- a/src/clang_tu.hh +++ b/src/clang_tu.hh @@ -51,7 +51,8 @@ Range FromTokenRangeDefaulted(const clang::SourceManager &SM, Range range); std::unique_ptr -BuildCompilerInvocation(std::vector args, +BuildCompilerInvocation(const std::string &main, + std::vector args, llvm::IntrusiveRefCntPtr VFS); const char *ClangBuiltinTypeName(int); diff --git a/src/indexer.cc b/src/indexer.cc index 83c8cbc2..43969f23 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -1230,14 +1230,15 @@ void Init() { std::vector> Index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs, - const std::string &opt_wdir, const std::string &file, + const std::string &opt_wdir, const std::string &main, const std::vector &args, const std::vector> &remapped, bool &ok) { ok = true; auto PCH = std::make_shared(); llvm::IntrusiveRefCntPtr FS = llvm::vfs::getRealFileSystem(); - std::shared_ptr CI = BuildCompilerInvocation(args, FS); + std::shared_ptr CI = + BuildCompilerInvocation(main, args, FS); // e.g. .s if (!CI) return {}; @@ -1257,35 +1258,13 @@ Index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs, // HSOpts.UseBuiltinIncludes) // HSOpts.ResourceDir = g_config->clang.resourceDir; } - std::string buf = wfiles->GetContent(file); + std::string buf = wfiles->GetContent(main); std::vector> Bufs; - if (buf.size()) { - // If there is a completion session, reuse its preamble if exists. - bool done_remap = false; -#if 0 - std::shared_ptr session = - manager->TryGetSession(file, false, false); - if (session) - if (auto preamble = session->GetPreamble()) { - Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(buf)); - auto Bounds = ComputePreambleBounds(*CI->getLangOpts(), Bufs.back().get(), 0); - if (preamble->Preamble.CanReuse(*CI, Bufs.back().get(), Bounds, - FS.get())) { - preamble->Preamble.AddImplicitPreamble(*CI, FS, Bufs.back().get()); - done_remap = true; - } - } -#endif + if (buf.size()) for (auto &[filename, content] : remapped) { - if (filename == file && done_remap) - continue; Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(content)); - CI->getPreprocessorOpts().addRemappedFile( - filename == file ? CI->getFrontendOpts().Inputs[0].getFile() - : StringRef(filename), - Bufs.back().get()); + CI->getPreprocessorOpts().addRemappedFile(filename, Bufs.back().get()); } - } DiagnosticConsumer DC; auto Clang = std::make_unique(PCH); @@ -1314,20 +1293,20 @@ Index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs, { llvm::CrashRecoveryContext CRC; auto parse = [&]() { - if (!Action->BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) - return; - if (!Action->Execute()) - return; - Action->EndSourceFile(); - ok = true; - }; + if (!Action->BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) + return; + if (!Action->Execute()) + return; + Action->EndSourceFile(); + ok = true; + }; if (!CRC.RunSafely(parse)) { - LOG_S(ERROR) << "clang crashed for " << file; + LOG_S(ERROR) << "clang crashed for " << main; return {}; } } if (!ok) { - LOG_S(ERROR) << "failed to index " << file; + LOG_S(ERROR) << "failed to index " << main; return {}; } for (auto &Buf : Bufs) @@ -1338,7 +1317,7 @@ Index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs, if (!it.second.db) continue; std::unique_ptr &entry = it.second.db; - entry->import_file = file; + entry->import_file = main; entry->args = args; for (auto &[_, it] : entry->uid2lid_and_path) entry->lid2path.emplace_back(it.first, std::move(it.second)); diff --git a/src/pipeline.cc b/src/pipeline.cc index e55f9a40..20f00df8 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -29,6 +29,7 @@ limitations under the License. #include #include +#include #include #include using namespace llvm; @@ -122,9 +123,11 @@ bool CacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path, } } + // For inferred files, allow -o a a.cc -> -o b b.cc + std::string stem = sys::path::stem(path); bool changed = prev->args.size() != args.size(); for (size_t i = 0; !changed && i < args.size(); i++) - if (strcmp(prev->args[i], args[i])) + if (strcmp(prev->args[i], args[i]) && sys::path::stem(args[i]) != stem) changed = true; if (changed) LOG_S(INFO) << "args changed for " << path @@ -207,7 +210,8 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, } // must_exist is currently unused. - Project::Entry entry = project->FindEntry(request.path, false); + Project::Entry entry = + project->FindEntry(request.path, true, request.must_exist); if (request.must_exist && entry.filename.empty()) return true; if (request.args.size()) diff --git a/src/project.cc b/src/project.cc index 9346af54..e15d8294 100644 --- a/src/project.cc +++ b/src/project.cc @@ -423,17 +423,24 @@ void Project::Load(const std::string &root) { } } -Project::Entry Project::FindEntry(const std::string &path, +Project::Entry Project::FindEntry(const std::string &path, bool can_redirect, bool must_exist) { Project::Folder *best_folder = nullptr; const Entry *best = nullptr; std::shared_lock lock(mtx); for (auto &[root, folder] : root2folder) { + // The entry may have different filename but it doesn't matter when building + // CompilerInvocation. The main filename is specified separately. auto it = folder.path2entry_index.find(path); if (it != folder.path2entry_index.end()) { Project::Entry &entry = folder.entries[it->second]; - if (!must_exist || entry.filename == path) + if (can_redirect || entry.filename == path) return entry; + if (entry.compdb_size) { + best_folder = &folder; + best = &entry; + } + break; } } @@ -483,7 +490,7 @@ out: best_folder = &folder; } } - } + } } ret.is_inferred = true; @@ -497,14 +504,6 @@ out: ret.root = best->root; ret.directory = best->directory; ret.args = best->args; - std::string base_name = sys::path::filename(best->filename); - for (const char *&arg : ret.args) { - try { - if (arg == best->filename || sys::path::filename(arg) == base_name) - arg = Intern(path); - } catch (...) { - } - } ret.args.resize(best->compdb_size); if (extra && extra->size()) ret.args.insert(ret.args.end(), extra->begin() + 1, extra->end()); diff --git a/src/project.hh b/src/project.hh index 0a700dea..89df1b25 100644 --- a/src/project.hh +++ b/src/project.hh @@ -68,7 +68,7 @@ struct Project { // Lookup the CompilationEntry for |filename|. If no entry was found this // will infer one based on existing project structure. - Entry FindEntry(const std::string &path, bool can_be_inferred); + Entry FindEntry(const std::string &path, bool can_redirect, bool must_exist); // If the client has overridden the flags, or specified them for a file // that is not in the compilation_database.json make sure those changes diff --git a/src/sema_manager.cc b/src/sema_manager.cc index 89388ee0..91fae5e3 100644 --- a/src/sema_manager.cc +++ b/src/sema_manager.cc @@ -420,7 +420,7 @@ void *PreambleMain(void *manager_) { IntrusiveRefCntPtr FS = stat_cache->Producer(session->FS); if (std::unique_ptr CI = - BuildCompilerInvocation(session->file.args, FS)) + BuildCompilerInvocation(task.path, session->file.args, FS)) BuildPreamble(*session, *CI, FS, task, std::move(stat_cache)); if (task.from_diag) { @@ -460,7 +460,7 @@ void *CompletionMain(void *manager_) { IntrusiveRefCntPtr FS = preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS; std::unique_ptr CI = - BuildCompilerInvocation(session->file.args, FS); + BuildCompilerInvocation(task->path, session->file.args, FS); if (!CI) continue; auto &FOpts = CI->getFrontendOpts(); @@ -562,7 +562,7 @@ void *DiagnosticMain(void *manager_) { } std::unique_ptr CI = - BuildCompilerInvocation(session->file.args, FS); + BuildCompilerInvocation(task.path, session->file.args, FS); if (!CI) continue; // If main file is a header, add -Wno-unused-function @@ -707,7 +707,7 @@ SemaManager::EnsureSession(const std::string &path, bool *created) { std::shared_ptr session = sessions.Get(path); if (!session) { session = std::make_shared( - project_->FindEntry(path, true), wfiles, PCH); + project_->FindEntry(path, false, false), wfiles, PCH); std::string line; if (LOG_V_ENABLED(1)) { line = "\n "; From 0b80a10ca178eaee0bd572158728a478e4bc5ae7 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Mon, 24 Dec 2018 22:20:00 -0800 Subject: [PATCH 03/10] Delay requests if the document has not not indexed (#176) This fixes a plethora of "not indexed" errors when the document has not been indexed. * Message handler throws NotIndexed if not overdue * The message is put into backlog and tagged with backlog_path * path2backlog[path] tracks backlog associated with document `path` * The backlog is cleared when the index is merged * backlog[0] is forced to run if it becomes overdue --- src/config.hh | 10 ++++- src/lsp.hh | 4 +- src/message_handler.cc | 38 ++++++++++++----- src/message_handler.hh | 12 +++++- src/messages/ccls_call.cc | 3 +- src/messages/ccls_inheritance.cc | 6 +-- src/messages/ccls_member.cc | 7 +--- src/messages/ccls_navigate.cc | 4 +- src/messages/ccls_vars.cc | 4 +- src/messages/initialize.cc | 2 +- src/messages/textDocument_code.cc | 17 +++----- src/messages/textDocument_completion.cc | 2 +- src/messages/textDocument_definition.cc | 21 +++------- src/messages/textDocument_document.cc | 15 ++----- src/messages/textDocument_foldingRange.cc | 7 +--- src/messages/textDocument_formatting.cc | 15 ++----- src/messages/textDocument_hover.cc | 7 +--- src/messages/textDocument_references.cc | 7 +--- src/messages/textDocument_rename.cc | 4 +- src/messages/textDocument_signatureHelp.cc | 5 +-- src/pipeline.cc | 48 ++++++++++++++++++++-- src/threaded_queue.hh | 9 ++++ 22 files changed, 142 insertions(+), 105 deletions(-) diff --git a/src/config.hh b/src/config.hh index 1fec1140..4c7eabbb 100644 --- a/src/config.hh +++ b/src/config.hh @@ -242,6 +242,12 @@ struct Config { std::vector whitelist; } index; + struct Request { + // If the document of a request has not been indexed, wait up to this many + // milleseconds before reporting error. + int64_t timeout = 5000; + } request; + struct Session { int maxNum = 10; } session; @@ -278,12 +284,14 @@ REFLECT_STRUCT(Config::Index, blacklist, comments, initialBlacklist, initialWhitelist, multiVersion, multiVersionBlacklist, multiVersionWhitelist, onChange, threads, trackDependency, whitelist); +REFLECT_STRUCT(Config::Request, timeout); REFLECT_STRUCT(Config::Session, maxNum); REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort); REFLECT_STRUCT(Config::Xref, maxNum); REFLECT_STRUCT(Config, compilationDatabaseCommand, compilationDatabaseDirectory, cacheDirectory, cacheFormat, clang, client, codeLens, completion, - diagnostics, highlight, index, session, workspaceSymbol, xref); + diagnostics, highlight, index, request, session, workspaceSymbol, + xref); extern Config *g_config; diff --git a/src/lsp.hh b/src/lsp.hh index c44dbdf5..8f8efda9 100644 --- a/src/lsp.hh +++ b/src/lsp.hh @@ -21,8 +21,8 @@ limitations under the License. #include +#include #include -#include namespace ccls { struct RequestId { @@ -43,6 +43,8 @@ struct InMessage { std::string method; std::unique_ptr message; std::unique_ptr document; + std::chrono::steady_clock::time_point deadline; + std::string backlog_path; }; enum class ErrorCode { diff --git a/src/message_handler.cc b/src/message_handler.cc index b88f777a..a9878f81 100644 --- a/src/message_handler.cc +++ b/src/message_handler.cc @@ -109,11 +109,8 @@ struct ScanLineEvent { }; } // namespace -void ReplyOnce::NotReady(bool file) { - if (file) - Error(ErrorCode::InvalidRequest, "not opened"); - else - Error(ErrorCode::InternalError, "not indexed"); +void ReplyOnce::NotOpened(std::string_view path) { + Error(ErrorCode::InvalidRequest, std::string(path) + " is not opened"); } void ReplyOnce::ReplyLocationLink(std::vector &result) { @@ -215,13 +212,11 @@ MessageHandler::MessageHandler() { void MessageHandler::Run(InMessage &msg) { rapidjson::Document &doc = *msg.document; - rapidjson::Value param; + rapidjson::Value null; auto it = doc.FindMember("params"); - if (it != doc.MemberEnd()) - param = it->value; - JsonReader reader(¶m); + JsonReader reader(it != doc.MemberEnd() ? &it->value : &null); if (msg.id.Valid()) { - ReplyOnce reply{msg.id}; + ReplyOnce reply{*this, msg.id}; auto it = method2request.find(msg.method); if (it != method2request.end()) { try { @@ -230,6 +225,8 @@ void MessageHandler::Run(InMessage &msg) { reply.Error(ErrorCode::InvalidParams, "invalid params of " + msg.method + ": expected " + ex.what() + " for " + reader.GetPath()); + } catch (NotIndexed &) { + throw; } catch (...) { reply.Error(ErrorCode::InternalError, "failed to process " + msg.method); } @@ -249,7 +246,8 @@ void MessageHandler::Run(InMessage &msg) { } } -QueryFile *MessageHandler::FindFile(const std::string &path, int *out_file_id) { +QueryFile *MessageHandler::FindFile(const std::string &path, + int *out_file_id) { QueryFile *ret = nullptr; auto it = db->name2file_id.find(LowerPathIfInsensitive(path)); if (it != db->name2file_id.end()) { @@ -266,6 +264,24 @@ QueryFile *MessageHandler::FindFile(const std::string &path, int *out_file_id) { return ret; } +std::pair +MessageHandler::FindOrFail(const std::string &path, ReplyOnce &reply, + int *out_file_id) { + WorkingFile *wf = wfiles->GetFile(path); + if (!wf) { + reply.NotOpened(path); + return {nullptr, nullptr}; + } + QueryFile *file = FindFile(path, out_file_id); + if (!file) { + if (!overdue) + throw NotIndexed{path}; + reply.Error(ErrorCode::InvalidRequest, "not indexed"); + return {nullptr, nullptr}; + } + return {file, wf}; +} + void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file) { CclsSetSkippedRanges params; params.uri = DocumentUri::FromPath(wfile->filename); diff --git a/src/message_handler.hh b/src/message_handler.hh index b0a3747c..949fc242 100644 --- a/src/message_handler.hh +++ b/src/message_handler.hh @@ -199,7 +199,13 @@ REFLECT_STRUCT(Diagnostic, range, severity, code, source, message); REFLECT_STRUCT(ShowMessageParam, type, message); REFLECT_UNDERLYING_B(LanguageId); +struct NotIndexed { + std::string path; +}; +struct MessageHandler; + struct ReplyOnce { + MessageHandler &handler; RequestId id; template void operator()(Res &&result) const { if (id.Valid()) @@ -210,7 +216,7 @@ struct ReplyOnce { if (id.Valid()) pipeline::ReplyError(id, [&](JsonWriter &w) { Reflect(w, err); }); } - void NotReady(bool file); + void NotOpened(std::string_view path); void ReplyLocationLink(std::vector &result); }; @@ -225,10 +231,14 @@ struct MessageHandler { llvm::StringMap> method2notification; llvm::StringMap> method2request; + bool overdue = false; MessageHandler(); void Run(InMessage &msg); QueryFile *FindFile(const std::string &path, int *out_file_id = nullptr); + std::pair FindOrFail(const std::string &path, + ReplyOnce &reply, + int *out_file_id = nullptr); private: void Bind(const char *method, void (MessageHandler::*handler)(JsonReader &)); diff --git a/src/messages/ccls_call.cc b/src/messages/ccls_call.cc index 5a0e6d8e..a2a221d4 100644 --- a/src/messages/ccls_call.cc +++ b/src/messages/ccls_call.cc @@ -200,8 +200,7 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) { Expand(this, &*result, param.callee, param.callType, param.qualified, param.levels); } else { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); if (!wf) return; for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) { diff --git a/src/messages/ccls_inheritance.cc b/src/messages/ccls_inheritance.cc index b7520ddf..a9c18b91 100644 --- a/src/messages/ccls_inheritance.cc +++ b/src/messages/ccls_inheritance.cc @@ -146,10 +146,10 @@ void Inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) { Expand(m, &*result, param.derived, param.qualified, param.levels))) result.reset(); } else { - QueryFile *file = m->FindFile(param.textDocument.uri.GetPath()); - if (!file) + auto [file, wf] = m->FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!wf) { return; - WorkingFile *wf = m->wfiles->GetFile(file->def->path); + } for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) if (sym.kind == Kind::Func || sym.kind == Kind::Type) { result = BuildInitial(m, sym, param.derived, param.qualified, diff --git a/src/messages/ccls_member.cc b/src/messages/ccls_member.cc index d94a3d88..52e46823 100644 --- a/src/messages/ccls_member.cc +++ b/src/messages/ccls_member.cc @@ -281,12 +281,9 @@ void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) { param.levels, param.kind))) result.reset(); } else { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!wf) return; - } for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) { switch (sym.kind) { case Kind::Func: diff --git a/src/messages/ccls_navigate.cc b/src/messages/ccls_navigate.cc index d09d57a8..2afef719 100644 --- a/src/messages/ccls_navigate.cc +++ b/src/messages/ccls_navigate.cc @@ -41,10 +41,8 @@ Maybe FindParent(QueryFile *file, Pos pos) { void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) { Param param; Reflect(reader, param); - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); if (!wf) { - reply.NotReady(file); return; } Position ls_pos = param.position; diff --git a/src/messages/ccls_vars.cc b/src/messages/ccls_vars.cc index 2673ecf0..4ad5bc21 100644 --- a/src/messages/ccls_vars.cc +++ b/src/messages/ccls_vars.cc @@ -31,10 +31,8 @@ REFLECT_STRUCT(Param, textDocument, position, kind); void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) { Param param; Reflect(reader, param); - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); if (!wf) { - reply.NotReady(file); return; } diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index bbafa9d2..9f2948d6 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -388,7 +388,7 @@ void MessageHandler::initialize(JsonReader &reader, ReplyOnce &reply) { void StandaloneInitialize(MessageHandler &handler, const std::string &root) { InitializeParam param; param.rootUri = DocumentUri::FromPath(root); - ReplyOnce reply; + ReplyOnce reply{handler}; Initialize(&handler, param, reply); } diff --git a/src/messages/textDocument_code.cc b/src/messages/textDocument_code.cc index 4d288f03..b3aa83df 100644 --- a/src/messages/textDocument_code.cc +++ b/src/messages/textDocument_code.cc @@ -35,11 +35,9 @@ REFLECT_STRUCT(CodeAction, title, kind, edit); } void MessageHandler::textDocument_codeAction(CodeActionParam ¶m, ReplyOnce &reply) { - WorkingFile *wf = wfiles->GetFile(param.textDocument.uri.GetPath()); - if (!wf) { - reply.NotReady(true); + WorkingFile *wf = FindOrFail(param.textDocument.uri.GetPath(), reply).second; + if (!wf) return; - } std::vector result; std::vector diagnostics; wfiles->WithLock([&]() { diagnostics = wf->diagnostics; }); @@ -96,16 +94,13 @@ struct CommonCodeLensParams { void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!wf) return; - } std::vector result; - auto Add = [&](const char *singular, Cmd_xref show, Range range, int num, - bool force_display = false) { + auto Add = [&, wf = wf](const char *singular, Cmd_xref show, Range range, + int num, bool force_display = false) { if (!num && !force_display) return; std::optional ls_range = GetLsRange(wf, range); diff --git a/src/messages/textDocument_completion.cc b/src/messages/textDocument_completion.cc index a29d895a..f401931e 100644 --- a/src/messages/textDocument_completion.cc +++ b/src/messages/textDocument_completion.cc @@ -459,7 +459,7 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m, std::string path = param.textDocument.uri.GetPath(); WorkingFile *wf = wfiles->GetFile(path); if (!wf) { - reply.NotReady(true); + reply.NotOpened(path); return; } diff --git a/src/messages/textDocument_definition.cc b/src/messages/textDocument_definition.cc index 46f04cf9..f857e496 100644 --- a/src/messages/textDocument_definition.cc +++ b/src/messages/textDocument_definition.cc @@ -48,12 +48,9 @@ std::vector GetNonDefDeclarationTargets(DB *db, SymbolRef sym) { void MessageHandler::textDocument_declaration(TextDocumentPositionParam ¶m, ReplyOnce &reply) { int file_id; - QueryFile *file = FindFile(param.textDocument.uri.GetPath(), &file_id); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id); + if (!wf) return; - } std::vector result; Position &ls_pos = param.position; @@ -69,12 +66,9 @@ void MessageHandler::textDocument_declaration(TextDocumentPositionParam ¶m, void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m, ReplyOnce &reply) { int file_id; - QueryFile *file = FindFile(param.textDocument.uri.GetPath(), &file_id); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id); + if (!wf) return; - } std::vector result; Maybe on_def; @@ -190,12 +184,9 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m, void MessageHandler::textDocument_typeDefinition( TextDocumentPositionParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!file) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!file) return; - } std::vector result; auto Add = [&](const QueryType &type) { diff --git a/src/messages/textDocument_document.cc b/src/messages/textDocument_document.cc index 32271c2f..ef8f57d0 100644 --- a/src/messages/textDocument_document.cc +++ b/src/messages/textDocument_document.cc @@ -44,12 +44,9 @@ REFLECT_STRUCT(DocumentHighlight, range, kind, role); void MessageHandler::textDocument_documentHighlight( TextDocumentPositionParam ¶m, ReplyOnce &reply) { int file_id; - QueryFile *file = FindFile(param.textDocument.uri.GetPath(), &file_id); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id); + if (!wf) return; - } std::vector result; std::vector syms = @@ -90,10 +87,8 @@ REFLECT_STRUCT(DocumentLink, range, target); void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); if (!wf) { - reply.NotReady(file); return; } @@ -165,10 +160,8 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader, Reflect(reader, param); int file_id; - QueryFile *file = FindFile(param.textDocument.uri.GetPath(), &file_id); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id); if (!wf) { - reply.NotReady(file); return; } diff --git a/src/messages/textDocument_foldingRange.cc b/src/messages/textDocument_foldingRange.cc index 67fb0c0a..a3f1981c 100644 --- a/src/messages/textDocument_foldingRange.cc +++ b/src/messages/textDocument_foldingRange.cc @@ -31,12 +31,9 @@ REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine, endCharacter, void MessageHandler::textDocument_foldingRange(TextDocumentParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!wf) return; - } std::vector result; std::optional ls_range; diff --git a/src/messages/textDocument_formatting.cc b/src/messages/textDocument_formatting.cc index 37bd7c46..ba46313d 100644 --- a/src/messages/textDocument_formatting.cc +++ b/src/messages/textDocument_formatting.cc @@ -80,21 +80,16 @@ void Format(ReplyOnce &reply, WorkingFile *wfile, tooling::Range range) { void MessageHandler::textDocument_formatting(DocumentFormattingParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!wf) return; - } Format(reply, wf, {0, (unsigned)wf->buffer_content.size()}); } void MessageHandler::textDocument_onTypeFormatting( DocumentOnTypeFormattingParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); if (!wf) { - reply.NotReady(file); return; } std::string_view code = wf->buffer_content; @@ -107,10 +102,8 @@ void MessageHandler::textDocument_onTypeFormatting( void MessageHandler::textDocument_rangeFormatting( DocumentRangeFormattingParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); if (!wf) { - reply.NotReady(file); return; } std::string_view code = wf->buffer_content; diff --git a/src/messages/textDocument_hover.cc b/src/messages/textDocument_hover.cc index 7a3e0341..848dc3a5 100644 --- a/src/messages/textDocument_hover.cc +++ b/src/messages/textDocument_hover.cc @@ -93,12 +93,9 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) { void MessageHandler::textDocument_hover(TextDocumentPositionParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!wf) return; - } Hover result; for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) { diff --git a/src/messages/textDocument_references.cc b/src/messages/textDocument_references.cc index 8c784553..e9858dfc 100644 --- a/src/messages/textDocument_references.cc +++ b/src/messages/textDocument_references.cc @@ -45,12 +45,9 @@ void MessageHandler::textDocument_references(JsonReader &reader, ReplyOnce &reply) { ReferenceParam param; Reflect(reader, param); - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!wf) return; - } for (auto &folder : param.folders) EnsureEndsInSlash(folder); diff --git a/src/messages/textDocument_rename.cc b/src/messages/textDocument_rename.cc index 05028263..7c478b12 100644 --- a/src/messages/textDocument_rename.cc +++ b/src/messages/textDocument_rename.cc @@ -56,10 +56,8 @@ WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym, } // namespace void MessageHandler::textDocument_rename(RenameParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); if (!wf) { - reply.NotReady(file); return; } diff --git a/src/messages/textDocument_signatureHelp.cc b/src/messages/textDocument_signatureHelp.cc index 3806ffe8..420ec164 100644 --- a/src/messages/textDocument_signatureHelp.cc +++ b/src/messages/textDocument_signatureHelp.cc @@ -152,12 +152,11 @@ public: void MessageHandler::textDocument_signatureHelp( TextDocumentPositionParam ¶m, ReplyOnce &reply) { static CompleteConsumerCache cache; - - std::string path = param.textDocument.uri.GetPath(); Position begin_pos = param.position; + std::string path = param.textDocument.uri.GetPath(); WorkingFile *wf = wfiles->GetFile(path); if (!wf) { - reply.NotReady(true); + reply.NotOpened(path); return; } { diff --git a/src/pipeline.cc b/src/pipeline.cc index 20f00df8..b83db890 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -32,7 +32,6 @@ limitations under the License. #include #include #include -using namespace llvm; #include #include @@ -41,6 +40,8 @@ using namespace llvm; #ifndef _WIN32 #include #endif +using namespace llvm; +namespace chrono = std::chrono; namespace ccls { namespace { @@ -527,8 +528,11 @@ void LaunchStdin() { if (method.empty()) continue; bool should_exit = method == "exit"; + // g_config is not available before "initialize". Use 0 in that case. on_request->PushBack( - {id, std::move(method), std::move(message), std::move(document)}); + {id, std::move(method), std::move(message), std::move(document), + chrono::steady_clock::now() + + chrono::milliseconds(g_config ? g_config->request.timeout : 0)}); if (should_exit) break; @@ -590,11 +594,34 @@ void MainLoop() { handler.include_complete = &include_complete; bool has_indexed = false; + std::deque backlog; + StringMap> path2backlog; while (true) { + if (backlog.size()) { + auto now = chrono::steady_clock::now(); + handler.overdue = true; + while (backlog.size()) { + if (backlog[0].backlog_path.size()) { + if (now < backlog[0].deadline) + break; + handler.Run(backlog[0]); + path2backlog[backlog[0].backlog_path].pop_front(); + } + backlog.pop_front(); + } + handler.overdue = false; + } + std::vector messages = on_request->DequeueAll(); bool did_work = messages.size(); for (InMessage &message : messages) - handler.Run(message); + try { + handler.Run(message); + } catch (NotIndexed &ex) { + backlog.push_back(std::move(message)); + backlog.back().backlog_path = ex.path; + path2backlog[ex.path].push_back(&backlog.back()); + } bool indexed = false; for (int i = 20; i--;) { @@ -604,6 +631,16 @@ void MainLoop() { did_work = true; indexed = true; Main_OnIndexed(&db, &wfiles, &*update); + if (update->files_def_update) { + auto it = path2backlog.find(update->files_def_update->first.path); + if (it != path2backlog.end()) { + for (auto &message : it->second) { + handler.Run(*message); + message->backlog_path.clear(); + } + path2backlog.erase(it); + } + } } if (did_work) { @@ -615,7 +652,10 @@ void MainLoop() { FreeUnusedMemory(); has_indexed = false; } - main_waiter->Wait(quit, on_indexed, on_request); + if (backlog.empty()) + main_waiter->Wait(quit, on_indexed, on_request); + else + main_waiter->WaitUntil(backlog[0].deadline, on_indexed, on_request); } } diff --git a/src/threaded_queue.hh b/src/threaded_queue.hh index a2b5f48a..103027aa 100644 --- a/src/threaded_queue.hh +++ b/src/threaded_queue.hh @@ -18,6 +18,7 @@ limitations under the License. #include "utils.hh" #include +#include #include #include #include @@ -76,6 +77,14 @@ struct MultiQueueWaiter { } return true; } + + template + void WaitUntil(std::chrono::steady_clock::time_point t, + BaseThreadQueue... queues) { + MultiQueueLock l(queues...); + if (!HasState({queues...})) + cv.wait_until(l, t); + } }; // A threadsafe-queue. http://stackoverflow.com/a/16075550 From d275ed570d7ea26dd8d762b8d82bc4e295971154 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Fri, 28 Dec 2018 12:30:15 -0800 Subject: [PATCH 04/10] textDocument/didOpen: index related files when a header is opened Fix #180 index.initialBlacklist: ["."] can inhibit initial indexing (useful for larger code bases). Opened files are still indexed, though. This heuristic allows related files (a/foo.c a/b/foo.cc) to be indexed when a header (foo.h) is opened. --- src/messages/textDocument_did.cc | 6 ++++-- src/project.cc | 18 ++++++++++++++++++ src/project.hh | 1 + 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/messages/textDocument_did.cc b/src/messages/textDocument_did.cc index b00b3c7d..d664c537 100644 --- a/src/messages/textDocument_did.cc +++ b/src/messages/textDocument_did.cc @@ -53,10 +53,12 @@ void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam ¶m) { // Submit new index request if it is not a header file or there is no // pending index request. - std::pair lang = lookupExtension(path); - if ((lang.first != LanguageId::Unknown && !lang.second) || + auto [lang, header] = lookupExtension(path); + if ((lang != LanguageId::Unknown && !header) || !pipeline::pending_index_requests) pipeline::Index(path, {}, IndexMode::Normal, false); + if (header) + project->IndexRelated(path); manager->OnView(path); } diff --git a/src/project.cc b/src/project.cc index e15d8294..d7fa1ed9 100644 --- a/src/project.cc +++ b/src/project.cc @@ -547,4 +547,22 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) { // trigger refreshing semantic highlight for all working files. pipeline::Index("", {}, IndexMode::NonInteractive, false); } + +void Project::IndexRelated(const std::string &path) { + auto &gi = g_config->index; + GroupMatch match(gi.whitelist, gi.blacklist); + std::string stem = sys::path::stem(path); + std::lock_guard lock(mtx); + for (auto &[root, folder] : root2folder) + if (StringRef(path).startswith(root)) { + for (const Project::Entry &entry : folder.entries) { + std::string reason; + if (sys::path::stem(entry.filename) == stem && entry.filename != path && + match.Matches(entry.filename, &reason)) + pipeline::Index(entry.filename, entry.args, IndexMode::NonInteractive, + true); + } + break; + } +} } // namespace ccls diff --git a/src/project.hh b/src/project.hh index 89df1b25..4a70c38a 100644 --- a/src/project.hh +++ b/src/project.hh @@ -77,5 +77,6 @@ struct Project { const std::string &path); void Index(WorkingFiles *wfiles, RequestId id); + void IndexRelated(const std::string &path); }; } // namespace ccls From e6e6f2adce128ff888106b679b86701880aaf29a Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Fri, 4 Jan 2019 10:54:20 +0800 Subject: [PATCH 05/10] Update wiki link --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5c3e251d..53ff9aac 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,10 @@ The comparison with cquery as noted on 2018-07-15: cquery has system include path detection (through running the compiler driver) while ccls uses clangDriver. -# >>> [Getting started](../../wiki/Getting-started) (CLICK HERE) <<< +# >>> [Getting started](../../wiki/Home) (CLICK HERE) <<< * [Build](../../wiki/Build) * [Client feature table](../../wiki/Client-feature-table) * [FAQ](../../wiki/FAQ) -ccls can index itself (~180MiB RSS when ide, noted on 2018-09-01), FreeBSD, glibc, Linux, LLVM (~1800MiB RSS), musl (~60MiB RSS), ... with decent memory footprint. See [wiki/compile_commands.json](../../wiki/compile_commands.json) for examples. +ccls can index itself (~180MiB RSS when idle, noted on 2018-09-01), FreeBSD, glibc, Linux, LLVM (~1800MiB RSS), musl (~60MiB RSS), ... with decent memory footprint. See [wiki/compile_commands.json](../../wiki/compile_commands.json) for examples. From e9f959689f986ebce486b93c7aab49151d510e93 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sun, 13 Jan 2019 18:33:18 +0800 Subject: [PATCH 06/10] completion: if preamble size changes, rebuild it Fix #190 If a new header is added, the preamble size changes. Language clients may cache completion results, thus we rebuild preamble to avoid inaccurate results. --- src/sema_manager.cc | 18 +++++++++++++----- src/sema_manager.hh | 9 +++++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/sema_manager.cc b/src/sema_manager.cc index 91fae5e3..c352f0f4 100644 --- a/src/sema_manager.cc +++ b/src/sema_manager.cc @@ -423,7 +423,9 @@ void *PreambleMain(void *manager_) { BuildCompilerInvocation(task.path, session->file.args, FS)) BuildPreamble(*session, *CI, FS, task, std::move(stat_cache)); - if (task.from_diag) { + if (task.comp_task) { + manager->comp_tasks.PushBack(std::move(task.comp_task)); + } else if (task.from_diag) { manager->ScheduleDiag(task.path, 0); } else { int debounce = @@ -474,12 +476,18 @@ void *CompletionMain(void *manager_) { DiagnosticConsumer DC; std::string content = manager->wfiles->GetContent(task->path); auto Buf = llvm::MemoryBuffer::getMemBuffer(content); + PreambleBounds Bounds = + ComputePreambleBounds(*CI->getLangOpts(), Buf.get(), 0); bool in_preamble = GetOffsetForPosition({task->position.line, task->position.character}, - content) < - ComputePreambleBounds(*CI->getLangOpts(), Buf.get(), 0).Size; - if (in_preamble) + content) < (int)Bounds.Size; + if (in_preamble) { preamble.reset(); + } else if (preamble && Bounds.Size != preamble->Preamble.getBounds().Size) { + manager->preamble_tasks.PushBack({task->path, std::move(task), false}, + true); + continue; + } auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC, preamble.get(), task->path, Buf); if (!Clang) @@ -556,7 +564,7 @@ void *DiagnosticMain(void *manager_) { } } if (rebuild) { - manager->preamble_tasks.PushBack({task.path, true}, true); + manager->preamble_tasks.PushBack({task.path, nullptr, true}, true); continue; } } diff --git a/src/sema_manager.hh b/src/sema_manager.hh index 5d573349..0e1584a1 100644 --- a/src/sema_manager.hh +++ b/src/sema_manager.hh @@ -114,10 +114,6 @@ struct SemaManager { std::function; using OnDropped = std::function; - struct PreambleTask { - std::string path; - bool from_diag = false; - }; struct CompTask { CompTask(const RequestId &id, const std::string &path, const Position &position, @@ -138,6 +134,11 @@ struct SemaManager { int64_t wait_until; int64_t debounce; }; + struct PreambleTask { + std::string path; + std::unique_ptr comp_task; + bool from_diag = false; + }; SemaManager(Project *project, WorkingFiles *wfiles, OnDiagnostic on_diagnostic, OnDropped on_dropped); From 6df4bf52736098a9d7e0b18bed7f5388aabd3774 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Mon, 21 Jan 2019 10:20:07 +0800 Subject: [PATCH 07/10] Add -log-file=stderr and make it default Change -log-file-append to a boolean flag --- src/log.cc | 1 + src/main.cc | 19 +++++++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/log.cc b/src/log.cc index a10a4a7d..3e2b5592 100644 --- a/src/log.cc +++ b/src/log.cc @@ -72,6 +72,7 @@ Message::~Message() { std::lock_guard lock(mtx); stream_ << '\n'; fputs(stream_.str().c_str(), file); + fflush(file); if (verbosity_ == Verbosity_FATAL) abort(); } diff --git a/src/main.cc b/src/main.cc index 95cd0363..39c6457f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -58,10 +58,10 @@ opt opt_index("index", value_desc("root"), cat(C)); list opt_init("init", desc("extra initialization options in JSON"), cat(C)); -opt opt_log_file("log-file", desc("log"), value_desc("filename"), +opt opt_log_file("log-file", desc("stderr or log file"), + value_desc("file"), init("stderr"), cat(C)); +opt opt_log_file_append("log-file-append", desc("append to log file"), cat(C)); -opt opt_log_file_append("log-file-append", desc("log"), - value_desc("filename"), cat(C)); void CloseLog() { fclose(ccls::log::file); } @@ -95,14 +95,13 @@ int main(int argc, char **argv) { bool language_server = true; - if (opt_log_file.size() || opt_log_file_append.size()) { - ccls::log::file = opt_log_file.size() - ? fopen(opt_log_file.c_str(), "wb") - : fopen(opt_log_file_append.c_str(), "ab"); + if (opt_log_file.size()) { + ccls::log::file = + opt_log_file == "stderr" + ? stderr + : fopen(opt_log_file.c_str(), opt_log_file_append ? "ab" : "wb"); if (!ccls::log::file) { - fprintf( - stderr, "failed to open %s\n", - (opt_log_file.size() ? opt_log_file : opt_log_file_append).c_str()); + fprintf(stderr, "failed to open %s\n", opt_log_file.c_str()); return 2; } setbuf(ccls::log::file, NULL); From b0286a3ff6eef6fc99cf17552d1ce80f79c0b5f2 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Mon, 21 Jan 2019 10:44:44 +0800 Subject: [PATCH 08/10] Drop support for clang 6 --- CMakeLists.txt | 2 +- cmake/FindClang.cmake | 1 - src/clang_tu.cc | 4 --- src/indexer.cc | 44 +++---------------------- src/messages/textDocument_completion.cc | 4 --- src/sema_manager.cc | 16 +++------ 6 files changed, 9 insertions(+), 62 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bf39ebfd..f84d67ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,7 +112,7 @@ endif() ### Libraries # See cmake/FindClang.cmake -find_package(Clang 6.0.0) +find_package(Clang 7.0.0) target_link_libraries(ccls PRIVATE Clang::Clang) # Enable threading support diff --git a/cmake/FindClang.cmake b/cmake/FindClang.cmake index 19402900..078395dd 100644 --- a/cmake/FindClang.cmake +++ b/cmake/FindClang.cmake @@ -72,7 +72,6 @@ _Clang_find_library(Clang_LIBRARY clangIndex) _Clang_find_add_library(clangFormat) _Clang_find_add_library(clangTooling) -# TODO Remove after dropping clang 6 _Clang_find_library(clangToolingInclusions_LIBRARY clangToolingInclusions) if (clangToolingInclusions_LIBRARY) list(APPEND _Clang_LIBRARIES ${clangToolingInclusions_LIBRARY}) diff --git a/src/clang_tu.cc b/src/clang_tu.cc index a2d982b4..70078c61 100644 --- a/src/clang_tu.cc +++ b/src/clang_tu.cc @@ -156,7 +156,6 @@ const char *ClangBuiltinTypeName(int kind) { return "double"; case BuiltinType::LongDouble: return "long double"; -#if LLVM_VERSION_MAJOR >= 7 case BuiltinType::ShortAccum: return "short _Accum"; case BuiltinType::Accum: @@ -205,7 +204,6 @@ const char *ClangBuiltinTypeName(int kind) { return "_Sat unsigned _Fract"; case BuiltinType::BuiltinType::SatULongFract: return "_Sat unsigned long _Fract"; -#endif case BuiltinType::Float16: return "_Float16"; case BuiltinType::Float128: @@ -213,10 +211,8 @@ const char *ClangBuiltinTypeName(int kind) { case BuiltinType::WChar_S: case BuiltinType::WChar_U: return "wchar_t"; -#if LLVM_VERSION_MAJOR >= 7 case BuiltinType::Char8: return "char8_t"; -#endif case BuiltinType::Char16: return "char16_t"; case BuiltinType::Char32: diff --git a/src/indexer.cc b/src/indexer.cc index 43969f23..716a092f 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -593,11 +593,7 @@ public: if (init) { SourceManager &SM = Ctx->getSourceManager(); const LangOptions &Lang = Ctx->getLangOpts(); - SourceRange R = SM.getExpansionRange(init->getSourceRange()) -#if LLVM_VERSION_MAJOR >= 7 - .getAsRange() -#endif - ; + SourceRange R = SM.getExpansionRange(init->getSourceRange()).getAsRange(); SourceLocation L = D->getLocation(); if (L.isMacroID() || !SM.isBeforeInTranslationUnit(L, R.getBegin())) return; @@ -688,41 +684,15 @@ public: } bool handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles, ArrayRef Relations, -#if LLVM_VERSION_MAJOR >= 7 - SourceLocation Loc, -#else - FileID LocFID, unsigned LocOffset, -#endif - ASTNodeInfo ASTNode) override { + SourceLocation Loc, ASTNodeInfo ASTNode) override { SourceManager &SM = Ctx->getSourceManager(); const LangOptions &Lang = Ctx->getLangOpts(); -#if LLVM_VERSION_MAJOR < 7 - SourceLocation Loc; - { - const SrcMgr::SLocEntry &Entry = SM.getSLocEntry(LocFID); - unsigned off = Entry.getOffset() + LocOffset; - if (!Entry.isFile()) - off |= 1u << 31; - Loc = SourceLocation::getFromRawEncoding(off); - } -#else FileID LocFID; -#endif SourceLocation Spell = SM.getSpellingLoc(Loc); const FileEntry *FE; Range loc; -#if LLVM_VERSION_MAJOR < 7 - CharSourceRange R; - if (SM.isMacroArgExpansion(Loc)) - R = CharSourceRange::getTokenRange(Spell); - else { - auto P = SM.getExpansionRange(Loc); - R = CharSourceRange::getTokenRange(P.first, P.second); - } -#else auto R = SM.isMacroArgExpansion(Loc) ? CharSourceRange::getTokenRange(Spell) : SM.getExpansionRange(Loc); -#endif loc = FromCharSourceRange(SM, Lang, R); LocFID = SM.getFileID(R.getBegin()); FE = SM.getFileEntryForID(LocFID); @@ -1085,12 +1055,8 @@ public: StringRef Included, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const Module *Imported -#if LLVM_VERSION_MAJOR >= 7 - , - SrcMgr::CharacteristicKind FileType -#endif - ) override { + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override { if (!File) return; llvm::sys::fs::UniqueID UniqueID; @@ -1283,9 +1249,7 @@ Index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs, IndexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::All; IndexOpts.IndexFunctionLocals = true; -#if LLVM_VERSION_MAJOR >= 7 IndexOpts.IndexImplicitInstantiation = true; -#endif std::unique_ptr Action = createIndexingAction( DataConsumer, IndexOpts, std::make_unique(param)); diff --git a/src/messages/textDocument_completion.cc b/src/messages/textDocument_completion.cc index f401931e..a1f0884e 100644 --- a/src/messages/textDocument_completion.cc +++ b/src/messages/textDocument_completion.cc @@ -436,7 +436,6 @@ public: ls_items[j].label = ls_items[j].filterText; } } -#if LLVM_VERSION_MAJOR >= 7 for (const FixItHint &FixIt : R.FixIts) { auto &AST = S.getASTContext(); TextEdit ls_edit = @@ -444,7 +443,6 @@ public: for (size_t j = first_idx; j < ls_items.size(); j++) ls_items[j].additionalTextEdits.push_back(ls_edit); } -#endif } } @@ -474,9 +472,7 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m, clang::CodeCompleteOptions CCOpts; CCOpts.IncludeBriefComments = true; CCOpts.IncludeCodePatterns = StringRef(buffer_line).ltrim().startswith("#"); -#if LLVM_VERSION_MAJOR >= 7 CCOpts.IncludeFixIts = true; -#endif CCOpts.IncludeMacros = true; if (param.context.triggerKind == CompletionTriggerKind::TriggerCharacter && diff --git a/src/sema_manager.cc b/src/sema_manager.cc index c352f0f4..a80bd0d7 100644 --- a/src/sema_manager.cc +++ b/src/sema_manager.cc @@ -190,11 +190,8 @@ public: StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const clang::Module *Imported -#if LLVM_VERSION_MAJOR >= 7 - , SrcMgr::CharacteristicKind FileKind -#endif - ) override { + const clang::Module *Imported, + SrcMgr::CharacteristicKind FileKind) override { (void)SM; if (File && Seen.insert(File).second) out.emplace_back(PathFromFileEntry(*File), File->getModificationTime()); @@ -304,15 +301,10 @@ std::unique_ptr BuildCompilerInstance( IntrusiveRefCntPtr FS, DiagnosticConsumer &DC, const PreambleData *preamble, const std::string &main, std::unique_ptr &Buf) { - if (preamble) { -#if LLVM_VERSION_MAJOR >= 7 + if (preamble) preamble->Preamble.OverridePreamble(*CI, FS, Buf.get()); -#else - preamble->Preamble.AddImplicitPreamble(*CI, FS, Buf.get()); -#endif - } else { + else CI->getPreprocessorOpts().addRemappedFile(main, Buf.get()); - } auto Clang = std::make_unique(session.PCH); Clang->setInvocation(std::move(CI)); From 88572f277c5b9d8caab7117e6fa37ce044e6803a Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Mon, 21 Jan 2019 12:15:28 +0800 Subject: [PATCH 09/10] Implement initialization option compilationDatabaseCommand on Windows --- src/platform.hh | 3 --- src/platform_posix.cc | 43 ------------------------------- src/platform_win.cc | 5 ---- src/project.cc | 60 ++++++++++++++++++++++++++++++------------- 4 files changed, 42 insertions(+), 69 deletions(-) diff --git a/src/platform.hh b/src/platform.hh index 12446263..79aca558 100644 --- a/src/platform.hh +++ b/src/platform.hh @@ -28,8 +28,5 @@ void FreeUnusedMemory(); // Stop self and wait for SIGCONT. void TraceMe(); -std::string GetExternalCommandOutput(const std::vector &command, - std::string_view input); - void SpawnThread(void *(*fn)(void *), void *arg); } // namespace ccls diff --git a/src/platform_posix.cc b/src/platform_posix.cc index 35d8d271..c172446b 100644 --- a/src/platform_posix.cc +++ b/src/platform_posix.cc @@ -73,49 +73,6 @@ void TraceMe() { raise(traceme[0] == 's' ? SIGSTOP : SIGTSTP); } -std::string GetExternalCommandOutput(const std::vector &command, - std::string_view input) { - int pin[2], pout[2]; - if (pipe(pin) < 0) { - perror("pipe(stdin)"); - return ""; - } - if (pipe(pout) < 0) { - perror("pipe(stdout)"); - close(pin[0]); - close(pin[1]); - return ""; - } - pid_t child = fork(); - if (child == 0) { - dup2(pout[0], 0); - dup2(pin[1], 1); - close(pin[0]); - close(pin[1]); - close(pout[0]); - close(pout[1]); - auto argv = new char *[command.size() + 1]; - for (size_t i = 0; i < command.size(); i++) - argv[i] = const_cast(command[i].c_str()); - argv[command.size()] = nullptr; - execvp(argv[0], argv); - _Exit(127); - } - close(pin[1]); - close(pout[0]); - // O_NONBLOCK is disabled, write(2) blocks until all bytes are written. - (void)write(pout[1], input.data(), input.size()); - close(pout[1]); - std::string ret; - char buf[4096]; - ssize_t n; - while ((n = read(pin[0], buf, sizeof buf)) > 0) - ret.append(buf, n); - close(pin[0]); - waitpid(child, NULL, 0); - return ret; -} - void SpawnThread(void *(*fn)(void *), void *arg) { pthread_t thd; pthread_attr_t attr; diff --git a/src/platform_win.cc b/src/platform_win.cc index a2ca5185..f933b257 100644 --- a/src/platform_win.cc +++ b/src/platform_win.cc @@ -57,11 +57,6 @@ void FreeUnusedMemory() {} // TODO Wait for debugger to attach void TraceMe() {} -std::string GetExternalCommandOutput(const std::vector &command, - std::string_view input) { - return ""; -} - void SpawnThread(void *(*fn)(void *), void *arg) { std::thread(fn, arg).detach(); } diff --git a/src/project.cc b/src/project.cc index d7fa1ed9..c2bcf718 100644 --- a/src/project.cc +++ b/src/project.cc @@ -31,13 +31,17 @@ limitations under the License. #include #include #include +#include #include -#if defined(__unix__) || defined(__APPLE__) -#include +#ifdef _WIN32 +# include +#else +# include #endif +#include #include #include #include @@ -308,7 +312,8 @@ int ComputeGuessScore(std::string_view a, std::string_view b) { } // namespace void Project::LoadDirectory(const std::string &root, Project::Folder &folder) { - SmallString<256> Path, CDBDir; + SmallString<256> CDBDir, Path, StdinPath; + std::string err_msg; folder.entries.clear(); if (g_config->compilationDatabaseCommand.empty()) { CDBDir = root; @@ -319,33 +324,49 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) { // If `compilationDatabaseCommand` is specified, execute it to get the // compdb. #ifdef _WIN32 - // TODO + char tmpdir[L_tmpnam]; + tmpnam_s(tmpdir, L_tmpnam); + CDBDir = tmpdir; + if (sys::fs::create_directory(tmpdir, false)) + return; #else char tmpdir[] = "/tmp/ccls-compdb-XXXXXX"; if (!mkdtemp(tmpdir)) return; CDBDir = tmpdir; - sys::path::append(Path, CDBDir, "compile_commands.json"); - rapidjson::StringBuffer input; - rapidjson::Writer writer(input); - JsonWriter json_writer(&writer); - Reflect(json_writer, *g_config); - std::string contents = GetExternalCommandOutput( - std::vector{g_config->compilationDatabaseCommand, root}, - input.GetString()); - FILE *fout = fopen(Path.c_str(), "wb"); - fwrite(contents.c_str(), contents.size(), 1, fout); - fclose(fout); #endif + sys::path::append(Path, CDBDir, "compile_commands.json"); + sys::path::append(StdinPath, CDBDir, "stdin"); + { + rapidjson::StringBuffer sb; + rapidjson::Writer writer(sb); + JsonWriter json_writer(&writer); + Reflect(json_writer, *g_config); + std::string input = sb.GetString(); + FILE *fout = fopen(StdinPath.c_str(), "wb"); + fwrite(input.c_str(), input.size(), 1, fout); + fclose(fout); + } + std::array, 3> Redir{StringRef(StdinPath), + StringRef(Path), StringRef()}; + std::vector args{g_config->compilationDatabaseCommand, root}; + if (sys::ExecuteAndWait(args[0], args, llvm::None, Redir, 0, 0, &err_msg) < + 0) { + LOG_S(ERROR) << "failed to execute " << args[0].str() << " " + << args[1].str() << ": " << err_msg; + return; + } } - std::string err_msg; std::unique_ptr CDB = tooling::CompilationDatabase::loadFromDirectory(CDBDir, err_msg); if (!g_config->compilationDatabaseCommand.empty()) { #ifdef _WIN32 - // TODO + DeleteFileA(StdinPath.c_str()); + DeleteFileA(Path.c_str()); + RemoveDirectoryA(CDBDir.c_str()); #else + unlink(StdinPath.c_str()); unlink(Path.c_str()); rmdir(CDBDir.c_str()); #endif @@ -354,7 +375,10 @@ void Project::LoadDirectory(const std::string &root, Project::Folder &folder) { ProjectProcessor proc(folder); StringSet<> Seen; std::vector result; - if (CDB) { + if (!CDB) { + if (g_config->compilationDatabaseCommand.size() || sys::fs::exists(Path)) + LOG_S(ERROR) << "failed to load " << Path.c_str(); + } else { LOG_S(INFO) << "loaded " << Path.c_str(); for (tooling::CompileCommand &Cmd : CDB->getAllCompileCommands()) { static bool once; From da360d2466b039e0075e8dbf6331137612c28bf6 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Tue, 22 Jan 2019 22:44:13 +0800 Subject: [PATCH 10/10] cmake: delete SYSTEM_CLANG and auto-download mechanism --- .appveyor.yml | 2 +- CMakeLists.txt | 67 +-------- .../LLVM-7.0.0-win64.exe.SHA256 | 1 - ....0.0-amd64-unknown-freebsd11.tar.xz.SHA256 | 1 - ...vm-7.0.0-x86_64-apple-darwin.tar.xz.SHA256 | 1 - ...86_64-linux-gnu-ubuntu-14.04.tar.xz.SHA256 | 1 - ...86_64-linux-gnu-ubuntu-16.04.tar.xz.SHA256 | 1 - cmake/DownloadAndExtract7zip.cmake | 52 ------- cmake/DownloadAndExtractClang.cmake | 129 ------------------ 9 files changed, 2 insertions(+), 253 deletions(-) delete mode 100644 clang_archive_hashes/LLVM-7.0.0-win64.exe.SHA256 delete mode 100644 clang_archive_hashes/clang+llvm-7.0.0-amd64-unknown-freebsd11.tar.xz.SHA256 delete mode 100644 clang_archive_hashes/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz.SHA256 delete mode 100644 clang_archive_hashes/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-14.04.tar.xz.SHA256 delete mode 100644 clang_archive_hashes/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz.SHA256 delete mode 100644 cmake/DownloadAndExtract7zip.cmake delete mode 100644 cmake/DownloadAndExtractClang.cmake diff --git a/.appveyor.yml b/.appveyor.yml index 0fc36d3f..42a7a4c0 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -15,7 +15,7 @@ install: - 7z x llvm.tar - git submodule update --init --recursive build_script: - - cmake -G"Visual Studio 15 2017 Win64" -H. -Bbuild -DCMAKE_BUILD_TYPE=Release -DSYSTEM_CLANG=ON -DCLANG_ROOT=C:\projects\ccls\llvm+clang-7.0.0-win64-msvc-release + - cmake -G"Visual Studio 15 2017 Win64" -H. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=C:\projects\ccls\llvm+clang-7.0.0-win64-msvc-release - cmake --build build --target ccls --config Release artifacts: diff --git a/CMakeLists.txt b/CMakeLists.txt index f84d67ab..993376d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,13 +5,7 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) include(DefaultCMakeBuildType) # Required Clang version -set(CLANG_DOWNLOAD_LOCATION ${CMAKE_BINARY_DIR} - CACHE STRING "Downloaded Clang location") -option(SYSTEM_CLANG "Use system installation of Clang instead of \ - downloading Clang" OFF) -option(ASAN "Compile with address sanitizers" OFF) option(LLVM_ENABLE_RTTI "-fno-rtti if OFF. This should match LLVM libraries" OFF) -option(CLANG_USE_BUNDLED_LIBC++ "Let Clang use bundled libc++" OFF) option(USE_SHARED_LLVM "Link against libLLVM.so instead separate LLVM{Option,Support,...}" OFF) # Sources for the executable are specified at end of CMakeLists.txt @@ -30,10 +24,7 @@ add_executable(ccls "") # Enable C++17 (Required) set_property(TARGET ccls PROPERTY CXX_STANDARD 17) set_property(TARGET ccls PROPERTY CXX_STANDARD_REQUIRED ON) -# Disable gnu extensions except for Cygwin which needs them to build properly -if(NOT CYGWIN) - set_property(TARGET ccls PROPERTY CXX_EXTENSIONS OFF) -endif() +set_property(TARGET ccls PROPERTY CXX_EXTENSIONS OFF) if(NOT LLVM_ENABLE_RTTI) # releases.llvm.org libraries are compiled with -fno-rtti @@ -76,37 +67,6 @@ else() target_compile_options(ccls PRIVATE $<$:-fno-limit-debug-info>) endif() - - if(ASAN) - target_compile_options(ccls PRIVATE -fsanitize=address,undefined) - # target_link_libraries also takes linker flags - target_link_libraries(ccls PRIVATE -fsanitize=address,undefined) - endif() -endif() - -### Download Clang if required - -if(NOT SYSTEM_CLANG) - message(STATUS "Using downloaded Clang") - - include(DownloadAndExtractClang) - download_and_extract_clang(${CLANG_DOWNLOAD_LOCATION}) - # Used by FindClang - set(CLANG_ROOT ${DOWNLOADED_CLANG_DIR}) - - if(${CMAKE_CXX_COMPILER_ID} STREQUAL Clang AND CLANG_USE_BUNDLED_LIBC++) - message(STATUS "Using bundled libc++") - target_compile_options(ccls PRIVATE -nostdinc++ -cxx-isystem ${CLANG_ROOT}/include/c++/v1) - if(${CMAKE_SYSTEM_NAME} STREQUAL Linux) - # Don't use -stdlib=libc++ because while ccls is linked with libc++, bundled clang+llvm require libstdc++ - target_link_libraries(ccls PRIVATE -L${CLANG_ROOT}/lib c++ c++abi) - - # FreeBSD defaults to -stdlib=libc++ and uses system libcxxrt.a - endif() - endif() - -else() - message(STATUS "Using system Clang") endif() ### Libraries @@ -143,31 +103,6 @@ target_include_directories(ccls SYSTEM PRIVATE install(TARGETS ccls RUNTIME DESTINATION bin) -if(NOT SYSTEM_CLANG AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL Windows) - - if(${CMAKE_SYSTEM_NAME} MATCHES Linux|FreeBSD) - set_property(TARGET ccls APPEND PROPERTY - INSTALL_RPATH $ORIGIN/../lib) - elseif(${CMAKE_SYSTEM_NAME} STREQUAL Darwin) - set_property(TARGET ccls APPEND PROPERTY - INSTALL_RPATH @loader_path/../lib) - endif() - - file(GLOB LIBCLANG_PLUS_SYMLINKS - ${DOWNLOADED_CLANG_DIR}/lib/libclang.[so,dylib]*) - install(FILES ${LIBCLANG_PLUS_SYMLINKS} DESTINATION lib) -endif() - -# Allow running from build Windows by copying libclang.dll to build directory -if(NOT SYSTEM_CLANG AND ${CMAKE_SYSTEM_NAME} STREQUAL Windows) - add_custom_command(TARGET ccls - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${DOWNLOADED_CLANG_DIR}/bin/libclang.dll - $ - COMMENT "Copying libclang.dll to build directory ...") -endif() - ### Tools # We use glob here since source files are already manually added with diff --git a/clang_archive_hashes/LLVM-7.0.0-win64.exe.SHA256 b/clang_archive_hashes/LLVM-7.0.0-win64.exe.SHA256 deleted file mode 100644 index 54bea9e6..00000000 --- a/clang_archive_hashes/LLVM-7.0.0-win64.exe.SHA256 +++ /dev/null @@ -1 +0,0 @@ -74b197a3959b0408adf0824be01db8dddfa2f9a967f4085af3fad900ed5fdbf6 \ No newline at end of file diff --git a/clang_archive_hashes/clang+llvm-7.0.0-amd64-unknown-freebsd11.tar.xz.SHA256 b/clang_archive_hashes/clang+llvm-7.0.0-amd64-unknown-freebsd11.tar.xz.SHA256 deleted file mode 100644 index b8a56bfa..00000000 --- a/clang_archive_hashes/clang+llvm-7.0.0-amd64-unknown-freebsd11.tar.xz.SHA256 +++ /dev/null @@ -1 +0,0 @@ -95ceb933ccf76e3ddaa536f41ab82c442bbac07cdea6f9fbf6e3b13cc1711255 \ No newline at end of file diff --git a/clang_archive_hashes/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz.SHA256 b/clang_archive_hashes/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz.SHA256 deleted file mode 100644 index 86f733c9..00000000 --- a/clang_archive_hashes/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz.SHA256 +++ /dev/null @@ -1 +0,0 @@ -b3ad93c3d69dfd528df9c5bb1a434367babb8f3baea47fbb99bf49f1b03c94ca \ No newline at end of file diff --git a/clang_archive_hashes/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-14.04.tar.xz.SHA256 b/clang_archive_hashes/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-14.04.tar.xz.SHA256 deleted file mode 100644 index bc830691..00000000 --- a/clang_archive_hashes/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-14.04.tar.xz.SHA256 +++ /dev/null @@ -1 +0,0 @@ -5c90e61b06d37270bc26edb305d7e498e2c7be22d99e0afd9f2274ef5458575a \ No newline at end of file diff --git a/clang_archive_hashes/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz.SHA256 b/clang_archive_hashes/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz.SHA256 deleted file mode 100644 index 1475a0a8..00000000 --- a/clang_archive_hashes/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz.SHA256 +++ /dev/null @@ -1 +0,0 @@ -69b85c833cd28ea04ce34002464f10a6ad9656dd2bba0f7133536a9927c660d2 \ No newline at end of file diff --git a/cmake/DownloadAndExtract7zip.cmake b/cmake/DownloadAndExtract7zip.cmake deleted file mode 100644 index 72bc879d..00000000 --- a/cmake/DownloadAndExtract7zip.cmake +++ /dev/null @@ -1,52 +0,0 @@ -# Downloads and extracts the 7-Zip MSI installer from https://www.7-zip.org/. -# -# Returns the extracted 7-Zip directory in DOWNLOADED_7ZIP_DIR -function(download_and_extract_7zip 7ZIP_DOWNLOAD_LOCATION) - -set(7ZIP_VERSION 1801) -set(7ZIP_EXT .msi) -set(7ZIP_NAME 7z${7ZIP_VERSION}-x64) -set(7ZIP_FULL_NAME ${7ZIP_NAME}${7ZIP_EXT}) - -set(7ZIP_FILE ${7ZIP_DOWNLOAD_LOCATION}/${7ZIP_FULL_NAME}) -set(7ZIP_EXTRACT_DIR ${7ZIP_DOWNLOAD_LOCATION}/${7ZIP_NAME}) -set(7ZIP_URL https://www.7-zip.org/a/${7ZIP_FULL_NAME}) - -# Exit if 7-Zip is already downloaded and extracted -find_program(7ZIP_EXECUTABLE 7z NO_DEFAULT_PATH - PATHS ${7ZIP_EXTRACT_DIR}/Files/7-Zip) -if(7ZIP_EXECUTABLE) - message(STATUS "7-Zip already downloaded") - return() -endif() - -message(STATUS "Downloading 7-Zip ${7ZIP_VERSION} (${7ZIP_URL}) ...") -file(DOWNLOAD ${7ZIP_URL} ${7ZIP_FILE}) - -find_program(MSIEXEC_EXECUTABLE msiexec) -if(NOT MSIEXEC_EXECUTABLE) - message(FATAL_ERROR "Unable to find msiexec (required to extract 7-Zip msi \ -installer). Install 7-Zip yourself and make sure it is available in the path") -endif() - -message(STATUS "Extracting downloaded 7-Zip ...") - -# msiexec requires Windows path separators (\) -file(TO_NATIVE_PATH ${7ZIP_FILE} 7ZIP_FILE) -file(TO_NATIVE_PATH ${7ZIP_EXTRACT_DIR} 7ZIP_EXTRACT_DIR) - -# msiexec with /a option allows extraction of msi installers without requiring -# admin privileges. We use this to extract the 7-Zip installer without -# requiring any actions from the user -execute_process(COMMAND ${MSIEXEC_EXECUTABLE} /a ${7ZIP_FILE} /qn - TARGETDIR=${7ZIP_EXTRACT_DIR} - WORKING_DIRECTORY ${7ZIP_DOWNLOAD_LOCATION} - OUTPUT_QUIET) - -# Convert back to CMake separators (/) before returning -file(TO_CMAKE_PATH ${7ZIP_EXTRACT_DIR} 7ZIP_EXTRACT_DIR) - -# Actual 7-Zip directory is nested inside the extract directory. -set(DOWNLOADED_7ZIP_DIR ${7ZIP_EXTRACT_DIR}/Files/7-Zip PARENT_SCOPE) - -endfunction() \ No newline at end of file diff --git a/cmake/DownloadAndExtractClang.cmake b/cmake/DownloadAndExtractClang.cmake deleted file mode 100644 index 9365b188..00000000 --- a/cmake/DownloadAndExtractClang.cmake +++ /dev/null @@ -1,129 +0,0 @@ -# Downloads and extracts the Clang archive for the current system from -# https://releases.llvm.org -# -# Returns the extracted Clang archive directory in DOWNLOADED_CLANG_DIR -# -# Downloads 7-Zip to extract Clang if it isn't available in the PATH -function(download_and_extract_clang CLANG_DOWNLOAD_LOCATION) - -set(CLANG_VERSION 7.0.0) -set(CLANG_ARCHIVE_EXT .tar.xz) - -if(${CMAKE_SYSTEM_NAME} STREQUAL Linux) - - # Default to Ubuntu 16.04 - set(CLANG_ARCHIVE_NAME - clang+llvm-${CLANG_VERSION}-x86_64-linux-gnu-ubuntu-16.04) - -elseif(${CMAKE_SYSTEM_NAME} STREQUAL Darwin) - - set(CLANG_ARCHIVE_NAME clang+llvm-${CLANG_VERSION}-x86_64-apple-darwin) - -elseif(${CMAKE_SYSTEM_NAME} STREQUAL Windows) - - set(CLANG_ARCHIVE_NAME LLVM-${CLANG_VERSION}-win64) - set(CLANG_ARCHIVE_EXT .exe) - -elseif(${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD) - - set(CLANG_ARCHIVE_NAME clang+llvm-${CLANG_VERSION}-amd64-unknown-freebsd11) - -endif() - -set(CLANG_ARCHIVE_FULL_NAME ${CLANG_ARCHIVE_NAME}${CLANG_ARCHIVE_EXT}) -set(CLANG_ARCHIVE_FILE ${CLANG_DOWNLOAD_LOCATION}/${CLANG_ARCHIVE_FULL_NAME}) -set(CLANG_ARCHIVE_EXTRACT_DIR ${CLANG_DOWNLOAD_LOCATION}/${CLANG_ARCHIVE_NAME}) -set(CLANG_ARCHIVE_URL - https://releases.llvm.org/${CLANG_VERSION}/${CLANG_ARCHIVE_FULL_NAME}) -set(CLANG_ARCHIVE_HASH_FILE - ${CMAKE_SOURCE_DIR}/clang_archive_hashes/${CLANG_ARCHIVE_FULL_NAME}.SHA256) - -# Exit if Clang is already downloaded and extracted -set(CLANG_ROOT ${CLANG_ARCHIVE_EXTRACT_DIR}) -find_package(Clang ${CLANG_VERSION} QUIET) -if(Clang_FOUND) - message(STATUS "Clang already downloaded") - set(DOWNLOADED_CLANG_DIR ${CLANG_ARCHIVE_EXTRACT_DIR} PARENT_SCOPE) - return() -endif() - -if(NOT CLANG_ARCHIVE_NAME) - message(FATAL_ERROR "No Clang archive url specified for current platform \ -(${CMAKE_SYSTEM_NAME}). Please file an issue to get it added.") -endif() - -if(NOT EXISTS ${CLANG_ARCHIVE_HASH_FILE}) - message(FATAL_ERROR "No SHA256 hash available for the current platform \ -(${CMAKE_SYSTEM_NAME}) + clang version (${CLANG_VERSION}) combination. Please \ -file an issue to get it added.") -endif() - -# Download Clang archive -message(STATUS "Downloading Clang ${CLANG_VERSION} (${CLANG_ARCHIVE_URL}) ...") -file(DOWNLOAD ${CLANG_ARCHIVE_URL} ${CLANG_ARCHIVE_FILE} - STATUS CLANG_ARCHIVE_DOWNLOAD_RESULT) - -# Abort if download failed -list(GET ${CLANG_ARCHIVE_DOWNLOAD_RESULT} 0 ERROR_CODE) -if(${ERROR_CODE}) - list(GET ${CLANG_ARCHIVE_DOWNLOAD_RESULT} 1 ERROR_STRING) - message(FATAL_ERROR ${ERROR_STRING}) -endif() - -# Retrieve expected hash from file and strip newline -file(READ ${CLANG_ARCHIVE_HASH_FILE} CLANG_ARCHIVE_EXPECTED_HASH) -string(STRIP ${CLANG_ARCHIVE_EXPECTED_HASH} CLANG_ARCHIVE_EXPECTED_HASH) -# Calculate actual hash -file(SHA256 ${CLANG_ARCHIVE_FILE} CLANG_ARCHIVE_HASH) -# Abort if hashes do not match -if(NOT ${CLANG_ARCHIVE_EXPECTED_HASH} STREQUAL ${CLANG_ARCHIVE_HASH}) - message(FATAL_ERROR "SHA256 hash of downloaded Clang does not match \ -expected hash. Remove the build directory and try running CMake again. If this \ -keeps happening, file an issue to report the problem.") -endif() - -if(${CLANG_ARCHIVE_EXT} STREQUAL .exe) - # Download and extract 7-zip if not found in PATH - find_program(7ZIP_EXECUTABLE 7z) - if(NOT 7ZIP_EXECUTABLE) - message(STATUS "7-Zip not found in PATH") - - include(DownloadAndExtract7zip) - download_and_extract_7zip(${CLANG_DOWNLOAD_LOCATION}) - find_program(7ZIP_EXECUTABLE - NAMES 7z - NO_DEFAULT_PATH - PATHS ${DOWNLOADED_7ZIP_DIR} - ) - else() - message(STATUS "7-Zip found in PATH") - endif() - - message(STATUS "Extracting downloaded Clang with 7-Zip ...") - - # Avoid running the Clang installer by extracting the exe with 7-Zip - execute_process( - COMMAND ${7ZIP_EXECUTABLE} x - -o${CLANG_ARCHIVE_EXTRACT_DIR} - -xr!$PLUGINSDIR ${CLANG_ARCHIVE_FILE} - WORKING_DIRECTORY ${CLANG_DOWNLOAD_LOCATION} - OUTPUT_QUIET - ) - -elseif(${CLANG_ARCHIVE_EXT} STREQUAL .tar.xz) - message(STATUS "Extracting downloaded Clang with CMake built-in tar ...") - - # CMake has builtin support for tar via the -E flag - execute_process( - COMMAND ${CMAKE_COMMAND} -E tar -xf ${CLANG_ARCHIVE_FILE} - # Specify working directory to allow running cmake from - # everywhere - # (example: cmake -H"$HOME/cquery" -B"$home/cquery/build") - WORKING_DIRECTORY ${CLANG_DOWNLOAD_LOCATION} - OUTPUT_QUIET - ) -endif() - -set(DOWNLOADED_CLANG_DIR ${CLANG_ARCHIVE_EXTRACT_DIR} PARENT_SCOPE) - -endfunction()