Clear Build and Cache Directory After Project Move or Copy (#2184)

* Clear build and Cache directories when copying or moving projects

Signed-off-by: nggieber <nggieber@amazon.com>

* Updated tests for moving projects and Added tests for copying projects and skipping build files

Signed-off-by: nggieber <nggieber@amazon.com>

* Address PR feedback and fix a few minor UX issues, and request project be rebuild after copying or moving

Signed-off-by: nggieber <nggieber@amazon.com>

* Include ProjectInfo.h in ProjectUtils.h so linux will compile

Signed-off-by: nggieber <nggieber@amazon.com>
main
AMZN-nggieber 4 years ago committed by GitHub
parent e6ddc29cb4
commit 69312a5e56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -9,4 +9,5 @@ set(FILES
Python_linux.cpp Python_linux.cpp
ProjectBuilderWorker_linux.cpp ProjectBuilderWorker_linux.cpp
ProjectUtils_linux.cpp ProjectUtils_linux.cpp
ProjectManagerDefs_linux.cpp
) )

@ -0,0 +1,15 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <ProjectManagerDefs.h>
namespace O3DE::ProjectManager
{
const QString ProjectBuildPathPostfix = ProjectBuildDirectoryName + "/linux";
} // namespace O3DE::ProjectManager

@ -9,4 +9,5 @@ set(FILES
Python_mac.cpp Python_mac.cpp
ProjectBuilderWorker_mac.cpp ProjectBuilderWorker_mac.cpp
ProjectUtils_mac.cpp ProjectUtils_mac.cpp
ProjectManagerDefs_mac.cpp
) )

@ -0,0 +1,15 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <ProjectManagerDefs.h>
namespace O3DE::ProjectManager
{
const QString ProjectBuildPathPostfix = ProjectBuildDirectoryName + "/mac_xcode";
} // namespace O3DE::ProjectManager

@ -9,4 +9,5 @@ set(FILES
Python_windows.cpp Python_windows.cpp
ProjectBuilderWorker_windows.cpp ProjectBuilderWorker_windows.cpp
ProjectUtils_windows.cpp ProjectUtils_windows.cpp
ProjectManagerDefs_windows.cpp
) )

@ -75,8 +75,17 @@ namespace O3DE::ProjectManager
m_configProjectProcess->start( m_configProjectProcess->start(
"cmake", "cmake",
QStringList{ "-B", QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix), "-S", m_projectInfo.m_path, "-G", QStringList
"Visual Studio 16", "-DLY_3RDPARTY_PATH=" + engineInfo.m_thirdPartyPath }); {
"-B",
QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix),
"-S",
m_projectInfo.m_path,
"-G",
"Visual Studio 16",
"-DLY_3RDPARTY_PATH=" + engineInfo.m_thirdPartyPath,
"-DLY_UNITY_BUILD=1"
});
if (!m_configProjectProcess->waitForStarted()) if (!m_configProjectProcess->waitForStarted())
{ {
@ -124,8 +133,16 @@ namespace O3DE::ProjectManager
m_buildProjectProcess->start( m_buildProjectProcess->start(
"cmake", "cmake",
QStringList{ "--build", QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix), "--target", QStringList
m_projectInfo.m_projectName + ".GameLauncher", "Editor", "--config", "profile" }); {
"--build",
QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix),
"--target",
m_projectInfo.m_projectName + ".GameLauncher",
"Editor",
"--config",
"profile"
});
if (!m_buildProjectProcess->waitForStarted()) if (!m_buildProjectProcess->waitForStarted())
{ {

@ -0,0 +1,14 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <ProjectManagerDefs.h>
namespace O3DE::ProjectManager
{
const QString ProjectBuildPathPostfix = ProjectBuildDirectoryName + "/windows_vs2019";
} // namespace O3DE::ProjectManager

@ -14,8 +14,6 @@
namespace O3DE::ProjectManager namespace O3DE::ProjectManager
{ {
const QString ProjectBuilderWorker::BuildCancelled = QObject::tr("Build Cancelled.");
ProjectBuilderWorker::ProjectBuilderWorker(const ProjectInfo& projectInfo) ProjectBuilderWorker::ProjectBuilderWorker(const ProjectInfo& projectInfo)
: QObject() : QObject()
, m_projectInfo(projectInfo) , m_projectInfo(projectInfo)

@ -22,7 +22,7 @@ namespace O3DE::ProjectManager
// QProcess::waitForFinished uses -1 to indicate that the process should not timeout // QProcess::waitForFinished uses -1 to indicate that the process should not timeout
static constexpr int MaxBuildTimeMSecs = -1; static constexpr int MaxBuildTimeMSecs = -1;
// Build was cancelled // Build was cancelled
static const QString BuildCancelled; inline static const QString BuildCancelled = QObject::tr("Build Cancelled.");
Q_OBJECT Q_OBJECT

@ -233,7 +233,7 @@ namespace O3DE::ProjectManager
AzQtComponents::ShowFileOnDesktop(m_projectInfo.m_path); AzQtComponents::ShowFileOnDesktop(m_projectInfo.m_path);
}); });
menu->addSeparator(); menu->addSeparator();
menu->addAction(tr("Duplicate"), this, [this]() { emit CopyProject(m_projectInfo.m_path); }); menu->addAction(tr("Duplicate"), this, [this]() { emit CopyProject(m_projectInfo); });
menu->addSeparator(); menu->addSeparator();
menu->addAction(tr("Remove from O3DE"), this, [this]() { emit RemoveProject(m_projectInfo.m_path); }); menu->addAction(tr("Remove from O3DE"), this, [this]() { emit RemoveProject(m_projectInfo.m_path); });
menu->addAction(tr("Delete this Project"), this, [this]() { emit DeleteProject(m_projectInfo.m_path); }); menu->addAction(tr("Delete this Project"), this, [this]() { emit DeleteProject(m_projectInfo.m_path); });

