Integrating github/staging through commit ab87ed9

This commit is contained in:
alexpete
2021-04-09 11:27:37 -07:00
parent ae62a97894
commit 1044dc3da1
1582 changed files with 29374 additions and 519051 deletions
@@ -12,33 +12,31 @@ Utility class to resolve Lumberyard directory paths & file mappings.
"""
import os
import pathlib
import warnings
from abc import ABCMeta, abstractmethod
from ly_test_tools.lumberyard.asset_processor import ASSET_PROCESSOR_PLATFORM_MAP
from ly_test_tools.environment.file_system import find_ancestor_file
def _find_engine_root(initial_path):
# type: (str) -> tuple
# type: (str) -> str
"""
Attempts to find the root engine directory to set the values for engine_root and dev_path.
Attempts to find the root engine directory to set the values for engine_root
Assumes it exists at or above the provided "initial_path", and not in a separate directory tree
ex. for the directory "C:\\root_dir\\dev\\":
"C:\\root_dir\\" is the engine_root
"C:\\root_dir\\dev\\" is the dev_path
ex. for the directory "C:\\root_dir\\":
If it contains an "engine.json" file then it is the engine root "C:\\root_dir\\"
:param initial_path: The initial directory to search for root from
:return: a tuple of 2 strings representing the engine_root and dev_path
:return: a string representing the engine_root
"""
root_file = "engineroot.txt"
root_file = "engine.json"
current_dir = initial_path
# Look upward a handful of levels, before assuming a missing root directory
# Assumes folder structure similar to: engine_root/dev/Tools/.../ly_test_tools/builtin
for _ in range(15):
if os.path.exists(os.path.join(current_dir, root_file)):
# The parent of the directory containing the engineroot.txt is the root directory
engine_root = os.path.abspath(os.path.join(current_dir, os.path.pardir))
dev_path = current_dir
return engine_root, dev_path
engine_root = current_dir
return engine_root
# Using an explicit else to avoid aberrant behavior from following filesystem links
else:
current_dir = os.path.abspath(os.path.join(current_dir, os.path.pardir))
@@ -46,6 +44,19 @@ def _find_engine_root(initial_path):
raise OSError(f"Unable to find engine root directory. Verify root file '{root_file}' exists")
def _find_project_json(engine_root, project):
# type (None) -> str
"""
Find the project.json file for this project.
:return: Full path to the project.json file
"""
project_json = find_ancestor_file('project.json')
if not project_json:
project_json = os.path.join(engine_root, project, 'project.json')
return project_json
class AbstractResourceLocator(object):
__metaclass__ = ABCMeta
@@ -53,13 +64,14 @@ class AbstractResourceLocator(object):
# type: (str, str) -> AbstractResourceLocator
"""
:param build_directory: The path to the build directory (i.e. <engine_root>/dev/windows_vs2017/bin/profile)
:param project: The game project (i.e. AutomatedTesting or StarterGame)
:param project: The game project (i.e. AutomatedTesting)
"""
engine_root, dev_path = _find_engine_root(os.path.abspath(__file__))
initial_search_path = str(pathlib.Path(__file__).resolve()) # __file__ is lowercase, this restores casing
engine_root = _find_engine_root(initial_search_path)
self._build_directory = build_directory
self._engine_root = engine_root
self._dev_path = dev_path
self._project_json = _find_project_json(engine_root, project)
self._project = project
self._cache_override = None
self._db_override = None
@@ -74,14 +86,6 @@ class AbstractResourceLocator(object):
"""
return self._engine_root
def dev(self):
"""
Returns the path to the dev directory
ex. <engine_root>\\dev
:return: dev_path
"""
return self._dev_path
def third_party(self):
"""
Return path to 3rdParty directory
@@ -118,11 +122,18 @@ class AbstractResourceLocator(object):
def project(self):
"""
Return path to the project directory
ex. engine_root/dev/AutomatedTesting
:return: path to <engine_root>/dev/Project
ex. engine_root/dev/AutomatedTesting for included projects or some_dir/project for external projects.
:return: path to the project directory
"""
return os.path.join(self.dev(), self._project)
return os.path.dirname(self._project_json)
def project_settings(self):
"""
Return full path to the project.json file for this project.
:return: Full path to the project.json file for this project.
"""
return self._project_json
def asset_processor(self):
"""
Return path for the AssetProcessor executable.
@@ -210,7 +221,6 @@ class AbstractResourceLocator(object):
"""
return os.path.join(self.ap_log_dir(), 'JobLogs')
def ap_batch_log(self):
"""
Return path to AssetProcessorBatch's log file using the project bin dir
@@ -241,10 +251,10 @@ class AbstractResourceLocator(object):
return os.path.join(self.build_directory(), 'CrySCompileServer')
def bootstrap_config_file(self):
return os.path.join(self.dev(), 'bootstrap.cfg')
return os.path.join(self.engine_root(), 'bootstrap.cfg')
def asset_processor_config_file(self):
return os.path.join(self.dev(), 'AssetProcessorPlatformConfig.setreg')
return os.path.join(self.engine_root(), 'AssetProcessorPlatformConfig.setreg')
def autoexec_file(self):
return os.path.join(
@@ -257,7 +267,7 @@ class AbstractResourceLocator(object):
Return the path to the TestResults directory containing test artifacts.
:return: path to TestResults dir
"""
return os.path.join(self.dev(), "TestResults")
return os.path.join(self.engine_root(), "TestResults")
def devices_file(self):
"""
@@ -36,7 +36,7 @@ class _MacResourceLocator(AbstractResourceLocator):
ex. engine_root/dev/system_osx_osx_gl.cfg
:return: path to the platform config file
"""
return os.path.join(self.dev(), CONFIG_FILE)
return os.path.join(self.engine_root(), CONFIG_FILE)
def platform_cache(self):
"""
@@ -42,7 +42,7 @@ class _WindowsResourceLocator(AbstractResourceLocator):
ex. engine_root/dev/system_osx_osx_gl.cfg
:return: path to the platform config file
"""
return os.path.join(self.dev(), CONFIG_FILE)
return os.path.join(self.engine_root(), CONFIG_FILE)
def platform_cache(self):
"""
@@ -62,33 +62,6 @@ def create_builtin_workspace(
return instance
def setup_bootstrap_project(workspace, project):
"""
Sets up the bootstrap.cfg file to be used for the given project
:param workspace: workspace to use
:param project: Lumberyard project to set as target
:return: None
"""
bootstrap_cfg = os.path.join(workspace.paths.dev(), "bootstrap.cfg")
os.chmod(bootstrap_cfg, stat.S_IWRITE)
lines = None
with open(bootstrap_cfg) as f:
lines = f.readlines()
found_gamefolder = False
for i, line in enumerate(lines):
if line.lstrip().startswith("project_path"):
lines[i] = f"project_path={project}\n"
found_gamefolder = True
break
assert found_gamefolder, "'project_path' not found in bootstrap.cfg"
with open(bootstrap_cfg, "w") as f:
f.writelines(lines)
def setup_builtin_workspace(workspace, test_name, artifact_folder_count):
# type: (internal_workspace.AbstractWorkspaceManager, str, int) -> internal_workspace.AbstractWorkspaceManager
"""
@@ -420,3 +420,34 @@ def reduce_file_name_length(file_name, max_length):
file_name = file_name[:-reduce_amount]
return file_name
def find_ancestor_file(target_file_name, start_path=os.getcwd()):
"""
Find a file with the given name in the ancestor directories by walking up the starting path until the file is found.
:param target_file_name: Name of the file to find.
:param start_path: Optional path to start looking for the file.
:return: Path to the file or None if not found.
"""
current_path = os.path.normpath(start_path)
candidate_path = os.path.join(current_path, target_file_name)
# Limit the number of directories to traverse, to avoid infinite loop in path cycles
for _ in range(15):
if not os.path.exists(candidate_path):
parent_path = os.path.dirname(current_path)
if parent_path == current_path:
# Only true when we are at the directory root, can't keep searching
break
candidate_path = os.path.join(parent_path, target_file_name)
current_path = parent_path
else:
# Found the file we wanted
break
if not os.path.exists(candidate_path):
logger.warning(f'The candidate path {candidate_path} does not exist.')
return None
return candidate_path
@@ -122,7 +122,7 @@ class AndroidLauncher(Launcher):
self._device_id = None
self.launch_proc = None
self.android_vfs_setreg_path = None
self.package_name = get_package_name(os.path.join(self.workspace.paths.dev(),
self.package_name = get_package_name(os.path.join(self.workspace.paths.engine_root(),
self.workspace.project))
self._device_id = self.get_device_config(config_file=self.workspace.paths.devices_file(),
device_section='android',
@@ -165,7 +165,7 @@ class WinLauncher(Launcher):
"""
# Update settings via the settings registry to avoid modifying the bootstrap.cfg
host_ip = '127.0.0.1'
self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/project_path={self.workspace.project}"')
self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/project_path={self.workspace.paths.project()}"')
self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/remote_ip={host_ip}"')
self.args.append('--regset="/Amazon/AzCore/Bootstrap/wait_for_connect=1"')
self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/allowed_list={host_ip}"')
@@ -17,20 +17,14 @@ import logging
import subprocess
import socket
import time
import itertools
from timeit import default_timer
import pytest
import platform
import random
import tempfile
import shutil
import stat
from typing import Dict, List, Tuple, Optional, Callable
from typing import List
import psutil
import ly_test_tools
import ly_test_tools.environment.waiter as waiter
import ly_test_tools.environment.process_utils as process_utils
import ly_test_tools.environment.file_system as file_system
import ly_test_tools.lumberyard.pipeline_utils as utils
from ly_test_tools.lumberyard.ap_log_parser import APLogParser
@@ -49,7 +43,7 @@ ASSET_PROCESSOR_PLATFORM_MAP = {
'windows': 'pc',
}
ASSET_PROCESSOR_SETTINGS_ROOT_KEY='/Amazon/AssetProcessor/Settings'
ASSET_PROCESSOR_SETTINGS_ROOT_KEY = '/Amazon/AssetProcessor/Settings'
class AssetProcessorError(Exception):
""" Indicates that the AssetProcessor raised an error """
@@ -70,7 +64,7 @@ class AssetProcessor(object):
self._ap_proc = None
self._temp_asset_directory = None
self._temp_asset_root = None
self._project_path = self._workspace.project
self._project_path = self._workspace.paths.project()
self._override_scan_folders = []
self._test_assets_source_folder = None
self._cache_folder = None
@@ -136,7 +130,6 @@ class AssetProcessor(object):
try:
self._control_connection.sendall(message.encode())
logger.info(f"Sent input {message}")
return True
except IOError as e:
@@ -485,8 +478,7 @@ class AssetProcessor(object):
logger.warning(f"Cannot capture output when leaving AP connection open.")
logger.info(f"Launching AP with command: {command}")
self._ap_proc = subprocess.Popen(command,
cwd=ap_exe_path)
self._ap_proc = subprocess.Popen(command, cwd=ap_exe_path)
if accept_input and not quitonidle:
self.connect_control()
@@ -668,10 +660,11 @@ class AssetProcessor(object):
make_dir = os.path.join(self._temp_asset_root, copy_dir)
if not os.path.isdir(make_dir):
os.makedirs(make_dir)
for copyfile_name in ['bootstrap.cfg', 'AssetProcessorPlatformConfig.setreg',
for copyfile_name in ['bootstrap.cfg',
'AssetProcessorPlatformConfig.setreg',
os.path.join(self._workspace.project, "project.json"),
os.path.join('Engine', 'exclude.filetag')]:
shutil.copyfile(os.path.join(self._workspace.paths.dev(), copyfile_name),
shutil.copyfile(os.path.join(self._workspace.paths.engine_root(), copyfile_name),
os.path.join(self._temp_asset_root, copyfile_name))
def delete_temp_asset_root(self):
@@ -756,7 +749,7 @@ class AssetProcessor(object):
if not self._temp_asset_root:
logger.warning(f"Can't create scan folder, no temporary asset workspace has been created")
return
scan_folder = os.path.join(self._temp_asset_root if self._temp_asset_root else self._workspace.paths.dev(),
scan_folder = os.path.join(self._temp_asset_root if self._temp_asset_root else self._workspace.paths.engine_root(),
folder_name)
if not os.path.isdir(scan_folder):
os.makedirs(scan_folder)
@@ -901,7 +894,7 @@ class AssetProcessor(object):
asset root if not supplied
:return: None
"""
source_root = source_root or self._workspace.paths.dev()
source_root = source_root or self._workspace.paths.engine_root()
if not self._temp_asset_root:
logger.warning(f"Can't add relative source asset, no temporary asset root created")
return
@@ -966,7 +959,7 @@ class AssetProcessor(object):
supplied
:return: path to project cache
"""
source_folder = os.path.join(self._workspace.paths.dev(), relative_source)
source_folder = os.path.join(self._workspace.paths.engine_root(), relative_source)
dest_relative = relative_dest or relative_source
dest_folder = os.path.join(self._temp_asset_root, dest_relative)
shutil.copytree(source_folder, dest_folder)
@@ -13,38 +13,14 @@ AssetProcessorPlatformConfig.setreg
"""
import json
import logging
import os.path as path
from ly_test_tools.lumberyard.settings import RegistrySettings
from ly_test_tools.lumberyard.asset_processor import ASSET_PROCESSOR_SETTINGS_ROOT_KEY
logger = logging.getLogger(__name__)
AssetProcessorConfig = "AssetProcessorPlatformConfig.setreg"
def load_asset_processor_platform_config(file_location):
"""
Loads the AssetProcessorPlatformConfig.setreg file removing any comments
:param file_location: The file path of AssetProcessorPlatformConfig.setreg
"""
json_dict = {}
with open(file_location, 'r') as json_file:
cleaned_lines = []
for line in json_file.readlines():
lineIndex = line.lstrip().startswith('//')
cleaned_lines.append(line if lineIndex == -1 else line[:lineIndex])
json_dict = json.loads(''.join(cleaned_lines))
return json_dict
def save_asset_processor_platform_config(file_location, json_dict):
"""
Saves the json_dict to the AssetProcessorPlatformConfig.setreg file
:param file_location: The file path of AssetProcessorPlatformConfig.setreg
"""
with open(file_location, 'w') as json_file:
json.dump(json_dict, json_file, indent=4)
return json_dict
def platform_exists(config_setreg_path, platform):
"""
Checks to see if a specific Platform Key is in Platforms Section
@@ -53,19 +29,15 @@ def platform_exists(config_setreg_path, platform):
:param platform: Name of the Platform Key that you're checking exists
:return: The boolean value of the existance of the platform
"""
logger.debug("Checking for Platform '{0}' in Platforms Section of '{1}"
.format(platform, AssetProcessorConfig))
file_location = path.join(config_setreg_path, AssetProcessorConfig)
json_dict = load_asset_processor_platform_config(file_location)
try:
platformExist = platform in json_dict['Amazon']['AssetProcessor']['Settings']['Platforms']
return platformExist
except KeyError as err:
logger.error(f'KeyError when attempting to query platform existance of {platform} in file {file_location}: {err}')
return False
with RegistrySettings(file_location) as settings:
platformExist = settings.get_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/Platforms/{platform}', False)
return platformExist
def is_platform_enabled(config_setreg_path, platform):
@@ -77,20 +49,15 @@ def is_platform_enabled(config_setreg_path, platform):
See asset_processor.SUPPORTED_PLATFORMS for listed of supported platforms
:return: The boolean value of the enabled state of the platform
"""
logger.debug("Checking if Platform '{0}' is enabled in Platform Section of '{1}"
.format(platform, AssetProcessorConfig))
file_location = path.join(config_setreg_path, AssetProcessorConfig)
# load AssetProcessorPlatformConfig.setreg removing any comments
json_dict = load_asset_processor_platform_config(file_location)
try:
enabled = json_dict['Amazon']['AssetProcessor']['Settings']['Platforms'][platform] == 'enabled'
return enabled
except KeyError as err:
logger.error(f'KeyError when attempting to check if platform {platform} is enabled in file {file_location}: {err}')
return False
with RegistrySettings(file_location) as settings:
enabled = settings.get_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/Platforms/{platform}', None) == 'enabled'
return enabled
def enable_platform(config_setreg_path, platform):
@@ -103,15 +70,11 @@ def enable_platform(config_setreg_path, platform):
:assert: Assert if the platform is not enabled
:return: None
"""
logger.debug("Enabling platform '{0}' in '{1}'".format(platform, AssetProcessorConfig))
file_location = path.join(config_setreg_path, AssetProcessorConfig)
json_dict = load_asset_processor_platform_config(file_location)
# Enable platform in settings registry
json_dict.setdefault('Amazon', {}).setdefault('AssetProcessor', {}).setdefault('Settings',{}) \
.setdefault('Platforms',{})[platform] = 'enabled'
save_asset_processor_platform_config(file_location)
with RegistrySettings(file_location) as settings:
settings.set_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/Platforms/{platform}', 'enabled')
def enable_all_platforms(config_setreg_path):
@@ -121,7 +84,6 @@ def enable_all_platforms(config_setreg_path):
:param config_setreg_path: The file path to the location of AssetProcessorPlatformConfig.setreg
:return: None
"""
logger.debug("Enabling all supported platforms in '{0}'.".format(AssetProcessorConfig))
for platform in SUPPORTED_PLATFORMS:
@@ -140,15 +102,13 @@ def disable_platform(config_setreg_path, platform):
:assert: Assert if the platform is not disabled
:return: None
"""
logger.debug("Disabling platform '{0}' in '{1}'".format(platform, AssetProcessorConfig))
file_location = path.join(config_setreg_path, AssetProcessorConfig)
# Disable platform in settings registry
json_dict = load_asset_processor_platform_config(file_location)
json_dict.setdefault('Amazon', {}).setdefault('AssetProcessor', {}).setdefault('Settings',{}) \
.setdefault('Platforms',{})[platform] = 'disabled'
save_asset_processor_platform_config(file_location)
file_location = path.join(config_setreg_path, AssetProcessorConfig)
with RegistrySettings(file_location) as settings:
settings.set_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/Platforms/{platform}', 'disabled')
def disable_all_platforms(config_setreg_path):
@@ -158,7 +118,6 @@ def disable_all_platforms(config_setreg_path):
:param config_setreg_path: The file path to the location of AssetProcessorPlatformConfig.setreg
:return: None
"""
logger.debug("Disabling all platforms in '{0}'".format(AssetProcessorConfig))
for platform in SUPPORTED_PLATFORMS:
@@ -174,18 +133,15 @@ def enable_scanfolder_engine(config_setreg_path):
:param config_setreg_path: The file path to the location of AssetProcessorPlatformConfig.setreg
:return: None
"""
file_location = path.join(config_setreg_path, AssetProcessorConfig)
section = 'ScanFolder Engine'
logger.debug("Enabling Asset Processor scanning of the Engine folder")
json_dict = load_asset_processor_platform_config(file_location)
json_dict.setdefault('Amazon', {}).setdefault('AssetProcessor', {}).setdefault('Settings',{}) \
.setdefault(section,{})['watch'] = '@ENGINEROOT@/Engine'
json_dict['Amazon']['AssetProcessor']['Settings'][section]['recursive'] = '1'
json_dict['Amazon']['AssetProcessor']['Settings'][section]['order'] = '20000'
save_asset_processor_platform_config(file_location)
with RegistrySettings(file_location) as settings:
settings.set_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{section}/watch', '@ENGINEROOT@/Engine')
settings.set_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{section}/recursive', '1')
settings.set_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{section}/order', '20000')
def disable_scanfolder_engine(config_setreg_path):
@@ -195,18 +151,12 @@ def disable_scanfolder_engine(config_setreg_path):
:param config_setreg_path: The file path to the location of AssetProcessorPlatformConfig.setreg
:return: None
"""
file_location = path.join(config_setreg_path, AssetProcessorConfig)
section = 'ScanFolder Engine'
logger.debug("Disabling Asset Processor scanning of the Engine folder")
json_dict = load_asset_processor_platform_config(file_location)
try:
del json_dict['Amazon']['AssetProcessor']['Settings'][section]
save_asset_processor_platform_config(file_location)
except KeyError as err:
logger.debug(f'No-op: f{section} key does not exist in file {file_location}')
with RegistrySettings(file_location) as settings:
settings.remove_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{section}')
def enable_scanfolder_editor(config_setreg_path):
@@ -216,21 +166,17 @@ def enable_scanfolder_editor(config_setreg_path):
:param config_setreg_path: The file path to the location of AssetProcessorPlatformConfig.setreg
:return: None
"""
file_location = path.join(config_setreg_path, AssetProcessorConfig)
section = 'ScanFolder Editor'
logger.debug("Enabling Asset Processor scanning of the Editor folder")
json_dict = load_asset_processor_platform_config(file_location)
json_dict.setdefault('Amazon', {}).setdefault('AssetProcessor', {}).setdefault('Settings',{}) \
.setdefault(section,{})['watch'] = '@ENGINEROOT@/Editor'
json_dict['Amazon']['AssetProcessor']['Settings'][section]['output'] = 'editor'
json_dict['Amazon']['AssetProcessor']['Settings'][section]['recursive'] = '1'
json_dict['Amazon']['AssetProcessor']['Settings'][section]['order'] = '30000'
json_dict['Amazon']['AssetProcessor']['Settings'][section]['include'] = 'tools,renderer'
save_asset_processor_platform_config(file_location)
with RegistrySettings(file_location) as settings:
settings.set_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{section}/watch', '@ENGINEROOT@/Editor')
settings.set_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{section}/output', 'editor')
settings.set_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{section}/recursive', '1')
settings.set_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{section}/order', '30000')
settings.set_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{section}/include', 'tools,renderer')
def disable_scanfolder_editor(config_setreg_path):
@@ -240,17 +186,13 @@ def disable_scanfolder_editor(config_setreg_path):
:param config_setreg_path: The file path to the location of AssetProcessorPlatformConfig.setreg
:return: None
"""
file_location = path.join(config_setreg_path, AssetProcessorConfig)
section = 'ScanFolder Editor'
logger.debug("Disabling Asset Processor scanning of the Editor folder")
try:
del json_dict['Amazon']['AssetProcessor']['Settings'][section]
save_asset_processor_platform_config(file_location)
except KeyError as err:
logger.debug(f'No-op: f{section} key does not exist in file {file_location}')
with RegistrySettings(file_location) as settings:
settings.remove_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{section}')
def enable_scanfolder_root(config_setreg_path):
@@ -260,18 +202,15 @@ def enable_scanfolder_root(config_setreg_path):
:param config_setreg_path: The file path to the location of AssetProcessorPlatformConfig.setreg
:return: None
"""
file_location = path.join(config_setreg_path, AssetProcessorConfig)
section = 'ScanFolder Root'
logger.debug("Enabling Asset Processor scanning of the Root folder")
json_dict = load_asset_processor_platform_config(file_location)
json_dict.setdefault('Amazon', {}).setdefault('AssetProcessor', {}).setdefault('Settings',{}) \
.setdefault(section,{})['watch'] = '@ROOT@'
json_dict['Amazon']['AssetProcessor']['Settings'][section]['recursive'] = '0'
json_dict['Amazon']['AssetProcessor']['Settings'][section]['order'] = '10000'
save_asset_processor_platform_config(file_location)
with RegistrySettings(file_location) as settings:
settings.set_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{section}/watch', '@ROOT@')
settings.set_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{section}/recursive', '0')
settings.set_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{section}/order', '10000')
def disable_scanfolder_root(config_setreg_path):
@@ -281,17 +220,13 @@ def disable_scanfolder_root(config_setreg_path):
:param config_setreg_path: The file path to the location of AssetProcessorPlatformConfig.setreg
:return: None
"""
file_location = path.join(config_setreg_path, AssetProcessorConfig)
section = 'ScanFolder Root'
logger.debug("Disabling Asset Processor scanning of the Root folder")
try:
del json_dict['Amazon']['AssetProcessor']['Settings'][section]
save_asset_processor_platform_config(file_location)
except KeyError as err:
logger.debug(f'No-op: f{section} key does not exist in file {file_location}')
with RegistrySettings(file_location) as settings:
settings.remove_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{section}')
def update_pattern(config_setreg_path, pattern, key, value):
@@ -304,15 +239,12 @@ def update_pattern(config_setreg_path, pattern, key, value):
:param value: The value to set the key to
:return: None
"""
file_location = path.join(config_setreg_path, AssetProcessorConfig)
rc_pattern = 'RC ' + pattern
logger.debug("Modifying pattern for '{0}'.".format(rc_pattern))
json_dict = load_asset_processor_platform_config(file_location)
json_dict.setdefault('Amazon', {}).setdefault('AssetProcessor', {}).setdefault('Settings',{}) \
.setdefault(rc_pattern,{})[key] = value
save_asset_processor_platform_config(file_location)
with RegistrySettings(file_location) as settings:
settings.set_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{rc_pattern}/{key}', value)
def get_pattern_key(config_setreg_path, pattern, key):
@@ -328,11 +260,7 @@ def get_pattern_key(config_setreg_path, pattern, key):
file_location = path.join(config_setreg_path, AssetProcessorConfig)
rc_pattern = 'RC ' + pattern
logger.debug("Retrieving the key '{0}' from pattern '{1}'.".format(key, rc_pattern))
json_dict = load_asset_processor_platform_config(file_location)
try:
value = json_dict['Amazon']['AssetProcessor']['Settings'][rc_pattern][key]
return value
except KeyError as err:
logger.error(f'KeyError attempting to query value for key "/Amazon/AssetProcessor/Settings/{rc_pattern}/{key}" in file {file_location}: {err}')
with RegistrySettings(file_location) as settings:
value = settings.get_key(f'{ASSET_PROCESSOR_SETTINGS_ROOT_KEY}/{rc_pattern}/{key}', None)
return None
return value
@@ -215,43 +215,6 @@ def backup_asset_processor_logs(bin_directory: str, backup_directory: str) -> No
shutil.copytree(ap_logs, destination)
def platform_enabled(workspace: pytest.fixture, platform: str) -> bool:
"""
Checks to see if the platform specified is enabled for the current build of LY.
:param workspace: The current testing workspace
:param platform: The name of the platform to lookup.
:return: True if the platform is enabled, False if not.
"""
user_settings_file = os.path.join(workspace.paths.dev(), "_WAF_", "user_settings.options")
assert os.path.exists(user_settings_file), f"User settings file not found at {user_settings_file}"
parser = ConfigParser()
parser.read(user_settings_file)
section = platform.title() + " Options" # The Platform Options section
option = "enable_" + platform.lower()
# Make sure the platform has an options section
assert parser.has_section(section), f"Section {section} was not found in {user_settings_file}"
if parser.has_option(section, option):
entry = parser.get(section, option)
if entry.lower() == "true":
# Found 'true'
return True
elif entry.lower() == "false":
# Found 'false'
return False
else:
# Found something unexpected
# fmt:off
logger.warning(f"Found unexpected value '{entry}' in {user_settings_file} - {section}:{option}. "
f"Using default value of 'False'")
# fmt:on
else:
logger.info(f"No option '{option}' was found in the section '{section}', defaulting to 'False'")
return False
def safe_subprocess(command: str or List[str], **kwargs: Dict) -> ProcessOutput:
"""
Forwards arguments to subprocess.Popen to have a processes output
@@ -12,6 +12,7 @@ LySettings provides an API for modifying settings files and creating/restoring b
"""
import fileinput
import json
import logging
import re
import os
@@ -89,6 +90,24 @@ class LySettings(object):
def restore_shader_compiler_settings(self, backup_path=None):
self._restore_settings(self._resource_locator.shader_compiler_config_file(), backup_path)
def backup_json_settings(self, json_settings_file, backup_path=None):
"""
Creates a backup of a Json/Registry Settings file in the backup_path. If no path is provided, it will store in
the workspace temp path (the contents of the workspace temp directory are removed during workspace teardown)
:param json_settings_file:
:param backup_path:
:return:
"""
self._backup_settings(json_settings_file, backup_path)
def restore_json_settings(self, json_settings_file, backup_path=None):
"""
Restores a Json/Registry settings file from its backup.
The backup is stored in the backup_path.
If no backup_path is provided, it will attempt to retrieve the backup from the workspace temp path.
"""
self._restore_settings(json_settings_file, backup_path)
def _backup_settings(self, settings_file, backup_path):
"""
Creates a backup of the settings file in the backup_path. If no path is
@@ -109,6 +128,136 @@ class LySettings(object):
ly_test_tools.environment.file_system.restore_backup(settings_file, backup_path)
class JsonSettings(object):
"""
JsonSettings provides an API for reading, modifying and writing Json settings files.
Can be used as a Context Manager.
"""
def __init__(self, file_path, line_clean_func=None):
self._data = None
self._file_path = file_path
self._line_clean_func = line_clean_func
self._modified = False
self._path_separator = '/'
def __enter__(self):
self.load()
return self
def __exit__(self, exception_type, exception_value, traceback):
if self._modified:
self.dump()
def _get_path_tokens(self, key_path):
"""
Splits the path using the separator and returns a list of tokens.
The first empty token is skipped, since it points to the root of the document.
:param key_path: Path to the key
:return: List of path tokens
"""
path_tokens = key_path.split(self._path_separator)
# Skip first empty token, it points to the root of the document
path_tokens = path_tokens[1:]
return path_tokens
def load(self):
"""
Load the Json file into memory
:return: None
"""
with open(self._file_path, 'r') as contents:
if not self._line_clean_func:
self._data = json.load(contents)
else:
cleaned_lines = []
for line in contents:
clean_line = self._line_clean_func(line)
cleaned_lines.append(clean_line)
self._data = json.loads(cleaned_lines)
def dump(self):
"""
Write the content into the Json file
:return: None
"""
with open(self._file_path, 'w') as out_file:
json.dump(self._data, out_file, indent=4)
def get_key(self, key_path, default_value=None):
"""
Retrieve a key by path from the Json
:param key_path: Path to the key
:param default_value: Default value to return if the key is not found
:return: Value of the key or default_value if not found
"""
path_tokens = self._get_path_tokens(key_path)
obj = self._data
try:
for token in path_tokens:
obj = obj[token]
return obj
except KeyError as err:
logger.error(f'KeyError when retrieving {key_path} from file {self._file_path}: {err}')
return default_value
def set_key(self, key_path, value):
"""
Set a key to a value
:param key_path: Path to the key
:param value: The value
:return: None
"""
path_tokens = self._get_path_tokens(key_path)
obj = self._data
try:
for token in path_tokens[0:-1]:
obj = obj.setdefault(token, {})
obj[path_tokens[-1]] = value
self._modified = True
except KeyError as err:
logger.error(f'KeyError when setting {key_path} from file {self._file_path}: {err}')
def remove_key(self, key_path):
"""
Remove a key by path from the Json
:param key_path: Path to the key
:return: None
"""
path_tokens = self._get_path_tokens(key_path)
obj = self._data
parent = None
key = None
try:
for token in path_tokens:
key = token
parent = obj
obj = obj[token]
if parent:
del parent[key]
self._modified = True
else:
logger.warning(f'Could not remove key {key} from file {self._file_path}')
except KeyError as err:
logger.error(f'KeyError when removing {key_path} from file {self._file_path}: {err}')
class RegistrySettings(JsonSettings):
"""
JsonSettings provides an API for reading, modifying and writing Registry settings files.
Files must be Json-compatible with optional line comments starting with '//'.
"""
def __init__(self, file_path):
super(RegistrySettings, self).__init__(file_path, RegistrySettings._clean_line)
@staticmethod
def _clean_line(line):
comment_index = line.lstrip.startswith('//')
return line if comment_index == -1 else line[:comment_index]
def _edit_text_settings_file(settings_file, setting, value, comment_char=""):
"""
Find and set a specific setting in a text based settings file. Uses "setting = value" syntax to identify setting,