LinuxDevice: Get rid of a marker for outputForRunInShell()

Don't rely on a synthetic marker when running a shell
command and expecting any output. The marker might always
be a part of the output data from the running command.
That's unreliable.

A solution is to do a similar thing that we are doing
inside runInShell(). We ignore the error channel there,
so it means that when calling outputForRunInShell() we
have two channels for our purposes. So, the output from
the command we pass through, and its error channel we
redirect to /dev/null. Later, we run "echo $?", which
prints the return code from the last command and we
redirect it to standard error channel. Inside
outputForRunInShell() we read the error channel as long
as we detect something there. We assert if it's being
convertible to int and we also assert that the command
itself succeeded. Afterwards we break the endless loop
and read the output channel for the first time - it
should contain all the output gathered from the running
command.

Change-Id: Ia0ad73623f813ef593c11dff9bdba4df9e524315
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Jarek Kobus
2022-03-17 02:14:41 +01:00
parent 162fc2708c
commit c94564d910

View File

@@ -57,6 +57,7 @@
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QMutex> #include <QMutex>
#include <QRegularExpression> #include <QRegularExpression>
#include <QScopeGuard>
#include <QThread> #include <QThread>
using namespace ProjectExplorer; using namespace ProjectExplorer;
@@ -233,6 +234,7 @@ public:
{ {
QTC_ASSERT(m_shell, return false); QTC_ASSERT(m_shell, return false);
QTC_CHECK(m_shell->readAllStandardOutput().isNull()); // clean possible left-overs QTC_CHECK(m_shell->readAllStandardOutput().isNull()); // clean possible left-overs
QTC_CHECK(m_shell->readAllStandardError().isNull()); // clean possible left-overs
const QByteArray prefix = !data.isEmpty() const QByteArray prefix = !data.isEmpty()
? QByteArray("echo '" + data.toBase64() + "' | base64 -d | ") : QByteArray(""); ? QByteArray("echo '" + data.toBase64() + "' | base64 -d | ") : QByteArray("");
@@ -255,26 +257,29 @@ public:
{ {
QTC_ASSERT(m_shell, return {}); QTC_ASSERT(m_shell, return {});
QTC_CHECK(m_shell->readAllStandardOutput().isNull()); // clean possible left-overs QTC_CHECK(m_shell->readAllStandardOutput().isNull()); // clean possible left-overs
QTC_CHECK(m_shell->readAllStandardError().isNull()); // clean possible left-overs
auto cleanup = qScopeGuard([this] { m_shell->readAllStandardOutput(); }); // clean on assert
static int val = 0; const QByteArray suffix = QByteArray(" 2> /dev/null \necho $? 1>&2\n");
const QByteArray delim = QString::number(++val, 16).toUtf8(); const QByteArray command = cmd.toUtf8() + suffix;
DEBUG("RUN2 " << cmd); m_shell->write(command);
const QByteArray marker = "___QTC___" + delim + "_OUTPUT_MARKER___"; DEBUG("RUN2 " << cmd.toUserOutput());
DEBUG(" CMD: " << cmd.toUtf8() + "\necho " + marker + "\n");
m_shell->write(cmd.toUtf8() + "\necho " + marker + "\n"); while (true) {
QByteArray output;
while (!output.contains(marker)) {
DEBUG("OUTPUT" << output);
m_shell->waitForReadyRead(); m_shell->waitForReadyRead();
output.append(m_shell->readAllStandardOutput()); const QByteArray error = m_shell->readAllStandardError();
if (!error.isNull()) {
bool ok = false;
const int result = error.toInt(&ok);
QTC_ASSERT(ok, return {});
QTC_ASSERT(!result, return {});
break;
} }
}
const QByteArray output = m_shell->readAllStandardOutput();
DEBUG("GOT2 " << output); DEBUG("GOT2 " << output);
LOG("Run command in shell:" << cmd << "output size:" << output.size()); LOG("Run command in shell:" << cmd << "output size:" << output.size());
const int pos = output.indexOf(marker);
if (pos >= 0)
output = output.left(pos);
DEBUG("CHOPPED2 " << output);
return output; return output;
} }