The enable-gem command registers gem with project if not registered (#2817)

* The enable_gems command now registers the gem with the project if only registered with o3de_manifest.json

Updated the `enable_gems` command to register the gem with the project if the gem is not registered with either the project or the engine being used.
This allows the gem to be added to the build system if it wasn't registered before.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>

* Adding quoting around the invocation of the OpenProjectManager command

The --project-path parameter now is able to pass in a path with spaces
to the invocation of the Project Manager.

Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
This commit is contained in:
lumberyard-employee-dm
2021-08-04 13:13:28 -05:00
committed by GitHub
parent 52e9083b9d
commit 21ca3a4aea
6 changed files with 183 additions and 23 deletions
+8 -1
View File
@@ -2901,7 +2901,14 @@ void CCryEditApp::OpenProjectManager(const AZStd::string& screen)
{
// provide the current project path for in case we want to update the project
AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath();
const AZStd::string commandLineOptions = AZStd::string::format(" --screen %s --project-path %s", screen.c_str(), projectPath.c_str());
#if !AZ_TRAIT_OS_PLATFORM_APPLE && !AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
const char* argumentQuoteString = R"(")";
#else
const char* argumentQuoteString = R"(\")";
#endif
const AZStd::string commandLineOptions = AZStd::string::format(R"( --screen %s --project-path %s%s%s)",
screen.c_str(),
argumentQuoteString, projectPath.c_str(), argumentQuoteString);
bool launchSuccess = AzFramework::ProjectManager::LaunchProjectManager(commandLineOptions);
if (!launchSuccess)
{
+17 -5
View File
@@ -16,7 +16,7 @@ import os
import pathlib
import sys
from o3de import cmake, manifest, validation
from o3de import cmake, manifest, register, validation
logger = logging.getLogger()
logging.basicConfig()
@@ -87,8 +87,7 @@ def enable_gem_in_project(gem_name: str = None,
if not enabled_gem_file.is_file():
logger.error(f'Enabled gem file {enabled_gem_file} is not present.')
return 1
# add the gem
ret_val = cmake.add_gem_dependency(enabled_gem_file, gem_json_data['gem_name'])
project_enabled_gem_file = enabled_gem_file
else:
# Find the path to enabled gem file.
@@ -96,8 +95,21 @@ def enable_gem_in_project(gem_name: str = None,
project_enabled_gem_file = cmake.get_enabled_gem_cmake_file(project_path=project_path)
if not project_enabled_gem_file.is_file():
project_enabled_gem_file.touch()
# add the gem
ret_val = cmake.add_gem_dependency(project_enabled_gem_file, gem_json_data['gem_name'])
# Before adding the gem_dependency check if the project is registered in either the project or engine
# manifest
buildable_gems = manifest.get_engine_gems()
buildable_gems.extend(manifest.get_project_gems(project_path))
# Convert each path to pathlib.Path object and filter out duplictes using dict.fromkeys
buildable_gems = list(dict.fromkeys(map(lambda gem_path_string: pathlib.Path(gem_path_string), buildable_gems)))
ret_val = 0
# If the gem is not part of buildable set, it needs to be registered
if not gem_path in buildable_gems:
ret_val = register.register(gem_path=gem_path, external_subdir_project_path=project_path)
# add the gem if it is registered in either the project.json or engine.json
ret_val = ret_val or cmake.add_gem_dependency(project_enabled_gem_file, gem_json_data['gem_name'])
return ret_val
+3 -3
View File
@@ -285,14 +285,14 @@ def register_o3de_object_path(json_data: dict,
manifest_data = None
if engine_path:
manifest_data = manifest.get_engine_json_data(json_data, engine_path)
manifest_data = manifest.get_engine_json_data(None, engine_path)
if not manifest_data:
logger.error(f'Cannot load engine.json data at path {engine_path}')
return 1
save_path = engine_path / 'engine.json'
elif project_path:
manifest_data = manifest.get_project_json_data(json_data, project_path)
manifest_data = manifest.get_project_json_data(None, project_path)
if not manifest_data:
logger.error(f'Cannot load project.json data at path {project_path}')
return 1
@@ -329,7 +329,7 @@ def register_o3de_object_path(json_data: dict,
try:
o3de_object_path = o3de_object_path.relative_to(save_path.parent)
except ValueError:
pass # It is OK relative path cannot be formed
pass # It is OK relative path cannot be formed
manifest_data[o3de_object_key].insert(0, o3de_object_path.as_posix())
if save_path:
manifest.save_o3de_manifest(manifest_data, save_path)
+7
View File
@@ -25,6 +25,13 @@ ly_add_pytest(
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
)
ly_add_pytest(
NAME o3de_enable_gem
PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_enable_gem.py
TEST_SUITE smoke
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
)
ly_add_pytest(
NAME o3de_global_project
PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_global_project.py
+126
View File
@@ -0,0 +1,126 @@
#
# 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
#
#
import io
import json
import logging
import pytest
import pathlib
from unittest.mock import patch
from o3de import enable_gem
TEST_PROJECT_JSON_PAYLOAD = '''
{
"project_name": "TestProject",
"origin": "The primary repo for TestProject goes here: i.e. http://www.mydomain.com",
"license": "What license TestProject uses goes here: i.e. https://opensource.org/licenses/MIT",
"display_name": "TestProject",
"summary": "A short description of TestProject.",
"canonical_tags": [
"Project"
],
"user_tags": [
"TestProject"
],
"icon_path": "preview.png",
"engine": "o3de-install",
"restricted_name": "projects",
"external_subdirectories": [
]
}
'''
TEST_GEM_JSON_PAYLOAD = '''
{
"gem_name": "TestGem",
"display_name": "TestGem",
"license": "What license TestGem uses goes here: i.e. https://opensource.org/licenses/MIT",
"origin": "The primary repo for TestGem goes here: i.e. http://www.mydomain.com",
"type": "Code",
"summary": "A short description of TestGem.",
"canonical_tags": [
"Gem"
],
"user_tags": [
"TestGem"
],
"icon_path": "preview.png",
"requirements": ""
}
'''
@pytest.fixture(scope='class')
def init_enable_gem_data(request):
class EnableGemData:
def __init__(self):
self.project_data = json.loads(TEST_PROJECT_JSON_PAYLOAD)
self.gem_data = json.loads(TEST_GEM_JSON_PAYLOAD)
request.cls.enable_gem = EnableGemData()
@pytest.mark.usefixtures('init_enable_gem_data')
class TestEnableGemCommand:
@pytest.mark.parametrize("gem_path, project_path, gem_registered_with_project, gem_registered_with_engine,"
"expected_result", [
pytest.param(pathlib.PurePath('E:/TestGem'), pathlib.PurePath('E:/TestProject'), False, True, 0),
pytest.param(pathlib.PurePath('E:/TestGem'), pathlib.PurePath('E:/TestProject'), False, False, 0),
pytest.param(pathlib.PurePath('E:/TestGem'), pathlib.PurePath('E:/TestProject'), True, False, 0),
]
)
def test_enable_gem_registers_gem_as_well(self, gem_path, project_path, gem_registered_with_project, gem_registered_with_engine,
expected_result):
def get_registered_path(project_name: str = None, gem_name: str = None) -> pathlib.Path:
if project_name:
return project_path
elif gem_name:
return gem_path
return None
def get_registered_gem_path(gem_name: str) -> pathlib.Path:
return gem_path
def save_o3de_manifest(new_project_data: dict, manifest_path: pathlib.Path = None) -> bool:
if manifest_path == project_path:
self.enable_gem.project_data = new_project_data
return True
def get_project_json_data(json_data: pathlib.Path, project_path: pathlib.Path):
return self.enable_gem.project_data
def get_gem_json_data(gem_path: pathlib.Path, project_path: pathlib.Path):
return self.enable_gem.gem_data
def get_project_gems(project_path: pathlib.Path):
return [gem_path] if gem_registered_with_project else []
def get_engine_gems():
return [gem_path] if gem_registered_with_engine else []
def add_gem_dependency(enable_gem_cmake_file: pathlib.Path, gem_name: str):
return 0
with patch('pathlib.Path.is_dir', return_value=True) as pathlib_is_dir_patch,\
patch('pathlib.Path.is_file', return_value=True) as pathlib_is_file_patch,\
patch('o3de.manifest.save_o3de_manifest', side_effect=save_o3de_manifest) as save_o3de_manifest_patch,\
patch('o3de.manifest.get_registered', side_effect=get_registered_path) as get_registered_patch,\
patch('o3de.manifest.get_gem_json_data', side_effect=get_gem_json_data) as get_gem_json_data_patch,\
patch('o3de.manifest.get_project_json_data', side_effect=get_project_json_data) as get_gem_json_data_patch,\
patch('o3de.manifest.get_project_gems', side_effect=get_project_gems) as get_project_gems_patch,\
patch('o3de.manifest.get_engine_gems', side_effect=get_engine_gems) as get_engine_gems_patch,\
patch('o3de.cmake.add_gem_dependency', side_effect=add_gem_dependency) as add_gem_dependency_patch,\
patch('o3de.validation.valid_o3de_gem_json', return_value=True) as valid_gem_json_patch:
result = enable_gem.enable_gem_in_project(gem_path=gem_path, project_path=project_path)
assert result == expected_result
# If the gem isn't registered with the engine or project already it should now be registered with the project
if not gem_registered_with_engine and gem_registered_with_project:
assert gem_path.as_posix() in self.enable_gem.project_data.get('external_subdirectories', [])
@@ -6,33 +6,41 @@
#
#
import json
import pytest
import pathlib
from unittest.mock import patch
from o3de import project_properties
TEST_DEFAULT_PROJECT_DATA = {
"template_name": "DefaultProject",
"restricted_name": "o3de",
"restricted_platform_relative_path": "Templates",
"origin": "The primary repo for DefaultProject goes here: i.e. http://www.mydomain.com",
"license": "What license DefaultProject uses goes here: i.e. https://opensource.org/licenses/MIT",
"display_name": "Default",
"summary": "A short description of DefaultProject.",
"included_gems": ["Atom","Camera","EMotionFX","UI","Maestro","Input","ImGui"],
"canonical_tags": [],
"user_tags": [
"DefaultProject"
TEST_PROJECT_JSON_PAYLOAD = '''
{
"project_name": "TestProject",
"origin": "The primary repo for TestProject goes here: i.e. http://www.mydomain.com",
"license": "What license TestProject uses goes here: i.e. https://opensource.org/licenses/MIT",
"display_name": "TestProject",
"summary": "A short description of TestProject.",
"canonical_tags": [
"Project"
],
"icon_path": "preview.png"
"user_tags": [
"TestProject"
],
"icon_path": "preview.png",
"engine": "o3de-install",
"restricted_name": "projects",
"external_subdirectories": [
"D:/TestGem"
]
}
'''
@pytest.fixture(scope='class')
def init_project_json_data(request):
class ProjectJsonData:
def __init__(self):
self.data = TEST_DEFAULT_PROJECT_DATA
self.data = json.loads(TEST_PROJECT_JSON_PAYLOAD)
request.cls.project_json = ProjectJsonData()
@pytest.mark.usefixtures('init_project_json_data')