Utils: Make more unix find code re-usable

Change-Id: I8fe95158ed77c8f9fb8c86a8e813b5c5de425b8a
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
hjk
2022-10-05 17:50:40 +02:00
parent a234443bee
commit 714e6f5c8f
3 changed files with 66 additions and 50 deletions

View File

@@ -5,6 +5,7 @@
#include "savefile.h" #include "savefile.h"
#include "algorithm.h" #include "algorithm.h"
#include "commandline.h"
#include "qtcassert.h" #include "qtcassert.h"
#include "hostosinfo.h" #include "hostosinfo.h"
@@ -583,6 +584,8 @@ FilePath FileUtils::getOpenFilePathFromDevice(QWidget *parent,
return {}; return {};
} }
#endif // QT_WIDGETS_LIB
// Used on 'ls' output on unix-like systems. // Used on 'ls' output on unix-like systems.
void FileUtils::iterateLsOutput(const FilePath &base, void FileUtils::iterateLsOutput(const FilePath &base,
const QStringList &entries, 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<QByteArray(const CommandLine &)> &runInShell,
const std::function<bool(const Utils::FilePath &)> &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<QByteArray(const CommandLine &)> &runInShell,
const std::function<bool(const FilePath &)> &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 Copies the directory specified by \a srcFilePath recursively to \a tgtFilePath. \a tgtFilePath will contain

View File

@@ -31,6 +31,8 @@ QT_END_NAMESPACE
namespace Utils { namespace Utils {
class CommandLine;
class QTCREATOR_UTILS_EXPORT FileUtils class QTCREATOR_UTILS_EXPORT FileUtils
{ {
public: public:
@@ -77,10 +79,18 @@ public:
static FilePaths toFilePathList(const QStringList &paths); static FilePaths toFilePathList(const QStringList &paths);
static void iterateLsOutput(const FilePath &base, static void iterateLsOutput(
const QStringList &entries, const FilePath &base,
const FileFilter &filter, const QStringList &entries,
const std::function<bool(const FilePath &)> &callBack); const FileFilter &filter,
const std::function<bool(const FilePath &)> &callBack);
static void iterateUnixDirectory(
const FilePath &base,
const FileFilter &filter,
bool *useFind,
const std::function<QByteArray(const CommandLine &)> &runInShell,
const std::function<bool(const FilePath &)> &callBack);
static qint64 bytesAvailableFromDFOutput(const QByteArray &dfOutput); static qint64 bytesAvailableFromDFOutput(const QByteArray &dfOutput);

View File

@@ -143,16 +143,12 @@ public:
QString repoAndTag() const { return m_data.repoAndTag(); } QString repoAndTag() const { return m_data.repoAndTag(); }
QString dockerImageId() const { return m_data.imageId; } QString dockerImageId() const { return m_data.imageId; }
bool useFind() const { return m_useFind; }
void setUseFind(bool useFind) { m_useFind = useFind; }
Environment environment(); Environment environment();
CommandLine withDockerExecCmd(const CommandLine &cmd, bool interactive = false); CommandLine withDockerExecCmd(const CommandLine &cmd, bool interactive = false);
bool prepareForBuild(const Target *target); bool prepareForBuild(const Target *target);
private:
bool createContainer(); bool createContainer();
void startContainer(); void startContainer();
void stopCurrentContainer(); void stopCurrentContainer();
@@ -895,52 +891,13 @@ bool DockerDevice::ensureReachable(const FilePath &other) const
return d->ensureReachable(other.parentDir()); return d->ensureReachable(other.parentDir());
} }
void DockerDevice::iterateWithFind(const FilePath &filePath,
const std::function<bool(const Utils::FilePath &)> &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, void DockerDevice::iterateDirectory(const FilePath &filePath,
const std::function<bool(const FilePath &)> &callBack, const std::function<bool(const FilePath &)> &callBack,
const FileFilter &filter) const const FileFilter &filter) const
{ {
QTC_ASSERT(handlesFile(filePath), return); QTC_ASSERT(handlesFile(filePath), return);
auto runInShell = [this](const CommandLine &cmd) { return d->outputForRunInShell(cmd); };
if (d->useFind()) { FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack);
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);
} }
std::optional<QByteArray> DockerDevice::fileContents(const FilePath &filePath, std::optional<QByteArray> DockerDevice::fileContents(const FilePath &filePath,