diff --git a/.gitignore b/.gitignore index a0783c5f..87cc1bce 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ build debug release +/compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 90c13cc9..63db0c0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,8 +170,6 @@ target_sources(ccls PRIVATE src/cache_manager.cc src/clang_complete.cc src/clang_cursor.cc - src/clang_format.cc - src/clang_index.cc src/clang_indexer.cc src/clang_translation_unit.cc src/clang_utils.cc @@ -201,11 +199,8 @@ target_sources(ccls PRIVATE src/query_utils.cc src/query.cc src/queue_manager.cc - src/recorder.cc - src/semantic_highlight_symbol_cache.cc src/serializer.cc src/standard_includes.cc - src/task.cc src/test.cc src/third_party_impl.cc src/timer.cc @@ -242,9 +237,7 @@ target_sources(ccls PRIVATE src/messages/text_document_document_highlight.cc src/messages/text_document_document_link.cc src/messages/text_document_document_symbol.cc - src/messages/text_document_formatting.cc src/messages/text_document_hover.cc - src/messages/text_document_range_formatting.cc src/messages/text_document_references.cc src/messages/text_document_rename.cc src/messages/text_document_signature_help.cc diff --git a/README.md b/README.md index 9f29f1b2..5d9737d2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # ccls -ccls is a fork of cquery, a C/C++/Objective-C language server. +ccls is a fork of cquery (originally written by Jacob Dufault), +a C/C++/Objective-C language server. * code completion (with both signature help and snippets) * finding [definition](src/messages/text_document_definition.cc)/[references](src/messages/text_document_references.cc) diff --git a/compile_commands.json b/compile_commands.json deleted file mode 120000 index 4595a4b0..00000000 --- a/compile_commands.json +++ /dev/null @@ -1 +0,0 @@ -build/release/compile_commands.json \ No newline at end of file diff --git a/src/clang_complete.h b/src/clang_complete.h index 0ed45896..142ab86a 100644 --- a/src/clang_complete.h +++ b/src/clang_complete.h @@ -1,6 +1,6 @@ #pragma once -#include "clang_index.h" +#include "clang_cursor.h" #include "clang_translation_unit.h" #include "lru_cache.h" #include "lsp_completion.h" diff --git a/src/clang_cursor.cc b/src/clang_cursor.cc index 8ce4c262..35197f72 100644 --- a/src/clang_cursor.cc +++ b/src/clang_cursor.cc @@ -2,10 +2,10 @@ #include "clang_utils.h" +#include #include - #include -#include +#include Range ResolveCXSourceRange(const CXSourceRange& range, CXFile* cx_file) { CXSourceLocation start = clang_getRangeStart(range); @@ -284,3 +284,22 @@ NtString ClangCursor::get_comments() const { std::string ClangCursor::ToString() const { return ::ToString(get_kind()) + " " + get_spell_name(); } + +ClangIndex::ClangIndex() : ClangIndex(1, 0) {} + +ClangIndex::ClangIndex(int exclude_declarations_from_pch, + int display_diagnostics) { + // llvm::InitializeAllTargets (and possibly others) called by + // clang_createIndex transtively modifies/reads lib/Support/TargetRegistry.cpp + // FirstTarget. There will be a race condition if two threads call + // clang_createIndex concurrently. + static std::mutex mutex_; + std::lock_guard lock(mutex_); + + cx_index = + clang_createIndex(exclude_declarations_from_pch, display_diagnostics); +} + +ClangIndex::~ClangIndex() { + clang_disposeIndex(cx_index); +} diff --git a/src/clang_cursor.h b/src/clang_cursor.h index 53f94cfa..6084408d 100644 --- a/src/clang_cursor.h +++ b/src/clang_cursor.h @@ -114,3 +114,14 @@ struct hash { } }; } // namespace std + +// Simple RAII wrapper about CXIndex. +// Note: building a ClangIndex instance acquires a global lock, since libclang +// API does not appear to be thread-safe here. +class ClangIndex { + public: + ClangIndex(); + ClangIndex(int exclude_declarations_from_pch, int display_diagnostics); + ~ClangIndex(); + CXIndex cx_index; +}; diff --git a/src/clang_format.cc b/src/clang_format.cc deleted file mode 100644 index 5bb238a8..00000000 --- a/src/clang_format.cc +++ /dev/null @@ -1,117 +0,0 @@ -#if USE_CLANG_CXX - -#include "clang_format.h" -#include "working_files.h" - -#include -#include - -using namespace clang; -using clang::format::FormatStyle; - -namespace { - -// TODO Objective-C 'header/interface' files may use .h, we should get this from -// project information. -FormatStyle::LanguageKind getLanguageKindFromFilename( - llvm::StringRef filename) { - if (filename.endswith(".m") || filename.endswith(".mm")) { - return FormatStyle::LK_ObjC; - } - return FormatStyle::LK_Cpp; -} - -} // namespace - -std::vector ClangFormatDocument( - WorkingFile* working_file, - int start, - int end, - lsFormattingOptions options) { - const auto language_kind = - getLanguageKindFromFilename(working_file->filename); - FormatStyle predefined_style; - getPredefinedStyle("chromium", language_kind, &predefined_style); - llvm::Expected style = - format::getStyle("file", working_file->filename, "chromium"); - if (!style) { - // If, for some reason, we cannot get a format style, use Chromium's with - // tab configuration provided by the client editor. - LOG_S(ERROR) << llvm::toString(style.takeError()); - predefined_style.UseTab = options.insertSpaces - ? FormatStyle::UseTabStyle::UT_Never - : FormatStyle::UseTabStyle::UT_Always; - predefined_style.IndentWidth = options.tabSize; - } - - auto format_result = reformat( - style ? *style : predefined_style, working_file->buffer_content, - llvm::ArrayRef(tooling::Range(start, end - start)), - working_file->filename); - return std::vector(format_result.begin(), - format_result.end()); -} - -TEST_SUITE("ClangFormat") { - TEST_CASE("entireDocument") { - const std::string sample_document = "int main() { int *i = 0; return 0; }"; - WorkingFile* file = new WorkingFile("foo.cc", sample_document); - lsFormattingOptions formatting_options; - formatting_options.insertSpaces = true; - const auto replacements = ClangFormatDocument( - file, 0, sample_document.size(), formatting_options); - - // echo "int main() { int *i = 0; return 0; }" | clang-format - // -style=Chromium -output-replacements-xml - // - // - // - // - // - // - // - // - // - - REQUIRE(replacements.size() == 5); - REQUIRE(replacements[0].getOffset() == 12); - REQUIRE(replacements[0].getLength() == 1); - REQUIRE(replacements[0].getReplacementText() == "\n "); - - REQUIRE(replacements[1].getOffset() == 16); - REQUIRE(replacements[1].getLength() == 1); - REQUIRE(replacements[1].getReplacementText() == ""); - - REQUIRE(replacements[2].getOffset() == 18); - REQUIRE(replacements[2].getLength() == 0); - REQUIRE(replacements[2].getReplacementText() == " "); - - REQUIRE(replacements[3].getOffset() == 24); - REQUIRE(replacements[3].getLength() == 1); - REQUIRE(replacements[3].getReplacementText() == "\n "); - - REQUIRE(replacements[4].getOffset() == 34); - REQUIRE(replacements[4].getLength() == 1); - REQUIRE(replacements[4].getReplacementText() == "\n"); - } - - TEST_CASE("range") { - const std::string sampleDocument = "int main() { int *i = 0; return 0; }"; - WorkingFile* file = new WorkingFile("foo.cc", sampleDocument); - lsFormattingOptions formattingOptions; - formattingOptions.insertSpaces = true; - const auto replacements = - ClangFormatDocument(file, 30, sampleDocument.size(), formattingOptions); - - REQUIRE(replacements.size() == 2); - REQUIRE(replacements[0].getOffset() == 24); - REQUIRE(replacements[0].getLength() == 1); - REQUIRE(replacements[0].getReplacementText() == "\n "); - - REQUIRE(replacements[1].getOffset() == 34); - REQUIRE(replacements[1].getLength() == 1); - REQUIRE(replacements[1].getReplacementText() == "\n"); - } -} - -#endif diff --git a/src/clang_format.h b/src/clang_format.h deleted file mode 100644 index 862f3e06..00000000 --- a/src/clang_format.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#if USE_CLANG_CXX - -#include "lsp.h" -#include "working_files.h" - -#include - -#include - -std::vector ClangFormatDocument( - WorkingFile* working_file, - int start, - int end, - lsFormattingOptions options); - -#endif diff --git a/src/clang_index.cc b/src/clang_index.cc deleted file mode 100644 index 9c74fdfa..00000000 --- a/src/clang_index.cc +++ /dev/null @@ -1,22 +0,0 @@ -#include "clang_index.h" - -#include - -ClangIndex::ClangIndex() : ClangIndex(1, 0) {} - -ClangIndex::ClangIndex(int exclude_declarations_from_pch, - int display_diagnostics) { - // llvm::InitializeAllTargets (and possibly others) called by - // clang_createIndex transtively modifies/reads lib/Support/TargetRegistry.cpp - // FirstTarget. There will be a race condition if two threads call - // clang_createIndex concurrently. - static std::mutex mutex_; - std::lock_guard lock(mutex_); - - cx_index = - clang_createIndex(exclude_declarations_from_pch, display_diagnostics); -} - -ClangIndex::~ClangIndex() { - clang_disposeIndex(cx_index); -} diff --git a/src/clang_index.h b/src/clang_index.h deleted file mode 100644 index 1350d900..00000000 --- a/src/clang_index.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -// Simple RAII wrapper about CXIndex. -// Note: building a ClangIndex instance acquires a global lock, since libclang -// API does not appear to be thread-safe here. -class ClangIndex { - public: - ClangIndex(); - ClangIndex(int exclude_declarations_from_pch, int display_diagnostics); - ~ClangIndex(); - CXIndex cx_index; -}; diff --git a/src/clang_indexer.cc b/src/clang_indexer.cc index f4240df3..4ff2c86d 100644 --- a/src/clang_indexer.cc +++ b/src/clang_indexer.cc @@ -1555,7 +1555,6 @@ void OnIndexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { SetVarDetail(var, std::string(decl->entityInfo->name), decl->cursor, decl->semanticContainer, !decl->isRedeclaration, db, param); - // FIXME https://github.com/jacobdufault/ccls/issues/239 var->def.kind = GetSymbolKind(decl->entityInfo->kind); if (var->def.kind == lsSymbolKind::Variable && decl->cursor.kind == CXCursor_ParmDecl) @@ -1776,8 +1775,6 @@ void OnIndexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { // For Typedef/CXXTypeAlias spanning a few lines, display the declaration // line, with spelling name replaced with qualified name. - // TODO Think how to display multi-line declaration like `typedef struct { - // ... } foo;` https://github.com/jacobdufault/ccls/issues/29 if (extent.end.line - extent.start.line < kMaxLinesDisplayTypeAliasDeclarations) { FileContents& fc = param->file_contents[db->path]; @@ -1924,7 +1921,6 @@ void OnIndexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { } } -// https://github.com/jacobdufault/ccls/issues/174 // Type-dependent member access expressions do not have accurate spelling // ranges. // @@ -2328,56 +2324,6 @@ void IndexInit() { clang_toggleCrashRecovery(1); } -void ClangSanityCheck() { - std::vector args = {"clang", "index_tests/vars/class_member.cc"}; - unsigned opts = 0; - CXIndex index = clang_createIndex(0, 1); - CXTranslationUnit tu; - clang_parseTranslationUnit2FullArgv(index, nullptr, args.data(), args.size(), - nullptr, 0, opts, &tu); - assert(tu); - - IndexerCallbacks callback = {0}; - callback.abortQuery = [](CXClientData client_data, void* reserved) { - return 0; - }; - callback.diagnostic = [](CXClientData client_data, - CXDiagnosticSet diagnostics, void* reserved) {}; - callback.enteredMainFile = [](CXClientData client_data, CXFile mainFile, - void* reserved) -> CXIdxClientFile { - return nullptr; - }; - callback.ppIncludedFile = - [](CXClientData client_data, - const CXIdxIncludedFileInfo* file) -> CXIdxClientFile { - return nullptr; - }; - callback.importedASTFile = - [](CXClientData client_data, - const CXIdxImportedASTFileInfo*) -> CXIdxClientASTFile { - return nullptr; - }; - callback.startedTranslationUnit = [](CXClientData client_data, - void* reserved) -> CXIdxClientContainer { - return nullptr; - }; - callback.indexDeclaration = [](CXClientData client_data, - const CXIdxDeclInfo* decl) {}; - callback.indexEntityReference = [](CXClientData client_data, - const CXIdxEntityRefInfo* ref) {}; - - const unsigned kIndexOpts = 0; - CXIndexAction index_action = clang_IndexAction_create(index); - int index_param = 0; - clang_toggleCrashRecovery(0); - clang_indexTranslationUnit(index_action, &index_param, &callback, - sizeof(IndexerCallbacks), kIndexOpts, tu); - clang_IndexAction_dispose(index_action); - - clang_disposeTranslationUnit(tu); - clang_disposeIndex(index); -} - std::string GetClangVersion() { return ToString(clang_getClangVersion()); } diff --git a/src/clang_translation_unit.h b/src/clang_translation_unit.h index efeedc6c..59e2f17f 100644 --- a/src/clang_translation_unit.h +++ b/src/clang_translation_unit.h @@ -1,7 +1,6 @@ #pragma once #include "clang_cursor.h" -#include "clang_index.h" #include @@ -28,4 +27,4 @@ struct ClangTranslationUnit { ~ClangTranslationUnit(); CXTranslationUnit cx_tu; -}; \ No newline at end of file +}; diff --git a/src/clang_utils.cc b/src/clang_utils.cc index d89f7856..cfd5ce6a 100644 --- a/src/clang_utils.cc +++ b/src/clang_utils.cc @@ -2,8 +2,6 @@ #include "platform.h" -#include - namespace { lsRange GetLsRangeForFixIt(const CXSourceRange& range) { @@ -106,31 +104,6 @@ std::optional BuildAndDisposeDiagnostic(CXDiagnostic diagnostic, return ls_diagnostic; } -#if USE_CLANG_CXX -static lsPosition OffsetToRange(llvm::StringRef document, size_t offset) { - // TODO: Support Windows line endings, etc. - llvm::StringRef text_before = document.substr(0, offset); - int num_line = text_before.count('\n'); - int num_column = text_before.size() - text_before.rfind('\n') - 1; - return {num_line, num_column}; -} - -std::vector ConvertClangReplacementsIntoTextEdits( - llvm::StringRef document, - const std::vector& clang_replacements) { - std::vector text_edits_result; - for (const auto& replacement : clang_replacements) { - const auto startPosition = OffsetToRange(document, replacement.getOffset()); - const auto endPosition = OffsetToRange( - document, replacement.getOffset() + replacement.getLength()); - text_edits_result.push_back( - {{startPosition, endPosition}, replacement.getReplacementText()}); - } - return text_edits_result; -} - -#endif - std::string FileName(CXFile file) { CXString cx_name = clang_getFileName(file); std::string name = ToString(cx_name); @@ -172,60 +145,9 @@ const char* ClangBuiltinTypeName(CXTypeKind kind) { case CXType_Double: return "double"; case CXType_LongDouble: return "long double"; case CXType_Float128: return "__float128"; -#if CINDEX_VERSION_MINOR >= 43 case CXType_Half: return "_Float16"; -#endif case CXType_NullPtr: return "nullptr"; default: return ""; - // clang-format on + // clang-format on } } - -#if USE_CLANG_CXX -TEST_SUITE("ClangUtils") { - TEST_CASE("replacements") { - const std::string sample_document = - "int \n" - "main() { int *i = 0; return 0; \n" - "}"; - const std::vector clang_replacements = { - {"foo.cc", 3, 2, " "}, {"foo.cc", 13, 1, "\n "}, - {"foo.cc", 17, 1, ""}, {"foo.cc", 19, 0, " "}, - {"foo.cc", 25, 1, "\n "}, {"foo.cc", 35, 2, "\n"}}; - - // Expected format: - // - // int main() { - // int *i = 0; - // return 0; - // } - - const auto text_edits = ConvertClangReplacementsIntoTextEdits( - sample_document, clang_replacements); - REQUIRE(text_edits.size() == 6); - REQUIRE(text_edits[0].range.start.line == 0); - REQUIRE(text_edits[0].range.start.character == 3); - REQUIRE(text_edits[0].newText == " "); - - REQUIRE(text_edits[1].range.start.line == 1); - REQUIRE(text_edits[1].range.start.character == 8); - REQUIRE(text_edits[1].newText == "\n "); - - REQUIRE(text_edits[2].range.start.line == 1); - REQUIRE(text_edits[2].range.start.character == 12); - REQUIRE(text_edits[2].newText == ""); - - REQUIRE(text_edits[3].range.start.line == 1); - REQUIRE(text_edits[3].range.start.character == 14); - REQUIRE(text_edits[3].newText == " "); - - REQUIRE(text_edits[4].range.start.line == 1); - REQUIRE(text_edits[4].range.start.character == 20); - REQUIRE(text_edits[4].newText == "\n "); - - REQUIRE(text_edits[5].range.start.line == 1); - REQUIRE(text_edits[5].range.start.character == 30); - REQUIRE(text_edits[5].newText == "\n"); - } -} -#endif diff --git a/src/clang_utils.h b/src/clang_utils.h index 65236876..8db9fedd 100644 --- a/src/clang_utils.h +++ b/src/clang_utils.h @@ -3,11 +3,8 @@ #include "lsp_diagnostic.h" #include -#if USE_CLANG_CXX -#include -#endif -#include +#include #include std::optional BuildAndDisposeDiagnostic(CXDiagnostic diagnostic, @@ -21,10 +18,3 @@ std::string ToString(CXString cx_string); std::string ToString(CXCursorKind cursor_kind); const char* ClangBuiltinTypeName(CXTypeKind); - -// Converts Clang formatting replacement operations into LSP text edits. -#if USE_CLANG_CXX -std::vector ConvertClangReplacementsIntoTextEdits( - llvm::StringRef document, - const std::vector& clang_replacements); -#endif diff --git a/src/command_line.cc b/src/command_line.cc index 700e5b10..c5be6682 100644 --- a/src/command_line.cc +++ b/src/command_line.cc @@ -19,8 +19,6 @@ #include "query.h" #include "query_utils.h" #include "queue_manager.h" -#include "recorder.h" -#include "semantic_highlight_symbol_cache.h" #include "serializer.h" #include "serializers/json.h" #include "test.h" @@ -43,19 +41,10 @@ #include #include -// TODO: provide a feature like 'https://github.com/goldsborough/clang-expand', -// ie, a fully linear view of a function with inline function calls expanded. -// We can probably use vscode decorators to achieve it. - -// TODO: implement ThreadPool type which monitors CPU usage / number of work -// items per second completed and scales up/down number of running threads. - std::string g_init_options; namespace { -std::vector kEmptyArgs; - // This function returns true if e2e timing should be displayed for the given // MethodId. bool ShouldDisplayMethodTiming(MethodType type) { @@ -70,9 +59,6 @@ void PrintHelp() { << R"help(ccls is a low-latency C/C++/Objective-C language server. Mode: - --clang-sanity-check - Run a simple index test. Verifies basic clang functionality. - Needs to be executed from the ccls root checkout directory. --test-unit Run unit tests. --test-index Run index tests. opt_filter_path can be used to specify which @@ -85,37 +71,20 @@ Other command line options: --init Override client provided initialization options https://github.com/cquery-project/cquery/wiki/Initialization-options - --record - Writes stdin to .in and stdout to .out --log-file Logging file for diagnostics --log-file-append Like --log-file, but appending --log-all-to-stderr Write all log messages to STDERR. - --wait-for-input Wait for an '[Enter]' before exiting --help Print this help information. --ci Prevents tests from prompting the user for input. Used for continuous integration so it can fail faster instead of timing out. -See more on https://github.com/cquery-project/cquery/wiki +See more on https://github.com/MaskRay/ccls/wiki )help"; } } // namespace -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // QUERYDB MAIN //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// @@ -248,20 +217,6 @@ void RunQueryDbThread(const std::string& bin_name, } } -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // STDIN MAIN ////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// @@ -333,8 +288,6 @@ void LaunchStdoutThread(std::unordered_map* request_times, time.ResetAndPrint("[e2e] Running " + std::string(message.method)); } - RecordOutput(message.content); - fwrite(message.content.c_str(), message.content.size(), 1, stdout); fflush(stdout); } @@ -360,23 +313,6 @@ void LanguageServerMain(const std::string& bin_name, RunQueryDbThread(bin_name, config, querydb_waiter, indexer_waiter); } -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// MAIN //////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// int main(int argc, char** argv) { TraceMe(); @@ -413,14 +349,6 @@ int main(int argc, char** argv) { loguru::Verbosity_MAX); } - if (HasOption(options, "--record")) - EnableRecording(options["--record"]); - - if (HasOption(options, "--clang-sanity-check")) { - language_server = false; - ClangSanityCheck(); - } - if (HasOption(options, "--test-unit")) { language_server = false; doctest::Context context; @@ -467,10 +395,5 @@ int main(int argc, char** argv) { &stdout_waiter); } - if (HasOption(options, "--wait-for-input")) { - std::cerr << std::endl << "[Enter] to exit" << std::endl; - getchar(); - } - return 0; } diff --git a/src/include_complete.cc b/src/include_complete.cc index 19f48c8e..dcbaf8bc 100644 --- a/src/include_complete.cc +++ b/src/include_complete.cc @@ -168,8 +168,6 @@ void IncludeComplete::AddFile(const std::string& absolute_path) { if (is_scanning) lock.lock(); InsertCompletionItem(absolute_path, std::move(item)); - if (lock) - lock.unlock(); } void IncludeComplete::InsertIncludesFromDirectory(std::string directory, diff --git a/src/indexer.h b/src/indexer.h index 025a2712..24db98c4 100644 --- a/src/indexer.h +++ b/src/indexer.h @@ -1,7 +1,6 @@ #pragma once #include "clang_cursor.h" -#include "clang_index.h" #include "clang_translation_unit.h" #include "clang_utils.h" #include "file_consumer.h" @@ -16,14 +15,13 @@ #include "symbol.h" #include "utils.h" +#include +#include +#include +#include +#include #include #include - -#include -#include -#include -#include -#include #include struct IndexFile; diff --git a/src/lsp.cc b/src/lsp.cc index bebe3cf8..eb1825a6 100644 --- a/src/lsp.cc +++ b/src/lsp.cc @@ -1,6 +1,5 @@ #include "lsp.h" -#include "recorder.h" #include "serializers/json.h" #include @@ -67,8 +66,6 @@ std::optional ReadJsonRpcContentFrom( content += *c; } - RecordInput(content); - return content; } diff --git a/src/message_handler.cc b/src/message_handler.cc index bcb97837..dbf6d091 100644 --- a/src/message_handler.cc +++ b/src/message_handler.cc @@ -4,7 +4,6 @@ #include "project.h" #include "query_utils.h" #include "queue_manager.h" -#include "semantic_highlight_symbol_cache.h" #include @@ -42,6 +41,77 @@ struct ScanLineEvent { }; } // namespace +SemanticHighlightSymbolCache::Entry::Entry( + SemanticHighlightSymbolCache* all_caches, + const std::string& path) + : all_caches_(all_caches), path(path) {} + +std::optional SemanticHighlightSymbolCache::Entry::TryGetStableId( + SymbolKind kind, + const std::string& detailed_name) { + TNameToId* map = GetMapForSymbol_(kind); + auto it = map->find(detailed_name); + if (it != map->end()) + return it->second; + + return std::nullopt; +} + +int SemanticHighlightSymbolCache::Entry::GetStableId( + SymbolKind kind, + const std::string& detailed_name) { + std::optional id = TryGetStableId(kind, detailed_name); + if (id) + return *id; + + // Create a new id. First try to find a key in another map. + all_caches_->cache_.IterateValues([&](const std::shared_ptr& entry) { + std::optional other_id = entry->TryGetStableId(kind, detailed_name); + if (other_id) { + id = other_id; + return false; + } + return true; + }); + + // Create a new id. + TNameToId* map = GetMapForSymbol_(kind); + if (!id) + id = all_caches_->next_stable_id_++; + return (*map)[detailed_name] = *id; +} + +SemanticHighlightSymbolCache::Entry::TNameToId* +SemanticHighlightSymbolCache::Entry::GetMapForSymbol_(SymbolKind kind) { + switch (kind) { + case SymbolKind::Type: + return &detailed_type_name_to_stable_id; + case SymbolKind::Func: + return &detailed_func_name_to_stable_id; + case SymbolKind::Var: + return &detailed_var_name_to_stable_id; + case SymbolKind::File: + case SymbolKind::Invalid: + break; + } + assert(false); + return nullptr; +} + +SemanticHighlightSymbolCache::SemanticHighlightSymbolCache() + : cache_(kCacheSize) {} + +void SemanticHighlightSymbolCache::Init(Config* config) { + match_ = std::make_unique(config->highlight.whitelist, + config->highlight.blacklist); +} + +std::shared_ptr +SemanticHighlightSymbolCache::GetCacheForFile(const std::string& path) { + return cache_.Get( + path, [&, this]() { return std::make_shared(this, path); }); +} + MessageHandler::MessageHandler() { // Dynamically allocate |message_handlers|, otherwise there will be static // initialization order races. diff --git a/src/message_handler.h b/src/message_handler.h index 49165a59..8fe0c6c4 100644 --- a/src/message_handler.h +++ b/src/message_handler.h @@ -1,12 +1,14 @@ #pragma once +#include "lru_cache.h" #include "lsp.h" +#include "match.h" #include "method.h" #include "query.h" #include - #include +#include #include struct ClangCompleteManager; @@ -20,11 +22,43 @@ struct IncludeComplete; struct MultiQueueWaiter; struct Project; struct QueryDatabase; -struct SemanticHighlightSymbolCache; struct TimestampManager; struct WorkingFile; struct WorkingFiles; +// Caches symbols for a single file for semantic highlighting to provide +// relatively stable ids. Only supports xxx files at a time. +struct SemanticHighlightSymbolCache { + struct Entry { + SemanticHighlightSymbolCache* all_caches_ = nullptr; + + // The path this cache belongs to. + std::string path; + // Detailed symbol name to stable id. + using TNameToId = std::unordered_map; + TNameToId detailed_type_name_to_stable_id; + TNameToId detailed_func_name_to_stable_id; + TNameToId detailed_var_name_to_stable_id; + + Entry(SemanticHighlightSymbolCache* all_caches, const std::string& path); + + std::optional TryGetStableId(SymbolKind kind, + const std::string& detailed_name); + int GetStableId(SymbolKind kind, const std::string& detailed_name); + + TNameToId* GetMapForSymbol_(SymbolKind kind); + }; + + constexpr static int kCacheSize = 10; + LruCache cache_; + uint32_t next_stable_id_ = 0; + std::unique_ptr match_; + + SemanticHighlightSymbolCache(); + void Init(Config*); + std::shared_ptr GetCacheForFile(const std::string& path); +}; + struct Out_CclsPublishSemanticHighlighting : public lsOutMessage { struct Symbol { diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index 9d97b7c2..fd42cd34 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -6,7 +6,6 @@ #include "platform.h" #include "project.h" #include "queue_manager.h" -#include "semantic_highlight_symbol_cache.h" #include "serializers/json.h" #include "timer.h" #include "work_thread.h" @@ -361,26 +360,13 @@ MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities, struct lsClientCapabilities { // Workspace specific client capabilities. - std::optional workspace; + lsWorkspaceClientCapabilites workspace; // Text document specific client capabilities. - std::optional textDocument; - - /** - * Experimental client capabilities. - */ - // experimental?: any; // TODO + lsTextDocumentClientCapabilities textDocument; }; MAKE_REFLECT_STRUCT(lsClientCapabilities, workspace, textDocument); -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -////////////////////////////// INITIALIZATION /////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// - struct lsInitializeParams { // The process Id of the parent process that started // the server. Is null if the process has not been started by another process. @@ -524,8 +510,8 @@ struct Handler_Initialize : BaseMessageHandler { } // Client capabilities - if (request->params.capabilities.textDocument) { - const auto& cap = *request->params.capabilities.textDocument; + { + const auto& cap = request->params.capabilities.textDocument; if (cap.completion && cap.completion->completionItem) config->client.snippetSupport = cap.completion->completionItem->snippetSupport.value_or(false); diff --git a/src/messages/text_document_completion.cc b/src/messages/text_document_completion.cc index fd85c311..a186e178 100644 --- a/src/messages/text_document_completion.cc +++ b/src/messages/text_document_completion.cc @@ -338,7 +338,10 @@ struct Handler_TextDocumentCompletion : MessageHandler { include_complete->completion_items_mutex, std::defer_lock); if (include_complete->is_scanning) lock.lock(); - out.result.items = include_complete->completion_items; + std::string quote = result.match[5]; + for (auto& item : include_complete->completion_items) + if (quote.empty() || quote == (item.use_angle_brackets_ ? "<" : "\"")) + out.result.items.push_back(item); } FilterAndSortCompletionResponse(&out, result.pattern, config->completion.filterAndSort); diff --git a/src/recorder.cc b/src/recorder.cc deleted file mode 100644 index f369d85f..00000000 --- a/src/recorder.cc +++ /dev/null @@ -1,56 +0,0 @@ -#include "recorder.h" - -#include - -#include -#include - -namespace { -std::ofstream* g_file_in = nullptr; -std::ofstream* g_file_out = nullptr; -} // namespace - -void EnableRecording(std::string path) { - if (path.empty()) - path = "ccls"; - - // We can only call |EnableRecording| once. - assert(!g_file_in && !g_file_out); - - // Open the files. - g_file_in = new std::ofstream( - path + ".in", std::ios::out | std::ios::trunc | std::ios::binary); - g_file_out = new std::ofstream( - path + ".out", std::ios::out | std::ios::trunc | std::ios::binary); - - // Make sure we can write to the files. - bool bad = false; - if (!g_file_in->good()) { - LOG_S(ERROR) << "record: cannot write to " << path << ".in"; - bad = true; - } - if (!g_file_out->good()) { - LOG_S(ERROR) << "record: cannot write to " << path << ".out"; - bad = true; - } - if (bad) { - delete g_file_in; - delete g_file_out; - g_file_in = nullptr; - g_file_out = nullptr; - } -} - -void RecordInput(std::string_view content) { - if (!g_file_in) - return; - (*g_file_in) << "Content-Length: " << content.size() << "\r\n\r\n" << content; - (*g_file_in).flush(); -} - -void RecordOutput(std::string_view content) { - if (!g_file_out) - return; - (*g_file_out) << content; - (*g_file_out).flush(); -} \ No newline at end of file diff --git a/src/recorder.h b/src/recorder.h deleted file mode 100644 index f3474ae6..00000000 --- a/src/recorder.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include - -#include - -void EnableRecording(std::string path); -void RecordInput(std::string_view content); -void RecordOutput(std::string_view content); \ No newline at end of file diff --git a/src/semantic_highlight_symbol_cache.cc b/src/semantic_highlight_symbol_cache.cc deleted file mode 100644 index 3fc045bc..00000000 --- a/src/semantic_highlight_symbol_cache.cc +++ /dev/null @@ -1,72 +0,0 @@ -#include "semantic_highlight_symbol_cache.h" - -SemanticHighlightSymbolCache::Entry::Entry( - SemanticHighlightSymbolCache* all_caches, - const std::string& path) - : all_caches_(all_caches), path(path) {} - -std::optional SemanticHighlightSymbolCache::Entry::TryGetStableId( - SymbolKind kind, - const std::string& detailed_name) { - TNameToId* map = GetMapForSymbol_(kind); - auto it = map->find(detailed_name); - if (it != map->end()) - return it->second; - - return std::nullopt; -} - -int SemanticHighlightSymbolCache::Entry::GetStableId( - SymbolKind kind, - const std::string& detailed_name) { - std::optional id = TryGetStableId(kind, detailed_name); - if (id) - return *id; - - // Create a new id. First try to find a key in another map. - all_caches_->cache_.IterateValues([&](const std::shared_ptr& entry) { - std::optional other_id = entry->TryGetStableId(kind, detailed_name); - if (other_id) { - id = other_id; - return false; - } - return true; - }); - - // Create a new id. - TNameToId* map = GetMapForSymbol_(kind); - if (!id) - id = all_caches_->next_stable_id_++; - return (*map)[detailed_name] = *id; -} - -SemanticHighlightSymbolCache::Entry::TNameToId* -SemanticHighlightSymbolCache::Entry::GetMapForSymbol_(SymbolKind kind) { - switch (kind) { - case SymbolKind::Type: - return &detailed_type_name_to_stable_id; - case SymbolKind::Func: - return &detailed_func_name_to_stable_id; - case SymbolKind::Var: - return &detailed_var_name_to_stable_id; - case SymbolKind::File: - case SymbolKind::Invalid: - break; - } - assert(false); - return nullptr; -} - -SemanticHighlightSymbolCache::SemanticHighlightSymbolCache() - : cache_(kCacheSize) {} - -void SemanticHighlightSymbolCache::Init(Config* config) { - match_ = std::make_unique(config->highlight.whitelist, - config->highlight.blacklist); -} - -std::shared_ptr -SemanticHighlightSymbolCache::GetCacheForFile(const std::string& path) { - return cache_.Get( - path, [&, this]() { return std::make_shared(this, path); }); -} diff --git a/src/semantic_highlight_symbol_cache.h b/src/semantic_highlight_symbol_cache.h deleted file mode 100644 index 907be1e6..00000000 --- a/src/semantic_highlight_symbol_cache.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "lru_cache.h" -#include "match.h" -#include "query.h" - -#include - -#include -#include - -// Caches symbols for a single file for semantic highlighting to provide -// relatively stable ids. Only supports xxx files at a time. -struct SemanticHighlightSymbolCache { - struct Entry { - SemanticHighlightSymbolCache* all_caches_ = nullptr; - - // The path this cache belongs to. - std::string path; - // Detailed symbol name to stable id. - using TNameToId = std::unordered_map; - TNameToId detailed_type_name_to_stable_id; - TNameToId detailed_func_name_to_stable_id; - TNameToId detailed_var_name_to_stable_id; - - Entry(SemanticHighlightSymbolCache* all_caches, const std::string& path); - - std::optional TryGetStableId(SymbolKind kind, - const std::string& detailed_name); - int GetStableId(SymbolKind kind, const std::string& detailed_name); - - TNameToId* GetMapForSymbol_(SymbolKind kind); - }; - - constexpr static int kCacheSize = 10; - LruCache cache_; - uint32_t next_stable_id_ = 0; - std::unique_ptr match_; - - SemanticHighlightSymbolCache(); - void Init(Config*); - std::shared_ptr GetCacheForFile(const std::string& path); -}; diff --git a/src/task.cc b/src/task.cc deleted file mode 100644 index 7a545d71..00000000 --- a/src/task.cc +++ /dev/null @@ -1,152 +0,0 @@ -#if false -#include "task.h" - -#include "utils.h" - -#include - -TaskManager::TaskManager() { - pending_tasks_[TaskThread::Indexer] = std::make_unique(); - pending_tasks_[TaskThread::QueryDb] = std::make_unique(); -} - -void TaskManager::Post(TaskThread thread, const TTask& task) { - TaskQueue* queue = pending_tasks_[thread].get(); - std::lock_guard lock_guard(queue->tasks_mutex); - queue->tasks.push_back(task); -} - -void TaskManager::SetIdle(TaskThread thread, const TIdleTask& task) { - TaskQueue* queue = pending_tasks_[thread].get(); - std::lock_guard lock_guard(queue->tasks_mutex); - assert(!queue->idle_task && "There is already an idle task"); - queue->idle_task = task; -} - -bool TaskManager::RunTasks(TaskThread thread, std::optional> max_time) { - auto start = std::chrono::high_resolution_clock::now(); - TaskQueue* queue = pending_tasks_[thread].get(); - - bool ran_task = false; - - while (true) { - std::optional task; - - // Get a task. - { - std::lock_guard lock_guard(queue->tasks_mutex); - if (queue->tasks.empty()) - break; - task = std::move(queue->tasks[queue->tasks.size() - 1]); - queue->tasks.pop_back(); - } - - // Execute task. - assert(task); - (*task)(); - ran_task = true; - - // Stop if we've run past our max time. Don't run idle_task. - auto elapsed = std::chrono::high_resolution_clock::now() - start; - if (max_time && elapsed > *max_time) - return ran_task; - } - - if (queue->idle_task) { - // Even if the idle task returns false we still ran something before. - ran_task = (*queue->idle_task)() || ran_task; - } - - return ran_task; -} - -TEST_SUITE("Task"); - -TEST_CASE("tasks are run as soon as they are posted") { - TaskManager tm; - - // Post three tasks. - int next = 1; - int a = 0, b = 0, c = 0; - tm.Post(TaskThread::QueryDb, [&] { - a = next++; - }); - tm.Post(TaskThread::QueryDb, [&] { - b = next++; - }); - tm.Post(TaskThread::QueryDb, [&] { - c = next++; - }); - - // Execute all tasks. - tm.RunTasks(TaskThread::QueryDb, std::nullopt); - - // Tasks are executed in reverse order. - REQUIRE(a == 3); - REQUIRE(b == 2); - REQUIRE(c == 1); -} - -TEST_CASE("post from inside task manager") { - TaskManager tm; - - // Post three tasks. - int next = 1; - int a = 0, b = 0, c = 0; - tm.Post(TaskThread::QueryDb, [&] () { - a = next++; - - tm.Post(TaskThread::QueryDb, [&] { - b = next++; - - tm.Post(TaskThread::QueryDb, [&] { - c = next++; - }); - }); - }); - - // Execute all tasks. - tm.RunTasks(TaskThread::QueryDb, std::nullopt); - - // Tasks are executed in normal order because the next task is not posted - // until the previous one is executed. - REQUIRE(a == 1); - REQUIRE(b == 2); - REQUIRE(c == 3); -} - -TEST_CASE("idle task is run after nested tasks") { - TaskManager tm; - - int count = 0; - tm.SetIdle(TaskThread::QueryDb, [&]() { - ++count; - return true; - }); - - // No tasks posted - idle runs once. - REQUIRE(tm.RunTasks(TaskThread::QueryDb, std::nullopt)); - REQUIRE(count == 1); - count = 0; - - // Idle runs after other posted tasks. - bool did_run = false; - tm.Post(TaskThread::QueryDb, [&]() { - did_run = true; - }); - REQUIRE(tm.RunTasks(TaskThread::QueryDb, std::nullopt)); - REQUIRE(did_run); - REQUIRE(count == 1); -} - -TEST_CASE("RunTasks returns false when idle task returns false and no other tasks were run") { - TaskManager tm; - - REQUIRE(tm.RunTasks(TaskThread::QueryDb, std::nullopt) == false); - - tm.SetIdle(TaskThread::QueryDb, []() { return false; }); - REQUIRE(tm.RunTasks(TaskThread::QueryDb, std::nullopt) == false); -} - -TEST_SUITE_END(); -#endif \ No newline at end of file diff --git a/src/task.h b/src/task.h deleted file mode 100644 index 5cdd4e0a..00000000 --- a/src/task.h +++ /dev/null @@ -1,41 +0,0 @@ -#if false -#pragma once - -#include - -#include -#include -#include -#include -#include - -enum class TaskThread { - Indexer, - QueryDb, -}; - -struct TaskManager { - using TTask = std::function; - using TIdleTask = std::function; - - TaskManager(); - - // Run |task| at some point in the future. This will run the task as soon as possible. - void Post(TaskThread thread, const TTask& task); - - // Run |task| whenever there is nothing else to run. - void SetIdle(TaskThread thread, const TIdleTask& idle_task); - - // Run pending tasks for |thread|. Stop running tasks after |max_time| has - // elapsed. Returns true if tasks were run. - bool RunTasks(TaskThread thread, std::optional> max_time); - - struct TaskQueue { - std::optional idle_task; - std::vector tasks; - std::mutex tasks_mutex; - }; - - std::unordered_map> pending_tasks_; -}; -#endif \ No newline at end of file diff --git a/wscript b/wscript deleted file mode 100644 index d090ab41..00000000 --- a/wscript +++ /dev/null @@ -1,424 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - -try: - from urllib2 import urlopen # Python 2 -except ImportError: - from urllib.request import urlopen # Python 3 - -import os.path -import string -import subprocess -import sys -import re -import ctypes -import shutil - -VERSION = '0.0.1' -APPNAME = 'cquery' - -top = '.' -out = 'build' - -CLANG_TARBALL_NAME = None -CLANG_TARBALL_EXT = '.tar.xz' -if sys.platform == 'darwin': - CLANG_TARBALL_NAME = 'clang+llvm-$version-x86_64-apple-darwin' -elif sys.platform.startswith('freebsd'): - CLANG_TARBALL_NAME = 'clang+llvm-$version-amd64-unknown-freebsd-10' -# It is either 'linux2' or 'linux3' before Python 3.3 -elif sys.platform.startswith('linux'): - # These executable depend on libtinfo.so.5 - CLANG_TARBALL_NAME = 'clang+llvm-$version-x86_64-linux-gnu-ubuntu-14.04' -elif sys.platform == 'win32': - CLANG_TARBALL_NAME = 'LLVM-$version-win64' - CLANG_TARBALL_EXT = '.exe' - -from waflib.Tools.compiler_cxx import cxx_compiler -cxx_compiler['linux'] = ['clang++', 'g++'] - -# Creating symbolic link on Windows requires a special priviledge SeCreateSymboliclinkPrivilege, -# which an non-elevated process lacks. Starting with Windows 10 build 14972, this got relaxed -# when Developer Mode is enabled. Triggering this new behaviour requires a new flag. -SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2 - -# Python 2 has no symlink function and Python 3's symlink function does not allow creation of -# symlinks without admin rights (even when Developer Mode is enabled) so we define our own -# symlink function when on Windows. -if sys.platform == 'win32': - kdll = ctypes.windll.kernel32 - def symlink(source, link_name, target_is_directory=False): - # SYMBOLIC_LINK_FLAG_DIRECTORY: 0x1 - flags = int(target_is_directory) | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE - - # Use unicode version (W suffix) of Windows symbolic link function and convert strings to - # unicode if Python 2 is used (strings are unicode by default in Python 3). - if sys.version_info < (3, 0): - ret = kdll.CreateSymbolicLinkW(unicode(link_name), unicode(source), flags) - else: - ret = kdll.CreateSymbolicLinkW(link_name, source, flags) - - if ret == 0: - err = ctypes.WinError() - raise err - os.symlink = symlink -elif sys.version_info.major < 3: - os_symlink_bak = os.symlink - os.symlink = lambda src, dst, target_is_directory=False: os_symlink_bak(src, dst) - -# There is a null pointer dereference issue in tools/libclang/CXIndexDataConsumer.cpp handleReference. -def patch_byte_in_libclang(filename, offset, old, new): - with open(filename, 'rb+') as f: - f.seek(offset) - t = f.read(1) - if t == old: - f.seek(offset) - f.write(new) - print('Applied byte patch hack at 0x{:x}'.format(offset)) - print('This is a makeshift for indexing default arguments of template template parameters, which otherwise would crash libclang') - print('See https://github.com/jacobdufault/cquery/issues/219 for details') - else: - assert t == new - -def options(opt): - opt.load('compiler_cxx') - grp = opt.add_option_group('Configuration options related to use of clang from the system (not recommended)') - grp.add_option('--enable-assert', action='store_true') - grp.add_option('--use-clang-cxx', dest='use_clang_cxx', default=False, action='store_true', - help='use clang C++ API') - grp.add_option('--bundled-clang', dest='bundled_clang', default='6.0.0', - help='bundled clang version, downloaded from https://releases.llvm.org/ , e.g. 5.0.1 6.0.0') - grp.add_option('--llvm-config', dest='llvm_config', - help='path to llvm-config to use system libclang, e.g. llvm-config llvm-config-6.0') - grp.add_option('--clang-prefix', dest='clang_prefix', - help='enable fallback configuration method by specifying a clang installation prefix (e.g. /opt/llvm)') - grp.add_option('--variant', default='release', - help='variant name for saving configuration and build results. Variants other than "debug" turn on -O3') - -def download_and_extract(destdir, url, ext): - dest = destdir + ext - - # Extract the tarball. - if not os.path.isdir(os.path.join(destdir, 'include')): - # Download and save the compressed tarball as |compressed_file_name|. - if not os.path.isfile(dest): - print('Downloading tarball') - print(' destination: {0}'.format(dest)) - print(' source: {0}'.format(url)) - # TODO: verify checksum - response = urlopen(url, timeout=60) - with open(dest, 'wb') as f: - f.write(response.read()) - else: - print('Found tarball at {0}'.format(dest)) - - print('Extracting {0}'.format(dest)) - # TODO: make portable. - if ext == '.exe': - subprocess.call(['7z', 'x', '-o{0}'.format(destdir), '-xr!$PLUGINSDIR', dest]) - else: - subprocess.call(['tar', '-x', '-C', out, '-f', dest]) - # TODO Remove after migrating to a clang release newer than 5.0.1 - # For 5.0.1 Mac OS X, the directory and the tarball have different name - if destdir == 'build/clang+llvm-5.0.1-x86_64-apple-darwin': - os.rename(destdir.replace('5.0.1', '5.0.1-final'), destdir) - # TODO Byte patch hack for other prebuilt binaries - elif destdir == 'build/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-14.04': - patch_byte_in_libclang(os.path.join(destdir, 'lib/libclang.so.4.0'), - 0x4172b5, b'\x48', b'\x4d') - elif destdir == 'build/clang+llvm-5.0.1-x86_64-linux-gnu-ubuntu-14.04': - patch_byte_in_libclang(os.path.join(destdir, 'lib/libclang.so.5.0'), - 0x47aece, b'\x48', b'\x4d') - else: - print('Found extracted at {0}'.format(destdir)) - -def copy_or_symlink(src, dst): - print('copy_or_symlink src={0}, dst={1}'.format(src, dst)) - try: - os.makedirs(os.path.dirname(dst)) - except OSError: - pass - - try: - os.symlink(src, dst) - except (OSError, NotImplementedError): - shutil.copy(src, dst) - -def configure(ctx): - ctx.resetenv(ctx.options.variant) - - ctx.load('compiler_cxx') - if ctx.env.CXX_NAME == 'msvc': - # /Zi: -g, /WX: -Werror, /W3: roughly -Wall, there is no -std=c++11 equivalent in MSVC. - # /wd4722: ignores warning C4722 (destructor never returns) in loguru - # /wd4267: ignores warning C4267 (conversion from 'size_t' to 'type'), roughly -Wno-sign-compare - # /MD: use multithread c library from DLL - cxxflags = ['/nologo', '/FS', '/EHsc', '/Zi', '/W3', '/wd4996', '/wd4722', '/wd4267', '/wd4800', '/MD'] - ldflags = [] - if 'release' in ctx.options.variant: - cxxflags.append('/O2') # There is no O3 - else: - cxxflags += ['/Zi', '/FS'] - ldflags += ['/DEBUG'] - else: - # So that dladdr() called in loguru.hpp gives symbol names in main executable - ldflags = ['-rdynamic'] - if ctx.env.CXXFLAGS: - cxxflags = ctx.env.CXXFLAGS - else: - cxxflags = ['-g', '-Wall', '-Wno-sign-compare'] - if ctx.env.CXX_NAME == 'gcc': - cxxflags.append('-Wno-return-type') - # Otherwise (void)write(...) => -Werror=unused-result - cxxflags.append('-Wno-unused-result') - - if all(not x.startswith('-std=') for x in ctx.env.CXXFLAGS): - cxxflags.append('-std=c++14') - - if ctx.options.use_clang_cxx: - # include/clang/Format/Format.h error: multi-line comment - cxxflags.append('-Wno-comment') - # Without -fno-rtti, some Clang C++ functions may report `undefined references to typeinfo` - cxxflags.append('-fno-rtti') - - if 'asan' in ctx.options.variant: - cxxflags.append('-fsanitize=address,undefined') - ldflags.append('-fsanitize=address,undefined') - if 'release' in ctx.options.variant: - cxxflags.append('-O' if 'asan' in ctx.options.variant else '-O3') - - if ctx.options.enable_assert is None: - if 'debug' in ctx.options.variant: - ctx.options.enable_assert = True - if not ctx.options.enable_assert: - ctx.define('NDEBUG', 1) - - if ctx.env.CXX_NAME == 'clang' and 'debug' in ctx.options.variant: - cxxflags.append('-fno-limit-debug-info') - - ctx.env.CXXFLAGS = cxxflags - if not ctx.env.LDFLAGS: - ctx.env.LDFLAGS = ldflags - - ctx.check(header_name='stdio.h', features='cxx cxxprogram', mandatory=True) - - ctx.load('clang_compilation_database', tooldir='.') - - ctx.env['use_clang_cxx'] = ctx.options.use_clang_cxx - ctx.env['llvm_config'] = ctx.options.llvm_config - ctx.env['bundled_clang'] = ctx.options.bundled_clang - def libname(lib): - # Newer MinGW and MSVC both wants full file name - if sys.platform == 'win32': - return 'lib' + lib - return lib - - # Do not use bundled clang+llvm - if ctx.options.llvm_config is not None or ctx.options.clang_prefix is not None: - if ctx.options.llvm_config is not None: - # Ask llvm-config for cflags and ldflags - ctx.find_program(ctx.options.llvm_config, msg='checking for llvm-config', var='LLVM_CONFIG', mandatory=False) - - ctx.env.rpath = [str(subprocess.check_output( - [ctx.options.llvm_config, '--libdir'], - stderr=subprocess.STDOUT).decode()).strip()] - - if ctx.options.clang_prefix: - ctx.start_msg('Checking for clang prefix') - prefix = ctx.root.find_node(ctx.options.clang_prefix) - if not prefix: - raise ctx.errors.ConfigurationError('clang prefix not found: "%s"'%ctx.options.clang_prefix) - ctx.end_msg(prefix) - - includes = [ n.abspath() for n in [ prefix.find_node('include') ] if n ] - libpath = [ n.abspath() for n in [ prefix.find_node(l) for l in ('lib', 'lib64')] if n ] - ctx.check_cxx(msg='Checking for library clang', lib=libname('clang'), uselib_store='clang', includes=includes, libpath=libpath) - else: - ctx.check_cfg(msg='Checking for clang flags', - path=ctx.env.LLVM_CONFIG, - package='', - uselib_store='clang', - args='--cppflags --ldflags') - # llvm-config does not provide the actual library we want so we check for it - # using the provided info so far. - ctx.check_cxx(lib=libname('clang'), uselib_store='clang', use='clang') - - - # Use CXX set by --check-cxx-compiler if it is "clang". - # See https://github.com/jacobdufault/cquery/issues/237 - clang = ctx.env.get_flat('CXX') - if 'clang' not in clang: - # Otherwise, infer the clang executable path with llvm-config --bindir - output = str(subprocess.check_output( - [ctx.options.llvm_config, '--bindir'], - stderr=subprocess.STDOUT).decode()).strip() - clang = os.path.join(output, 'clang') - - # Use the detected clang executable to infer resource directory - # Use `clang -### -xc /dev/null` instead of `clang -print-resource-dir` because the option is unavailable in 4.0.0 - devnull = '/dev/null' if sys.platform != 'win32' else 'NUL' - output = str(subprocess.check_output( - [clang, '-###', '-xc', devnull], - stderr=subprocess.STDOUT).decode()) - match = re.search(r'"-resource-dir" "([^"]*)"', output, re.M) - if match: - ctx.env.default_resource_directory = match.group(1) - else: - bld.fatal("Failed to found system clang resource directory.") - - else: - global CLANG_TARBALL_NAME, CLANG_TARBALL_EXT - - if CLANG_TARBALL_NAME is None: - sys.stderr.write('ERROR: releases.llvm.org does not provide prebuilt binary for your platform {0}\n'.format(sys.platform)) - sys.exit(1) - - # TODO Remove after 5.0.1 is stable - if ctx.options.bundled_clang == '5.0.0' and sys.platform.startswith('linux'): - CLANG_TARBALL_NAME = 'clang+llvm-$version-linux-x86_64-ubuntu14.04' - - CLANG_TARBALL_NAME = string.Template(CLANG_TARBALL_NAME).substitute(version=ctx.options.bundled_clang) - # Directory clang has been extracted to. - CLANG_DIRECTORY = '{0}/{1}'.format(out, CLANG_TARBALL_NAME) - # URL of the tarball to download. - CLANG_TARBALL_URL = 'http://releases.llvm.org/{0}/{1}{2}'.format(ctx.options.bundled_clang, CLANG_TARBALL_NAME, CLANG_TARBALL_EXT) - - print('Checking for clang') - download_and_extract(CLANG_DIRECTORY, CLANG_TARBALL_URL, CLANG_TARBALL_EXT) - - bundled_clang_dir = os.path.join(out, ctx.options.variant, 'lib', CLANG_TARBALL_NAME) - try: - os.makedirs(os.path.dirname(bundled_clang_dir)) - except OSError: - pass - clang_dir = os.path.normpath('../../' + CLANG_TARBALL_NAME) - try: - if not os.path.exists(bundled_clang_dir): - os.symlink(clang_dir, bundled_clang_dir, target_is_directory=True) - except (OSError, NotImplementedError): - # Copying the whole directory instead. - print ('shutil.copytree({0}, {1})'.format(os.path.join(out, CLANG_TARBALL_NAME), bundled_clang_dir)) - try: - shutil.copytree(os.path.join(out, CLANG_TARBALL_NAME), bundled_clang_dir) - except Exception as e: - print('Failed to copy tree, ', e) - - clang_node = ctx.path.find_dir(bundled_clang_dir) - ctx.check_cxx(uselib_store='clang', - includes=clang_node.find_dir('include').abspath(), - libpath=clang_node.find_dir('lib').abspath(), - lib=libname('clang')) - - clang_tarball_name = os.path.basename(os.path.dirname(ctx.env['LIBPATH_clang'][0])) - ctx.env.clang_tarball_name = clang_tarball_name - ctx.env.default_resource_directory = '../lib/{}/lib/clang/{}'.format(clang_tarball_name, ctx.env.bundled_clang) - - if sys.platform.startswith('freebsd') or sys.platform.startswith('linux'): - ctx.env.rpath = ['$ORIGIN/../lib/' + clang_tarball_name + '/lib'] - elif sys.platform == 'darwin': - ctx.env.rpath = ['@loader_path/../lib/' + clang_tarball_name + '/lib'] - elif sys.platform == 'win32': - # Poor Windows users' RPATH - copy libclang.lib and libclang.dll to the build directory. - ctx.env.rpath = [] # Unsupported - clang_dir = os.path.dirname(ctx.env['LIBPATH_clang'][0]) - dest_dir = os.path.join(ctx.path.get_bld().abspath(), ctx.options.variant, 'bin') - # copy_or_symlink(os.path.join(clang_dir, 'lib', 'libclang.lib'), os.path.join(dest_dir, 'libclang.lib')) - copy_or_symlink(os.path.join(clang_dir, 'bin', 'libclang.dll'), os.path.join(dest_dir, 'libclang.dll')) - else: - ctx.env.rpath = ctx.env['LIBPATH_clang'] - - ctx.msg('Clang includes', ctx.env.INCLUDES_clang) - ctx.msg('Clang library dir', ctx.env.LIBPATH_clang) - -def build(bld): - cc_files = bld.path.ant_glob(['src/*.cc', 'src/messages/*.cc', 'third_party/*.cc']) - if bld.env['use_clang_cxx']: - cc_files += bld.path.ant_glob(['src/clang_cxx/*.cc']) - - lib = [] - if sys.platform.startswith('linux'): - # For __atomic_* when lock free instructions are unavailable - # (either through hardware or OS support) - lib.append('pthread') - # loguru calls dladdr - lib.append('dl') - elif sys.platform.startswith('freebsd'): - # loguru::stacktrace_as_stdstring calls backtrace_symbols - lib.append('execinfo') - # sparsepp/spp_memory.h uses libkvm - lib.append('kvm') - - lib.append('pthread') - lib.append('thr') - elif sys.platform == 'darwin': - lib.append('pthread') - elif sys.platform == 'msys': - lib.append('psapi') # GetProcessMemoryInfo - - if bld.env['use_clang_cxx']: - # -fno-rtti is required for object files using clang/llvm C++ API - - # The order is derived by topological sorting LINK_LIBS in clang/lib/*/CMakeLists.txt - lib.append('clangFormat') - lib.append('clangToolingCore') - lib.append('clangRewrite') - lib.append('clangAST') - lib.append('clangLex') - lib.append('clangBasic') - - # The order is derived from llvm-config --libs core - lib.append('LLVMCore') - lib.append('LLVMBinaryFormat') - lib.append('LLVMSupport') - lib.append('LLVMDemangle') - - lib.append('ncurses') - - # https://waf.io/apidocs/tools/c_aliases.html#waflib.Tools.c_aliases.program - bld.program( - source=cc_files, - use=['clang'], - includes=[ - 'src/', - 'third_party/', - 'third_party/doctest/', - 'third_party/loguru/', - 'third_party/msgpack-c/include', - 'third_party/rapidjson/include/', - 'third_party/sparsepp/'] + - (['libclang'] if bld.env['use_clang_cxx'] else []), - defines=[ - 'LOGURU_WITH_STREAMS=1', - 'LOGURU_FILENAME_WIDTH=18', - 'LOGURU_THREADNAME_WIDTH=13', - 'DEFAULT_RESOURCE_DIRECTORY="' + bld.env.get_flat('default_resource_directory') + '"'] + - (['USE_CLANG_CXX=1', 'LOGURU_RTTI=0'] - if bld.env['use_clang_cxx'] - else []), - lib=lib, - rpath=bld.env.rpath, - target='bin/cquery') - - if bld.cmd == 'install' and 'clang_tarball_name' in bld.env: - clang_tarball_name = bld.env.clang_tarball_name - if sys.platform != 'win32': - bld.install_files('${PREFIX}/lib/' + clang_tarball_name + '/lib', bld.path.get_bld().ant_glob('lib/' + clang_tarball_name + '/lib/libclang.(dylib|so.[4-9])', quiet=True)) - # TODO This may be cached and cannot be re-triggered. Use proper shell escape. - bld(rule='rsync -rtR {}/./lib/{}/lib/clang/*/include {}/'.format(bld.path.get_bld(), clang_tarball_name, bld.env['PREFIX'])) - -def init(ctx): - from waflib.Build import BuildContext, CleanContext, InstallContext, UninstallContext - for y in (BuildContext, CleanContext, InstallContext, UninstallContext): - class tmp(y): - variant = ctx.options.variant - - # This is needed because waf initializes the ConfigurationContext with - # an arbitrary setenv('') which would rewrite the previous configuration - # cache for the default variant if the configure step finishes. - # Ideally ConfigurationContext should just let us override this at class - # level like the other Context subclasses do with variant - from waflib.Configure import ConfigurationContext - class cctx(ConfigurationContext): - def resetenv(self, name): - self.all_envs = {} - self.setenv(name)