mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-21 23:25:07 +00:00
.
This commit is contained in:
parent
b839389f77
commit
f7872d143d
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
build
|
build
|
||||||
debug
|
debug
|
||||||
release
|
release
|
||||||
|
/compile_commands.json
|
||||||
|
@ -170,8 +170,6 @@ target_sources(ccls PRIVATE
|
|||||||
src/cache_manager.cc
|
src/cache_manager.cc
|
||||||
src/clang_complete.cc
|
src/clang_complete.cc
|
||||||
src/clang_cursor.cc
|
src/clang_cursor.cc
|
||||||
src/clang_format.cc
|
|
||||||
src/clang_index.cc
|
|
||||||
src/clang_indexer.cc
|
src/clang_indexer.cc
|
||||||
src/clang_translation_unit.cc
|
src/clang_translation_unit.cc
|
||||||
src/clang_utils.cc
|
src/clang_utils.cc
|
||||||
@ -201,11 +199,8 @@ target_sources(ccls PRIVATE
|
|||||||
src/query_utils.cc
|
src/query_utils.cc
|
||||||
src/query.cc
|
src/query.cc
|
||||||
src/queue_manager.cc
|
src/queue_manager.cc
|
||||||
src/recorder.cc
|
|
||||||
src/semantic_highlight_symbol_cache.cc
|
|
||||||
src/serializer.cc
|
src/serializer.cc
|
||||||
src/standard_includes.cc
|
src/standard_includes.cc
|
||||||
src/task.cc
|
|
||||||
src/test.cc
|
src/test.cc
|
||||||
src/third_party_impl.cc
|
src/third_party_impl.cc
|
||||||
src/timer.cc
|
src/timer.cc
|
||||||
@ -242,9 +237,7 @@ target_sources(ccls PRIVATE
|
|||||||
src/messages/text_document_document_highlight.cc
|
src/messages/text_document_document_highlight.cc
|
||||||
src/messages/text_document_document_link.cc
|
src/messages/text_document_document_link.cc
|
||||||
src/messages/text_document_document_symbol.cc
|
src/messages/text_document_document_symbol.cc
|
||||||
src/messages/text_document_formatting.cc
|
|
||||||
src/messages/text_document_hover.cc
|
src/messages/text_document_hover.cc
|
||||||
src/messages/text_document_range_formatting.cc
|
|
||||||
src/messages/text_document_references.cc
|
src/messages/text_document_references.cc
|
||||||
src/messages/text_document_rename.cc
|
src/messages/text_document_rename.cc
|
||||||
src/messages/text_document_signature_help.cc
|
src/messages/text_document_signature_help.cc
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# ccls
|
# 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)
|
* code completion (with both signature help and snippets)
|
||||||
* finding [definition](src/messages/text_document_definition.cc)/[references](src/messages/text_document_references.cc)
|
* 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
|
#pragma once
|
||||||
|
|
||||||
#include "clang_index.h"
|
#include "clang_cursor.h"
|
||||||
#include "clang_translation_unit.h"
|
#include "clang_translation_unit.h"
|
||||||
#include "lru_cache.h"
|
#include "lru_cache.h"
|
||||||
#include "lsp_completion.h"
|
#include "lsp_completion.h"
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
#include "clang_utils.h"
|
#include "clang_utils.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <mutex>
|
||||||
|
|
||||||
Range ResolveCXSourceRange(const CXSourceRange& range, CXFile* cx_file) {
|
Range ResolveCXSourceRange(const CXSourceRange& range, CXFile* cx_file) {
|
||||||
CXSourceLocation start = clang_getRangeStart(range);
|
CXSourceLocation start = clang_getRangeStart(range);
|
||||||
@ -284,3 +284,22 @@ NtString ClangCursor::get_comments() const {
|
|||||||
std::string ClangCursor::ToString() const {
|
std::string ClangCursor::ToString() const {
|
||||||
return ::ToString(get_kind()) + " " + get_spell_name();
|
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
|
} // 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,
|
SetVarDetail(var, std::string(decl->entityInfo->name), decl->cursor,
|
||||||
decl->semanticContainer, !decl->isRedeclaration, db, param);
|
decl->semanticContainer, !decl->isRedeclaration, db, param);
|
||||||
|
|
||||||
// FIXME https://github.com/jacobdufault/ccls/issues/239
|
|
||||||
var->def.kind = GetSymbolKind(decl->entityInfo->kind);
|
var->def.kind = GetSymbolKind(decl->entityInfo->kind);
|
||||||
if (var->def.kind == lsSymbolKind::Variable &&
|
if (var->def.kind == lsSymbolKind::Variable &&
|
||||||
decl->cursor.kind == CXCursor_ParmDecl)
|
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
|
// For Typedef/CXXTypeAlias spanning a few lines, display the declaration
|
||||||
// line, with spelling name replaced with qualified name.
|
// 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 <
|
if (extent.end.line - extent.start.line <
|
||||||
kMaxLinesDisplayTypeAliasDeclarations) {
|
kMaxLinesDisplayTypeAliasDeclarations) {
|
||||||
FileContents& fc = param->file_contents[db->path];
|
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
|
// Type-dependent member access expressions do not have accurate spelling
|
||||||
// ranges.
|
// ranges.
|
||||||
//
|
//
|
||||||
@ -2328,56 +2324,6 @@ void IndexInit() {
|
|||||||
clang_toggleCrashRecovery(1);
|
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() {
|
std::string GetClangVersion() {
|
||||||
return ToString(clang_getClangVersion());
|
return ToString(clang_getClangVersion());
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "clang_cursor.h"
|
#include "clang_cursor.h"
|
||||||
#include "clang_index.h"
|
|
||||||
|
|
||||||
#include <clang-c/Index.h>
|
#include <clang-c/Index.h>
|
||||||
|
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
#include <doctest/doctest.h>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
lsRange GetLsRangeForFixIt(const CXSourceRange& range) {
|
lsRange GetLsRangeForFixIt(const CXSourceRange& range) {
|
||||||
@ -106,31 +104,6 @@ std::optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,
|
|||||||
return ls_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) {
|
std::string FileName(CXFile file) {
|
||||||
CXString cx_name = clang_getFileName(file);
|
CXString cx_name = clang_getFileName(file);
|
||||||
std::string name = ToString(cx_name);
|
std::string name = ToString(cx_name);
|
||||||
@ -172,60 +145,9 @@ const char* ClangBuiltinTypeName(CXTypeKind kind) {
|
|||||||
case CXType_Double: return "double";
|
case CXType_Double: return "double";
|
||||||
case CXType_LongDouble: return "long double";
|
case CXType_LongDouble: return "long double";
|
||||||
case CXType_Float128: return "__float128";
|
case CXType_Float128: return "__float128";
|
||||||
#if CINDEX_VERSION_MINOR >= 43
|
|
||||||
case CXType_Half: return "_Float16";
|
case CXType_Half: return "_Float16";
|
||||||
#endif
|
|
||||||
case CXType_NullPtr: return "nullptr";
|
case CXType_NullPtr: return "nullptr";
|
||||||
default: return "";
|
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::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 "lsp_diagnostic.h"
|
||||||
|
|
||||||
#include <clang-c/Index.h>
|
#include <clang-c/Index.h>
|
||||||
#if USE_CLANG_CXX
|
|
||||||
#include <clang/Format/Format.h>
|
|
||||||
#endif
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
std::optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,
|
std::optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,
|
||||||
@ -21,10 +18,3 @@ std::string ToString(CXString cx_string);
|
|||||||
std::string ToString(CXCursorKind cursor_kind);
|
std::string ToString(CXCursorKind cursor_kind);
|
||||||
|
|
||||||
const char* ClangBuiltinTypeName(CXTypeKind);
|
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.h"
|
||||||
#include "query_utils.h"
|
#include "query_utils.h"
|
||||||
#include "queue_manager.h"
|
#include "queue_manager.h"
|
||||||
#include "recorder.h"
|
|
||||||
#include "semantic_highlight_symbol_cache.h"
|
|
||||||
#include "serializer.h"
|
#include "serializer.h"
|
||||||
#include "serializers/json.h"
|
#include "serializers/json.h"
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
@ -43,19 +41,10 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#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;
|
std::string g_init_options;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::vector<std::string> kEmptyArgs;
|
|
||||||
|
|
||||||
// This function returns true if e2e timing should be displayed for the given
|
// This function returns true if e2e timing should be displayed for the given
|
||||||
// MethodId.
|
// MethodId.
|
||||||
bool ShouldDisplayMethodTiming(MethodType type) {
|
bool ShouldDisplayMethodTiming(MethodType type) {
|
||||||
@ -70,9 +59,6 @@ void PrintHelp() {
|
|||||||
<< R"help(ccls is a low-latency C/C++/Objective-C language server.
|
<< R"help(ccls is a low-latency C/C++/Objective-C language server.
|
||||||
|
|
||||||
Mode:
|
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-unit Run unit tests.
|
||||||
--test-index <opt_filter_path>
|
--test-index <opt_filter_path>
|
||||||
Run index tests. opt_filter_path can be used to specify which
|
Run index tests. opt_filter_path can be used to specify which
|
||||||
@ -85,37 +71,20 @@ Other command line options:
|
|||||||
--init <initializationOptions>
|
--init <initializationOptions>
|
||||||
Override client provided initialization options
|
Override client provided initialization options
|
||||||
https://github.com/cquery-project/cquery/wiki/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 <path> Logging file for diagnostics
|
||||||
--log-file-append <path> Like --log-file, but appending
|
--log-file-append <path> Like --log-file, but appending
|
||||||
--log-all-to-stderr Write all log messages to STDERR.
|
--log-all-to-stderr Write all log messages to STDERR.
|
||||||
--wait-for-input Wait for an '[Enter]' before exiting
|
|
||||||
--help Print this help information.
|
--help Print this help information.
|
||||||
--ci Prevents tests from prompting the user for input. Used for
|
--ci Prevents tests from prompting the user for input. Used for
|
||||||
continuous integration so it can fail faster instead of timing
|
continuous integration so it can fail faster instead of timing
|
||||||
out.
|
out.
|
||||||
|
|
||||||
See more on https://github.com/cquery-project/cquery/wiki
|
See more on https://github.com/MaskRay/ccls/wiki
|
||||||
)help";
|
)help";
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// QUERYDB MAIN ////////////////////////////////////////////////////////////////
|
// QUERYDB MAIN ////////////////////////////////////////////////////////////////
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -248,20 +217,6 @@ void RunQueryDbThread(const std::string& bin_name,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// STDIN MAIN //////////////////////////////////////////////////////////////////
|
// STDIN MAIN //////////////////////////////////////////////////////////////////
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -333,8 +288,6 @@ void LaunchStdoutThread(std::unordered_map<MethodType, Timer>* request_times,
|
|||||||
time.ResetAndPrint("[e2e] Running " + std::string(message.method));
|
time.ResetAndPrint("[e2e] Running " + std::string(message.method));
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordOutput(message.content);
|
|
||||||
|
|
||||||
fwrite(message.content.c_str(), message.content.size(), 1, stdout);
|
fwrite(message.content.c_str(), message.content.size(), 1, stdout);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
@ -360,23 +313,6 @@ void LanguageServerMain(const std::string& bin_name,
|
|||||||
RunQueryDbThread(bin_name, config, querydb_waiter, indexer_waiter);
|
RunQueryDbThread(bin_name, config, querydb_waiter, indexer_waiter);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MAIN ////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
TraceMe();
|
TraceMe();
|
||||||
|
|
||||||
@ -413,14 +349,6 @@ int main(int argc, char** argv) {
|
|||||||
loguru::Verbosity_MAX);
|
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")) {
|
if (HasOption(options, "--test-unit")) {
|
||||||
language_server = false;
|
language_server = false;
|
||||||
doctest::Context context;
|
doctest::Context context;
|
||||||
@ -467,10 +395,5 @@ int main(int argc, char** argv) {
|
|||||||
&stdout_waiter);
|
&stdout_waiter);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasOption(options, "--wait-for-input")) {
|
|
||||||
std::cerr << std::endl << "[Enter] to exit" << std::endl;
|
|
||||||
getchar();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -168,8 +168,6 @@ void IncludeComplete::AddFile(const std::string& absolute_path) {
|
|||||||
if (is_scanning)
|
if (is_scanning)
|
||||||
lock.lock();
|
lock.lock();
|
||||||
InsertCompletionItem(absolute_path, std::move(item));
|
InsertCompletionItem(absolute_path, std::move(item));
|
||||||
if (lock)
|
|
||||||
lock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IncludeComplete::InsertIncludesFromDirectory(std::string directory,
|
void IncludeComplete::InsertIncludesFromDirectory(std::string directory,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "clang_cursor.h"
|
#include "clang_cursor.h"
|
||||||
#include "clang_index.h"
|
|
||||||
#include "clang_translation_unit.h"
|
#include "clang_translation_unit.h"
|
||||||
#include "clang_utils.h"
|
#include "clang_utils.h"
|
||||||
#include "file_consumer.h"
|
#include "file_consumer.h"
|
||||||
@ -16,14 +15,13 @@
|
|||||||
#include "symbol.h"
|
#include "symbol.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <unordered_map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
struct IndexFile;
|
struct IndexFile;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#include "lsp.h"
|
#include "lsp.h"
|
||||||
|
|
||||||
#include "recorder.h"
|
|
||||||
#include "serializers/json.h"
|
#include "serializers/json.h"
|
||||||
|
|
||||||
#include <doctest/doctest.h>
|
#include <doctest/doctest.h>
|
||||||
@ -67,8 +66,6 @@ std::optional<std::string> ReadJsonRpcContentFrom(
|
|||||||
content += *c;
|
content += *c;
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordInput(content);
|
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#include "project.h"
|
#include "project.h"
|
||||||
#include "query_utils.h"
|
#include "query_utils.h"
|
||||||
#include "queue_manager.h"
|
#include "queue_manager.h"
|
||||||
#include "semantic_highlight_symbol_cache.h"
|
|
||||||
|
|
||||||
#include <loguru.hpp>
|
#include <loguru.hpp>
|
||||||
|
|
||||||
@ -42,6 +41,77 @@ struct ScanLineEvent {
|
|||||||
};
|
};
|
||||||
} // namespace
|
} // 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() {
|
MessageHandler::MessageHandler() {
|
||||||
// Dynamically allocate |message_handlers|, otherwise there will be static
|
// Dynamically allocate |message_handlers|, otherwise there will be static
|
||||||
// initialization order races.
|
// initialization order races.
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "lru_cache.h"
|
||||||
#include "lsp.h"
|
#include "lsp.h"
|
||||||
|
#include "match.h"
|
||||||
#include "method.h"
|
#include "method.h"
|
||||||
#include "query.h"
|
#include "query.h"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
struct ClangCompleteManager;
|
struct ClangCompleteManager;
|
||||||
@ -20,11 +22,43 @@ struct IncludeComplete;
|
|||||||
struct MultiQueueWaiter;
|
struct MultiQueueWaiter;
|
||||||
struct Project;
|
struct Project;
|
||||||
struct QueryDatabase;
|
struct QueryDatabase;
|
||||||
struct SemanticHighlightSymbolCache;
|
|
||||||
struct TimestampManager;
|
struct TimestampManager;
|
||||||
struct WorkingFile;
|
struct WorkingFile;
|
||||||
struct WorkingFiles;
|
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
|
struct Out_CclsPublishSemanticHighlighting
|
||||||
: public lsOutMessage<Out_CclsPublishSemanticHighlighting> {
|
: public lsOutMessage<Out_CclsPublishSemanticHighlighting> {
|
||||||
struct Symbol {
|
struct Symbol {
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "project.h"
|
#include "project.h"
|
||||||
#include "queue_manager.h"
|
#include "queue_manager.h"
|
||||||
#include "semantic_highlight_symbol_cache.h"
|
|
||||||
#include "serializers/json.h"
|
#include "serializers/json.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "work_thread.h"
|
#include "work_thread.h"
|
||||||
@ -361,26 +360,13 @@ MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities,
|
|||||||
|
|
||||||
struct lsClientCapabilities {
|
struct lsClientCapabilities {
|
||||||
// Workspace specific client capabilities.
|
// Workspace specific client capabilities.
|
||||||
std::optional<lsWorkspaceClientCapabilites> workspace;
|
lsWorkspaceClientCapabilites workspace;
|
||||||
|
|
||||||
// Text document specific client capabilities.
|
// Text document specific client capabilities.
|
||||||
std::optional<lsTextDocumentClientCapabilities> textDocument;
|
lsTextDocumentClientCapabilities textDocument;
|
||||||
|
|
||||||
/**
|
|
||||||
* Experimental client capabilities.
|
|
||||||
*/
|
|
||||||
// experimental?: any; // TODO
|
|
||||||
};
|
};
|
||||||
MAKE_REFLECT_STRUCT(lsClientCapabilities, workspace, textDocument);
|
MAKE_REFLECT_STRUCT(lsClientCapabilities, workspace, textDocument);
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////// INITIALIZATION ///////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
struct lsInitializeParams {
|
struct lsInitializeParams {
|
||||||
// The process Id of the parent process that started
|
// The process Id of the parent process that started
|
||||||
// the server. Is null if the process has not been started by another process.
|
// 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
|
// 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)
|
if (cap.completion && cap.completion->completionItem)
|
||||||
config->client.snippetSupport =
|
config->client.snippetSupport =
|
||||||
cap.completion->completionItem->snippetSupport.value_or(false);
|
cap.completion->completionItem->snippetSupport.value_or(false);
|
||||||
|
@ -338,7 +338,10 @@ struct Handler_TextDocumentCompletion : MessageHandler {
|
|||||||
include_complete->completion_items_mutex, std::defer_lock);
|
include_complete->completion_items_mutex, std::defer_lock);
|
||||||
if (include_complete->is_scanning)
|
if (include_complete->is_scanning)
|
||||||
lock.lock();
|
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,
|
FilterAndSortCompletionResponse(&out, result.pattern,
|
||||||
config->completion.filterAndSort);
|
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