RemoteLinux: Support terminal aspect

This is possible now that the remote process is started via a local
tool.

[ChangeLog] Remote Linux applications can be run in a terminal now.

Change-Id: I9f7df87563a18880d85c6d16ad18fb10a4d9f0e0
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2019-01-10 09:21:26 +01:00
parent bad0d193e5
commit 8a2327068e
9 changed files with 68 additions and 19 deletions

View File

@@ -31,6 +31,7 @@
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QDir>
#include <QTimer> #include <QTimer>
#include <cstring> #include <cstring>
@@ -84,20 +85,15 @@ SshRemoteProcess::SshRemoteProcess(const QByteArray &command, const QStringList
void SshRemoteProcess::doStart() void SshRemoteProcess::doStart()
{ {
QTC_ASSERT(!isRunning(), return); QTC_ASSERT(!isRunning(), return);
QStringList args = QStringList("-q") << d->connectionArgs; const QStringList args = fullLocalCommandLine();
if (d->useTerminal)
args.prepend("-tt");
if (!d->displayName.isEmpty()) { if (!d->displayName.isEmpty()) {
args.prepend("-X");
QProcessEnvironment env = processEnvironment(); QProcessEnvironment env = processEnvironment();
env.insert("DISPLAY", d->displayName); env.insert("DISPLAY", d->displayName);
setProcessEnvironment(env); setProcessEnvironment(env);
} }
if (!d->remoteCommand.isEmpty()) qCDebug(sshLog) << "starting remote process:" << QDir::toNativeSeparators(args.first())
args << QLatin1String(d->remoteCommand);
qCDebug(sshLog) << "starting remote process:" << SshSettings::sshFilePath().toUserOutput()
<< args; << args;
QProcess::start(SshSettings::sshFilePath().toString(), args); QProcess::start(args.first(), args.mid(1));
} }
SshRemoteProcess::~SshRemoteProcess() SshRemoteProcess::~SshRemoteProcess()
@@ -125,4 +121,17 @@ bool SshRemoteProcess::isRunning() const
return state() == QProcess::Running; return state() == QProcess::Running;
} }
QStringList SshRemoteProcess::fullLocalCommandLine() const
{
QStringList args = QStringList("-q") << d->connectionArgs;
if (d->useTerminal)
args.prepend("-tt");
if (!d->displayName.isEmpty())
args.prepend("-X");
if (!d->remoteCommand.isEmpty())
args << QLatin1String(d->remoteCommand);
args.prepend(SshSettings::sshFilePath().toString());
return args;
}
} // namespace QSsh } // namespace QSsh

View File

@@ -50,6 +50,7 @@ public:
void start(); void start();
bool isRunning() const; bool isRunning() const;
QStringList fullLocalCommandLine() const;
signals: signals:
void done(const QString &error); void done(const QString &error);

View File

