forked from qt-creator/qt-creator
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 <ulf.hermann@qt.io>
This commit is contained in:
@@ -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,17 +962,28 @@ GdbServerPortsGatherer::~GdbServerPortsGatherer()
|
||||
{
|
||||
}
|
||||
|
||||
Port GdbServerPortsGatherer::gdbServerPort() const
|
||||
{
|
||||
QUrl url = channel(0);
|
||||
return Port(url.port());
|
||||
}
|
||||
|
||||
QString GdbServerPortsGatherer::gdbServerChannel() const
|
||||
{
|
||||
QUrl url = channel(0);
|
||||
const QString host = m_device->sshParameters().host;
|
||||
return QString("%1:%2").arg(host).arg(m_gdbServerPort.number());
|
||||
return QString("%1:%2").arg(host).arg(url.port());
|
||||
}
|
||||
|
||||
Port GdbServerPortsGatherer::qmlServerPort() const
|
||||
{
|
||||
QUrl url = channel(1);
|
||||
return Port(url.port());
|
||||
}
|
||||
|
||||
QUrl GdbServerPortsGatherer::qmlServer() const
|
||||
{
|
||||
QUrl server = m_device->toolControlChannel(IDevice::QmlControlChannel);
|
||||
server.setPort(m_qmlServerPort.number());
|
||||
return server;
|
||||
return channel(1);
|
||||
}
|
||||
|
||||
void GdbServerPortsGatherer::setDevice(IDevice::ConstPtr device)
|
||||
@@ -986,34 +991,6 @@ void GdbServerPortsGatherer::setDevice(IDevice::ConstPtr device)
|
||||
m_device = device;
|
||||
}
|
||||
|
||||
void GdbServerPortsGatherer::start()
|
||||
{
|
||||
appendMessage(tr("Checking available ports..."), NormalMessageFormat);
|
||||
m_portsGatherer.start(m_device);
|
||||
}
|
||||
|
||||
void GdbServerPortsGatherer::handlePortListReady()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
// GdbServerRunner
|
||||
|
||||
GdbServerRunner::GdbServerRunner(RunControl *runControl, GdbServerPortsGatherer *portsGatherer)
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user