diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index 57b1be11162..d8cb9483723 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -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) diff --git a/src/plugins/debugger/debuggerruncontrol.h b/src/plugins/debugger/debuggerruncontrol.h index 5ab3e8efa8e..7d7cb6cafbb 100644 --- a/src/plugins/debugger/debuggerruncontrol.h +++ b/src/plugins/debugger/debuggerruncontrol.h @@ -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; }; diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp index 335933bee27..69193788b5e 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp +++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp @@ -28,9 +28,12 @@ #include +#include + #include #include #include +#include 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(sharedEndpointGatherer); + if (m_portGatherer) { + if (auto creator = device()->workerCreator("ChannelForwarder")) { + m_channelForwarder = qobject_cast(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 diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h index cb1508aae7c..f92abea4fa1 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h +++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h @@ -32,8 +32,11 @@ #include 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; + 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 m_channelProviders; +}; + } // namespace ProjectExplorer