@ -88,7 +88,7 @@ namespace O3DE::ProjectManager
signals: signals:
void OpenProject(const QString& projectName); void OpenProject(const QString& projectName);
void EditProject(const QString& projectName); void EditProject(const QString& projectName);
void CopyProject(const QString& projectName); void CopyProject(const ProjectInfo& projectInfo);
void RemoveProject(const QString& projectName); void RemoveProject(const QString& projectName);
void DeleteProject(const QString& projectName); void DeleteProject(const QString& projectName);
void BuildProject(const ProjectInfo& projectInfo); void BuildProject(const ProjectInfo& projectInfo);

@ -14,8 +14,10 @@ namespace O3DE::ProjectManager
inline constexpr static int ProjectPreviewImageHeight = 280; inline constexpr static int ProjectPreviewImageHeight = 280;
inline constexpr static int ProjectTemplateImageWidth = 92; inline constexpr static int ProjectTemplateImageWidth = 92;
static const QString ProjectBuildPathPostfix = "build/windows_vs2019"; static const QString ProjectBuildDirectoryName = "build";
extern const QString ProjectBuildPathPostfix;
static const QString ProjectBuildPathCmakeFiles = "CMakeFiles"; static const QString ProjectBuildPathCmakeFiles = "CMakeFiles";
static const QString ProjectBuildErrorLogName = "CMakeProjectBuildError.log"; static const QString ProjectBuildErrorLogName = "CMakeProjectBuildError.log";
static const QString ProjectCacheDirectoryName = "Cache";
static const QString ProjectPreviewImagePath = "preview.png"; static const QString ProjectPreviewImagePath = "preview.png";
} // namespace O3DE::ProjectManager } // namespace O3DE::ProjectManager

