diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 417f74eb370..8d3efae79de 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -5,6 +5,7 @@ #include "savefile.h" #include "algorithm.h" +#include "commandline.h" #include "qtcassert.h" #include "hostosinfo.h" @@ -583,6 +584,8 @@ FilePath FileUtils::getOpenFilePathFromDevice(QWidget *parent, return {}; } +#endif // QT_WIDGETS_LIB + // Used on 'ls' output on unix-like systems. void FileUtils::iterateLsOutput(const FilePath &base, const QStringList &entries, @@ -619,7 +622,53 @@ void FileUtils::iterateLsOutput(const FilePath &base, } } -#endif // QT_WIDGETS_LIB +// returns whether 'find' could be used. +static bool iterateWithFind(const FilePath &filePath, + const FileFilter &filter, + const std::function &runInShell, + const std::function &callBack) +{ + QTC_CHECK(filePath.isAbsolutePath()); + QStringList arguments{filePath.path()}; + arguments << filter.asFindArguments(); + + const QByteArray output = runInShell({"find", arguments}); + const QString out = QString::fromUtf8(output.data(), output.size()); + if (!output.isEmpty() && !out.startsWith(filePath.path())) // missing find, unknown option + return false; + + const QStringList entries = out.split("\n", Qt::SkipEmptyParts); + for (const QString &entry : entries) { + if (entry.startsWith("find: ")) { + const FilePath fp = FilePath::fromString(entry); + if (!callBack(fp.onDevice(filePath))) + break; + } + } + return true; +} + +void FileUtils::iterateUnixDirectory(const FilePath &filePath, + const FileFilter &filter, + bool *useFind, + const std::function &runInShell, + const std::function &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 + const QByteArray output = runInShell({"ls", {"-1", "-b", "--", filePath.path()}}); + const QStringList entries = QString::fromUtf8(output).split('\n', Qt::SkipEmptyParts); + FileUtils::iterateLsOutput(filePath, entries, filter, callBack); +} /*! Copies the directory specified by \a srcFilePath recursively to \a tgtFilePath. \a tgtFilePath will contain diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 8f4544a0fd1..09ba6ece639 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -31,6 +31,8 @@ QT_END_NAMESPACE namespace Utils { +class CommandLine; + class QTCREATOR_UTILS_EXPORT FileUtils { public: @@ -77,10 +79,18 @@ public: static FilePaths toFilePathList(const QStringList &paths); - static void iterateLsOutput(const FilePath &base, - const QStringList &entries, - const FileFilter &filter, - const std::function &callBack); + static void iterateLsOutput( + const FilePath &base, + const QStringList &entries, + const FileFilter &filter, + const std::function &callBack); + + static void iterateUnixDirectory( + const FilePath &base, + const FileFilter &filter, + bool *useFind, + const std::function &runInShell, + const std::function &callBack); static qint64 bytesAvailableFromDFOutput(const QByteArray &dfOutput); diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 260b857815b..d50e81ccbe6 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -143,16 +143,12 @@ public: QString repoAndTag() const { return m_data.repoAndTag(); } QString dockerImageId() const { return m_data.imageId; } - bool useFind() const { return m_useFind; } - void setUseFind(bool useFind) { m_useFind = useFind; } - Environment environment(); CommandLine withDockerExecCmd(const CommandLine &cmd, bool interactive = false); bool prepareForBuild(const Target *target); -private: bool createContainer(); void startContainer(); void stopCurrentContainer(); @@ -895,52 +891,13 @@ bool DockerDevice::ensureReachable(const FilePath &other) const return d->ensureReachable(other.parentDir()); } -void DockerDevice::iterateWithFind(const FilePath &filePath, - const std::function &callBack, - const FileFilter &filter) const -{ - QTC_ASSERT(callBack, return); - QTC_CHECK(filePath.isAbsolutePath()); - QStringList arguments{filePath.path()}; - arguments << filter.asFindArguments(); - - const QByteArray output = d->outputForRunInShell({"find", arguments}); - const QString out = QString::fromUtf8(output.data(), output.size()); - if (!output.isEmpty() && !out.startsWith(filePath.path())) { // missing find, unknown option - qCDebug(dockerDeviceLog) << "Setting 'do not use find'" << out.left(out.indexOf('\n')); - d->setUseFind(false); - return; - } - - const QStringList entries = out.split("\n", Qt::SkipEmptyParts); - for (const QString &entry : entries) { - if (entry.startsWith("find: ")) - continue; - const FilePath fp = FilePath::fromString(entry); - - if (!callBack(fp.onDevice(filePath))) - break; - } -} - void DockerDevice::iterateDirectory(const FilePath &filePath, const std::function &callBack, const FileFilter &filter) const { QTC_ASSERT(handlesFile(filePath), return); - - if (d->useFind()) { - iterateWithFind(filePath, callBack, filter); - // d->m_useFind will be set to false if 'find' is not found. In this - // case fall back to 'ls' below. - if (d->useFind()) - return; - } - - // if we do not have find - use ls as fallback - const QByteArray output = d->outputForRunInShell({"ls", {"-1", "-b", "--", filePath.path()}}); - const QStringList entries = QString::fromUtf8(output).split('\n', Qt::SkipEmptyParts); - FileUtils::iterateLsOutput(filePath, entries, filter, callBack); + auto runInShell = [this](const CommandLine &cmd) { return d->outputForRunInShell(cmd); }; + FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack); } std::optional DockerDevice::fileContents(const FilePath &filePath,