mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-26 17:41:58 +00:00
.
This commit is contained in:
parent
da649891ae
commit
89dd4b066b
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
||||
build
|
||||
debug
|
||||
release
|
||||
/compile_commands.json
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -1 +0,0 @@
|
||||
build/release/compile_commands.json
|
@ -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"
|
||||
|
@ -2,10 +2,10 @@
|
||||
|
||||
#include "clang_utils.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
|
||||
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<std::mutex> lock(mutex_);
|
||||
|
||||
cx_index =
|
||||
clang_createIndex(exclude_declarations_from_pch, display_diagnostics);
|
||||
}
|
||||
|
||||
ClangIndex::~ClangIndex() {
|
||||
clang_disposeIndex(cx_index);
|
||||
}
|
||||
|
@ -114,3 +114,14 @@ struct hash<ClangCursor> {
|
||||
}
|
||||
};
|
||||
} // 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;
|
||||
};
|
||||
|
@ -1,117 +0,0 @@
|
||||
#if USE_CLANG_CXX
|
||||
|
||||
#include "clang_format.h"
|
||||
#include "working_files.h"
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
#include <loguru.hpp>
|
||||
|
||||
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<tooling::Replacement> 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<FormatStyle> 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>(tooling::Range(start, end - start)),
|
||||
working_file->filename);
|
||||
return std::vector<tooling::Replacement>(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
|
||||
//
|
||||
// <?xml version='1.0'?>
|
||||
// <replacements xml:space='preserve' incomplete_format='false'>
|
||||
// <replacement offset='12' length='1'> </replacement>
|
||||
// <replacement offset='16' length='1'></replacement>
|
||||
// <replacement offset='18' length='0'> </replacement>
|
||||
// <replacement offset='24' length='1'> </replacement>
|
||||
// <replacement offset='34' length='1'> </replacement>
|
||||
// </replacements>
|
||||
|
||||
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
|
@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#if USE_CLANG_CXX
|
||||
|
||||
#include "lsp.h"
|
||||
#include "working_files.h"
|
||||
|
||||
#include <clang/Format/Format.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
std::vector<clang::tooling::Replacement> ClangFormatDocument(
|
||||
WorkingFile* working_file,
|
||||
int start,
|
||||
int end,
|
||||
lsFormattingOptions options);
|
||||
|
||||
#endif
|
@ -1,22 +0,0 @@
|
||||
#include "clang_index.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
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<std::mutex> lock(mutex_);
|
||||
|
||||
cx_index =
|
||||
clang_createIndex(exclude_declarations_from_pch, display_diagnostics);
|
||||
}
|
||||
|
||||
ClangIndex::~ClangIndex() {
|
||||
clang_disposeIndex(cx_index);
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
|
||||
// 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;
|
||||
};
|
@ -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<const char*> 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());
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "clang_cursor.h"
|
||||
#include "clang_index.h"
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
|
||||
|
@ -2,8 +2,6 @@
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
namespace {
|
||||
|
||||
lsRange GetLsRangeForFixIt(const CXSourceRange& range) {
|
||||
@ -106,31 +104,6 @@ std::optional<lsDiagnostic> 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<lsTextEdit> ConvertClangReplacementsIntoTextEdits(
|
||||
llvm::StringRef document,
|
||||
const std::vector<clang::tooling::Replacement>& clang_replacements) {
|
||||
std::vector<lsTextEdit> 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
|
||||
}
|
||||
}
|
||||
|
||||
#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::tooling::Replacement> 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
|
||||
|
@ -3,11 +3,8 @@
|
||||
#include "lsp_diagnostic.h"
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
#if USE_CLANG_CXX
|
||||
#include <clang/Format/Format.h>
|
||||
#endif
|
||||
#include <optional>
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
std::optional<lsDiagnostic> 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<lsTextEdit> ConvertClangReplacementsIntoTextEdits(
|
||||
llvm::StringRef document,
|
||||
const std::vector<clang::tooling::Replacement>& clang_replacements);
|
||||
#endif
|
||||
|
@ -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 <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
// 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<std::string> 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 <opt_filter_path>
|
||||
Run index tests. opt_filter_path can be used to specify which
|
||||
@ -85,37 +71,20 @@ Other command line options:
|
||||
--init <initializationOptions>
|
||||
Override client provided initialization options
|
||||
https://github.com/cquery-project/cquery/wiki/Initialization-options
|
||||
--record <path>
|
||||
Writes stdin to <path>.in and stdout to <path>.out
|
||||
--log-file <path> Logging file for diagnostics
|
||||
--log-file-append <path> 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<MethodType, Timer>* 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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
struct IndexFile;
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "lsp.h"
|
||||
|
||||
#include "recorder.h"
|
||||
#include "serializers/json.h"
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
@ -67,8 +66,6 @@ std::optional<std::string> ReadJsonRpcContentFrom(
|
||||
content += *c;
|
||||
}
|
||||
|
||||
RecordInput(content);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include "project.h"
|
||||
#include "query_utils.h"
|
||||
#include "queue_manager.h"
|
||||
#include "semantic_highlight_symbol_cache.h"
|
||||
|
||||
#include <loguru.hpp>
|
||||
|
||||
@ -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<int> 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<int> 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>& entry) {
|
||||
std::optional<int> 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<GroupMatch>(config->highlight.whitelist,
|
||||
config->highlight.blacklist);
|
||||
}
|
||||
|
||||
std::shared_ptr<SemanticHighlightSymbolCache::Entry>
|
||||
SemanticHighlightSymbolCache::GetCacheForFile(const std::string& path) {
|
||||
return cache_.Get(
|
||||
path, [&, this]() { return std::make_shared<Entry>(this, path); });
|
||||
}
|
||||
|
||||
MessageHandler::MessageHandler() {
|
||||
// Dynamically allocate |message_handlers|, otherwise there will be static
|
||||
// initialization order races.
|
||||
|
@ -1,12 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "lru_cache.h"
|
||||
#include "lsp.h"
|
||||
#include "match.h"
|
||||
#include "method.h"
|
||||
#include "query.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
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<std::string, int>;
|
||||
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<int> 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<std::string, Entry> cache_;
|
||||
uint32_t next_stable_id_ = 0;
|
||||
std::unique_ptr<GroupMatch> match_;
|
||||
|
||||
SemanticHighlightSymbolCache();
|
||||
void Init(Config*);
|
||||
std::shared_ptr<Entry> GetCacheForFile(const std::string& path);
|
||||
};
|
||||
|
||||
struct Out_CclsPublishSemanticHighlighting
|
||||
: public lsOutMessage<Out_CclsPublishSemanticHighlighting> {
|
||||
struct Symbol {
|
||||
|
@ -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<lsWorkspaceClientCapabilites> workspace;
|
||||
lsWorkspaceClientCapabilites workspace;
|
||||
|
||||
// Text document specific client capabilities.
|
||||
std::optional<lsTextDocumentClientCapabilities> 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<In_InitializeRequest> {
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
@ -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);
|
||||
|
@ -1,56 +0,0 @@
|
||||
#include "recorder.h"
|
||||
|
||||
#include <loguru.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
|
||||
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();
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <string>
|
||||
|
||||
void EnableRecording(std::string path);
|
||||
void RecordInput(std::string_view content);
|
||||
void RecordOutput(std::string_view content);
|
@ -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<int> 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<int> 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>& entry) {
|
||||
std::optional<int> 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<GroupMatch>(config->highlight.whitelist,
|
||||
config->highlight.blacklist);
|
||||
}
|
||||
|
||||
std::shared_ptr<SemanticHighlightSymbolCache::Entry>
|
||||
SemanticHighlightSymbolCache::GetCacheForFile(const std::string& path) {
|
||||
return cache_.Get(
|
||||
path, [&, this]() { return std::make_shared<Entry>(this, path); });
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "lru_cache.h"
|
||||
#include "match.h"
|
||||
#include "query.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
// 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<std::string, int>;
|
||||
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<int> 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<std::string, Entry> cache_;
|
||||
uint32_t next_stable_id_ = 0;
|
||||
std::unique_ptr<GroupMatch> match_;
|
||||
|
||||
SemanticHighlightSymbolCache();
|
||||
void Init(Config*);
|
||||
std::shared_ptr<Entry> GetCacheForFile(const std::string& path);
|
||||
};
|
152
src/task.cc
152
src/task.cc
@ -1,152 +0,0 @@
|
||||
#if false
|
||||
#include "task.h"
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
TaskManager::TaskManager() {
|
||||
pending_tasks_[TaskThread::Indexer] = std::make_unique<TaskQueue>();
|
||||
pending_tasks_[TaskThread::QueryDb] = std::make_unique<TaskQueue>();
|
||||
}
|
||||
|
||||
void TaskManager::Post(TaskThread thread, const TTask& task) {
|
||||
TaskQueue* queue = pending_tasks_[thread].get();
|
||||
std::lock_guard<std::mutex> 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<std::mutex> 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<std::chrono::duration<long long, std::nano>> max_time) {
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
TaskQueue* queue = pending_tasks_[thread].get();
|
||||
|
||||
bool ran_task = false;
|
||||
|
||||
while (true) {
|
||||
std::optional<TTask> task;
|
||||
|
||||
// Get a task.
|
||||
{
|
||||
std::lock_guard<std::mutex> 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
|
41
src/task.h
41
src/task.h
@ -1,41 +0,0 @@
|
||||
#if false
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
enum class TaskThread {
|
||||
Indexer,
|
||||
QueryDb,
|
||||
};
|
||||
|
||||
struct TaskManager {
|
||||
using TTask = std::function<void()>;
|
||||
using TIdleTask = std::function<bool()>;
|
||||
|
||||
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<std::chrono::duration<long long, std::nano>> max_time);
|
||||
|
||||
struct TaskQueue {
|
||||
std::optional<TIdleTask> idle_task;
|
||||
std::vector<TTask> tasks;
|
||||
std::mutex tasks_mutex;
|
||||
};
|
||||
|
||||
std::unordered_map<TaskThread, std::unique_ptr<TaskQueue>> pending_tasks_;
|
||||
};
|
||||
#endif
|
424
wscript
424
wscript
@ -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)
|
Loading…
Reference in New Issue
Block a user