@@ -411,6 +411,7 @@ void ApplicationLauncherPrivate::start(const Runnable &runnable, const IDevice::
m_success = true; m_success = true;
m_deviceProcess = device->createProcess(this); m_deviceProcess = device->createProcess(this);
m_deviceProcess->setRunInTerminal(m_useTerminal);
connect(m_deviceProcess, &DeviceProcess::started, connect(m_deviceProcess, &DeviceProcess::started,
q, &ApplicationLauncher::remoteProcessStarted); q, &ApplicationLauncher::remoteProcessStarted);
connect(m_deviceProcess, &DeviceProcess::readyReadStandardOutput, connect(m_deviceProcess, &DeviceProcess::readyReadStandardOutput,

View File

@@ -57,6 +57,9 @@ public:
virtual qint64 write(const QByteArray &data) = 0; virtual qint64 write(const QByteArray &data) = 0;
void setRunInTerminal(bool term) { m_runInTerminal = term; }
bool runInTerminal() const { return m_runInTerminal; }
signals: signals:
void started(); void started();
void finished(); void finished();
@@ -71,6 +74,7 @@ protected:
private: private:
const QSharedPointer<const IDevice> m_device; const QSharedPointer<const IDevice> m_device;
bool m_runInTerminal = false;
}; };
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -31,12 +31,15 @@
#include <ssh/sshconnection.h> #include <ssh/sshconnection.h>
#include <ssh/sshconnectionmanager.h> #include <ssh/sshconnectionmanager.h>
#include <ssh/sshremoteprocess.h> #include <ssh/sshremoteprocess.h>
#include <utils/consoleprocess.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QString> #include <QString>
#include <QTimer> #include <QTimer>
using namespace Utils;
namespace ProjectExplorer { namespace ProjectExplorer {
enum class Signal { Interrupt, Terminate, Kill }; enum class Signal { Interrupt, Terminate, Kill };
@@ -49,6 +52,7 @@ public:
SshDeviceProcess * const q; SshDeviceProcess * const q;
QSsh::SshConnection *connection = nullptr; QSsh::SshConnection *connection = nullptr;
QSsh::SshRemoteProcessPtr process; QSsh::SshRemoteProcessPtr process;
ConsoleProcess consoleProcess;
Runnable runnable; Runnable runnable;
QString errorMessage; QString errorMessage;
QProcess::ExitStatus exitStatus = QProcess::NormalExit; QProcess::ExitStatus exitStatus = QProcess::NormalExit;
@@ -114,11 +118,15 @@ void SshDeviceProcess::interrupt()
void SshDeviceProcess::terminate() void SshDeviceProcess::terminate()
{ {
d->doSignal(Signal::Terminate); d->doSignal(Signal::Terminate);
if (runInTerminal())
d->consoleProcess.stop();
} }
void SshDeviceProcess::kill() void SshDeviceProcess::kill()
{ {
d->doSignal(Signal::Kill); d->doSignal(Signal::Kill);
if (runInTerminal())
d->consoleProcess.stop();
} }
QProcess::ProcessState SshDeviceProcess::state() const QProcess::ProcessState SshDeviceProcess::state() const
@@ -178,16 +186,32 @@ void SshDeviceProcess::handleConnected()
d->setState(SshDeviceProcessPrivate::Connected); d->setState(SshDeviceProcessPrivate::Connected);
d->process = d->connection->createRemoteProcess(fullCommandLine(d->runnable).toUtf8()); d->process = d->connection->createRemoteProcess(fullCommandLine(d->runnable).toUtf8());
connect(d->process.get(), &QSsh::SshRemoteProcess::started, this, &SshDeviceProcess::handleProcessStarted);
connect(d->process.get(), &QSsh::SshRemoteProcess::done, this, &SshDeviceProcess::handleProcessFinished);
connect(d->process.get(), &QSsh::SshRemoteProcess::readyReadStandardOutput, this, &SshDeviceProcess::handleStdout);
connect(d->process.get(), &QSsh::SshRemoteProcess::readyReadStandardError, this, &SshDeviceProcess::handleStderr);
const QString display = d->displayName(); const QString display = d->displayName();
if (!display.isEmpty()) if (!display.isEmpty())
d->process->requestX11Forwarding(display); d->process->requestX11Forwarding(display);
if (runInTerminal()) {
d->process->requestTerminal();
const QStringList cmdLine = d->process->fullLocalCommandLine();
connect(&d->consoleProcess,
static_cast<void (ConsoleProcess::*)(QProcess::ProcessError)>(&ConsoleProcess::error),
this, &DeviceProcess::error);
connect(&d->consoleProcess, &ConsoleProcess::processStarted,
this, &SshDeviceProcess::handleProcessStarted);
connect(&d->consoleProcess, &ConsoleProcess::stubStopped,
this, [this] { handleProcessFinished(d->consoleProcess.errorString()); });
d->consoleProcess.start(cmdLine.first(), cmdLine.mid(1).join(' '));
} else {
connect(d->process.get(), &QSsh::SshRemoteProcess::started,
this, &SshDeviceProcess::handleProcessStarted);
connect(d->process.get(), &QSsh::SshRemoteProcess::done,
this, &SshDeviceProcess::handleProcessFinished);
connect(d->process.get(), &QSsh::SshRemoteProcess::readyReadStandardOutput,
this, &SshDeviceProcess::handleStdout);
connect(d->process.get(), &QSsh::SshRemoteProcess::readyReadStandardError,
this, &SshDeviceProcess::handleStderr);
d->process->start(); d->process->start();
} }
}
void SshDeviceProcess::handleConnectionError() void SshDeviceProcess::handleConnectionError()
{ {
@@ -226,7 +250,7 @@ void SshDeviceProcess::handleProcessStarted()
void SshDeviceProcess::handleProcessFinished(const QString &error) void SshDeviceProcess::handleProcessFinished(const QString &error)
{ {
d->errorMessage = error; d->errorMessage = error;
d->exitCode = d->process->exitCode(); d->exitCode = runInTerminal() ? d->consoleProcess.exitCode() : d->process->exitCode();
d->setState(SshDeviceProcessPrivate::Inactive); d->setState(SshDeviceProcessPrivate::Inactive);
emit finished(); emit finished();
} }
@@ -327,6 +351,7 @@ void SshDeviceProcess::SshDeviceProcessPrivate::setState(SshDeviceProcess::SshDe
killOperation.clear(); killOperation.clear();
} }
killTimer.stop(); killTimer.stop();
consoleProcess.disconnect();
if (process) if (process)
process->disconnect(q); process->disconnect(q);
if (connection) { if (connection) {
@@ -338,6 +363,7 @@ void SshDeviceProcess::SshDeviceProcessPrivate::setState(SshDeviceProcess::SshDe
qint64 SshDeviceProcess::write(const QByteArray &data) qint64 SshDeviceProcess::write(const QByteArray &data)
{ {
QTC_ASSERT(!runInTerminal(), return -1);
return d->process->write(data); return d->process->write(data);
} }

View File

@@ -54,7 +54,7 @@ void LinuxDeviceProcess::setRcFilesToSource(const QStringList &filePaths)
QByteArray LinuxDeviceProcess::readAllStandardOutput() QByteArray LinuxDeviceProcess::readAllStandardOutput()
{ {
QByteArray output = SshDeviceProcess::readAllStandardOutput(); QByteArray output = SshDeviceProcess::readAllStandardOutput();
if (m_processId != 0) if (m_processId != 0 || runInTerminal())
return output; return output;
m_processIdString.append(output); m_processIdString.append(output);
@@ -91,9 +91,11 @@ QString LinuxDeviceProcess::fullCommandLine(const Runnable &runnable) const
envString.append(env.key(it)).append(QLatin1String("='")).append(env.value(it)) envString.append(env.key(it)).append(QLatin1String("='")).append(env.value(it))
.append(QLatin1Char('\'')); .append(QLatin1Char('\''));
} }
if (!runInTerminal())
fullCommandLine.append("echo $$ && "); fullCommandLine.append("echo $$ && ");
if (!envString.isEmpty()) if (!envString.isEmpty())
fullCommandLine.append(envString); fullCommandLine.append(envString);
if (!runInTerminal())
fullCommandLine.append(" exec "); fullCommandLine.append(" exec ");
fullCommandLine.append(quote(runnable.executable)); fullCommandLine.append(quote(runnable.executable));
if (!runnable.commandLineArguments.isEmpty()) { if (!runnable.commandLineArguments.isEmpty()) {

View File

@@ -60,6 +60,8 @@ RemoteLinuxCustomRunConfiguration::RemoteLinuxCustomRunConfiguration(Target *tar
addAspect<ArgumentsAspect>(); addAspect<ArgumentsAspect>();
addAspect<WorkingDirectoryAspect>(); addAspect<WorkingDirectoryAspect>();
if (HostOsInfo::isAnyUnixHost())
addAspect<TerminalAspect>();
addAspect<RemoteLinuxEnvironmentAspect>(target); addAspect<RemoteLinuxEnvironmentAspect>(target);
if (Utils::HostOsInfo::isAnyUnixHost()) if (Utils::HostOsInfo::isAnyUnixHost())
addAspect<X11ForwardingAspect>(); addAspect<X11ForwardingAspect>();

View File

@@ -34,7 +34,7 @@ namespace RemoteLinux {
namespace Internal { namespace Internal {
LinuxDeviceDebugSupport::LinuxDeviceDebugSupport(RunControl *runControl) LinuxDeviceDebugSupport::LinuxDeviceDebugSupport(RunControl *runControl)
: DebuggerRunTool(runControl) : DebuggerRunTool(runControl, nullptr, false)
{ {
setId("LinuxDeviceDebugSupport"); setId("LinuxDeviceDebugSupport");

View File

@@ -38,6 +38,8 @@
#include <qtsupport/qtoutputformatter.h> #include <qtsupport/qtoutputformatter.h>
#include <utils/hostosinfo.h>
using namespace ProjectExplorer; using namespace ProjectExplorer;
using namespace Utils; using namespace Utils;
@@ -60,6 +62,8 @@ RemoteLinuxRunConfiguration::RemoteLinuxRunConfiguration(Target *target, Core::I
addAspect<ArgumentsAspect>(); addAspect<ArgumentsAspect>();
addAspect<WorkingDirectoryAspect>(); addAspect<WorkingDirectoryAspect>();
if (HostOsInfo::isAnyUnixHost())
addAspect<TerminalAspect>();
addAspect<RemoteLinuxEnvironmentAspect>(target); addAspect<RemoteLinuxEnvironmentAspect>(target);
if (id == IdPrefix && Utils::HostOsInfo::isAnyUnixHost()) if (id == IdPrefix && Utils::HostOsInfo::isAnyUnixHost())
addAspect<X11ForwardingAspect>(); addAspect<X11ForwardingAspect>();