diff --git a/src/plugins/clangcodemodel/CMakeLists.txt b/src/plugins/clangcodemodel/CMakeLists.txt index 50007413e15..dbfba36ee06 100644 --- a/src/plugins/clangcodemodel/CMakeLists.txt +++ b/src/plugins/clangcodemodel/CMakeLists.txt @@ -50,7 +50,6 @@ extend_qtc_plugin(ClangCodeModel CONDITION WITH_TESTS SOURCES test/activationsequenceprocessortest.cpp test/activationsequenceprocessortest.h - test/clangbatchfileprocessor.cpp test/clangbatchfileprocessor.h test/clangdtests.cpp test/clangdtests.h test/clangfixittest.cpp test/clangfixittest.h test/data/clangtestdata.qrc diff --git a/src/plugins/clangcodemodel/clangcodemodel.qbs b/src/plugins/clangcodemodel/clangcodemodel.qbs index 45a6abc347d..b4a1bc93a53 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.qbs +++ b/src/plugins/clangcodemodel/clangcodemodel.qbs @@ -101,8 +101,6 @@ QtcPlugin { files: [ "activationsequenceprocessortest.cpp", "activationsequenceprocessortest.h", - "clangbatchfileprocessor.cpp", - "clangbatchfileprocessor.h", "clangdtests.cpp", "clangdtests.h", "clangfixittest.cpp", diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp index e974152584e..0c5379ccecf 100644 --- a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp @@ -10,7 +10,6 @@ #ifdef WITH_TESTS # include "test/activationsequenceprocessortest.h" -# include "test/clangbatchfileprocessor.h" # include "test/clangdtests.h" # include "test/clangfixittest.h" #endif @@ -80,15 +79,8 @@ void ClangCodeModelPlugin::initialize() { TaskHub::addCategory(Constants::TASK_CATEGORY_DIAGNOSTICS, Tr::tr("Clang Code Model")); - - connect(ProjectExplorerPlugin::instance(), - &ProjectExplorerPlugin::finishedInitialization, - this, - &ClangCodeModelPlugin::maybeHandleBatchFileAndExit); - CppEditor::CppModelManager::instance()->activateClangCodeModel( std::make_unique()); - createCompilationDBAction(); } @@ -166,18 +158,6 @@ void ClangCodeModelPlugin::createCompilationDBAction() }); } -// For e.g. creation of profile-guided optimization builds. -void ClangCodeModelPlugin::maybeHandleBatchFileAndExit() const -{ -#ifdef WITH_TESTS - const QString batchFilePath = qtcEnvironmentVariable("QTC_CLANG_BATCH"); - if (!batchFilePath.isEmpty() && QTC_GUARD(QFileInfo::exists(batchFilePath))) { - const bool runSucceeded = runClangBatchFile(batchFilePath); - QCoreApplication::exit(!runSucceeded); - } -#endif -} - #ifdef WITH_TESTS QVector ClangCodeModelPlugin::createTestObjects() const { diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.h b/src/plugins/clangcodemodel/clangcodemodelplugin.h index 6f4d9af6533..b03f49c77a7 100644 --- a/src/plugins/clangcodemodel/clangcodemodelplugin.h +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.h @@ -24,8 +24,6 @@ public: void initialize() override; private: - void maybeHandleBatchFileAndExit() const; - void generateCompilationDB(); void createCompilationDBAction(); diff --git a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp b/src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp deleted file mode 100644 index 1f0219df997..00000000000 --- a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp +++ /dev/null @@ -1,729 +0,0 @@ -// Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "clangbatchfileprocessor.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -using namespace ProjectExplorer; - -namespace ClangCodeModel { -namespace Internal { - -static Q_LOGGING_CATEGORY(debug, "qtc.clangcodemodel.batch", QtWarningMsg); - -static int timeOutFromEnvironmentVariable() -{ - bool isConversionOk = false; - const int intervalAsInt = Utils::qtcEnvironmentVariableIntValue("QTC_CLANG_BATCH_TIMEOUT", - &isConversionOk); - if (!isConversionOk) { - qCDebug(debug, "Environment variable QTC_CLANG_BATCH_TIMEOUT is not set, assuming 30000."); - return 30000; - } - - return intervalAsInt; -} - -int timeOutInMs() -{ - static int timeOut = timeOutFromEnvironmentVariable(); - return timeOut; -} - -namespace { - -class BatchFileLineTokenizer -{ -public: - BatchFileLineTokenizer(const QString &line); - - QString nextToken(); - -private: - const QChar *advanceToTokenBegin(); - const QChar *advanceToTokenEnd(); - - bool atEnd() const; - bool atWhiteSpace() const; - bool atQuotationMark() const; - -private: - bool m_isWithinQuotation = false; - QString m_line; - const QChar *m_currentChar; -}; - -BatchFileLineTokenizer::BatchFileLineTokenizer(const QString &line) - : m_line(line) - , m_currentChar(m_line.unicode()) -{ -} - -QString BatchFileLineTokenizer::nextToken() -{ - if (const QChar *tokenBegin = advanceToTokenBegin()) { - if (const QChar *tokenEnd = advanceToTokenEnd()) { - const int length = tokenEnd - tokenBegin; - return QString(tokenBegin, length); - } - } - - return QString(); -} - -const QChar *BatchFileLineTokenizer::advanceToTokenBegin() -{ - m_isWithinQuotation = false; - - forever { - if (atEnd()) - return nullptr; - - if (atQuotationMark()) { - m_isWithinQuotation = true; - ++m_currentChar; - return m_currentChar; - } - - if (!atWhiteSpace()) - return m_currentChar; - - ++m_currentChar; - } -} - -const QChar *BatchFileLineTokenizer::advanceToTokenEnd() -{ - forever { - if (m_isWithinQuotation) { - if (atEnd()) { - qWarning("ClangBatchFileProcessor: error: unfinished quotation."); - return nullptr; - } - - if (atQuotationMark()) - return m_currentChar++; - - } else if (atWhiteSpace() || atEnd()) { - return m_currentChar; - } - - ++m_currentChar; - } -} - -bool BatchFileLineTokenizer::atEnd() const -{ - return *m_currentChar == QLatin1Char('\0'); -} - -bool BatchFileLineTokenizer::atWhiteSpace() const -{ - return *m_currentChar == ' ' - || *m_currentChar == '\t' - || *m_currentChar == '\n'; -} - -bool BatchFileLineTokenizer::atQuotationMark() const -{ - return *m_currentChar == '"'; -} - -struct CommandContext { - QString filePath; - int lineNumber = -1; -}; - -class Command -{ -public: - using Ptr = QSharedPointer; - -public: - Command(const CommandContext &context) : m_commandContext(context) {} - virtual ~Command() = default; - - const CommandContext &context() const { return m_commandContext; } - virtual bool run() { return true; } - -private: - const CommandContext m_commandContext; -}; - -class OpenProjectCommand : public Command -{ -public: - OpenProjectCommand(const CommandContext &context, - const QString &projectFilePath); - - bool run() override; - - static Command::Ptr parse(BatchFileLineTokenizer &arguments, - const CommandContext &context); - -private: - QString m_projectFilePath; -}; - -OpenProjectCommand::OpenProjectCommand(const CommandContext &context, - const QString &projectFilePath) - : Command(context) - , m_projectFilePath(projectFilePath) -{ -} - -bool OpenProjectCommand::run() -{ - qCDebug(debug) << "line" << context().lineNumber << "OpenProjectCommand" << m_projectFilePath; - - const ProjectExplorerPlugin::OpenProjectResult openProjectSucceeded - = ProjectExplorerPlugin::openProject(Utils::FilePath::fromString(m_projectFilePath)); - QTC_ASSERT(openProjectSucceeded, return false); - - Project *project = openProjectSucceeded.project(); - project->configureAsExampleProject(nullptr); - - return CppEditor::Tests::TestCase::waitUntilProjectIsFullyOpened(project, timeOutInMs()); -} - -Command::Ptr OpenProjectCommand::parse(BatchFileLineTokenizer &arguments, - const CommandContext &context) -{ - const QString projectFilePath = arguments.nextToken(); - if (projectFilePath.isEmpty()) { - qWarning("%s:%d: error: No project file path given.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - const QString absoluteProjectFilePath = QFileInfo(projectFilePath).absoluteFilePath(); - - return Command::Ptr(new OpenProjectCommand(context, absoluteProjectFilePath)); -} - -class OpenDocumentCommand : public Command -{ -public: - OpenDocumentCommand(const CommandContext &context, - const QString &documentFilePath); - - bool run() override; - - static Command::Ptr parse(BatchFileLineTokenizer &arguments, const CommandContext &context); - -private: - Utils::FilePath m_documentFilePath; -}; - -OpenDocumentCommand::OpenDocumentCommand(const CommandContext &context, - const QString &documentFilePath) - : Command(context) - , m_documentFilePath(Utils::FilePath::fromString(documentFilePath)) -{ -} - -class WaitForUpdatedCodeWarnings : public QObject -{ -public: - WaitForUpdatedCodeWarnings(ClangEditorDocumentProcessor *processor); - - bool wait(int timeOutInMs) const; - -private: - void onCodeWarningsUpdated() { m_gotResults = true; } - -private: - - bool m_gotResults = false; -}; - -WaitForUpdatedCodeWarnings::WaitForUpdatedCodeWarnings(ClangEditorDocumentProcessor *processor) -{ - connect(processor, - &ClangEditorDocumentProcessor::codeWarningsUpdated, - this, &WaitForUpdatedCodeWarnings::onCodeWarningsUpdated); -} - -bool WaitForUpdatedCodeWarnings::wait(int timeOutInMs) const -{ - QElapsedTimer time; - time.start(); - - forever { - if (time.elapsed() > timeOutInMs) { - qWarning("WaitForUpdatedCodeWarnings: timeout of %d ms reached.", timeOutInMs); - return false; - } - - if (m_gotResults) - return true; - - QCoreApplication::processEvents(); - QThread::msleep(20); - } -} - -bool OpenDocumentCommand::run() -{ - qCDebug(debug) << "line" << context().lineNumber << "OpenDocumentCommand" << m_documentFilePath; - - const bool openEditorSucceeded = Core::EditorManager::openEditor(m_documentFilePath); - QTC_ASSERT(openEditorSucceeded, return false); - - auto *processor = ClangEditorDocumentProcessor::get(m_documentFilePath); - QTC_ASSERT(processor, return false); - - WaitForUpdatedCodeWarnings waiter(processor); - return waiter.wait(timeOutInMs()); -} - -Command::Ptr OpenDocumentCommand::parse(BatchFileLineTokenizer &arguments, - const CommandContext &context) -{ - const QString documentFilePath = arguments.nextToken(); - if (documentFilePath.isEmpty()) { - qWarning("%s:%d: error: No document file path given.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - const QString absoluteDocumentFilePath = QFileInfo(documentFilePath).absoluteFilePath(); - - return Command::Ptr(new OpenDocumentCommand(context, absoluteDocumentFilePath)); -} - -class CloseAllDocuments : public Command -{ -public: - CloseAllDocuments(const CommandContext &context); - - bool run() override; - - static Command::Ptr parse(BatchFileLineTokenizer &arguments, const CommandContext &context); -}; - -CloseAllDocuments::CloseAllDocuments(const CommandContext &context) - : Command(context) -{ -} - -bool CloseAllDocuments::run() -{ - qCDebug(debug) << "line" << context().lineNumber << "CloseAllDocuments"; - - return Core::EditorManager::closeAllEditors(/*askAboutModifiedEditors=*/ false); -} - -Command::Ptr CloseAllDocuments::parse(BatchFileLineTokenizer &arguments, - const CommandContext &context) -{ - const QString argument = arguments.nextToken(); - if (!argument.isEmpty()) { - qWarning("%s:%d: error: Unexpected argument.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - return Command::Ptr(new CloseAllDocuments(context)); -} - -class InsertTextCommand : public Command -{ -public: - // line and column are 1-based - InsertTextCommand(const CommandContext &context, const QString &text); - - bool run() override; - - static Command::Ptr parse(BatchFileLineTokenizer &arguments, - const CommandContext &context); - -private: - const QString m_textToInsert; -}; - -InsertTextCommand::InsertTextCommand(const CommandContext &context, const QString &text) - : Command(context) - , m_textToInsert(text) -{ -} - -TextEditor::BaseTextEditor *currentTextEditor() -{ - return qobject_cast(Core::EditorManager::currentEditor()); -} - -bool InsertTextCommand::run() -{ - qCDebug(debug) << "line" << context().lineNumber << "InsertTextCommand" << m_textToInsert; - - TextEditor::BaseTextEditor *editor = currentTextEditor(); - QTC_ASSERT(editor, return false); - const Utils::FilePath documentFilePath = editor->document()->filePath(); - auto processor = ClangEditorDocumentProcessor::get(documentFilePath); - QTC_ASSERT(processor, return false); - - editor->insert(m_textToInsert); - - WaitForUpdatedCodeWarnings waiter(processor); - return waiter.wait(timeOutInMs()); -} - -Command::Ptr InsertTextCommand::parse(BatchFileLineTokenizer &arguments, - const CommandContext &context) -{ - const QString textToInsert = arguments.nextToken(); - if (textToInsert.isEmpty()) { - qWarning("%s:%d: error: No text to insert given.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - return Command::Ptr(new InsertTextCommand(context, textToInsert)); -} - -class SetCursorCommand : public Command -{ -public: - // line and column are 1-based - SetCursorCommand(const CommandContext &context, int line, int column); - - bool run() override; - - static Command::Ptr parse(BatchFileLineTokenizer &arguments, - const CommandContext &context); - -private: - int m_line; - int m_column; -}; - -SetCursorCommand::SetCursorCommand(const CommandContext &context, int line, int column) - : Command(context) - , m_line(line) - , m_column(column) -{ -} - -bool SetCursorCommand::run() -{ - qCDebug(debug) << "line" << context().lineNumber << "SetCursorCommand" << m_line << m_column; - - TextEditor::BaseTextEditor *editor = currentTextEditor(); - QTC_ASSERT(editor, return false); - - editor->gotoLine(m_line, m_column - 1); - - return true; -} - -Command::Ptr SetCursorCommand::parse(BatchFileLineTokenizer &arguments, - const CommandContext &context) -{ - // Process line - const QString line = arguments.nextToken(); - if (line.isEmpty()) { - qWarning("%s:%d: error: No line number given.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - bool converted = false; - const int lineNumber = line.toInt(&converted); - if (!converted) { - qWarning("%s:%d: error: Invalid line number.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - // Process column - const QString column = arguments.nextToken(); - if (column.isEmpty()) { - qWarning("%s:%d: error: No column number given.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - converted = false; - const int columnNumber = column.toInt(&converted); - if (!converted) { - qWarning("%s:%d: error: Invalid column number.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - return Command::Ptr(new SetCursorCommand(context, lineNumber, columnNumber)); -} - -class ProcessEventsCommand : public Command -{ -public: - ProcessEventsCommand(const CommandContext &context, int durationInMs); - - bool run() override; - - static Command::Ptr parse(BatchFileLineTokenizer &arguments, - const CommandContext &context); - -private: - int m_durationInMs; -}; - -ProcessEventsCommand::ProcessEventsCommand(const CommandContext &context, - int durationInMs) - : Command(context) - , m_durationInMs(durationInMs) -{ -} - -bool ProcessEventsCommand::run() -{ - qCDebug(debug) << "line" << context().lineNumber << "ProcessEventsCommand" << m_durationInMs; - - QElapsedTimer time; - time.start(); - - forever { - if (time.elapsed() > m_durationInMs) - return true; - - QCoreApplication::processEvents(); - QThread::msleep(20); - } -} - -Command::Ptr ProcessEventsCommand::parse(BatchFileLineTokenizer &arguments, - const CommandContext &context) -{ - const QString durationInMsText = arguments.nextToken(); - if (durationInMsText.isEmpty()) { - qWarning("%s:%d: error: No duration given.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - bool converted = false; - const int durationInMs = durationInMsText.toInt(&converted); - if (!converted) { - qWarning("%s:%d: error: Invalid duration given.", - qPrintable(context.filePath), - context.lineNumber); - return Command::Ptr(); - } - - return Command::Ptr(new ProcessEventsCommand(context, durationInMs)); -} - -class BatchFileReader -{ -public: - BatchFileReader(const QString &filePath); - - bool isFilePathValid() const; - - QString read() const; - -private: - const QString m_batchFilePath; -}; - -BatchFileReader::BatchFileReader(const QString &filePath) - : m_batchFilePath(filePath) -{ -} - -bool BatchFileReader::isFilePathValid() const -{ - QFileInfo fileInfo(m_batchFilePath); - - return !m_batchFilePath.isEmpty() - && fileInfo.isFile() - && fileInfo.isReadable(); -} - -QString BatchFileReader::read() const -{ - QFile file(m_batchFilePath); - QTC_CHECK(file.open(QFile::ReadOnly | QFile::Text)); - - return QString::fromLocal8Bit(file.readAll()); -} - -class BatchFileParser -{ -public: - BatchFileParser(const QString &filePath, - const QString &commands); - - bool parse(); - QVector commands() const; - -private: - bool advanceLine(); - QString currentLine() const; - bool parseLine(const QString &line); - -private: - using ParseFunction = Command::Ptr (*)(BatchFileLineTokenizer &, const CommandContext &); - using CommandToParseFunction = QHash; - CommandToParseFunction m_commandParsers; - - int m_currentLineIndex = -1; - CommandContext m_context; - QStringList m_lines; - QVector m_commands; -}; - -BatchFileParser::BatchFileParser(const QString &filePath, - const QString &commands) - : m_lines(commands.split('\n')) -{ - m_context.filePath = filePath; - - m_commandParsers.insert("openProject", &OpenProjectCommand::parse); - m_commandParsers.insert("openDocument", &OpenDocumentCommand::parse); - m_commandParsers.insert("closeAllDocuments", &CloseAllDocuments::parse); - m_commandParsers.insert("setCursor", &SetCursorCommand::parse); - m_commandParsers.insert("insertText", &InsertTextCommand::parse); - m_commandParsers.insert("processEvents", &ProcessEventsCommand::parse); -} - -bool BatchFileParser::parse() -{ - while (advanceLine()) { - const QString line = currentLine().trimmed(); - if (line.isEmpty() || line.startsWith('#')) - continue; - - if (!parseLine(line)) - return false; - } - - return true; -} - -QVector BatchFileParser::commands() const -{ - return m_commands; -} - -bool BatchFileParser::advanceLine() -{ - ++m_currentLineIndex; - m_context.lineNumber = m_currentLineIndex + 1; - return m_currentLineIndex < m_lines.size(); -} - -QString BatchFileParser::currentLine() const -{ - return m_lines[m_currentLineIndex]; -} - -bool BatchFileParser::parseLine(const QString &line) -{ - BatchFileLineTokenizer tokenizer(line); - QString command = tokenizer.nextToken(); - QTC_CHECK(!command.isEmpty()); - - if (const ParseFunction parseFunction = m_commandParsers.value(command)) { - if (Command::Ptr cmd = parseFunction(tokenizer, m_context)) { - m_commands.append(cmd); - return true; - } - - return false; - } - - qWarning("%s:%d: error: Unknown command \"%s\".", - qPrintable(m_context.filePath), - m_context.lineNumber, - qPrintable(command)); - - return false; -} - -} // anonymous namespace - -static QString applySubstitutions(const QString &filePath, const QString &text) -{ - const QString dirPath = QFileInfo(filePath).absolutePath(); - - QString result = text; - result.replace("${PWD}", dirPath); - - return result; -} - -bool runClangBatchFile(const QString &filePath) -{ - qWarning("ClangBatchFileProcessor: Running \"%s\".", qPrintable(filePath)); - - BatchFileReader reader(filePath); - QTC_ASSERT(reader.isFilePathValid(), return false); - const QString fileContent = reader.read(); - const QString fileContentWithSubstitutionsApplied = applySubstitutions(filePath, fileContent); - - BatchFileParser parser(filePath, fileContentWithSubstitutionsApplied); - QTC_ASSERT(parser.parse(), return false); - const QVector commands = parser.commands(); - - Utils::ExecuteOnDestruction closeAllEditors([] { - qWarning("ClangBatchFileProcessor: Finished, closing all documents."); - QTC_CHECK(Core::EditorManager::closeAllEditors(/*askAboutModifiedEditors=*/ false)); - }); - - for (const Command::Ptr &command : commands) { - const bool runSucceeded = command->run(); - QCoreApplication::processEvents(); // Update GUI - - if (!runSucceeded) { - const CommandContext context = command->context(); - qWarning("%s:%d: Failed to run.", - qPrintable(context.filePath), - context.lineNumber); - return false; - } - } - - return true; -} - -} // namespace Internal -} // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.h b/src/plugins/clangcodemodel/test/clangbatchfileprocessor.h deleted file mode 100644 index 942269b3648..00000000000 --- a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -namespace ClangCodeModel { -namespace Internal { - -int timeOutInMs(); - -bool runClangBatchFile(const QString &filePath); - -} // namespace Internal -} // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp index cf94865098c..a87061ef70f 100644 --- a/src/plugins/clangcodemodel/test/clangdtests.cpp +++ b/src/plugins/clangcodemodel/test/clangdtests.cpp @@ -3,7 +3,6 @@ #include "clangdtests.h" -#include "clangbatchfileprocessor.h" #include "../clangdclient.h" #include "../clangmodelmanagersupport.h" @@ -69,6 +68,27 @@ static QString qrcPath(const QString &relativeFilePath) return ":/unittests/ClangCodeModel/" + relativeFilePath; } +static Q_LOGGING_CATEGORY(debug, "qtc.clangcodemodel.batch", QtWarningMsg); + +static int timeOutFromEnvironmentVariable() +{ + bool isConversionOk = false; + const int intervalAsInt = Utils::qtcEnvironmentVariableIntValue("QTC_CLANG_BATCH_TIMEOUT", + &isConversionOk); + if (!isConversionOk) { + qCDebug(debug, "Environment variable QTC_CLANG_BATCH_TIMEOUT is not set, assuming 30000."); + return 30000; + } + + return intervalAsInt; +} + +int timeOutInMs() +{ + static int timeOut = timeOutFromEnvironmentVariable(); + return timeOut; +} + ClangdTest::~ClangdTest() { EditorManager::closeAllEditors(false);