add QProcess::Forwarded{Output,Error}Channel

Change-Id: Ifc5ed20c38f3228ef25c28681f296d0456b61abe
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
This commit is contained in:
Oswald Buddenhagen 2013-09-03 21:52:22 +02:00 committed by The Qt Project
parent dd9d6b3d5b
commit fba0a30791
6 changed files with 79 additions and 15 deletions

View File

@ -533,7 +533,11 @@ void QProcessPrivate::Channel::clear()
MergedChannels before starting the process to activative
this feature. You also have the option of forwarding the output of
the running process to the calling, main process, by passing
ForwardedChannels as the argument.
ForwardedChannels as the argument. It is also possible to forward
only one of the output channels - typically one would use
ForwardedErrorChannel, but ForwardedOutputChannel also exists.
Note that using channel forwarding is typically a bad idea in GUI
applications - you should present errors graphically instead.
Certain processes need special environment settings in order to
operate. You can set environment variables for your process by
@ -625,6 +629,17 @@ void QProcessPrivate::Channel::clear()
writes to its standard output and standard error will be written
to the standard output and standard error of the main process.
\value ForwardedErrorChannel QProcess manages the standard output
of the running process, but forwards its standard error onto the
main process. This reflects the typical use of command line tools
as filters, where the standard output is redirected to another
process or a file, while standard error is printed to the console
for diagnostic purposes.
(This value was introduced in Qt 5.2.)
\value ForwardedOutputChannel Complementary to ForwardedErrorChannel.
(This value was introduced in Qt 5.2.)
\note Windows intentionally suppresses output from GUI-only
applications to inherited consoles.
This does \e not apply to output redirected to files or pipes.

View File

