forked from qt-creator/qt-creator
Utils: Add FilePath::copyRecursively
Change-Id: I0cb07158906a5e163ea35670f46f3b4fd9ec40b8 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: hjk <hjk@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
@@ -11,6 +11,10 @@
|
||||
#include "qtcassert.h"
|
||||
#include "utilstr.h"
|
||||
|
||||
#ifndef UTILS_STATIC_LIBRARY
|
||||
#include "qtcprocess.h"
|
||||
#endif
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QOperatingSystemVersion>
|
||||
#include <QRegularExpression>
|
||||
@@ -147,6 +151,101 @@ expected_str<void> DeviceFileAccess::copyFile(const FilePath &filePath, const Fi
|
||||
Tr::tr("copyFile is not implemented for \"%1\"").arg(filePath.toUserOutput()));
|
||||
}
|
||||
|
||||
expected_str<void> copyRecursively_fallback(const FilePath &src, const FilePath &target)
|
||||
{
|
||||
QString error;
|
||||
src.iterateDirectory(
|
||||
[&target, &src, &error](const FilePath &path) {
|
||||
const FilePath relative = path.relativePathFrom(src);
|
||||
const FilePath targetPath = target.pathAppended(relative.path());
|
||||
|
||||
if (!targetPath.parentDir().ensureWritableDir()) {
|
||||
error = QString("Could not create directory %1")
|
||||
.arg(targetPath.parentDir().toUserOutput());
|
||||
return false;
|
||||
}
|
||||
|
||||
const expected_str<void> result = path.copyFile(targetPath);
|
||||
if (!result) {
|
||||
error = result.error();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{{"*"}, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::Subdirectories});
|
||||
|
||||
if (error.isEmpty())
|
||||
return {};
|
||||
|
||||
return make_unexpected(error);
|
||||
}
|
||||
|
||||
expected_str<void> DeviceFileAccess::copyRecursively(const FilePath &src,
|
||||
const FilePath &target) const
|
||||
{
|
||||
#ifdef UTILS_STATIC_LIBRARY
|
||||
return copyRecursively_fallback(src, target);
|
||||
#else
|
||||
if (!target.isWritableDir()) {
|
||||
return make_unexpected(Tr::tr("Cannot copy %1 to %2, it is not a writable directory.")
|
||||
.arg(src.toUserOutput())
|
||||
.arg(target.toUserOutput()));
|
||||
}
|
||||
|
||||
const FilePath tar = FilePath::fromString("tar").onDevice(target);
|
||||
const FilePath targetTar = tar.searchInPath();
|
||||
|
||||
const FilePath sTar = FilePath::fromString("tar").onDevice(src);
|
||||
const FilePath sourceTar = sTar.searchInPath();
|
||||
|
||||
if (!targetTar.isExecutableFile() || !sourceTar.isExecutableFile())
|
||||
return copyRecursively_fallback(src, target);
|
||||
|
||||
QtcProcess srcProcess;
|
||||
QtcProcess targetProcess;
|
||||
|
||||
targetProcess.setProcessMode(ProcessMode::Writer);
|
||||
|
||||
QObject::connect(&srcProcess,
|
||||
&QtcProcess::readyReadStandardOutput,
|
||||
&targetProcess,
|
||||
[&srcProcess, &targetProcess]() {
|
||||
targetProcess.writeRaw(srcProcess.readAllRawStandardOutput());
|
||||
});
|
||||
|
||||
srcProcess.setCommand({sourceTar, {"-C", src.path(), "-cf", "-", "."}});
|
||||
targetProcess.setCommand({targetTar, {"xf", "-", "-C", target.path()}});
|
||||
|
||||
targetProcess.start();
|
||||
targetProcess.waitForStarted();
|
||||
|
||||
srcProcess.start();
|
||||
srcProcess.waitForFinished();
|
||||
|
||||
targetProcess.closeWriteChannel();
|
||||
|
||||
if (srcProcess.result() != ProcessResult::FinishedWithSuccess) {
|
||||
targetProcess.kill();
|
||||
return make_unexpected(
|
||||
Tr::tr("Failed to copy recursively from \"%1\" to \"%2\" while "
|
||||
"trying to create tar archive from source: %3")
|
||||
.arg(src.toUserOutput(), target.toUserOutput(), srcProcess.readAllStandardError()));
|
||||
}
|
||||
|
||||
targetProcess.waitForFinished();
|
||||
|
||||
if (targetProcess.result() != ProcessResult::FinishedWithSuccess) {
|
||||
return make_unexpected(Tr::tr("Failed to copy recursively from \"%1\" to \"%2\" while "
|
||||
"trying to extract tar archive to target: %3")
|
||||
.arg(src.toUserOutput(),
|
||||
target.toUserOutput(),
|
||||
targetProcess.readAllStandardError()));
|
||||
}
|
||||
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
bool DeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const
|
||||
{
|
||||
Q_UNUSED(filePath)
|
||||
|
@@ -37,6 +37,8 @@ protected:
|
||||
virtual bool removeFile(const FilePath &filePath) const;
|
||||
virtual bool removeRecursively(const FilePath &filePath, QString *error) const;
|
||||
virtual expected_str<void> copyFile(const FilePath &filePath, const FilePath &target) const;
|
||||
virtual expected_str<void> copyRecursively(const FilePath &filePath,
|
||||
const FilePath &target) const;
|
||||
virtual bool renameFile(const FilePath &filePath, const FilePath &target) const;
|
||||
|
||||
virtual OsType osType(const FilePath &filePath) const;
|
||||
|
@@ -1398,6 +1398,11 @@ bool FilePath::removeRecursively(QString *error) const
|
||||
return fileAccess()->removeRecursively(*this, error);
|
||||
}
|
||||
|
||||
expected_str<void> FilePath::copyRecursively(const FilePath &target) const
|
||||
{
|
||||
return fileAccess()->copyRecursively(*this, target);
|
||||
}
|
||||
|
||||
expected_str<void> FilePath::copyFile(const FilePath &target) const
|
||||
{
|
||||
if (host() != target.host()) {
|
||||
|
@@ -122,6 +122,7 @@ public:
|
||||
OsType osType() const;
|
||||
bool removeFile() const;
|
||||
bool removeRecursively(QString *error = nullptr) const;
|
||||
expected_str<void> copyRecursively(const FilePath &target) const;
|
||||
expected_str<void> copyFile(const FilePath &target) const;
|
||||
bool renameFile(const FilePath &target) const;
|
||||
qint64 fileSize() const;
|
||||
|
@@ -637,38 +637,35 @@ FilePathInfo FileUtils::filePathInfoFromTriple(const QString &infos)
|
||||
return {size, flags, dt};
|
||||
}
|
||||
|
||||
/*!
|
||||
Copies the directory specified by \a srcFilePath recursively to \a tgtFilePath. \a tgtFilePath will contain
|
||||
the target directory, which will be created. Example usage:
|
||||
|
||||
\code
|
||||
QString error;
|
||||
bool ok = Utils::FileUtils::copyRecursively("/foo/bar", "/foo/baz", &error);
|
||||
if (!ok)
|
||||
qDebug() << error;
|
||||
\endcode
|
||||
|
||||
This will copy the contents of /foo/bar into to the baz directory under /foo, which will be created in the process.
|
||||
|
||||
\note The \a error parameter is optional.
|
||||
|
||||
Returns whether the operation succeeded.
|
||||
*/
|
||||
|
||||
bool FileUtils::copyRecursively(const FilePath &srcFilePath, const FilePath &tgtFilePath, QString *error)
|
||||
bool FileUtils::copyRecursively(
|
||||
const FilePath &srcFilePath,
|
||||
const FilePath &tgtFilePath,
|
||||
QString *error,
|
||||
std::function<bool(const FilePath &, const FilePath &, QString *)> copyHelper)
|
||||
{
|
||||
return copyRecursively(
|
||||
srcFilePath, tgtFilePath, error, [](const FilePath &src, const FilePath &dest, QString *error) {
|
||||
if (!src.copyFile(dest)) {
|
||||
if (error) {
|
||||
*error = QCoreApplication::translate("Utils::FileUtils",
|
||||
"Could not copy file \"%1\" to \"%2\".")
|
||||
.arg(src.toUserOutput(), dest.toUserOutput());
|
||||
}
|
||||
return false;
|
||||
if (srcFilePath.isDir()) {
|
||||
if (!tgtFilePath.ensureWritableDir()) {
|
||||
if (error) {
|
||||
*error = QCoreApplication::translate("Utils::FileUtils",
|
||||
"Failed to create directory \"%1\".")
|
||||
.arg(tgtFilePath.toUserOutput());
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return false;
|
||||
}
|
||||
const QDir sourceDir(srcFilePath.toString());
|
||||
const QStringList fileNames = sourceDir.entryList(
|
||||
QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
|
||||
for (const QString &fileName : fileNames) {
|
||||
const FilePath newSrcFilePath = srcFilePath / fileName;
|
||||
const FilePath newTgtFilePath = tgtFilePath / fileName;
|
||||
if (!copyRecursively(newSrcFilePath, newTgtFilePath, error, copyHelper))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!copyHelper(srcFilePath, tgtFilePath, error))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@@ -61,14 +61,12 @@ public:
|
||||
};
|
||||
#endif // QT_GUI_LIB
|
||||
|
||||
static bool copyRecursively(const FilePath &srcFilePath,
|
||||
const FilePath &tgtFilePath,
|
||||
QString *error = nullptr);
|
||||
template<typename T>
|
||||
static bool copyRecursively(const FilePath &srcFilePath,
|
||||
const FilePath &tgtFilePath,
|
||||
QString *error,
|
||||
T &©Helper);
|
||||
static bool copyRecursively(
|
||||
const FilePath &srcFilePath,
|
||||
const FilePath &tgtFilePath,
|
||||
QString *error,
|
||||
std::function<bool(const FilePath &, const FilePath &, QString *)> helper);
|
||||
|
||||
static bool copyIfDifferent(const FilePath &srcFilePath,
|
||||
const FilePath &tgtFilePath);
|
||||
static QString fileSystemFriendlyName(const QString &name);
|
||||
@@ -126,39 +124,6 @@ public:
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool FileUtils::copyRecursively(const FilePath &srcFilePath,
|
||||
const FilePath &tgtFilePath,
|
||||
QString *error,
|
||||
T &©Helper)
|
||||
{
|
||||
if (srcFilePath.isDir()) {
|
||||
if (!tgtFilePath.exists()) {
|
||||
if (!tgtFilePath.ensureWritableDir()) {
|
||||
if (error) {
|
||||
*error = QCoreApplication::translate("Utils::FileUtils",
|
||||
"Failed to create directory \"%1\".")
|
||||
.arg(tgtFilePath.toUserOutput());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const QDir sourceDir(srcFilePath.toString());
|
||||
const QStringList fileNames = sourceDir.entryList(
|
||||
QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
|
||||
for (const QString &fileName : fileNames) {
|
||||
const FilePath newSrcFilePath = srcFilePath / fileName;
|
||||
const FilePath newTgtFilePath = tgtFilePath / fileName;
|
||||
if (!copyRecursively(newSrcFilePath, newTgtFilePath, error, copyHelper))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!copyHelper(srcFilePath, tgtFilePath, error))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// for actually finding out if e.g. directories are writable on Windows
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
|
@@ -1421,11 +1421,10 @@ void ProjectExplorerPlugin::testProject_multipleBuildConfigs()
|
||||
// Copy project from qrc file and set it up.
|
||||
QTemporaryDir * const tempDir = TemporaryDirectory::masterTemporaryDirectory();
|
||||
QVERIFY(tempDir->isValid());
|
||||
QString error;
|
||||
const FilePath projectDir = FilePath::fromString(tempDir->path() + "/generic-project");
|
||||
FileUtils::copyRecursively(":/projectexplorer/testdata/generic-project",
|
||||
projectDir, &error);
|
||||
QVERIFY2(error.isEmpty(), qPrintable(error));
|
||||
const auto copyResult = FilePath(":/projectexplorer/testdata/generic-project").copyRecursively(projectDir);
|
||||
|
||||
QVERIFY2(copyResult, qPrintable(copyResult.error()));
|
||||
const QFileInfoList files = QDir(projectDir.toString()).entryInfoList(QDir::Files | QDir::Dirs);
|
||||
for (const QFileInfo &f : files)
|
||||
QFile(f.absoluteFilePath()).setPermissions(f.permissions() | QFile::WriteUser);
|
||||
|
@@ -124,10 +124,12 @@ QString ExamplesWelcomePage::copyToAlternativeLocation(const QFileInfo& proFileI
|
||||
QMessageBox::NoButton);
|
||||
return QString();
|
||||
} else {
|
||||
QString error;
|
||||
QString targetDir = destBaseDir + QLatin1Char('/') + exampleDirName;
|
||||
if (FileUtils::copyRecursively(FilePath::fromString(projectDir),
|
||||
FilePath::fromString(targetDir), &error)) {
|
||||
|
||||
expected_str<void> result
|
||||
= FilePath::fromString(projectDir).copyRecursively(FilePath::fromString(targetDir));
|
||||
|
||||
if (result) {
|
||||
// set vars to new location
|
||||
const QStringList::Iterator end = filesToOpen.end();
|
||||
for (QStringList::Iterator it = filesToOpen.begin(); it != end; ++it)
|
||||
@@ -136,21 +138,21 @@ QString ExamplesWelcomePage::copyToAlternativeLocation(const QFileInfo& proFileI
|
||||
for (const QString &dependency : dependencies) {
|
||||
const FilePath targetFile = FilePath::fromString(targetDir)
|
||||
.pathAppended(QDir(dependency).dirName());
|
||||
if (!FileUtils::copyRecursively(FilePath::fromString(dependency), targetFile,
|
||||
&error)) {
|
||||
result = FilePath::fromString(dependency).copyRecursively(targetFile);
|
||||
if (!result) {
|
||||
QMessageBox::warning(ICore::dialogParent(),
|
||||
Tr::tr("Cannot Copy Project"),
|
||||
error);
|
||||
result.error());
|
||||
// do not fail, just warn;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return targetDir + QLatin1Char('/') + proFileInfo.fileName();
|
||||
} else {
|
||||
QMessageBox::warning(ICore::dialogParent(), Tr::tr("Cannot Copy Project"), error);
|
||||
QMessageBox::warning(ICore::dialogParent(),
|
||||
Tr::tr("Cannot Copy Project"),
|
||||
result.error());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (code == Keep)
|
||||
|
@@ -629,9 +629,10 @@ WelcomeMode::WelcomeMode()
|
||||
|
||||
m_dataModelDownloader = new DataModelDownloader(this);
|
||||
if (!m_dataModelDownloader->exists()) { //Fallback if data cannot be downloaded
|
||||
Utils::FileUtils::copyRecursively(Utils::FilePath::fromUserInput(welcomePagePath
|
||||
+ "/dataImports"),
|
||||
m_dataModelDownloader->targetFolder());
|
||||
// TODO: Check result?
|
||||
Utils::FilePath::fromUserInput(welcomePagePath + "/dataImports")
|
||||
.copyRecursively(m_dataModelDownloader->targetFolder());
|
||||
|
||||
m_dataModelDownloader->setForceDownload(true);
|
||||
}
|
||||
Utils::FilePath readme = Utils::FilePath::fromUserInput(m_dataModelDownloader->targetFolder().toString()
|
||||
|
Reference in New Issue
Block a user