Merge branch 'ly-as-sdk/LYN-2948' into ly-as-sdk/LYN-2948-phistere

main
phistere 5 years ago
commit 77a035f015

@ -53,7 +53,7 @@ namespace Platform
#define Py_To_String(obj) 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
namespace O3DE::ProjectManager
namespace O3DE::ProjectManager
{
PythonBindings::PythonBindings(const AZ::IO::PathView& enginePath)
: m_enginePath(enginePath)
@ -112,7 +112,7 @@ namespace O3DE::ProjectManager
AZ_Warning("ProjectManagerWindow", result != -1, "Append to sys path failed");
// import required modules
m_registration = pybind11::module::import("cmake.Tools.registration");
m_registration = pybind11::module::import("o3de.registration");
return result == 0 && !PyErr_Occurred();
} catch ([[maybe_unused]] const std::exception& e)
@ -153,22 +153,22 @@ namespace O3DE::ProjectManager
}
}
AZ::Outcome<EngineInfo> PythonBindings::GetEngineInfo()
AZ::Outcome<EngineInfo> PythonBindings::GetEngineInfo()
{
return AZ::Failure();
}
bool PythonBindings::SetEngineInfo([[maybe_unused]] const EngineInfo& engineInfo)
bool PythonBindings::SetEngineInfo([[maybe_unused]] const EngineInfo& engineInfo)
{
return false;
}
AZ::Outcome<GemInfo> PythonBindings::GetGem(const QString& path)
AZ::Outcome<GemInfo> PythonBindings::GetGem(const QString& path)
{
GemInfo gemInfo = GemInfoFromPath(pybind11::str(path.toStdString()));
if (gemInfo.IsValid())
{
return AZ::Success(AZStd::move(gemInfo));
return AZ::Success(AZStd::move(gemInfo));
}
else
{
@ -176,18 +176,18 @@ namespace O3DE::ProjectManager
}
}
AZ::Outcome<QVector<GemInfo>> PythonBindings::GetGems()
AZ::Outcome<QVector<GemInfo>> PythonBindings::GetGems()
{
QVector<GemInfo> gems;
bool result = ExecuteWithLock([&] {
// external gems
// external gems
for (auto path : m_registration.attr("get_gems")())
{
gems.push_back(GemInfoFromPath(path));
}
// gems from the engine
// gems from the engine
for (auto path : m_registration.attr("get_engine_gems")())
{
gems.push_back(GemInfoFromPath(path));
@ -200,21 +200,21 @@ namespace O3DE::ProjectManager
}
else
{
return AZ::Success(AZStd::move(gems));
return AZ::Success(AZStd::move(gems));
}
}
AZ::Outcome<ProjectInfo> PythonBindings::CreateProject([[maybe_unused]] const ProjectTemplateInfo& projectTemplate,[[maybe_unused]] const ProjectInfo& projectInfo)
AZ::Outcome<ProjectInfo> PythonBindings::CreateProject([[maybe_unused]] const ProjectTemplateInfo& projectTemplate,[[maybe_unused]] const ProjectInfo& projectInfo)
{
return AZ::Failure();
}
AZ::Outcome<ProjectInfo> PythonBindings::GetProject(const QString& path)
AZ::Outcome<ProjectInfo> PythonBindings::GetProject(const QString& path)
{
ProjectInfo projectInfo = ProjectInfoFromPath(pybind11::str(path.toStdString()));
if (projectInfo.IsValid())
{
return AZ::Success(AZStd::move(projectInfo));
return AZ::Success(AZStd::move(projectInfo));
}
else
{
@ -225,7 +225,7 @@ namespace O3DE::ProjectManager
GemInfo PythonBindings::GemInfoFromPath(pybind11::handle path)
{
GemInfo gemInfo;
gemInfo.m_path = Py_To_String(path);
gemInfo.m_path = Py_To_String(path);
auto data = m_registration.attr("get_gem_data")(pybind11::none(), path);
if (pybind11::isinstance<pybind11::dict>(data))
@ -233,13 +233,13 @@ namespace O3DE::ProjectManager
try
{
// required
gemInfo.m_name = Py_To_String(data["Name"]);
gemInfo.m_uuid = AZ::Uuid(Py_To_String(data["Uuid"]));
gemInfo.m_name = Py_To_String(data["Name"]);
gemInfo.m_uuid = AZ::Uuid(Py_To_String(data["Uuid"]));
// optional
gemInfo.m_displayName = Py_To_String_Optional(data, "DisplayName", gemInfo.m_name);
gemInfo.m_summary = Py_To_String_Optional(data, "Summary", "");
gemInfo.m_version = Py_To_String_Optional(data, "Version", "");
gemInfo.m_displayName = Py_To_String_Optional(data, "DisplayName", gemInfo.m_name);
gemInfo.m_summary = Py_To_String_Optional(data, "Summary", "");
gemInfo.m_version = Py_To_String_Optional(data, "Version", "");
if (data.contains("Dependencies"))
{
@ -268,7 +268,7 @@ namespace O3DE::ProjectManager
ProjectInfo PythonBindings::ProjectInfoFromPath(pybind11::handle path)
{
ProjectInfo projectInfo;
projectInfo.m_path = Py_To_String(path);
projectInfo.m_path = Py_To_String(path);
auto projectData = m_registration.attr("get_project_data")(pybind11::none(), path);
if (pybind11::isinstance<pybind11::dict>(projectData))
@ -276,9 +276,9 @@ namespace O3DE::ProjectManager
try
{
// required fields
projectInfo.m_productName = Py_To_String(projectData["product_name"]);
projectInfo.m_projectName = Py_To_String(projectData["project_name"]);
projectInfo.m_projectId = AZ::Uuid(Py_To_String(projectData["project_id"]));
projectInfo.m_productName = Py_To_String(projectData["product_name"]);
projectInfo.m_projectName = Py_To_String(projectData["project_name"]);
projectInfo.m_projectId = AZ::Uuid(Py_To_String(projectData["project_id"]));
}
catch ([[maybe_unused]] const std::exception& e)
{
@ -289,18 +289,18 @@ namespace O3DE::ProjectManager
return projectInfo;
}
AZ::Outcome<QVector<ProjectInfo>> PythonBindings::GetProjects()
AZ::Outcome<QVector<ProjectInfo>> PythonBindings::GetProjects()
{
QVector<ProjectInfo> projects;
bool result = ExecuteWithLock([&] {
// external projects
// external projects
for (auto path : m_registration.attr("get_projects")())
{
projects.push_back(ProjectInfoFromPath(path));
}
// projects from the engine
// projects from the engine
for (auto path : m_registration.attr("get_engine_projects")())
{
projects.push_back(ProjectInfoFromPath(path));
@ -313,11 +313,11 @@ namespace O3DE::ProjectManager
}
else
{
return AZ::Success(AZStd::move(projects));
return AZ::Success(AZStd::move(projects));
}
}
bool PythonBindings::UpdateProject([[maybe_unused]] const ProjectInfo& projectInfo)
bool PythonBindings::UpdateProject([[maybe_unused]] const ProjectInfo& projectInfo)
{
return false;
}
@ -325,7 +325,7 @@ namespace O3DE::ProjectManager
ProjectTemplateInfo PythonBindings::ProjectTemplateInfoFromPath(pybind11::handle path)
{
ProjectTemplateInfo templateInfo;
templateInfo.m_path = Py_To_String(path);
templateInfo.m_path = Py_To_String(path);
auto data = m_registration.attr("get_template_data")(pybind11::none(), path);
if (pybind11::isinstance<pybind11::dict>(data))
@ -333,10 +333,10 @@ namespace O3DE::ProjectManager
try
{
// required
templateInfo.m_displayName = Py_To_String(data["display_name"]);
templateInfo.m_name = Py_To_String(data["template_name"]);
templateInfo.m_summary = Py_To_String(data["summary"]);
templateInfo.m_displayName = Py_To_String(data["display_name"]);
templateInfo.m_name = Py_To_String(data["template_name"]);
templateInfo.m_summary = Py_To_String(data["summary"]);
// optional
if (data.contains("canonical_tags"))
{
@ -362,7 +362,7 @@ namespace O3DE::ProjectManager
return templateInfo;
}
AZ::Outcome<QVector<ProjectTemplateInfo>> PythonBindings::GetProjectTemplates()
AZ::Outcome<QVector<ProjectTemplateInfo>> PythonBindings::GetProjectTemplates()
{
QVector<ProjectTemplateInfo> templates;
@ -379,7 +379,7 @@ namespace O3DE::ProjectManager
}
else
{
return AZ::Success(AZStd::move(templates));
return AZ::Success(AZStd::move(templates));
}
}
}

@ -270,6 +270,8 @@ if (NOT CMAKE_SCRIPT_MODE_FILE)
ly_pip_install_local_package_editable(${LY_ROOT_FOLDER}/Tools/RemoteConsole/ly_remote_console ly-remote-console)
ly_pip_install_local_package_editable(${LY_ROOT_FOLDER}/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools editor-python-test-tools)
endif()
ly_pip_install_local_package_editable(${LY_ROOT_FOLDER}/scripts/o3de o3de)
endif()
endif()

@ -11,5 +11,6 @@
add_subdirectory(detect_file_changes)
add_subdirectory(commit_validation)
add_subdirectory(o3de)
add_subdirectory(project_manager)
add_subdirectory(ctest)

@ -10,17 +10,24 @@
#
import argparse
import pathlib
import sys
import os
# Resolve the common python module
ROOT_DEV_PATH = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
if ROOT_DEV_PATH not in sys.path:
sys.path.append(ROOT_DEV_PATH)
from cmake.Tools import engine_template
from cmake.Tools import global_project
from cmake.Tools import registration
# As o3de.py shares the same name as the o3de package attempting to use a regular
# from o3de import <module> line tries to import from the current o3de.py script and not the package
# So the current script directory is removed from the sys.path temporary
SCRIPT_DIR_REMOVED = False
SCRIPT_DIR = pathlib.Path(__file__).parent.resolve()
if str(SCRIPT_DIR) in sys.path:
SCRIPT_DIR_REMOVED = True
sys.path.remove(str(SCRIPT_DIR))
from o3de import engine_template
from o3de import global_project
from o3de import registration
if SCRIPT_DIR_REMOVED:
sys.path.insert(0, str(SCRIPT_DIR))
def add_args(parser, subparsers) -> None:

@ -0,0 +1,12 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
add_subdirectory(tests)

@ -0,0 +1,41 @@
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
INTRODUCTION
------------
o3de is a package of scripts containing functionality to register engine, projects, gems,
templates and download repositories with the o3de manifests
It also contains functionality for creating new projects, gems and templates as well
as querying existing gems and templates
REQUIREMENTS
------------
* Python 3.7.10 (64-bit)
INSTALL
-----------
It is recommended to set up these these tools with O3DE's CMake build commands.
Assuming CMake is already setup on your operating system, below are some sample build commands:
cd /path/to/od3e/
cmake -B windows_vs2019 -S . -G"Visual Studio 16" -DLY_3RDPARTY_PATH="%LY_3RDPARTY_PATH%"
To manually install the project in development mode using your own installed Python interpreter:
cd /path/to/od3e/o3de
/path/to/your/python -m pip install -e .
UNINSTALLATION
--------------
The preferred way to uninstall the project is:
/path/to/your/python -m pip uninstall o3de

@ -0,0 +1,10 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#

@ -20,8 +20,8 @@ import json
import uuid
import re
from cmake.Tools import utils
import cmake.Tools.registration as registration
from o3de import utils, registration
logger = logging.getLogger()
logging.basicConfig()
@ -2423,7 +2423,7 @@ if __name__ == "__main__":
the_parser = argparse.ArgumentParser()
# add subparsers
the_subparsers = the_parser.add_subparsers(help='sub-command help')
the_subparsers = the_parser.add_subparsers(help='sub-command help', dest='command', required=True)
# add args to the parser
add_args(the_parser, the_subparsers)
@ -2432,7 +2432,8 @@ if __name__ == "__main__":
the_args = the_parser.parse_args()
# run
ret = the_args.func(the_args)
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
# return
sys.exit(ret)

@ -16,7 +16,7 @@ import sys
import re
import pathlib
import json
import cmake.Tools.registration as registration
from o3de import registration
logger = logging.getLogger()
logging.basicConfig()
@ -153,7 +153,7 @@ if __name__ == "__main__":
the_parser = argparse.ArgumentParser()
# add subparsers
the_subparsers = the_parser.add_subparsers(help='sub-command help')
the_subparsers = the_parser.add_subparsers(help='sub-command help', dest='command', required=True)
# add args to the parser
add_args(the_parser, the_subparsers)
@ -162,7 +162,7 @@ if __name__ == "__main__":
the_args = the_parser.parse_args()
# run
ret = the_args.func(the_args)
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
# return
sys.exit(ret)

@ -128,7 +128,7 @@ def get_o3de_logs_folder() -> pathlib.Path:
return restricted_folder
def register_shipped_engine_o3de_objects() -> int:
def register_shipped_engine_o3de_objects(force: bool = False) -> int:
engine_path = get_this_engine_path()
ret_val = 0
@ -137,7 +137,7 @@ def register_shipped_engine_o3de_objects() -> int:
starting_engines_directories = [
]
for engines_directory in sorted(starting_engines_directories, reverse=True):
error_code = register_all_engines_in_folder(engines_path=engines_directory)
error_code = register_all_engines_in_folder(engines_path=engines_directory, force=force)
if error_code:
ret_val = error_code
@ -145,7 +145,7 @@ def register_shipped_engine_o3de_objects() -> int:
starting_engines = [
]
for engine_path in sorted(starting_engines):
error_code = register(engine_path=engine_path)
error_code = register(engine_path=engine_path, force=force)
if error_code:
ret_val = error_code
@ -162,7 +162,7 @@ def register_shipped_engine_o3de_objects() -> int:
f'{engine_path}/AutomatedTesting'
]
for project_path in sorted(starting_projects, reverse=True):
error_code = register(engine_path=engine_path, project_path=project_path)
error_code = register(engine_path=engine_path, project_path=project_path, force=force)
if error_code:
ret_val = error_code
@ -179,7 +179,7 @@ def register_shipped_engine_o3de_objects() -> int:
starting_gems = [
]
for gem_path in sorted(starting_gems, reverse=True):
error_code = register(engine_path=engine_path, gem_path=gem_path)
error_code = register(engine_path=engine_path, gem_path=gem_path, force=force)
if error_code:
ret_val = error_code
@ -196,7 +196,7 @@ def register_shipped_engine_o3de_objects() -> int:
starting_templates = [
]
for template_path in sorted(starting_templates, reverse=True):
error_code = register(engine_path=engine_path, template_path=template_path)
error_code = register(engine_path=engine_path, template_path=template_path, force=force)
if error_code:
ret_val = error_code
@ -212,7 +212,7 @@ def register_shipped_engine_o3de_objects() -> int:
starting_restricted = [
]
for restricted_path in sorted(starting_restricted, reverse=True):
error_code = register(engine_path=engine_path, restricted_path=restricted_path)
error_code = register(engine_path=engine_path, restricted_path=restricted_path, force=force)
if error_code:
ret_val = error_code
@ -228,12 +228,12 @@ def register_shipped_engine_o3de_objects() -> int:
starting_repos = [
]
for repo_uri in sorted(starting_repos, reverse=True):
error_code = register(repo_uri=repo_uri)
error_code = register(repo_uri=repo_uri, force=force)
if error_code:
ret_val = error_code
# register anything in the users default folders globally
error_code = register_all_engines_in_folder(get_registered(default_folder='engines'))
error_code = register_all_engines_in_folder(get_registered(default_folder='engines'), force=force)
if error_code:
ret_val = error_code
error_code = register_all_projects_in_folder(get_registered(default_folder='projects'))
@ -266,7 +266,7 @@ def register_shipped_engine_o3de_objects() -> int:
gem_path = pathlib.Path(gem_path).resolve()
gem_cmake_lists_txt = gem_path / 'CMakeLists.txt'
if gem_cmake_lists_txt.is_file():
add_gem_to_cmake(engine_path=engine_path, gem_path=gem_path, supress_errors=True) # don't care about errors
add_gem_to_cmake(engine_path=engine_path, gem_path=gem_path, suppress_errors=True) # don't care about errors
return ret_val
@ -344,7 +344,8 @@ def register_all_in_folder(folder_path: str or pathlib.Path,
def register_all_engines_in_folder(engines_path: str or pathlib.Path,
remove: bool = False) -> int:
remove: bool = False,
force: bool = False) -> int:
if not engines_path:
logger.error(f'Engines path cannot be empty.')
return 1
@ -360,10 +361,10 @@ def register_all_engines_in_folder(engines_path: str or pathlib.Path,
for root, dirs, files in os.walk(engines_path):
for name in files:
if name == 'engine.json':
engines_set.add(name)
engines_set.add(root)
for engine in sorted(engines_set, reverse=True):
error_code = register(engine_path=engine, remove=remove)
error_code = register(engine_path=engine, remove=remove, force=force)
if error_code:
ret_val = error_code
@ -602,21 +603,65 @@ def save_o3de_manifest(json_data: dict) -> None:
logger.error(f'Manifest json failed to save: {str(e)}')
def remove_engine_name_to_path(json_data: dict,
engine_path: pathlib.Path) -> int:
"""
Remove the engine at the specified path if it exist in the o3de manifest
:param json_data in-memory json view of the o3de_manifest.json data
:param engine_path path to engine to remove from the manifest data
returns 0 to indicate no issues has occurred with removal
"""
if engine_path.is_dir() and valid_o3de_engine_json(engine_path):
engine_json_data = get_engine_data(engine_path=engine_path)
if 'engine_name' in engine_json_data and 'engines_path' in json_data:
engine_name = engine_json_data['engine_name']
try:
del json_data['engines_path'][engine_name]
except KeyError:
# Attempting to remove a non-existent engine_name is fine
pass
return 0
def add_engine_name_to_path(json_data: dict, engine_path: pathlib.Path, force: bool):
# Add an engine path JSON object which maps the "engine_name" -> "engine_path"
engine_json_data = get_engine_data(engine_path=engine_path)
if not engine_json_data:
logger.error(f'Unable to retrieve json data from engine.json at path {engine_path.as_posix()}')
return 1
engines_path_json = json_data.setdefault('engines_path', {})
if 'engine_name' not in engine_json_data:
logger.error(f'engine.json at path {engine_path.as_posix()} is missing "engine_name" key')
return 1
engine_name = engine_json_data['engine_name']
if not force and engine_name in engines_path_json and \
pathlib.PurePath(engines_path_json[engine_name]) != engine_path:
logger.error(
f'Attempting to register existing engine "{engine_name}" with a new path of {engine_path.as_posix()}.'
f' The current path is {pathlib.Path(engines_path_json[engine_name]).as_posix()}.'
f' To force registration of a new engine path, specify the -f/--force option.')
return 1
engines_path_json[engine_name] = engine_path.as_posix()
return 0
def register_engine_path(json_data: dict,
engine_path: str or pathlib.Path,
remove: bool = False) -> int:
remove: bool = False,
force: bool = False) -> int:
if not engine_path:
logger.error(f'Engine path cannot be empty.')
return 1
engine_path = pathlib.Path(engine_path).resolve()
for engine_object in json_data['engines']:
for engine_object in json_data.get('engines', {}):
engine_object_path = pathlib.Path(engine_object['path']).resolve()
if engine_object_path == engine_path:
json_data['engines'].remove(engine_object)
if remove:
return 0
return remove_engine_name_to_path(json_data, engine_path)
if not engine_path.is_dir():
logger.error(f'Engine path {engine_path} does not exist.')
@ -635,9 +680,9 @@ def register_engine_path(json_data: dict,
engine_object.update({'restricted': []})
engine_object.update({'external_subdirectories': []})
json_data['engines'].insert(0, engine_object)
json_data.setdefault('engines', []).insert(0, engine_object)
return 0
return add_engine_name_to_path(json_data, engine_path, force)
def register_gem_path(json_data: dict,
@ -1234,7 +1279,8 @@ def register(engine_path: str or pathlib.Path = None,
default_gems_folder: str or pathlib.Path = None,
default_templates_folder: str or pathlib.Path = None,
default_restricted_folder: str or pathlib.Path = None,
remove: bool = False
remove: bool = False,
force: bool = False
) -> int:
"""
Adds/Updates entries to the .o3de/o3de_manifest.json
@ -1251,6 +1297,7 @@ def register(engine_path: str or pathlib.Path = None,
:param default_templates_folder: default templates folder
:param default_restricted_folder: default restricted code folder
:param remove: add/remove the entries
:param force: force update of the engine_path for specified "engine_name" from the engine.json file
:return: 0 for success or non 0 failure code
"""
@ -1312,7 +1359,7 @@ def register(engine_path: str or pathlib.Path = None,
if not engine_path:
logger.error(f'Engine path cannot be empty.')
return 1
result = register_engine_path(json_data, engine_path, remove)
result = register_engine_path(json_data, engine_path, remove, force)
if not result:
save_o3de_manifest(json_data)
@ -2122,23 +2169,23 @@ def get_engine_data(engine_name: str = None,
engine_path: str or pathlib.Path = None, ) -> dict or None:
if not engine_name and not engine_path:
logger.error('Must specify either a Engine name or Engine Path.')
return 1
return None
if engine_name and not engine_path:
engine_path = get_registered(engine_name=engine_name)
if not engine_path:
logger.error(f'Engine Path {engine_path} has not been registered.')
return 1
return None
engine_path = pathlib.Path(engine_path).resolve()
engine_json = engine_path / 'engine.json'
if not engine_json.is_file():
logger.error(f'Engine json {engine_json} is not present.')
return 1
return None
if not valid_o3de_engine_json(engine_json):
logger.error(f'Engine json {engine_json} is not valid.')
return 1
return None
with engine_json.open('r') as f:
try:
@ -2155,23 +2202,23 @@ def get_project_data(project_name: str = None,
project_path: str or pathlib.Path = None, ) -> dict or None:
if not project_name and not project_path:
logger.error('Must specify either a Project name or Project Path.')
return 1
return None
if project_name and not project_path:
project_path = get_registered(project_name=project_name)
if not project_path:
logger.error(f'Project Path {project_path} has not been registered.')
return 1
return None
project_path = pathlib.Path(project_path).resolve()
project_json = project_path / 'project.json'
if not project_json.is_file():
logger.error(f'Project json {project_json} is not present.')
return 1
return None
if not valid_o3de_project_json(project_json):
logger.error(f'Project json {project_json} is not valid.')
return 1
return None
with project_json.open('r') as f:
try:
@ -2188,23 +2235,23 @@ def get_gem_data(gem_name: str = None,
gem_path: str or pathlib.Path = None, ) -> dict or None:
if not gem_name and not gem_path:
logger.error('Must specify either a Gem name or Gem Path.')
return 1
return None
if gem_name and not gem_path:
gem_path = get_registered(gem_name=gem_name)
if not gem_path:
logger.error(f'Gem Path {gem_path} has not been registered.')
return 1
return None
gem_path = pathlib.Path(gem_path).resolve()
gem_json = gem_path / 'gem.json'
if not gem_json.is_file():
logger.error(f'Gem json {gem_json} is not present.')
return 1
return None
if not valid_o3de_gem_json(gem_json):
logger.error(f'Gem json {gem_json} is not valid.')
return 1
return None
with gem_json.open('r') as f:
try:
@ -2221,23 +2268,23 @@ def get_template_data(template_name: str = None,
template_path: str or pathlib.Path = None, ) -> dict or None:
if not template_name and not template_path:
logger.error('Must specify either a Template name or Template Path.')
return 1
return None
if template_name and not template_path:
template_path = get_registered(template_name=template_name)
if not template_path:
logger.error(f'Template Path {template_path} has not been registered.')
return 1
return None
template_path = pathlib.Path(template_path).resolve()
template_json = template_path / 'template.json'
if not template_json.is_file():
logger.error(f'Template json {template_json} is not present.')
return 1
return None
if not valid_o3de_template_json(template_json):
logger.error(f'Template json {template_json} is not valid.')
return 1
return None
with template_json.open('r') as f:
try:
@ -2254,23 +2301,23 @@ def get_restricted_data(restricted_name: str = None,
restricted_path: str or pathlib.Path = None, ) -> dict or None:
if not restricted_name and not restricted_path:
logger.error('Must specify either a Restricted name or Restricted Path.')
return 1
return None
if restricted_name and not restricted_path:
restricted_path = get_registered(restricted_name=restricted_name)
if not restricted_path:
logger.error(f'Restricted Path {restricted_path} has not been registered.')
return 1
return None
restricted_path = pathlib.Path(restricted_path).resolve()
restricted_json = restricted_path / 'restricted.json'
if not restricted_json.is_file():
logger.error(f'Restricted json {restricted_json} is not present.')
return 1
return None
if not valid_o3de_restricted_json(restricted_json):
logger.error(f'Restricted json {restricted_json} is not valid.')
return 1
return None
with restricted_json.open('r') as f:
try:
@ -3259,30 +3306,30 @@ def get_gem_targets(gem_name: str = None,
def add_external_subdirectory(external_subdir: str or pathlib.Path,
engine_path: str or pathlib.Path = None,
supress_errors: bool = False) -> int:
suppress_errors: bool = False) -> int:
"""
add external subdirectory to a cmake
:param external_subdir: external subdirectory to add to cmake
:param engine_path: optional engine path, defaults to this engine
:param supress_errors: optional silence errors
:param suppress_errors: optional silence errors
:return: 0 for success or non 0 failure code
"""
external_subdir = pathlib.Path(external_subdir).resolve()
if not external_subdir.is_dir():
if not supress_errors:
if not suppress_errors:
logger.error(f'Add External Subdirectory Failed: {external_subdir} does not exist.')
return 1
external_subdir_cmake = external_subdir / 'CMakeLists.txt'
if not external_subdir_cmake.is_file():
if not supress_errors:
if not suppress_errors:
logger.error(f'Add External Subdirectory Failed: {external_subdir} does not contain a CMakeLists.txt.')
return 1
json_data = load_o3de_manifest()
engine_object = find_engine_data(json_data, engine_path)
if not engine_object:
if not supress_errors:
if not suppress_errors:
logger.error(f'Add External Subdirectory Failed: {engine_path} not registered.')
return 1
@ -3290,7 +3337,7 @@ def add_external_subdirectory(external_subdir: str or pathlib.Path,
engine_object['external_subdirectories'].remove(external_subdir.as_posix())
def parse_cmake_file(cmake: str or pathlib.Path,
files: set()):
files: set):
cmake_path = pathlib.Path(cmake).resolve()
cmake_file = cmake_path
if cmake_path.is_dir():
@ -3335,7 +3382,7 @@ def add_external_subdirectory(external_subdir: str or pathlib.Path,
if external_subdir in cmake_files:
save_o3de_manifest(json_data)
if not supress_errors:
if not suppress_errors:
logger.error(f'External subdirectory {external_subdir.as_posix()} already included by add_subdirectory().')
return 1
@ -3374,18 +3421,18 @@ def add_gem_to_cmake(gem_name: str = None,
gem_path: str or pathlib.Path = None,
engine_name: str = None,
engine_path: str or pathlib.Path = None,
supress_errors: bool = False) -> int:
suppress_errors: bool = False) -> int:
"""
add a gem to a cmake as an external subdirectory for an engine
:param gem_name: name of the gem to add to cmake
:param gem_path: the path of the gem to add to cmake
:param engine_name: name of the engine to add to cmake
:param engine_path: the path of the engine to add external subdirectory to, default to this engine
:param supress_errors: optional silence errors
:param suppress_errors: optional silence errors
:return: 0 for success or non 0 failure code
"""
if not gem_name and not gem_path:
if not supress_errors:
if not suppress_errors:
logger.error('Must specify either a Gem name or Gem Path.')
return 1
@ -3393,18 +3440,18 @@ def add_gem_to_cmake(gem_name: str = None,
gem_path = get_registered(gem_name=gem_name)
if not gem_path:
if not supress_errors:
if not suppress_errors:
logger.error(f'Gem Path {gem_path} has not been registered.')
return 1
gem_path = pathlib.Path(gem_path).resolve()
gem_json = gem_path / 'gem.json'
if not gem_json.is_file():
if not supress_errors:
if not suppress_errors:
logger.error(f'Gem json {gem_json} is not present.')
return 1
if not valid_o3de_gem_json(gem_json):
if not supress_errors:
if not suppress_errors:
logger.error(f'Gem json {gem_json} is not valid.')
return 1
@ -3415,21 +3462,21 @@ def add_gem_to_cmake(gem_name: str = None,
engine_path = get_registered(engine_name=engine_name)
if not engine_path:
if not supress_errors:
if not suppress_errors:
logger.error(f'Engine Path {engine_path} has not been registered.')
return 1
engine_json = engine_path / 'engine.json'
if not engine_json.is_file():
if not supress_errors:
if not suppress_errors:
logger.error(f'Engine json {engine_json} is not present.')
return 1
if not valid_o3de_engine_json(engine_json):
if not supress_errors:
if not suppress_errors:
logger.error(f'Engine json {engine_json} is not valid.')
return 1
return add_external_subdirectory(external_subdir=gem_path, engine_path=engine_path, supress_errors=supress_errors)
return add_external_subdirectory(external_subdir=gem_path, engine_path=engine_path, suppress_errors=suppress_errors)
def remove_gem_from_cmake(gem_name: str = None,
@ -3930,13 +3977,13 @@ def _run_register(args: argparse) -> int:
remove_invalid_o3de_objects()
return refresh_repos()
elif args.this_engine:
ret_val = register(engine_path=get_this_engine_path())
error_code = register_shipped_engine_o3de_objects()
ret_val = register(engine_path=get_this_engine_path(), force=args.force)
error_code = register_shipped_engine_o3de_objects(force=args.force)
if error_code:
ret_val = error_code
return ret_val
elif args.all_engines_path:
return register_all_engines_in_folder(args.all_engines_path, args.remove)
return register_all_engines_in_folder(args.all_engines_path, args.remove, args.force)
elif args.all_projects_path:
return register_all_projects_in_folder(args.all_projects_path, args.remove)
elif args.all_gems_path:
@ -3959,7 +4006,8 @@ def _run_register(args: argparse) -> int:
default_gems_folder=args.default_gems_folder,
default_templates_folder=args.default_templates_folder,
default_restricted_folder=args.default_restricted_folder,
remove=args.remove)
remove=args.remove,
force=args.force)
def _run_add_external_subdirectory(args: argparse) -> int:
@ -4096,6 +4144,8 @@ def add_args(parser, subparsers) -> None:
register_subparser.add_argument('-r', '--remove', action='store_true', required=False,
default=False,
help='Remove entry.')
register_subparser.add_argument('-f', '--force', action='store_true', default=False,
help='For the update of the registration field being modified.')
register_subparser.set_defaults(func=_run_register)
# show
@ -4366,7 +4416,7 @@ if __name__ == "__main__":
the_parser = argparse.ArgumentParser()
# add subparsers
the_subparsers = the_parser.add_subparsers(help='sub-command help')
the_subparsers = the_parser.add_subparsers(help='sub-command help', dest='command', required=True)
# add args to the parser
add_args(the_parser, the_subparsers)
@ -4375,7 +4425,7 @@ if __name__ == "__main__":
the_args = the_parser.parse_args()
# run
ret = the_args.func(the_args)
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
# return
sys.exit(ret)

@ -0,0 +1,42 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import os
import platform
from setuptools import setup, find_packages
from setuptools.command.develop import develop
from setuptools.command.build_py import build_py
PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__))
PYTHON_64 = platform.architecture()[0] == '64bit'
if __name__ == '__main__':
if not PYTHON_64:
raise RuntimeError("32-bit Python is not a supported platform.")
with open(os.path.join(PACKAGE_ROOT, 'README.txt')) as f:
long_description = f.read()
setup(
name="o3de",
version="1.0.0",
description='O3DE editor Python bindings test tools',
long_description=long_description,
packages=find_packages(where='o3de', exclude=['tests']),
install_requires=[
],
tests_require=[
],
entry_points={
},
)

@ -0,0 +1,22 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(NOT PAL_TRAIT_BUILD_TESTS_SUPPORTED)
return()
endif()
# Add a test to test out the o3de package `o3de.py register` command
ly_add_pytest(
NAME o3de_register
PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_registration.py
TEST_SUITE smoke
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
)

@ -0,0 +1,10 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#

@ -0,0 +1,66 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
import argparse
import json
import logging
import pytest
import pathlib
from unittest.mock import patch
from .. import registration
string_manifest_data = '{}'
@pytest.mark.parametrize(
"engine_path, engine_name, force, expected_result", [
pytest.param(pathlib.PurePath('D:/o3de/o3de'), "o3de", False, 0),
# Same engine_name and path should result in valid registration
pytest.param(pathlib.PurePath('D:/o3de/o3de'), "o3de", False, 0),
# Same engine_name and but different path should fail
pytest.param(pathlib.PurePath('D:/o3de/engine-path'), "o3de", False, 1),
# New engine_name should result in valid registration
pytest.param(pathlib.PurePath('D:/o3de/engine-path'), "o3de-other", False, 0),
# Same engine_name and but different path with --force should result in valid registration
pytest.param(pathlib.PurePath('F:/Open3DEngine'), "o3de", True, 0),
]
)
def test_register_engine_path(engine_path, engine_name, force, expected_result):
parser = argparse.ArgumentParser()
subparser = parser.add_subparsers(help='sub-command help')
# Register the registration script subparsers with the current argument parser
registration.add_args(parser, subparser)
arg_list = ['register', '--engine-path', str(engine_path)]
if force:
arg_list += ['--force']
args = parser.parse_args(arg_list)
def load_manifest_from_string() -> dict:
try:
manifest_json = json.loads(string_manifest_data)
except json.JSONDecodeError as err:
logging.error("Error decoding Json from Manifest file")
else:
return manifest_json
def save_manifest_to_string(manifest_json: dict) -> None:
global string_manifest_data
string_manifest_data = json.dumps(manifest_json)
engine_json_data = {'engine_name': engine_name}
with patch('o3de.registration.load_o3de_manifest', side_effect=load_manifest_from_string) as load_manifest_mock, \
patch('o3de.registration.save_o3de_manifest', side_effect=save_manifest_to_string) as save_manifest_mock, \
patch('o3de.registration.get_engine_data', return_value=engine_json_data) as engine_paths_mock, \
patch('o3de.registration.valid_o3de_engine_json', return_value=True) as valid_engine_mock, \
patch('pathlib.Path.is_dir', return_value=True) as pathlib_is_dir_mock:
result = registration._run_register(args)
assert result == expected_result

@ -29,12 +29,11 @@ executable_path = ''
logger = logging.getLogger()
logger.setLevel(logging.INFO)
from cmake.Tools import engine_template
from cmake.Tools import registration
from o3de import engine_template, registration
o3de_folder = registration.get_o3de_folder()
o3de_logs_folder = registration.get_o3de_logs_folder()
project_manager_log_file_path = o3de_log_folder / "project_manager.log"
project_manager_log_file_path = o3de_logs_folder / "project_manager.log"
log_file_handler = RotatingFileHandler(filename=project_manager_log_file_path, maxBytes=1024 * 1024, backupCount=1)
formatter = logging.Formatter('%(asctime)s | %(levelname)s : %(message)s')
log_file_handler.setFormatter(formatter)

Loading…
Cancel
Save