From fba0a307914772b286e8f315e0d1dec5ce7935f8 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Tue, 3 Sep 2013 21:52:22 +0200 Subject: [PATCH] add QProcess::Forwarded{Output,Error}Channel Change-Id: Ifc5ed20c38f3228ef25c28681f296d0456b61abe Reviewed-by: Simon Hausmann Reviewed-by: Lars Knoll Reviewed-by: Olivier Goffart --- src/corelib/io/qprocess.cpp | 17 +++++++++- src/corelib/io/qprocess.h | 4 ++- src/corelib/io/qprocess_unix.cpp | 15 +++++++-- src/corelib/io/qprocess_win.cpp | 6 ++-- .../io/qprocess/testForwarding/main.cpp | 21 ++++++++++--- .../auto/corelib/io/qprocess/tst_qprocess.cpp | 31 ++++++++++++++++--- 6 files changed, 79 insertions(+), 15 deletions(-) diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp index d473281acc..27a8bce3fe 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -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. diff --git a/src/corelib/io/qprocess.h b/src/corelib/io/qprocess.h index 2919788e34..0984d501fd 100644 --- a/src/corelib/io/qprocess.h +++ b/src/corelib/io/qprocess.h @@ -124,7 +124,9 @@ public: enum ProcessChannelMode { SeparateChannels, MergedChannels, - ForwardedChannels + ForwardedChannels, + ForwardedOutputChannel, + ForwardedErrorChannel }; enum ExitStatus { NormalExit, diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index bc0ae5a382..0e0ead5897 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -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); } } diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp index 880cbf659d..f545386f56 100644 --- a/src/corelib/io/qprocess_win.cpp +++ b/src/corelib/io/qprocess_win.cpp @@ -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())); diff --git a/tests/auto/corelib/io/qprocess/testForwarding/main.cpp b/tests/auto/corelib/io/qprocess/testForwarding/main.cpp index 3122232ee1..deff2b95ad 100644 --- a/tests/auto/corelib/io/qprocess/testForwarding/main.cpp +++ b/tests/auto/corelib/io/qprocess/testForwarding/main.cpp @@ -42,30 +42,41 @@ #include #include +#include + 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; } diff --git a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp index 183f827836..e03c1fb75e 100644 --- a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp +++ b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp @@ -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("mode"); + QTest::addColumn("outdata"); + QTest::addColumn("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