Fix project creation (#1445)

* Add ability to change project name

* Fixed several issues where path types were changed

* Added PythonBindings CreateProject unit test

* Fix python warning format

* Validate new project name in CLI

* Fix issue creating pathview on linux

* Use better testing macros

* Refactored the unit_test_engine_template.py test to actually test
against the current engine_template.py commands

The commands of create-template, create-from-template, create-project
and create-gem is now being validated.

Registered the unit_test_engine_template.py script with CTest in the smoke test
suite so that it runs in Automated Review

Fixed issues in the engine_template.py script where the template_restricted_path parameter was required in the create_project and create_gem functions

Co-authored-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
This commit is contained in:
Alex Peterson
2021-06-23 09:19:17 -07:00
committed by GitHub
parent 64533431d8
commit aa885e5d0b
10 changed files with 492 additions and 700 deletions
@@ -83,6 +83,8 @@ namespace O3DE::ProjectManager
{
ProjectInfo projectInfo;
projectInfo.m_projectName = m_projectName->lineEdit()->text();
// currently we don't have separate fields for changing the project name and display name
projectInfo.m_displayName = projectInfo.m_projectName;
projectInfo.m_path = m_projectPath->lineEdit()->text();
return projectInfo;
}
@@ -52,8 +52,10 @@ namespace Platform
} // namespace Platform
#define Py_To_String(obj) obj.cast<std::string>().c_str()
#define Py_To_String(obj) pybind11::str(obj).cast<std::string>().c_str()
#define Py_To_String_Optional(dict, key, default_string) dict.contains(key) ? Py_To_String(dict[key]) : default_string
#define QString_To_Py_String(value) pybind11::str(value.toStdString())
#define QString_To_Py_Path(value) m_pathlib.attr("Path")(value.toStdString())
namespace RedirectOutput
{
@@ -298,6 +300,7 @@ namespace O3DE::ProjectManager
m_enableGemProject = pybind11::module::import("o3de.enable_gem");
m_disableGemProject = pybind11::module::import("o3de.disable_gem");
m_editProjectProperties = pybind11::module::import("o3de.project_properties");
m_pathlib = pybind11::module::import("pathlib");
// make sure the engine is registered
RegisterThisEngine();
@@ -346,7 +349,7 @@ namespace O3DE::ProjectManager
}
}
auto result = m_register.attr("register")(m_enginePath.c_str());
auto result = m_register.attr("register")(QString_To_Py_Path(QString(m_enginePath.c_str())));
registrationResult = (result.cast<int>() == 0);
});
@@ -388,7 +391,7 @@ namespace O3DE::ProjectManager
{
EngineInfo engineInfo;
bool result = ExecuteWithLock([&] {
pybind11::str enginePath = m_manifest.attr("get_this_engine_path")();
auto enginePath = m_manifest.attr("get_this_engine_path")();
auto o3deData = m_manifest.attr("load_o3de_manifest")();
if (pybind11::isinstance<pybind11::dict>(o3deData))
@@ -399,7 +402,7 @@ namespace O3DE::ProjectManager
engineInfo.m_defaultRestrictedFolder = Py_To_String(o3deData["default_restricted_folder"]);
engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]);
pybind11::str defaultThirdPartyFolder = m_manifest.attr("get_o3de_third_party_folder")();
auto defaultThirdPartyFolder = m_manifest.attr("get_o3de_third_party_folder")();
engineInfo.m_thirdPartyPath = Py_To_String_Optional(o3deData,"default_third_party_folder", Py_To_String(defaultThirdPartyFolder));
}
@@ -433,14 +436,8 @@ namespace O3DE::ProjectManager
bool PythonBindings::SetEngineInfo(const EngineInfo& engineInfo)
{
bool result = ExecuteWithLock([&] {
pybind11::str enginePath = engineInfo.m_path.toStdString();
pybind11::str defaultProjectsFolder = engineInfo.m_defaultProjectsFolder.toStdString();
pybind11::str defaultGemsFolder = engineInfo.m_defaultGemsFolder.toStdString();
pybind11::str defaultTemplatesFolder = engineInfo.m_defaultTemplatesFolder.toStdString();
pybind11::str defaultThirdPartyFolder = engineInfo.m_thirdPartyPath.toStdString();
auto registrationResult = m_register.attr("register")(
enginePath, // engine_path
QString_To_Py_Path(engineInfo.m_path),
pybind11::none(), // project_path
pybind11::none(), // gem_path
pybind11::none(), // external_subdir_path
@@ -448,11 +445,11 @@ namespace O3DE::ProjectManager
pybind11::none(), // restricted_path
pybind11::none(), // repo_uri
pybind11::none(), // default_engines_folder
defaultProjectsFolder,
defaultGemsFolder,
defaultTemplatesFolder,
QString_To_Py_Path(engineInfo.m_defaultProjectsFolder),
QString_To_Py_Path(engineInfo.m_defaultGemsFolder),
QString_To_Py_Path(engineInfo.m_defaultTemplatesFolder),
pybind11::none(), // default_restricted_folder
defaultThirdPartyFolder
QString_To_Py_Path(engineInfo.m_thirdPartyPath)
);
if (registrationResult.cast<int>() != 0)
@@ -466,7 +463,7 @@ namespace O3DE::ProjectManager
AZ::Outcome<GemInfo> PythonBindings::GetGemInfo(const QString& path, const QString& projectPath)
{
GemInfo gemInfo = GemInfoFromPath(pybind11::str(path.toStdString()), pybind11::str(projectPath.toStdString()));
GemInfo gemInfo = GemInfoFromPath(QString_To_Py_String(path), QString_To_Py_Path(projectPath));
if (gemInfo.IsValid())
{
return AZ::Success(AZStd::move(gemInfo));
@@ -503,7 +500,7 @@ namespace O3DE::ProjectManager
auto result = ExecuteWithLockErrorHandling([&]
{
pybind11::str pyProjectPath = projectPath.toStdString();
auto pyProjectPath = QString_To_Py_Path(projectPath);
for (auto path : m_manifest.attr("get_all_gems")(pyProjectPath))
{
gems.push_back(GemInfoFromPath(path, pyProjectPath));
@@ -524,10 +521,9 @@ namespace O3DE::ProjectManager
pybind11::str enabledGemsFilename;
auto result = ExecuteWithLockErrorHandling([&]
{
const pybind11::str pyProjectPath = projectPath.toStdString();
enabledGemsFilename = m_cmake.attr("get_enabled_gem_cmake_file")(
pybind11::none(), // project_name
pyProjectPath); // project_path
QString_To_Py_Path(projectPath)); // project_path
});
if (!result.IsSuccess())
{
@@ -558,7 +554,7 @@ namespace O3DE::ProjectManager
bool result = ExecuteWithLock(
[&]
{
pybind11::str projectPath = path.toStdString();
auto projectPath = QString_To_Py_Path(path);
auto pythonRegistrationResult = m_register.attr("register")(pybind11::none(), projectPath);
// Returns an exit code so boolify it then invert result
@@ -574,10 +570,9 @@ namespace O3DE::ProjectManager
bool result = ExecuteWithLock(
[&]
{
pybind11::str projectPath = path.toStdString();
auto pythonRegistrationResult = m_register.attr("register")(
pybind11::none(), // engine_path
projectPath, // project_path
QString_To_Py_Path(path), // project_path
pybind11::none(), // gem_path
pybind11::none(), // external_subdir_path
pybind11::none(), // template_path
@@ -606,14 +601,12 @@ namespace O3DE::ProjectManager
{
ProjectInfo createdProjectInfo;
bool result = ExecuteWithLock([&] {
pybind11::str projectPath = projectInfo.m_path.toStdString();
pybind11::str projectName = projectInfo.m_projectName.toStdString();
pybind11::str templatePath = projectTemplatePath.toStdString();
auto projectPath = QString_To_Py_Path(projectInfo.m_path);
auto createProjectResult = m_engineTemplate.attr("create_project")(
projectPath,
projectName,
templatePath
QString_To_Py_String(projectInfo.m_projectName),
QString_To_Py_Path(projectTemplatePath)
);
if (createProjectResult.cast<int>() == 0)
{
@@ -633,7 +626,7 @@ namespace O3DE::ProjectManager
AZ::Outcome<ProjectInfo> PythonBindings::GetProject(const QString& path)
{
ProjectInfo projectInfo = ProjectInfoFromPath(pybind11::str(path.toStdString()));
ProjectInfo projectInfo = ProjectInfoFromPath(QString_To_Py_Path(path));
if (projectInfo.IsValid())
{
return AZ::Success(AZStd::move(projectInfo));
@@ -745,14 +738,11 @@ namespace O3DE::ProjectManager
{
return ExecuteWithLockErrorHandling([&]
{
pybind11::str pyGemPath = gemPath.toStdString();
pybind11::str pyProjectPath = projectPath.toStdString();
m_enableGemProject.attr("enable_gem_in_project")(
pybind11::none(), // gem name not needed as path is provided
pyGemPath,
QString_To_Py_Path(gemPath),
pybind11::none(), // project name not needed as path is provided
pyProjectPath
QString_To_Py_Path(projectPath)
);
});
}
@@ -761,21 +751,19 @@ namespace O3DE::ProjectManager
{
return ExecuteWithLockErrorHandling([&]
{
pybind11::str pyGemPath = gemPath.toStdString();
pybind11::str pyProjectPath = projectPath.toStdString();
m_disableGemProject.attr("disable_gem_in_project")(
pybind11::none(), // gem name not needed as path is provided
pyGemPath,
QString_To_Py_Path(gemPath),
pybind11::none(), // project name not needed as path is provided
pyProjectPath
QString_To_Py_Path(projectPath)
);
});
}
AZ::Outcome<void, AZStd::string> PythonBindings::UpdateProject(const ProjectInfo& projectInfo)
{
return ExecuteWithLockErrorHandling([&]
bool updateProjectSucceeded = false;
auto result = ExecuteWithLockErrorHandling([&]
{
std::list<std::string> newTags;
for (const auto& i : projectInfo.m_userTags)
@@ -783,23 +771,36 @@ namespace O3DE::ProjectManager
newTags.push_back(i.toStdString());
}
m_editProjectProperties.attr("edit_project_props")(
pybind11::str(projectInfo.m_path.toStdString()), // proj_path
auto editResult = m_editProjectProperties.attr("edit_project_props")(
QString_To_Py_Path(projectInfo.m_path),
pybind11::none(), // proj_name not used
pybind11::str(projectInfo.m_origin.toStdString()), // new_origin
pybind11::str(projectInfo.m_displayName.toStdString()), // new_display
pybind11::str(projectInfo.m_summary.toStdString()), // new_summary
pybind11::str(projectInfo.m_iconPath.toStdString()), // new_icon
QString_To_Py_String(projectInfo.m_projectName),
QString_To_Py_String(projectInfo.m_origin),
QString_To_Py_String(projectInfo.m_displayName),
QString_To_Py_String(projectInfo.m_summary),
QString_To_Py_String(projectInfo.m_iconPath), // new_icon
pybind11::none(), // add_tags not used
pybind11::none(), // remove_tags not used
pybind11::list(pybind11::cast(newTags))); // replace_tags
pybind11::list(pybind11::cast(newTags)));
updateProjectSucceeded = (editResult.cast<int>() == 0);
});
if (!result.IsSuccess())
{
return result;
}
else if (!updateProjectSucceeded)
{
return AZ::Failure<AZStd::string>("Failed to update project.");
}
return AZ::Success();
}
ProjectTemplateInfo PythonBindings::ProjectTemplateInfoFromPath(pybind11::handle path, pybind11::handle pyProjectPath)
{
ProjectTemplateInfo templateInfo;
templateInfo.m_path = Py_To_String(pybind11::str(path));
templateInfo.m_path = Py_To_String(path);
auto data = m_manifest.attr("get_template_json_data")(pybind11::none(), path, pyProjectPath);
if (pybind11::isinstance<pybind11::dict>(data))
@@ -848,10 +849,9 @@ namespace O3DE::ProjectManager
QVector<ProjectTemplateInfo> templates;
bool result = ExecuteWithLock([&] {
pybind11::str pyProjectPath = projectPath.toStdString();
for (auto path : m_manifest.attr("get_templates_for_project_creation")())
{
templates.push_back(ProjectTemplateInfoFromPath(path, pyProjectPath));
templates.push_back(ProjectTemplateInfoFromPath(path, QString_To_Py_Path(projectPath)));
}
});
@@ -75,13 +75,15 @@ namespace O3DE::ProjectManager
bool m_pythonStarted = false;
AZ::IO::FixedMaxPath m_enginePath;
pybind11::handle m_engineTemplate;
AZStd::recursive_mutex m_lock;
pybind11::handle m_engineTemplate;
pybind11::handle m_cmake;
pybind11::handle m_register;
pybind11::handle m_manifest;
pybind11::handle m_enableGemProject;
pybind11::handle m_disableGemProject;
pybind11::handle m_editProjectProperties;
pybind11::handle m_pathlib;
};
}