From 0aeec80eeb16f9412647124c2ebe3c4499fb0684 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 1 Dec 2022 07:59:35 +0100 Subject: [PATCH] Docker: Add Filepath::localSource() FilePath::localSource can return a filepath that represents a local version of a remote file. It is used to let the debugger select the local version of a file when debugging a remote target. Change-Id: Ieb934ef0d454e8ff55e71df41dca825974d85da7 Reviewed-by: Reviewed-by: hjk --- src/libs/utils/filepath.cpp | 10 +++++++ src/libs/utils/filepath.h | 3 +++ src/plugins/debugger/gdb/gdbengine.cpp | 4 ++- src/plugins/debugger/stackframe.cpp | 3 ++- src/plugins/docker/dockerdevice.cpp | 27 +++++++++++++++++++ src/plugins/docker/dockerdevice.h | 1 + .../devicesupport/devicemanager.cpp | 8 ++++++ .../projectexplorer/devicesupport/idevice.cpp | 10 +++++-- .../projectexplorer/devicesupport/idevice.h | 2 ++ 9 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 7f4e38d73ad..944fe2a5ce7 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -1588,6 +1588,16 @@ FilePath FilePath::resolvePath(const QString &tail) const return resolvePath(FilePath::fromString(tail)); } +expected_str FilePath::localSource() const +{ + if (!needsDevice()) + return *this; + + QTC_ASSERT(s_deviceHooks.localSource, + return make_unexpected(Tr::tr("No 'localSource' device hook set."))); + return s_deviceHooks.localSource(*this); +} + // Cleans path part similar to QDir::cleanPath() // - directory separators normalized (that is, platform-native // separators converted to "/") and redundant ones removed, and "."s and ".."s diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h index 5f5a5bad7f0..2897b278ec6 100644 --- a/src/libs/utils/filepath.h +++ b/src/libs/utils/filepath.h @@ -238,6 +238,8 @@ public: [[nodiscard]] static int schemeAndHostLength(const QStringView path); static QString calcRelativePath(const QString &absolutePath, const QString &absoluteAnchorPath); + //! Returns a filepath the represents the same file on a local drive + expected_str localSource() const; private: friend class ::tst_fileutils; @@ -268,6 +270,7 @@ public: std::function ensureReachable; std::function environment; std::function isSameDevice; + std::function(const FilePath &)> localSource; }; } // namespace Utils diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 67e0dd72e5b..b3327cac376 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -1165,7 +1165,9 @@ void GdbEngine::handleStopResponse(const GdbMi &data) showMessage("INVALID STOPPED REASON", LogWarning); } - const FilePath fileName = FilePath::fromString(fullName); + const FilePath onDevicePath = FilePath::fromString(fullName).onDevice( + runParameters().debugger.command.executable()); + const FilePath fileName = onDevicePath.localSource().value_or(onDevicePath); if (!nr.isEmpty() && frame.isValid()) { // Use opportunity to update the breakpoint marker position. diff --git a/src/plugins/debugger/stackframe.cpp b/src/plugins/debugger/stackframe.cpp index 1a68e4ae3a0..740868889e6 100644 --- a/src/plugins/debugger/stackframe.cpp +++ b/src/plugins/debugger/stackframe.cpp @@ -74,7 +74,8 @@ StackFrame StackFrame::parseFrame(const GdbMi &frameMi, const DebuggerRunParamet frame.function = frameMi["function"].data(); frame.module = frameMi["module"].data(); const FilePath debugger = rp.debugger.command.executable(); - frame.file = FilePath::fromString(frameMi["file"].data()).onDevice(debugger); + const FilePath onDevicePath = FilePath::fromString(frameMi["file"].data()).onDevice(debugger); + frame.file = onDevicePath.localSource().value_or(onDevicePath); frame.line = frameMi["line"].toInt(); frame.address = frameMi["address"].toAddress(); frame.context = frameMi["context"].data(); diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index c37e2d106e2..a71ccfef50a 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -144,6 +144,7 @@ public: void changeMounts(QStringList newMounts); bool ensureReachable(const FilePath &other); void shutdown(); + expected_str localSource(const FilePath &other) const; QString containerId() { return m_container; } DockerDeviceData data() { return m_data; } @@ -828,6 +829,11 @@ bool DockerDevice::ensureReachable(const FilePath &other) const return d->ensureReachable(other.parentDir()); } +expected_str DockerDevice::localSource(const Utils::FilePath &other) const +{ + return d->localSource(other); +} + Environment DockerDevice::systemEnvironment() const { return d->environment(); @@ -1125,6 +1131,27 @@ void DockerDevicePrivate::changeMounts(QStringList newMounts) } } +expected_str DockerDevicePrivate::localSource(const FilePath &other) const +{ + const auto devicePath = FilePath::fromString(other.path()); + for (const TemporaryMountInfo &info : m_temporaryMounts) { + if (devicePath.isChildOf(info.containerPath)) { + const FilePath relativePath = devicePath.relativeChildPath(info.containerPath); + return info.path.pathAppended(relativePath.path()); + } + } + + for (const QString &mount : m_data.mounts) { + const FilePath mountPoint = FilePath::fromString(mount); + if (devicePath.isChildOf(mountPoint)) { + const FilePath relativePath = devicePath.relativeChildPath(mountPoint); + return mountPoint.pathAppended(relativePath.path()); + } + } + + return make_unexpected(Tr::tr("localSource: No mount point found for %1").arg(other.toString())); +} + bool DockerDevicePrivate::ensureReachable(const FilePath &other) { for (const QString &mount : m_data.mounts) { diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index 4c5cd20e921..03bfc3f2a6c 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -86,6 +86,7 @@ public: bool handlesFile(const Utils::FilePath &filePath) const override; bool ensureReachable(const Utils::FilePath &other) const override; + Utils::expected_str localSource(const Utils::FilePath &other) const override; Utils::Environment systemEnvironment() const override; diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index f65839bacc4..f23bf701138 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -4,6 +4,7 @@ #include "devicemanager.h" #include "idevicefactory.h" +#include "projectexplorertr.h" #include #include @@ -414,6 +415,13 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique expected_str { + auto device = DeviceManager::deviceForPath(file); + if (!device) + return make_unexpected(Tr::tr("No device for path \"%1\"").arg(file.toUserOutput())); + return device->localSource(file); + }; + deviceHooks.fileAccess = [](const FilePath &filePath) -> DeviceFileAccess * { if (!filePath.needsDevice()) return DesktopDeviceFileAccess::instance(); diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index 83cfa72fd3c..e1cc72bc717 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -10,11 +10,11 @@ #include "../kit.h" #include "../kitinformation.h" +#include "../projectexplorertr.h" +#include "../target.h" #include -#include - #include #include #include @@ -630,6 +630,12 @@ bool IDevice::ensureReachable(const FilePath &other) const return handlesFile(other); // Some first approximation. } +expected_str IDevice::localSource(const Utils::FilePath &other) const +{ + Q_UNUSED(other); + return make_unexpected(Tr::tr("localSource() not implemented for this device type.")); +} + bool IDevice::prepareForBuild(const Target *target) { Q_UNUSED(target) diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index b61e9c94715..e2dfe57ffd3 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -7,6 +7,7 @@ #include "idevicefwd.h" #include +#include #include #include #include @@ -215,6 +216,7 @@ public: virtual void aboutToBeRemoved() const {} virtual bool ensureReachable(const Utils::FilePath &other) const; + virtual Utils::expected_str localSource(const Utils::FilePath &other) const; virtual bool prepareForBuild(const Target *target); virtual std::optional clangdExecutable() const;