diff options
Diffstat (limited to 'src/plugins/projectexplorer/abstractprocessstep.cpp')
-rw-r--r-- | src/plugins/projectexplorer/abstractprocessstep.cpp | 175 |
1 files changed, 34 insertions, 141 deletions
diff --git a/src/plugins/projectexplorer/abstractprocessstep.cpp b/src/plugins/projectexplorer/abstractprocessstep.cpp index f3027eb609..7b1dd44d3c 100644 --- a/src/plugins/projectexplorer/abstractprocessstep.cpp +++ b/src/plugins/projectexplorer/abstractprocessstep.cpp @@ -24,7 +24,6 @@ ****************************************************************************/ #include "abstractprocessstep.h" -#include "ansifilterparser.h" #include "buildconfiguration.h" #include "buildstep.h" #include "ioutputparser.h" @@ -37,14 +36,15 @@ #include <coreplugin/reaper.h> -#include <utils/fileinprojectfinder.h> #include <utils/fileutils.h> +#include <utils/outputformatter.h> #include <utils/qtcassert.h> #include <utils/qtcprocess.h> #include <QDir> #include <QHash> #include <QPair> +#include <QTextDecoder> #include <QUrl> #include <algorithm> @@ -105,21 +105,15 @@ public: AbstractProcessStep *q; std::unique_ptr<Utils::QtcProcess> m_process; - std::unique_ptr<IOutputParser> m_outputParserChain; ProcessParameters m_param; - Utils::FileInProjectFinder m_fileFinder; - QByteArray deferredText; bool m_ignoreReturnValue = false; - bool m_skipFlush = false; bool m_lowPriority = false; - - void readData(void (AbstractProcessStep::*func)(const QString &), bool isUtf8 = false); - void processLine(const QByteArray &data, - void (AbstractProcessStep::*func)(const QString &), - bool isUtf8 = false); + std::unique_ptr<QTextDecoder> stdoutStream; + std::unique_ptr<QTextDecoder> stderrStream; + OutputFormatter *outputFormatter = nullptr; }; -AbstractProcessStep::AbstractProcessStep(BuildStepList *bsl, Core::Id id) : +AbstractProcessStep::AbstractProcessStep(BuildStepList *bsl, Utils::Id id) : BuildStep(bsl, id), d(new Private(this)) { @@ -130,39 +124,6 @@ AbstractProcessStep::~AbstractProcessStep() delete d; } -/*! - Deletes all existing output parsers and starts a new chain with the - given parser. - - Derived classes need to call this function. -*/ - -void AbstractProcessStep::setOutputParser(IOutputParser *parser) -{ - d->m_outputParserChain.reset(new AnsiFilterParser); - d->m_outputParserChain->appendOutputParser(parser); - - connect(d->m_outputParserChain.get(), &IOutputParser::addOutput, this, &AbstractProcessStep::outputAdded); - connect(d->m_outputParserChain.get(), &IOutputParser::addTask, this, &AbstractProcessStep::taskAdded); -} - -/*! - Appends the given output parser to the existing chain of parsers. -*/ -void AbstractProcessStep::appendOutputParser(IOutputParser *parser) -{ - if (!parser) - return; - - QTC_ASSERT(d->m_outputParserChain, return); - d->m_outputParserChain->appendOutputParser(parser); -} - -IOutputParser *AbstractProcessStep::outputParser() const -{ - return d->m_outputParserChain.get(); -} - void AbstractProcessStep::emitFaultyConfigurationMessage() { emit addOutput(tr("Configuration is faulty. Check the Issues view for details."), @@ -193,11 +154,16 @@ void AbstractProcessStep::setIgnoreReturnValue(bool b) bool AbstractProcessStep::init() { - d->m_fileFinder.setProjectDirectory(project()->projectDirectory()); - d->m_fileFinder.setProjectFiles(project()->files(Project::AllFiles)); return !d->m_process; } +void AbstractProcessStep::setupOutputFormatter(OutputFormatter *formatter) +{ + formatter->setDemoteErrorsToWarnings(d->m_ignoreReturnValue); + d->outputFormatter = formatter; + BuildStep::setupOutputFormatter(formatter); +} + /*! Reimplemented from BuildStep::init(). You need to call this from YourBuildStep::run(). @@ -225,10 +191,19 @@ void AbstractProcessStep::doRun() return; } + d->stdoutStream = std::make_unique<QTextDecoder>(buildEnvironment().hasKey("VSLANG") + ? QTextCodec::codecForName("UTF-8") : QTextCodec::codecForLocale()); + d->stderrStream = std::make_unique<QTextDecoder>(QTextCodec::codecForLocale()); + d->m_process.reset(new Utils::QtcProcess()); d->m_process->setUseCtrlCStub(Utils::HostOsInfo::isWindowsHost()); d->m_process->setWorkingDirectory(wd.absolutePath()); - d->m_process->setEnvironment(d->m_param.environment()); + // Enforce PWD in the environment because some build tools use that. + // PWD can be different from getcwd in case of symbolic links (getcwd resolves symlinks). + // For example Clang uses PWD for paths in debug info, see QTCREATORBUG-23788 + Environment envWithPwd = d->m_param.environment(); + envWithPwd.set("PWD", d->m_process->workingDirectory()); + d->m_process->setEnvironment(envWithPwd); d->m_process->setCommand(effectiveCommand); if (d->m_lowPriority && ProjectExplorerPlugin::projectExplorerSettings().lowBuildPriority) d->m_process->setLowPriority(); @@ -244,7 +219,6 @@ void AbstractProcessStep::doRun() if (!d->m_process->waitForStarted()) { processStartupFailed(); d->m_process.reset(); - d->m_outputParserChain.reset(); finish(false); return; } @@ -272,7 +246,6 @@ void AbstractProcessStep::cleanUp(QProcess *process) processFinished(process->exitCode(), process->exitStatus()); const bool returnValue = processSucceeded(process->exitCode(), process->exitStatus()) || d->m_ignoreReturnValue; - d->m_outputParserChain.reset(); d->m_process.reset(); // Report result @@ -302,9 +275,6 @@ void AbstractProcessStep::processStarted() void AbstractProcessStep::processFinished(int exitCode, QProcess::ExitStatus status) { - if (d->m_outputParserChain) - d->m_outputParserChain->flush(); - QString command = QDir::toNativeSeparators(d->m_param.effectiveCommand().toString()); if (status == QProcess::NormalExit && exitCode == 0) { emit addOutput(tr("The process \"%1\" exited normally.").arg(command), @@ -338,7 +308,7 @@ void AbstractProcessStep::processStartupFailed() bool AbstractProcessStep::processSucceeded(int exitCode, QProcess::ExitStatus status) { - if (outputParser() && outputParser()->hasFatalErrors()) + if (d->outputFormatter->hasFatalErrors()) return false; return exitCode == 0 && status == QProcess::NormalExit; @@ -348,43 +318,7 @@ void AbstractProcessStep::processReadyReadStdOutput() { if (!d->m_process) return; - d->m_process->setReadChannel(QProcess::StandardOutput); - BuildConfiguration *bc = buildConfiguration(); - if (!bc) - bc = target()->activeBuildConfiguration(); - const bool utf8Output = bc && bc->environment().hasKey("VSLANG"); - d->readData(&AbstractProcessStep::stdOutput, utf8Output); -} - -void AbstractProcessStep::Private::readData(void (AbstractProcessStep::*func)(const QString &), - bool isUtf8) -{ - while (m_process->bytesAvailable()) { - const bool hasLine = m_process->canReadLine(); - const QByteArray data = hasLine ? m_process->readLine() : m_process->readAll(); - int startPos = 0; - int crPos = -1; - while ((crPos = data.indexOf('\r', startPos)) >= 0) { - if (data.size() > crPos + 1 && data.at(crPos + 1) == '\n') - break; - processLine(data.mid(startPos, crPos - startPos + 1), func, isUtf8); - startPos = crPos + 1; - } - if (hasLine) - processLine(data.mid(startPos), func, isUtf8); - else if (startPos < data.count()) - deferredText += data.mid(startPos); - } -} - -void AbstractProcessStep::Private::processLine(const QByteArray &data, - void (AbstractProcessStep::*func)(const QString &), - bool isUtf8) -{ - const QByteArray text = deferredText + data; - deferredText.clear(); - const QString line = isUtf8 ? QString::fromUtf8(text) : QString::fromLocal8Bit(text); - (q->*func)(line); + stdOutput(d->stdoutStream->toUnicode(d->m_process->readAllStandardOutput())); } /*! @@ -393,19 +327,16 @@ void AbstractProcessStep::Private::processLine(const QByteArray &data, The default implementation adds the line to the application output window. */ -void AbstractProcessStep::stdOutput(const QString &line) +void AbstractProcessStep::stdOutput(const QString &output) { - if (d->m_outputParserChain) - d->m_outputParserChain->stdOutput(line); - emit addOutput(line, BuildStep::OutputFormat::Stdout, BuildStep::DontAppendNewline); + emit addOutput(output, BuildStep::OutputFormat::Stdout, BuildStep::DontAppendNewline); } void AbstractProcessStep::processReadyReadStdError() { if (!d->m_process) return; - d->m_process->setReadChannel(QProcess::StandardError); - d->readData(&AbstractProcessStep::stdError); + stdError(d->stderrStream->toUnicode(d->m_process->readAllStandardError())); } /*! @@ -414,11 +345,9 @@ void AbstractProcessStep::processReadyReadStdError() The default implementation adds the line to the application output window. */ -void AbstractProcessStep::stdError(const QString &line) +void AbstractProcessStep::stdError(const QString &output) { - if (d->m_outputParserChain) - d->m_outputParserChain->stdError(line); - emit addOutput(line, BuildStep::OutputFormat::Stderr, BuildStep::DontAppendNewline); + emit addOutput(output, BuildStep::OutputFormat::Stderr, BuildStep::DontAppendNewline); } void AbstractProcessStep::finish(bool success) @@ -426,37 +355,6 @@ void AbstractProcessStep::finish(bool success) emit finished(success); } -void AbstractProcessStep::taskAdded(const Task &task, int linkedOutputLines, int skipLines) -{ - // Do not bother to report issues if we do not care about the results of - // the buildstep anyway: - if (d->m_ignoreReturnValue) - return; - - // flush out any pending tasks before proceeding: - if (!d->m_skipFlush && d->m_outputParserChain) { - d->m_skipFlush = true; - d->m_outputParserChain->flush(); - d->m_skipFlush = false; - } - - Task editable(task); - QString filePath = task.file.toString(); - if (!filePath.isEmpty() && !filePath.startsWith('<') && !QDir::isAbsolutePath(filePath)) { - while (filePath.startsWith("../")) - filePath.remove(0, 3); - bool found = false; - const Utils::FilePaths candidates - = d->m_fileFinder.findFile(QUrl::fromLocalFile(filePath), &found); - if (found && candidates.size() == 1) - editable.file = candidates.first(); - else - qWarning() << "Could not find absolute location of file " << filePath; - } - - emit addTask(editable, linkedOutputLines, skipLines); -} - void AbstractProcessStep::outputAdded(const QString &string, BuildStep::OutputFormat format) { emit addOutput(string, format, BuildStep::DontAppendNewline); @@ -467,15 +365,10 @@ void AbstractProcessStep::slotProcessFinished(int, QProcess::ExitStatus) QProcess *process = d->m_process.get(); if (!process) // Happens when the process was canceled and handed over to the Reaper. process = qobject_cast<QProcess *>(sender()); // The process was canceled! - - const QString stdErrLine = process ? QString::fromLocal8Bit(process->readAllStandardError()) : QString(); - for (const QString &l : stdErrLine.split('\n')) - stdError(l); - - const QString stdOutLine = process ? QString::fromLocal8Bit(process->readAllStandardOutput()) : QString(); - for (const QString &l : stdOutLine.split('\n')) - stdOutput(l); - + if (process) { + stdError(d->stderrStream->toUnicode(process->readAllStandardError())); + stdOutput(d->stdoutStream->toUnicode(process->readAllStandardOutput())); + } cleanUp(process); } |