aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcus Tillmanns <marcus.tillmanns@qt.io>2022-10-05 15:42:14 +0200
committerMarcus Tillmanns <marcus.tillmanns@qt.io>2022-10-11 08:53:02 +0000
commitae58d373b02a4a1e3391aa7f6df17d103db1a060 (patch)
tree8c1d7502c723849dcea408990716fed5ffe16d8b
parente5e90ad9310f6f662e4eae8dbf2c5b7f8a279168 (diff)
Add FSEngine FilePath Cache
To speed up file dialogs we introduce a 1 minute cache for the FilePathInfo. A new version of "IDevice::iterateDirectories" allows implementations to provide the FilePathInfo directly. DockerDevice implements fetching the filePathInfo during iterateDirectories which greatly improves the speed again. Change-Id: I24ac16adb2478cbf16a22012e72fcb8910dcdac5 Reviewed-by: hjk <hjk@qt.io>
-rw-r--r--src/libs/utils/CMakeLists.txt2
-rw-r--r--src/libs/utils/filepath.cpp78
-rw-r--r--src/libs/utils/filepath.h25
-rw-r--r--src/libs/utils/filepathinfo.h56
-rw-r--r--src/libs/utils/fileutils.cpp165
-rw-r--r--src/libs/utils/fileutils.h13
-rw-r--r--src/libs/utils/fsengine/fileiconprovider.cpp60
-rw-r--r--src/libs/utils/fsengine/filepathinfocache.h68
-rw-r--r--src/libs/utils/fsengine/fsengine_impl.cpp52
-rw-r--r--src/plugins/docker/dockerdevice.cpp115
-rw-r--r--src/plugins/docker/dockerdevice.h10
-rw-r--r--src/plugins/projectexplorer/devicesupport/devicemanager.cpp18
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevice.cpp33
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevice.h8
14 files changed, 579 insertions, 124 deletions
diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt
index 2cd8223d83e..1d7099bc3a9 100644
--- a/src/libs/utils/CMakeLists.txt
+++ b/src/libs/utils/CMakeLists.txt
@@ -48,6 +48,7 @@ add_qtc_library(Utils
fileinprojectfinder.cpp fileinprojectfinder.h
filenamevalidatinglineedit.cpp filenamevalidatinglineedit.h
filepath.cpp filepath.h
+ filepathinfo.h
filesearch.cpp filesearch.h
filesystemmodel.cpp filesystemmodel.h
filesystemwatcher.cpp filesystemwatcher.h
@@ -264,6 +265,7 @@ extend_qtc_library(Utils
fsengine/fixedlistfsengine.h
fsengine/fsenginehandler.cpp
fsengine/fsenginehandler.h
+ fsengine/filepathinfocache.h
)
if (WIN32)
diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp
index 3aa7e4a11d6..80035ac504f 100644
--- a/src/libs/utils/filepath.cpp
+++ b/src/libs/utils/filepath.cpp
@@ -513,8 +513,7 @@ FilePaths FilePath::dirEntries(QDir::Filters filters) const
// either of the specified \a nameFilters.
// An empty \nameFilters list matches every name.
-void FilePath::iterateDirectory(const std::function<bool(const FilePath &item)> &callBack,
- const FileFilter &filter) const
+void FilePath::iterateDirectory(const IterateDirCallback &callBack, const FileFilter &filter) const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.iterateDirectory, return);
@@ -529,8 +528,25 @@ void FilePath::iterateDirectory(const std::function<bool(const FilePath &item)>
}
}
+void FilePath::iterateDirectory(const IterateDirWithInfoCallback &callBack,
+ const FileFilter &filter) const
+{
+ if (needsDevice()) {
+ QTC_ASSERT(s_deviceHooks.iterateDirectoryWithInfo, return);
+ s_deviceHooks.iterateDirectoryWithInfo(*this, callBack, filter);
+ return;
+ }
+
+ QDirIterator it(path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags);
+ while (it.hasNext()) {
+ const FilePath path = FilePath::fromString(it.next());
+ if (!callBack(path, path.filePathInfo()))
+ return;
+ }
+}
+
void FilePath::iterateDirectories(const FilePaths &dirs,
- const std::function<bool(const FilePath &)> &callBack,
+ const IterateDirCallback &callBack,
const FileFilter &filter)
{
for (const FilePath &dir : dirs)
@@ -601,6 +617,38 @@ bool FilePath::writeFileContents(const QByteArray &data, qint64 offset) const
return res == data.size();
}
+FilePathInfo FilePath::filePathInfo() const
+{
+ if (needsDevice()) {
+ QTC_ASSERT(s_deviceHooks.filePathInfo, return {});
+ return s_deviceHooks.filePathInfo(*this);
+ }
+
+ FilePathInfo result;
+
+ QFileInfo fi(path());
+ result.fileSize = fi.size();
+ result.lastModified = fi.lastModified();
+ result.fileFlags = (FilePathInfo::FileFlag) fi.permissions().toInt();
+
+ if (fi.isDir())
+ result.fileFlags |= FilePathInfo::DirectoryType;
+ if (fi.isFile())
+ result.fileFlags |= FilePathInfo::FileType;
+ if (fi.exists())
+ result.fileFlags |= FilePathInfo::ExistsFlag;
+ if (fi.isSymbolicLink())
+ result.fileFlags |= FilePathInfo::LinkType;
+ if (fi.isBundle())
+ result.fileFlags |= FilePathInfo::BundleType;
+ if (fi.isHidden())
+ result.fileFlags |= FilePathInfo::HiddenFlag;
+ if (fi.isRoot())
+ result.fileFlags |= FilePathInfo::RootFlag;
+
+ return result;
+}
+
void FilePath::asyncWriteFileContents(const Continuation<bool> &cont,
const QByteArray &data,
qint64 offset) const
@@ -1834,17 +1882,18 @@ FileFilter::FileFilter(const QStringList &nameFilters,
{
}
-QStringList FileFilter::asFindArguments() const
+QStringList FileFilter::asFindArguments(const QString &path) const
{
QStringList arguments;
const QDir::Filters filters = fileFilters;
- if (filters & QDir::NoSymLinks)
- arguments.prepend("-H");
+
+ if (iteratorFlags.testFlag(QDirIterator::FollowSymlinks))
+ arguments << "-L";
else
- arguments.prepend("-L");
+ arguments << "-H";
- arguments.append({"-mindepth", "1"});
+ arguments << path;
if (!iteratorFlags.testFlag(QDirIterator::Subdirectories))
arguments.append({"-maxdepth", "1"});
@@ -1854,14 +1903,23 @@ QStringList FileFilter::asFindArguments() const
if (!(filters & QDir::Hidden))
filterOptions << "!" << "-name" << ".*";
+ QStringList typesToList;
+
QStringList filterFilesAndDirs;
- if (filters & QDir::Dirs)
+ if (filters.testFlag(QDir::Dirs))
filterFilesAndDirs << "-type" << "d";
- if (filters & QDir::Files) {
+ if (filters.testFlag(QDir::Files)) {
if (!filterFilesAndDirs.isEmpty())
filterFilesAndDirs << "-o";
filterFilesAndDirs << "-type" << "f";
}
+
+ if (!filters.testFlag(QDir::NoSymLinks)) {
+ if (!filterFilesAndDirs.isEmpty())
+ filterFilesAndDirs << "-o";
+ filterFilesAndDirs << "-type" << "l";
+ }
+
if (!filterFilesAndDirs.isEmpty())
filterOptions << "(" << filterFilesAndDirs << ")";
diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h
index d4b1f714b24..f03d89d4d8b 100644
--- a/src/libs/utils/filepath.h
+++ b/src/libs/utils/filepath.h
@@ -5,6 +5,7 @@
#include "utils_global.h"
+#include "filepathinfo.h"
#include "osspecificaspects.h"
#include <QDir>
@@ -36,7 +37,7 @@ public:
const QDir::Filters fileFilters = QDir::NoFilter,
const QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags);
- QStringList asFindArguments() const;
+ QStringList asFindArguments(const QString &path) const;
const QStringList nameFilters;
const QDir::Filters fileFilters = QDir::NoFilter;
@@ -119,6 +120,7 @@ public:
FilePaths dirEntries(QDir::Filters filters) const;
std::optional<QByteArray> fileContents(qint64 maxSize = -1, qint64 offset = 0) const;
bool writeFileContents(const QByteArray &data, qint64 offset = 0) const;
+ FilePathInfo filePathInfo() const;
bool operator==(const FilePath &other) const;
bool operator!=(const FilePath &other) const;
@@ -151,10 +153,17 @@ public:
[[nodiscard]] Environment deviceEnvironment() const;
[[nodiscard]] FilePath onDevice(const FilePath &deviceTemplate) const;
[[nodiscard]] FilePath withNewPath(const QString &newPath) const;
- void iterateDirectory(const std::function<bool(const FilePath &item)> &callBack,
+
+ using IterateDirCallback = std::function<bool(const FilePath &item)>;
+ using IterateDirWithInfoCallback
+ = std::function<bool(const FilePath &item, const FilePathInfo &info)>;
+
+ void iterateDirectory(const IterateDirCallback &callBack, const FileFilter &filter) const;
+ void iterateDirectory(const IterateDirWithInfoCallback &callBack,
const FileFilter &filter) const;
+
static void iterateDirectories(const FilePaths &dirs,
- const std::function<bool(const FilePath &item)> &callBack,
+ const IterateDirCallback &callBack,
const FileFilter &filter);
enum PathAmending { AppendToPath, PrependToPath };
@@ -258,8 +267,13 @@ public:
std::function<FilePath(const FilePath &)> symLinkTarget;
std::function<QString(const FilePath &)> mapToDevicePath;
std::function<void(const FilePath &,
- const std::function<bool(const FilePath &)> &, // Abort on 'false' return.
- const FileFilter &)> iterateDirectory;
+ const FilePath::IterateDirCallback &, // Abort on 'false' return.
+ const FileFilter &)>
+ iterateDirectory;
+ std::function<void(const FilePath &,
+ const FilePath::IterateDirWithInfoCallback &, // Abort on 'false' return.
+ const FileFilter &)>
+ iterateDirectoryWithInfo;
std::function<std::optional<QByteArray>(const FilePath &, qint64, qint64)> fileContents;
std::function<bool(const FilePath &, const QByteArray &, qint64)> writeFileContents;
std::function<QDateTime(const FilePath &)> lastModified;
@@ -271,6 +285,7 @@ public:
std::function<qint64(const FilePath &)> bytesAvailable;
std::function<QString(const FilePath &)> deviceDisplayName;
std::function<bool(const FilePath &, const FilePath &)> isSameDevice;
+ std::function<FilePathInfo(const FilePath &)> filePathInfo;
template <class ...Args> using Continuation = std::function<void(Args...)>;
diff --git a/src/libs/utils/filepathinfo.h b/src/libs/utils/filepathinfo.h
new file mode 100644
index 00000000000..7f05e678343
--- /dev/null
+++ b/src/libs/utils/filepathinfo.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QDateTime>
+
+namespace Utils {
+
+struct FilePathInfo
+{
+ // Copy of QAbstractFileEngine::FileFlags so we don't need to include private headers.
+ enum FileFlag {
+ //perms (overlaps the QFile::Permission)
+ ReadOwnerPerm = 0x4000, // 0x100
+ WriteOwnerPerm = 0x2000, // 0x80
+ ExeOwnerPerm = 0x1000, // 0x40
+ ReadUserPerm = 0x0400,
+ WriteUserPerm = 0x0200,
+ ExeUserPerm = 0x0100,
+ ReadGroupPerm = 0x0040, // 0x20
+ WriteGroupPerm = 0x0020, // 0x10
+ ExeGroupPerm = 0x0010, // 0x8
+ ReadOtherPerm = 0x0004, // 0x4
+ WriteOtherPerm = 0x0002, // 0x2
+ ExeOtherPerm = 0x0001, // 0x1
+
+ //types
+ LinkType = 0x10000, // 0xa000
+ FileType = 0x20000, // 0x8000
+ DirectoryType = 0x40000, // 0x4000
+ BundleType = 0x80000,
+
+ //flags
+ HiddenFlag = 0x0100000,
+ LocalDiskFlag = 0x0200000, // 0x6000
+ ExistsFlag = 0x0400000,
+ RootFlag = 0x0800000,
+ Refresh = 0x1000000,
+
+ //masks
+ PermsMask = 0x0000FFFF,
+ TypesMask = 0x000F0000,
+ FlagsMask = 0x0FF00000,
+ FileInfoAll = FlagsMask | PermsMask | TypesMask
+ };
+
+ Q_DECLARE_FLAGS(FileFlags, FileFlag)
+
+
+ qint64 fileSize = 0;
+ FileFlags fileFlags;
+ QDateTime lastModified;
+};
+
+} // namespace Utils
diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp
index b4b952166fd..cb893ff4a46 100644
--- a/src/libs/utils/fileutils.cpp
+++ b/src/libs/utils/fileutils.cpp
@@ -620,34 +620,139 @@ void FileUtils::iterateLsOutput(const FilePath &base,
}
}
-// returns whether 'find' could be used.
-static bool iterateWithFind(const FilePath &filePath,
- const FileFilter &filter,
- const std::function<RunResult(const CommandLine &)> &runInShell,
- const std::function<bool(const FilePath &)> &callBack)
+FilePathInfo::FileFlags fileInfoFlagsfromStatRawModeHex(const QString &hexString)
+{
+ bool ok = false;
+ uint mode = hexString.toUInt(&ok, 16);
+
+ QTC_ASSERT(ok, return {});
+
+ FilePathInfo::FileFlags result;
+
+ if (mode & 0x100) // S_IRUSR
+ result |= FilePathInfo::ReadOwnerPerm;
+ if (mode & 0x80) // S_IWUSR
+ result |= FilePathInfo::WriteOwnerPerm;
+ if (mode & 0x40) // S_IXUSR
+ result |= FilePathInfo::ExeOwnerPerm;
+ if (mode & 0x20) // S_IRGRP
+ result |= FilePathInfo::ReadGroupPerm;
+ if (mode & 0x10) // S_IWGRP
+ result |= FilePathInfo::WriteGroupPerm;
+ if (mode & 0x8) // S_IXGRP
+ result |= FilePathInfo::ExeGroupPerm;
+ if (mode & 0x4) // S_IROTH
+ result |= FilePathInfo::ReadOtherPerm;
+ if (mode & 0x2) // S_IWOTH
+ result |= FilePathInfo::WriteOtherPerm;
+ if (mode & 0x1) // S_IXOTH
+ result |= FilePathInfo::ExeOtherPerm;
+ if (mode & 0xa000) // S_IFLNK
+ result |= FilePathInfo::LinkType;
+ if (mode & 0x8000) // S_IFREG
+ result |= FilePathInfo::FileType;
+ if (mode & 0x4000) // S_IFDIR
+ result |= FilePathInfo::DirectoryType;
+ if (mode & 0x6000) // S_IFBLK
+ result |= FilePathInfo::LocalDiskFlag;
+
+ if (result != 0) // There is no Exist flag, but if anything was set before, it must exist.
+ result |= FilePathInfo::ExistsFlag;
+
+ return result;
+}
+
+FilePathInfo FileUtils::filePathInfoFromTriple(const QString &infos)
+{
+ const QStringList parts = infos.split(' ', Qt::SkipEmptyParts);
+ if (parts.size() != 3)
+ return {};
+
+ FilePathInfo::FileFlags flags = fileInfoFlagsfromStatRawModeHex(parts[0]);
+
+ const QDateTime dt = QDateTime::fromSecsSinceEpoch(parts[1].toLongLong(), Qt::UTC);
+ qint64 size = parts[2].toLongLong();
+ return {size, flags, dt};
+}
+
+bool iterateWithFind(const FilePath &filePath,
+ const FileFilter &filter,
+ const std::function<RunResult(const CommandLine &)> &runInShell,
+ const std::function<bool(const QString &)> callBack,
+ const QString &extraArguments)
{
QTC_CHECK(filePath.isAbsolutePath());
- QStringList arguments{filePath.path()};
- arguments << filter.asFindArguments();
+ const QStringList arguments = filter.asFindArguments(filePath.path());
- const RunResult result = runInShell({"find", arguments});
- if (!result.stdErr.isEmpty()) {
- // missing find, unknown option e.g. "find: unknown predicate `-L'\n"
- // qDebug() << "find error: " << result.stdErr;
- return false;
- }
+ CommandLine cmdLine{"find", arguments};
+ if (!extraArguments.isEmpty())
+ cmdLine.addArgs(extraArguments, CommandLine::Raw);
+ const RunResult result = runInShell(cmdLine);
const QString out = QString::fromUtf8(result.stdOut);
- const QStringList entries = out.split("\n", Qt::SkipEmptyParts);
+ if (result.exitCode != 0) {
+ // Find returns non-zero exit code for any error it encounters, even if it finds some files.
+
+ if (!out.startsWith('"' + filePath.path())) {
+ if (!filePath.exists()) // File does not exist, so no files to find.
+ return true;
+
+ // If the output does not start with the path we are searching in, find has failed.
+ // Possibly due to unknown options.
+ return false;
+ }
+ }
+
+ QStringList entries = out.split("\n", Qt::SkipEmptyParts);
+ // Remove the first line, it is always the directory we are searching in.
+ // as long as we do not specify "mindepth > 0"
+ entries.pop_front();
for (const QString &entry : entries) {
- const FilePath fp = FilePath::fromString(entry);
- // Call back returning 'false' indicates a request to abort iteration.
- if (!callBack(fp.onDevice(filePath)))
+ if (!callBack(entry))
break;
}
+
return true;
}
+// returns whether 'find' could be used.
+static bool iterateWithFind(const FilePath &filePath,
+ const FileFilter &filter,
+ const std::function<RunResult(const CommandLine &)> &runInShell,
+ const FilePath::IterateDirCallback &callBack)
+{
+ const auto toFilePath = [&filePath, &callBack](const QString &entry){
+ return callBack(filePath.withNewPath(entry));
+ };
+
+ return iterateWithFind(filePath, filter, runInShell, toFilePath, {});
+}
+
+// returns whether 'find' could be used.
+static bool iterateWithFind(const FilePath &filePath,
+ const FileFilter &filter,
+ const std::function<RunResult(const CommandLine &)> &runInShell,
+ const FilePath::IterateDirWithInfoCallback &callBack)
+{
+ // TODO: Using stat -L will always return the link target, not the link itself.
+ // We may wan't to add the information that it is a link at some point.
+ const QString infoArgs(R"(-exec echo -n \"{}\"" " \; -exec stat -L -c "%f %Y %s" "{}" \;)");
+
+ const auto toFilePathAndInfo = [&filePath, &callBack](const QString &entry) {
+ const QString fileName = entry.mid(1, entry.lastIndexOf('\"') - 1);
+ const QString infos = entry.mid(fileName.length() + 3);
+
+ const FilePathInfo fi = FileUtils::filePathInfoFromTriple(infos);
+ if (!fi.fileFlags)
+ return true;
+
+ const FilePath fp = filePath.withNewPath(fileName);
+ return callBack(fp, fi);
+ };
+
+ return iterateWithFind(filePath, filter, runInShell, toFilePathAndInfo, infoArgs);
+}
+
static void findUsingLs(const QString &current,
const FileFilter &filter,
const std::function<RunResult(const CommandLine &)> &runInShell,
@@ -670,7 +775,7 @@ void FileUtils::iterateUnixDirectory(const FilePath &filePath,
const FileFilter &filter,
bool *useFind,
const std::function<RunResult (const CommandLine &)> &runInShell,
- const std::function<bool(const FilePath &)> &callBack)
+ const FilePath::IterateDirCallback &callBack)
{
QTC_ASSERT(callBack, return);
@@ -688,6 +793,30 @@ void FileUtils::iterateUnixDirectory(const FilePath &filePath,
FileUtils::iterateLsOutput(filePath, entries, filter, callBack);
}
+void FileUtils::iterateUnixDirectory(const FilePath &filePath,
+ const FileFilter &filter,
+ bool *useFind,
+ const std::function<RunResult(const CommandLine &)> &runInShell,
+ const FilePath::IterateDirWithInfoCallback &callBack)
+{
+ QTC_ASSERT(callBack, return);
+
+ // We try to use 'find' first, because that can filter better directly.
+ // Unfortunately, it's not installed on all devices by default.
+ if (useFind && *useFind) {
+ if (iterateWithFind(filePath, filter, runInShell, callBack))
+ return;
+ *useFind = false; // remember the failure for the next time and use the 'ls' fallback below.
+ }
+
+ // if we do not have find - use ls as fallback
+ QStringList entries;
+ findUsingLs(filePath.path(), filter, runInShell, &entries);
+ FileUtils::iterateLsOutput(filePath, entries, filter, [&callBack](const FilePath & filePath){
+ return callBack(filePath, filePath.filePathInfo());
+ });
+}
+
/*!
Copies the directory specified by \a srcFilePath recursively to \a tgtFilePath. \a tgtFilePath will contain
the target directory, which will be created. Example usage:
diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h
index 5a1c5e1f075..fe3a388bb0b 100644
--- a/src/libs/utils/fileutils.h
+++ b/src/libs/utils/fileutils.h
@@ -35,7 +35,7 @@ class CommandLine;
struct QTCREATOR_UTILS_EXPORT RunResult
{
- int exitCode = 0;
+ int exitCode = -1;
QByteArray stdOut;
QByteArray stdErr;
};
@@ -97,10 +97,19 @@ public:
const FileFilter &filter,
bool *useFind,
const std::function<RunResult(const CommandLine &)> &runInShell,
- const std::function<bool(const FilePath &)> &callBack);
+ const FilePath::IterateDirCallback &callBack);
+
+ static void iterateUnixDirectory(
+ const FilePath &base,
+ const FileFilter &filter,
+ bool *useFind,
+ const std::function<RunResult(const CommandLine &)> &runInShell,
+ const FilePath::IterateDirWithInfoCallback &callBack);
static qint64 bytesAvailableFromDFOutput(const QByteArray &dfOutput);
+ static FilePathInfo filePathInfoFromTriple(const QString &infos);
+
#ifdef QT_WIDGETS_LIB
static void setDialogParentGetter(const std::function<QWidget *()> &getter);
diff --git a/src/libs/utils/fsengine/fileiconprovider.cpp b/src/libs/utils/fsengine/fileiconprovider.cpp
index b397cc9447b..12ca91e2a2b 100644
--- a/src/libs/utils/fsengine/fileiconprovider.cpp
+++ b/src/libs/utils/fsengine/fileiconprovider.cpp
@@ -121,27 +121,22 @@ QIcon FileIconProviderImplementation::icon(IconType type) const
return QFileIconProvider::icon(type);
}
-QIcon FileIconProviderImplementation::icon(const QFileInfo &fi) const
-{
- return icon(FilePath::fromString(fi.filePath()));
-}
-
QString FileIconProviderImplementation::type(const QFileInfo &fi) const
{
const FilePath fPath = FilePath::fromString(fi.filePath());
if (fPath.needsDevice()) {
- if (fPath.isDir()) {
+ if (fi.isDir()) {
#ifdef Q_OS_WIN
return QGuiApplication::translate("QAbstractFileIconProvider", "File Folder", "Match Windows Explorer");
#else
return QGuiApplication::translate("QAbstractFileIconProvider", "Folder", "All other platforms");
#endif
}
- if (fPath.isExecutableFile()) {
+ if (fi.isExecutable()) {
return "Program";
}
- return QFileIconProvider::type(fi);
+ return "File";
}
return QFileIconProvider::type(fi);
}
@@ -169,6 +164,55 @@ static const QIcon &dirIcon()
return icon;
}
+QIcon FileIconProviderImplementation::icon(const QFileInfo &fi) const
+{
+ qCDebug(fileIconProvider) << "FileIconProvider::icon" << fi.absoluteFilePath();
+
+ const FilePath filePath = FilePath::fromString(fi.filePath());
+
+ if (filePath.isEmpty())
+ return unknownFileIcon();
+
+ // Check if its one of the virtual devices directories
+ if (filePath.path().startsWith(
+ FilePath::specialPath(FilePath::SpecialPathComponent::RootPath))) {
+ // If the filepath does not need a device, it is a virtual device directory
+ if (!filePath.needsDevice())
+ return dirIcon();
+ }
+
+ bool isDir = fi.isDir();
+
+ // Check for cached overlay icons by file suffix.
+ const QString filename = !isDir ? fi.fileName() : QString();
+ if (!filename.isEmpty()) {
+ const std::optional<QIcon> icon = getIcon(m_filenameCache, filename);
+ if (icon)
+ return *icon;
+ }
+
+ const QString suffix = !isDir ? fi.suffix() : QString();
+ if (!suffix.isEmpty()) {
+ const std::optional<QIcon> icon = getIcon(m_suffixCache, suffix);
+ if (icon)
+ return *icon;
+ }
+
+ if (filePath.needsDevice())
+ return isDir ? dirIcon() : unknownFileIcon();
+
+ // Get icon from OS (and cache it based on suffix!)
+ QIcon icon;
+ if (HostOsInfo::isWindowsHost() || HostOsInfo::isMacHost())
+ icon = QFileIconProvider::icon(filePath.toFileInfo());
+ else // File icons are unknown on linux systems.
+ icon = isDir ? QFileIconProvider::icon(filePath.toFileInfo()) : unknownFileIcon();
+
+ if (!isDir && !suffix.isEmpty())
+ m_suffixCache.insert(suffix, icon);
+ return icon;
+}
+
QIcon FileIconProviderImplementation::icon(const FilePath &filePath) const
{
qCDebug(fileIconProvider) << "FileIconProvider::icon" << filePath.absoluteFilePath();
diff --git a/src/libs/utils/fsengine/filepathinfocache.h b/src/libs/utils/fsengine/filepathinfocache.h
new file mode 100644
index 00000000000..e0b7094cbfe
--- /dev/null
+++ b/src/libs/utils/fsengine/filepathinfocache.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "../filepath.h"
+
+#include <QCache>
+#include <QMutex>
+#include <QMutexLocker>
+
+namespace Utils::Internal {
+
+class FilePathInfoCache
+{
+public:
+ struct CachedData
+ {
+ FilePathInfo filePathInfo;
+ QDateTime timeout;
+ };
+
+ using RetrievalFunction = CachedData (*)(const FilePath &);
+
+public:
+ FilePathInfoCache()
+ : m_cache(50000)
+ {}
+
+ CachedData cached(const FilePath &filePath, const RetrievalFunction &retrievalFunction)
+ {
+ QMutexLocker lk(&m_mutex);
+ CachedData *data = m_cache.object(filePath);
+
+ // If the cache entry is too old, don't use it ...
+ if (data && data->timeout < QDateTime::currentDateTime())
+ data = nullptr;
+
+ // If no data was found, retrieve it and store it in the cache ...
+ if (!data) {
+ data = new CachedData;
+ *data = retrievalFunction(filePath);
+ m_cache.insert(filePath, data);
+ }
+
+ // Return a copy of the data, so it cannot be deleted by the cache
+ return *data;
+ }
+
+ void cache(const FilePath &path, CachedData *data)
+ {
+ QMutexLocker lk(&m_mutex);
+ m_cache.insert(path, data);
+ }
+
+ void cache(const QList<QPair<FilePath, CachedData>> &fileDataList)
+ {
+ QMutexLocker lk(&m_mutex);
+ for (const auto &[path, data] : fileDataList)
+ m_cache.insert(path, new CachedData(data));
+ }
+
+private:
+ QMutex m_mutex;
+ QCache<FilePath, CachedData> m_cache;
+};
+
+} // namespace Utils::Internal
diff --git a/src/libs/utils/fsengine/fsengine_impl.cpp b/src/libs/utils/fsengine/fsengine_impl.cpp
index 02ffe3a58f4..f8277195b28 100644
--- a/src/libs/utils/fsengine/fsengine_impl.cpp
+++ b/src/libs/utils/fsengine/fsengine_impl.cpp
@@ -4,6 +4,7 @@
#include "fsengine_impl.h"
#include "diriterator.h"
+#include "filepathinfocache.h"
#include "../filepath.h"
#include "../qtcassert.h"
@@ -15,6 +16,15 @@ namespace Utils {
namespace Internal {
+FilePathInfoCache g_filePathInfoCache;
+
+FilePathInfoCache::CachedData createCacheData(const FilePath &filePath) {
+ FilePathInfoCache::CachedData data;
+ data.filePathInfo = filePath.filePathInfo();
+ data.timeout = QDateTime::currentDateTime().addSecs(60);
+ return data;
+};
+
FSEngineImpl::FSEngineImpl(FilePath filePath)
: m_filePath(std::move(filePath))
{}
@@ -30,6 +40,10 @@ bool FSEngineImpl::open(QIODeviceBase::OpenMode openMode, std::optional<QFile::P
bool FSEngineImpl::open(QIODevice::OpenMode openMode)
#endif
{
+ const FilePathInfoCache::CachedData data = g_filePathInfoCache.cached(m_filePath,
+ createCacheData);
+ bool exists = (data.filePathInfo.fileFlags & QAbstractFileEngine::ExistsFlag);
+
ensureStorage();
QTC_ASSERT(m_tempStorage->open(), return false);
@@ -38,10 +52,10 @@ bool FSEngineImpl::open(QIODevice::OpenMode openMode)
bool write = openMode & QIODevice::WriteOnly;
bool append = openMode & QIODevice::Append;
- if (!write && !m_filePath.exists())
+ if (!write && !exists)
return false;
- if (openMode & QIODevice::NewOnly && m_filePath.exists())
+ if (openMode & QIODevice::NewOnly && exists)
return false;
if (read || append) {
@@ -88,7 +102,7 @@ bool FSEngineImpl::syncToDisk()
qint64 FSEngineImpl::size() const
{
- return m_filePath.fileSize();
+ return g_filePathInfoCache.cached(m_filePath, createCacheData).filePathInfo.fileSize;
}
qint64 FSEngineImpl::pos() const
@@ -175,8 +189,12 @@ QStringList FSEngineImpl::entryList(QDir::Filters filters, const QStringList &fi
{
QStringList result;
m_filePath.iterateDirectory(
- [&result](const FilePath &p) {
+ [&result](const FilePath &p, const FilePathInfo &fi) {
result.append(p.toFSPathString());
+ g_filePathInfoCache
+ .cache(p,
+ new FilePathInfoCache::CachedData{fi,
+ QDateTime::currentDateTime().addSecs(60)});
return true;
},
{filterNames, filters});
@@ -185,22 +203,8 @@ QStringList FSEngineImpl::entryList(QDir::Filters filters, const QStringList &fi
QAbstractFileEngine::FileFlags FSEngineImpl::fileFlags(FileFlags type) const
{
- FileFlags result{0};
-
- if (type & FileInfoAll && m_filePath.exists()) {
- result |= QAbstractFileEngine::ExistsFlag;
-
- if (type & DirectoryType && m_filePath.isDir())
- result |= QAbstractFileEngine::DirectoryType;
- if (type & FileType && m_filePath.isFile())
- result |= QAbstractFileEngine::FileType;
-
- if (type & PermsMask) {
- result |= FileFlags::fromInt(m_filePath.permissions().toInt());
- }
- }
-
- return result;
+ Q_UNUSED(type);
+ return {g_filePathInfoCache.cached(m_filePath, createCacheData).filePathInfo.fileFlags.toInt()};
}
bool FSEngineImpl::setPermissions(uint /*perms*/)
@@ -265,7 +269,7 @@ bool FSEngineImpl::setFileTime(const QDateTime &newDate, FileTime time)
QDateTime FSEngineImpl::fileTime(FileTime time) const
{
Q_UNUSED(time)
- return m_filePath.lastModified();
+ return g_filePathInfoCache.cached(m_filePath, createCacheData).filePathInfo.lastModified;
}
void FSEngineImpl::setFileName(const QString &file)
@@ -289,8 +293,12 @@ QAbstractFileEngine::Iterator *FSEngineImpl::beginEntryList(QDir::Filters filter
{
FilePaths paths{m_filePath.pathAppended(".")};
m_filePath.iterateDirectory(
- [&paths](const FilePath &p) {
+ [&paths](const FilePath &p, const FilePathInfo &fi) {
paths.append(p);
+ FilePathInfoCache::CachedData *data
+ = new FilePathInfoCache::CachedData{fi,
+ QDateTime::currentDateTime().addSecs(60)};
+ g_filePathInfoCache.cache(p, data);
return true;
},
{filterNames, filters});
diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp
index 38a30aa76f3..401e98d59c4 100644
--- a/src/plugins/docker/dockerdevice.cpp
+++ b/src/plugins/docker/dockerdevice.cpp
@@ -67,12 +67,11 @@
#include <QThread>
#include <QToolButton>
-
#include <numeric>
#ifdef Q_OS_UNIX
-#include <unistd.h>
#include <sys/types.h>
+#include <unistd.h>
#endif
using namespace Core;
@@ -92,13 +91,13 @@ public:
: m_settings(settings)
, m_containerId(containerId)
, m_devicePath(devicePath)
- {
- }
+ {}
private:
void setupShellProcess(QtcProcess *shellProcess) final
{
- shellProcess->setCommand({m_settings->dockerBinaryPath.filePath(), {"container", "start", "-i", "-a", m_containerId}});
+ shellProcess->setCommand({m_settings->dockerBinaryPath.filePath(),
+ {"container", "start", "-i", "-a", m_containerId}});
}
CommandLine createFallbackCommand(const CommandLine &cmdLine)
@@ -162,7 +161,8 @@ public:
DockerDeviceData m_data;
DockerSettings *m_settings;
- struct TemporaryMountInfo {
+ struct TemporaryMountInfo
+ {
FilePath path;
FilePath containerPath;
};
@@ -327,11 +327,9 @@ Tasks DockerDevice::validate() const
return result;
}
-
DockerDevice::DockerDevice(DockerSettings *settings, const DockerDeviceData &data)
: d(new DockerDevicePrivate(this, settings, data))
{
-
setDisplayType(Tr::tr("Docker"));
setOsType(OsTypeOtherUnix);
setDefaultDisplayName(Tr::tr("Docker Image"));
@@ -421,7 +419,11 @@ CommandLine DockerDevicePrivate::withDockerExecCmd(const CommandLine &cmd, bool
void DockerDevicePrivate::stopCurrentContainer()
{
- if (!m_settings || m_container.isEmpty() || !DockerApi::isDockerDaemonAvailable(false).value_or(false))
+ if (!m_settings)
+ return;
+ if (m_container.isEmpty())
+ return;
+ if (!DockerApi::isDockerDaemonAvailable(false).value_or(false))
return;
m_shell.reset();
@@ -589,8 +591,7 @@ void DockerDevice::fromMap(const QVariantMap &map)
data.tag = map.value(DockerDeviceDataTagKey).toString();
data.imageId = map.value(DockerDeviceDataImageIdKey).toString();
data.size = map.value(DockerDeviceDataSizeKey).toString();
- data.useLocalUidGid = map.value(DockerDeviceUseOutsideUser, HostOsInfo::isLinuxHost())
- .toBool();
+ data.useLocalUidGid = map.value(DockerDeviceUseOutsideUser, HostOsInfo::isLinuxHost()).toBool();
data.mounts = map.value(DockerDeviceMappedPaths).toStringList();
data.keepEntryPoint = map.value(DockerDeviceKeepEntryPoint).toBool();
d->setData(data);
@@ -623,26 +624,24 @@ bool DockerDevice::canAutoDetectPorts() const
PortsGatheringMethod DockerDevice::portsGatheringMethod() const
{
- return {
- [this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine {
- // We might encounter the situation that protocol is given IPv6
- // but the consumer of the free port information decides to open
- // an IPv4(only) port. As a result the next IPv6 scan will
- // report the port again as open (in IPv6 namespace), while the
- // same port in IPv4 namespace might still be blocked, and
- // re-use of this port fails.
- // GDBserver behaves exactly like this.
-
- Q_UNUSED(protocol)
-
- // /proc/net/tcp* covers /proc/net/tcp and /proc/net/tcp6
- return {filePath("sed"),
- "-e 's/.*: [[:xdigit:]]*:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' /proc/net/tcp*",
- CommandLine::Raw};
- },
-
- &Port::parseFromSedOutput
- };
+ return {[this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine {
+ // We might encounter the situation that protocol is given IPv6
+ // but the consumer of the free port information decides to open
+ // an IPv4(only) port. As a result the next IPv6 scan will
+ // report the port again as open (in IPv6 namespace), while the
+ // same port in IPv4 namespace might still be blocked, and
+ // re-use of this port fails.
+ // GDBserver behaves exactly like this.
+
+ Q_UNUSED(protocol)
+
+ // /proc/net/tcp* covers /proc/net/tcp and /proc/net/tcp6
+ return {filePath("sed"),
+ "-e 's/.*: [[:xdigit:]]*:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' /proc/net/tcp*",
+ CommandLine::Raw};
+ },
+
+ &Port::parseFromSedOutput};
};
DeviceProcessList *DockerDevice::createProcessListModel(QObject *) const
@@ -714,12 +713,12 @@ bool DockerDevice::handlesFile(const FilePath &filePath) const
if (filePath.scheme() == u"device" && filePath.host() == id().toString())
return true;
- if (filePath.scheme() == Constants::DOCKER_DEVICE_SCHEME
- && filePath.host() == d->dockerImageId())
+ const bool isDockerScheme = filePath.scheme() == Constants::DOCKER_DEVICE_SCHEME;
+
+ if (isDockerScheme && filePath.host() == d->dockerImageId())
return true;
- if (filePath.scheme() == Constants::DOCKER_DEVICE_SCHEME
- && filePath.host() == QString(d->repoAndTag()))
+ if (isDockerScheme && filePath.host() == QString(d->repoAndTag()))
return true;
return false;
@@ -875,7 +874,8 @@ QFileDevice::Permissions DockerDevice::permissions(const FilePath &filePath) con
return perm;
}
-bool DockerDevice::setPermissions(const FilePath &filePath, QFileDevice::Permissions permissions) const
+bool DockerDevice::setPermissions(const FilePath &filePath,
+ QFileDevice::Permissions permissions) const
{
Q_UNUSED(permissions)
QTC_ASSERT(handlesFile(filePath), return {});
@@ -894,7 +894,16 @@ bool DockerDevice::ensureReachable(const FilePath &other) const
}
void DockerDevice::iterateDirectory(const FilePath &filePath,
- const std::function<bool(const FilePath &)> &callBack,
+ const FilePath::IterateDirCallback &callBack,
+ const FileFilter &filter) const
+{
+ QTC_ASSERT(handlesFile(filePath), return);
+ auto runInShell = [this](const CommandLine &cmd) { return d->runInShell(cmd); };
+ FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack);
+}
+
+void DockerDevice::iterateDirectory(const FilePath &filePath,
+ const FilePath::IterateDirWithInfoCallback &callBack,
const FileFilter &filter) const
{
QTC_ASSERT(handlesFile(filePath), return);
@@ -923,6 +932,13 @@ bool DockerDevice::writeFileContents(const FilePath &filePath,
return d->runInShellSuccess(cmd, data);
}
+FilePathInfo DockerDevice::filePathInfo(const FilePath &filePath) const
+{
+ QTC_ASSERT(handlesFile(filePath), return {});
+ const RunResult stat = d->runInShell({"stat", {"-L", "-c", "%f %Y %s", filePath.path()}});
+ return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut));
+}
+
Environment DockerDevice::systemEnvironment() const
{
return d->environment();
@@ -973,8 +989,7 @@ void DockerDevicePrivate::fetchSystemEnviroment()
proc.waitForFinished();
const QString remoteOutput = proc.cleanedStdOut();
- m_cachedEnviroment = Environment(remoteOutput.split('\n', Qt::SkipEmptyParts),
- q->osType());
+ m_cachedEnviroment = Environment(remoteOutput.split('\n', Qt::SkipEmptyParts), q->osType());
const QString remoteError = proc.cleanedStdErr();
if (!remoteError.isEmpty())
@@ -1078,6 +1093,7 @@ public:
m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
using namespace Layouting;
+
Column {
Stack {
statusLabel,
@@ -1085,17 +1101,16 @@ public:
},
m_log,
errorLabel,
- Row {
- showUnnamedContainers,
- m_buttons
- },
- }.attachTo(this);
+ Row{showUnnamedContainers, m_buttons},
+ }
+ .attachTo(this);
connect(m_buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false);
- CommandLine cmd{m_settings->dockerBinaryPath.filePath(), {"images", "--format", "{{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.Size}}"}};
+ CommandLine cmd{m_settings->dockerBinaryPath.filePath(),
+ {"images", "--format", "{{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.Size}}"}};
m_log->append(Tr::tr("Running \"%1\"\n").arg(cmd.toUserOutput()));
m_process = new QtcProcess(this);
@@ -1146,7 +1161,8 @@ public:
{
const QModelIndexList selectedRows = m_view->selectionModel()->selectedRows();
QTC_ASSERT(selectedRows.size() == 1, return {});
- DockerImageItem *item = m_model.itemForIndex(m_proxyModel->mapToSource(selectedRows.front()));
+ DockerImageItem *item = m_model.itemForIndex(
+ m_proxyModel->mapToSource(selectedRows.front()));
QTC_ASSERT(item, return {});
auto device = DockerDevice::create(m_settings, *item);
@@ -1201,10 +1217,9 @@ void DockerDeviceFactory::shutdownExistingDevices()
bool DockerDevicePrivate::addTemporaryMount(const FilePath &path, const FilePath &containerPath)
{
- bool alreadyAdded = anyOf(m_temporaryMounts,
- [containerPath](const TemporaryMountInfo &info) {
- return info.containerPath == containerPath;
- });
+ bool alreadyAdded = anyOf(m_temporaryMounts, [containerPath](const TemporaryMountInfo &info) {
+ return info.containerPath == containerPath;
+ });
if (alreadyAdded)
return false;
diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h
index 4e7840b9282..8398ee4e235 100644
--- a/src/plugins/docker/dockerdevice.h
+++ b/src/plugins/docker/dockerdevice.h
@@ -100,7 +100,10 @@ public:
bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override;
Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override;
void iterateDirectory(const Utils::FilePath &filePath,
- const std::function<bool(const Utils::FilePath &)> &callBack,
+ const Utils::FilePath::IterateDirCallback &callBack,
+ const Utils::FileFilter &filter) const override;
+ void iterateDirectory(const Utils::FilePath &filePath,
+ const Utils::FilePath::IterateDirWithInfoCallback &callBack,
const Utils::FileFilter &filter) const override;
std::optional<QByteArray> fileContents(const Utils::FilePath &filePath,
qint64 limit,
@@ -108,6 +111,7 @@ public:
bool writeFileContents(const Utils::FilePath &filePath,
const QByteArray &data,
qint64 offset) const override;
+ Utils::FilePathInfo filePathInfo(const Utils::FilePath &filePath) const override;
QDateTime lastModified(const Utils::FilePath &filePath) const override;
qint64 fileSize(const Utils::FilePath &filePath) const override;
QFileDevice::Permissions permissions(const Utils::FilePath &filePath) const override;
@@ -132,10 +136,6 @@ protected:
QVariantMap toMap() const final;
private:
- void iterateWithFind(const Utils::FilePath &filePath,
- const std::function<bool(const Utils::FilePath &)> &callBack,
- const Utils::FileFilter &filter) const;
-
void aboutToBeRemoved() const final;
class DockerDevicePrivate *d = nullptr;
diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
index febcc6436eb..c5a7f4e8102 100644
--- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
@@ -521,10 +521,18 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager
};
deviceHooks.iterateDirectory = [](const FilePath &filePath,
- const std::function<bool(const FilePath &)> &callBack,
+ const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) {
auto device = DeviceManager::deviceForPath(filePath);
- QTC_ASSERT(device, return);
+ QTC_ASSERT(device, return );
+ device->iterateDirectory(filePath, callBack, filter);
+ };
+
+ deviceHooks.iterateDirectoryWithInfo = [](const FilePath &filePath,
+ const FilePath::IterateDirWithInfoCallback &callBack,
+ const FileFilter &filter) {
+ auto device = DeviceManager::deviceForPath(filePath);
+ QTC_ASSERT(device, return );
device->iterateDirectory(filePath, callBack, filter);
};
@@ -552,6 +560,12 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager
return device->writeFileContents(filePath, data, offset);
};
+ deviceHooks.filePathInfo = [](const FilePath &filePath) -> FilePathInfo {
+ auto device = DeviceManager::deviceForPath(filePath);
+ QTC_ASSERT(device, return {});
+ return device->filePathInfo(filePath);
+ };
+
deviceHooks.lastModified = [](const FilePath &filePath) {
auto device = DeviceManager::deviceForPath(filePath);
QTC_ASSERT(device, return QDateTime());
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp
index 9c391de5aed..294be9f18a0 100644
--- a/src/plugins/projectexplorer/devicesupport/idevice.cpp
+++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp
@@ -356,7 +356,7 @@ FilePath IDevice::symLinkTarget(const FilePath &filePath) const
}
void IDevice::iterateDirectory(const FilePath &filePath,
- const std::function<bool(const FilePath &)> &callBack,
+ const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) const
{
Q_UNUSED(filePath);
@@ -365,6 +365,15 @@ void IDevice::iterateDirectory(const FilePath &filePath,
QTC_CHECK(false);
}
+void IDevice::iterateDirectory(const FilePath &filePath,
+ const FilePath::IterateDirWithInfoCallback &callBack,
+ const FileFilter &filter) const
+{
+ iterateDirectory(filePath, [callBack](const FilePath &path) {
+ return callBack(path, path.filePathInfo());
+ }, filter);
+}
+
std::optional<QByteArray> IDevice::fileContents(const FilePath &filePath,
qint64 limit,
qint64 offset) const
@@ -393,6 +402,28 @@ bool IDevice::writeFileContents(const FilePath &filePath, const QByteArray &data
return {};
}
+FilePathInfo IDevice::filePathInfo(const Utils::FilePath &filePath) const
+{
+ bool exists = filePath.exists();
+ if (!exists)
+ return {};
+
+ FilePathInfo result {
+ filePath.fileSize(),
+ {FilePathInfo::ExistsFlag},
+ filePath.lastModified(),
+ };
+
+ if (filePath.isDir())
+ result.fileFlags |= FilePathInfo::DirectoryType;
+ if (filePath.isFile())
+ result.fileFlags |= FilePathInfo::FileType;
+ if (filePath.isRootPath())
+ result.fileFlags |= FilePathInfo::RootFlag;
+
+ return result;
+}
+
void IDevice::asyncWriteFileContents(const Continuation<bool> &cont,
const FilePath &filePath,
const QByteArray &data,
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h
index 58d0892fc65..57f2ba0a3c7 100644
--- a/src/plugins/projectexplorer/devicesupport/idevice.h
+++ b/src/plugins/projectexplorer/devicesupport/idevice.h
@@ -238,14 +238,20 @@ public:
const Utils::FilePaths &dirs) const;
virtual Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const;
virtual void iterateDirectory(const Utils::FilePath &filePath,
- const std::function<bool(const Utils::FilePath &)> &callBack,
+ const Utils::FilePath::IterateDirCallback &callBack,
const Utils::FileFilter &filter) const;
+
+ virtual void iterateDirectory(const Utils::FilePath &filePath,
+ const Utils::FilePath::IterateDirWithInfoCallback &callBack,
+ const Utils::FileFilter &filter) const;
+
virtual std::optional<QByteArray> fileContents(const Utils::FilePath &filePath,
qint64 limit,
qint64 offset) const;
virtual bool writeFileContents(const Utils::FilePath &filePath,
const QByteArray &data,
qint64 offset) const;
+ virtual Utils::FilePathInfo filePathInfo(const Utils::FilePath &filePath) const;
virtual QDateTime lastModified(const Utils::FilePath &filePath) const;
virtual QFile::Permissions permissions(const Utils::FilePath &filePath) const;
virtual bool setPermissions(const Utils::FilePath &filePath, QFile::Permissions) const;