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: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Marcus Tillmanns
2022-12-01 07:59:35 +01:00
parent 2ffa843d40
commit 0aeec80eeb
9 changed files with 64 additions and 4 deletions

View File

@@ -1588,6 +1588,16 @@ FilePath FilePath::resolvePath(const QString &tail) const
return resolvePath(FilePath::fromString(tail)); return resolvePath(FilePath::fromString(tail));
} }
expected_str<FilePath> 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() // Cleans path part similar to QDir::cleanPath()
// - directory separators normalized (that is, platform-native // - directory separators normalized (that is, platform-native
// separators converted to "/") and redundant ones removed, and "."s and ".."s // separators converted to "/") and redundant ones removed, and "."s and ".."s

View File

@@ -238,6 +238,8 @@ public:
[[nodiscard]] static int schemeAndHostLength(const QStringView path); [[nodiscard]] static int schemeAndHostLength(const QStringView path);
static QString calcRelativePath(const QString &absolutePath, const QString &absoluteAnchorPath); static QString calcRelativePath(const QString &absolutePath, const QString &absoluteAnchorPath);
//! Returns a filepath the represents the same file on a local drive
expected_str<FilePath> localSource() const;
private: private:
friend class ::tst_fileutils; friend class ::tst_fileutils;
@@ -268,6 +270,7 @@ public:
std::function<bool(const FilePath &, const FilePath &)> ensureReachable; std::function<bool(const FilePath &, const FilePath &)> ensureReachable;
std::function<Environment(const FilePath &)> environment; std::function<Environment(const FilePath &)> environment;
std::function<bool(const FilePath &left, const FilePath &right)> isSameDevice; std::function<bool(const FilePath &left, const FilePath &right)> isSameDevice;
std::function<expected_str<FilePath>(const FilePath &)> localSource;
}; };
} // namespace Utils } // namespace Utils

View File

@@ -1165,7 +1165,9 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
showMessage("INVALID STOPPED REASON", LogWarning); 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()) { if (!nr.isEmpty() && frame.isValid()) {
// Use opportunity to update the breakpoint marker position. // Use opportunity to update the breakpoint marker position.

View File

@@ -74,7 +74,8 @@ StackFrame StackFrame::parseFrame(const GdbMi &frameMi, const DebuggerRunParamet
frame.function = frameMi["function"].data(); frame.function = frameMi["function"].data();
frame.module = frameMi["module"].data(); frame.module = frameMi["module"].data();
const FilePath debugger = rp.debugger.command.executable(); 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.line = frameMi["line"].toInt();
frame.address = frameMi["address"].toAddress(); frame.address = frameMi["address"].toAddress();
frame.context = frameMi["context"].data(); frame.context = frameMi["context"].data();

View File

@@ -144,6 +144,7 @@ public:
void changeMounts(QStringList newMounts); void changeMounts(QStringList newMounts);
bool ensureReachable(const FilePath &other); bool ensureReachable(const FilePath &other);
void shutdown(); void shutdown();
expected_str<FilePath> localSource(const FilePath &other) const;
QString containerId() { return m_container; } QString containerId() { return m_container; }
DockerDeviceData data() { return m_data; } DockerDeviceData data() { return m_data; }
@@ -828,6 +829,11 @@ bool DockerDevice::ensureReachable(const FilePath &other) const
return d->ensureReachable(other.parentDir()); return d->ensureReachable(other.parentDir());
} }
expected_str<FilePath> DockerDevice::localSource(const Utils::FilePath &other) const
{
return d->localSource(other);
}
Environment DockerDevice::systemEnvironment() const Environment DockerDevice::systemEnvironment() const
{ {
return d->environment(); return d->environment();
@@ -1125,6 +1131,27 @@ void DockerDevicePrivate::changeMounts(QStringList newMounts)
} }
} }
expected_str<FilePath> 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) bool DockerDevicePrivate::ensureReachable(const FilePath &other)
{ {
for (const QString &mount : m_data.mounts) { for (const QString &mount : m_data.mounts) {

View File

@@ -86,6 +86,7 @@ public:
bool handlesFile(const Utils::FilePath &filePath) const override; bool handlesFile(const Utils::FilePath &filePath) const override;
bool ensureReachable(const Utils::FilePath &other) const override; bool ensureReachable(const Utils::FilePath &other) const override;
Utils::expected_str<Utils::FilePath> localSource(const Utils::FilePath &other) const override;
Utils::Environment systemEnvironment() const override; Utils::Environment systemEnvironment() const override;

View File

@@ -4,6 +4,7 @@
#include "devicemanager.h" #include "devicemanager.h"
#include "idevicefactory.h" #include "idevicefactory.h"
#include "projectexplorertr.h"
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h> #include <coreplugin/messagemanager.h>
@@ -414,6 +415,13 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager
return leftDevice == rightDevice; return leftDevice == rightDevice;
}; };
deviceHooks.localSource = [](const FilePath &file) -> expected_str<FilePath> {
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 * { deviceHooks.fileAccess = [](const FilePath &filePath) -> DeviceFileAccess * {
if (!filePath.needsDevice()) if (!filePath.needsDevice())
return DesktopDeviceFileAccess::instance(); return DesktopDeviceFileAccess::instance();

View File

@@ -10,11 +10,11 @@
#include "../kit.h" #include "../kit.h"
#include "../kitinformation.h" #include "../kitinformation.h"
#include "../projectexplorertr.h"
#include "../target.h"
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <projectexplorer/target.h>
#include <utils/devicefileaccess.h> #include <utils/devicefileaccess.h>
#include <utils/displayname.h> #include <utils/displayname.h>
#include <utils/icon.h> #include <utils/icon.h>
@@ -630,6 +630,12 @@ bool IDevice::ensureReachable(const FilePath &other) const
return handlesFile(other); // Some first approximation. return handlesFile(other); // Some first approximation.
} }
expected_str<FilePath> 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) bool IDevice::prepareForBuild(const Target *target)
{ {
Q_UNUSED(target) Q_UNUSED(target)

View File

@@ -7,6 +7,7 @@
#include "idevicefwd.h" #include "idevicefwd.h"
#include <utils/id.h> #include <utils/id.h>
#include <utils/expected.h>
#include <utils/filepath.h> #include <utils/filepath.h>
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/tasktree.h> #include <utils/tasktree.h>
@@ -215,6 +216,7 @@ public:
virtual void aboutToBeRemoved() const {} virtual void aboutToBeRemoved() const {}
virtual bool ensureReachable(const Utils::FilePath &other) const; virtual bool ensureReachable(const Utils::FilePath &other) const;
virtual Utils::expected_str<Utils::FilePath> localSource(const Utils::FilePath &other) const;
virtual bool prepareForBuild(const Target *target); virtual bool prepareForBuild(const Target *target);
virtual std::optional<Utils::FilePath> clangdExecutable() const; virtual std::optional<Utils::FilePath> clangdExecutable() const;