logic: made parsing interruptable by introducing TaskScheduler

This change introduces the TaskScheduler, which queues and processes Tasks on a separate thread. The Task class provides
a common interface for deriving all specific tasks. A task can split it's processing into multiple update calls. The
TaskScheduler will update a task until it is finished or interrupt it when necessary. TaskGroups can be used to bundle
multiple Tasks together. So far only TaskGroupSequential was implemented which runs the Tasks in the set order.

TaskParseCxx utilizes the CxxParser to parse each source file in a single update call. Parsing can be interrupted using
the ESC key. The Statusbar shows the parsing progress.
This commit is contained in:
Eberhard Graether
2015-04-23 18:57:38 +02:00
parent a18213d172
commit 3c7c3da527
34 changed files with 1000 additions and 221 deletions
+1 -2
View File
@@ -5,9 +5,8 @@
#include <string>
#include <vector>
#include "data/parser/ParserClient.h"
class FilePath;
class ParserClient;
class TextAccess;
class Parser
+1
View File
@@ -51,6 +51,7 @@ public:
virtual ~ParserClient();
virtual void onError(const ParseLocation& location, const std::string& message) = 0;
virtual size_t getErrorCount() const = 0;
virtual Id onTypedefParsed(
const ParseLocation& location, const NameHierarchy& nameHierarchy,
-2
View File
@@ -21,8 +21,6 @@ std::unique_ptr<clang::ASTConsumer> ASTAction::CreateASTConsumer(clang::Compiler
bool ASTAction::BeginSourceFileAction(clang::CompilerInstance& compiler, llvm::StringRef filePath)
{
m_client->onFileParsed(filePath.str());
clang::Preprocessor& preprocessor = compiler.getPreprocessor();
preprocessor.addPPCallbacks(
llvm::make_unique<PreprocessorCallbacks>(compiler.getSourceManager(), m_client, m_fileRegister));
+75 -47
View File
@@ -1,5 +1,8 @@
#include "data/parser/cxx/CxxParser.h"
#include "clang/Tooling/Tooling.h"
#include "utility/file/FileManager.h"
#include "utility/file/FileRegister.h"
#include "utility/logging/logging.h"
#include "utility/text/TextAccess.h"
@@ -50,7 +53,7 @@ namespace
CxxParser::CxxParser(ParserClient* client, const FileManager* fileManager)
: Parser(client)
, m_fileManager(fileManager)
, m_fileRegister(std::make_shared<FileRegister>(fileManager))
{
}
@@ -60,70 +63,33 @@ CxxParser::~CxxParser()
void CxxParser::parseFiles(const std::vector<FilePath>& filePaths, const Arguments& arguments)
{
// Commandline flags passed to the programm. Everything after '--' will be interpreted by the ClangTool.
std::vector<std::string> args = getCommandlineArguments(arguments);
args.insert(args.begin(), "app");
args.insert(args.begin() + 1, "--");
int argc = args.size();
const char** argv = new const char*[argc];
for (size_t i = 0; i < args.size(); i++)
{
argv[i] = args[i].c_str();
}
std::shared_ptr<clang::tooling::FixedCompilationDatabase> compilationDatabase(
clang::tooling::FixedCompilationDatabase::loadFromCommandLine(argc, argv)
);
if (!compilationDatabase)
{
LOG_ERROR("Failed to load compilation database");
return;
}
FileRegister fileRegister(m_fileManager, filePaths);
setupParsing(filePaths, arguments);
std::vector<std::string> sourcePaths;
for (const FilePath& path : fileRegister.getSourceFilePaths())
for (const FilePath& path : m_fileRegister->getUnparsedSourceFilePaths())
{
sourcePaths.push_back(path.absoluteStr());
}
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> options = new clang::DiagnosticOptions();
CxxDiagnosticConsumer reporter(llvm::errs(), &*options, m_client, arguments.logErrors);
runTool(sourcePaths);
ASTActionFactory actionFactory(m_client, &fileRegister);
clang::tooling::ClangTool tool(*compilationDatabase, sourcePaths);
tool.setDiagnosticConsumer(&reporter);
tool.run(&actionFactory);
std::vector<FilePath> unparsedHeaders = fileRegister.getUnparsedIncludeFilePaths();
std::vector<FilePath> unparsedHeaders = m_fileRegister->getUnparsedIncludeFilePaths();
for (const FilePath& path : unparsedHeaders)
{
if (!fileRegister.includeFileIsParsed(path))
if (!m_fileRegister->includeFileIsParsed(path))
{
clang::tooling::ClangTool tool(*compilationDatabase, std::vector<std::string>(1, path.str()));
tool.setDiagnosticConsumer(&reporter);
tool.run(&actionFactory);
runTool(std::vector<std::string>(1, path.str()));
}
}
delete argv;
}
void CxxParser::parseFile(std::shared_ptr<TextAccess> textAccess, const Arguments& arguments)
{
std::vector<std::string> args = getCommandlineArguments(arguments);
std::shared_ptr<CxxDiagnosticConsumer> diagnostics = getDiagnostics(arguments);
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> options = new clang::DiagnosticOptions();
CxxDiagnosticConsumer reporter(llvm::errs(), &*options, m_client, arguments.logErrors);
FileRegister fileRegister(m_fileManager, std::vector<FilePath>());
ASTActionFactory actionFactory(m_client, &fileRegister);
runToolOnCodeWithArgs(&reporter, actionFactory.create(), textAccess->getText(), args);
ASTActionFactory actionFactory(m_client, m_fileRegister.get());
runToolOnCodeWithArgs(diagnostics.get(), actionFactory.create(), textAccess->getText(), args);
}
std::vector<std::string> CxxParser::getCommandlineArguments(const Arguments& arguments) const
@@ -165,3 +131,65 @@ std::vector<std::string> CxxParser::getCommandlineArguments(const Arguments& arg
return args;
}
std::shared_ptr<clang::tooling::FixedCompilationDatabase> CxxParser::getCompilationDatabase(
const Arguments& arguments
) const {
// Commandline flags passed to the programm. Everything after '--' will be interpreted by the ClangTool.
std::vector<std::string> args = getCommandlineArguments(arguments);
args.insert(args.begin(), "app");
args.insert(args.begin() + 1, "--");
int argc = args.size();
const char** argv = new const char*[argc];
for (size_t i = 0; i < args.size(); i++)
{
argv[i] = args[i].c_str();
}
std::shared_ptr<clang::tooling::FixedCompilationDatabase> compilationDatabase(
clang::tooling::FixedCompilationDatabase::loadFromCommandLine(argc, argv)
);
delete argv;
if (!compilationDatabase)
{
LOG_ERROR("Failed to load compilation database");
return nullptr;
}
return compilationDatabase;
}
std::shared_ptr<CxxDiagnosticConsumer> CxxParser::getDiagnostics(const Arguments& arguments) const
{
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> options = new clang::DiagnosticOptions();
return std::make_shared<CxxDiagnosticConsumer>(llvm::errs(), &*options, m_client, arguments.logErrors);
}
void CxxParser::setupParsing(const std::vector<FilePath>& filePaths, const Arguments& arguments)
{
m_fileRegister->setFilePaths(filePaths);
m_compilationDatabase = getCompilationDatabase(arguments);
m_diagnostics = getDiagnostics(arguments);
}
void CxxParser::runTool(const std::vector<std::string>& files)
{
clang::tooling::ClangTool tool(*m_compilationDatabase, files);
tool.setDiagnosticConsumer(m_diagnostics.get());
ASTActionFactory actionFactory(m_client, m_fileRegister.get());
tool.run(&actionFactory);
}
FileRegister* CxxParser::getFileRegister()
{
return m_fileRegister.get();
}
ParserClient* CxxParser::getParserClient()
{
return m_client;
}
+30 -2
View File
@@ -2,7 +2,19 @@
#define CXX_PARSER_H
#include "data/parser/Parser.h"
#include "utility/file/FileManager.h"
namespace clang
{
namespace tooling
{
class FixedCompilationDatabase;
}
}
class CxxDiagnosticConsumer;
class FileManager;
class FileRegister;
class TaskParseCxx;
class CxxParser: public Parser
{
@@ -10,13 +22,29 @@ public:
CxxParser(ParserClient* client, const FileManager* fileManager);
~CxxParser();
// ParserClient implementation
virtual void parseFiles(const std::vector<FilePath>& filePaths, const Arguments& arguments);
virtual void parseFile(std::shared_ptr<TextAccess> textAccess, const Arguments& arguments);
private:
std::vector<std::string> getCommandlineArguments(const Arguments& arguments) const;
std::shared_ptr<clang::tooling::FixedCompilationDatabase> getCompilationDatabase(const Arguments& arguments) const;
const FileManager* m_fileManager;
std::shared_ptr<CxxDiagnosticConsumer> getDiagnostics(const Arguments& arguments) const;
// Accessed by TaskParseCxx
void setupParsing(const std::vector<FilePath>& filePaths, const Arguments& arguments);
void runTool(const std::vector<std::string>& files);
FileRegister* getFileRegister();
ParserClient* getParserClient();
friend class TaskParseCxx;
std::shared_ptr<FileRegister> m_fileRegister;
std::shared_ptr<clang::tooling::FixedCompilationDatabase> m_compilationDatabase;
std::shared_ptr<CxxDiagnosticConsumer> m_diagnostics;
};
#endif // CXX_PARSER_H
@@ -26,6 +26,7 @@ void PreprocessorCallbacks::FileChanged(
const clang::FileEntry *fileEntry = m_sourceManager.getFileEntryForID(m_sourceManager.getFileID(location));
if (fileEntry && m_fileRegister->getFileManager()->hasFilePath(fileEntry->getName()))
{
m_client->onFileParsed(fileEntry->getName());
m_fileRegister->markIncludeFileParsing(fileEntry->getName());
}
}
+97
View File
@@ -0,0 +1,97 @@
#include "data/parser/cxx/TaskParseCxx.h"
#include <sstream>
#include "utility/file/FileRegister.h"
#include "utility/messaging/type/MessageFinishedParsing.h"
#include "utility/messaging/type/MessageStatus.h"
#include "data/parser/ParserClient.h"
TaskParseCxx::TaskParseCxx(
ParserClient* client,
const FileManager* fileManager,
const Parser::Arguments& arguments,
const std::vector<FilePath>& files
)
: m_parser(client, fileManager)
, m_arguments(arguments)
, m_files(files)
{
}
void TaskParseCxx::enter()
{
m_start = utility::durationStart();
m_parser.setupParsing(m_files, m_arguments);
for (const FilePath& path : m_parser.getFileRegister()->getUnparsedSourceFilePaths())
{
m_sourcePaths.push(path.absoluteStr());
}
}
Task::TaskState TaskParseCxx::update()
{
std::string sourcePath;
bool isSource = false;
FileRegister* fileRegister = m_parser.getFileRegister();
if (m_sourcePaths.size())
{
sourcePath = m_sourcePaths.front();
m_sourcePaths.pop();
isSource = true;
}
else
{
std::vector<FilePath> unparsedHeaders = fileRegister->getUnparsedIncludeFilePaths();
if (unparsedHeaders.size())
{
sourcePath = unparsedHeaders[0].str();
}
}
if (!sourcePath.size())
{
return Task::STATE_FINISHED;
}
std::stringstream ss;
ss << "parsing (ESC to quit): [";
ss << fileRegister->getParsedFilesCount() << "/" << fileRegister->getFilesCount() << "] ";
ss << sourcePath;
MessageStatus(ss.str()).dispatch();
m_parser.runTool(std::vector<std::string>(1, sourcePath));
if (isSource)
{
fileRegister->markSourceFileParsed(sourcePath);
}
return Task::STATE_RUNNING;
}
void TaskParseCxx::exit()
{
FileRegister* fileRegister = m_parser.getFileRegister();
MessageFinishedParsing(
fileRegister->getParsedFilesCount(),
fileRegister->getFilesCount(),
utility::duration(m_start),
m_parser.getParserClient()->getErrorCount()
).dispatch();
}
void TaskParseCxx::interrupt()
{
}
void TaskParseCxx::revert()
{
}
+39
View File
@@ -0,0 +1,39 @@
#ifndef TASK_PARSE_CXX_H
#define TASK_PARSE_CXX_H
#include <queue>
#include "utility/scheduling/Task.h"
#include "utility/utility.h"
#include "data/parser/cxx/CxxParser.h"
class TaskParseCxx
: public Task
{
public:
TaskParseCxx(
ParserClient* client,
const FileManager* fileManager,
const Parser::Arguments& arguments,
const std::vector<FilePath>& files
);
virtual void enter();
virtual TaskState update();
virtual void exit();
virtual void interrupt();
virtual void revert();
private:
CxxParser m_parser;
const Parser::Arguments m_arguments;
const std::vector<FilePath> m_files;
std::queue<std::string> m_sourcePaths;
utility::TimePoint m_start;
};
#endif // TASK_PARSE_CXX_H