diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp index 56feb9f70a..7867f6e5fd 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace O3DE::ProjectManager { @@ -18,7 +19,10 @@ namespace O3DE::ProjectManager AZ::Outcome GetCommandLineProcessEnvironment() { - return AZ::Success(QProcessEnvironment(QProcessEnvironment::systemEnvironment())); + QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment()); + currentEnvironment.insert("CC", "clang-12"); + currentEnvironment.insert("CXX", "clang++-12"); + return AZ::Success(currentEnvironment); } AZ::Outcome FindSupportedCompilerForPlatform() @@ -27,7 +31,7 @@ namespace O3DE::ProjectManager auto whichCMakeResult = ProjectUtils::ExecuteCommandResult("which", QStringList{ProjectCMakeCommand}, QProcessEnvironment::systemEnvironment()); if (!whichCMakeResult.IsSuccess()) { - return AZ::Failure(QObject::tr("CMake not found. \n\n" + return AZ::Failure(QObject::tr("CMake not found.

" "Make sure that the minimum version of CMake is installed and available from the command prompt. " "Refer to the O3DE requirements page for more information.")); } @@ -45,10 +49,42 @@ namespace O3DE::ProjectManager return AZ::Success(supportClangCommand); } } - return AZ::Failure(QObject::tr("Clang not found. \n\n" + return AZ::Failure(QObject::tr("Clang not found.

" "Make sure that the clang is installed and available from the command prompt. " "Refer to the O3DE requirements page for more information.")); } + + + AZ::Outcome OpenCMakeGUI(const QString& projectPath) + { + AZ::Outcome processEnvResult = GetCommandLineProcessEnvironment(); + if (!processEnvResult.IsSuccess()) + { + return AZ::Failure(processEnvResult.GetError()); + } + + QString projectBuildPath = QDir(projectPath).filePath(ProjectBuildPathPostfix); + AZ::Outcome projectBuildPathResult = GetProjectBuildPath(projectPath); + if (projectBuildPathResult.IsSuccess()) + { + projectBuildPath = projectBuildPathResult.GetValue(); + } + + QProcess process; + process.setProcessEnvironment(processEnvResult.GetValue()); + + // if the project build path is relative, it should be relative to the project path + process.setWorkingDirectory(projectPath); + + process.setProgram("cmake-gui"); + process.setArguments({ "-S", projectPath, "-B", projectBuildPath }); + if(!process.startDetached()) + { + return AZ::Failure(QObject::tr("Failed to start CMake GUI")); + } + + return AZ::Success(); + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp index 0d150abc3b..b50039790d 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp @@ -8,6 +8,7 @@ #include #include +#include namespace O3DE::ProjectManager { @@ -61,5 +62,37 @@ namespace O3DE::ProjectManager return AZ::Success(xcodeBuilderVersionNumber); } + + AZ::Outcome OpenCMakeGUI(const QString& projectPath) + { + const QString cmakeHelp = QObject::tr("Please verify you've installed CMake.app from " + "cmake.org or, if using HomeBrew, " + "have installed it with
brew install --cask cmake
"); + QString cmakeAppPath = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, "CMake.app", QStandardPaths::LocateDirectory); + if (cmakeAppPath.isEmpty()) + { + return AZ::Failure(QObject::tr("CMake.app not found.") + cmakeHelp); + } + + QString projectBuildPath = QDir(projectPath).filePath(ProjectBuildPathPostfix); + AZ::Outcome result = GetProjectBuildPath(projectPath); + if (result.IsSuccess()) + { + projectBuildPath = result.GetValue(); + } + + QProcess process; + + // if the project build path is relative, it should be relative to the project path + process.setWorkingDirectory(projectPath); + process.setProgram("open"); + process.setArguments({"-a", "CMake", "--args", "-S", projectPath, "-B", projectBuildPath}); + if(!process.startDetached()) + { + return AZ::Failure(QObject::tr("CMake.app failed to open.") + cmakeHelp); + } + + return AZ::Success(); + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp index d012ca8921..16b80c55e8 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp @@ -92,12 +92,43 @@ namespace O3DE::ProjectManager } } - return AZ::Failure(QObject::tr("Visual Studio 2019 version 16.9.2 or higher not found.\n\n" + return AZ::Failure(QObject::tr("Visual Studio 2019 version 16.9.2 or higher not found.

" "Visual Studio 2019 is required to build this project." " Install any edition of Visual Studio 2019" " or update to a newer version before proceeding to the next step." " While installing configure Visual Studio with these workloads.")); } + AZ::Outcome OpenCMakeGUI(const QString& projectPath) + { + AZ::Outcome processEnvResult = GetCommandLineProcessEnvironment(); + if (!processEnvResult.IsSuccess()) + { + return AZ::Failure(processEnvResult.GetError()); + } + + QString projectBuildPath = QDir(projectPath).filePath(ProjectBuildPathPostfix); + AZ::Outcome projectBuildPathResult = GetProjectBuildPath(projectPath); + if (projectBuildPathResult.IsSuccess()) + { + projectBuildPath = projectBuildPathResult.GetValue(); + } + + QProcess process; + process.setProcessEnvironment(processEnvResult.GetValue()); + + // if the project build path is relative, it should be relative to the project path + process.setWorkingDirectory(projectPath); + + process.setProgram("cmake-gui"); + process.setArguments({ "-S", projectPath, "-B", projectBuildPath }); + if(!process.startDetached()) + { + return AZ::Failure(QObject::tr("Failed to start CMake GUI")); + } + + return AZ::Success(); + } + } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index eeed316cbc..f91a597481 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -438,6 +438,11 @@ QTabBar::tab:focus { max-height:26px; } +#projectActionButton, #openEditorButton { + min-height:26px; + max-height:26px; +} + #labelButtonOverlay { background-color: rgba(50,50,50,200); min-width:210px; diff --git a/Code/Tools/ProjectManager/Resources/Warning.svg b/Code/Tools/ProjectManager/Resources/Warning.svg index 28f7bc5f42..a330c9163c 100644 --- a/Code/Tools/ProjectManager/Resources/Warning.svg +++ b/Code/Tools/ProjectManager/Resources/Warning.svg @@ -1,5 +1,4 @@ - diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp index 0f51524341..c425c344e1 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp @@ -88,6 +88,7 @@ namespace O3DE::ProjectManager QHBoxLayout* horizontalButtonLayout = new QHBoxLayout(); horizontalButtonLayout->addSpacing(34); m_actionButton = new QPushButton(tr("Project Action"), this); + m_actionButton->setObjectName("projectActionButton"); m_actionButton->setVisible(false); horizontalButtonLayout->addWidget(m_actionButton); horizontalButtonLayout->addSpacing(34); @@ -198,6 +199,7 @@ namespace O3DE::ProjectManager QMenu* menu = new QMenu(this); menu->addAction(tr("Edit Project Settings..."), this, [this]() { emit EditProject(m_projectInfo.m_path); }); menu->addAction(tr("Build"), this, [this]() { emit BuildProject(m_projectInfo); }); + menu->addAction(tr("Open CMake GUI..."), this, [this]() { emit OpenCMakeGUI(m_projectInfo); }); menu->addSeparator(); menu->addAction(tr("Open Project folder..."), this, [this]() { @@ -259,15 +261,39 @@ namespace O3DE::ProjectManager } projectActionButton->setText(text); + projectActionButton->setMenu(nullptr); m_actionButtonConnection = connect(projectActionButton, &QPushButton::clicked, lambda); } - void ProjectButton::SetProjectBuildButtonAction() + void ProjectButton::ShowDefaultBuildButton() { - m_projectImageLabel->GetWarningLabel()->setText(tr("Building project required.")); - m_projectImageLabel->GetWarningIcon()->setVisible(true); - m_projectImageLabel->GetWarningLabel()->setVisible(true); - SetProjectButtonAction(tr("Build Project"), [this]() { emit BuildProject(m_projectInfo); }); + QPushButton* projectActionButton = m_projectImageLabel->GetActionButton(); + projectActionButton->setVisible(true); + projectActionButton->setText(tr("Build Project")); + disconnect(m_actionButtonConnection); + + QMenu* menu = new QMenu(this); + QAction* autoBuildAction = menu->addAction(tr("Build Now")); + connect( autoBuildAction, &QAction::triggered, this, [this](){ emit BuildProject(m_projectInfo); }); + + QAction* openCMakeAction = menu->addAction(tr("Open CMake GUI...")); + connect( openCMakeAction, &QAction::triggered, this, [this](){ emit OpenCMakeGUI(m_projectInfo); }); + + projectActionButton->setMenu(menu); + } + + void ProjectButton::ShowBuildRequired() + { + ShowWarning(true, tr("Building project required")); + ShowDefaultBuildButton(); + } + + void ProjectButton::ShowWarning(bool show, const QString& warning) + { + m_projectImageLabel->GetWarningLabel()->setTextInteractionFlags(Qt::LinksAccessibleByMouse); + m_projectImageLabel->GetWarningLabel()->setText(warning); + m_projectImageLabel->GetWarningLabel()->setVisible(show); + m_projectImageLabel->GetWarningIcon()->setVisible(show); } void ProjectButton::SetBuildLogsLink(const QUrl& logUrl) @@ -279,18 +305,15 @@ namespace O3DE::ProjectManager { if (!logUrl.isEmpty()) { - m_projectImageLabel->GetWarningLabel()->setText(tr("Failed to build. Click to view logs.")); + ShowWarning(show, tr("Failed to build. Click to view logs.")); } else { - m_projectImageLabel->GetWarningLabel()->setText(tr("Project failed to build.")); + ShowWarning(show, tr("Project failed to build.")); } - m_projectImageLabel->GetWarningLabel()->setTextInteractionFlags(Qt::LinksAccessibleByMouse); - m_projectImageLabel->GetWarningIcon()->setVisible(show); - m_projectImageLabel->GetWarningLabel()->setVisible(show); - m_projectImageLabel->SetLogUrl(logUrl); - SetProjectButtonAction(tr("Build Project"), [this]() { emit BuildProject(m_projectInfo); }); + SetBuildLogsLink(logUrl); + ShowDefaultBuildButton(); } void ProjectButton::SetProjectBuilding() diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h index 9f64b74eab..5e81dfc2d9 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h @@ -82,9 +82,9 @@ namespace O3DE::ProjectManager void RestoreDefaultState(); void SetProjectButtonAction(const QString& text, AZStd::function lambda); - void SetProjectBuildButtonAction(); void SetBuildLogsLink(const QUrl& logUrl); void ShowBuildFailed(bool show, const QUrl& logUrl); + void ShowBuildRequired(); void SetProjectBuilding(); void SetLaunchButtonEnabled(bool enabled); @@ -99,10 +99,13 @@ namespace O3DE::ProjectManager void RemoveProject(const QString& projectName); void DeleteProject(const QString& projectName); void BuildProject(const ProjectInfo& projectInfo); + void OpenCMakeGUI(const ProjectInfo& projectInfo); private: void enterEvent(QEvent* event) override; void leaveEvent(QEvent* event) override; + void ShowWarning(bool show, const QString& warning); + void ShowDefaultBuildButton(); ProjectInfo m_projectInfo; diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp index 81eb70391d..e248613d1f 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include @@ -537,5 +539,33 @@ namespace O3DE::ProjectManager QString resultOutput = execProcess.readAllStandardOutput(); return AZ::Success(resultOutput); } + + AZ::Outcome GetProjectBuildPath(const QString& projectPath) + { + auto registry = AZ::SettingsRegistry::Get(); + + // the project_build_path should be in the user settings registry inside the project folder + AZ::IO::FixedMaxPath projectUserPath(projectPath.toUtf8().constData()); + projectUserPath /= AZ::SettingsRegistryInterface::DevUserRegistryFolder; + if (!QDir(projectUserPath.c_str()).exists()) + { + return AZ::Failure(QObject::tr("Failed to find the user registry folder %1").arg(projectUserPath.c_str())); + } + + AZ::SettingsRegistryInterface::Specializations specializations; + if(!registry->MergeSettingsFolder(projectUserPath.Native(), specializations, AZ_TRAIT_OS_PLATFORM_CODENAME)) + { + return AZ::Failure(QObject::tr("Failed to merge registry settings in user registry folder %1").arg(projectUserPath.c_str())); + } + + AZ::IO::FixedMaxPath projectBuildPath; + if (!registry->Get(projectBuildPath.Native(), AZ::SettingsRegistryMergeUtils::ProjectBuildPath)) + { + return AZ::Failure(QObject::tr("No project build path setting was found in the user registry folder %1").arg(projectUserPath.c_str())); + } + + return AZ::Success(QString(projectBuildPath.c_str())); + } + } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.h b/Code/Tools/ProjectManager/Source/ProjectUtils.h index 0866b57923..6dd46e3857 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.h +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.h @@ -42,6 +42,8 @@ namespace O3DE::ProjectManager int commandTimeoutSeconds = ProjectCommandLineTimeoutSeconds); AZ::Outcome GetCommandLineProcessEnvironment(); + AZ::Outcome GetProjectBuildPath(const QString& projectPath); + AZ::Outcome OpenCMakeGUI(const QString& projectPath); } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp index b313c05230..cf42da88fa 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp @@ -185,6 +185,15 @@ namespace O3DE::ProjectManager connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsScreen::HandleRemoveProject); connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsScreen::HandleDeleteProject); connect(projectButton, &ProjectButton::BuildProject, this, &ProjectsScreen::QueueBuildProject); + connect(projectButton, &ProjectButton::OpenCMakeGUI, this, + [this](const ProjectInfo& projectInfo) + { + AZ::Outcome result = ProjectUtils::OpenCMakeGUI(projectInfo.m_path); + if (!result) + { + QMessageBox::critical(this, tr("Failed to open CMake GUI"), result.GetError(), QMessageBox::Ok); + } + }); return projectButton; } @@ -308,7 +317,7 @@ namespace O3DE::ProjectManager } else { - projectIter.value()->SetProjectBuildButtonAction(); + projectIter.value()->ShowBuildRequired(); } } }