mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-26 01:21:57 +00:00
Fix dependency scanning on import.
The same dependency would cause multiple translation units to get reindexed.
This commit is contained in:
parent
a06f730958
commit
ba7461fc24
@ -11,6 +11,7 @@
|
|||||||
-IC:/Users/jacob/Desktop/cquery/third_party/doctest
|
-IC:/Users/jacob/Desktop/cquery/third_party/doctest
|
||||||
-IC:/Users/jacob/Desktop/cquery/third_party/rapidjson/include
|
-IC:/Users/jacob/Desktop/cquery/third_party/rapidjson/include
|
||||||
-IC:/Users/jacob/Desktop/cquery/third_party/sparsepp
|
-IC:/Users/jacob/Desktop/cquery/third_party/sparsepp
|
||||||
|
-IC:/Users/jacob/Desktop/cquery/third_party/loguru
|
||||||
-IC:/Program Files/LLVM/include
|
-IC:/Program Files/LLVM/include
|
||||||
-isystemC:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.10.25017/include
|
-isystemC:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.10.25017/include
|
||||||
-isystemC:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt
|
-isystemC:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt
|
||||||
@ -21,6 +22,7 @@
|
|||||||
-I/Users/jdufault/personal/cquery/third_party/doctest
|
-I/Users/jdufault/personal/cquery/third_party/doctest
|
||||||
-I/Users/jdufault/personal/cquery/third_party/rapidjson/include
|
-I/Users/jdufault/personal/cquery/third_party/rapidjson/include
|
||||||
-I/Users/jdufault/personal/cquery/third_party/sparsepp
|
-I/Users/jdufault/personal/cquery/third_party/sparsepp
|
||||||
|
-I/Users/jdufault/personal/cquery/third_party/loguru
|
||||||
-I/Users/jdufault/personal/cquery/build/clang+llvm-4.0.0-x86_64-apple-darwin/include
|
-I/Users/jdufault/personal/cquery/build/clang+llvm-4.0.0-x86_64-apple-darwin/include
|
||||||
|
|
||||||
# Use libcxx
|
# Use libcxx
|
||||||
|
@ -730,24 +730,35 @@ void InsertSymbolIntoResult(QueryDatabase* db, WorkingFiles* working_files, Symb
|
|||||||
//
|
//
|
||||||
// NOTE: This is not thread safe and should only be used on the querydb thread.
|
// NOTE: This is not thread safe and should only be used on the querydb thread.
|
||||||
struct ImportManager {
|
struct ImportManager {
|
||||||
|
// Try to mark the given dependency as imported. A dependency can only ever be
|
||||||
|
// imported once.
|
||||||
|
bool TryMarkDependencyImported(const std::string& path) {
|
||||||
|
std::lock_guard<std::mutex> lock(depdency_mutex_);
|
||||||
|
return depdency_imported_.insert(path).second;
|
||||||
|
}
|
||||||
|
|
||||||
// Try to import the given file into querydb. We should only ever be
|
// Try to import the given file into querydb. We should only ever be
|
||||||
// importing a file into querydb once per file. Returns true if the file
|
// importing a file into querydb once per file. Returns true if the file
|
||||||
// can be imported.
|
// can be imported.
|
||||||
bool StartQueryDbImport(const std::string& path) {
|
bool StartQueryDbImport(const std::string& path) {
|
||||||
return import_.insert(path).second;
|
return querydb_processing_.insert(path).second;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The file has been fully imported and can be imported again later on.
|
// The file has been fully imported and can be imported again later on.
|
||||||
void DoneQueryDbImport(const std::string& path) {
|
void DoneQueryDbImport(const std::string& path) {
|
||||||
import_.erase(path);
|
querydb_processing_.erase(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if there any any files currently being imported.
|
// Returns true if there any any files currently being imported.
|
||||||
bool HasActiveImports() {
|
bool HasActiveQuerydbImports() {
|
||||||
return !import_.empty();
|
return !querydb_processing_.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unordered_set<std::string> import_;
|
std::unordered_set<std::string> querydb_processing_;
|
||||||
|
|
||||||
|
// TODO: use std::shared_mutex so we can have multiple readers.
|
||||||
|
std::mutex depdency_mutex_;
|
||||||
|
std::unordered_set<std::string> depdency_imported_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Manages loading caches from file paths for the indexer process.
|
// Manages loading caches from file paths for the indexer process.
|
||||||
@ -886,6 +897,7 @@ std::vector<Index_DoIdMap> DoParseFile(
|
|||||||
clang::Index* index,
|
clang::Index* index,
|
||||||
FileConsumer::SharedState* file_consumer_shared,
|
FileConsumer::SharedState* file_consumer_shared,
|
||||||
TimestampManager* timestamp_manager,
|
TimestampManager* timestamp_manager,
|
||||||
|
ImportManager* import_manager,
|
||||||
CacheLoader* cache_loader,
|
CacheLoader* cache_loader,
|
||||||
bool is_interactive,
|
bool is_interactive,
|
||||||
const std::string& path,
|
const std::string& path,
|
||||||
@ -894,13 +906,20 @@ std::vector<Index_DoIdMap> DoParseFile(
|
|||||||
std::vector<Index_DoIdMap> result;
|
std::vector<Index_DoIdMap> result;
|
||||||
|
|
||||||
IndexFile* previous_index = cache_loader->TryLoad(path);
|
IndexFile* previous_index = cache_loader->TryLoad(path);
|
||||||
if (previous_index) {
|
if (previous_index && !is_interactive) {
|
||||||
// If none of the dependencies have changed, skip parsing and just load from cache.
|
// If none of the dependencies have changed and the index is not
|
||||||
|
// interactive (ie, requested by a file save), skip parsing and just load
|
||||||
|
// from cache.
|
||||||
|
|
||||||
// Checks if |path| needs to be reparsed. This will modify cached state
|
// Checks if |path| needs to be reparsed. This will modify cached state
|
||||||
// such that calling this function twice with the same path may return true
|
// such that calling this function twice with the same path may return true
|
||||||
// the first time but will return false the second.
|
// the first time but will return false the second.
|
||||||
auto file_needs_parse = [&](const std::string& path) {
|
auto file_needs_parse = [&](const std::string& path, bool is_dependency) {
|
||||||
|
// If the file is a dependency but another file as already imported it,
|
||||||
|
// don't bother.
|
||||||
|
if (is_dependency && !import_manager->TryMarkDependencyImported(path))
|
||||||
|
return FileParseQuery::DoesNotNeedParse;
|
||||||
|
|
||||||
optional<int64_t> modification_timestamp = GetLastModificationTime(path);
|
optional<int64_t> modification_timestamp = GetLastModificationTime(path);
|
||||||
if (!modification_timestamp)
|
if (!modification_timestamp)
|
||||||
return FileParseQuery::BadFile;
|
return FileParseQuery::BadFile;
|
||||||
@ -916,7 +935,7 @@ std::vector<Index_DoIdMap> DoParseFile(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Check timestamps and update |file_consumer_shared|.
|
// Check timestamps and update |file_consumer_shared|.
|
||||||
FileParseQuery path_state = file_needs_parse(path);
|
FileParseQuery path_state = file_needs_parse(path, false /*is_dependency*/);
|
||||||
if (path_state == FileParseQuery::BadFile)
|
if (path_state == FileParseQuery::BadFile)
|
||||||
return result;
|
return result;
|
||||||
bool needs_reparse = path_state == FileParseQuery::NeedsParse;
|
bool needs_reparse = path_state == FileParseQuery::NeedsParse;
|
||||||
@ -924,7 +943,7 @@ std::vector<Index_DoIdMap> DoParseFile(
|
|||||||
for (const std::string& dependency : previous_index->dependencies) {
|
for (const std::string& dependency : previous_index->dependencies) {
|
||||||
assert(!dependency.empty());
|
assert(!dependency.empty());
|
||||||
|
|
||||||
if (file_needs_parse(dependency) == FileParseQuery::NeedsParse) {
|
if (file_needs_parse(dependency, true /*is_dependency*/) == FileParseQuery::NeedsParse) {
|
||||||
LOG_S(INFO) << "Timestamp has changed for " << dependency << " (via " << previous_index->path << ")";
|
LOG_S(INFO) << "Timestamp has changed for " << dependency << " (via " << previous_index->path << ")";
|
||||||
needs_reparse = true;
|
needs_reparse = true;
|
||||||
// SUBTLE: Do not break here, as |file_consumer_shared| is updated
|
// SUBTLE: Do not break here, as |file_consumer_shared| is updated
|
||||||
@ -952,18 +971,6 @@ std::vector<Index_DoIdMap> DoParseFile(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: fix indexing issue.
|
|
||||||
// - threaded_queue.h timestamp change from command_line.cc (force repase)
|
|
||||||
// - threaded_queue.h timestamp change from clang_complete.cc (force reparse)
|
|
||||||
// - threaded_queue.h cached index emitted from ipc_manager (load cache)
|
|
||||||
//
|
|
||||||
// RESULT:
|
|
||||||
// - we indexed command_line.cc and clang_complete.cc but did not update threaded_queue.h
|
|
||||||
//
|
|
||||||
// Ideas:
|
|
||||||
// - find the set of changes, try to commit/lock those changes, redo logic if failed.
|
|
||||||
|
|
||||||
|
|
||||||
LOG_S(INFO) << "Parsing " << path;
|
LOG_S(INFO) << "Parsing " << path;
|
||||||
|
|
||||||
// Load file contents for all dependencies into memory. If the dependencies
|
// Load file contents for all dependencies into memory. If the dependencies
|
||||||
@ -1043,6 +1050,7 @@ std::vector<Index_DoIdMap> ParseFile(
|
|||||||
clang::Index* index,
|
clang::Index* index,
|
||||||
FileConsumer::SharedState* file_consumer_shared,
|
FileConsumer::SharedState* file_consumer_shared,
|
||||||
TimestampManager* timestamp_manager,
|
TimestampManager* timestamp_manager,
|
||||||
|
ImportManager* import_manager,
|
||||||
bool is_interactive,
|
bool is_interactive,
|
||||||
const Project::Entry& entry,
|
const Project::Entry& entry,
|
||||||
const optional<std::string>& contents) {
|
const optional<std::string>& contents) {
|
||||||
@ -1058,7 +1066,7 @@ std::vector<Index_DoIdMap> ParseFile(
|
|||||||
// complain about if indexed by itself.
|
// complain about if indexed by itself.
|
||||||
IndexFile* entry_cache = cache_loader.TryLoad(entry.filename);
|
IndexFile* entry_cache = cache_loader.TryLoad(entry.filename);
|
||||||
std::string tu_path = entry_cache ? entry_cache->import_file : entry.filename;
|
std::string tu_path = entry_cache ? entry_cache->import_file : entry.filename;
|
||||||
return DoParseFile(config, working_files, index, file_consumer_shared, timestamp_manager, &cache_loader, is_interactive, tu_path, entry.args, file_contents);
|
return DoParseFile(config, working_files, index, file_consumer_shared, timestamp_manager, import_manager, &cache_loader, is_interactive, tu_path, entry.args, file_contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IndexMain_DoParse(
|
bool IndexMain_DoParse(
|
||||||
@ -1067,6 +1075,7 @@ bool IndexMain_DoParse(
|
|||||||
QueueManager* queue,
|
QueueManager* queue,
|
||||||
FileConsumer::SharedState* file_consumer_shared,
|
FileConsumer::SharedState* file_consumer_shared,
|
||||||
TimestampManager* timestamp_manager,
|
TimestampManager* timestamp_manager,
|
||||||
|
ImportManager* import_manager,
|
||||||
clang::Index* index) {
|
clang::Index* index) {
|
||||||
|
|
||||||
optional<Index_Request> request = queue->index_request.TryDequeue();
|
optional<Index_Request> request = queue->index_request.TryDequeue();
|
||||||
@ -1076,7 +1085,7 @@ bool IndexMain_DoParse(
|
|||||||
Project::Entry entry;
|
Project::Entry entry;
|
||||||
entry.filename = request->path;
|
entry.filename = request->path;
|
||||||
entry.args = request->args;
|
entry.args = request->args;
|
||||||
std::vector<Index_DoIdMap> responses = ParseFile(config, working_files, index, file_consumer_shared, timestamp_manager, request->is_interactive, entry, request->contents);
|
std::vector<Index_DoIdMap> responses = ParseFile(config, working_files, index, file_consumer_shared, timestamp_manager, import_manager, request->is_interactive, entry, request->contents);
|
||||||
|
|
||||||
// Don't bother sending an IdMap request if there are no responses.
|
// Don't bother sending an IdMap request if there are no responses.
|
||||||
if (responses.empty())
|
if (responses.empty())
|
||||||
@ -1187,6 +1196,7 @@ WorkThread::Result IndexMain(
|
|||||||
Config* config,
|
Config* config,
|
||||||
FileConsumer::SharedState* file_consumer_shared,
|
FileConsumer::SharedState* file_consumer_shared,
|
||||||
TimestampManager* timestamp_manager,
|
TimestampManager* timestamp_manager,
|
||||||
|
ImportManager* import_manager,
|
||||||
Project* project,
|
Project* project,
|
||||||
WorkingFiles* working_files,
|
WorkingFiles* working_files,
|
||||||
MultiQueueWaiter* waiter,
|
MultiQueueWaiter* waiter,
|
||||||
@ -1204,7 +1214,7 @@ WorkThread::Result IndexMain(
|
|||||||
// IndexMain_DoCreateIndexUpdate so we don't starve querydb from doing any
|
// IndexMain_DoCreateIndexUpdate so we don't starve querydb from doing any
|
||||||
// work. Running both also lets the user query the partially constructed
|
// work. Running both also lets the user query the partially constructed
|
||||||
// index.
|
// index.
|
||||||
bool did_parse = IndexMain_DoParse(config, working_files, queue, file_consumer_shared, timestamp_manager, &index);
|
bool did_parse = IndexMain_DoParse(config, working_files, queue, file_consumer_shared, timestamp_manager, import_manager, &index);
|
||||||
|
|
||||||
bool did_create_update =
|
bool did_create_update =
|
||||||
IndexMain_DoCreateIndexUpdate(config, queue, timestamp_manager);
|
IndexMain_DoCreateIndexUpdate(config, queue, timestamp_manager);
|
||||||
@ -1467,7 +1477,7 @@ bool QueryDbMainLoop(
|
|||||||
std::cerr << "[querydb] Starting " << config->indexerCount << " indexers" << std::endl;
|
std::cerr << "[querydb] Starting " << config->indexerCount << " indexers" << std::endl;
|
||||||
for (int i = 0; i < config->indexerCount; ++i) {
|
for (int i = 0; i < config->indexerCount; ++i) {
|
||||||
WorkThread::StartThread("indexer" + std::to_string(i), [&]() {
|
WorkThread::StartThread("indexer" + std::to_string(i), [&]() {
|
||||||
return IndexMain(config, file_consumer_shared, timestamp_manager, project, working_files, waiter, queue);
|
return IndexMain(config, file_consumer_shared, timestamp_manager, import_manager, project, working_files, waiter, queue);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2678,7 +2688,7 @@ bool QueryDbMainLoop(
|
|||||||
int idle_count = 0;
|
int idle_count = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
bool has_work = false;
|
bool has_work = false;
|
||||||
has_work |= import_manager->HasActiveImports();
|
has_work |= import_manager->HasActiveQuerydbImports();
|
||||||
has_work |= queue->HasWork();
|
has_work |= queue->HasWork();
|
||||||
has_work |= QueryDb_ImportMain(config, db, import_manager, queue, working_files);
|
has_work |= QueryDb_ImportMain(config, db, import_manager, queue, working_files);
|
||||||
if (!has_work)
|
if (!has_work)
|
||||||
|
Loading…
Reference in New Issue
Block a user