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:
parent
dd9d6b3d5b
commit
fba0a30791
@ -533,7 +533,11 @@ void QProcessPrivate::Channel::clear()
|
|||||||
MergedChannels before starting the process to activative
|
MergedChannels before starting the process to activative
|
||||||
this feature. You also have the option of forwarding the output of
|
this feature. You also have the option of forwarding the output of
|
||||||
the running process to the calling, main process, by passing
|
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
|
Certain processes need special environment settings in order to
|
||||||
operate. You can set environment variables for your process by
|
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
|
writes to its standard output and standard error will be written
|
||||||
to the standard output and standard error of the main process.
|
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
|
\note Windows intentionally suppresses output from GUI-only
|
||||||
applications to inherited consoles.
|
applications to inherited consoles.
|
||||||
This does \e not apply to output redirected to files or pipes.
|
This does \e not apply to output redirected to files or pipes.
|
||||||
|
@ -124,7 +124,9 @@ public:
|
|||||||
enum ProcessChannelMode {
|
enum ProcessChannelMode {
|
||||||
SeparateChannels,
|
SeparateChannels,
|
||||||
MergedChannels,
|
MergedChannels,
|
||||||
ForwardedChannels
|
ForwardedChannels,
|
||||||
|
ForwardedOutputChannel,
|
||||||
|
ForwardedErrorChannel
|
||||||
};
|
};
|
||||||
enum ExitStatus {
|
enum ExitStatus {
|
||||||
NormalExit,
|
NormalExit,
|
||||||
|
@ -813,6 +813,16 @@ pid_t QProcessPrivate::spawnChild(const char *workingDir, char **argv, char **en
|
|||||||
fd_map[1] = QT_FILENO(stdout);
|
fd_map[1] = QT_FILENO(stdout);
|
||||||
fd_map[2] = QT_FILENO(stderr);
|
fd_map[2] = QT_FILENO(stderr);
|
||||||
break;
|
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:
|
case QProcess::MergedChannels:
|
||||||
fd_map[0] = stdinChannel.pipe[0];
|
fd_map[0] = stdinChannel.pipe[0];
|
||||||
fd_map[1] = stdoutChannel.pipe[1];
|
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
|
// copy the stdout and stderr if asked to
|
||||||
if (processChannelMode != QProcess::ForwardedChannels) {
|
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
|
// merge stdout and stderr if asked to
|
||||||
if (processChannelMode == QProcess::MergedChannels) {
|
if (processChannelMode == QProcess::MergedChannels) {
|
||||||
qt_safe_dup2(fileno(stdout), fileno(stderr), 0);
|
qt_safe_dup2(fileno(stdout), fileno(stderr), 0);
|
||||||
} else {
|
} else if (processChannelMode != QProcess::ForwardedErrorChannel) {
|
||||||
qt_safe_dup2(stderrChannel.pipe[1], fileno(stderr), 0);
|
qt_safe_dup2(stderrChannel.pipe[1], fileno(stderr), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,8 @@ bool QProcessPrivate::createChannel(Channel &channel)
|
|||||||
} else {
|
} else {
|
||||||
QWindowsPipeReader *pipeReader = 0;
|
QWindowsPipeReader *pipeReader = 0;
|
||||||
if (&channel == &stdoutChannel) {
|
if (&channel == &stdoutChannel) {
|
||||||
if (processChannelMode != QProcess::ForwardedChannels) {
|
if (processChannelMode != QProcess::ForwardedChannels
|
||||||
|
&& processChannelMode != QProcess::ForwardedOutputChannel) {
|
||||||
if (!stdoutReader) {
|
if (!stdoutReader) {
|
||||||
stdoutReader = new QWindowsPipeReader(q);
|
stdoutReader = new QWindowsPipeReader(q);
|
||||||
q->connect(stdoutReader, SIGNAL(readyRead()), SLOT(_q_canReadStandardOutput()));
|
q->connect(stdoutReader, SIGNAL(readyRead()), SLOT(_q_canReadStandardOutput()));
|
||||||
@ -170,7 +171,8 @@ bool QProcessPrivate::createChannel(Channel &channel)
|
|||||||
duplicateStdWriteChannel(channel.pipe, STD_OUTPUT_HANDLE);
|
duplicateStdWriteChannel(channel.pipe, STD_OUTPUT_HANDLE);
|
||||||
}
|
}
|
||||||
} else /* if (&channel == &stderrChannel) */ {
|
} else /* if (&channel == &stderrChannel) */ {
|
||||||
if (processChannelMode != QProcess::ForwardedChannels) {
|
if (processChannelMode != QProcess::ForwardedChannels
|
||||||
|
&& processChannelMode != QProcess::ForwardedErrorChannel) {
|
||||||
if (!stderrReader) {
|
if (!stderrReader) {
|
||||||
stderrReader = new QWindowsPipeReader(q);
|
stderrReader = new QWindowsPipeReader(q);
|
||||||
q->connect(stderrReader, SIGNAL(readyRead()), SLOT(_q_canReadStandardError()));
|
q->connect(stderrReader, SIGNAL(readyRead()), SLOT(_q_canReadStandardError()));
|
||||||
|
@ -42,30 +42,41 @@
|
|||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
#include <QtCore/QProcess>
|
#include <QtCore/QProcess>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
return 13;
|
||||||
|
|
||||||
#ifndef QT_NO_PROCESS
|
#ifndef QT_NO_PROCESS
|
||||||
QProcess 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;
|
return 1;
|
||||||
|
|
||||||
process.start("testProcessEcho/testProcessEcho");
|
process.start("testProcessEcho2/testProcessEcho2");
|
||||||
|
|
||||||
if (!process.waitForStarted(5000))
|
if (!process.waitForStarted(5000))
|
||||||
return 2;
|
return 2;
|
||||||
|
|
||||||
if (process.write("forwarded\n") != 10)
|
if (process.write("forwarded") != 9)
|
||||||
return 3;
|
return 3;
|
||||||
|
|
||||||
process.closeWriteChannel();
|
process.closeWriteChannel();
|
||||||
if (!process.waitForFinished(5000))
|
if (!process.waitForFinished(5000))
|
||||||
return 4;
|
return 4;
|
||||||
|
|
||||||
if (process.bytesAvailable() != 0)
|
if ((mode == QProcess::ForwardedOutputChannel || mode == QProcess::ForwardedChannels)
|
||||||
|
&& !process.readAllStandardOutput().isEmpty())
|
||||||
return 5;
|
return 5;
|
||||||
|
if ((mode == QProcess::ForwardedErrorChannel || mode == QProcess::ForwardedChannels)
|
||||||
|
&& !process.readAllStandardError().isEmpty())
|
||||||
|
return 6;
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,7 @@ private slots:
|
|||||||
void softExitInSlots_data();
|
void softExitInSlots_data();
|
||||||
void softExitInSlots();
|
void softExitInSlots();
|
||||||
void mergedChannels();
|
void mergedChannels();
|
||||||
|
void forwardedChannels_data();
|
||||||
void forwardedChannels();
|
void forwardedChannels();
|
||||||
void atEnd();
|
void atEnd();
|
||||||
void atEnd2();
|
void atEnd2();
|
||||||
@ -1089,10 +1090,31 @@ void tst_QProcess::mergedChannels()
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
#ifndef Q_OS_WINCE
|
#ifndef Q_OS_WINCE
|
||||||
// Reading and writing to a process is not supported on Qt/CE
|
// 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()
|
void tst_QProcess::forwardedChannels()
|
||||||
{
|
{
|
||||||
|
QFETCH(int, mode);
|
||||||
|
QFETCH(QByteArray, outdata);
|
||||||
|
QFETCH(QByteArray, errdata);
|
||||||
|
|
||||||
QProcess process;
|
QProcess process;
|
||||||
process.start("testForwarding/testForwarding");
|
process.start("testForwarding/testForwarding", QStringList() << QString::number(mode));
|
||||||
QVERIFY(process.waitForStarted(5000));
|
QVERIFY(process.waitForStarted(5000));
|
||||||
QVERIFY(process.waitForFinished(5000));
|
QVERIFY(process.waitForFinished(5000));
|
||||||
const char *err;
|
const char *err;
|
||||||
@ -1103,12 +1125,13 @@ void tst_QProcess::forwardedChannels()
|
|||||||
case 3: err = "failed to write"; break;
|
case 3: err = "failed to write"; break;
|
||||||
case 4: err = "did not finish"; break;
|
case 4: err = "did not finish"; break;
|
||||||
case 5: err = "unexpected stdout"; 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;
|
default: err = "unknown exit code"; break;
|
||||||
}
|
}
|
||||||
QVERIFY2(!process.exitCode(), err);
|
QVERIFY2(!process.exitCode(), err);
|
||||||
QByteArray data = process.readAll();
|
QCOMPARE(process.readAllStandardOutput(), outdata);
|
||||||
QVERIFY(!data.isEmpty());
|
QCOMPARE(process.readAllStandardError(), errdata);
|
||||||
QVERIFY(data.contains("forwarded"));
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user