@ -6,6 +6,7 @@
*/ */
#include <ProjectUtils.h> #include <ProjectUtils.h>
#include <ProjectManagerDefs.h>
#include <PythonBindingsInterface.h> #include <PythonBindingsInterface.h>
#include <QFileDialog> #include <QFileDialog>
@ -59,29 +60,63 @@ namespace O3DE::ProjectManager
return false; return false;
} }
static bool SkipFilePaths(const QString& curPath, QStringList& skippedPaths, QStringList& deeperSkippedPaths)
{
bool skip = false;
for (const QString& skippedPath : skippedPaths)
{
QString nativeSkippedPath = QDir::toNativeSeparators(skippedPath);
QString firstSectionSkippedPath = nativeSkippedPath.section(QDir::separator(), 0, 0);
if (curPath == firstSectionSkippedPath)
{
// We are at the end of the path to skip, so skip it
if (nativeSkippedPath == firstSectionSkippedPath)
{
skippedPaths.removeAll(skippedPath);
skip = true;
break;
}
// Append the next section of the skipped path
else
{
deeperSkippedPaths.append(nativeSkippedPath.section(QDir::separator(), 1));
}
}
}
return skip;
}
typedef AZStd::function<void(/*fileCount=*/int, /*totalSizeInBytes=*/int)> StatusFunction; typedef AZStd::function<void(/*fileCount=*/int, /*totalSizeInBytes=*/int)> StatusFunction;
static void RecursiveGetAllFiles(const QDir& directory, QStringList& outFileList, qint64& outTotalSizeInBytes, StatusFunction statusCallback) static void RecursiveGetAllFiles(const QDir& directory, QStringList& skippedPaths, int& outFileCount, qint64& outTotalSizeInBytes, StatusFunction statusCallback)
{ {
const QStringList entries = directory.entryList(QDir::Dirs | QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot); const QStringList entries = directory.entryList(QDir::Dirs | QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot);
for (const QString& entryPath : entries) for (const QString& entryPath : entries)
{ {
const QString filePath = QDir::toNativeSeparators(QString("%1/%2").arg(directory.path()).arg(entryPath)); const QString filePath = QDir::toNativeSeparators(QString("%1/%2").arg(directory.path()).arg(entryPath));
QStringList deeperSkippedPaths;
if (SkipFilePaths(entryPath, skippedPaths, deeperSkippedPaths))
{
continue;
}
QFileInfo fileInfo(filePath); QFileInfo fileInfo(filePath);
if (fileInfo.isDir()) if (fileInfo.isDir())
{ {
QDir subDirectory(filePath); QDir subDirectory(filePath);
RecursiveGetAllFiles(subDirectory, outFileList, outTotalSizeInBytes, statusCallback); RecursiveGetAllFiles(subDirectory, deeperSkippedPaths, outFileCount, outTotalSizeInBytes, statusCallback);
} }
else else
{ {
outFileList.push_back(filePath); ++outFileCount;
outTotalSizeInBytes += fileInfo.size(); outTotalSizeInBytes += fileInfo.size();
const int updateStatusEvery = 64; const int updateStatusEvery = 64;
if (outFileList.size() % updateStatusEvery == 0) if (outFileCount % updateStatusEvery == 0)
{ {
statusCallback(outFileList.size(), outTotalSizeInBytes); statusCallback(outFileCount, outTotalSizeInBytes);
} }
} }
} }
@ -90,7 +125,8 @@ namespace O3DE::ProjectManager
static bool CopyDirectory(QProgressDialog* progressDialog, static bool CopyDirectory(QProgressDialog* progressDialog,
const QString& origPath, const QString& origPath,
const QString& newPath, const QString& newPath,
QStringList& filesToCopy, QStringList& skippedPaths,
int filesToCopyCount,
int& outNumCopiedFiles, int& outNumCopiedFiles,
qint64 totalSizeToCopy, qint64 totalSizeToCopy,
qint64& outCopiedFileSize, qint64& outCopiedFileSize,
@ -102,18 +138,24 @@ namespace O3DE::ProjectManager
return false; return false;
} }
for (QString directory : original.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) for (const QString& directory : original.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
{ {
if (progressDialog->wasCanceled()) if (progressDialog->wasCanceled())
{ {
return false; return false;
} }
QStringList deeperSkippedPaths;
if (SkipFilePaths(directory, skippedPaths, deeperSkippedPaths))
{
continue;
}
QString newDirectoryPath = newPath + QDir::separator() + directory; QString newDirectoryPath = newPath + QDir::separator() + directory;
original.mkpath(newDirectoryPath); original.mkpath(newDirectoryPath);
if (!CopyDirectory(progressDialog, origPath + QDir::separator() + directory, if (!CopyDirectory(progressDialog, origPath + QDir::separator() + directory, newDirectoryPath, deeperSkippedPaths,
newDirectoryPath, filesToCopy, outNumCopiedFiles, totalSizeToCopy, outCopiedFileSize, showIgnoreFileDialog)) filesToCopyCount, outNumCopiedFiles, totalSizeToCopy, outCopiedFileSize, showIgnoreFileDialog))
{ {
return false; return false;
} }
@ -121,18 +163,25 @@ namespace O3DE::ProjectManager
QLocale locale; QLocale locale;
const float progressDialogRangeHalf = qFabs(progressDialog->maximum() - progressDialog->minimum()) * 0.5f; const float progressDialogRangeHalf = qFabs(progressDialog->maximum() - progressDialog->minimum()) * 0.5f;
for (QString file : original.entryList(QDir::Files)) for (const QString& file : original.entryList(QDir::Files))
{ {
if (progressDialog->wasCanceled()) if (progressDialog->wasCanceled())
{ {
return false; return false;
} }
// Unused by this function but neccesary to pass in to SkipFilePaths
QStringList deeperSkippedPaths;
if (SkipFilePaths(file, skippedPaths, deeperSkippedPaths))
{
continue;
}
// Progress window update // Progress window update
{ {
// Weight in the number of already copied files as well as the copied bytes to get a better progress indication // Weight in the number of already copied files as well as the copied bytes to get a better progress indication
// for cases combining many small files and some really large files. // for cases combining many small files and some really large files.
const float normalizedNumFiles = static_cast<float>(outNumCopiedFiles) / filesToCopy.count(); const float normalizedNumFiles = static_cast<float>(outNumCopiedFiles) / filesToCopyCount;
const float normalizedFileSize = static_cast<float>(outCopiedFileSize) / totalSizeToCopy; const float normalizedFileSize = static_cast<float>(outCopiedFileSize) / totalSizeToCopy;
const int progress = normalizedNumFiles * progressDialogRangeHalf + normalizedFileSize * progressDialogRangeHalf; const int progress = normalizedNumFiles * progressDialogRangeHalf + normalizedFileSize * progressDialogRangeHalf;
progressDialog->setValue(progress); progressDialog->setValue(progress);
@ -140,7 +189,7 @@ namespace O3DE::ProjectManager
const QString copiedFileSizeString = locale.formattedDataSize(outCopiedFileSize); const QString copiedFileSizeString = locale.formattedDataSize(outCopiedFileSize);
const QString totalFileSizeString = locale.formattedDataSize(totalSizeToCopy); const QString totalFileSizeString = locale.formattedDataSize(totalSizeToCopy);
progressDialog->setLabelText(QString("Coping file %1 of %2 (%3 of %4) ...").arg(QString::number(outNumCopiedFiles), progressDialog->setLabelText(QString("Coping file %1 of %2 (%3 of %4) ...").arg(QString::number(outNumCopiedFiles),
QString::number(filesToCopy.count()), QString::number(filesToCopyCount),
copiedFileSizeString, copiedFileSizeString,
totalFileSizeString)); totalFileSizeString));
qApp->processEvents(QEventLoop::ExcludeUserInputEvents); qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
@ -194,6 +243,39 @@ namespace O3DE::ProjectManager
return true; return true;
} }
static bool ClearProjectBuildArtifactsAndCache(const QString& origPath, const QString& newPath, QWidget* parent)
{
QDir buildDirectory = QDir(newPath);
if ((!buildDirectory.cd(ProjectBuildDirectoryName) || !DeleteProjectFiles(buildDirectory.path(), true))
&& QDir(origPath).cd(ProjectBuildDirectoryName))
{
QMessageBox::warning(
parent,
QObject::tr("Clear Build Artifacts"),
QObject::tr("Build artifacts failed to delete for moved project. Please manually delete build directory at \"%1\"")
.arg(buildDirectory.path()),
QMessageBox::Close);
return false;
}
QDir cacheDirectory = QDir(newPath);
if ((!cacheDirectory.cd(ProjectCacheDirectoryName) || !DeleteProjectFiles(cacheDirectory.path(), true))
&& QDir(origPath).cd(ProjectCacheDirectoryName))
{
QMessageBox::warning(
parent,
QObject::tr("Clear Asset Cache"),
QObject::tr("Asset cache failed to delete for moved project. Please manually delete cache directory at \"%1\"")
.arg(cacheDirectory.path()),
QMessageBox::Close);
return false;
}
return false;
}
bool AddProjectDialog(QWidget* parent) bool AddProjectDialog(QWidget* parent)
{ {
QString path = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(parent, QObject::tr("Select Project Directory"))); QString path = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(parent, QObject::tr("Select Project Directory")));
@ -215,7 +297,7 @@ namespace O3DE::ProjectManager
return PythonBindingsInterface::Get()->RemoveProject(path); return PythonBindingsInterface::Get()->RemoveProject(path);
} }
bool CopyProjectDialog(const QString& origPath, QWidget* parent) bool CopyProjectDialog(const QString& origPath, ProjectInfo& newProjectInfo, QWidget* parent)
{ {
bool copyResult = false; bool copyResult = false;
@ -225,6 +307,8 @@ namespace O3DE::ProjectManager
QFileDialog::getExistingDirectory(parent, QObject::tr("Select New Project Directory"), parentOrigDir.path())); QFileDialog::getExistingDirectory(parent, QObject::tr("Select New Project Directory"), parentOrigDir.path()));
if (!newPath.isEmpty()) if (!newPath.isEmpty())
{ {
newProjectInfo.m_path = newPath;
if (!WarnDirectoryOverwrite(newPath, parent)) if (!WarnDirectoryOverwrite(newPath, parent))
{ {
return false; return false;
@ -236,7 +320,7 @@ namespace O3DE::ProjectManager
return copyResult; return copyResult;
} }
bool CopyProject(const QString& origPath, const QString& newPath, QWidget* parent) bool CopyProject(const QString& origPath, const QString& newPath, QWidget* parent, bool skipRegister)
{ {
// Disallow copying from or into subdirectory // Disallow copying from or into subdirectory
if (IsDirectoryDescedent(origPath, newPath) || IsDirectoryDescedent(newPath, origPath)) if (IsDirectoryDescedent(origPath, newPath) || IsDirectoryDescedent(newPath, origPath))
@ -244,8 +328,13 @@ namespace O3DE::ProjectManager
return false; return false;
} }
QStringList filesToCopy; int filesToCopyCount = 0;
qint64 totalSizeInBytes = 0; qint64 totalSizeInBytes = 0;
QStringList skippedPaths
{
ProjectBuildDirectoryName,
ProjectCacheDirectoryName
};
QProgressDialog* progressDialog = new QProgressDialog(parent); QProgressDialog* progressDialog = new QProgressDialog(parent);
progressDialog->setAutoClose(true); progressDialog->setAutoClose(true);
@ -256,7 +345,8 @@ namespace O3DE::ProjectManager
progressDialog->show(); progressDialog->show();
QLocale locale; QLocale locale;
RecursiveGetAllFiles(origPath, filesToCopy, totalSizeInBytes, [=](int fileCount, int sizeInBytes) QStringList getFilesSkippedPaths(skippedPaths);
RecursiveGetAllFiles(origPath, getFilesSkippedPaths, filesToCopyCount, totalSizeInBytes, [=](int fileCount, int sizeInBytes)
{ {
// Create a human-readable version of the file size. // Create a human-readable version of the file size.
const QString fileSizeString = locale.formattedDataSize(sizeInBytes); const QString fileSizeString = locale.formattedDataSize(sizeInBytes);
@ -275,8 +365,10 @@ namespace O3DE::ProjectManager
// Phase 1: Copy files // Phase 1: Copy files
bool showIgnoreFileDialog = true; bool showIgnoreFileDialog = true;
bool success = CopyDirectory(progressDialog, origPath, newPath, filesToCopy, numFilesCopied, totalSizeInBytes, copiedFileSize, showIgnoreFileDialog); QStringList copyFilesSkippedPaths(skippedPaths);
if (success) bool success = CopyDirectory(progressDialog, origPath, newPath, copyFilesSkippedPaths, filesToCopyCount, numFilesCopied,
totalSizeInBytes, copiedFileSize, showIgnoreFileDialog);
if (success && !skipRegister)
{ {
// Phase 2: Register project // Phase 2: Register project
success = RegisterProject(newPath); success = RegisterProject(newPath);
@ -299,7 +391,7 @@ namespace O3DE::ProjectManager
QDir projectDirectory(path); QDir projectDirectory(path);
if (projectDirectory.exists()) if (projectDirectory.exists())
{ {
// Check if there is an actual project hereor just force it // Check if there is an actual project here or just force it
if (force || PythonBindingsInterface::Get()->GetProject(path).IsSuccess()) if (force || PythonBindingsInterface::Get()->GetProject(path).IsSuccess())
{ {
return projectDirectory.removeRecursively(); return projectDirectory.removeRecursively();
@ -309,12 +401,12 @@ namespace O3DE::ProjectManager
return false; return false;
} }
bool MoveProject(QString origPath, QString newPath, QWidget* parent, bool ignoreRegister) bool MoveProject(QString origPath, QString newPath, QWidget* parent, bool skipRegister)
{ {
origPath = QDir::toNativeSeparators(origPath); origPath = QDir::toNativeSeparators(origPath);
newPath = QDir::toNativeSeparators(newPath); newPath = QDir::toNativeSeparators(newPath);
if (!WarnDirectoryOverwrite(newPath, parent) || (!ignoreRegister && !UnregisterProject(origPath))) if (!WarnDirectoryOverwrite(newPath, parent) || (!skipRegister && !UnregisterProject(origPath)))
{ {
return false; return false;
} }
@ -334,8 +426,13 @@ namespace O3DE::ProjectManager
DeleteProjectFiles(origPath, true); DeleteProjectFiles(origPath, true);
} }
else
{
// If directoy rename succeeded then build and cache directories need to be deleted seperately
ClearProjectBuildArtifactsAndCache(origPath, newPath, parent);
}
if (!ignoreRegister && !RegisterProject(newPath)) if (!skipRegister && !RegisterProject(newPath))
{ {
return false; return false;
} }

@ -7,6 +7,8 @@
#pragma once #pragma once
#include <ScreenDefs.h> #include <ScreenDefs.h>
#include <ProjectInfo.h>
#include <QWidget> #include <QWidget>
#include <AzCore/Outcome/Outcome.h> #include <AzCore/Outcome/Outcome.h>
@ -17,10 +19,10 @@ namespace O3DE::ProjectManager
bool AddProjectDialog(QWidget* parent = nullptr); bool AddProjectDialog(QWidget* parent = nullptr);
bool RegisterProject(const QString& path); bool RegisterProject(const QString& path);
bool UnregisterProject(const QString& path); bool UnregisterProject(const QString& path);
bool CopyProjectDialog(const QString& origPath, QWidget* parent = nullptr); bool CopyProjectDialog(const QString& origPath, ProjectInfo& newProjectInfo, QWidget* parent = nullptr);
bool CopyProject(const QString& origPath, const QString& newPath, QWidget* parent); bool CopyProject(const QString& origPath, const QString& newPath, QWidget* parent, bool skipRegister = false);
bool DeleteProjectFiles(const QString& path, bool force = false); bool DeleteProjectFiles(const QString& path, bool force = false);
bool MoveProject(QString origPath, QString newPath, QWidget* parent = nullptr, bool ignoreRegister = false); bool MoveProject(QString origPath, QString newPath, QWidget* parent, bool skipRegister = false);
bool ReplaceFile(const QString& origFile, const QString& newFile, QWidget* parent = nullptr, bool interactive = true); bool ReplaceFile(const QString& origFile, const QString& newFile, QWidget* parent = nullptr, bool interactive = true);

@ -384,14 +384,17 @@ namespace O3DE::ProjectManager
emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject); emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject);
} }
} }
void ProjectsScreen::HandleCopyProject(const QString& projectPath) void ProjectsScreen::HandleCopyProject(const ProjectInfo& projectInfo)
{ {
if (!WarnIfInBuildQueue(projectPath)) if (!WarnIfInBuildQueue(projectInfo.m_path))
{ {
ProjectInfo newProjectInfo(projectInfo);
// Open file dialog and choose location for copied project then register copy with O3DE // Open file dialog and choose location for copied project then register copy with O3DE
if (ProjectUtils::CopyProjectDialog(projectPath, this)) if (ProjectUtils::CopyProjectDialog(projectInfo.m_path, newProjectInfo, this))
{ {
ResetProjectsContent(); ResetProjectsContent();
emit NotifyBuildProject(newProjectInfo);
emit ChangeScreenRequest(ProjectManagerScreen::Projects); emit ChangeScreenRequest(ProjectManagerScreen::Projects);
} }
} }

@ -44,7 +44,7 @@ namespace O3DE::ProjectManager
void HandleAddProjectButton(); void HandleAddProjectButton();
void HandleOpenProject(const QString& projectPath); void HandleOpenProject(const QString& projectPath);
void HandleEditProject(const QString& projectPath); void HandleEditProject(const QString& projectPath);
void HandleCopyProject(const QString& projectPath); void HandleCopyProject(const ProjectInfo& projectInfo);
void HandleRemoveProject(const QString& projectPath); void HandleRemoveProject(const QString& projectPath);
void HandleDeleteProject(const QString& projectPath); void HandleDeleteProject(const QString& projectPath);

@ -219,11 +219,13 @@ namespace O3DE::ProjectManager
// Move project first to avoid trying to update settings at the new location before it has been moved there // Move project first to avoid trying to update settings at the new location before it has been moved there
if (newProjectSettings.m_path != m_projectInfo.m_path) if (newProjectSettings.m_path != m_projectInfo.m_path)
{ {
if (!ProjectUtils::MoveProject(m_projectInfo.m_path, newProjectSettings.m_path)) if (!ProjectUtils::MoveProject(m_projectInfo.m_path, newProjectSettings.m_path, this))
{ {
QMessageBox::critical(this, tr("Project move failed"), tr("Failed to move project.")); QMessageBox::critical(this, tr("Project move failed"), tr("Failed to move project."));
return false; return false;
} }
emit NotifyBuildProject(newProjectSettings);
} }
// Update project if settings changed // Update project if settings changed

@ -8,6 +8,7 @@
#include <AzCore/UnitTest/TestTypes.h> #include <AzCore/UnitTest/TestTypes.h>
#include <Application.h> #include <Application.h>
#include <ProjectUtils.h> #include <ProjectUtils.h>
#include <ProjectManagerDefs.h>
#include <ProjectManager_Test_Traits_Platform.h> #include <ProjectManager_Test_Traits_Platform.h>
#include <QFile> #include <QFile>
@ -25,16 +26,31 @@ namespace O3DE::ProjectManager
: public ::UnitTest::ScopedAllocatorSetupFixture : public ::UnitTest::ScopedAllocatorSetupFixture
{ {
public: public:
static inline QString ReplaceFirstAWithB(const QString& originalString)
{
QString bString(originalString);
return bString.replace(bString.indexOf('A'), 1, 'B');
}
ProjectManagerUtilsTests() ProjectManagerUtilsTests()
{ {
m_application = AZStd::make_unique<ProjectManager::Application>(); m_application = AZStd::make_unique<ProjectManager::Application>();
m_application->Init(false); m_application->Init(false);
m_projectAPath = "ProjectA";
// Replaces first 'A' with 'B'
m_projectBPath = ReplaceFirstAWithB(m_projectAPath);
m_projectABuildPath = QString("%1%2%3").arg(m_projectAPath, QDir::separator(), ProjectBuildDirectoryName);
m_projectBBuildPath = ReplaceFirstAWithB(m_projectABuildPath);
QDir dir; QDir dir;
dir.mkdir("ProjectA"); dir.mkpath(m_projectABuildPath);
dir.mkdir("ProjectB"); dir.mkdir(m_projectBPath);
QFile origFile("ProjectA/origFile.txt"); m_projectAOrigFilePath = QString("%1%2%3").arg(m_projectAPath, QDir::separator(), "origFile.txt");
m_projectBOrigFilePath = ReplaceFirstAWithB(m_projectAOrigFilePath);
QFile origFile(m_projectAOrigFilePath);
if (origFile.open(QIODevice::ReadWrite)) if (origFile.open(QIODevice::ReadWrite))
{ {
QTextStream stream(&origFile); QTextStream stream(&origFile);
@ -42,63 +58,153 @@ namespace O3DE::ProjectManager
origFile.close(); origFile.close();
} }
QFile replaceFile("ProjectA/replaceFile.txt"); m_projectAReplaceFilePath = QString("%1%2%3").arg(m_projectAPath, QDir::separator(), "replaceFile.txt");
m_projectBReplaceFilePath = ReplaceFirstAWithB(m_projectAReplaceFilePath);
QFile replaceFile(m_projectAReplaceFilePath);
if (replaceFile.open(QIODevice::ReadWrite)) if (replaceFile.open(QIODevice::ReadWrite))
{ {
QTextStream stream(&replaceFile); QTextStream stream(&replaceFile);
stream << "replace" << Qt::endl; stream << "replace" << Qt::endl;
replaceFile.close(); replaceFile.close();
} }
m_projectABuildFilePath = QString("%1%2%3").arg(m_projectABuildPath, QDir::separator(), "build.obj");
m_projectBBuildFilePath = ReplaceFirstAWithB(m_projectABuildFilePath);
QFile buildFile(m_projectABuildFilePath);
if (buildFile.open(QIODevice::ReadWrite))
{
QTextStream stream(&buildFile);
stream << "x0FFFFFFFF" << Qt::endl;
buildFile.close();
}
} }
~ProjectManagerUtilsTests() ~ProjectManagerUtilsTests()
{ {
QDir dirA("ProjectA"); QDir dirA(m_projectAPath);
dirA.removeRecursively(); dirA.removeRecursively();
QDir dirB("ProjectB"); QDir dirB(m_projectBPath);
dirB.removeRecursively(); dirB.removeRecursively();
m_application.reset(); m_application.reset();
} }
AZStd::unique_ptr<ProjectManager::Application> m_application; AZStd::unique_ptr<ProjectManager::Application> m_application;
QString m_projectAPath;
QString m_projectAOrigFilePath;
QString m_projectAReplaceFilePath;
QString m_projectABuildPath;
QString m_projectABuildFilePath;
QString m_projectBPath;
QString m_projectBOrigFilePath;
QString m_projectBReplaceFilePath;
QString m_projectBBuildPath;
QString m_projectBBuildFilePath;
}; };
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS #if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
TEST_F(ProjectManagerUtilsTests, DISABLED_MoveProject_Succeeds) TEST_F(ProjectManagerUtilsTests, DISABLED_MoveProject_MovesExpectedFiles)
#else
TEST_F(ProjectManagerUtilsTests, MoveProject_MovesExpectedFiles)
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
{
EXPECT_TRUE(MoveProject(
QDir::currentPath() + QDir::separator() + m_projectAPath,
QDir::currentPath() + QDir::separator() + m_projectBPath,
nullptr, true));
QFileInfo origFile(m_projectAOrigFilePath);
EXPECT_FALSE(origFile.exists());
QFileInfo replaceFile(m_projectAReplaceFilePath);
EXPECT_FALSE(replaceFile.exists());
QFileInfo origFileMoved(m_projectBOrigFilePath);
EXPECT_TRUE(origFileMoved.exists() && origFileMoved.isFile());
QFileInfo replaceFileMoved(m_projectBReplaceFilePath);
EXPECT_TRUE(replaceFileMoved.exists() && replaceFileMoved.isFile());
}
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
TEST_F(ProjectManagerUtilsTests, DISABLED_MoveProject_DoesntMoveBuild)
#else #else
TEST_F(ProjectManagerUtilsTests, MoveProject_Succeeds) TEST_F(ProjectManagerUtilsTests, MoveProject_DoesntMoveBuild)
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS #endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
{ {
EXPECT_TRUE(MoveProject( EXPECT_TRUE(MoveProject(
QDir::currentPath() + QDir::separator() + "ProjectA", QDir::currentPath() + QDir::separator() + m_projectAPath,
QDir::currentPath() + QDir::separator() + "ProjectB", QDir::currentPath() + QDir::separator() + m_projectBPath,
nullptr, true)); nullptr, true));
QFileInfo origFile("ProjectA/origFile.txt"); QFileInfo origFile(m_projectAOrigFilePath);
EXPECT_TRUE(!origFile.exists()); EXPECT_FALSE(origFile.exists());
QFileInfo replaceFile("ProjectA/replaceFile.txt"); QFileInfo origFileMoved(m_projectBOrigFilePath);
EXPECT_TRUE(!replaceFile.exists()); EXPECT_TRUE(origFileMoved.exists() && origFileMoved.isFile());
QDir buildDir(m_projectBBuildPath);
EXPECT_FALSE(buildDir.exists());
}
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
TEST_F(ProjectManagerUtilsTests, DISABLED_CopyProject_CopiesExpectedFiles)
#else
TEST_F(ProjectManagerUtilsTests, CopyProject_CopiesExpectedFiles)
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
{
EXPECT_TRUE(CopyProject(
QDir::currentPath() + QDir::separator() + m_projectAPath,
QDir::currentPath() + QDir::separator() + m_projectBPath,
nullptr, true));
QFileInfo origFileMoved("ProjectB/origFile.txt"); QFileInfo origFile(m_projectAOrigFilePath);
EXPECT_TRUE(origFile.exists());
QFileInfo replaceFile(m_projectAReplaceFilePath);
EXPECT_TRUE(replaceFile.exists());
QFileInfo origFileMoved(m_projectBOrigFilePath);
EXPECT_TRUE(origFileMoved.exists() && origFileMoved.isFile()); EXPECT_TRUE(origFileMoved.exists() && origFileMoved.isFile());
QFileInfo replaceFileMoved("ProjectB/replaceFile.txt"); QFileInfo replaceFileMoved(m_projectBReplaceFilePath);
EXPECT_TRUE(replaceFileMoved.exists() && replaceFileMoved.isFile()); EXPECT_TRUE(replaceFileMoved.exists() && replaceFileMoved.isFile());
} }
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
TEST_F(ProjectManagerUtilsTests, DISABLED_CopyProject_DoesntCopyBuild)
#else
TEST_F(ProjectManagerUtilsTests, CopyProject_DoesntCopyBuild)
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
{
EXPECT_TRUE(CopyProject(
QDir::currentPath() + QDir::separator() + m_projectAPath,
QDir::currentPath() + QDir::separator() + m_projectBPath,
nullptr, true));
QFileInfo origFile(m_projectAOrigFilePath);
EXPECT_TRUE(origFile.exists());
QFileInfo origFileMoved(m_projectBOrigFilePath);
EXPECT_TRUE(origFileMoved.exists() && origFileMoved.isFile());
QDir buildDir(m_projectBBuildPath);
EXPECT_FALSE(buildDir.exists());
}
#if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS #if AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
TEST_F(ProjectManagerUtilsTests, DISABLED_ReplaceFile_Succeeds) TEST_F(ProjectManagerUtilsTests, DISABLED_ReplaceFile_Succeeds)
#else #else
TEST_F(ProjectManagerUtilsTests, ReplaceFile_Succeeds) TEST_F(ProjectManagerUtilsTests, ReplaceFile_Succeeds)
#endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS #endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS
{ {
EXPECT_TRUE(ReplaceFile("ProjectA/origFile.txt", "ProjectA/replaceFile.txt", nullptr, false)); EXPECT_TRUE(ReplaceFile(m_projectAOrigFilePath, m_projectAReplaceFilePath, nullptr, false));
QFile origFile("ProjectA/origFile.txt"); QFile origFile(m_projectAOrigFilePath);
if (origFile.open(QIODevice::ReadOnly)) EXPECT_TRUE(origFile.open(QIODevice::ReadOnly));
{ {
QTextStream stream(&origFile); QTextStream stream(&origFile);
QString line = stream.readLine(); QString line = stream.readLine();
@ -106,10 +212,6 @@ namespace O3DE::ProjectManager
origFile.close(); origFile.close();
} }
else
{
FAIL();
}
} }
} // namespace ProjectUtils } // namespace ProjectUtils
} // namespace O3DE::ProjectManager } // namespace O3DE::ProjectManager

Loading…
Cancel
Save