Utils: Join SynchronousProcess::run and runBlocking implementations

Make functionality dependent on an (intentionally ugly)
setProcessUserEventWhileRunning call.

Also, back-paddle a bit on API combination of QtcProcess and
SynchronousPrceoss for now and prevent the QtcProcess-and-
runBlocking and SynchronousProcess-and-start combinations.

Goal is still to have all in QtcProcess in the end, but this
may take a while.

Change-Id: Ic146ec5db0ab8dc9613e5b2af5f4dc90bc7465ca
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
hjk
2021-05-20 13:14:15 +02:00
parent c36b50f401
commit 770d87709e
12 changed files with 102 additions and 106 deletions

View File

@@ -134,6 +134,7 @@ public:
bool m_timeOutMessageBoxEnabled = false;
bool m_waitingForUser = false;
bool m_isSynchronousProcess = false;
bool m_processUserEvents = false;
};
void QtcProcessPrivate::clearForRun()
@@ -744,6 +745,11 @@ SynchronousProcess::~SynchronousProcess()
disconnect(this, nullptr, this, nullptr);
}
void SynchronousProcess::setProcessUserEventWhileRunning()
{
d->m_processUserEvents = true;
}
void QtcProcess::setTimeoutS(int timeoutS)
{
QTC_CHECK(d->m_isSynchronousProcess);
@@ -783,14 +789,14 @@ static bool isGuiThread()
}
#endif
void QtcProcess::run()
void SynchronousProcess::runBlocking()
{
QTC_CHECK(d->m_isSynchronousProcess);
// FIXME: Implement properly
if (d->m_commandLine.executable().needsDevice()) {
// writeData ?
start();
QtcProcess::start();
waitForFinished();
@@ -801,99 +807,75 @@ void QtcProcess::run()
return;
};
qCDebug(processLog).noquote() << "Starting:" << d->m_commandLine.toUserOutput();
qCDebug(processLog).noquote() << "Starting blocking:" << d->m_commandLine.toUserOutput()
<< " process user events: " << d->m_processUserEvents;
ExecuteOnDestruction logResult([this] { qCDebug(processLog) << *this; });
d->clearForRun();
d->m_binary = d->m_commandLine.executable();
// using QProcess::start() and passing program, args and OpenMode results in a different
// quoting of arguments than using QProcess::setArguments() beforehand and calling start()
// only with the OpenMode
if (!d->m_writeData.isEmpty()) {
connect(this, &QProcess::started, this, [this] {
write(d->m_writeData);
closeWriteChannel();
});
}
setOpenMode(d->m_writeData.isEmpty() ? QIODevice::ReadOnly : QIODevice::ReadWrite);
start();
// On Windows, start failure is triggered immediately if the
// executable cannot be found in the path. Do not start the
// event loop in that case.
if (!d->m_startFailure) {
d->m_timer.start();
if (d->m_processUserEvents) {
if (!d->m_writeData.isEmpty()) {
connect(this, &QProcess::started, this, [this] {
write(d->m_writeData);
closeWriteChannel();
});
}
setOpenMode(d->m_writeData.isEmpty() ? QIODevice::ReadOnly : QIODevice::ReadWrite);
QtcProcess::start();
// On Windows, start failure is triggered immediately if the
// executable cannot be found in the path. Do not start the
// event loop in that case.
if (!d->m_startFailure) {
d->m_timer.start();
#ifdef QT_GUI_LIB
if (isGuiThread())
QApplication::setOverrideCursor(Qt::WaitCursor);
if (isGuiThread())
QApplication::setOverrideCursor(Qt::WaitCursor);
#endif
d->m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
d->m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
d->m_stdOut.append(readAllStandardOutput(), false);
d->m_stdErr.append(readAllStandardError(), false);
d->m_timer.stop();
#ifdef QT_GUI_LIB
if (isGuiThread())
QApplication::restoreOverrideCursor();
#endif
}
} else {
setOpenMode(QIODevice::ReadOnly);
QtcProcess::start();
if (!waitForStarted(d->m_maxHangTimerCount * 1000)) {
d->m_result = QtcProcess::StartFailed;
return;
}
closeWriteChannel();
if (!waitForFinished(d->m_maxHangTimerCount * 1000)) {
d->m_result = QtcProcess::Hang;
terminate();
if (!waitForFinished(1000)) {
kill();
waitForFinished(1000);
}
}
if (state() != QProcess::NotRunning)
return;
d->m_exitCode = exitCode();
if (d->m_result == QtcProcess::StartFailed) {
if (exitStatus() != QProcess::NormalExit)
d->m_result = QtcProcess::TerminatedAbnormally;
else
d->m_result = d->interpretExitCode(d->m_exitCode);
}
d->m_stdOut.append(readAllStandardOutput(), false);
d->m_stdErr.append(readAllStandardError(), false);
d->m_timer.stop();
#ifdef QT_GUI_LIB
if (isGuiThread())
QApplication::restoreOverrideCursor();
#endif
}
}
void QtcProcess::runBlocking()
{
QTC_CHECK(d->m_isSynchronousProcess);
// FIXME: Implement properly
if (d->m_commandLine.executable().needsDevice()) {
// writeData ?
start();
waitForFinished();
d->m_result = QtcProcess::Finished;
d->m_exitCode = exitCode();
d->m_stdOut.rawData += readAllStandardOutput();
d->m_stdErr.rawData += readAllStandardError();
return;
};
qCDebug(processLog).noquote() << "Starting blocking:" << d->m_commandLine.toUserOutput();
ExecuteOnDestruction logResult([this] { qCDebug(processLog) << *this; });
d->clearForRun();
d->m_binary = d->m_commandLine.executable();
setOpenMode(QIODevice::ReadOnly);
start();
if (!waitForStarted(d->m_maxHangTimerCount * 1000)) {
d->m_result = QtcProcess::StartFailed;
return;
}
closeWriteChannel();
if (!waitForFinished(d->m_maxHangTimerCount * 1000)) {
d->m_result = QtcProcess::Hang;
terminate();
if (!waitForFinished(1000)) {
kill();
waitForFinished(1000);
}
}
if (state() != QProcess::NotRunning)
return;
d->m_exitCode = exitCode();
if (d->m_result == QtcProcess::StartFailed) {
if (exitStatus() != QProcess::NormalExit)
d->m_result = QtcProcess::TerminatedAbnormally;
else
d->m_result = d->interpretExitCode(d->m_exitCode);
}
d->m_stdOut.append(readAllStandardOutput(), false);
d->m_stdErr.append(readAllStandardError(), false);
}
void QtcProcess::setStdOutCallback(const std::function<void (const QString &)> &callback)
{
QTC_CHECK(d->m_isSynchronousProcess);

View File

@@ -89,16 +89,6 @@ public:
// FIXME: This is currently only used in run(), not in start()
void setWriteData(const QByteArray &writeData);
// Starts a nested event loop and runs the command
void run();
// Starts the command blocking the UI fully
void runBlocking();
// FIXME: Remove. Kept for downstream for a while.
void run(const CommandLine &cmd) { setCommand(cmd); run(); }
void runBlocking(const CommandLine &cmd) { setCommand(cmd); runBlocking(); }
void setStdOutCallback(const std::function<void(const QString &)> &callback);
void setStdErrCallback(const std::function<void(const QString &)> &callback);
@@ -156,6 +146,16 @@ class QTCREATOR_UTILS_EXPORT SynchronousProcess : public QtcProcess
public:
SynchronousProcess();
~SynchronousProcess() override;
// Force the use of 'runBlocking' for now.
void start() = delete;
// This starts a nested event loop when running the command.
void setProcessUserEventWhileRunning(); // Avoid.
// Starts the command and waits for finish. User input processing depends
// on whether setProcessUserEventWhileRunning was called.
void runBlocking();
};
} // namespace Utils

