diff --git a/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake index 4125861f2b..22f8e785f2 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake +++ b/Code/Tools/ProjectManager/Platform/Linux/PAL_linux_files.cmake @@ -9,4 +9,5 @@ set(FILES Python_linux.cpp ProjectBuilderWorker_linux.cpp ProjectUtils_linux.cpp + ProjectManagerDefs_linux.cpp ) diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectManagerDefs_linux.cpp b/Code/Tools/ProjectManager/Platform/Linux/ProjectManagerDefs_linux.cpp new file mode 100644 index 0000000000..5ccfbf8eff --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Linux/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 + + +namespace O3DE::ProjectManager +{ + const QString ProjectBuildPathPostfix = ProjectBuildDirectoryName + "/linux"; + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake index a0d3add840..550698af0b 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake +++ b/Code/Tools/ProjectManager/Platform/Mac/PAL_mac_files.cmake @@ -9,4 +9,5 @@ set(FILES Python_mac.cpp ProjectBuilderWorker_mac.cpp ProjectUtils_mac.cpp + ProjectManagerDefs_mac.cpp ) diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectManagerDefs_mac.cpp b/Code/Tools/ProjectManager/Platform/Mac/ProjectManagerDefs_mac.cpp new file mode 100644 index 0000000000..01a7f9e375 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Mac/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 + + +namespace O3DE::ProjectManager +{ + const QString ProjectBuildPathPostfix = ProjectBuildDirectoryName + "/mac_xcode"; + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake index 8af7638696..e0a3fc6bcd 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake +++ b/Code/Tools/ProjectManager/Platform/Windows/PAL_windows_files.cmake @@ -9,4 +9,5 @@ set(FILES Python_windows.cpp ProjectBuilderWorker_windows.cpp ProjectUtils_windows.cpp + ProjectManagerDefs_windows.cpp ) diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp b/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp index 87a8a6c0ec..eca7b0b9a8 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectBuilderWorker_windows.cpp @@ -75,8 +75,17 @@ namespace O3DE::ProjectManager m_configProjectProcess->start( "cmake", - QStringList{ "-B", QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix), "-S", m_projectInfo.m_path, "-G", - "Visual Studio 16", "-DLY_3RDPARTY_PATH=" + engineInfo.m_thirdPartyPath }); + QStringList + { + "-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()) { @@ -124,8 +133,16 @@ namespace O3DE::ProjectManager m_buildProjectProcess->start( "cmake", - QStringList{ "--build", QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix), "--target", - m_projectInfo.m_projectName + ".GameLauncher", "Editor", "--config", "profile" }); + QStringList + { + "--build", + QDir(m_projectInfo.m_path).filePath(ProjectBuildPathPostfix), + "--target", + m_projectInfo.m_projectName + ".GameLauncher", + "Editor", + "--config", + "profile" + }); if (!m_buildProjectProcess->waitForStarted()) { diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectManagerDefs_windows.cpp b/Code/Tools/ProjectManager/Platform/Windows/ProjectManagerDefs_windows.cpp new file mode 100644 index 0000000000..6bd2194967 --- /dev/null +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectManagerDefs_windows.cpp @@ -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 + +namespace O3DE::ProjectManager +{ + const QString ProjectBuildPathPostfix = ProjectBuildDirectoryName + "/windows_vs2019"; + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp index 63899035e0..5aed95e531 100644 --- a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.cpp @@ -14,8 +14,6 @@ namespace O3DE::ProjectManager { - const QString ProjectBuilderWorker::BuildCancelled = QObject::tr("Build Cancelled."); - ProjectBuilderWorker::ProjectBuilderWorker(const ProjectInfo& projectInfo) : QObject() , m_projectInfo(projectInfo) diff --git a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h index 0557b21831..3219632098 100644 --- a/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h +++ b/Code/Tools/ProjectManager/Source/ProjectBuilderWorker.h @@ -22,7 +22,7 @@ namespace O3DE::ProjectManager // QProcess::waitForFinished uses -1 to indicate that the process should not timeout static constexpr int MaxBuildTimeMSecs = -1; // Build was cancelled - static const QString BuildCancelled; + inline static const QString BuildCancelled = QObject::tr("Build Cancelled."); Q_OBJECT diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp index af9a1a7bd9..8ac5f096fb 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp @@ -233,7 +233,7 @@ namespace O3DE::ProjectManager AzQtComponents::ShowFileOnDesktop(m_projectInfo.m_path); }); 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->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); }); diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h index 27559b325e..fe1ce9ad05 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h @@ -88,7 +88,7 @@ namespace O3DE::ProjectManager signals: void OpenProject(const QString& projectName); void EditProject(const QString& projectName); - void CopyProject(const QString& projectName); + void CopyProject(const ProjectInfo& projectInfo); void RemoveProject(const QString& projectName); void DeleteProject(const QString& projectName); void BuildProject(const ProjectInfo& projectInfo); diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h b/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h index 79ad04c43a..7b49b89be1 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h +++ b/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h @@ -14,8 +14,10 @@ namespace O3DE::ProjectManager inline constexpr static int ProjectPreviewImageHeight = 280; 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 ProjectBuildErrorLogName = "CMakeProjectBuildError.log"; + static const QString ProjectCacheDirectoryName = "Cache"; static const QString ProjectPreviewImagePath = "preview.png"; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp index 827973cbec..6b3750b6bb 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -59,29 +60,63 @@ namespace O3DE::ProjectManager 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 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); for (const QString& entryPath : entries) { const QString filePath = QDir::toNativeSeparators(QString("%1/%2").arg(directory.path()).arg(entryPath)); + + QStringList deeperSkippedPaths; + if (SkipFilePaths(entryPath, skippedPaths, deeperSkippedPaths)) + { + continue; + } + QFileInfo fileInfo(filePath); if (fileInfo.isDir()) { QDir subDirectory(filePath); - RecursiveGetAllFiles(subDirectory, outFileList, outTotalSizeInBytes, statusCallback); + RecursiveGetAllFiles(subDirectory, deeperSkippedPaths, outFileCount, outTotalSizeInBytes, statusCallback); } else { - outFileList.push_back(filePath); + ++outFileCount; outTotalSizeInBytes += fileInfo.size(); 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, const QString& origPath, const QString& newPath, - QStringList& filesToCopy, + QStringList& skippedPaths, + int filesToCopyCount, int& outNumCopiedFiles, qint64 totalSizeToCopy, qint64& outCopiedFileSize, @@ -102,18 +138,24 @@ namespace O3DE::ProjectManager return false; } - for (QString directory : original.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) + for (const QString& directory : original.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { if (progressDialog->wasCanceled()) { return false; } + QStringList deeperSkippedPaths; + if (SkipFilePaths(directory, skippedPaths, deeperSkippedPaths)) + { + continue; + } + QString newDirectoryPath = newPath + QDir::separator() + directory; original.mkpath(newDirectoryPath); - if (!CopyDirectory(progressDialog, origPath + QDir::separator() + directory, - newDirectoryPath, filesToCopy, outNumCopiedFiles, totalSizeToCopy, outCopiedFileSize, showIgnoreFileDialog)) + if (!CopyDirectory(progressDialog, origPath + QDir::separator() + directory, newDirectoryPath, deeperSkippedPaths, + filesToCopyCount, outNumCopiedFiles, totalSizeToCopy, outCopiedFileSize, showIgnoreFileDialog)) { return false; } @@ -121,18 +163,25 @@ namespace O3DE::ProjectManager QLocale locale; 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()) { return false; } + // Unused by this function but neccesary to pass in to SkipFilePaths + QStringList deeperSkippedPaths; + if (SkipFilePaths(file, skippedPaths, deeperSkippedPaths)) + { + continue; + } + // Progress window update { // 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. - const float normalizedNumFiles = static_cast(outNumCopiedFiles) / filesToCopy.count(); + const float normalizedNumFiles = static_cast(outNumCopiedFiles) / filesToCopyCount; const float normalizedFileSize = static_cast(outCopiedFileSize) / totalSizeToCopy; const int progress = normalizedNumFiles * progressDialogRangeHalf + normalizedFileSize * progressDialogRangeHalf; progressDialog->setValue(progress); @@ -140,7 +189,7 @@ namespace O3DE::ProjectManager const QString copiedFileSizeString = locale.formattedDataSize(outCopiedFileSize); const QString totalFileSizeString = locale.formattedDataSize(totalSizeToCopy); progressDialog->setLabelText(QString("Coping file %1 of %2 (%3 of %4) ...").arg(QString::number(outNumCopiedFiles), - QString::number(filesToCopy.count()), + QString::number(filesToCopyCount), copiedFileSizeString, totalFileSizeString)); qApp->processEvents(QEventLoop::ExcludeUserInputEvents); @@ -194,6 +243,39 @@ namespace O3DE::ProjectManager 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) { QString path = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(parent, QObject::tr("Select Project Directory"))); @@ -215,7 +297,7 @@ namespace O3DE::ProjectManager return PythonBindingsInterface::Get()->RemoveProject(path); } - bool CopyProjectDialog(const QString& origPath, QWidget* parent) + bool CopyProjectDialog(const QString& origPath, ProjectInfo& newProjectInfo, QWidget* parent) { bool copyResult = false; @@ -225,6 +307,8 @@ namespace O3DE::ProjectManager QFileDialog::getExistingDirectory(parent, QObject::tr("Select New Project Directory"), parentOrigDir.path())); if (!newPath.isEmpty()) { + newProjectInfo.m_path = newPath; + if (!WarnDirectoryOverwrite(newPath, parent)) { return false; @@ -236,7 +320,7 @@ namespace O3DE::ProjectManager 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 if (IsDirectoryDescedent(origPath, newPath) || IsDirectoryDescedent(newPath, origPath)) @@ -244,8 +328,13 @@ namespace O3DE::ProjectManager return false; } - QStringList filesToCopy; + int filesToCopyCount = 0; qint64 totalSizeInBytes = 0; + QStringList skippedPaths + { + ProjectBuildDirectoryName, + ProjectCacheDirectoryName + }; QProgressDialog* progressDialog = new QProgressDialog(parent); progressDialog->setAutoClose(true); @@ -256,7 +345,8 @@ namespace O3DE::ProjectManager progressDialog->show(); 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. const QString fileSizeString = locale.formattedDataSize(sizeInBytes); @@ -275,8 +365,10 @@ namespace O3DE::ProjectManager // Phase 1: Copy files bool showIgnoreFileDialog = true; - bool success = CopyDirectory(progressDialog, origPath, newPath, filesToCopy, numFilesCopied, totalSizeInBytes, copiedFileSize, showIgnoreFileDialog); - if (success) + QStringList copyFilesSkippedPaths(skippedPaths); + bool success = CopyDirectory(progressDialog, origPath, newPath, copyFilesSkippedPaths, filesToCopyCount, numFilesCopied, + totalSizeInBytes, copiedFileSize, showIgnoreFileDialog); + if (success && !skipRegister) { // Phase 2: Register project success = RegisterProject(newPath); @@ -299,7 +391,7 @@ namespace O3DE::ProjectManager QDir projectDirectory(path); 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()) { return projectDirectory.removeRecursively(); @@ -309,12 +401,12 @@ namespace O3DE::ProjectManager 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); newPath = QDir::toNativeSeparators(newPath); - if (!WarnDirectoryOverwrite(newPath, parent) || (!ignoreRegister && !UnregisterProject(origPath))) + if (!WarnDirectoryOverwrite(newPath, parent) || (!skipRegister && !UnregisterProject(origPath))) { return false; } @@ -334,8 +426,13 @@ namespace O3DE::ProjectManager 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; } diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.h b/Code/Tools/ProjectManager/Source/ProjectUtils.h index 1035ceea65..2c4e873b65 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.h +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.h @@ -7,6 +7,8 @@ #pragma once #include +#include + #include #include @@ -17,10 +19,10 @@ namespace O3DE::ProjectManager bool AddProjectDialog(QWidget* parent = nullptr); bool RegisterProject(const QString& path); bool UnregisterProject(const QString& path); - bool CopyProjectDialog(const QString& origPath, QWidget* parent = nullptr); - bool CopyProject(const QString& origPath, const QString& newPath, QWidget* parent); + bool CopyProjectDialog(const QString& origPath, ProjectInfo& newProjectInfo, QWidget* parent = nullptr); + bool CopyProject(const QString& origPath, const QString& newPath, QWidget* parent, bool skipRegister = 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); diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp index 54fdc370d7..974858f2b2 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp @@ -384,14 +384,17 @@ namespace O3DE::ProjectManager 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 - if (ProjectUtils::CopyProjectDialog(projectPath, this)) + if (ProjectUtils::CopyProjectDialog(projectInfo.m_path, newProjectInfo, this)) { ResetProjectsContent(); + emit NotifyBuildProject(newProjectInfo); emit ChangeScreenRequest(ProjectManagerScreen::Projects); } } diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.h b/Code/Tools/ProjectManager/Source/ProjectsScreen.h index 787db16c59..a6e5c20c52 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.h +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.h @@ -44,7 +44,7 @@ namespace O3DE::ProjectManager void HandleAddProjectButton(); void HandleOpenProject(const QString& projectPath); void HandleEditProject(const QString& projectPath); - void HandleCopyProject(const QString& projectPath); + void HandleCopyProject(const ProjectInfo& projectInfo); void HandleRemoveProject(const QString& projectPath); void HandleDeleteProject(const QString& projectPath); diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index 5bd6cc4552..89555d2e72 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -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 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.")); return false; } + + emit NotifyBuildProject(newProjectSettings); } // Update project if settings changed diff --git a/Code/Tools/ProjectManager/tests/UtilsTests.cpp b/Code/Tools/ProjectManager/tests/UtilsTests.cpp index 51f7162278..a02a9cb796 100644 --- a/Code/Tools/ProjectManager/tests/UtilsTests.cpp +++ b/Code/Tools/ProjectManager/tests/UtilsTests.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -25,16 +26,31 @@ namespace O3DE::ProjectManager : public ::UnitTest::ScopedAllocatorSetupFixture { public: + static inline QString ReplaceFirstAWithB(const QString& originalString) + { + QString bString(originalString); + return bString.replace(bString.indexOf('A'), 1, 'B'); + } + ProjectManagerUtilsTests() { m_application = AZStd::make_unique(); m_application->Init(false); - QDir dir; - dir.mkdir("ProjectA"); - dir.mkdir("ProjectB"); + m_projectAPath = "ProjectA"; - QFile origFile("ProjectA/origFile.txt"); + // 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; + dir.mkpath(m_projectABuildPath); + dir.mkdir(m_projectBPath); + + 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)) { QTextStream stream(&origFile); @@ -42,63 +58,153 @@ namespace O3DE::ProjectManager 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)) { QTextStream stream(&replaceFile); stream << "replace" << Qt::endl; 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() { - QDir dirA("ProjectA"); + QDir dirA(m_projectAPath); dirA.removeRecursively(); - QDir dirB("ProjectB"); + QDir dirB(m_projectBPath); dirB.removeRecursively(); m_application.reset(); } AZStd::unique_ptr 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 - TEST_F(ProjectManagerUtilsTests, DISABLED_MoveProject_Succeeds) + TEST_F(ProjectManagerUtilsTests, DISABLED_MoveProject_MovesExpectedFiles) #else - TEST_F(ProjectManagerUtilsTests, MoveProject_Succeeds) + TEST_F(ProjectManagerUtilsTests, MoveProject_MovesExpectedFiles) #endif // !AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS { EXPECT_TRUE(MoveProject( - QDir::currentPath() + QDir::separator() + "ProjectA", - QDir::currentPath() + QDir::separator() + "ProjectB", + QDir::currentPath() + QDir::separator() + m_projectAPath, + QDir::currentPath() + QDir::separator() + m_projectBPath, nullptr, true)); - QFileInfo origFile("ProjectA/origFile.txt"); - EXPECT_TRUE(!origFile.exists()); + QFileInfo origFile(m_projectAOrigFilePath); + EXPECT_FALSE(origFile.exists()); - QFileInfo replaceFile("ProjectA/replaceFile.txt"); - EXPECT_TRUE(!replaceFile.exists()); + QFileInfo replaceFile(m_projectAReplaceFilePath); + EXPECT_FALSE(replaceFile.exists()); - QFileInfo origFileMoved("ProjectB/origFile.txt"); + QFileInfo origFileMoved(m_projectBOrigFilePath); EXPECT_TRUE(origFileMoved.exists() && origFileMoved.isFile()); - QFileInfo replaceFileMoved("ProjectB/replaceFile.txt"); + 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 + TEST_F(ProjectManagerUtilsTests, MoveProject_DoesntMoveBuild) +#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 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 + 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 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()); + + QFileInfo replaceFileMoved(m_projectBReplaceFilePath); + 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 TEST_F(ProjectManagerUtilsTests, DISABLED_ReplaceFile_Succeeds) #else TEST_F(ProjectManagerUtilsTests, ReplaceFile_Succeeds) #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"); - if (origFile.open(QIODevice::ReadOnly)) + QFile origFile(m_projectAOrigFilePath); + EXPECT_TRUE(origFile.open(QIODevice::ReadOnly)); { QTextStream stream(&origFile); QString line = stream.readLine(); @@ -106,10 +212,6 @@ namespace O3DE::ProjectManager origFile.close(); } - else - { - FAIL(); - } } } // namespace ProjectUtils } // namespace O3DE::ProjectManager