Merge branch 'ly-as-sdk/LYN-2948' of https://github.com/aws-lumberyard-dev/o3de into ly-as-sdk/LYN-2948

This commit is contained in:
pappeste
2021-05-27 20:13:24 -07:00
9 changed files with 192 additions and 204 deletions
+1 -1
View File
@@ -83,7 +83,7 @@ def disable_gem_in_project(gem_name: str = None,
project_path = manifest.get_registered(project_name=project_name)
if not project_path:
logger.error(f'Unable to locate project path from the registered manifest.json files:'
f' {str(pathlib.Path.home() / ".o3de/manifest.json")}, engine.json')
f' {str(pathlib.Path("~/.o3de/o3de_manifest.json").expanduser())}, engine.json')
return 1
project_path = pathlib.Path(project_path).resolve()
+1 -1
View File
@@ -117,7 +117,7 @@ def enable_gem_in_project(gem_name: str = None,
gem_path = manifest.get_registered(gem_name=gem_name)
if not gem_path:
logger.error(f'Unable to locate gem path from the registered manifest.json files:'
f' {str(pathlib.Path.home() / ".o3de/manifest.json")},'
f' {str(pathlib.Path( "~/.o3de/o3de_manifest.json").expanduser())},'
f' {project_path / "project.json"}, engine.json')
return 1
+134 -87
View File
@@ -16,94 +16,122 @@ import sys
import re
import pathlib
import json
from o3de import manifest
from o3de import manifest, validation
logger = logging.getLogger()
logging.basicConfig()
DEFAULT_BOOTSTRAP_SETREG = pathlib.Path('~/.o3de/Registry/bootstrap.setreg').expanduser()
PROJECT_PATH_KEY = ('Amazon', 'AzCore', 'Bootstrap', 'project_path')
def set_global_project(project_name: str or None,
project_path: str or pathlib.Path or None) -> int:
def get_json_data(input_path: pathlib.Path):
setreg_json_data = {}
# If the output_path exist validate that it is a valid json file
if input_path.is_file():
with input_path.open('r') as f:
try:
setreg_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.error(f'The file: {input_path} is not a valid json file: {str(e)}')
return setreg_json_data
def set_global_project(output_path: pathlib.Path,
project_name: str = None,
project_path: pathlib.Path = None,
force: bool = False) -> int:
"""
set what the current project is
:param project_name: the name of the project you want to set, resolves project_path
:param project_path: the path of the project you want to set
Adds a project path the a settings registry file in the users ~/.o3de/Registry directory
:param output_path: path to .setreg file to store project_path value into
:param project_name: name of the project to lookup path for
:param project_path: path to the project to add to .setreg file
:param force: if set, the project path will be set within the .setreg file regardless of if the path doesn't exist
:return: 0 for success or non 0 failure code
"""
if project_path and project_name:
logger.error(f'Project Name and Project Path provided, these are mutually exclusive.')
return 1
# we need either a project name or path
if not project_name and not project_path:
logger.error('Must specify either a Project name or Project Path.')
logger.error(f'Must either specify a Project path or Project Name.')
return 1
# if project name resolve it into a path
if project_name and not project_path:
project_path = manifest.get_registered(project_name=project_name)
if not project_path:
logger.error(f'Project Path {project_path} has not been registered.')
logger.error(
f'The project name has been supplied. Unable to locate project path from the registered manifest.json files:'
f' {str(pathlib.Path("~/.o3de/o3de_manifest.json").expanduser())}, engine.json\n'
'A The --project-path parameter can be used directly to skip checking the manifest')
return 1
project_path = pathlib.Path(project_path).resolve()
# Only perform project path validations when force=False
if not force:
if not project_path.is_dir():
logger.error(f'Project path {project_path} is not a folder.')
return 1
bootstrap_setreg_file = manifest.get_o3de_registry_folder() / 'bootstrap.setreg'
if bootstrap_setreg_file.is_file():
with bootstrap_setreg_file.open('r') as f:
# Validate that the supplied path points contains a valid project.json
if not validation.valid_o3de_project_json(project_path / 'project.json'):
logger.error(f'The supplied project path does not contain a valid project.json.\n'
f'The Path will not be set')
return 1
# If the output_path exist validate that it is a valid json file and read it's json data
setreg_json_data = get_json_data(output_path)
if output_path.is_file():
with output_path.open('r') as f:
try:
json_data = json.load(f)
except json.JSONDecodeError as e:
logger.error(f'Bootstrap.setreg failed to load: {str(e)}')
else:
try:
json_data["Amazon"]["AzCore"]["Bootstrap"]["project_path"] = project_path
except KeyError as e:
logger.error(f'Bootstrap.setreg failed to load: {str(e)}')
else:
try:
os.unlink(bootstrap_setreg_file)
except OSError as e:
logger.error(f'Failed to unlink bootstrap file {bootstrap_setreg_file}: {str(e)}')
return 1
else:
json_data = {}
json_data.update({"Amazon":{"AzCore":{"Bootstrap":{"project_path":project_path.as_posix()}}}})
setreg_json_data = json.load(f)
except (json.JSONDecodeError) as e:
logger.error(f'The output file: {output_path} is not a valid json file: {str(e)}')
return 1
with bootstrap_setreg_file.open('w') as s:
s.write(json.dumps(json_data, indent=4))
# Add a json dictionary that will be merged with any existing json data from the .setreg file
merge_json_data = {}
json_object_iter = merge_json_data
for json_key in PROJECT_PATH_KEY[:-1]:
# Add the parent json object for the key to update
json_object_iter = json_object_iter.setdefault(json_key, {})
# Set the project path value here
json_object_iter[PROJECT_PATH_KEY[-1]] = project_path.as_posix()
setreg_json_data.update(merge_json_data)
# Create the parent directories
if output_path.parent:
output_path.parent.mkdir(parents=True, exist_ok=True)
try:
with output_path.open('w') as s:
s.write(json.dumps(setreg_json_data, indent=4) + '\n')
except OSError as e:
logger.error(f'Failed to write project path {project_path} to file {output_path}: {str(e)}')
return 1
return 0
def get_global_project() -> pathlib.Path or None:
def get_global_project(input_path: pathlib.Path) -> pathlib.Path or None:
"""
get what the current project set is
Retrieves the /Amazon/AzCore/Bootstrap/project_path key from the supplied file path
:return: project_path or None on failure
"""
bootstrap_setreg_file = manifest.get_o3de_registry_folder() / 'bootstrap.setreg'
if not bootstrap_setreg_file.is_file():
logger.error(f'Bootstrap.setreg file {bootstrap_setreg_file} does not exist.')
return None
setreg_json_data = get_json_data(input_path)
with bootstrap_setreg_file.open('r') as f:
try:
json_data = json.load(f)
except json.JSONDecodeError as e:
logger.error(f'Bootstrap.setreg failed to load: {str(e)}')
else:
try:
project_path = json_data["Amazon"]["AzCore"]["Bootstrap"]["project_path"]
except KeyError as e:
logger.error(f'Bootstrap.setreg cannot find Amazon:AzCore:Bootstrap:project_path: {str(e)}')
else:
return pathlib.Path(project_path).resolve()
try:
# Iterate over each element of the tuple and read the json key from each successive json object
json_object_iter = setreg_json_data
for json_key in PROJECT_PATH_KEY:
json_object_iter = json_object_iter[json_key]
except KeyError as e:
logger.error(f'Cannot read key /{"/".join(PROJECT_PATH_KEY)} from file {input_path.as_posix()}: {str(e)}')
else:
project_path = json_object_iter
return pathlib.Path(project_path).resolve()
return None
def _run_get_global_project(args: argparse) -> int:
if args.override_home_folder:
manifest.override_home_folder = args.override_home_folder
project_path = get_global_project()
project_path = get_global_project(args.input_path)
if project_path:
print(project_path.as_posix())
return 0
@@ -111,51 +139,66 @@ def _run_get_global_project(args: argparse) -> int:
def _run_set_global_project(args: argparse) -> int:
if args.override_home_folder:
manifest.override_home_folder = args.override_home_folder
return set_global_project(args.output_path,
args.project_name,
args.project_path,
args.force)
return set_global_project(args.project_name,
args.project_path)
def add_parser_args(get_project_parser, set_project_parser):
"""
add_parser_args is called to add arguments to each command such that it can be
invoked locally or added by a central python file.
Ex. Directly run from this file alone with: python global_project.py --project-path "D:/TestProject"
:param parser: the caller passes an argparse parser like instance to this method
"""
# get-current-project
get_project_parser.add_argument('-i', '--input-path', type=pathlib.Path, required=False, default=DEFAULT_BOOTSTRAP_SETREG,
help=f'Optional path to file to read /{"/".join(PROJECT_PATH_KEY)} key from.'
f' If not supplied, then {DEFAULT_BOOTSTRAP_SETREG} is used instead')
get_project_parser.set_defaults(func=_run_get_global_project)
# set-current-project
group = set_project_parser.add_mutually_exclusive_group(required=True)
group.add_argument('-pp', '--project-path', type=pathlib.Path, required=False,
help='The path to the project.')
group.add_argument('-pn', '--project-name', type=str, required=False,
help='The name of the project.')
set_project_parser.add_argument('-o', '--output-path', type=pathlib.Path, required=False,
default=DEFAULT_BOOTSTRAP_SETREG,
help=f'Optional path to output file to write project_path key to. '
f'If not supplied, then {DEFAULT_BOOTSTRAP_SETREG} is used instead')
set_project_parser.add_argument('-f', '--force', action='store_true', default=False,
help=f'Force the setting of the project path in the supplied setreg file')
set_project_parser.set_defaults(func=_run_set_global_project)
def add_args(subparsers) -> None:
"""
add_args is called to add expected parser arguments and subparsers arguments to each command such that it can be
invoked locally or aggregated by a central python file.
Ex. Directly run from this file alone with: python global_project.py set_global_project --project-name TestProject
OR
o3de.py can aggregate commands by importing global_project, call add_args and
execute: python o3de.py set_global_project --project-path C:/TestProject
add_args is called to add subparsers arguments to each command such that it can be
a central python file such as o3de.py.
It can be run from the o3de.py script as follows
call add_args and execute: python o3de.py set-global-project --project-path "D:/TestProject"
:param subparsers: the caller instantiates subparsers and passes it in here
"""
get_global_project_subparser = subparsers.add_parser('get-global-project')
get_global_project_subparser.add_argument('-ohf', '--override-home-folder', type=str, required=False,
help='By default the home folder is the user folder, override it to this folder.')
get_global_project_subparser.set_defaults(func=_run_get_global_project)
set_global_project_subparser = subparsers.add_parser('set-global-project')
group = set_global_project_subparser.add_mutually_exclusive_group(required=True)
group.add_argument('-pn', '--project-name', required=False,
help='The name of the project. If supplied this will resolve the --project-path.')
group.add_argument('-pp', '--project-path', required=False,
help='The path to the project')
set_global_project_subparser.add_argument('-ohf', '--override-home-folder', type=str, required=False,
help='By default the home folder is the user folder, override it to this folder.')
set_global_project_subparser.set_defaults(func=_run_set_global_project)
get_project_subparser = subparsers.add_parser('get-global-project')
set_project_subparser = subparsers.add_parser('set-global-project')
add_parser_args(get_project_subparser, set_project_subparser)
if __name__ == "__main__":
def main():
"""
Runs this script as standalone script
"""
# parse the command line args
the_parser = argparse.ArgumentParser()
# add subparsers
the_subparsers = the_parser.add_subparsers(help='sub-command help', dest='command', required=True)
project_subparsers = the_parser.add_subparsers(help="Commands for modifying the project path in the user's home"
" setreg files")
# add args to the parser
add_args(the_subparsers)
add_args(project_subparsers)
# parse args
the_args = the_parser.parse_args()
@@ -165,3 +208,7 @@ if __name__ == "__main__":
# return
sys.exit(ret)
if __name__ == "__main__":
main()
+6 -6
View File
@@ -142,7 +142,7 @@ def get_o3de_manifest() -> pathlib.Path:
with default_restricted_folder_json.open('w') as s:
restricted_json_data = {}
restricted_json_data.update({'restricted_name': 'o3de'})
s.write(json.dumps(restricted_json_data, indent=4))
s.write(json.dumps(restricted_json_data, indent=4) + '\n')
json_data.update({'default_restricted_folder': default_restricted_folder.as_posix()})
default_projects_restricted_folder_json = default_projects_restricted_folder / 'restricted.json'
@@ -150,24 +150,24 @@ def get_o3de_manifest() -> pathlib.Path:
with default_projects_restricted_folder_json.open('w') as s:
restricted_json_data = {}
restricted_json_data.update({'restricted_name': 'projects'})
s.write(json.dumps(restricted_json_data, indent=4))
s.write(json.dumps(restricted_json_data, indent=4) + '\n')
default_gems_restricted_folder_json = default_gems_restricted_folder / 'restricted.json'
if not default_gems_restricted_folder_json.is_file():
with default_gems_restricted_folder_json.open('w') as s:
restricted_json_data = {}
restricted_json_data.update({'restricted_name': 'gems'})
s.write(json.dumps(restricted_json_data, indent=4))
s.write(json.dumps(restricted_json_data, indent=4) + '\n')
default_templates_restricted_folder_json = default_templates_restricted_folder / 'restricted.json'
if not default_templates_restricted_folder_json.is_file():
with default_templates_restricted_folder_json.open('w') as s:
restricted_json_data = {}
restricted_json_data.update({'restricted_name': 'templates'})
s.write(json.dumps(restricted_json_data, indent=4))
s.write(json.dumps(restricted_json_data, indent=4) + '\n')
with manifest_path.open('w') as s:
s.write(json.dumps(json_data, indent=4))
s.write(json.dumps(json_data, indent=4) + '\n')
return manifest_path
@@ -201,7 +201,7 @@ def save_o3de_manifest(json_data: dict, manifest_path: pathlib.Path = None) -> N
manifest_path = get_o3de_manifest()
with manifest_path.open('w') as s:
try:
s.write(json.dumps(json_data, indent=4))
s.write(json.dumps(json_data, indent=4) + '\n')
except OSError as e:
logger.error(f'Manifest json failed to save: {str(e)}')
+2 -6
View File
@@ -410,12 +410,8 @@ def register_project_path(json_data: dict,
if update_project_json:
project_json_data['engine'] = this_engine_json['engine_name']
utils.backup_file(project_json)
with project_json.open('w') as s:
try:
s.write(json.dumps(project_json_data, indent=4))
except OSError as e:
logger.error(f'Project json failed to save: {str(e)}')
return 1
if not manifest.save_o3de_manifest(project_json_data, project_path):
return 1
return 0
+1 -1
View File
@@ -51,7 +51,7 @@ def sha256(file_path: str or pathlib.Path,
utils.backup_file(json_path)
with json_path.open('w') as s:
try:
s.write(json.dumps(json_data, indent=4))
s.write(json.dumps(json_data, indent=4) + '\n')
except OSError as e:
logger.error(f'Failed to write Json path {json_path}: {str(e)}')
return 1
+7
View File
@@ -27,3 +27,10 @@ ly_add_pytest(
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
TEST_SUITE smoke
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
)
+40
View File
@@ -0,0 +1,40 @@
#
# 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 io
import json
import logging
import pytest
import pathlib
from unittest.mock import patch
from o3de import global_project
logger = logging.getLogger()
logging.basicConfig()
DEFAULT_BOOTSTRAP_SETREG = pathlib.Path('~/.o3de/Registry/bootstrap.setreg').expanduser()
PROJECT_PATH_KEY = ('Amazon', 'AzCore', 'Bootstrap', 'project_path')
class TestSetGlobalProject:
@pytest.mark.parametrize(
"output_path, project_path, force, expected_result", [
pytest.param(pathlib.Path('~/.o3de/Registry/bootstrap.setreg'), pathlib.Path('A:/'), False, False),
pytest.param(pathlib.Path('~/.o3de/Registry/bootstrap.setreg'), pathlib.Path('A:/'), True, True)
]
)
def test_set_global_project_non_existent_project_path(self, output_path, project_path, force, expected_result):
with patch('pathlib.Path.open', return_value=io.StringIO()) as pathlib_open_mock:
result = global_project.set_global_project(output_path, project_path=project_path, force=force) == 0
assert result == expected_result
@@ -1,102 +0,0 @@
#
# 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 pytest
from . import current_project
TEST_BOOTSTRAP_CONTENT_1 = """
project_path = Game1
foo = bar
key1 = value1
key2 = value2
assets = pc
"""
TEST_BOOTSTRAP_CONTENT_2 = """
project_path=Game1
foo = bar
key1 = value1
key2 = value2
assets = pc
"""
TEST_BOOTSTRAP_CONTENT_3 = """
project_path= Game1
foo = bar
key1 = value1
key2 = value2
assets = pc
"""
TEST_BOOTSTRAP_CONTENT_4 = """
project_path =Game1
foo = bar
key1 = value1
key2 = value2
assets = pc
"""
TEST_BOOTSTRAP_CONTENT_5 = """
project_path = Game1
foo = bar
key1 = value1
key2 = value2
assets = pc
"""
@pytest.mark.parametrize(
"contents, expected_result", [
pytest.param(TEST_BOOTSTRAP_CONTENT_1, 'Game1'),
pytest.param(TEST_BOOTSTRAP_CONTENT_2, 'Game1'),
pytest.param(TEST_BOOTSTRAP_CONTENT_3, 'Game1'),
pytest.param(TEST_BOOTSTRAP_CONTENT_4, 'Game1'),
pytest.param(TEST_BOOTSTRAP_CONTENT_5, 'Game1'),
]
)
def test_get_current_project(tmpdir, contents, expected_result):
dev_root = str(tmpdir.join('dev').realpath()).replace('\\', '/')
os.makedirs(dev_root, exist_ok=True)
bootstrap_file = f'{dev_root}/bootstrap.cfg'
if os.path.isfile(bootstrap_file):
os.unlink(bootstrap_file)
with open(bootstrap_file, 'a') as s:
s.write(contents)
result = current_project.get_current_project(dev_root)
assert expected_result == result
@pytest.mark.parametrize(
"contents, project_to_set, expected_result", [
pytest.param(TEST_BOOTSTRAP_CONTENT_1, 'Test1', 0),
pytest.param(TEST_BOOTSTRAP_CONTENT_1, ' Test2', 0),
pytest.param(TEST_BOOTSTRAP_CONTENT_1, 'Test3 ', 0),
pytest.param(TEST_BOOTSTRAP_CONTENT_1, '/Test4', 1),
pytest.param(TEST_BOOTSTRAP_CONTENT_1, '=Test5', 1),
]
)
def test_set_current_project(tmpdir, contents, project_to_set, expected_result):
dev_root = str(tmpdir.join('dev').realpath()).replace('\\', '/')
os.makedirs(dev_root, exist_ok=True)
bootstrap_file = f'{dev_root}/bootstrap.cfg'
if os.path.isfile(bootstrap_file):
os.unlink(bootstrap_file)
with open(bootstrap_file, 'a') as s:
s.write(contents)
result = current_project.set_current_project(dev_root, project_to_set)
assert expected_result == result
if result == 0:
project_that_is_set = current_project.get_current_project(dev_root)
print(project_that_is_set)
print(project_to_set)
assert project_to_set.strip() == project_that_is_set