Cmake: Fix compile output glitch

Using a dedicated OutputLineParser ensures that we only ever see
complete lines and thus prevents "partially red" lines in the compile
output pane.

Fixes: QTCREATORBUG-24209
Change-Id: I12b3de70b81789afe727b66e366facdcc81f8ab8
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Reviewed-by: Cristian Adam <cristian.adam@qt.io>
This commit is contained in:
Christian Kandeler
2020-07-30 13:16:43 +02:00
parent 2944b33530
commit 21fbf36a5d
3 changed files with 74 additions and 59 deletions

View File

@@ -47,6 +47,7 @@
#include <QFormLayout>
#include <QLineEdit>
#include <QListWidget>
#include <QRegularExpression>
using namespace ProjectExplorer;
@@ -59,6 +60,53 @@ const char TOOL_ARGUMENTS_KEY[] = "CMakeProjectManager.MakeStep.AdditionalArgume
const char ADD_RUNCONFIGURATION_ARGUMENT_KEY[] = "CMakeProjectManager.MakeStep.AddRunConfigurationArgument";
const char ADD_RUNCONFIGURATION_TEXT[] = "Current executable";
class CmakeProgressParser : public Utils::OutputLineParser
{
Q_OBJECT
signals:
void progress(int percentage);
private:
Result handleLine(const QString &line, Utils::OutputFormat format) override
{
if (format != Utils::StdOutFormat)
return Status::NotHandled;
static const QRegularExpression percentProgress("^\\[\\s*(\\d*)%\\]");
static const QRegularExpression ninjaProgress("^\\[\\s*(\\d*)/\\s*(\\d*)");
QRegularExpressionMatch match = percentProgress.match(line);
if (match.hasMatch()) {
bool ok = false;
const int percent = match.captured(1).toInt(&ok);
if (ok)
emit progress(percent);
return Status::Done;
}
match = ninjaProgress.match(line);
if (match.hasMatch()) {
m_useNinja = true;
bool ok = false;
const int done = match.captured(1).toInt(&ok);
if (ok) {
const int all = match.captured(2).toInt(&ok);
if (ok && all != 0) {
const int percent = static_cast<int>(100.0 * done / all);
emit progress(percent);
}
}
return Status::Done;
}
return Status::NotHandled;
}
bool hasDetectedRedirection() const override { return m_useNinja; }
// TODO: Shouldn't we know the backend in advance? Then we could merge this class
// with CmakeParser.
bool m_useNinja = false;
};
class CMakeBuildStepConfigWidget : public BuildStepConfigWidget
{
Q_DECLARE_TR_FUNCTIONS(CMakeProjectManager::Internal::CMakeBuildStepConfigWidget)
@@ -88,8 +136,6 @@ static bool isCurrentExecutableTarget(const QString &target)
CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Utils::Id id) :
AbstractProcessStep(bsl, id)
{
m_percentProgress = QRegExp("^\\[\\s*(\\d*)%\\]");
m_ninjaProgress = QRegExp("^\\[\\s*(\\d*)/\\s*(\\d*)");
m_ninjaProgressString = "[%f/%t "; // ninja: [33/100
//: Default display name for the cmake make step.
setDefaultDisplayName(tr("CMake Build"));
@@ -211,9 +257,18 @@ bool CMakeBuildStep::init()
void CMakeBuildStep::setupOutputFormatter(Utils::OutputFormatter *formatter)
{
CMakeParser *cmakeParser = new CMakeParser;
CmakeProgressParser * const progressParser = new CmakeProgressParser;
connect(progressParser, &CmakeProgressParser::progress, this, [this](int percent) {
emit progress(percent, {});
});
formatter->addLineParser(progressParser);
cmakeParser->setSourceDirectory(project()->projectDirectory().toString());
formatter->addLineParsers({cmakeParser, new GnuMakeParser});
formatter->addLineParsers(target()->kit()->createOutputParsers());
const QList<Utils::OutputLineParser *> additionalParsers
= target()->kit()->createOutputParsers();
for (Utils::OutputLineParser * const p : additionalParsers)
p->setRedirectionDetector(progressParser);
formatter->addLineParsers(additionalParsers);
formatter->addSearchDir(processParameters()->effectiveWorkingDirectory());
AbstractProcessStep::setupOutputFormatter(formatter);
}
@@ -276,47 +331,6 @@ QString CMakeBuildStep::defaultBuildTarget() const
return allTarget();
}
void CMakeBuildStep::stdOutput(const QString &output)
{
int offset = 0;
while (offset != -1) {
const int newlinePos = output.indexOf('\n', offset);
QString line;
if (newlinePos == -1) {
line = output.mid(offset);
offset = -1;
} else {
line = output.mid(offset, newlinePos - offset + 1);
offset = newlinePos + 1;
}
if (m_percentProgress.indexIn(line) != -1) {
AbstractProcessStep::stdOutput(line);
bool ok = false;
int percent = m_percentProgress.cap(1).toInt(&ok);
if (ok)
emit progress(percent, QString());
continue;
} else if (m_ninjaProgress.indexIn(line) != -1) {
AbstractProcessStep::stdOutput(line);
m_useNinja = true;
bool ok = false;
int done = m_ninjaProgress.cap(1).toInt(&ok);
if (ok) {
int all = m_ninjaProgress.cap(2).toInt(&ok);
if (ok && all != 0) {
const int percent = static_cast<int>(100.0 * done/all);
emit progress(percent, QString());
}
}
continue;
}
if (m_useNinja)
AbstractProcessStep::stdError(line);
else
AbstractProcessStep::stdOutput(line);
}
}
QStringList CMakeBuildStep::buildTargets() const
{
return m_buildTargets;
@@ -573,12 +587,6 @@ CMakeBuildStepFactory::CMakeBuildStepFactory()
setSupportedProjectType(Constants::CMAKE_PROJECT_ID);
}
void CMakeBuildStep::processStarted()
{
m_useNinja = false;
AbstractProcessStep::processStarted();
}
void CMakeBuildStep::processFinished(int exitCode, QProcess::ExitStatus status)
{
AbstractProcessStep::processFinished(exitCode, status);
@@ -587,3 +595,5 @@ void CMakeBuildStep::processFinished(int exitCode, QProcess::ExitStatus status)
} // Internal
} // CMakeProjectManager
#include <cmakebuildstep.moc>