View File

@@ -422,7 +422,8 @@ void ShellCommand::runSynchronous(SynchronousProcess &process,
if (d->m_codec)
process.setCodec(d->m_codec);
process.run();
process.setProcessUserEventWhileRunning();
process.runBlocking();
}
const QVariant &ShellCommand::cookie() const

View File

@@ -1010,7 +1010,8 @@ QAbstractItemModel *AndroidBuildApkStep::keystoreCertificates()
SynchronousProcess keytoolProc;
keytoolProc.setTimeoutS(30);
keytoolProc.setCommand({AndroidConfigurations::currentConfig().keytoolPath(), params});
keytoolProc.run();
keytoolProc.setProcessUserEventWhileRunning();
keytoolProc.runBlocking();
if (keytoolProc.result() > QtcProcess::FinishedError)
QMessageBox::critical(nullptr, tr("Error"), tr("Failed to run keytool."));
else

View File

@@ -199,7 +199,8 @@ void AndroidCreateKeystoreCertificate::buttonBoxAccepted()
SynchronousProcess genKeyCertProc;
genKeyCertProc.setTimeoutS(15);
genKeyCertProc.setCommand(command);
genKeyCertProc.run();
genKeyCertProc.setProcessUserEventWhileRunning();
genKeyCertProc.runBlocking();
if (genKeyCertProc.result() != QtcProcess::Finished || genKeyCertProc.exitCode() != 0) {
QMessageBox::critical(this, tr("Error"),

View File

@@ -483,7 +483,8 @@ void AndroidDeployQtStep::runCommand(const CommandLine &command)
OutputFormat::NormalMessage);
buildProc.setCommand(command);
buildProc.run();
buildProc.setProcessUserEventWhileRunning();
buildProc.runBlocking();
if (buildProc.result() != QtcProcess::Finished || buildProc.exitCode() != 0) {
const QString error = buildProc.exitMessage();
emit addOutput(error, OutputFormat::ErrorMessage);

View File

@@ -539,7 +539,8 @@ bool AndroidManager::checkKeystorePassword(const QString &keystorePath, const QS
SynchronousProcess proc;
proc.setTimeoutS(10);
proc.setCommand(cmd);
proc.run();
proc.setProcessUserEventWhileRunning();
proc.runBlocking();
return proc.result() == QtcProcess::Finished && proc.exitCode() == 0;
}
@@ -556,7 +557,8 @@ bool AndroidManager::checkCertificatePassword(const QString &keystorePath, const
SynchronousProcess proc;
proc.setTimeoutS(10);
proc.setCommand({AndroidConfigurations::currentConfig().keytoolPath(), arguments});
proc.run();
proc.setProcessUserEventWhileRunning();
proc.runBlocking();
return proc.result() == QtcProcess::Finished && proc.exitCode() == 0;
}
@@ -570,7 +572,8 @@ bool AndroidManager::checkCertificateExists(const QString &keystorePath,
SynchronousProcess proc;
proc.setTimeoutS(10);
proc.setCommand({AndroidConfigurations::currentConfig().keytoolPath(), arguments});
proc.run();
proc.setProcessUserEventWhileRunning();
proc.runBlocking();
return proc.result() == QtcProcess::Finished && proc.exitCode() == 0;
}
@@ -727,7 +730,8 @@ SdkToolResult AndroidManager::runCommand(const CommandLine &command,
cmdProc.setWriteData(writeData);
qCDebug(androidManagerLog) << "Running command (sync):" << command.toUserOutput();
cmdProc.setCommand(command);
cmdProc.run();
cmdProc.setProcessUserEventWhileRunning();
cmdProc.runBlocking();
cmdResult.m_stdOut = cmdProc.stdOut().trimmed();
cmdResult.m_stdErr = cmdProc.stdErr().trimmed();
cmdResult.m_success = cmdProc.result() == QtcProcess::Finished;

View File

@@ -155,7 +155,8 @@ static bool sdkManagerCommand(const AndroidConfig &config, const QStringList &ar
proc.setTimeoutS(timeout);
proc.setTimeOutMessageBoxEnabled(true);
proc.setCommand({config.sdkManagerToolPath(), newArgs});
proc.run();
proc.setProcessUserEventWhileRunning();
proc.runBlocking();
if (output)
*output = proc.allOutput();
return proc.result() == QtcProcess::Finished;
@@ -196,7 +197,8 @@ static void sdkManagerCommand(const AndroidConfig &config, const QStringList &ar
&proc, &SynchronousProcess::stopProcess);
}
proc.setCommand({config.sdkManagerToolPath(), newArgs});
proc.run();
proc.setProcessUserEventWhileRunning();
proc.runBlocking();
if (assertionFound) {
output.success = false;
output.stdOutput = proc.stdOut();

View File

@@ -2359,7 +2359,8 @@ QString ClearCasePluginPrivate::runExtDiff(const QString &workingDir, const QStr
process.setWorkingDirectory(workingDir);
process.setCodec(outputCodec ? outputCodec : QTextCodec::codecForName("UTF-8"));
process.setCommand(diff);
process.run();
process.setProcessUserEventWhileRunning();
process.runBlocking();
if (process.result() != QtcProcess::Finished)
return QString();
return process.allOutput();

View File

@@ -68,7 +68,8 @@ void XcodeProbe::detectDeveloperPaths()
Utils::SynchronousProcess selectedXcode;
selectedXcode.setTimeoutS(5);
selectedXcode.setCommand({"/usr/bin/xcode-select", {"--print-path"}});
selectedXcode.run();
selectedXcode.setProcessUserEventWhileRunning();
selectedXcode.runBlocking();
if (selectedXcode.result() != QtcProcess::Finished)
qCWarning(probeLog)
<< QString::fromLatin1("Could not detect selected Xcode using xcode-select");

View File

@@ -1273,7 +1273,8 @@ PerforceResponse PerforcePluginPrivate::synchronousProcess(const QString &workin
}
process.setTimeOutMessageBoxEnabled(true);
process.setCommand({m_settings.p4BinaryPath.value(), args});
process.run();
process.setProcessUserEventWhileRunning();
process.runBlocking();
PerforceResponse response;
response.error = true;

View File

@@ -113,7 +113,8 @@ static bool
qDebug("In %s, running:\n%s\n", qPrintable(workingDirectory),
qPrintable(cmd.toUserOutput()));
process.setCommand(cmd);
process.run();
process.setProcessUserEventWhileRunning();
process.runBlocking();
if (process.result() != Utils::QtcProcess::Finished) {
*errorMessage = QString("Generator script failed: %1").arg(process.exitMessage());
const QString stdErr = process.stdErr();