forked from qt-creator/qt-creator
CMakePM: Start integrating ctest
Gather some more information of a CMake based project to be able to provide this later on to the AutoTest plugin. Task-number: QTCREATORBUG-23332 Change-Id: I2beaf0a6456d57871dcf65832f0a79f37fe5fddc Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -62,6 +62,9 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
@@ -543,8 +546,11 @@ void CMakeBuildSystem::combineScanAndParse()
|
|||||||
m_reader.resetData();
|
m_reader.resetData();
|
||||||
|
|
||||||
m_currentGuard = {};
|
m_currentGuard = {};
|
||||||
|
m_testNames.clear();
|
||||||
|
|
||||||
emitBuildSystemUpdated();
|
emitBuildSystemUpdated();
|
||||||
|
|
||||||
|
QTimer::singleShot(0, this, &CMakeBuildSystem::runCTest);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMakeBuildSystem::checkAndReportError(QString &errorMessage)
|
void CMakeBuildSystem::checkAndReportError(QString &errorMessage)
|
||||||
@@ -715,6 +721,8 @@ void CMakeBuildSystem::handleParsingSucceeded()
|
|||||||
checkAndReportError(errorMessage);
|
checkAndReportError(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_ctestPath = m_reader.ctestPath();
|
||||||
|
|
||||||
setApplicationTargets(appTargets());
|
setApplicationTargets(appTargets());
|
||||||
setDeploymentData(deploymentData());
|
setDeploymentData(deploymentData());
|
||||||
|
|
||||||
@@ -735,6 +743,8 @@ void CMakeBuildSystem::handleParsingFailed(const QString &msg)
|
|||||||
cmakeBuildConfiguration()->setConfigurationFromCMake(cmakeConfig);
|
cmakeBuildConfiguration()->setConfigurationFromCMake(cmakeConfig);
|
||||||
// ignore errorMessage here, we already got one.
|
// ignore errorMessage here, we already got one.
|
||||||
|
|
||||||
|
m_ctestPath.clear();
|
||||||
|
|
||||||
QTC_CHECK(m_waitingForParse);
|
QTC_CHECK(m_waitingForParse);
|
||||||
m_waitingForParse = false;
|
m_waitingForParse = false;
|
||||||
m_combinedScanAndParseResult = false;
|
m_combinedScanAndParseResult = false;
|
||||||
@@ -871,6 +881,49 @@ int CMakeBuildSystem::takeReparseParameters()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CMakeBuildSystem::runCTest()
|
||||||
|
{
|
||||||
|
if (!cmakeBuildConfiguration()->error().isEmpty() || m_ctestPath.isEmpty()) {
|
||||||
|
qCDebug(cmakeBuildSystemLog) << "Cancel ctest run after failed cmake run";
|
||||||
|
emit testInformationUpdated();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qCDebug(cmakeBuildSystemLog) << "Requesting ctest run after cmake run";
|
||||||
|
|
||||||
|
CommandLine cmd{m_ctestPath, {"-N", "--show-only=json-v1"}};
|
||||||
|
SynchronousProcess ctest;
|
||||||
|
ctest.setTimeoutS(1);
|
||||||
|
ctest.setEnvironment(cmakeBuildConfiguration()->environment().toStringList());
|
||||||
|
ctest.setWorkingDirectory(cmakeBuildConfiguration()->buildDirectory().toString());
|
||||||
|
|
||||||
|
const SynchronousProcessResponse response = ctest.run(cmd);
|
||||||
|
if (response.result == SynchronousProcessResponse::Finished) {
|
||||||
|
const QJsonDocument json = QJsonDocument::fromJson(response.allRawOutput());
|
||||||
|
if (!json.isEmpty() && json.isObject()) {
|
||||||
|
const QJsonObject jsonObj = json.object();
|
||||||
|
const QJsonObject btGraph = jsonObj.value("backtraceGraph").toObject();
|
||||||
|
const QJsonArray cmakelists = btGraph.value("files").toArray();
|
||||||
|
const QJsonArray nodes = btGraph.value("nodes").toArray();
|
||||||
|
const QJsonArray tests = jsonObj.value("tests").toArray();
|
||||||
|
for (const QJsonValue &testVal : tests) {
|
||||||
|
const QJsonObject test = testVal.toObject();
|
||||||
|
QTC_ASSERT(!test.isEmpty(), continue);
|
||||||
|
const int bt = test.value("backtrace").toInt(-1);
|
||||||
|
QTC_ASSERT(bt != -1, continue);
|
||||||
|
const QJsonObject btRef = nodes.at(bt).toObject();
|
||||||
|
int file = btRef.value("file").toInt(-1);
|
||||||
|
int line = btRef.value("line").toInt(-1);
|
||||||
|
QTC_ASSERT(file != -1 && line != -1, continue);
|
||||||
|
m_testNames.append({test.value("name").toString(),
|
||||||
|
FilePath::fromString(cmakelists.at(file).toString()),
|
||||||
|
line
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit testInformationUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
CMakeBuildConfiguration *CMakeBuildSystem::cmakeBuildConfiguration() const
|
CMakeBuildConfiguration *CMakeBuildSystem::cmakeBuildConfiguration() const
|
||||||
{
|
{
|
||||||
return static_cast<CMakeBuildConfiguration *>(BuildSystem::buildConfiguration());
|
return static_cast<CMakeBuildConfiguration *>(BuildSystem::buildConfiguration());
|
||||||
@@ -943,6 +996,26 @@ CMakeConfig CMakeBuildSystem::parseCMakeCacheDotTxt(const Utils::FilePath &cache
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QList<TestCaseInfo> CMakeBuildSystem::testcasesInfo() const
|
||||||
|
{
|
||||||
|
return m_testNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandLine CMakeBuildSystem::commandLineForTests(const QList<QString> &tests,
|
||||||
|
const QStringList &options) const
|
||||||
|
{
|
||||||
|
QStringList args = options;
|
||||||
|
auto current = Utils::transform<QSet<QString>>(m_testNames, &TestCaseInfo::name);
|
||||||
|
if (tests.isEmpty() || current == Utils::toSet(tests))
|
||||||
|
return {m_ctestPath, args};
|
||||||
|
|
||||||
|
const QString regex = Utils::transform(tests, [](const QString ¤t) {
|
||||||
|
return QRegularExpression::escape(current);
|
||||||
|
}).join('|');
|
||||||
|
args << "-R" << QString('(' + regex + ')');
|
||||||
|
return {m_ctestPath, args};
|
||||||
|
}
|
||||||
|
|
||||||
DeploymentData CMakeBuildSystem::deploymentData() const
|
DeploymentData CMakeBuildSystem::deploymentData() const
|
||||||
{
|
{
|
||||||
DeploymentData result;
|
DeploymentData result;
|
||||||
|
@@ -89,6 +89,10 @@ public:
|
|||||||
|
|
||||||
CMakeBuildConfiguration *cmakeBuildConfiguration() const;
|
CMakeBuildConfiguration *cmakeBuildConfiguration() const;
|
||||||
|
|
||||||
|
QList<ProjectExplorer::TestCaseInfo> const testcasesInfo() const final;
|
||||||
|
Utils::CommandLine commandLineForTests(const QList<QString> &tests,
|
||||||
|
const QStringList &options) const final;
|
||||||
|
|
||||||
// Generic CMake helper functions:
|
// Generic CMake helper functions:
|
||||||
static CMakeConfig parseCMakeCacheDotTxt(const Utils::FilePath &cacheFile,
|
static CMakeConfig parseCMakeCacheDotTxt(const Utils::FilePath &cacheFile,
|
||||||
QString *errorMessage);
|
QString *errorMessage);
|
||||||
@@ -143,6 +147,8 @@ private:
|
|||||||
void updateReparseParameters(const int parameters);
|
void updateReparseParameters(const int parameters);
|
||||||
int takeReparseParameters();
|
int takeReparseParameters();
|
||||||
|
|
||||||
|
void runCTest();
|
||||||
|
|
||||||
ProjectExplorer::TreeScanner m_treeScanner;
|
ProjectExplorer::TreeScanner m_treeScanner;
|
||||||
QHash<QString, bool> m_mimeBinaryCache;
|
QHash<QString, bool> m_mimeBinaryCache;
|
||||||
QList<const ProjectExplorer::FileNode *> m_allFiles;
|
QList<const ProjectExplorer::FileNode *> m_allFiles;
|
||||||
@@ -164,6 +170,10 @@ private:
|
|||||||
m_buildDirToTempDir;
|
m_buildDirToTempDir;
|
||||||
FileApiReader m_reader;
|
FileApiReader m_reader;
|
||||||
mutable bool m_isHandlingError = false;
|
mutable bool m_isHandlingError = false;
|
||||||
|
|
||||||
|
// CTest integration
|
||||||
|
QString m_ctestPath;
|
||||||
|
QList<ProjectExplorer::TestCaseInfo> m_testNames;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
@@ -203,6 +203,7 @@ void CMakeProcess::handleProcessFinished(int code, QProcess::ExitStatus status)
|
|||||||
} else if (code != 0) {
|
} else if (code != 0) {
|
||||||
msg = tr("CMake process exited with exit code %1.").arg(code);
|
msg = tr("CMake process exited with exit code %1.").arg(code);
|
||||||
}
|
}
|
||||||
|
m_lastExitCode = code;
|
||||||
|
|
||||||
if (!msg.isEmpty()) {
|
if (!msg.isEmpty()) {
|
||||||
Core::MessageManager::write(msg);
|
Core::MessageManager::write(msg);
|
||||||
|
@@ -62,6 +62,7 @@ public:
|
|||||||
void processStandardOutput();
|
void processStandardOutput();
|
||||||
void processStandardError();
|
void processStandardError();
|
||||||
|
|
||||||
|
int lastExitCode() const { return m_lastExitCode; }
|
||||||
signals:
|
signals:
|
||||||
void started();
|
void started();
|
||||||
void finished(int exitCode, QProcess::ExitStatus exitStatus);
|
void finished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||||
@@ -76,6 +77,7 @@ private:
|
|||||||
bool m_processWasCanceled = false;
|
bool m_processWasCanceled = false;
|
||||||
QTimer m_cancelTimer;
|
QTimer m_cancelTimer;
|
||||||
QElapsedTimer m_elapsed;
|
QElapsedTimer m_elapsed;
|
||||||
|
int m_lastExitCode = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
@@ -689,6 +689,8 @@ FileApiQtcData extractData(FileApiData &input,
|
|||||||
|
|
||||||
setupLocationInfoForTargets(result.rootProjectNode.get(), result.buildTargets);
|
setupLocationInfoForTargets(result.rootProjectNode.get(), result.buildTargets);
|
||||||
|
|
||||||
|
result.ctestPath = input.replyFile.ctestExecutable;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -54,6 +54,7 @@ public:
|
|||||||
ProjectExplorer::RawProjectParts projectParts;
|
ProjectExplorer::RawProjectParts projectParts;
|
||||||
std::unique_ptr<CMakeProjectNode> rootProjectNode;
|
std::unique_ptr<CMakeProjectNode> rootProjectNode;
|
||||||
QSet<Utils::FilePath> knownHeaders;
|
QSet<Utils::FilePath> knownHeaders;
|
||||||
|
QString ctestPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
FileApiQtcData extractData(FileApiData &data,
|
FileApiQtcData extractData(FileApiData &data,
|
||||||
|
@@ -135,6 +135,7 @@ static ReplyFileContents readReplyFile(const QFileInfo &fi, QString &errorMessag
|
|||||||
const QJsonObject paths = cmakeObject.value("paths").toObject();
|
const QJsonObject paths = cmakeObject.value("paths").toObject();
|
||||||
{
|
{
|
||||||
result.cmakeExecutable = paths.value("cmake").toString();
|
result.cmakeExecutable = paths.value("cmake").toString();
|
||||||
|
result.ctestExecutable = paths.value("ctest").toString();
|
||||||
result.cmakeRoot = paths.value("root").toString();
|
result.cmakeRoot = paths.value("root").toString();
|
||||||
}
|
}
|
||||||
const QJsonObject generator = cmakeObject.value("generator").toObject();
|
const QJsonObject generator = cmakeObject.value("generator").toObject();
|
||||||
|
@@ -57,6 +57,7 @@ class ReplyFileContents
|
|||||||
public:
|
public:
|
||||||
QString generator;
|
QString generator;
|
||||||
QString cmakeExecutable;
|
QString cmakeExecutable;
|
||||||
|
QString ctestExecutable;
|
||||||
QString cmakeRoot;
|
QString cmakeRoot;
|
||||||
|
|
||||||
QVector<ReplyObject> replies;
|
QVector<ReplyObject> replies;
|
||||||
|
@@ -192,6 +192,12 @@ CMakeConfig FileApiReader::takeParsedConfiguration(QString &errorMessage)
|
|||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString FileApiReader::ctestPath() const
|
||||||
|
{
|
||||||
|
// if we failed to run cmake we should not offer ctest information either
|
||||||
|
return m_lastCMakeExitCode == 0 ? m_ctestPath : QString();
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<CMakeProjectNode> FileApiReader::generateProjectTree(
|
std::unique_ptr<CMakeProjectNode> FileApiReader::generateProjectTree(
|
||||||
const QList<const FileNode *> &allFiles, QString &errorMessage, bool includeHeaderNodes)
|
const QList<const FileNode *> &allFiles, QString &errorMessage, bool includeHeaderNodes)
|
||||||
{
|
{
|
||||||
@@ -267,6 +273,7 @@ void FileApiReader::endState(const QFileInfo &replyFi)
|
|||||||
m_projectParts = std::move(value->projectParts);
|
m_projectParts = std::move(value->projectParts);
|
||||||
m_rootProjectNode = std::move(value->rootProjectNode);
|
m_rootProjectNode = std::move(value->rootProjectNode);
|
||||||
m_knownHeaders = std::move(value->knownHeaders);
|
m_knownHeaders = std::move(value->knownHeaders);
|
||||||
|
m_ctestPath = std::move(value->ctestPath);
|
||||||
|
|
||||||
if (value->errorMessage.isEmpty()) {
|
if (value->errorMessage.isEmpty()) {
|
||||||
emit this->dataAvailable();
|
emit this->dataAvailable();
|
||||||
@@ -296,6 +303,7 @@ void FileApiReader::cmakeFinishedState(int code, QProcess::ExitStatus status)
|
|||||||
Q_UNUSED(code)
|
Q_UNUSED(code)
|
||||||
Q_UNUSED(status)
|
Q_UNUSED(status)
|
||||||
|
|
||||||
|
m_lastCMakeExitCode = m_cmakeProcess->lastExitCode();
|
||||||
m_cmakeProcess.release()->deleteLater();
|
m_cmakeProcess.release()->deleteLater();
|
||||||
|
|
||||||
endState(FileApiParser::scanForCMakeReplyFile(m_parameters.workDirectory));
|
endState(FileApiParser::scanForCMakeReplyFile(m_parameters.workDirectory));
|
||||||
|
@@ -66,6 +66,7 @@ public:
|
|||||||
QSet<Utils::FilePath> projectFilesToWatch() const;
|
QSet<Utils::FilePath> projectFilesToWatch() const;
|
||||||
QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage);
|
QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage);
|
||||||
CMakeConfig takeParsedConfiguration(QString &errorMessage);
|
CMakeConfig takeParsedConfiguration(QString &errorMessage);
|
||||||
|
QString ctestPath() const;
|
||||||
std::unique_ptr<CMakeProjectNode> generateProjectTree(
|
std::unique_ptr<CMakeProjectNode> generateProjectTree(
|
||||||
const QList<const ProjectExplorer::FileNode *> &allFiles,
|
const QList<const ProjectExplorer::FileNode *> &allFiles,
|
||||||
QString &errorMessage,
|
QString &errorMessage,
|
||||||
@@ -95,6 +96,8 @@ private:
|
|||||||
ProjectExplorer::RawProjectParts m_projectParts;
|
ProjectExplorer::RawProjectParts m_projectParts;
|
||||||
std::unique_ptr<CMakeProjectNode> m_rootProjectNode;
|
std::unique_ptr<CMakeProjectNode> m_rootProjectNode;
|
||||||
QSet<Utils::FilePath> m_knownHeaders;
|
QSet<Utils::FilePath> m_knownHeaders;
|
||||||
|
QString m_ctestPath;
|
||||||
|
int m_lastCMakeExitCode = 0;
|
||||||
|
|
||||||
Utils::optional<QFuture<FileApiQtcData *>> m_future;
|
Utils::optional<QFuture<FileApiQtcData *>> m_future;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user