@ -124,7 +124,9 @@ public:
enum ProcessChannelMode {
SeparateChannels,
MergedChannels,
ForwardedChannels
ForwardedChannels,
ForwardedOutputChannel,
ForwardedErrorChannel
};
enum ExitStatus {
NormalExit,

View File

@ -813,6 +813,16 @@ pid_t QProcessPrivate::spawnChild(const char *workingDir, char **argv, char **en
fd_map[1] = QT_FILENO(stdout);
fd_map[2] = QT_FILENO(stderr);
break;
case QProcess::ForwardedOutputChannel:
fd_map[0] = stdinChannel.pipe[0];
fd_map[1] = QT_FILENO(stdout);
fd_map[2] = stderrChannel.pipe[1];
break;
case QProcess::ForwardedErrorChannel:
fd_map[0] = stdinChannel.pipe[0];
fd_map[1] = stdoutChannel.pipe[1];
fd_map[2] = QT_FILENO(stderr);
break;
case QProcess::MergedChannels:
fd_map[0] = stdinChannel.pipe[0];
fd_map[1] = stdoutChannel.pipe[1];
@ -850,12 +860,13 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv
// copy the stdout and stderr if asked to
if (processChannelMode != QProcess::ForwardedChannels) {
qt_safe_dup2(stdoutChannel.pipe[1], fileno(stdout), 0);
if (processChannelMode != QProcess::ForwardedOutputChannel)
qt_safe_dup2(stdoutChannel.pipe[1], fileno(stdout), 0);
// merge stdout and stderr if asked to
if (processChannelMode == QProcess::MergedChannels) {
qt_safe_dup2(fileno(stdout), fileno(stderr), 0);
} else {
} else if (processChannelMode != QProcess::ForwardedErrorChannel) {
qt_safe_dup2(stderrChannel.pipe[1], fileno(stderr), 0);
}
}

View File

@ -160,7 +160,8 @@ bool QProcessPrivate::createChannel(Channel &channel)
} else {
QWindowsPipeReader *pipeReader = 0;
if (&channel == &stdoutChannel) {
if (processChannelMode != QProcess::ForwardedChannels) {
if (processChannelMode != QProcess::ForwardedChannels
&& processChannelMode != QProcess::ForwardedOutputChannel) {
if (!stdoutReader) {
stdoutReader = new QWindowsPipeReader(q);
q->connect(stdoutReader, SIGNAL(readyRead()), SLOT(_q_canReadStandardOutput()));
@ -170,7 +171,8 @@ bool QProcessPrivate::createChannel(Channel &channel)
duplicateStdWriteChannel(channel.pipe, STD_OUTPUT_HANDLE);
}
} else /* if (&channel == &stderrChannel) */ {
if (processChannelMode != QProcess::ForwardedChannels) {
if (processChannelMode != QProcess::ForwardedChannels
&& processChannelMode != QProcess::ForwardedErrorChannel) {
if (!stderrReader) {
stderrReader = new QWindowsPipeReader(q);
q->connect(stderrReader, SIGNAL(readyRead()), SLOT(_q_canReadStandardError()));

View File

@ -42,30 +42,41 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QProcess>
#include <stdlib.h>
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
if (argc < 2)
return 13;
#ifndef QT_NO_PROCESS
QProcess process;
process.setProcessChannelMode(QProcess::ForwardedChannels);
if (process.processChannelMode() != QProcess::ForwardedChannels)
QProcess::ProcessChannelMode mode = (QProcess::ProcessChannelMode)atoi(argv[1]);
process.setProcessChannelMode(mode);
if (process.processChannelMode() != mode)
return 1;
process.start("testProcessEcho/testProcessEcho");
process.start("testProcessEcho2/testProcessEcho2");
if (!process.waitForStarted(5000))
return 2;
if (process.write("forwarded\n") != 10)
if (process.write("forwarded") != 9)
return 3;
process.closeWriteChannel();
if (!process.waitForFinished(5000))
return 4;
if (process.bytesAvailable() != 0)
if ((mode == QProcess::ForwardedOutputChannel || mode == QProcess::ForwardedChannels)
&& !process.readAllStandardOutput().isEmpty())
return 5;
if ((mode == QProcess::ForwardedErrorChannel || mode == QProcess::ForwardedChannels)
&& !process.readAllStandardError().isEmpty())
return 6;
#endif
return 0;
}

View File

@ -107,6 +107,7 @@ private slots:
void softExitInSlots_data();
void softExitInSlots();
void mergedChannels();
void forwardedChannels_data();
void forwardedChannels();
void atEnd();
void atEnd2();
@ -1089,10 +1090,31 @@ void tst_QProcess::mergedChannels()
//-----------------------------------------------------------------------------
#ifndef Q_OS_WINCE
// Reading and writing to a process is not supported on Qt/CE
void tst_QProcess::forwardedChannels_data()
{
QTest::addColumn<int>("mode");
QTest::addColumn<QByteArray>("outdata");
QTest::addColumn<QByteArray>("errdata");
QTest::newRow("separate") << int(QProcess::SeparateChannels)
<< QByteArray() << QByteArray();
QTest::newRow("forwarded") << int(QProcess::ForwardedChannels)
<< QByteArray("forwarded") << QByteArray("forwarded");
QTest::newRow("stdout") << int(QProcess::ForwardedOutputChannel)
<< QByteArray("forwarded") << QByteArray();
QTest::newRow("stderr") << int(QProcess::ForwardedErrorChannel)
<< QByteArray() << QByteArray("forwarded");
}
void tst_QProcess::forwardedChannels()
{
QFETCH(int, mode);
QFETCH(QByteArray, outdata);
QFETCH(QByteArray, errdata);
QProcess process;
process.start("testForwarding/testForwarding");
process.start("testForwarding/testForwarding", QStringList() << QString::number(mode));
QVERIFY(process.waitForStarted(5000));
QVERIFY(process.waitForFinished(5000));
const char *err;
@ -1103,12 +1125,13 @@ void tst_QProcess::forwardedChannels()
case 3: err = "failed to write"; break;
case 4: err = "did not finish"; break;
case 5: err = "unexpected stdout"; break;
case 6: err = "unexpected stderr"; break;
case 13: err = "parameter error"; break;
default: err = "unknown exit code"; break;
}
QVERIFY2(!process.exitCode(), err);
QByteArray data = process.readAll();
QVERIFY(!data.isEmpty());
QVERIFY(data.contains("forwarded"));
QCOMPARE(process.readAllStandardOutput(), outdata);
QCOMPARE(process.readAllStandardError(), errdata);
}
#endif