Commit 19d93d29 authored by hjk's avatar hjk

Introduce a ChannelProvider run worker

... to provide a set of urls indicating usable connection
points for 'server-using' tools (typically one, like gdbserver
and the Qml tooling, but two for mixed debugging).

Urls can describe local or tcp servers that are directly
accessible to the host tools, if needed port forwarding
could be set up when needed.

Use it as new base for GdbServerPortsGatherer for starters.

Note: Since none of the customization points for actual port
forwarding are currently provided by device implementations
only non-forwarding cases are working right now. Incidentally
this does not affect existing setups, as the only case where
it would be needed (Android/adb) have a complete custom
implementation. The medium-term plan there is of course to use
this new setup here and have the AndroidDevice implementation
only provide the forwarding, not the whole debugging (etc...)

Change-Id: I42c9783348cd430b1c435bbca56329c678ac485c
Reviewed-by: Ulf Hermann's avatarUlf Hermann <ulf.hermann@qt.io>
parent e5fae9b9
......@@ -952,15 +952,9 @@ void DebuggerRunTool::showMessage(const QString &msg, int channel, int timeout)
// GdbServerPortGatherer
GdbServerPortsGatherer::GdbServerPortsGatherer(RunControl *runControl)
: RunWorker(runControl)
: ChannelProvider(runControl, 2)
{
setDisplayName("GdbServerPortsGatherer");
connect(&m_portsGatherer, &DeviceUsedPortsGatherer::error,
this, &RunWorker::reportFailure);
connect(&m_portsGatherer, &DeviceUsedPortsGatherer::portListReady,
this, &GdbServerPortsGatherer::handlePortListReady);
m_device = runControl->device();
}
......@@ -968,50 +962,33 @@ GdbServerPortsGatherer::~GdbServerPortsGatherer()
{
}
QString GdbServerPortsGatherer::gdbServerChannel() const
Port GdbServerPortsGatherer::gdbServerPort() const
{
const QString host = m_device->sshParameters().host;
return QString("%1:%2").arg(host).arg(m_gdbServerPort.number());
QUrl url = channel(0);
return Port(url.port());
}
QUrl GdbServerPortsGatherer::qmlServer() const
QString GdbServerPortsGatherer::gdbServerChannel() const
{
QUrl server = m_device->toolControlChannel(IDevice::QmlControlChannel);
server.setPort(m_qmlServerPort.number());
return server;
QUrl url = channel(0);
const QString host = m_device->sshParameters().host;
return QString("%1:%2").arg(host).arg(url.port());
}
void GdbServerPortsGatherer::setDevice(IDevice::ConstPtr device)
Port GdbServerPortsGatherer::qmlServerPort() const
{
m_device = device;
QUrl url = channel(1);
return Port(url.port());
}
void GdbServerPortsGatherer::start()
QUrl GdbServerPortsGatherer::qmlServer() const
{
appendMessage(tr("Checking available ports..."), NormalMessageFormat);
m_portsGatherer.start(m_device);
return channel(1);
}
void GdbServerPortsGatherer::handlePortListReady()
void GdbServerPortsGatherer::setDevice(IDevice::ConstPtr device)
{
Utils::PortList portList = m_device->freePorts();
appendMessage(tr("Found %n free ports.", nullptr, portList.count()), NormalMessageFormat);
if (m_useGdbServer) {
m_gdbServerPort = m_portsGatherer.getNextFreePort(&portList);
if (!m_gdbServerPort.isValid()) {
reportFailure(tr("Not enough free ports on device for C++ debugging."));
return;
}
}
if (m_useQmlServer) {
m_qmlServerPort = m_portsGatherer.getNextFreePort(&portList);
if (!m_qmlServerPort.isValid()) {
reportFailure(tr("Not enough free ports on device for QML debugging."));
return;
}
}
// reportDone();
reportStarted();
m_device = device;
}
// GdbServerRunner
......
......@@ -147,7 +147,7 @@ private:
bool m_isDying = false;
};
class DEBUGGER_EXPORT GdbServerPortsGatherer : public ProjectExplorer::RunWorker
class DEBUGGER_EXPORT GdbServerPortsGatherer : public ProjectExplorer::ChannelProvider
{
Q_OBJECT
......@@ -157,25 +157,19 @@ public:
void setUseGdbServer(bool useIt) { m_useGdbServer = useIt; }
bool useGdbServer() const { return m_useGdbServer; }
Utils::Port gdbServerPort() const { return m_gdbServerPort; }
Utils::Port gdbServerPort() const;
QString gdbServerChannel() const;
void setUseQmlServer(bool useIt) { m_useQmlServer = useIt; }
bool useQmlServer() const { return m_useQmlServer; }
Utils::Port qmlServerPort() const { return m_qmlServerPort; }
Utils::Port qmlServerPort() const;
QUrl qmlServer() const;
void setDevice(ProjectExplorer::IDevice::ConstPtr device);
private:
void start() override;
void handlePortListReady();
ProjectExplorer::DeviceUsedPortsGatherer m_portsGatherer;
bool m_useGdbServer = false;
bool m_useQmlServer = false;
Utils::Port m_gdbServerPort;
Utils::Port m_qmlServerPort;
ProjectExplorer::IDevice::ConstPtr m_device;
};
......
......@@ -28,9 +28,12 @@
#include <projectexplorer/runnables.h>
#include <ssh/sshconnection.h>
#include <utils/port.h>
#include <utils/portlist.h>
#include <utils/qtcassert.h>
#include <utils/url.h>
using namespace QSsh;
using namespace Utils;
......@@ -207,4 +210,160 @@ void PortsGatherer::stop()
reportStopped();
}
// ChannelForwarder
/*!
\class ProjectExplorer::ChannelForwarder
\internal
\brief The class provides a \c RunWorker handling the forwarding
from one device to another.
Both endpoints are specified by \c{QUrl}s, typically with
a "tcp" or "socket" scheme.
*/
ChannelForwarder::ChannelForwarder(RunControl *runControl)
: RunWorker(runControl)
{}
void ChannelForwarder::setFromUrlGetter(const UrlGetter &urlGetter)
{
m_fromUrlGetter = urlGetter;
}
namespace Internal {
// SubChannelProvider
/*!
\class ProjectExplorer::SubChannelProvider
\internal
This is a helper RunWorker implementation to either use or not
use port forwarding for one SubChannel in the ChannelProvider
implementation.
A device implementation can provide a "ChannelForwarder"
RunWorker non-trivial implementation if needed.
By default it is assumed that no forwarding is needed, i.e.
end points provided by the shared endpoint resource provider
are directly accessible.
*/
class SubChannelProvider : public RunWorker
{
public:
SubChannelProvider(RunControl *runControl, RunWorker *sharedEndpointGatherer)
: RunWorker(runControl)
{
setDisplayName("SubChannelProvider");
m_portGatherer = qobject_cast<PortsGatherer *>(sharedEndpointGatherer);
if (m_portGatherer) {
if (auto creator = device()->workerCreator("ChannelForwarder")) {
m_channelForwarder = qobject_cast<ChannelForwarder *>(creator(runControl));
if (m_channelForwarder) {
m_channelForwarder->addStartDependency(m_portGatherer);
m_channelForwarder->setFromUrlGetter([this] {
QUrl url;
url.setScheme(urlTcpScheme());
url.setHost(device()->sshParameters().host);
url.setPort(m_portGatherer->findPort().number());
return url;
});
addStartDependency(m_channelForwarder);
}
}
}
}
void start() final
{
m_channel.setScheme(urlTcpScheme());
m_channel.setHost(device()->toolControlChannel(IDevice::ControlChannelHint()).host());
if (m_channelForwarder)
m_channel.setPort(m_channelForwarder->recordedData("LocalPort").toUInt());
else if (m_portGatherer)
m_channel.setPort(m_portGatherer->findPort().number());
reportStarted();
}
QUrl channel() const { return m_channel; }
private:
QUrl m_channel;
PortsGatherer *m_portGatherer = nullptr;
ChannelForwarder *m_channelForwarder = nullptr;
};
} // Internal
// ChannelProvider
/*!
\class ProjectExplorer::ChannelProvider
\internal
The class implements a \c RunWorker to provide
to provide a set of urls indicating usable connection end
points for 'server-using' tools (typically one, like plain
gdbserver and the Qml tooling, but two for mixed debugging).
Urls can describe local or tcp servers that are directly
accessible to the host tools.
The tool implementations can assume that any needed port
forwarding setup is setup and handled transparently by
a \c ChannelProvider instance.
If there are multiple subchannels needed that need to share a
common set of resources on the remote side, a device implementation
can provide a "SharedEndpointGatherer" RunWorker.
If none is provided, it is assumed that the shared resource
is open TCP ports, provided by the device's PortGatherer i
implementation.
FIXME: The current implementation supports only the case
of "any number of TCP channels that do not need actual
forwarding.
*/
ChannelProvider::ChannelProvider(RunControl *runControl, int requiredChannels)
: RunWorker(runControl)
{
setDisplayName("ChannelProvider");
RunWorker *sharedEndpoints = nullptr;
if (auto sharedEndpointGatherer = device()->workerCreator("SharedEndpointGatherer")) {
// null is a legit value indicating 'no need to share'.
sharedEndpoints = sharedEndpointGatherer(runControl);
} else {
sharedEndpoints = new PortsGatherer(runControl);
}
for (int i = 0; i < requiredChannels; ++i) {
auto channelProvider = new Internal::SubChannelProvider(runControl, sharedEndpoints);
m_channelProviders.append(channelProvider);
addStartDependency(channelProvider);
}
}
ChannelProvider::~ChannelProvider()
{
}
QUrl ChannelProvider::channel(int i) const
{
if (Internal::SubChannelProvider *provider = m_channelProviders.value(i))
return provider->channel();
return QUrl();
}
} // namespace ProjectExplorer
......@@ -32,8 +32,11 @@
#include <utils/portlist.h>
namespace ProjectExplorer {
namespace Internal { class DeviceUsedPortsGathererPrivate; }
class StandardRunnable;
namespace Internal {
class DeviceUsedPortsGathererPrivate;
class SubChannelProvider;
} // Internal
class PROJECTEXPLORER_EXPORT DeviceUsedPortsGatherer : public QObject
{
......@@ -82,4 +85,37 @@ private:
Utils::PortList m_portList;
};
class PROJECTEXPLORER_EXPORT ChannelForwarder : public RunWorker
{
Q_OBJECT
public:
explicit ChannelForwarder(RunControl *runControl);
using UrlGetter = std::function<QUrl()>;
void setFromUrlGetter(const UrlGetter &urlGetter);
QUrl fromUrl() const { return m_fromUrl; }
QUrl toUrl() const { return m_toUrl; }
private:
UrlGetter m_fromUrlGetter;
QUrl m_fromUrl;
QUrl m_toUrl;
};
class PROJECTEXPLORER_EXPORT ChannelProvider : public RunWorker
{
Q_OBJECT
public:
ChannelProvider(RunControl *runControl, int requiredChannels = 1);
~ChannelProvider() override;
QUrl channel(int i = 0) const;
private:
QVector<Internal::SubChannelProvider *> m_channelProviders;
};
} // namespace ProjectExplorer
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment