Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,193 @@
#
# 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.
#
from Params import Params
from utils.util import *
class PackageEnv(Params):
def __init__(self, target_platform, json_file):
super(PackageEnv, self).__init__()
self.__cur_dir = os.path.dirname(os.path.abspath(__file__))
with open(json_file, 'r') as source:
self.__data = json.load(source)
self.__platforms = self.__data.get('platforms')
if target_platform not in self.__platforms:
ly_build_error('Target platform {} is not supported'.format(target_platform))
self.__target_platform = target_platform
# visited_platform is used to track the platform reference chain, in order to avoid chain cycle.
visited_platform = [target_platform]
platform_env = self.__platforms.get(target_platform)
# If platform_env starts with @, it means that platform_env references another platform
while isinstance(platform_env, str) and platform_env.startswith('@'):
referenced_platform = platform_env.lstrip('@')
if referenced_platform in visited_platform:
ly_build_error('Found reference chain cycle started from {}.\nSee {}'.format(referenced_platform, json_file))
visited_platform.append(referenced_platform)
platform_env = self.__platforms.get(referenced_platform)
self.__platform_env = platform_env
self.__global_env = self.__data.get('global')
def get_target_platform(self):
return self.__target_platform
def get_global_env(self):
return self.__global_env
def get_platform_env(self):
return self.__platform_env
def __get_global_value(self, key):
key = key.upper()
value = self.__global_env.get(key)
if value is None:
ly_build_error('{} is not defined in global env'.format(key))
return value
def __get_platform_value(self, key):
key = key.upper()
value = self.__platform_env.get(key)
if value is None:
ly_build_error('{} is not defined in platform env for {}'.format(key, self.__target_platform))
return value
def __evaluate_boolean(self, v):
return str(v).lower() in ['1', 'true']
def __get_engine_root(self):
def validate_engine_root(engine_root):
if not os.path.isdir(engine_root):
return False
return os.path.exists(os.path.join(engine_root, 'engineroot.txt'))
# Jenkins only
workspace = os.getenv('WORKSPACE')
if workspace is not None:
print('Environment variable WORKSPACE={} detected'.format(workspace))
if validate_engine_root(workspace):
print('Setting ENGINE_ROOT to {}'.format(workspace))
return workspace
engine_root = os.path.join(workspace, 'dev')
if validate_engine_root(engine_root):
print('Setting ENGINE_ROOT to {}'.format(engine_root))
return engine_root
print('Cannot locate ENGINE_ROOT with Environment variable WORKSPACE')
# End Jenkins only
engine_root = os.getenv('ENGINE_ROOT', '')
if validate_engine_root(engine_root):
return engine_root
print('Environment variable ENGINE_ROOT is not set or invalid, checking ENGINE_ROOT in env json file')
engine_root = self.__global_env.get('ENGINE_ROOT')
if validate_engine_root(engine_root):
return engine_root
# Set engine_root based on script location
engine_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(self.__cur_dir))))
print('ENGINE_ROOT from env json file is invalid, defaulting to {}'.format(engine_root))
if validate_engine_root(engine_root):
return engine_root
else:
error('Cannot Locate ENGINE_ROOT')
def __get_thirdparty_home(self):
third_party_home = os.getenv('ENV_3RDPARTY_PATH', '')
if os.path.exists(third_party_home):
print('ENV_3RDPARTY_PATH found, using {} as 3rdParty path.'.format(third_party_home))
return third_party_home
third_party_home = self.__get_global_value('THIRDPARTY_HOME')
if os.path.isdir(third_party_home):
return third_party_home
# Set engine_root based on script location
print('THIRDPARTY_HOME is not valid, looking for THIRD_PARTY_HOME')
# Finding THIRD_PARTY_HOME
cur_dir = self.__get_engine_root()
last_dir = None
while last_dir != cur_dir:
third_party_home = os.path.join(cur_dir, '3rdParty')
print('Cheking THIRDPARTY_HOME {}'.format(third_party_home))
if os.path.exists(os.path.join(third_party_home, '3rdParty.txt')):
print('Setting THIRDPARTY_HOME to {}'.format(third_party_home))
return third_party_home
last_dir = cur_dir
cur_dir = os.path.dirname(cur_dir)
error('Cannot locate THIRDPARTY_HOME')
def __get_package_name_pattern(self):
package_name_pattern = self.__get_global_value('PACKAGE_NAME_PATTERN')
if os.getenv('PACKAGE_NAME_PATTERN') is not None:
package_name_pattern = os.getenv('PACKAGE_NAME_PATTERN')
return package_name_pattern
def __get_build_number(self):
build_number = self.__get_global_value('BUILD_NUMBER')
if os.getenv('BUILD_NUMBER') is not None:
build_number = os.getenv('BUILD_NUMBER')
return build_number
def __get_p4_changelist(self):
p4_changelist = self.__get_global_value('P4_CHANGELIST')
if os.getenv('P4_CHANGELIST') is not None:
p4_changelist = os.getenv('P4_CHANGELIST')
return p4_changelist
def __get_major_version(self):
major_version = self.__get_global_value('MAJOR_VERSION')
if os.getenv('MAJOR_VERSION') is not None:
major_version = os.getenv('MAJOR_VERSION')
return major_version
def __get_minor_version(self):
minor_version = self.__get_global_value('MINOR_VERSION')
if os.getenv('MINOR_VERSION') is not None:
minor_version = os.getenv('MINOR_VERSION')
return minor_version
def __get_scrub_params(self):
return self.__get_platform_value('SCRUB_PARAMS')
def __get_validator_platforms(self):
return self.__get_platform_value('VALIDATOR_PLATFORMS')
def __get_package_targets(self):
return self.__get_platform_value('PACKAGE_TARGETS')
def __get_build_targets(self):
return self.__get_platform_value('BUILD_TARGETS')
def __get_asset_processor_path(self):
return self.__get_platform_value('ASSET_PROCESSOR_PATH')
def __get_asset_game_folders(self):
return self.__get_platform_value('ASSET_GAME_FOLDERS')
def __get_asset_platform(self):
return self.__get_platform_value('ASSET_PLATFORM')
def __get_bootstrap_cfg_game_folder(self):
return self.__get_platform_value('BOOTSTRAP_CFG_GAME_FOLDER')
def __get_run_launcher_unit_test(self):
run_launcher_unit_test = os.getenv('RUN_LAUNCHER_UNIT_TEST')
if run_launcher_unit_test is None:
run_launcher_unit_test = self.__platform_env.get('RUN_LAUNCHER_UNIT_TEST')
return self.__evaluate_boolean(run_launcher_unit_test)
def __get_skip_build(self):
skip_build = os.getenv('SKIP_BUILD')
if skip_build is None:
skip_build = self.__platform_env.get('SKIP_BUILD')
return self.__evaluate_boolean(skip_build)
@@ -0,0 +1,81 @@
#
# 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.
#
from utils.util import *
class Params(object):
def __init__(self):
# Cache params
self.__params = {}
def get(self, param_name):
param_value = self.__params.get(param_name)
if param_value is not None:
return param_value
# Call __get_${param_name} function
func = getattr(self, '_{}__get_{}'.format(self.__class__.__name__, param_name.lower()), None)
if func is not None:
param_value = func()
# Replace all ${env} in value
if isinstance(param_value, str):
param_value = self.__process_string(param_name, param_value)
elif isinstance(param_value, list):
param_value = self.__process_list(param_name, param_value)
elif isinstance(param_value, dict):
param_value = self.__process_dict(param_name, param_value)
# Cache param
self.__params[param_name] = param_value
return param_value
ly_build_error('method __get_{} is not defined in class {}'.format(param_name.lower(), self.__class__.__name__))
def set(self, param_name, param_value):
self.__params[param_name] = param_value
def exists(self, param_name):
try:
self.get(param_name)
except LyBuildError:
return False
return True
def __process_string(self, param_name, param_value):
# Find all param with format ${param}
params = re.findall('\${(\w+)}', param_value)
# Avoid using the same param name in value, like 'WORKSPACE': '${WORKSPACE} some string'
if param_name in params:
ly_build_error('The use of same parameter name({}) in value is not allowed'.format(param_name))
# Replace ${param} with actual value
for param in params:
param_value = param_value.replace('${' + param + '}', self.get(param))
return param_value
def __process_list(self, param_name, param_value):
processed_list = []
for entry in param_value:
if isinstance(entry, str):
entry = self.__process_string(param_name, entry)
elif isinstance(entry, list):
entry = self.__process_list(param_name, entry)
elif isinstance(entry, dict):
entry = self.__process_dict(param_name, entry)
processed_list.append(entry)
return processed_list
def __process_dict(self, param_name, param_value):
for key in param_value:
if isinstance(param_value[key], str):
param_value[key] = self.__process_string(param_name, param_value[key])
elif isinstance(param_value[key], list):
param_value[key] = self.__process_list(param_name, param_value[key])
elif isinstance(param_value[key], dict):
param_value[key] = self.__process_dict(param_name, param_value[key])
return param_value
@@ -0,0 +1,118 @@
{
"metrics": {
"TAGS":[
"weekly"
],
"COMMAND":"../Windows/python_windows.cmd",
"PARAMETERS": {
"SCRIPT_PATH":"Tools/build/JenkinsScripts/build/ci_build_metrics.py",
"SCRIPT_PARAMETERS":"--platform Android --jobname \"!JOB_NAME!\" --jobnumber \"!BUILD_NUMBER!\" --jobnode \"!NODE_NAME!\" --changelist \"!CHANGE_ID!\""
}
},
"debug": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"../Windows/build_ninja_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"debug",
"OUTPUT_DIRECTORY":"build\\android",
"CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"all",
"CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!"
}
},
"profile": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"../Windows/build_ninja_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\android",
"CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"all",
"CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!"
}
},
"profile_nounity": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"../Windows/build_ninja_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\android",
"CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_UNITY_BUILD=FALSE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"all",
"CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!"
}
},
"asset_profile": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"../Windows/build_asset_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\windows_vs2019",
"CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"AssetProcessorBatch",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo",
"ASSET_PROCESSOR_BINARY": "bin\\profile\\AssetProcessorBatch.exe",
"ASSET_PROCESSOR_OPTIONS": "/zeroAnalysisMode",
"ASSET_PROCESSOR_PLATFORMS":"es3"
}
},
"release": {
"TAGS":[
"metric"
],
"COMMAND":"../Windows/build_ninja_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"release",
"OUTPUT_DIRECTORY":"build\\android",
"CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"all",
"CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!"
}
},
"monolithic_release": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"../Windows/build_ninja_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"release",
"OUTPUT_DIRECTORY":"build\\mono_android",
"CMAKE_OPTIONS":"-G \"Ninja Multi-Config\" -DCMAKE_TOOLCHAIN_FILE=cmake\\Platform\\Android\\Toolchain_android.cmake -DANDROID_ABI=arm64-v8a -DANDROID_ARM_MODE=arm -DANDROID_ARM_NEON=FALSE -DANDROID_NATIVE_API_LEVEL=21 -DLY_NDK_DIR=\"!LY_3RDPARTY_PATH!\\android-ndk\\r21d\" -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"all",
"CMAKE_BUILD_ARGS":"-j!NUMBER_OF_PROCESSORS!"
}
},
"gradle": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"gradle_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\android_gradle",
"GAME_PROJECT": "CMakeTestbed",
"ANDROID_NDK_PLATFORM": "21",
"ANDROID_SDK_PLATFORM": "29"
}
}
}
@@ -0,0 +1,108 @@
@ECHO OFF
REM
REM All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
REM its licensors.
REM
REM For complete copyright and license terms please see the LICENSE at the root of this
REM distribution (the "License"). All use of this software is governed by the License,
REM or, if provided, by the license below or the license accompanying this file. Do not
REM remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM
IF NOT EXIST "%LY_3RDPARTY_PATH%" (
ECHO [ci_build] LY_3RDPARTY_PATH is invalid or not set
GOTO :error
)
IF NOT EXIST "%GRADLE_HOME%" (
REM This is the default for developers
SET GRADLE_HOME=C:\Gradle\gradle-5.6.4
)
IF NOT EXIST "%GRADLE_HOME%" (
ECHO [ci_build] FAIL: GRADLE_HOME=%GRADLE_HOME%
GOTO :error
)
IF NOT EXIST "%CMAKE_HOME%" (
SET CMAKE_HOME=%LY_3RDPARTY_PATH%/CMake/3.19.1/Windows/
)
IF NOT EXIST "%CMAKE_HOME%" (
ECHO [ci_build] FAIL: CMAKE_HOME=%CMAKE_HOME%
GOTO :error
)
IF NOT EXIST "%LY_NINJA_PATH%" (
SET LY_NINJA_PATH=%LY_3RDPARTY_PATH%/ninja/1.10.1/Windows
)
IF NOT EXIST "%LY_NINJA_PATH%" (
ECHO [ci_build] FAIL: LY_NINJA_PATH=%LY_NINJA_PATH%
GOTO :error
)
IF NOT EXIST "%LY_ANDROID_SDK%" (
SET LY_ANDROID_SDK=%LY_3RDPARTY_PATH%/android-sdk/platform-29
)
IF NOT EXIST "%LY_ANDROID_SDK%" (
ECHO [ci_build] FAIL: LY_ANDROID_SDK=%LY_ANDROID_SDK%
GOTO :error
)
IF NOT EXIST "%LY_ANDROID_NDK%" (
set LY_ANDROID_NDK=%LY_3RDPARTY_PATH%/android-ndk/r21d
)
IF NOT EXIST "%LY_ANDROID_NDK%" (
ECHO [ci_build] LY_ANDROID_NDK=%LY_ANDROID_NDK%
GOTO :error
)
REM Delete output directory if CLEAN_OUTPUT_DIRECTORY env variable is set
IF "%CLEAN_OUTPUT_DIRECTORY%"=="true" (
IF EXIST %OUTPUT_DIRECTORY% (
ECHO [ci_build] CLEAN_OUTPUT_DIRECTORY option set with value "%CLEAN_OUTPUT_DIRECTORY%"
ECHO [ci_build] Deleting "%OUTPUT_DIRECTORY%"
DEL /s /q /f %OUTPUT_DIRECTORY%
)
)
IF NOT EXIST %OUTPUT_DIRECTORY% (
mkdir %OUTPUT_DIRECTORY%
)
REM Jenkins reports MSB8029 when TMP/TEMP is not defined, define a dummy folder
SET TMP=%cd%/temp
SET TEMP=%cd%/temp
IF NOT EXIST %TMP% (
mkdir temp
)
SET PYTHON=python\python.cmd
ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --dev-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_HOME% --cmake-install-path=%CMAKE_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-ndk-path=%LY_ANDROID_NDK% --android-sdk-path=%LY_ANDROID_SDK% --android-ndk-version=%ANDROID_NDK_PLATFORM% --android-sdk-version=%ANDROID_SDK_PLATFORM%
CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --dev-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_HOME% --cmake-install-path=%CMAKE_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-ndk-path=%LY_ANDROID_NDK% --android-sdk-path=%LY_ANDROID_SDK% --android-ndk-version=%ANDROID_NDK_PLATFORM% --android-sdk-version=%ANDROID_SDK_PLATFORM%
IF NOT %ERRORLEVEL%==0 GOTO :error
PUSHD %OUTPUT_DIRECTORY%
REM Stop any running or orphaned gradle daemon
ECHO [ci_build] gradlew --stop
gradlew --stop
ECHO [ci_build] gradlew --no-daemon -build%CONFIGURATION%
gradlew --no-daemon build%CONFIGURATION%
IF NOT %ERRORLEVEL%==0 GOTO :popd_error
POPD
ECHO [ci_build] gradlew --stop
gradlew --stop
EXIT /b 0
:popd_error
POPD
:error
ECHO [ci_build] gradlew --stop
gradlew --stop
EXIT /b 1
@@ -0,0 +1,9 @@
{
"GRADLE_HOME": "C:/Gradle/gradle-5.6.4",
"JOB_NAME": "ANDROID",
"LABEL": "windows",
"LY_3RDPARTY_PATH": "C:/ly/3rdParty",
"TIMEOUT": 30,
"WORKSPACE": "D:/workspace",
"MOUNT_VOLUME": true
}
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
#
# 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.
#
set -o errexit # exit on the first failure encountered
# Delete output directory if CLEAN_OUTPUT_DIRECTORY env variable is set
if [[ $CLEAN_OUTPUT_DIRECTORY == "true" ]]; then
if [[ -d Cache ]]; then
echo "[ci_build] CLEAN_OUTPUT_DIRECTORY option set with value \"${CLEAN_OUTPUT_DIRECTORY}\""
echo "[ci_build] Deleting \"Cache\""
rm -rf Cache
fi
fi
if [[ ! -d $OUTPUT_DIRECTORY ]]; then
echo [ci_build] Error: $OUTPUT_DIRECTORY was not found
exit 1
fi
pushd $OUTPUT_DIRECTORY
if [[ ! -e $ASSET_PROCESSOR_BINARY ]]; then
echo [ci_build] Error: $ASSET_PROCESSOR_BINARY was not found
exit 1
fi
for project in $(echo $CMAKE_LY_PROJECTS | sed "s/;/ /g")
do
echo [ci_build] ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --gamefolder=$project --platforms=$ASSET_PROCESSOR_PLATFORMS
${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --gamefolder=$project --platforms=$ASSET_PROCESSOR_PLATFORMS
done
popd
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
#
# 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.
#
set -o errexit # exit on the first failure encountered
BASEDIR=$(dirname "$0")
source $BASEDIR/build_linux.sh
source $BASEDIR/asset_linux.sh
@@ -0,0 +1,143 @@
{
"metrics": {
"TAGS":[
"weekly"
],
"COMMAND":"python_linux.sh",
"PARAMETERS": {
"SCRIPT_PATH":"Tools/build/JenkinsScripts/build/ci_build_metrics.py",
"SCRIPT_PARAMETERS":"--platform Linux --jobname '${JOB_NAME}' --jobnumber '${BUILD_NUMBER}' --jobnode '${NODE_NAME}' --changelist '${CHANGE_ID}'"
}
},
"debug": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"build_linux.sh",
"PARAMETERS": {
"CONFIGURATION":"debug",
"OUTPUT_DIRECTORY":"build/linux",
"CMAKE_OPTIONS":"-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"all"
}
},
"profile": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"build_linux.sh",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build/linux",
"CMAKE_OPTIONS":"-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"all"
}
},
"profile_nounity": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"build_linux.sh",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build/linux",
"CMAKE_OPTIONS":"-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"all"
}
},
"test_profile": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"build_test_linux.sh",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build/linux",
"CMAKE_OPTIONS":"-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"AzCore.Tests",
"CTEST_OPTIONS":"-R AzCore.Tests -L (smoke|main)"
}
},
"asset_profile": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"build_asset_linux.sh",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build/linux",
"CMAKE_OPTIONS":"-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample",
"CMAKE_TARGET":"AssetProcessorBatch",
"ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch",
"ASSET_PROCESSOR_OPTIONS": "/zeroAnalysisMode",
"ASSET_PROCESSOR_PLATFORMS":"pc,server"
}
},
"periodic_test_profile": {
"TAGS":[
"nightly",
"metric"
],
"COMMAND":"build_test_linux.sh",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build/linux",
"CMAKE_OPTIONS":"-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"TEST_SUITE_periodic",
"CTEST_OPTIONS":"-L \"(periodic)\""
}
},
"benchmark_test_profile": {
"TAGS":[
"nightly",
"metric"
],
"COMMAND":"build_test_linux.sh",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build/linux",
"CMAKE_OPTIONS":"-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"TEST_SUITE_benchmark",
"CTEST_OPTIONS":"-L \"(benchmark)\""
}
},
"release": {
"TAGS":[
"metric"
],
"COMMAND":"build_linux.sh",
"PARAMETERS": {
"CONFIGURATION":"release",
"OUTPUT_DIRECTORY":"build/linux",
"CMAKE_OPTIONS":"-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"all"
}
},
"monolithic_release": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"build_linux.sh",
"PARAMETERS": {
"CONFIGURATION":"release",
"OUTPUT_DIRECTORY":"build/mono_linux",
"CMAKE_OPTIONS":"-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"all"
}
}
}
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
#
# 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.
#
set -o errexit # exit on the first failure encountered
BASEDIR=$(dirname "$0")
source $BASEDIR/env_linux.sh
# Delete output directory if CLEAN_OUTPUT_DIRECTORY env variable is set
if [[ $CLEAN_OUTPUT_DIRECTORY == "true" ]]; then
if [[ -d $OUTPUT_DIRECTORY ]]; then
echo "[ci_build] CLEAN_OUTPUT_DIRECTORY option set with value \"${CLEAN_OUTPUT_DIRECTORY}\""
echo "[ci_build] Deleting \"${OUTPUT_DIRECTORY}\""
rm -rf ${OUTPUT_DIRECTORY}
fi
fi
mkdir -p ${OUTPUT_DIRECTORY}
SOURCE_DIRECTORY=${PWD}
pushd $OUTPUT_DIRECTORY
LAST_CONFIGURE_CMD_FILE=ci_last_configure_cmd.txt
CONFIGURE_CMD="cmake ${SOURCE_DIRECTORY} ${CMAKE_OPTIONS} ${EXTRA_CMAKE_OPTIONS} -DLY_3RDPARTY_PATH=${LY_3RDPARTY_PATH} -DLY_PROJECTS='${CMAKE_LY_PROJECTS}'"
if [[ ! -e "CMakeCache.txt" ]]; then
echo [ci_build] First run, generating
RUN_CONFIGURE=1
elif [[ ! -e ${LAST_CONFIGURE_CMD_FILE} ]]; then
echo [ci_build] Last run command not found, generating
RUN_CONFIGURE=1
else
# Detect if the input has changed
LAST_CMD=$(<${LAST_CONFIGURE_CMD_FILE})
if [[ "${LAST_CMD}" != "${CONFIGURE_CMD}" ]]; then
echo [ci_build] Last run command different, generating
RUN_CONFIGURE=1
fi
fi
if [[ ! -z "$RUN_CONFIGURE" ]]; then
# have to use eval since $CMAKE_OPTIONS (${EXTRA_CMAKE_OPTIONS}) contains quotes that need to be processed
echo [ci_build] ${CONFIGURE_CMD}
eval ${CONFIGURE_CMD}
# Save the run only if success
echo "${CONFIGURE_CMD}" > ${LAST_CONFIGURE_CMD_FILE}
fi
echo [ci_build] cmake --build . --target ${CMAKE_TARGET} --config ${CONFIGURATION} -j $(grep -c processor /proc/cpuinfo) -- ${CMAKE_NATIVE_BUILD_ARGS}
cmake --build . --target ${CMAKE_TARGET} --config ${CONFIGURATION} -j $(grep -c processor /proc/cpuinfo) -- ${CMAKE_NATIVE_BUILD_ARGS}
popd
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
#
# 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.
#
set -o errexit # exit on the first failure encountered
BASEDIR=$(dirname "$0")
source $BASEDIR/build_linux.sh
source $BASEDIR/test_linux.sh
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
#
# 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.
#
set -o errexit # exit on the first failure encountered
if ! command -v cmake &> /dev/null; then
if [[ -z $LY_CMAKE_PATH ]]; then LY_CMAKE_PATH=${LY_3RDPARTY_PATH}/CMake/3.19.1/Linux/bin; fi
if [[ ! -d $LY_CMAKE_PATH ]]; then
echo "[ci_build] CMake path not found"
exit 1
fi
PATH=${LY_CMAKE_PATH}:${PATH}
if ! command -v cmake &> /dev/null; then
echo "[ci_build] CMake not found"
exit 1
fi
fi
if ! command -v ninja &> /dev/null; then
if [[ -z $LY_NINJA_PATH ]]; then LY_NINJA_PATH=${LY_3RDPARTY_PATH}/ninja/1.10.1/Linux; fi
if [[ ! -d $LY_NINJA_PATH ]]; then
echo "[ci_build] Ninja path not found"
exit 1
fi
PATH=${LY_NINJA_PATH}:${PATH}
if ! command -v ninja &> /dev/null; then
echo "[ci_build] Ninja not found"
exit 1
fi
fi
@@ -0,0 +1,8 @@
{
"JOB_NAME": "LINUX",
"LABEL": "linux",
"LY_3RDPARTY_PATH": "/home/lybuilder/ly/workspace/3rdParty",
"TIMEOUT": 30,
"WORKSPACE": "/data/workspace",
"MOUNT_VOLUME": true
}
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
#
# 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.
#
set -o errexit # exit on the first failure encountered
echo [ci_build] python/python.sh -u ${SCRIPT_PATH} ${SCRIPT_PARAMETERS}
python/python.sh -u ${SCRIPT_PATH} ${SCRIPT_PARAMETERS}
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
#
# 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.
#
set -o errexit # exit on the first failure encountered
BASEDIR=$(dirname "$0")
source $BASEDIR/env_linux.sh
if [[ ! -d $OUTPUT_DIRECTORY ]]; then
echo [ci_build] Error: $OUTPUT_DIRECTORY was not found
exit 1
fi
pushd $OUTPUT_DIRECTORY
# Find the CTEST_RUN_FLAGS from cmake's cache variables, then replace the $<CONFIG> with the current configuration
IFS='=' read -ra CTEST_RUN_FLAGS <<< $(cmake -N -LA . | grep "CTEST_RUN_FLAGS:STRING")
CTEST_RUN_FLAGS=${CTEST_RUN_FLAGS[1]/$<CONFIG>/${CONFIGURATION}}
# Run ctest
echo [ci_build] ctest ${CTEST_RUN_FLAGS} ${CTEST_OPTIONS}
ctest ${CTEST_RUN_FLAGS} ${CTEST_OPTIONS}
popd
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
#
# 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.
#
set -o errexit # exit on the first failure encountered
# Delete output directory if CLEAN_OUTPUT_DIRECTORY env variable is set
if [[ $CLEAN_OUTPUT_DIRECTORY == "true" ]]; then
if [[ -d Cache ]]; then
echo "[ci_build] CLEAN_OUTPUT_DIRECTORY option set with value \"${CLEAN_OUTPUT_DIRECTORY}\""
echo "[ci_build] Deleting \"Cache\""
rm -rf Cache
fi
fi
if [[ ! -d $OUTPUT_DIRECTORY ]]; then
echo [ci_build] Error: $OUTPUT_DIRECTORY was not found
exit 1
fi
pushd $OUTPUT_DIRECTORY
if [[ ! -e $ASSET_PROCESSOR_BINARY ]]; then
echo [ci_build] Error: $ASSET_PROCESSOR_BINARY was not found
exit 1
fi
for project in $(echo $CMAKE_LY_PROJECTS | sed "s/;/ /g")
do
echo [ci_build] ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --gamefolder=$project --platforms=$ASSET_PROCESSOR_PLATFORMS
${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --gamefolder=$project --platforms=$ASSET_PROCESSOR_PLATFORMS
done
popd
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
#
# 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.
#
set -o errexit # exit on the first failure encountered
BASEDIR=$(dirname "$0")
source $BASEDIR/build_mac.sh
source $BASEDIR/asset_mac.sh
@@ -0,0 +1,121 @@
{
"metrics": {
"TAGS":[
"weekly"
],
"COMMAND":"python_mac.sh",
"PARAMETERS": {
"SCRIPT_PATH":"Tools/build/JenkinsScripts/build/ci_build_metrics.py",
"SCRIPT_PARAMETERS":"--platform Mac --jobname '${JOB_NAME}' --jobnumber '${BUILD_NUMBER}' --jobnode '${NODE_NAME}' --changelist '${CHANGE_ID}'"
}
},
"debug": {
"TAGS":[
"nightly"
],
"COMMAND":"build_mac.sh",
"PARAMETERS": {
"CONFIGURATION":"debug",
"OUTPUT_DIRECTORY":"build/mac",
"CMAKE_OPTIONS":"-G Xcode -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD"
}
},
"profile": {
"TAGS":[
"nightly"
],
"COMMAND":"build_mac.sh",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build/mac",
"CMAKE_OPTIONS":"-G Xcode -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD"
}
},
"profile_nounity": {
"TAGS":[
"nightly"
],
"COMMAND":"build_mac.sh",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build/mac",
"CMAKE_OPTIONS":"-G Xcode -DLY_UNITY_BUILD=FALSE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD"
}
},
"asset_profile": {
"TAGS":[
"nightly"
],
"COMMAND":"build_asset_mac.sh",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build/mac",
"CMAKE_OPTIONS":"-G Xcode -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample",
"CMAKE_TARGET":"AssetProcessorBatch",
"ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch",
"ASSET_PROCESSOR_OPTIONS": "/zeroAnalysisMode",
"ASSET_PROCESSOR_PLATFORMS":"osx_gl"
}
},
"periodic_test_profile": {
"TAGS":[
"nightly"
],
"COMMAND":"build_test_mac.sh",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build/mac",
"CMAKE_OPTIONS":"-G Xcode -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"TEST_SUITE_periodic",
"CTEST_OPTIONS":"-L \"(periodic)\""
}
},
"benchmark_test_profile": {
"TAGS":[
"nightly"
],
"COMMAND":"build_test_mac.sh",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build/mac",
"CMAKE_OPTIONS":"-G Xcode -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"TEST_SUITE_benchmark",
"CTEST_OPTIONS":"-L \"(benchmark)\""
}
},
"release": {
"TAGS":[
"nightly"
],
"COMMAND":"build_mac.sh",
"PARAMETERS": {
"CONFIGURATION":"release",
"OUTPUT_DIRECTORY":"build/mac",
"CMAKE_OPTIONS":"-G Xcode -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD"
}
},
"monolithic_release": {
"TAGS":[
"nightly"
],
"COMMAND":"build_mac.sh",
"PARAMETERS": {
"CONFIGURATION":"release",
"OUTPUT_DIRECTORY":"build/mono_mac",
"CMAKE_OPTIONS":"-G Xcode -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD"
}
}
}
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
#
# 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.
#
set -o errexit # exit on the first failure encountered
BASEDIR=$(dirname "$0")
source $BASEDIR/env_mac.sh
# Delete output directory if CLEAN_OUTPUT_DIRECTORY env variable is set
if [[ $CLEAN_OUTPUT_DIRECTORY == "true" ]]; then
if [[ -d $OUTPUT_DIRECTORY ]]; then
echo "[ci_build] CLEAN_OUTPUT_DIRECTORY option set with value \"${CLEAN_OUTPUT_DIRECTORY}\""
echo "[ci_build] Deleting \"${OUTPUT_DIRECTORY}\""
rm -rf ${OUTPUT_DIRECTORY}
fi
fi
mkdir -p ${OUTPUT_DIRECTORY}
SOURCE_DIRECTORY=${PWD}
pushd $OUTPUT_DIRECTORY
LAST_CONFIGURE_CMD_FILE=ci_last_configure_cmd.txt
CONFIGURE_CMD="cmake ${SOURCE_DIRECTORY} ${CMAKE_OPTIONS} ${EXTRA_CMAKE_OPTIONS} -DLY_3RDPARTY_PATH=${LY_3RDPARTY_PATH} -DLY_PROJECTS='${CMAKE_LY_PROJECTS}'"
if [[ ! -e "CMakeCache.txt" ]]; then
echo [ci_build] First run, generating
RUN_CONFIGURE=1
elif [[ ! -e ${LAST_CONFIGURE_CMD_FILE} ]]; then
echo [ci_build] Last run command not found, generating
RUN_CONFIGURE=1
else
# Detect if the input has changed
LAST_CMD=$(<${LAST_CONFIGURE_CMD_FILE})
if [[ "${LAST_CMD}" != "${CONFIGURE_CMD}" ]]; then
echo [ci_build] Last run command different, generating
RUN_CONFIGURE=1
fi
fi
if [[ ! -z "$RUN_CONFIGURE" ]]; then
# have to use eval since $CMAKE_OPTIONS (${EXTRA_CMAKE_OPTIONS}) contains quotes that need to be processed
echo [ci_build] ${CONFIGURE_CMD}
eval ${CONFIGURE_CMD}
# Save the run only if success
echo "${CONFIGURE_CMD}" > ${LAST_CONFIGURE_CMD_FILE}
fi
echo [ci_build] cmake --build . --target ${CMAKE_TARGET} --config ${CONFIGURATION} -j $(sysctl -n hw.ncpu) -- ${CMAKE_NATIVE_BUILD_ARGS} -UseModernBuildSystem=NO
cmake --build . --target ${CMAKE_TARGET} --config ${CONFIGURATION} -j $(sysctl -n hw.ncpu) -- ${CMAKE_NATIVE_BUILD_ARGS} -UseModernBuildSystem=NO
popd
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
#
# 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.
#
set -o errexit # exit on the first failure encountered
BASEDIR=$(dirname "$0")
source $BASEDIR/build_mac.sh
source $BASEDIR/test_mac.sh
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
#
# 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.
#
set -o errexit # exit on the first failure encountered
if ! command -v cmake &> /dev/null; then
if [[ -z $LY_CMAKE_PATH ]]; then LY_CMAKE_PATH=${LY_3RDPARTY_PATH}/CMake/3.19.1/Mac/CMake.app/Contents/bin; fi
if [[ ! -d $LY_CMAKE_PATH ]]; then
echo "[ci_build] CMake path not found"
exit 1
fi
PATH=${LY_CMAKE_PATH}:${PATH}
if ! command -v cmake &> /dev/null; then
echo "[ci_build] CMake not found"
exit 1
fi
fi
@@ -0,0 +1,8 @@
{
"JOB_NAME": "MAC",
"LABEL": "mac",
"LY_3RDPARTY_PATH": "/Users/lybuilder/3rdParty",
"TIMEOUT": 30,
"WORKSPACE": "/Users/lybuilder/workspace",
"MOUNT_VOLUME": false
}
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
#
# 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.
#
set -o errexit # exit on the first failure encountered
echo [ci_build] python/python.sh -u ${SCRIPT_PATH} ${SCRIPT_PARAMETERS}
python/python.sh -u ${SCRIPT_PATH} ${SCRIPT_PARAMETERS}
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
#
# 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.
#
set -o errexit # exit on the first failure encountered
BASEDIR=$(dirname "$0")
source $BASEDIR/env_mac.sh
if [[ ! -d $OUTPUT_DIRECTORY ]]; then
echo [ci_build] Error: $OUTPUT_DIRECTORY was not found
exit 1
fi
pushd $OUTPUT_DIRECTORY
# Find the CTEST_RUN_FLAGS from the CMakeCache.txt file, then replace the $<CONFIG> with the current configuration
IFS='=' read -ra CTEST_RUN_FLAGS <<< $(cmake -N -LA . | grep "CTEST_RUN_FLAGS:STRING")
CTEST_RUN_FLAGS=${CTEST_RUN_FLAGS[1]/$<CONFIG>/${CONFIGURATION}}
# Run ctest
echo [ci_build] ctest ${CTEST_RUN_FLAGS} ${CTEST_OPTIONS}
ctest ${CTEST_RUN_FLAGS} ${CTEST_OPTIONS}
popd
@@ -0,0 +1,48 @@
@ECHO OFF
REM
REM All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
REM its licensors.
REM
REM For complete copyright and license terms please see the LICENSE at the root of this
REM distribution (the "License"). All use of this software is governed by the License,
REM or, if provided, by the license below or the license accompanying this file. Do not
REM remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM
SETLOCAL EnableDelayedExpansion
REM Delete output directory if CLEAN_OUTPUT_DIRECTORY env variable is set
IF "%CLEAN_OUTPUT_DIRECTORY%"=="true" (
IF EXIST Cache (
ECHO [ci_build] CLEAN_OUTPUT_DIRECTORY option set with value "%CLEAN_OUTPUT_DIRECTORY%"
ECHO [ci_build] Deleting "Cache"
DEL /s /q /f Cache
)
)
IF NOT EXIST %OUTPUT_DIRECTORY% (
ECHO [ci_build] Error: %OUTPUT_DIRECTORY% was not found
GOTO :error
)
PUSHD %OUTPUT_DIRECTORY%
IF NOT EXIST %ASSET_PROCESSOR_BINARY% (
ECHO [ci_build] Error: %ASSET_PROCESSOR_BINARY% was not found
GOTO :error
)
FOR %%P in (%CMAKE_LY_PROJECTS%) do (
ECHO [ci_build] %ASSET_PROCESSOR_BINARY% %ASSET_PROCESSOR_OPTIONS% --gamefolder=%%P --platforms=%ASSET_PROCESSOR_PLATFORMS%
%ASSET_PROCESSOR_BINARY% %ASSET_PROCESSOR_OPTIONS% --gamefolder=%%P --platforms=%ASSET_PROCESSOR_PLATFORMS%
IF NOT !ERRORLEVEL!==0 GOTO :popd_error
)
POPD
EXIT /b 0
:popd_error
POPD
:error
EXIT /b 1
@@ -0,0 +1,22 @@
@ECHO OFF
REM
REM All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
REM its licensors.
REM
REM For complete copyright and license terms please see the LICENSE at the root of this
REM distribution (the "License"). All use of this software is governed by the License,
REM or, if provided, by the license below or the license accompanying this file. Do not
REM remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM
CALL "%~dp0build_windows.cmd"
IF NOT %ERRORLEVEL%==0 GOTO :error
CALL "%~dp0asset_windows.cmd"
IF NOT %ERRORLEVEL%==0 GOTO :error
EXIT /b 0
:error
EXIT /b 1
@@ -0,0 +1,326 @@
{
"scrubbing": {
"TAGS":[
"default"
],
"COMMAND":"python_windows.cmd",
"PARAMETERS": {
"SCRIPT_PATH":"Tools/build/JenkinsScripts/build/scrubbing_job.py"
}
},
"validation": {
"TAGS":[
"default"
],
"COMMAND":"python_windows.cmd",
"PARAMETERS": {
"SCRIPT_PATH":"scripts/commit_validation/validate_file_or_folder.py"
}
},
"metrics": {
"TAGS":[
"weekly"
],
"COMMAND":"python_windows.cmd",
"PARAMETERS": {
"SCRIPT_PATH":"Tools/build/JenkinsScripts/build/ci_build_metrics.py",
"SCRIPT_PARAMETERS":"--platform Windows --jobname \"!JOB_NAME!\" --jobnumber \"!BUILD_NUMBER!\" --jobnode \"!NODE_NAME!\" --changelist \"!CHANGE_ID!\""
}
},
"windows_packaging_all": {
"TAGS":[
"packaging"
],
"COMMAND":"python_windows.cmd",
"PARAMETERS": {
"SCRIPT_PATH":"scripts/build/package/package.py",
"SCRIPT_PARAMETERS":"--platform Windows --type all"
}
},
"3rdParty_all": {
"TAGS":[
"packaging"
],
"COMMAND":"python_windows.cmd",
"PARAMETERS": {
"SCRIPT_PATH":"scripts/build/package/package.py",
"SCRIPT_PARAMETERS":"--platform 3rdParty --type 3rdParty_all"
}
},
"debug_vs2017": {
"TAGS":[
"metric"
],
"COMMAND":"build_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"debug",
"OUTPUT_DIRECTORY":"build\\windows_vs2017",
"CMAKE_OPTIONS":"-G \"Visual Studio 15 2017\" -A x64 -T host=x64 -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample",
"CMAKE_TARGET":"ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "/m:4 /p:CL_MPCount=!HALF_PROCESSORS! /nologo"
}
},
"test_debug_vs2017": {
"TAGS":[
"metric"
],
"COMMAND":"build_test_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"debug",
"OUTPUT_DIRECTORY":"build\\windows_vs2017",
"CMAKE_OPTIONS":"-G \"Visual Studio 15 2017\" -A x64 -T host=x64 -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample",
"CMAKE_TARGET":"TEST_SUITE_smoke TEST_SUITE_main",
"CMAKE_NATIVE_BUILD_ARGS": "/m:4 /p:CL_MPCount=!HALF_PROCESSORS! /nologo",
"CTEST_OPTIONS":"-L \"(smoke|main)\""
}
},
"profile_vs2017": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"build_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\windows_vs2017",
"CMAKE_OPTIONS":"-G \"Visual Studio 15 2017\" -A x64 -T host=x64 -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "/m:4 /p:CL_MPCount=!HALF_PROCESSORS! /nologo"
}
},
"test_profile_vs2017": {
"TAGS":[
"metric"
],
"COMMAND":"build_test_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\windows_vs2017",
"CMAKE_OPTIONS":"-G \"Visual Studio 15 2017\" -A x64 -T host=x64 -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"TEST_SUITE_smoke TEST_SUITE_main",
"CMAKE_NATIVE_BUILD_ARGS": "/m:4 /p:CL_MPCount=!HALF_PROCESSORS! /nologo",
"CTEST_OPTIONS":"-L \"(smoke|main)\""
}
},
"asset_profile_vs2017": {
"TAGS":[
"metric"
],
"COMMAND":"build_asset_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\windows_vs2017",
"CMAKE_OPTIONS":"-G \"Visual Studio 15 2017\" -A x64 -T host=x64 -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"AssetProcessorBatch",
"CMAKE_NATIVE_BUILD_ARGS": "/m:4 /p:CL_MPCount=!HALF_PROCESSORS! /nologo",
"ASSET_PROCESSOR_BINARY": "bin\\profile\\AssetProcessorBatch.exe",
"ASSET_PROCESSOR_OPTIONS": "/zeroAnalysisMode",
"ASSET_PROCESSOR_PLATFORMS":"pc,server"
}
},
"release_vs2017": {
"TAGS":[
"metric"
],
"COMMAND":"build_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"release",
"OUTPUT_DIRECTORY":"build\\windows_vs2017",
"CMAKE_OPTIONS":"-G \"Visual Studio 15 2017\" -A x64 -T host=x64 -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "/m:4 /p:CL_MPCount=!HALF_PROCESSORS! /nologo"
}
},
"monolithic_release_vs2017": {
"TAGS":[
"metric"
],
"COMMAND":"build_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"release",
"OUTPUT_DIRECTORY":"build\\mono_windows_vs2017",
"CMAKE_OPTIONS":"-G \"Visual Studio 15 2017\" -A x64 -T host=x64 -DCMAKE_SYSTEM_VERSION=10.0 -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "/m:4 /p:CL_MPCount=!HALF_PROCESSORS! /nologo"
}
},
"debug_vs2019": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"build_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"debug",
"OUTPUT_DIRECTORY":"build\\windows_vs2019",
"CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample",
"CMAKE_TARGET":"ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo"
}
},
"test_debug_vs2019": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"build_test_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"debug",
"OUTPUT_DIRECTORY":"build\\windows_vs2019",
"CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_BUILD_WITH_INCREMENTAL_LINKING_DEBUG=FALSE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample",
"CMAKE_TARGET":"TEST_SUITE_smoke TEST_SUITE_main",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo",
"CTEST_OPTIONS":"-L \"(smoke|main)\""
}
},
"profile_vs2019": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"build_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\windows_vs2019",
"CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo"
}
},
"profile_vs2019_nounity": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"build_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\windows_vs2019",
"CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=FALSE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo"
}
},
"test_profile_vs2019": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"build_test_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\windows_vs2019",
"CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"TEST_SUITE_smoke TEST_SUITE_main",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo",
"CTEST_OPTIONS":"-L \"(smoke|main)\""
}
},
"asset_profile_vs2019": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"build_asset_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\windows_vs2019",
"CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"AssetProcessorBatch",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo",
"ASSET_PROCESSOR_BINARY": "bin\\profile\\AssetProcessorBatch.exe",
"ASSET_PROCESSOR_OPTIONS": "/zeroAnalysisMode",
"ASSET_PROCESSOR_PLATFORMS":"pc,server"
}
},
"periodic_test_profile_vs2019" : {
"TAGS":[
"nightly",
"metric"
],
"COMMAND":"build_test_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\windows_vs2019",
"CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"TEST_SUITE_periodic",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo",
"CTEST_OPTIONS":"-L \"(periodic)\""
}
},
"sandbox_test_profile_vs2019" : {
"TAGS":[
"nightly",
"metric"
],
"COMMAND":"build_test_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\windows_vs2019",
"CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"TEST_SUITE_sandbox",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo",
"CTEST_OPTIONS":"-L \"(sandbox)\""
}
},
"benchmark_test_profile_vs2019" : {
"TAGS":[
"nightly",
"metric"
],
"COMMAND":"build_test_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\windows_vs2019",
"CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"TEST_SUITE_benchmark",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo",
"CTEST_OPTIONS":"-L \"(benchmark)\""
}
},
"release_vs2019": {
"TAGS":[
"metric"
],
"COMMAND":"build_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"release",
"OUTPUT_DIRECTORY":"build\\windows_vs2019",
"CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo"
}
},
"monolithic_release_vs2019": {
"TAGS":[
"default",
"metric"
],
"COMMAND":"build_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"release",
"OUTPUT_DIRECTORY":"build\\mono_windows_vs2019",
"CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;AtomTest;AtomSampleViewer;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo"
}
}
}
@@ -0,0 +1,30 @@
@ECHO OFF
REM
REM All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
REM its licensors.
REM
REM For complete copyright and license terms please see the LICENSE at the root of this
REM distribution (the "License"). All use of this software is governed by the License,
REM or, if provided, by the license below or the license accompanying this file. Do not
REM remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM
SETLOCAL EnableDelayedExpansion
IF NOT EXIST "%LY_NINJA_PATH%" (
SET LY_NINJA_PATH=%LY_3RDPARTY_PATH%/ninja/1.10.1/Windows
)
IF NOT EXIST "%LY_NINJA_PATH%" (
ECHO [ci_build] FAIL: LY_NINJA_PATH=%LY_NINJA_PATH%
GOTO :error
)
PATH %LY_NINJA_PATH%;%PATH%
CALL "%~dp0build_windows.cmd"
IF NOT %ERRORLEVEL%==0 GOTO :error
EXIT /b 0
:error
EXIT /b 1
@@ -0,0 +1,22 @@
@ECHO OFF
REM
REM All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
REM its licensors.
REM
REM For complete copyright and license terms please see the LICENSE at the root of this
REM distribution (the "License"). All use of this software is governed by the License,
REM or, if provided, by the license below or the license accompanying this file. Do not
REM remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM
CALL "%~dp0build_windows.cmd"
IF NOT %ERRORLEVEL%==0 GOTO :error
CALL "%~dp0test_windows.cmd"
IF NOT %ERRORLEVEL%==0 GOTO :error
EXIT /b 0
:error
EXIT /b 1
@@ -0,0 +1,82 @@
@ECHO OFF
REM
REM All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
REM its licensors.
REM
REM For complete copyright and license terms please see the LICENSE at the root of this
REM distribution (the "License"). All use of this software is governed by the License,
REM or, if provided, by the license below or the license accompanying this file. Do not
REM remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM
SETLOCAL EnableDelayedExpansion
CALL %~dp0env_windows.cmd
REM Delete output directory if CLEAN_OUTPUT_DIRECTORY env variable is set
IF "%CLEAN_OUTPUT_DIRECTORY%"=="true" (
IF EXIST %OUTPUT_DIRECTORY% (
ECHO [ci_build] CLEAN_OUTPUT_DIRECTORY option set with value "%CLEAN_OUTPUT_DIRECTORY%"
ECHO [ci_build] Deleting "%OUTPUT_DIRECTORY%"
DEL /s /q /f %OUTPUT_DIRECTORY%
)
)
IF NOT EXIST "%OUTPUT_DIRECTORY%" (
MKDIR %OUTPUT_DIRECTORY%.
)
SET SOURCE_DIRECTORY=%CD%
PUSHD %OUTPUT_DIRECTORY%
ECHO [ci_build] cmake --version
cmake --version
IF ERRORLEVEL 1 (
ECHO [ci_build] CMAKE not found!
exit /b 1
)
REM Jenkins reports MSB8029 when TMP/TEMP is not defined, define a dummy folder
SET TMP=%cd%/temp
SET TEMP=%cd%/temp
IF NOT EXIST %TMP% (
MKDIR temp
)
REM Compute half the amount of processors so some jobs can run
SET /a HALF_PROCESSORS = NUMBER_OF_PROCESSORS / 2
SET LAST_CONFIGURE_CMD_FILE=ci_last_configure_cmd.txt
SET CONFIGURE_CMD=cmake %SOURCE_DIRECTORY% %CMAKE_OPTIONS% %EXTRA_CMAKE_OPTIONS% -DLY_3RDPARTY_PATH="%LY_3RDPARTY_PATH%" -DLY_PROJECTS=%CMAKE_LY_PROJECTS%
IF NOT EXIST CMakeCache.txt (
ECHO [ci_build] First run, generating
SET RUN_CONFIGURE=1
) ELSE IF NOT EXIST %LAST_CONFIGURE_CMD_FILE% (
ECHO [ci_build] Last run command not found, generating
SET RUN_CONFIGURE=1
) ELSE (
REM Detect if the input has changed
FOR /F "delims=" %%x in (%LAST_CONFIGURE_CMD_FILE%) DO SET LAST_CMD=%%x
IF !LAST_CMD! NEQ !CONFIGURE_CMD! (
ECHO [ci_build] Last run command different, generating
SET RUN_CONFIGURE=1
)
)
IF DEFINED RUN_CONFIGURE (
ECHO [ci_build] %CONFIGURE_CMD%
%CONFIGURE_CMD%
IF NOT !ERRORLEVEL!==0 GOTO :error
ECHO !CONFIGURE_CMD!> %LAST_CONFIGURE_CMD_FILE%
)
ECHO [ci_build] cmake --build . --target %CMAKE_TARGET% --config %CONFIGURATION% %CMAKE_BUILD_ARGS% -- %CMAKE_NATIVE_BUILD_ARGS%
cmake --build . --target %CMAKE_TARGET% --config %CONFIGURATION% %CMAKE_BUILD_ARGS% -- %CMAKE_NATIVE_BUILD_ARGS%
IF NOT %ERRORLEVEL%==0 GOTO :error
POPD
EXIT /b 0
:error
POPD
EXIT /b 1
@@ -0,0 +1,31 @@
@ECHO OFF
REM
REM All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
REM its licensors.
REM
REM For complete copyright and license terms please see the LICENSE at the root of this
REM distribution (the "License"). All use of this software is governed by the License,
REM or, if provided, by the license below or the license accompanying this file. Do not
REM remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM
where /Q cmake
IF NOT %ERRORLEVEL%==0 (
IF "%LY_CMAKE_PATH%"=="" (SET LY_CMAKE_PATH=%LY_3RDPARTY_PATH%/CMake/3.19.1/Windows/bin)
IF NOT EXIST !LY_CMAKE_PATH! (
ECHO [ci_build] CMake path not found
GOTO :error
)
PATH !LY_CMAKE_PATH!;!PATH!
where /Q cmake
IF NOT !ERRORLEVEL!==0 (
ECHO [ci_build] CMake not found
GOTO :error
)
)
EXIT /b 0
:error
EXIT /b 1
@@ -0,0 +1,24 @@
{
"profile_vs2017_atom": {
"COMMAND": "build_windows.cmd",
"PARAMETERS": {
"CONFIGURATION": "profile",
"OUTPUT_DIRECTORY": "windows_vs2017",
"CMAKE_OPTIONS": "-G \"Visual Studio 15 2017\" -A x64 -T host=x64 -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS": "AtomTest;AtomSampleViewer",
"CMAKE_TARGET": "ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "/m:4 /p:CL_MPCount=!HALF_PROCESSORS! /nologo"
}
},
"profile_vs2019_atom": {
"COMMAND":"build_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"windows_vs2019",
"CMAKE_OPTIONS":"-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"AtomTest;AtomSampleViewer",
"CMAKE_TARGET":"ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo"
}
}
}
@@ -0,0 +1,8 @@
{
"JOB_NAME": "WIN",
"LABEL": "windows",
"LY_3RDPARTY_PATH": "C:/ly/3rdParty",
"TIMEOUT": 30,
"WORKSPACE": "D:/workspace",
"MOUNT_VOLUME": true
}
@@ -0,0 +1,18 @@
@ECHO OFF
REM
REM All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
REM its licensors.
REM
REM For complete copyright and license terms please see the LICENSE at the root of this
REM distribution (the "License"). All use of this software is governed by the License,
REM or, if provided, by the license below or the license accompanying this file. Do not
REM remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM
SETLOCAL EnableDelayedExpansion
ECHO [ci_build] python/python.cmd -u %SCRIPT_PATH% %SCRIPT_PARAMETERS%
CALL python/python.cmd -u %SCRIPT_PATH% %SCRIPT_PARAMETERS%
EXIT /b %ERRORLEVEL%
@@ -0,0 +1,41 @@
@ECHO OFF
REM
REM All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
REM its licensors.
REM
REM For complete copyright and license terms please see the LICENSE at the root of this
REM distribution (the "License"). All use of this software is governed by the License,
REM or, if provided, by the license below or the license accompanying this file. Do not
REM remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM
SETLOCAL EnableDelayedExpansion
CALL %~dp0env_windows.cmd
IF NOT EXIST %OUTPUT_DIRECTORY% (
ECHO [ci_build] Error: $OUTPUT_DIRECTORY was not found
GOTO :error
)
PUSHD %OUTPUT_DIRECTORY%
REM Find the CTEST_RUN_FLAGS from cmake's cache variables, then replace the $<CONFIG> with the current configuration
FOR /F "tokens=2 delims==" %%V IN ('cmake -N -LA . ^| findstr /I CTEST_RUN_FLAGS:STRING') do (
SET CTEST_RUN_FLAGS=%%V
)
SET CTEST_RUN_FLAGS=%CTEST_RUN_FLAGS:$<CONFIG>=!CONFIGURATION!%
REM Run ctest
ECHO [ci_build] ctest !CTEST_RUN_FLAGS! !CTEST_OPTIONS!
ctest !CTEST_RUN_FLAGS! !CTEST_OPTIONS!
IF NOT %ERRORLEVEL%==0 GOTO :popd_error
POPD
EXIT /b 0
:popd_error
POPD
:error
EXIT /b 1
@@ -0,0 +1,84 @@
{
"metrics": {
"TAGS":[
"weekly"
],
"COMMAND":"../Mac/python_mac.sh",
"PARAMETERS": {
"SCRIPT_PATH":"Tools/build/JenkinsScripts/build/ci_build_metrics.py",
"SCRIPT_PARAMETERS":"--platform iOS --jobname '${JOB_NAME}' --jobnumber '${BUILD_NUMBER}' --jobnode '${NODE_NAME}' --changelist '${CHANGE_ID}'"
}
},
"debug": {
"TAGS":[
"nightly"
],
"COMMAND":"../Mac/build_mac.sh",
"PARAMETERS": {
"CONFIGURATION":"debug",
"OUTPUT_DIRECTORY":"build/ios",
"CMAKE_OPTIONS":"-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "-destination generic/platform=iOS"
}
},
"profile": {
"TAGS":[
"nightly"
],
"COMMAND":"../Mac/build_mac.sh",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build/ios",
"CMAKE_OPTIONS":"-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "-destination generic/platform=iOS"
}
},
"profile_nounity": {
"TAGS":[
"nightly"
],
"COMMAND":"../Mac/build_mac.sh",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build/ios",
"CMAKE_OPTIONS":"-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE -DLY_UNITY_BUILD=FALSE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "-destination generic/platform=iOS"
}
},
"asset_profile": {
"TAGS":[
"nightly"
],
"COMMAND":"../Mac/build_asset_mac.sh",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build/mac",
"CMAKE_OPTIONS":"-G Xcode -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample",
"CMAKE_TARGET":"AssetProcessorBatch",
"ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch",
"ASSET_PROCESSOR_OPTIONS": "/zeroAnalysisMode",
"ASSET_PROCESSOR_PLATFORMS":"ios"
}
},
"release": {
"TAGS":[
"nightly"
],
"COMMAND":"../Mac/build_mac.sh",
"PARAMETERS": {
"CONFIGURATION":"release",
"OUTPUT_DIRECTORY":"build/ios",
"CMAKE_OPTIONS":"-G Xcode -DCMAKE_TOOLCHAIN_FILE=cmake/Platform/iOS/Toolchain_ios.cmake -DLY_MONOLITHIC_GAME=TRUE -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=FALSE -DLY_IOS_CODE_SIGNING_IDENTITY=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS=\"\" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=FALSE -DLY_UNITY_BUILD=TRUE",
"CMAKE_LY_PROJECTS":"CMakeTestbed;StarterGame;SamplesProject;Helios;MultiplayerSample;AutomatedTesting",
"CMAKE_TARGET":"ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "-destination generic/platform=iOS"
}
}
}
@@ -0,0 +1,8 @@
{
"JOB_NAME": "IOS",
"LABEL": "mac",
"LY_3RDPARTY_PATH": "/Users/lybuilder/3rdParty",
"TIMEOUT": 30,
"WORKSPACE": "/Users/lybuilder/workspace",
"MOUNT_VOLUME": false
}
@@ -0,0 +1,104 @@
#
# 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 os
import sys
import subprocess
def parse_args():
cur_dir = os.path.dirname(os.path.abspath(__file__))
parser = argparse.ArgumentParser()
parser.add_argument('-p', '--platform', dest="build_platform", help="Platform to build")
parser.add_argument('-t', '--type', dest="build_type", help="Target config type to build")
parser.add_argument('-c', '--config', dest="build_config_filename",
default="build_config.json",
help="JSON filename in Platform/<platform> that defines build configurations for the platform")
args = parser.parse_args()
# Input validation
if args.build_platform is None:
print('[ci_build] No platform specified')
sys.exit(-1)
if args.build_type is None:
print('[ci_build] No type specified')
sys.exit(-1)
return args
def build(build_config_filename, build_platform, build_type):
# Read build_config and locate build_type
current_dir = os.path.dirname(os.path.abspath(__file__))
cwd_dir = os.path.abspath(os.path.join(current_dir, '../../../..')) # engine's root
config_dir = os.path.abspath(os.path.join(current_dir, 'Platform', build_platform))
build_config_abspath = os.path.join(config_dir, build_config_filename)
if not os.path.exists(build_config_abspath):
config_dir = os.path.abspath(os.path.join(cwd_dir, 'restricted', build_platform, os.path.relpath(current_dir, cwd_dir)))
build_config_abspath = os.path.join(config_dir, build_config_filename)
if not os.path.exists(build_config_abspath):
print('[ci_build] File: {} not found'.format(build_config_abspath))
return -1
with open(build_config_abspath) as f:
build_config_json = json.load(f)
build_type_config = build_config_json[build_type]
if build_type_config is None:
print('[ci_build] Build type {} was not found in {}'.format(build_type, build_config_abspath))
# Load the command to execute
build_cmd = build_type_config['COMMAND']
if build_cmd is None:
print('[ci_build] Build type {} in {} is missing required COMMAND entry'.format(build_type, build_config_abspath))
return -1
build_params = build_type_config['PARAMETERS']
# Parameters are optional, so we could have none
# build_cmd is relative to the folder where this file is
build_cmd_path = os.path.join(current_dir, 'Platform/{}/{}'.format(build_platform, build_cmd))
if not os.path.exists(build_cmd_path):
config_dir = os.path.abspath(os.path.join(cwd_dir, 'restricted', build_platform, os.path.relpath(current_dir, cwd_dir)))
build_cmd_path = os.path.join(config_dir, build_cmd)
if not os.path.exists(build_cmd_path):
print('[ci_build] File: {} not found'.format(build_cmd_path))
return -1
print('[ci_build] Executing \"{}\"'.format(build_cmd_path))
print(' cwd = {}'.format(cwd_dir))
print(' paramaters:')
env_params = os.environ.copy()
for v in build_params:
if v[:6] == "FORCE:":
env_params[v[6:]] = build_params[v]
print(' {} = {} (forced)'.format(v[6:], env_params[v[6:]]))
else:
existing_param = env_params.get(v)
if not existing_param:
env_params[v] = build_params[v]
print(' {} = {} {}'.format(v, env_params[v], '(environment override)' if existing_param else ''))
print('--------------------------------------------------------------------------------', flush=True)
process_return = subprocess.run(build_cmd_path, cwd=cwd_dir, env=env_params)
print('--------------------------------------------------------------------------------')
if process_return.returncode != 0:
print('[ci_build] FAIL: Command {} returned {}'.format(build_cmd_path, process_return.returncode), flush=True)
return process_return.returncode
else:
print('[ci_build] OK', flush=True)
return 0
if __name__ == "__main__":
args = parse_args()
ret = build(args.build_config_filename, args.build_platform, args.build_type)
sys.exit(ret)
@@ -0,0 +1,292 @@
#
# 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 datetime
import json
import math
import os
import platform
import psutil
import shutil
import stat
import subprocess
import sys
import time
import ci_build
from pathlib import Path
import submit_metrics
if platform.system() == 'Windows':
EXE_EXTENSION = '.exe'
else:
EXE_EXTENSION = ''
DATE_FORMAT = "%Y-%m-%dT%H:%M:%S"
METRICS_TEST_MODE = os.environ.get('METRICS_TEST_MODE')
def parse_args():
cur_dir = os.path.dirname(os.path.abspath(__file__))
parser = argparse.ArgumentParser()
parser.add_argument('-p', '--platform', dest="platform", help="Platform to gather metrics for")
parser.add_argument('-a', '--jobname', dest="jobname", default="unknown", help="Name/tag of the job in the CI system (used to track where the report comes from, constant through multiple runs)")
parser.add_argument('-u', '--jobnumber', dest="jobnumber", default=-1, help="Number of run in the CI system (used to track where the report comes from, variable through runs)")
parser.add_argument('-o', '--jobnode', dest="jobnode", default="unknown", help="Build node name (used to track where the build happened in CI systems where the same jobs run in different hosts)")
parser.add_argument('-l', '--changelist', dest="changelist", default=-1, help="Last changelist in this workspace")
parser.add_argument('-c', '--config', dest="build_config_filename", default="build_config.json",
help="JSON filename in Platform/<platform> that defines build configurations for the platform")
args = parser.parse_args()
# Input validation
if args.platform is None:
print('[ci_build_metrics] No platform specified')
sys.exit(-1)
return args
def shutdown_processes():
process_list = {f'AssetBuilder{EXE_EXTENSION}', f'AssetProcessor{EXE_EXTENSION}', f'RC{EXE_EXTENSION}', f'Editor{EXE_EXTENSION}'}
for process in psutil.process_iter():
try:
if process.name() in process_list:
process.kill()
except Exception as e:
print(f'Exception while trying to kill process: {e}')
def on_rmtree_error(func, path, exc_info):
if os.path.exists(path):
try:
os.chmod(path, stat.S_IWRITE)
os.unlink(path)
except Exception as e:
print(f'Exception while trying to remove {path}: {e}')
def clean_folder(folder):
if os.path.exists(folder):
print(f'[ci_build_metrics] Cleaning {folder}...', flush=True)
if not METRICS_TEST_MODE:
shutil.rmtree(folder, onerror=on_rmtree_error)
print(f'[ci_build_metrics] Cleaned {folder}', flush=True)
def compute_folder_size(folder):
total = 0
if os.path.exists(folder):
folder_path = Path(folder)
print(f'[ci_build_metrics] Computing size of {folder_path}...', flush=True)
if not METRICS_TEST_MODE:
total += sum(f.stat().st_size for f in folder_path.glob('**/*') if f.is_file())
print(f'[ci_build_metrics] Computed size of {folder_path}', flush=True)
return total
def build(metrics, folders_of_interest, build_config_filename, platform, build_type, output_directory, time_delta = 0):
build_start = time.time()
if not METRICS_TEST_MODE:
metrics['result'] = ci_build.build(build_config_filename, platform, build_type)
else:
metrics['result'] = -1 # mark as failure so the data is not used in elastic
build_end = time.time()
# truncate the duration (expressed in seconds), we dont need more precision
metrics['duration'] = math.trunc(build_end - build_start - time_delta)
metrics['output_sizes'] = []
output_sizes = metrics['output_sizes']
for folder in folders_of_interest:
output_size = dict()
if folder != output_directory:
output_size['folder'] = folder
else:
output_size['folder'] = 'OUTPUT_DIRECTORY' # make it homogenous to facilitate search
output_size['output_size'] = compute_folder_size(os.path.join(engine_dir, folder))
output_sizes.append(output_size)
def gather_build_metrics(current_dir, build_config_filename, platform):
config_dir = os.path.abspath(os.path.join(current_dir, 'Platform', platform))
build_config_abspath = os.path.join(config_dir, build_config_filename)
if not os.path.exists(build_config_abspath):
cwd_dir = os.path.abspath(os.path.join(current_dir, '../../../..')) # engine's root
config_dir = os.path.abspath(os.path.join(cwd_dir, 'restricted', platform, os.path.relpath(current_dir, cwd_dir)))
build_config_abspath = os.path.join(config_dir, build_config_filename)
if not os.path.exists(build_config_abspath):
print(f'[ci_build_metrics] File: {build_config_abspath} not found', flush=True)
sys.exit(-1)
with open(build_config_abspath) as f:
build_config_json = json.load(f)
all_builds_metrics = []
for build_type in build_config_json:
build_config = build_config_json[build_type]
if not 'metric' in build_config['TAGS']:
# skip build configs that are not tagged with 'metric'
continue
print(f'[ci_build_metrics] Starting {build_type}', flush=True)
metrics = dict()
metrics['build_type'] = build_type
build_parameters = build_config['PARAMETERS']
if not build_parameters:
metrics['result'] = -1
reason = f'PARAMETERS entry {build_type} in {build_config_abspath} is missing.'
metrics['reason'] = reason
print(f'[ci_build_metrics] {reason}', flush=True)
continue
# Clean the output
output_directory = build_parameters['OUTPUT_DIRECTORY']
if not output_directory:
metrics['result'] = -1
reason = f'OUTPUT_DIRECTORY entry in {build_config_abspath} is missing.'
metrics['reason'] = reason
print(f'[ci_build_metrics] {reason}', flush=True)
continue
folders_of_interest = ['Cache', 'AssetProcessorTemp', output_directory]
metrics['build_metrics'] = []
build_metrics = metrics['build_metrics']
# Do the clean build
shutdown_processes()
for folder in folders_of_interest:
clean_folder(os.path.join(engine_dir, folder))
build_metric_clean = dict()
build_metric_clean['build_metric'] = 'clean'
build(build_metric_clean, folders_of_interest, build_config_filename, platform, build_type, output_directory)
build_metrics.append(build_metric_clean)
# Do the incremental "zero" build
build_metric_zero = dict()
build_metric_zero['build_metric'] = 'zero'
build(build_metric_zero, folders_of_interest, build_config_filename, platform, build_type, output_directory)
build_metrics.append(build_metric_zero)
# Do a reconfigure
# To measure a reconfigure, we will delete the "ci_last_configure_cmd.txt" file from the output and trigger a
# zero build, then we will substract the time from the zero_build above
last_configure_file = os.path.join(output_directory, 'ci_last_configure_cmd.txt')
if os.path.exists(last_configure_file):
os.remove(last_configure_file)
build_metric_generation = dict()
build_metric_generation['build_metric'] = 'generation'
build(build_metric_generation, folders_of_interest, build_config_filename, platform, build_type, output_directory, build_metric_zero['duration'])
build_metrics.append(build_metric_generation)
# Clean the otuput before ending to reduce the size of these workspaces
shutdown_processes()
for folder in folders_of_interest:
clean_folder(os.path.join(engine_dir, folder))
metrics['result'] = 0
metrics['reason'] = 'OK'
all_builds_metrics.append(metrics)
return all_builds_metrics
def prepare_metrics(args, build_metrics):
return {
'changelist': args.changelist,
'job': {'name': args.jobname, 'number': args.jobnumber, 'node': args.jobnode},
'platform': args.platform,
'build_types': build_metrics,
'timestamp': timestamp.strftime("%Y-%m-%dT%H:%M:%S")
}
def upload_to_s3(upload_script_path, base_dir, bucket, key_prefix):
try:
subprocess.run([sys.executable, upload_script_path,
'--base_dir', base_dir,
'--file_regex', '.*',
'--bucket', bucket,
'--key_prefix', key_prefix],
check=True)
except subprocess.CalledProcessError as err:
print(f'[ci_build_metrics] {upload_script_path} failed with error {err}')
sys.exit(1)
def submit_report_document(report_file):
print(f'[ci_build_metrics] Submitting {report_file}')
with open(report_file) as json_file:
report_json = json.load(json_file)
ret = True
for build_type in report_json['build_types']:
for build_metric in build_type['build_metrics']:
newjson = {
'timestamp': report_json['timestamp'],
'changelist': report_json['changelist'],
'job': report_json['job'],
'platform': report_json['platform'],
'type': build_type['build_type'],
'result': int(build_type['result']) or int(build_metric['result']),
'reason': build_type['reason'],
'metric': build_metric['build_metric'],
'duration': build_metric['duration'],
'output_sizes': build_metric['output_sizes']
}
index = "pappeste.build_metrics." + datetime.datetime.strptime(report_json['timestamp'], DATE_FORMAT).strftime("%Y.%m")
ret &= submit_metrics.submit(index, newjson)
if ret:
print(f'[ci_build_metrics] {report_file} submitted')
else:
print(f'[ci_build_metrics] {report_file} failed to submit')
return ret
if __name__ == "__main__":
args = parse_args()
print(f"[ci_build_metrics] Generatic build metrics for:"
f"\n\tPlatform: {args.platform}"
f"\n\tJob Name: {args.jobname}"
f"\n\tJob Number: {args.jobnumber}"
f"\n\tJob Node: {args.jobnode}"
f"\n\tChangelist: {args.changelist}")
# Read build_config
current_dir = os.path.dirname(os.path.abspath(__file__))
engine_dir = os.path.abspath(os.path.join(current_dir, '../../../..')) # engine's root
timestamp = datetime.datetime.now()
build_metrics = gather_build_metrics(current_dir, args.build_config_filename, args.platform)
metrics = prepare_metrics(args, build_metrics)
# Temporarly just printing the metrics until we get an API to uplaod it to CloudWatch
# SPEC-1810 will then upload these metrics
print("[ci_build_metrics] metrics:")
print(json.dumps(metrics, sort_keys=True, indent=4))
metric_file_path = os.path.join(engine_dir, 'build_metrics')
if os.path.exists(metric_file_path):
shutil.rmtree(metric_file_path)
os.makedirs(metric_file_path)
metric_file_path = os.path.join(metric_file_path, timestamp.strftime("%Y%m%d_%H%M%S.json"))
with open(metric_file_path, 'w') as metric_file:
json.dump(metrics, metric_file, sort_keys=True, indent=4)
# transfer
upload_script = os.path.join(current_dir, 'utils', 'upload_to_s3.py')
upload_to_s3(upload_script, os.path.join(engine_dir, 'build_metrics'), 'ly-jenkins-cmake-metrics', args.jobname)
# submit
submit_report_document(metric_file_path)
# Dont cleanup, next build will remove the file, but leaving it helps to do some post-build forensics
@@ -0,0 +1,360 @@
#
# 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 sys
import glob_to_regex
import zipfile
import timeit
import stat
from optparse import OptionParser
from PackageEnv import PackageEnv
from ci_build import build
from utils.util import *
from utils.lib.glob3 import glob
def package(options):
package_platform = options.package_platform
package_env = PackageEnv(package_platform, options.package_env)
engine_root = package_env.get('ENGINE_ROOT')
# Ask the validator code to tell us which files need to be removed from the package
prohibited_file_mask = get_prohibited_file_mask(package_platform, engine_root)
# Scrub files. This is destructive, but is necessary to allow the current file existance checks to work properly. Better to copy and then build, or to
# mask on sync, but this is what we have for now
# No need to run scrubbing script since all restricted platform codes are moved to dev/restricted folder
#scrub_files(package_env, prohibited_file_mask)
# validate files
validate_restricted_files(package_platform, package_env)
# Override values in bootstrap.cfg for PC package
override_bootstrap_cfg(package_env)
# Generate GameTemplates whitelist information for metrics reporting
template_whitelist_script = os.path.join(engine_root, 'Tools/build/JenkinsScripts/distribution/Metrics/GameTemplates/buildGameTemplateWhitelist.py')
if os.path.exists(template_whitelist_script):
if sys.platform == 'win32':
python = os.path.join(engine_root, 'Tools', 'Python', 'python.cmd')
else:
python = os.path.join(engine_root, 'Tools', 'Python', 'python.sh')
project_templates_folder = os.path.join(engine_root, 'ProjectTemplates')
args = [python, template_whitelist_script, '--projectTemplatesFolder', project_templates_folder]
#execute_system_call(args)
if not package_env.get('SKIP_BUILD'):
print('SKIP_BUILD is False, running CMake build...')
cmake_build(package_env)
# TODO Compile Assets
#if package_env.exists('ASSET_PROCESSOR_PATH'):
# compile_assets(package_env)
#create packages
create_packages(package_env)
def override_bootstrap_cfg(package_env):
print('Override values in bootstrap.cfg')
engine_root = package_env.get('ENGINE_ROOT')
bootstrap_path = os.path.join(engine_root, 'bootstrap.cfg')
replace_values = {'sys_game_folder':'{}'.format(package_env.get('BOOTSTRAP_CFG_GAME_FOLDER'))}
try:
with open(bootstrap_path, 'r') as bootstrap_cfg:
content = bootstrap_cfg.read()
except:
error('Cannot read file {}'.format(bootstrap_path))
content = content.split('\n')
new_content = []
for line in content:
if not line.startswith('--'):
strs = line.split('=')
if len(strs):
key = strs[0].strip(' ')
if key in replace_values:
line = '{}={}'.format(key, replace_values[key])
new_content.append(line)
try:
with open(bootstrap_path, 'w') as out:
out.write('\n'.join(new_content))
except:
error('Cannot write to file {}'.format(bootstrap_path))
print('{} updated with value {}'.format(bootstrap_path, replace_values))
def get_prohibited_file_mask(package_platform, engine_root):
sys.path.append(os.path.join(engine_root, 'Tools', 'build', 'JenkinsScripts', 'distribution', 'scrubbing'))
from validator_data_LEGAL_REVIEW_REQUIRED import get_prohibited_platforms_for_package
# The list of prohibited platforms is controlled by the validator on a per-package basis
prohibited_platforms = get_prohibited_platforms_for_package(package_platform)
prohibited_platforms.append('all')
excludes_list = []
for p in prohibited_platforms:
platform_excludes = glob_to_regex.generate_excludes_for_platform(engine_root, p)
excludes_list.extend(platform_excludes)
prohibited_file_mask = re.compile('|'.join(excludes_list), re.IGNORECASE)
return prohibited_file_mask
def scrub_files(package_env, prohibited_file_mask):
print('Perform the Code Scrubbing')
engine_root = package_env.get('ENGINE_ROOT')
success = True
for dirname, subFolders, files in os.walk(engine_root):
for filename in files:
full_path = os.path.join(dirname, filename)
if prohibited_file_mask.match(full_path):
try:
print('Deleting: {}'.format(full_path))
os.chmod(full_path, stat.S_IWRITE)
os.unlink(full_path)
except:
e = sys.exc_info()[0]
sys.stderr.write('Error: could not delete {} ... aborting.\n'.format(full_path))
sys.stderr.write('{}\n'.format(str(e)))
success = False
if not success:
sys.stderr.write('ERROR: scrub_files failed\n')
sys.exit(1)
def validate_restricted_files(package, package_env):
print('Perform the Code Scrubbing')
engine_root = package_env.get('ENGINE_ROOT')
# Run validator
success = True
validator_path = os.path.join(engine_root, 'Tools/build/JenkinsScripts/distribution/scrubbing/validator.py')
if sys.platform == 'win32':
python = os.path.join(engine_root, 'Tools', 'Python', 'python3.cmd')
else:
python = os.path.join(engine_root, 'Tools', 'Python', 'python3.sh')
args = [python, validator_path, '--package', package, engine_root]
return_code = safe_execute_system_call(args)
if return_code != 0:
success = False
if not success:
error('Restricted file validator failed.')
print('Restricted file validator completed successfully.')
def cmake_build(package_env):
build_targets = package_env.get('BUILD_TARGETS')
for build_target in build_targets:
build(build_target['BUILD_CONFIG_FILENAME'], build_target['PLATFORM'], build_target['TYPE'])
def create_packages(package_env):
package_targets = package_env.get('PACKAGE_TARGETS')
for package_target in package_targets:
print('Creating zipfile for package target {}'.format(package_target))
cur_dir = os.path.dirname(os.path.abspath(__file__))
filelist = os.path.join(cur_dir, 'package_filelists', '{}.json'.format(package_target['TYPE']))
with open(filelist, 'r') as source:
data = json.load(source)
lyengine = os.path.dirname(package_env.get('ENGINE_ROOT'))
print('Calculating filelists...')
files = {}
# We have to include 3rdParty in Mac/Xenia/Provo/Salem/Consoles package until LAD is available for those platforms
# Remove this when LAD is available for those platforms.
if package_target['TYPE'] in ['cmake_consoles', 'consoles']:
files.update(get_3rdparty_filelist(package_env, 'common'))
files.update(get_3rdparty_filelist(package_env, 'vc141'))
files.update(get_3rdparty_filelist(package_env, 'vc142'))
files.update(get_3rdparty_filelist(package_env, 'provo'))
files.update(get_3rdparty_filelist(package_env, 'xenia'))
elif package_target['TYPE'] in ['cmake_atom_pc']:
files.update(get_3rdparty_filelist(package_env, 'common'))
files.update(get_3rdparty_filelist(package_env, 'vc141'))
files.update(get_3rdparty_filelist(package_env, 'vc142'))
elif package_target['TYPE'] in ['cmake_all']:
if package_env.get_target_platform() == 'mac':
files.update(get_3rdparty_filelist(package_env, 'common'))
files.update(get_3rdparty_filelist(package_env, 'mac'))
elif package_env.get_target_platform() == 'consoles':
files.update(get_3rdparty_filelist(package_env, 'common'))
files.update(get_3rdparty_filelist(package_env, 'vc141'))
files.update(get_3rdparty_filelist(package_env, 'vc142'))
files.update(get_3rdparty_filelist(package_env, 'provo'))
files.update(get_3rdparty_filelist(package_env, 'xenia'))
if '@lyengine' in data:
if '@engine_root' in data['@lyengine']:
engine_root_basename = os.path.basename(package_env.get('ENGINE_ROOT'))
data['@lyengine'][engine_root_basename] = data['@lyengine']['@engine_root']
data['@lyengine'].pop('@engine_root')
files.update(filter_files(data['@lyengine'], lyengine))
if '@3rdParty' in data:
files.update(filter_files(data['@3rdParty'], package_env.get('THIRDPARTY_HOME')))
package_path = os.path.join(lyengine, package_target['PACKAGE_NAME'])
print('Creating zipfile at {}'.format(package_path))
start = timeit.default_timer()
with zipfile.ZipFile(package_path, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) as myzip:
for f in files:
if os.path.islink(f):
zipInfo = zipfile.ZipInfo(files[f])
zipInfo.create_system = 3
# long type of hex val of '0xA1ED0000L',
# say, symlink attr magic...
#zipInfo.external_attr = 0xA1ED0000L
zipInfo.external_attr |= 0xA0000000
myzip.writestr(zipInfo, os.readlink(f))
else:
myzip.write(f, files[f])
stop = timeit.default_timer()
total_time = int(stop - start)
print('{} is created. Total time: {} seconds.'.format(package_path, total_time))
def get_MD5(file_path):
from hashlib import md5
chunk_size = 200 * 1024
h = md5()
with open(file_path, 'rb') as f:
while True:
chunk = f.read(chunk_size)
if len(chunk):
h.update(chunk)
else:
break
return h.hexdigest()
md5_file = '{}.MD5'.format(package_path)
print('Creating MD5 file at {}'.format(md5_file))
start = timeit.default_timer()
with open(md5_file, 'w') as output:
output.write(get_MD5(package_path))
stop = timeit.default_timer()
total_time = int(stop - start)
print('{} is created. Total time: {} seconds.'.format(md5_file, total_time))
def filter_files(data, base, prefix='', support_symlinks=True):
includes = {}
excludes = set()
for key, value in data.items():
pattern = os.path.join(base, prefix, key)
if not isinstance(value, dict):
pattern = os.path.normpath(pattern)
result = glob(pattern, recursive=True)
files = [x for x in result if os.path.isfile(x) or (support_symlinks and os.path.islink(x))]
if value == "#exclude":
excludes.update(files)
elif value == "#include":
for file in files:
includes[file] = os.path.relpath(file, base)
else:
if value.startswith('#move:'):
for file in files:
file_name = os.path.relpath(file, os.path.join(base, prefix))
dst_dir = value.replace('#move:', '').strip(' ')
includes[file] = os.path.join(dst_dir, file_name)
elif value.startswith('#rename:'):
for file in files:
dst_file = value.replace('#rename:', '').strip(' ')
includes[file] = dst_file
else:
warn('Unknown directive {} for pattern {}'.format(value, pattern))
else:
includes.update(filter_files(value, base, os.path.join(prefix, key), support_symlinks))
for exclude in excludes:
try:
includes.pop(exclude)
except KeyError:
pass
return includes
def get_3rdparty_filelist(package_env, platform, support_symlinks=True):
engine_root = package_env.get('ENGINE_ROOT')
include_pattern_file = 'include_pattern_file'
if os.path.isfile(include_pattern_file):
os.remove(include_pattern_file)
exclude_pattern_file = 'exclude_pattern_file'
if os.path.isfile(exclude_pattern_file):
os.remove(exclude_pattern_file)
versions_file = 'versions_file'
if os.path.isfile(versions_file):
os.remove(versions_file)
# Generate 3rdParty version file
ly_dep_version_tool = os.path.join(engine_root, 'Tools/build/JenkinsScripts/distribution/ly_dep_version_tool.py')
setup_assistant_config = os.path.join(engine_root, 'SetupAssistantConfig.json')
if sys.platform == 'win32':
python = os.path.join(engine_root, 'Tools', 'Python', 'python.cmd')
else:
python = os.path.join(engine_root, 'Tools', 'Python', 'python.sh')
args = [python, ly_dep_version_tool, '-o', versions_file, '-s', setup_assistant_config]
execute_system_call(args)
# Generate 3rdParty include pattern and exclude pattern
generate_external_3rdparty_file_list = os.path.join(engine_root, 'Tools/build/JenkinsScripts/distribution/ThirdParty/generate_external_3rdparty_file_list.py')
package_config = os.path.join(engine_root, 'Tools/build/JenkinsScripts/distribution/ThirdParty/CMakePackageConfig.json')
args = [python, generate_external_3rdparty_file_list, '-s', versions_file, '-c', package_config, '-p', platform, '-i', include_pattern_file, '-e', exclude_pattern_file]
execute_system_call(args)
# Calculate filelist using include pattern and exclude pattern
thirdparty_home = package_env.get('THIRDPARTY_HOME')
filelist = {}
with open(include_pattern_file, 'r') as source:
include_patterns = source.readlines()
for include_pattern in include_patterns:
pattern = os.path.join(thirdparty_home, include_pattern.strip('\n'))
pattern = os.path.normpath(pattern)
result = glob(pattern, recursive=True)
files = [x for x in result if os.path.isfile(x) or (support_symlinks and os.path.islink(x))]
for file in files:
filelist[file] = os.path.join('3rdParty', os.path.relpath(file, thirdparty_home))
with open(exclude_pattern_file, 'r') as source:
exclude_patterns = source.readlines()
for exclude_pattern in exclude_patterns:
pattern = os.path.join(thirdparty_home, exclude_pattern.strip('\n'))
pattern = os.path.normpath(pattern)
result = glob(pattern, recursive=True)
files = [x for x in result if os.path.isfile(x) or (support_symlinks and os.path.islink(x))]
for file in files:
try:
filelist.pop(file)
except KeyError:
pass
return filelist
def parse_args():
cur_dir = os.path.dirname(os.path.abspath(__file__))
parser = OptionParser()
parser.add_option("--release", dest="release", default=False, action='store_true', help="Release build")
parser.add_option("--package_platform", dest="package_platform", default='consoles', help="Target platform to package")
parser.add_option("--package_env", dest="package_env", default=os.path.join(cur_dir, "cmake_package_env.json"),
help="JSON file that defines package environment variables")
parser.add_option("--package_build_configurations_json", dest="package_build_configurations_json",
default=os.path.join(cur_dir, "package_build_configurations.json"),
help="JSON file that defines build parameters")
(options, args) = parser.parse_args()
if options.package_platform is None:
error('No package platform specified')
return options, args
if __name__ == "__main__":
(options, args) = parse_args()
package(options)
@@ -0,0 +1,111 @@
{
"global":{
"ENGINE_ROOT":"",
"THIRDPARTY_HOME":"",
"PACKAGE_NAME_PATTERN":"lumberyard-${MAJOR_VERSION}.${MINOR_VERSION}-${P4_CHANGELIST}",
"BUILD_NUMBER":"0",
"P4_CHANGELIST":"0",
"MAJOR_VERSION":"0",
"MINOR_VERSION":"0",
"LAD_PACKAGE_STORAGE_URL":"https://d7qxx8qkrwa8l.cloudfront.net"
},
"platforms":{
"consoles":{
"PACKAGE_TARGETS":[
{
"TYPE": "cmake_all",
"PACKAGE_NAME": "${PACKAGE_NAME_PATTERN}-consoles-${BUILD_NUMBER}.zip"
},
{
"TYPE": "symbols",
"PACKAGE_NAME": "${PACKAGE_NAME_PATTERN}-consoles-symbols-${BUILD_NUMBER}.zip"
}
],
"BOOTSTRAP_CFG_GAME_FOLDER":"CMakeTestbed",
"SKIP_BUILD": 1,
"BUILD_TARGETS":[
{
"BUILD_CONFIG_FILENAME": "build_config.json",
"PLATFORM": "Windows",
"TYPE": "profile_vs2017"
},
{
"BUILD_CONFIG_FILENAME": "build_config.json",
"PLATFORM": "Windows",
"TYPE": "profile_vs2019"
},
{
"BUILD_CONFIG_FILENAME": "build_config.json",
"PLATFORM": "Provo",
"TYPE": "profile"
},
{
"BUILD_CONFIG_FILENAME": "build_config.json",
"PLATFORM": "Xenia",
"TYPE": "profile"
}
]
},
"cmake_atom_pc":{
"PACKAGE_TARGETS":[
{
"TYPE": "cmake_atom_pc",
"PACKAGE_NAME": "${PACKAGE_NAME_PATTERN}-cmake_atom_pc-${BUILD_NUMBER}.zip"
}
],
"BOOTSTRAP_CFG_GAME_FOLDER":"AtomSampleViewer;AtomTest",
"SKIP_BUILD": 1,
"BUILD_TARGETS":[
{
"BUILD_CONFIG_FILENAME": "package_build_config.json",
"PLATFORM": "Windows",
"TYPE": "profile_vs2017_atom"
},
{
"BUILD_CONFIG_FILENAME": "package_build_config.json",
"PLATFORM": "Windows",
"TYPE": "profile_vs2019_atom"
}
]
},
"mac":{
"PACKAGE_TARGETS":[
{
"TYPE": "cmake_all",
"PACKAGE_NAME": "${PACKAGE_NAME_PATTERN}-cmake_mac_all-${BUILD_NUMBER}.zip"
}
],
"BOOTSTRAP_CFG_GAME_FOLDER":"CMakeTestbed",
"SKIP_BUILD": 1,
"BUILD_TARGETS":[
{
"BUILD_CONFIG_FILENAME": "build_config.json",
"PLATFORM": "Mac",
"TYPE": "profile"
},
{
"BUILD_CONFIG_FILENAME": "build_config.json",
"PLATFORM": "iOS",
"TYPE": "profile"
}
]
},
"linux":{
"PACKAGE_TARGETS":[
{
"TYPE": "cmake_all",
"PACKAGE_NAME": "${PACKAGE_NAME_PATTERN}-cmake_linux_all-${BUILD_NUMBER}.zip"
}
],
"BOOTSTRAP_CFG_GAME_FOLDER":"CMakeTestbed",
"SKIP_BUILD": 1,
"BUILD_TARGETS":[
{
"BUILD_CONFIG_FILENAME": "build_config.json",
"PLATFORM": "Linux",
"TYPE": "profile"
}
]
}
}
}
@@ -0,0 +1,80 @@
"""
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.
Downloads the latest package from a S3 and unzips it to a desired location.
"""
import argparse
import boto3
import os
import re
import zipfile
def download_and_unzip_package(bucket_name, package_regex, build_number_regex, folder_path, destination_path):
"""
Downloads a given package from a S3 and unzips it.
:param bucket_name: S3 bucket
:param package_regex: Regex to find the desired package
:param build_number_regex: Regex to find the build number from the package name
:param folder_path: Folder path to the package
:param destination_path: Where to download the package to
:return:
"""
# Make sure the directory exists
if not os.path.isdir(destination_path):
os.makedirs(destination_path)
# Sorting function for latest package
def get_build_number(file_name_to_parse):
return re.search(build_number_regex, file_name_to_parse).group(0)[:-4] # [:-4] removes the .zip extension
s3 = boto3.resource('s3')
bucket = s3.Bucket(bucket_name)
largest_build_number = -1
latest_file = 'No file found!'
# Find the latest package
print 'Reading files from bucket...'
for bucket_file in bucket.objects.filter(Prefix=folder_path):
file_name = bucket_file.key
if re.search(package_regex, file_name) and get_build_number(file_name) > largest_build_number:
largest_build_number = get_build_number(file_name)
latest_file = file_name
package_name = latest_file.split('/')[-1]
# Download the package
print('Downloading package: {0} from bucket {1} to {2}'.format(latest_file, bucket_name, destination_path))
s3.Bucket(bucket_name).download_file(latest_file, os.path.join(destination_path, package_name))
# Unzip the package
with zipfile.ZipFile(os.path.join(destination_path, package_name), 'r') as zip_ref:
print('Unzipping package: {0} to {1}'.format(package_name, destination_path))
zip_ref.extractall(destination_path)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-b', '--bucket_name', required=True, help='Bucket that holds the package.')
parser.add_argument('-p', '--package_regex', required=True,
help='Regex to identify a package. Such as: lumberyard-0.0-[\d]{6,7}-pc-[\d]{4}.zip\s to find '
'the main pc package.')
parser.add_argument('-n', '--build_number_regex', required=True,
help='Regex to identify the build number. Such as [\d]{4,5}.zip$ to find the build number from '
'the name of the main pc package')
parser.add_argument('-d', '--destination_path', required=True, help='Destination for the contents of the packages.')
parser.add_argument('-f', '--folder_path', help='Folder that contains the package, must include /.')
args = parser.parse_args()
download_and_unzip_package(args.bucket_name, args.package_regex, args.build_number_regex, args.folder_path,
args.destination_path)
if __name__ == "__main__":
main()
@@ -0,0 +1,55 @@
"""
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.
Downloads packages and unzips them.
"""
import argparse
import boto3
import os
import zipfile
def download_and_unzip_packages(bucket_name, package_key, folder_path, destination_path):
"""
Downloads a given package from a S3 and unzips it.
:param bucket_name: S3 bucket
:param package_key: Key for the package
:param folder_path: Folder path to the package
:param destination_path: Where to download the package to
:return:
"""
# Make sure the directory exists
if not os.path.isdir(destination_path):
os.makedirs(destination_path)
# Download the package
s3 = boto3.resource('s3')
print('Downloading package: {0} from bucket {1} to {2}'.format(package_key, bucket_name, destination_path))
s3.Bucket(bucket_name).download_file(folder_path + package_key, os.path.join(destination_path, package_key))
# Unzip the package
with zipfile.ZipFile(os.path.join(destination_path, package_key), 'r') as zip_ref:
print('Unzipping package: {0} to {1}'.format(package_key, destination_path))
zip_ref.extractall(destination_path)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-b', '--bucket_name', required=True, help='Bucket that holds the package.')
parser.add_argument('-p', '--package_key', required=True, help='Desired package\'s key.')
parser.add_argument('-d', '--destination_path', required=True, help='Destination for the contents of the packages.')
parser.add_argument('-f', '--folder_path', help='Folder that contains the package, must include /.')
args = parser.parse_args()
download_and_unzip_packages(args.bucket_name, args.package_key, args.folder_path, args.destination_path)
if __name__ == "__main__":
main()
@@ -0,0 +1,57 @@
"""
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.
"""
'''
All this script is doing is writing grabbing a file that was written previously marking the start of when the Perforce would run
and then getting the current time to find out how long we spent in Perforce
'''
import time
from utils.util import *
def write_metrics():
enable_build_metrics = os.environ.get('ENABLE_BUILD_METRICS')
metrics_namespace = os.environ.get('METRICS_NAMESPACE')
if enable_build_metrics == 'true':
scm_end = int(time.time())
workspace = os.environ.get('WORKSPACE')
metrics_file_name = 'scm_start.txt'
if workspace is None:
safe_exit_with_error('{} must be run in Jenkins job.'.format(os.path.basename(__file__)))
try:
with open(os.path.join(workspace, metrics_file_name), 'r') as f:
scm_start = int(f.readline())
except:
safe_exit_with_error('Failed to read from {}'.format(metrics_file_name))
scm_total = scm_end - scm_start
script_path = os.path.join(workspace, 'dev/Tools/build/waf-1.7.13/build_metrics/write_build_metric.py')
build_tag = os.environ.get('BUILD_TAG')
p4_changelist = os.environ.get('P4_CHANGELIST')
if build_tag is not None and p4_changelist is not None:
os.environ['BUILD_ID'] = '{0}.{1}'.format(build_tag, p4_changelist)
cwd = os.getcwd()
os.chdir(os.path.join(workspace, 'dev'))
cmd = 'python {} SCMTime {} Seconds --enable-build-metrics {} --metrics-namespace {} --project-spec None'.format(script_path, scm_total, True, metrics_namespace)
# metrics call shouldn't fail the job
safe_execute_system_call(cmd, shell=True)
os.chdir(cwd)
if __name__ == "__main__":
write_metrics()
@@ -0,0 +1,76 @@
{
"@lyengine": {
"dev": {
"_WAF_/specs/atom.json": "#include",
"_WAF_/specs/autotests_atom.json" : "#include",
"AtomTest/**": "#include",
"AtomSampleViewer/**": "#include",
"Bin64vc???" : {
"Builders" : {
"AZSLc/**" : "#include",
"DirectXShaderCompiler/**" : "#include",
"DirectXShaderCompilerAz/**" : "#include",
"glslang/**" : "#exclude",
"SPIRVCross/**" : "#include"
},
"AtomTestLauncher.*" : "#include",
"AtomSampleViewer.*" : "#include",
"AtomSampleViewerLauncher.*" : "#include",
"CryRenderAtomShim.*" : "#include",
"CryRenderOther.*" : "#include",
"Gem.Atom_*" : "#include",
"Gem.AtomImGuiTools.*" : "#include",
"Gem.AtomLyIntegration*" : "#include",
"Gem.AtomTest.*" : "#include",
"Gem.AtomSampleViewerGem.*" : "#include",
"Gem.EMotionFX_Atom.*" : "#include",
"Gem.ImageProcessingAtom.*" : "#include",
"Gem.ImguiAtom.*" : "#include",
"Gem.MaterialEditor.*" : "#include",
"MaterialEditor.*" : "#include"
},
"BinMac" : {
"Builders" : {
"AZSLc/**" : "#include",
"DirectXShaderCompiler/**" : "#include",
"DirectXShaderCompilerAz/**" : "#include",
"glslang/**" : "#exclude",
"SPIRVCross/**" : "#include"
},
"AtomTestLauncher.*" : "#include",
"AtomSampleViewer.*" : "#include",
"AtomSampleViewerLauncher.*" : "#include",
"CryRenderAtomShim.*" : "#include",
"CryRenderOther.*" : "#include",
"Gem.Atom_*" : "#include",
"Gem.AtomImGuiTools.*" : "#include",
"Gem.AtomLyIntegration*" : "#include",
"Gem.AtomTest.*" : "#include",
"Gem.AtomSampleViewerGem.*" : "#include",
"Gem.EMotionFX_Atom.*" : "#include",
"Gem.ImageProcessingAtom.*" : "#include",
"Gem.ImguiAtom.*" : "#include",
"Gem.MaterialEditor.*" : "#include",
"MaterialEditor.*" : "#include"
},
"Code": {
"Framework": {
"AtomCore/**": "#include"
},
"Viewers/**": "#include"
},
"Gems": {
"Atom/**": "#include",
"AtomLyIntegration/**": "#include",
"AtomSampleViewer/**": "#include",
"AtomTest/**": "#include"
},
"ProjectTemplates": {
"AtomTemplate/**": "#include",
"GemTemplate/atom_project_gem_template/**": "#include",
"GemTemplate/**/atom_*.json": "#include"
}
},
"LICENSE.txt": "#include"
}
}
@@ -0,0 +1,15 @@
{
"@lyengine": {
"dev": {
"Bin64vc???" : {
"AtomDemoLauncher.*" : "#include",
"Gem.AtomDemo.*" : "#include"
},
"Gems": {
"AtomContent/**": "#include"
},
"AtomDemo/**": "#include"
},
"LICENSE.txt": "#include"
}
}
@@ -0,0 +1,311 @@
{
"@3rdParty": {
"**/.owner": "#exclude",
"3rdParty.txt": "#move:3rdParty",
"OpenEXR/**": "#move:3rdParty",
"CMake/3.17.0/**": "#move:3rdParty",
"Redistributables":{
"WwiseLTX": {
"LTX_2018.1.2.6762": {
"**": "#move:dev/Tools/Redistributables/WwiseLTX/LTX_2018.1.2.6762",
"*.app.zip": "#exclude",
"WwiseLauncher.pkg": "#exclude"
}
},
"FbxSdk": {
"2016.1.2": {
"*win*": "#move:dev/Tools/Redistributables/FbxSdk/2016.1.2-az.1",
"*vs2013*": "#exclude"
}
}
}
},
"@lyengine": {
"dev": {
"**/*.crcfix_old": "#exclude",
"**/*.pyc": "#exclude",
"*.cfg": "#include",
"*.ini": "#include",
".p4ignore": "#include",
"BuildDBAs.bat": "#include",
"Build_AssetBundler_AuxiliaryContent_PC.bat": "#include",
"windows_vs2019/bin/**": "#include",
"AtomTest":
{
"**":"#include",
"**/*.ma":"#exclude",
"**/*.max":"#exclude",
"**/*.mb":"#exclude",
"**/*.psd":"#exclude"
},
"AtomSampleViewer":
{
"**":"#include",
"Gem/Resources/ProvoLauncher/**":"#exclude",
"Gem/Resources/XeniaLauncher/**":"#exclude",
"Standalone/Resources/ProvoLauncher/**":"#exclude",
"Standalone/Resources/XeniaLauncher/**":"#exclude",
"**/*.ma":"#exclude",
"**/*.max":"#exclude",
"**/*.mb":"#exclude",
"**/*.psd":"#exclude"
},
"cmake/**": "#include",
"Code": {
"CryEngine": {
"**": "#include",
"CryFont/Project.mk": "#exclude"
},
"Deprecated/**": "#include",
"Engine/**": "#include",
"Framework": {
"**": "#include",
"GridMate/Build/**": "#exclude"
},
"LauncherUnified/**": "#include",
"PrepareBuildEnvironment.ps1": "#include",
"Sandbox": {
"**": "#include",
"**/moc_*": "#exclude",
"**/rcc_*": "#exclude",
"**/ui_*": "#exclude",
".p4ignore": "#exclude"
},
"StarterGame/**": "#include",
"Tools": {
"Android/**": "#include",
"AWSNativeSDKInit/**": "#include",
"AssetProcessor*/**": "#include",
"AssetBundler/**": "#include",
"AzCodeGenerator/**": "#include",
"AzTestRunner/**": "#include",
"AzTestScanner/**": "#include",
"AzTestScanner/data/**": "#exclude",
"AzTestScanner/aztest/plugins/py_setup_plugin/**": "#exclude",
"ClangReflect/**": "#include",
"CrashHandler/**": "#include",
"CryCommonTools/**": "#include",
"CryD3DCompilerStub/**": "#include",
"CrySCompileServer/**": "#include",
"CryXML/**": "#include",
"DeltaCataloger/**": "#include",
"GemRegistry/**": "#include",
"GridHub/**": "#include",
"HLSLCrossCompiler/**": "#include",
"HLSLCrossCompilerMETAL/**": "#include",
"LyIdentity/**": "#include",
"LyMetrics/**": "#include",
"LuaRemoteDebugger/**": "#include",
"News/**": "#include",
"PRT/**": "#include",
"PythonBindingsExample/**": "#include",
"QtTGAImageFormatPlugin/**": "#include",
"RC/**": "#include",
"RemoteConsole": {
"Core/**": "#include",
"Platform/**": "#include",
"*": "#include"
},
"SceneAPI/**": "#include",
"SerializeContextTools/**": "#include",
"ShaderCacheGen/**": "#include",
"SharedSettings/**": "#include",
"SharedQMLResource/**": "#include",
"SphericalHarmonics": {
"**": "#include",
"shroadmap.rtf": "#exclude"
},
"ToolsLauncher/**": "#include",
"Woodpecker/**": "#include",
"CMakeLists.txt": "#include"
},
"CMakeLists.txt": "#include"
},
"ctest_scripts/**": "#include",
"DetermineRCandAP.bat": "#include",
"Editor/**": "#include",
"Engine/**": "#include",
"Gems": {
"Achievements/**": "#include",
"AssetMemoryAnalyzer/**": "#include",
"AssetValidation/**": "#include",
"AudioEngineWwise/**": "#include",
"AudioSystem/**": "#include",
"Atom/**": "#include",
"AtomLyIntegration/**": "#include",
"AutomatedLauncherTesting/**": "#include",
"AWS/**": "#include",
"AWSLambdaLanguageDemo/**": "#include",
"Camera/**": "#include",
"CameraFramework/**": "#include",
"CertificateManager/**": "#include",
"ChatPlay/**": "#include",
"CloudCanvasCommon/**": "#include",
"CloudGemAWSScriptBehaviors/**": "#include",
"CloudGemComputeFarm": {
"v1": {
"AWS/**": "#include",
"Code/**": "#include",
"Harness/**": "#include",
"gem.json": "#include",
"preview.png": "#include"
}
},
"CloudGemDefectReporter/**": "#include",
"CloudGemDynamicContent/**": "#include",
"CloudGemFramework/**": "#include",
"CloudGemInGameSurvey/**": "#include",
"CloudGemLeaderboard/**": "#include",
"CloudGemMessageOfTheDay/**": "#include",
"CloudGemMetric/**": "#include",
"CloudGemPlayerAccount/**": "#include",
"CloudGemSpeechRecognition/**": "#include",
"CloudGemTextToSpeech/**": "#include",
"CloudGemWebCommunicator/**": "#include",
"Clouds/**": "#include",
"CrashReporting/**": "#include",
"CryEntityRemoval/**": "#include",
"CryLegacy/**": "#include",
"CryLegacyAnimation/**": "#include",
"CustomAssetExample/**": "#include",
"DebugDraw/**": "#include",
"DevTextures/**": "#include",
"EditorPythonBindings/**": "#include",
"EMotionFX/**": "#include",
"ExpressionEvaluation/**": "#include",
"FastNoise/**": "#include",
"GameEffectSystem/**": "#include",
"GameLift/**": "#include",
"GameState/**": "#include",
"GameStateSamples/**": "#include",
"Gestures/**": "#include",
"GradientSignal/**": "#include",
"GraphCanvas/**": "#include",
"GraphModel/**": "#include",
"HMDFramework/**": "#include",
"HttpRequestor/**": "#include",
"ImageProcessing/**": "#include",
"ImGui/**": "#include",
"InAppPurchases/**": "#include",
"LandscapeCanvas/**": "#include",
"LegacyTerrain/**": "#include",
"LegacyGameInterface/**": "#include",
"LegacyTimeDemoRecorder/**": "#include",
"LmbrCentral/**": "#include",
"LocalUser/**": "#include",
"LyShine/**": "#include",
"LyShineExamples/**": "#include",
"Maestro/**": "#include",
"MessagePopup/**": "#include",
"Metastream/**": "#include",
"Microphone/**": "#include",
"Multiplayer/**": "#include",
"MultiplayerImGui/**": "#include",
"NativeUI/**": "#include",
"NullVR/**": "#include",
"NvCloth/**": "#include",
"Oculus/**": "#include",
"OpenVR/**": "#include",
"OSVR/**": "#include",
"QtForPython/**": "#include",
"PBSreferenceMaterials/**": "#include",
"PhysicsEntities/**": "#include",
"PhysX/**": "#include",
"PhysXCharacters/**": "#include",
"PhysXDebug/**": "#include",
"PhysXSamples/**": "#include",
"Presence/**": "#include",
"PrimitiveAssets/**": "#include",
"ProcessLifeManagement/**": "#include",
"RADTelemetry/**": "#include",
"RenderToTexture/**": "#include",
"RoadsAndRivers/**": "#include",
"SaveData/**": "#include",
"SceneLoggingExample/**": "#include",
"SceneProcessing/**": "#include",
"ScriptCanvas/**": "#include",
"ScriptCanvasDeveloper/**": "#include",
"ScriptCanvasDiagnosticLibrary/**": "#include",
"ScriptCanvasPhysics/**": "#include",
"ScriptCanvasTesting/**": "#include",
"ScriptedEntityTweener/**": "#include",
"ScriptEvents/**": "#include",
"SliceFavorites/**": "#include",
"StarterGameGem/**": "#include",
"StartingPointCamera/**": "#include",
"StartingPointInput/**": "#include",
"StartingPointMovement/**": "#include",
"StaticData/**": "#include",
"Substance/**": "#include",
"SurfaceData/**": "#include",
"SVOGI/**": "#include",
"TestAssetBuilder/**": "#include",
"TestHeaderOnlyLibraries/**": "#include",
"TextureAtlas/**": "#include",
"TickBusOrderViewer/**": "#include",
"TouchBending/**": "#include",
"Twitch/**": "#include",
"UiBasics/**": "#include",
"Vegetation/**": "#include",
"VideoPlayback/**": "#include",
"VideoPlaybackBink/**": "#include",
"VideoPlaybackFramework/**": "#include",
"VirtualGamepad/**": "#include",
"Visibility/**": "#include",
"Water/**": "#include",
"WhiteBox/**": "#include",
"CMakeLists.txt": "#include"
},
"LYConfig_Default.xml": "#include",
"LYConfig_Editor.xml": "#include",
"LYConfig_Monolithic.xml": "#include",
"ProjectTemplates/**": "#exclude",
"Tools": {
"3dsmax/**": "#include",
"7za.exe": "#include",
"7za_legal_notice.txt": "#include",
"AWSNativeSDK": {
"**": "#include",
"Upgrader/restricted_platforms.py": "#exclude"
},
"AWSPythonSDK/**": "#include",
"AppDetective/**": "#include",
"AzCodeGenerator/bin/**": "#include",
"Crashpad/**": "#include",
"CryMaxTools/**": "#include",
"CrySCompileServer/**": "#include",
"InternalSDKs/**": "#include",
"LuaRemoteDebugger/**": "#include",
"LyTestTools/**": "#include",
"PakShaders/**": "#include",
"Python/**": "#include",
"Redistributables": {
"**": "#include",
"ANGLE/**": "#exclude",
"D3DCompiler/**": "#exclude",
"DbgHelp/**": "#exclude",
"FFMpeg/**": "#exclude",
"LuaCompiler/**": "#exclude",
"MSVC90/**": "#exclude",
"OpenGL32/**": "#exclude",
"SSLEAY/**": "#exclude"
},
"RemoteConsole/**": "#include",
"__init__.py": "#include",
"WwiseAuthoringScripts/**": "#include",
"crcfix/bin/**": "#include",
"lmbr_aws/**": "#include",
"maxscript/**": "#include",
"maya/**": "#include",
"photoshop/**": "#include"
},
"CMakeLists.txt": "#include",
"editor.cfg": "#include",
"engine.json": "#include",
"engineroot.txt": "#include",
"SliceBuilderSettings.json": "#include"
},
"docs/**": "#include",
"LICENSE.txt": "#include"
}
}
@@ -0,0 +1,20 @@
{
"@lyengine": {
"dev": {
"**/.owner": "#exclude",
"Gems/**/InternalOnly/**": "#include",
"SamplesProject/Levels/Samples/InternalOnly/**": "#include",
"Tests/**": "#include",
"Tools/Python/3.7.5/internal/**": "#include",
"Tools/PythonTestTools/**": "#include",
"Tools/RemoteConsole/ly_remote_console/**": "#include",
"Tools/Flume/**": "#include",
"Tools/TestRailImporter/**": "#include",
"Tools/LyTestTools/**": "#include",
"Tools/build/JenkinsScripts/build/**": "#include",
"Code/Tools/AzTestScanner/aztest/plugins/py_setup_plugin/**": "#include",
"conftest.py": "#include"
},
"LICENSE.txt": "#include"
}
}
@@ -0,0 +1,415 @@
{
"@3rdParty": {
"**/.owner": "#exclude",
"3rdParty.txt": "#move:3rdParty",
"OpenEXR/**": "#move:3rdParty",
"Redistributables":{
"WwiseLTX": {
"LTX_2018.1.2.6762": {
"**": "#move:dev/Tools/Redistributables/WwiseLTX/LTX_2018.1.2.6762",
"WwiseLauncher.msi": "#exclude"
}
},
"FbxSdk": {
"2016.1.2": {
"*mac*": "#move:dev/Tools/Redistributables/FbxSdk/2016.1.2-az.1"
}
}
}
},
"@lyengine": {
"dev": {
"**/*.crcfix_old": "#exclude",
"**/*.pdb": "#exclude",
"**/*.pyc": "#exclude",
"*.cfg": "#include",
"*.ini": "#include",
"*waf*": "#include",
".p4ignore": "#include",
"_WAF_": {
"**": "#include",
"_cache_/**": "#exclude",
"specs/3rd_party.json": "#exclude",
"specs/atom.json": "#exclude",
"specs/autotests_atom.json": "#exclude",
"specs/external_sdks.json": "#exclude",
"specs/pipeline.json": "#exclude",
"user_settings.options": "#exclude"
},
"Bin64": {
"**": "#include",
"**/*.dll": "#exclude",
"**/*.exe": "#exclude",
"LumberyardLauncherBatch": "#exclude",
"MilesRedist/**": "#exclude",
"rc/rc_log*.log": "#exclude",
"rc/rc_createdfiles.txt": "#exclude",
"rc/rc_deletedfiles.txt": "#exclude",
"rc/rc_outputfiles.txt": "#exclude",
"user/**": "#exclude",
"*BeachCity*": "#exclude"
},
"BinMac64": {
"*.dylib": "#include",
"Builders/AZSLc/**" : "#exclude",
"Builders/DirectXShaderCompiler/**" : "#exclude",
"Builders/DirectXShaderCompilerAz/**" : "#exclude",
"Builders/glslang/**" : "#exclude",
"Builders/SPIRVCross/**" : "#exclude",
"AtomTestLauncher.*" : "#exclude",
"AtomDemoLauncher.*" : "#exclude",
"AtomSampleViewer.*" : "#exclude",
"AtomSampleViewerLauncher.*" : "#exclude",
"CryRenderAtomShim.*" : "#exclude",
"CryRenderOther.*" : "#exclude",
"Gem.Atom_*" : "#exclude",
"Gem.AtomImGuiTools.*" : "#exclude",
"Gem.AtomLyIntegration*" : "#exclude",
"Gem.AtomTest.*" : "#exclude",
"Gem.AtomSampleViewerGem.*" : "#exclude",
"Gem.ImageProcessingAtom.*" : "#exclude",
"Gem.ImguiAtom.*" : "#exclude",
"Gem.MaterialEditor.*" : "#exclude",
"Gem.AtomDemo.*" : "#exclude",
"Gem.EMotionFX_Atom.*" : "#exclude",
"MaterialEditor.*" : "#exclude",
"AssetBuilder": "#include",
"AssetProcessor": "#include",
"AssetProcessorBatch": "#include",
"AssetBundlerBatch": "#include",
"AssetProcessorConfiguration.ini": "#include",
"astcenc": "#include",
"azcg/**": "#include",
"Builders/**": "#include",
"EditorPlugins/**": "#include",
"lmbr": "#include",
"LuaIDE": "#include",
"qtlibs/**": "#include",
"rc/**": "#include"
},
"Code": {
"CryEngine": {
"**": "#include",
"CryFont/Project.mk": "#exclude"
},
"Deprecated/**": "#include",
"Engine": "#include",
"Framework": {
"**": "#include",
"AtomCore": "#exclude",
"GridMate/Build/": "#exclude"
},
"LauncherUnified/**": "#include",
"PrepareBuildEnvironment.ps1": "#include",
"Sandbox": {
"**": "#include",
"**/moc_*": "#exclude",
"**/rcc_*": "#exclude",
"**/ui_*": "#exclude",
".p4ignore": "#exclude"
},
"Tools": {
"Android/**": "#include",
"Apple/**": "#include",
"AWSNativeSDKInit/**": "#include",
"AWSNativeSDKInit/source/Consoles/**": "#exclude",
"AssetProcessor*/**": "#include",
"AssetBundler/**": "#include",
"AzCodeGenerator/**": "#include",
"AzTestScanner/**": "#include",
"AzTestScanner/data/**": "#exclude",
"AzTestScanner/aztest/plugins/py_setup_plugin/**": "#exclude",
"ClangReflect/**": "#include",
"CryCommonTools/**": "#include",
"CryD3DCompilerStub/**": "#include",
"CrySCompileServer/**": "#include",
"CryXML/**": "#include",
"GemRegistry/**": "#include",
"GridHub/**": "#include",
"HLSLCrossCompiler/**": "#include",
"HLSLCrossCompilerMETAL/**": "#include",
"LuaRemoteDebugger/**": "#include",
"News/*": "#include",
"News/NewsShared/**": "#include",
"RC/**": "#include",
"RemoteConsole": {
"Core/**": "#include",
"Platform/**": "#include",
"*": "#include"
},
"SceneAPI/**": "#include",
"ShaderCacheGen/**": "#include",
"SharedSettings/**": "#include",
"ToolsLauncher/**": "#include",
"Woodpecker/**": "#include",
"expat.waf_files": "#include",
"wscript": "#include"
},
"wscript": "#include"
},
"Editor/**": "#include",
"Engine/**": "#include",
"Gems": {
"Achievements/**": "#include",
"AssetMemoryAnalyzer/**": "#include",
"AssetValidation/**": "#include",
"AudioEngineWwise/**": "#include",
"AudioSystem/**": "#include",
"AtomLyIntegration/**": "#exclude",
"AWS/**": "#include",
"Camera/**": "#include",
"CameraFramework/**": "#include",
"CertificateManager/**": "#include",
"ChatPlay/**": "#include",
"Clouds/**": "#include",
"CustomAssetExample/**": "#include",
"DebugDraw/**": "#include",
"DevTextures/**": "#include",
"EditorPythonBindings/**": "#include",
"EMotionFX/**": "#include",
"EMotionFX/Atom/**": "#exclude",
"ExpressionEvaluation/**": "#include",
"FastNoise/**": "#include",
"GameEffectSystem/**": "#include",
"GameLift/**": "#include",
"GameState/**": "#include",
"GameStateSamples/**": "#include",
"Gestures/**": "#include",
"GradientSignal/**": "#include",
"GraphCanvas/**": "#include",
"GraphModel/**": "#include",
"HttpRequestor/**": "#include",
"ImageProcessing/**": "#include",
"ImGui/**": "#include",
"InAppPurchases/**": "#include",
"LandscapeCanvas/**": "#include",
"LmbrCentral/**": "#include",
"LocalUser/**": "#include",
"LyShine/**": "#include",
"LyShineExamples/**": "#include",
"Maestro/**": "#include",
"MessagePopup/**": "#include",
"Metastream/**": "#include",
"Microphone/**": "#include",
"Multiplayer/**": "#include",
"MultiplayerImGui/**": "#include",
"NativeUI/**": "#include",
"NvCloth/**": "#include",
"PBSreferenceMaterials/**": "#include",
"PhysicsEntities/**": "#include",
"PhysX/**": "#include",
"PhysXCharacters/**": "#include",
"PhysXDebug/**": "#include",
"PhysXSamples/**": "#include",
"Presence/**": "#include",
"PrimitiveAssets/**": "#include",
"RADTelemetry": {
"3rdParty/**": "#include",
"Code/**": "#include",
"External/**": "#exclude",
"Tools/**": "#exclude",
"gem.json": "#include",
"preview.png": "#include"
},
"RenderToTexture/**": "#include",
"SaveData/**": "#include",
"SceneLoggingExample/**": "#include",
"SceneProcessing/**": "#include",
"ScriptCanvas/**": "#include",
"ScriptCanvasDiagnosticLibrary/**": "#include",
"ScriptCanvasPhysics/**": "#include",
"ScriptCanvasTesting/**": "#include",
"ScriptedEntityTweener/**": "#include",
"ScriptEvents/**": "#include",
"SliceFavorites/**": "#include",
"StarterGame/**": "#include",
"StarterGameGem/**": "#exclude",
"StartingPointCamera/**": "#include",
"StartingPointInput/**": "#include",
"StartingPointMovement/**": "#include",
"StaticData/**": "#include",
"Substance/**": "#include",
"SurfaceData/**": "#include",
"SVOGI/**": "#include",
"TestAssetBuilder/**": "#include",
"TextureAtlas/**": "#include",
"TickBusOrderViewer/**": "#include",
"TouchBending/**": "#include",
"Twitch/**": "#include",
"UiBasics/**": "#include",
"Vegetation/**": "#include",
"VideoPlayback/**": "#include",
"VideoPlaybackBink/**": "#exclude",
"VideoPlaybackFramework/**": "#include",
"VirtualGamepad/**": "#include",
"Visibility/**": "#include",
"Water/**": "#include",
"WhiteBox/**": "#include"
},
"MultiplayerSample": {
"**/*.ma": "#exclude",
"**/*.max": "#exclude",
"**/*.mb": "#exclude",
"**/*.obj": "#exclude",
"**/*.psd": "#exclude",
"Config/**": "#include",
"Fonts/**": "#include",
"Gem": {
"Code/**": "#include",
"Resources": {
"AppleTVLauncher": "#include",
"IOSLauncher/**": "#include",
"MacLauncher/**": "#include",
"*": "#include"
},
"*": "#include"
},
"Levels/**": "#include",
"libs/**": "#include",
"objects/**": "#include",
"Scripts/**": "#include",
"slices/**": "#include",
"Sounds/**": "#include",
"textures/**": "#include",
"ui/**": "#include",
"WAFSpec/**": "#include",
"*": "#include"
},
"MultiplayerSample_CreateGameLiftPackage.sh": "#include",
"ProjectTemplates": {
"DefaultTemplate/**": "#include",
"EmptyTemplate/**": "#include",
"ExternalProjectTemplate/**": "#include",
"GemTemplate/**": "#include",
"GemTemplate/atom_project_gem_template/**": "#exclude",
"GemTemplate/**/atom_*.json": "#exclude",
"TemplateListForMetrics.json": "#include"
},
"SamplesProject": {
"**/*.ma": "#exclude",
"**/*.max": "#exclude",
"**/*.mb": "#exclude",
"**/*.obj": "#exclude",
"**/*.psd": "#exclude",
"Animations/**": "#include",
"AnimationSamples/**": "#include",
"Config/**": "#include",
"EntityIcons/**": "#include",
"Fonts/**": "#include",
"Gem": {
"Code/**": "#include",
"Resources": {
"AppleTVLauncher": "#include",
"IOSLauncher/**": "#include",
"MacLauncher/**": "#include",
"*": "#include"
},
"*": "#include"
},
"IAP_ProductIds/**": "#include",
"inputbindings/**": "#include",
"Levels": {
"Samples": {
"Advanced_RinLocomotion/**": "#include",
"Audio_Sample/**": "#include",
"Fur_Technical_Sample/**": "#include",
"Gems_Samples/**": "#include",
"InternalOnly/**": "#include",
"Metastream_Sample/**": "#include",
"ScriptCanvas_Sample/**": "#include",
"ScriptedEntityTweenerSample/**": "#include",
"Simple_JackLocomotion/**": "#include"
},
"UI/**": "#include"
},
"libs/**": "#include",
"Localization/**": "#include",
"materials/**": "#include",
"Objects/**": "#include",
"prefabs/**": "#include",
"ScriptCanvas/**": "#include",
"Scripts": {
"**": "#include",
"Test/**": "#exclude"
},
"slices": {
"**": "#include",
"Test/**": "#exclude"
},
"Sounds/**": "#include",
"textures/**": "#include",
"UI/**": "#include",
"*": "#include"
},
"StarterGame": {
"**/*.ma": "#exclude",
"**/*.max": "#exclude",
"**/*.mb": "#exclude",
"**/*.obj": "#exclude",
"**/*.psd": "#exclude",
"animations/**": "#include",
"AWS/**": "#include",
"Config/**": "#include",
"Fonts/**": "#include",
"Gem/**": "#include",
"InputBindings/**": "#include",
"LauncherTests/**": "#include",
"Levels": {
"Game/**": "#include",
"SeedAssetList.seed": "#include"
},
"libs/**": "#include",
"Materials/**": "#include",
"Objects/**": "#include",
"scriptcanvas/**": "#include",
"scriptevents/**": "#include",
"Scripts/**": "#include",
"slices/**": "#include",
"Sounds/**": "#include",
"Textures/**": "#include",
"UI/**": "#include",
"*": "#include"
},
"Tools": {
"3dsmax/**": "#include",
"AWSPythonSDK/**": "#include",
"AppTester/**": "#include",
"AzCodeGenerator/bin/**": "#include",
"CryMaxTools/**": "#include",
"CrySCompileServer/**": {
"osx/profile/CrySCompileServer": "#include",
"Compiler/LLVMGL/release/dxcGL": "#include",
"Compiler/LLVMMETAL/release/dxcMetal": "#include"
},
"InternalSDKs/**": "#include",
"LuaRemoteDebugger/**": "#include",
"PakShaders/**": "#include",
"Python/**": "#include",
"Python/pip.*": "#exclude",
"Python/python.*": "#exclude",
"Python/2.7.*/**": "#exclude",
"Python/3.7.5/internal/**": "#exclude",
"RemoteConsole/**": "#include",
"__init__.py": "#include",
"WwiseAuthoringScripts/**": "#include",
"build/waf-1.7.13/**": "#include",
"build/waf-1.7.13/lmbrwaflib/crash_reporting*.py": "#exclude",
"lmbr_aws/**": "#include",
"maxscript/**": "#include",
"maya/**": "#include"
},
"config.provopath": "#exclude",
"editor.cfg": "#include",
"engine.json": "#include",
"engineroot.txt": "#include",
"lmbr_aws.sh": "#include",
"lmbr_pak_shaders.sh": "#include",
"lmbr_test.sh": "#include",
"lmbr_test_blacklist.txt": "#include",
"SliceBuilderSettings.json": "#include",
"wscript": "#include"
},
"docs/**": "#include",
"LICENSE.txt": "#include"
}
}
@@ -0,0 +1,429 @@
{
"@3rdParty": {
"**/.owner": "#exclude",
"3rdParty.txt": "#move:3rdParty",
"OpenEXR/**": "#move:3rdParty",
"Redistributables":{
"WwiseLTX": {
"LTX_2018.1.2.6762": {
"**": "#move:dev/Tools/Redistributables/WwiseLTX/LTX_2018.1.2.6762",
"*.app.zip": "#exclude",
"WwiseLauncher.pkg": "#exclude"
}
},
"FbxSdk": {
"2016.1.2": {
"*win*": "#move:dev/Tools/Redistributables/FbxSdk/2016.1.2-az.1",
"*vs2013*": "#exclude"
}
}
}
},
"@lyengine": {
"**/*.crcfix_old": "#exclude",
"**/*.pdb": "#exclude",
"**/*.pyc": "#exclude",
"*.cfg": "#include",
"*.ini": "#include",
"*waf*": "#include",
".p4ignore": "#include",
"_WAF_": {
"**": "#include",
"_cache_/**": "#exclude",
"specs/3rd_party.json": "#exclude",
"specs/atom.json": "#exclude",
"specs/autotests_atom.json": "#exclude",
"specs/external_sdks.json": "#exclude",
"specs/pipeline.json": "#exclude",
"user_settings.options": "#exclude"
},
"Bin64": {
"**": "#include",
"LumberyardLauncherBatch": "#exclude"
},
"Bin64vc???": {
"**": "#include",
"**/ffmpeg.exe": "#exclude",
"logs/**": "#exclude",
"NewsBuilder*": "#exclude",
"Builders/AZSLc/**" : "#exclude",
"Builders/DirectXShaderCompiler/**" : "#exclude",
"Builders/DirectXShaderCompilerAz/**" : "#exclude",
"Builders/glslang/**" : "#exclude",
"Builders/SPIRVCross/**" : "#exclude",
"AtomTestLauncher.*" : "#exclude",
"AtomDemoLauncher.*" : "#exclude",
"AtomSampleViewer.*" : "#exclude",
"AtomSampleViewerLauncher.*" : "#exclude",
"CryRenderAtomShim.*" : "#exclude",
"CryRenderOther.*" : "#exclude",
"Gem.Atom_*" : "#exclude",
"Gem.AtomImGuiTools.*" : "#exclude",
"Gem.AtomLyIntegration*": "#exclude",
"Gem.AtomTest.*" : "#exclude",
"Gem.AtomSampleViewerGem.*" : "#exclude",
"Gem.EMotionFX_Atom.*" : "#exclude",
"Gem.ImageProcessingAtom.*" : "#exclude",
"Gem.ImguiAtom.*" : "#exclude",
"Gem.MaterialEditor.*" : "#exclude",
"Gem.AtomDemo.*" : "#exclude",
"Gem.EMotionFX_Atom.*" : "#exclude",
"MaterialEditor.*" : "#exclude"
},
"BuildDBAs.bat": "#include",
"BuildMultiplayerSample_Paks_PC.bat": "#include",
"BuildMultiplayerSample_Paks_PC_dedicated.bat": "#include",
"BuildSamplesProject_Paks_PC.bat": "#include",
"BuildSamplesProject_Paks_iOS.bat": "#include",
"Build_AssetBundler_AuxiliaryContent_PC.bat": "#include",
"BuildReleaseAuxiliaryContent.py": "#include",
"Cache": {
"StarterGame/pc/**": "#include",
"StarterGame/assetdb.sqlite": "#include"
},
"Code": {
"CryEngine": {
"**": "#include",
"CryFont/Project.mk": "#exclude"
},
"Deprecated/**": "#include",
"Engine": "#include",
"Framework": {
"**": "#include",
"AtomCore": "#exclude",
"GridMate/Build/": "#exclude"
},
"LauncherUnified/**": "#include",
"BuildDBAs.bat": "#include",
"BuildMultiplayerSample_Paks_PC.bat": "#include",
"BuildMultiplayerSample_Paks_PC_dedicated.bat": "#include",
"BuildSamplesProject_Paks_PC.bat": "#include",
"BuildSamplesProject_Paks_iOS.bat": "#include",
"Build_AssetBundler_AuxiliaryContent_PC.bat": "#include",
"BuildReleaseAuxiliaryContent.py": "#include",
"Cache": {
"StarterGame/pc/**": "#include",
"StarterGame/assetdb.sqlite": "#include",
"StarterGame/package_mark.txt": "#include"
},
"Tools": {
"Android/**": "#include",
"AWSNativeSDKInit/**": "#include",
"AWSNativeSDKInit/source/Consoles/**": "#exclude",
"AssetProcessor*/**": "#include",
"AssetBundler/**": "#include",
"AzCodeGenerator/**": "#include",
"AzTestScanner/**": "#include",
"AzTestScanner/data/**": "#exclude",
"AzTestScanner/aztest/plugins/py_setup_plugin/**": "#exclude",
"ClangReflect/**": "#include",
"CryCommonTools/**": "#include",
"CryD3DCompilerStub/**": "#include",
"CrySCompileServer/**": "#include",
"CryXML/**": "#include",
"GemRegistry/**": "#include",
"GridHub/**": "#include",
"HLSLCrossCompiler/**": "#include",
"HLSLCrossCompilerMETAL/**": "#include",
"LuaRemoteDebugger/**": "#include",
"News/*": "#include",
"News/NewsShared/**": "#include",
"RC/**": "#include",
"RemoteConsole": {
"Core/**": "#include",
"Platform/**": "#include",
"*": "#include"
},
"SceneAPI/**": "#include",
"SerializeContextTools/**": "#include",
"ShaderCacheGen/**": "#include",
"SharedSettings/**": "#include",
"ToolsLauncher/**": "#include",
"Woodpecker/**": "#include",
"expat.waf_files": "#include",
"wscript": "#include"
},
"wscript": "#include"
},
"DetermineRCandAP.bat": "#include",
"Editor/**": "#include",
"Engine/**": "#include",
"Gems": {
"Achievements/**": "#include",
"AssetMemoryAnalyzer/**": "#include",
"AssetValidation/**": "#include",
"AudioEngineWwise/**": "#include",
"AudioSystem/**": "#include",
"AWS/**": "#include",
"Camera/**": "#include",
"CameraFramework/**": "#include",
"CertificateManager/**": "#include",
"ChatPlay/**": "#include",
"Clouds/**": "#include",
"CustomAssetExample/**": "#include",
"DebugDraw/**": "#include",
"DevTextures/**": "#include",
"EditorPythonBindings/**": "#include",
"EMotionFX/**": "#include",
"EMotionFX/Atom/**": "#exclude",
"ExpressionEvaluation/**": "#include",
"EMotionFX/Atom/**": "#exclude",
"FastNoise/**": "#include",
"GameEffectSystem/**": "#include",
"GameLift/**": "#include",
"GameState/**": "#include",
"GameStateSamples/**": "#include",
"Gestures/**": "#include",
"GradientSignal/**": "#include",
"GraphCanvas/**": "#include",
"GraphModel/**": "#include",
"HttpRequestor/**": "#include",
"ImageProcessing/**": "#include",
"ImGui/**": "#include",
"InAppPurchases/**": "#include",
"LandscapeCanvas/**": "#include",
"LmbrCentral/**": "#include",
"LocalUser/**": "#include",
"LyShine/**": "#include",
"LyShineExamples/**": "#include",
"Maestro/**": "#include",
"MessagePopup/**": "#include",
"Metastream/**": "#include",
"Microphone/**": "#include",
"Multiplayer/**": "#include",
"MultiplayerImGui/**": "#include",
"NvCloth/**": "#include",
"PBSreferenceMaterials/**": "#include",
"PhysicsEntities/**": "#include",
"PhysX/**": "#include",
"PhysXCharacters/**": "#include",
"PhysXDebug/**": "#include",
"PhysXSamples/**": "#include",
"Presence/**": "#include",
"PrimitiveAssets/**": "#include",
"QtForPython/**": "#include",
"RADTelemetry": {
"3rdParty/**": "#include",
"Code/**": "#include",
"External/**": "#exclude",
"Tools/**": "#exclude",
"gem.json": "#include",
"preview.png": "#include"
},
"RenderToTexture/**": "#include",
"SaveData/**": "#include",
"SceneLoggingExample/**": "#include",
"SceneProcessing/**": "#include",
"ScriptCanvas/**": "#include",
"ScriptCanvasDiagnosticLibrary/**": "#include",
"ScriptCanvasPhysics/**": "#include",
"ScriptCanvasTesting/**": "#include",
"ScriptedEntityTweener/**": "#include",
"ScriptEvents/**": "#include",
"SliceFavorites/**": "#include",
"StarterGame/**": "#include",
"StarterGameGem/**": "#exclude",
"StartingPointCamera/**": "#include",
"StartingPointInput/**": "#include",
"StartingPointMovement/**": "#include",
"StaticData/**": "#include",
"Substance/**": "#include",
"SurfaceData/**": "#include",
"SVOGI/**": "#include",
"TestAssetBuilder/**": "#include",
"TextureAtlas/**": "#include",
"TickBusOrderViewer/**": "#include",
"TouchBending/**": "#include",
"Twitch/**": "#include",
"UiBasics/**": "#include",
"Vegetation/**": "#include",
"VideoPlayback/**": "#include",
"VideoPlaybackBink/**": "#exclude",
"VideoPlaybackFramework/**": "#include",
"VirtualGamepad/**": "#include",
"Visibility/**": "#include",
"Water/**": "#include",
"WhiteBox/**": "#include"
},
"LYConfig_Default.xml": "#include",
"LYConfig_Editor.xml": "#include",
"LYConfig_Monolithic.xml": "#include",
"MultiplayerSample": {
"**/*.ma": "#exclude",
"**/*.max": "#exclude",
"**/*.mb": "#exclude",
"**/*.obj": "#exclude",
"**/*.psd": "#exclude",
"Config/**": "#include",
"Fonts/**": "#include",
"Gem": {
"Code/**": "#include",
"Resources": {
"*": "#include"
},
"*": "#include"
},
"Levels/**": "#include",
"libs/**": "#include",
"objects/**": "#include",
"Scripts/**": "#include",
"slices/**": "#include",
"Sounds/**": "#include",
"textures/**": "#include",
"ui/**": "#include",
"WAFSpec/**": "#include",
"*": "#include"
},
"MultiplayerSample_CreateGameLiftPackage.sh": "#include",
"MultiplayerSample_LinuxPacker.bat": "#include",
"ProjectTemplates": {
"DefaultTemplate/**": "#include",
"EmptyTemplate/**": "#include",
"ExternalProjectTemplate/**": "#include",
"GemTemplate/**": "#include",
"GemTemplate/atom_project_gem_template/**": "#exclude",
"GemTemplate/**/atom_*.json": "#exclude",
"TemplateListForMetrics.json": "#include"
},
"SamplesProject": {
"**/*.ma": "#exclude",
"**/*.max": "#exclude",
"**/*.mb": "#exclude",
"**/*.obj": "#exclude",
"**/*.psd": "#exclude",
"Animations/**": "#include",
"AnimationSamples/**": "#include",
"Config/**": "#include",
"EntityIcons/**": "#include",
"Fonts/**": "#include",
"Gem": {
"Code/**": "#include",
"Resources": {
"*": "#include"
},
"*": "#include"
},
"StarterGame": {
"**/*.ma": "#exclude",
"**/*.max": "#exclude",
"**/*.mb": "#exclude",
"**/*.obj": "#exclude",
"**/*.psd": "#exclude",
"animations/**": "#include",
"AWS/**": "#include",
"Config/**": "#include",
"Fonts/**": "#include",
"Gem/**": "#include",
"InputBindings/**": "#include",
"LauncherTests/**": "#include",
"Levels": {
"Game/**": "#include",
"SeedAssetList.seed": "#include"
},
"TwitchChatBasics/**": "#include",
"UI/**": "#include"
},
"libs/**": "#include",
"Localization/**": "#include",
"materials/**": "#include",
"Objects/**": "#include",
"prefabs/**": "#include",
"ScriptCanvas/**": "#include",
"Scripts": {
"**": "#include",
"Test/**": "#exclude"
},
"slices": {
"**": "#include",
"Test/**": "#exclude"
},
"Sounds/**": "#include",
"textures/**": "#include",
"UI/**": "#include",
"*": "#include"
},
"StarterGame": {
"**/*.ma": "#exclude",
"**/*.max": "#exclude",
"**/*.mb": "#exclude",
"**/*.obj": "#exclude",
"**/*.psd": "#exclude",
"animations/**": "#include",
"AWS/**": "#include",
"Config/**": "#include",
"Fonts/**": "#include",
"Gem/**": "#include",
"InputBindings/**": "#include",
"LauncherTests/**": "#include",
"Levels": {
"Game/**": "#include"
},
"libs/**": "#include",
"Materials/**": "#include",
"Objects/**": "#include",
"scriptcanvas/**": "#include",
"scriptevents/**": "#include",
"Scripts/**": "#include",
"slices/**": "#include",
"Sounds/**": "#include",
"Textures/**": "#include",
"UI/**": "#include",
"*": "#include"
},
"Tools": {
"3dsmax/**": "#include",
"7za.exe": "#include",
"7za_legal_notice.txt": "#include",
"AWSNativeSDK": {
"**": "#include",
"Upgrader/restricted_platforms.py": "#exclude"
},
"AWSPythonSDK/**": "#include",
"AzCodeGenerator/bin/**": "#include",
"CryMaxTools/**": "#include",
"CrySCompileServer/**": "#include",
"InternalSDKs/**": "#include",
"LuaRemoteDebugger/**": "#include",
"PakShaders/**": "#include",
"Python/**": "#include",
"Python/pip.*": "#exclude",
"Python/python.*": "#exclude",
"Python/2.7.*/**": "#exclude",
"Python/3.7.5/internal/**": "#exclude",
"Redistributables": {
"**": "#include",
"ANGLE/**": "#exclude",
"D3DCompiler/**": "#exclude",
"DbgHelp/**": "#exclude",
"FFMpeg/**": "#exclude",
"LuaCompiler/**": "#exclude",
"MSVC90/**": "#exclude",
"OpenGL32/**": "#exclude",
"SSLEAY/**": "#exclude"
},
"RemoteConsole/**": "#include",
"__init__.py": "#include",
"WwiseAuthoringScripts/**": "#include",
"build/waf-1.7.13/**": "#include",
"build/waf-1.7.13/lmbrwaflib/crash_reporting*.py": "#exclude",
"crcfix/bin/**": "#include",
"lmbr_aws/**": "#include",
"maxscript/**": "#include",
"maya/**": "#include"
},
"config.provopath": "#exclude",
"editor.cfg": "#include",
"engine.json": "#include",
"engineroot.txt": "#include",
"lmbr_aws.cmd": "#include",
"lmbr_pak_shaders.bat": "#include",
"lmbr_pak_shaders.sh": "#include",
"lmbr_test.cmd": "#include",
"lmbr_test.sh": "#include",
"lmbr_test_blacklist.txt": "#include",
"SliceBuilderSettings.json": "#include",
"wscript": "#include",
"docs/**": "#include",
"LICENSE.txt": "#include"
}
}
@@ -0,0 +1,19 @@
{
"@lyengine": {
"dev": {
"Bin64vc???.Test": {
"**": "#include",
"**/ffmpeg.exe": "#exclude",
"logs/**": "#exclude",
"NewsBuilder*": "#exclude"
},
"Bin64vc???.Debug.Test": {
"**": "#include",
"**/ffmpeg.exe": "#exclude",
"logs/**": "#exclude",
"NewsBuilder*": "#exclude"
}
}
}
}
@@ -0,0 +1,7 @@
{
"@lyengine": {
"dev/**/*.pdb": "#include",
"dev/Tools/Crashpad/**": "#exclude",
"dev/Code/Tools/GameLabTestLauncher/**": "#exclude"
}
}
@@ -0,0 +1,30 @@
#
# 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 utils.util
import os
import sys
# Run validator
success = True
validator_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../distribution/scrubbing/validator.py')
engine_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))))
if sys.platform == 'win32':
python = os.path.join(engine_root, 'python', 'python.cmd')
else:
python = os.path.join(engine_root, 'python', 'python.sh')
args = [python, validator_path, '--package_platform', 'Windows', '--package_type', 'all', engine_root]
return_code = utils.util.safe_execute_system_call(args)
if return_code != 0:
success = False
if not success:
utils.util.error('Restricted file validator failed.')
print('Restricted file validator completed successfully.')
@@ -0,0 +1,92 @@
#
# 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 logging
import json
import socket
from datetime import datetime
SOCKET_TIMEOUT = 60
DATE_FORMAT = "%Y-%m-%dT%H:%M:%S"
FILEBEAT_PIPELINE = "filebeat"
FILEBEAT_DEFAULT_IP = "127.0.0.1"
FILEBEAT_DEFAULT_PORT = 9000
def parse_args():
parser = argparse.ArgumentParser(
prog="submit_metrics.py",
description="Pushes a JSON document via Filebeat.",
add_help=False
)
def file_arg(arg):
try:
with open(arg) as json_file:
return json.load(json_file)
except ValueError:
raise argparse.ArgumentTypeError("Invalid json file '%s'" % arg)
parser.add_argument("-f", "--file", default=None, type=file_arg, help="File containing JSON data to upload.")
parser.add_argument("-i", "--index", default=None, help="Index to use when sending the data")
parser.add_argument("-ip", "--filebeat_ip", default=FILEBEAT_DEFAULT_IP, help="IP address where filebeat service is listening")
parser.add_argument("-port", "--filebeat_port", default=FILEBEAT_DEFAULT_PORT, help="Port where filebeat service is listening")
return parser.parse_args()
def submit(index, payload, filebeat_ip = FILEBEAT_DEFAULT_IP, filebeat_port = FILEBEAT_DEFAULT_PORT):
try:
filebeat_address = filebeat_ip, filebeat_port
logging.debug(f"Connecting to Filebeat on '{filebeat_address[0]}:{filebeat_address[1]}'")
fb_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
fb_socket.settimeout(SOCKET_TIMEOUT)
fb_socket.connect(filebeat_address)
event = {
"index": index,
"timestamp": datetime.strptime(payload['timestamp'], DATE_FORMAT).strftime(DATE_FORMAT),
"pipeline": FILEBEAT_PIPELINE,
"payload": json.dumps(payload),
}
# Serialise event, add new line and encode as UTF-8 before sending to Filebeat.
data = json.dumps(event) + "\n"
data = data.encode()
total_sent = 0
logging.debug(f"Sending JSON data")
while total_sent < len(data):
try:
sent = fb_socket.send(data[total_sent:])
except BrokenPipeError:
print("An exception occurred while sending data")
fb_socket.close()
total_sent = 0
else:
total_sent = total_sent + sent
logging.debug("JSON data sent")
fb_socket.close()
logging.debug(f"Disconnected from Filebeat on '{filebeat_address[0]}:{filebeat_address[1]}'")
except (ConnectionError, socket.timeout):
logging.error("Failed to connect to Filebeat")
return False
return True
if __name__ == "__main__":
# Parse CLI arguments.
args = parse_args()
if not args.index:
logging.error(f"Index not specified")
exit(1)
if not submit(args.index, json.dumps(args.file), args.filebeat_ip, args.filebeat_port):
exit(1)
@@ -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.
"""
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) Amazon.com, Inc.
-->
<project name="CopyLadThirdParty" default="CopyLadThirdParty" basedir="../../../">
<fail message="Error: 3rdParty.home is not set">
<condition>
<not>
<isset property="3rdParty.home"/>
</not>
</condition>
</fail>
<fail message="Error: 3rdParty.destination is not set">
<condition>
<not>
<isset property="3rdParty.destination"/>
</not>
</condition>
</fail>
<fail message="Error: platform is not set">
<condition>
<not>
<isset property="platform"/>
</not>
</condition>
</fail>
<include file="../../distribution/package/3rdParty.xml" optional="false" />
<target name="CopyLadThirdParty">
<ThirdPartySDKsGeneratePlatformPatternSet platform="${platform}"/>
<copy todir="${3rdParty.destination}">
<fileset dir="${3rdParty.home}">
<patternset refid="include-3rdparty-patternset-common" />
</fileset>
</copy>
<copy todir="${3rdParty.destination}">
<fileset dir="${3rdParty.home}">
<patternset refid="include-3rdparty-patternset-${platform}" />
</fileset>
</copy>
<copy todir="${3rdParty.destination}">
<fileset dir="${3rdParty.home}">
<patternset id="include-3rdparty-patternset-non-shipped">
<include name="FbxSdk/**"/>
</patternset>
</fileset>
</copy>
</target>
</project>
@@ -0,0 +1,102 @@
"""
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.
"""
'''
Usage:
Use EC2 role to download files to %WORKSPACE% folder from bucket bucket_name:
python download_from_s3.py --base_dir %WORKSPACE% --files_to_download "file1,file2" --bucket bucket_name
Use profile to download files to %WORKSPACE% folder from bucket bucket_name:
python download_from_s3.py --base_dir %WORKSPACE% --profile profile --files_to_download "file1,file2" --bucket bucket_name
'''
import os
import json
import boto3
from optparse import OptionParser
from util import error
def parse_args():
parser = OptionParser()
parser.add_option("--base_dir", dest="base_dir", default=os.getcwd(), help="Base directory to download files, If not given, then current directory is used.")
parser.add_option("--files_to_download", dest="files_to_download", default=None, help="Files to download, separated by comma.")
parser.add_option("--profile", dest="profile", default=None, help="The name of a profile to use. If not given, then the default profile is used.")
parser.add_option("--bucket", dest="bucket", default=None, help="S3 bucket the files are downloaded from.")
parser.add_option("--key_prefix", dest="key_prefix", default='', help="Object key prefix.")
'''
ExtraArgs used to call s3.download_file(), should be in json format. extra_args key must be one of: ACL, CacheControl, ContentDisposition, ContentEncoding, ContentLanguage, ContentType, Expires,
GrantFullControl, GrantRead, GrantReadACP, GrantWriteACP, Metadata, RequestPayer, ServerSideEncryption, StorageClass,
SSECustomerAlgorithm, SSECustomerKey, SSECustomerKeyMD5, SSEKMSKeyId, WebsiteRedirectLocation
'''
parser.add_option("--extra_args", dest="extra_args", default=None, help="Additional parameters used to download file.")
parser.add_option("--max_retry", dest="max_retry", default=1, help="Maximum retry times to download file.")
(options, args) = parser.parse_args()
if not os.path.isdir(options.base_dir):
error('{} is not a valid directory'.format(options.base_dir))
if not options.files_to_download:
error('Use --files_to_download to specify files to download, separated by comma.')
if not options.bucket:
error('Use --bucket to specify bucket that the files are downloaded from.')
return options
def get_client(service_name, profile_name=None):
session = boto3.session.Session(profile_name=profile_name)
client = session.client(service_name)
return client
def s3_download_file(client, base_dir, file, bucket, key_prefix=None, extra_args=None, max_retry=1):
print 'Downloading file {} from bucket {}.'.format(file, bucket)
key = file if key_prefix is None else '{}/{}'.format(key_prefix, file)
for x in range(max_retry):
try:
client.download_file(
bucket, key, os.path.join(base_dir, file),
ExtraArgs=extra_args
)
print 'Download succeeded'
return True
except:
print 'Retrying download...'
print 'Download failed'
return False
def download_files(base_dir, files_to_download, bucket, key_prefix=None, profile=None, extra_args=None, max_retry=1):
client = get_client('s3', profile)
files_to_download = files_to_download.split(',')
extra_args = json.loads(extra_args) if extra_args else None
print 'Downloading {} files from bucket {}.'.format(len(files_to_download), bucket)
failure = []
success = []
for file in files_to_download:
if not s3_download_file(client, base_dir, file, bucket, key_prefix, extra_args, max_retry):
failure.append(file)
else:
success.append(file)
print '{} files are downloaded successfully:'.format(len(success))
print '\n'.join(success)
print '{} files failed to download:'.format(len(failure))
print '\n'.join(failure)
# Exit with error code 1 if any file is failed to download
if len(failure) > 0:
return False
return True
if __name__ == "__main__":
options = parse_args()
download_files(options.base_dir, options.files_to_download, options.bucket, options.key_prefix, options.profile, options.extra_args, options.max_retry)
@@ -0,0 +1,129 @@
"""
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.
"""
"""
This script will be used in https://jenkins.agscollab.com/view/%7ESandbox/job/PACKAGE_COPY_S3/
PACKAGE_COPY_S3 is a downstream job of nightly packaging job, it copies the nightly packages from Infra S3 bucket to Lionbridge S3 bucket based on the INCLUDE_FILTER passed from packaging job
"""
import os
import re
import json
import requests
from requests.auth import HTTPBasicAuth
import boto3
from util import error, warn
# Write EMAIL_TEMPLATE to a file and inject it into the email sent to Lionbridge
EMAIL_TEMPLATE = '''Packages are uploaded to S3 bucket {}
Package List:
{}
Changelists:
{}
'''
def get_jenkins_env(key):
try:
return os.environ[key]
except KeyError:
print 'Error: Jenkins parameters {} is not set.'.format(key)
return None
JENKINS_USERNAME = get_jenkins_env('JENKINS_USERNAME')
JENKINS_API_TOKEN = get_jenkins_env('JENKINS_API_TOKEN')
JENKINS_URL = get_jenkins_env('JENKINS_URL')
WORKSPACE = get_jenkins_env('WORKSPACE')
S3_TARGET = get_jenkins_env('S3_TARGET')
INCLUDE_FILTER = get_jenkins_env('INCLUDE_FILTER')
EMAIL_TEMPLATE_FILE = get_jenkins_env('EMAIL_TEMPLATE_FILE')
if None in [JENKINS_USERNAME, JENKINS_API_TOKEN, JENKINS_URL, WORKSPACE, S3_TARGET, INCLUDE_FILTER, EMAIL_TEMPLATE_FILE]:
error('Please make sure all Jenkins parameters are set correctly.')
def parse_include_filter(include_filter):
try:
res = re.search('^(\w*)-*lumberyard-(\d+)\.(\d+)-(\d+)-(\w+).*\*(\d+)\.\*', include_filter)
branch = res.group(1)
major_version = int(res.group(2))
minor_version = int(res.group(3))
changelist_number = res.group(4)
platform = res.group(5)
build_number = res.group(6)
return branch, major_version, minor_version, changelist_number, platform, build_number
except (AttributeError, IndexError):
error('Unable to parse INCLUDE_FILTER, please make sure the INCLUDE_FILTER is set correctly')
# Get the changelists that trigger the build
def get_changelists(job_name, build_number):
changelists = []
headers = {'Content-type': 'application/json', 'Accept': 'application/json'}
try:
res = requests.get('{}/job/{}/{}/api/json'.format(JENKINS_URL, job_name, build_number),
auth=HTTPBasicAuth(JENKINS_USERNAME, JENKINS_API_TOKEN), headers=headers, verify=False)
res = json.loads(res.content)
changelists = res.get('changeSet').get('items')
return changelists
except:
warn('Error: Failed to get changes from build {} in job {}'.format(build_number, job_name))
return []
def get_packaging_job_name(branch, major_version, minor_version, platform):
if branch == '':
branch = 'ML' if major_version + minor_version == 0 else 'v{}_{}'.format(major_version, minor_version)
job_name = 'PKG_{}_{}'.format(branch, platform.capitalize())
return job_name
# Get package names by looking up S3 bucket
def get_package_names(branch, major_version, minor_version, include_filter, build_number):
package_names = []
prefix = include_filter[:include_filter.find('*')]
pattern = '.*{}.*{}..*'.format(prefix, build_number)
if branch == '':
bucket_name = 'ly-packages-mainline' if major_version + minor_version == 0 else 'ly-packages-release-candidate'
folder = 'lumberyard-packages'
else:
bucket_name = 'ly-packages-feature-branches'
folder = 'lumberyard-packages/{}'.format(branch)
s3 = boto3.resource('s3')
bucket = s3.Bucket(bucket_name)
for obj in bucket.objects.filter(Prefix='{}/{}'.format(folder, prefix)):
package_name = obj.key
if re.match(pattern, package_name):
package_names.append(package_name.replace('{}/'.format(folder), ''))
return package_names
if __name__ == "__main__":
branch, major_version, minor_version, changelist_number, platform, build_number = parse_include_filter(INCLUDE_FILTER)
packaging_job_name = get_packaging_job_name(branch, major_version, minor_version, platform)
changelists = get_changelists(packaging_job_name, build_number)
package_names = get_package_names(branch, major_version, minor_version, INCLUDE_FILTER, build_number)
with open(os.path.join(WORKSPACE, EMAIL_TEMPLATE_FILE), 'w+') as output:
if len(package_names) > 0:
package_list_str = '\n'.join(package_names)
changelists_str = ''
for item in changelists:
changelists_str += '---------------------------------------------------------------------------------------------\n'
try:
changelists_str += 'CL{} by {} on {}\n{}\n'.format(item['changeNumber'], item['author']['fullName'], item['changeTime'], item['msg'].encode('utf-8', 'ignore'))
except KeyError:
error('Internal error, check the output of Jenkins API.')
output.write(EMAIL_TEMPLATE.format(S3_TARGET, package_list_str, changelists_str))
@@ -0,0 +1,662 @@
"""
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 ast
import boto3
import datetime
import urllib2
import os
import time
import subprocess
import sys
import tempfile
import traceback
import shutil
import platform
import stat
IAM_ROLE_NAME = 'ec2-jenkins-node'
if os.name == 'nt':
import ctypes
import win32api
import collections
import locale
locale.setlocale(locale.LC_ALL, '') # set locale to default to get thousands separators
PULARGE_INTEGER = ctypes.POINTER(ctypes.c_ulonglong) # Pointer to large unsigned integer
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
kernel32.GetDiskFreeSpaceExW.argtypes = (ctypes.c_wchar_p,) + (PULARGE_INTEGER,) * 3
class UsageTuple(collections.namedtuple('UsageTuple', 'total, used, free')):
def __str__(self):
# Add thousands separator to numbers displayed
return self.__class__.__name__ + '(total={:n}, used={:n}, free={:n})'.format(*self)
def is_dir_symlink(path):
FILE_ATTRIBUTE_REPARSE_POINT = 0x0400
return os.path.isdir(path) and (ctypes.windll.kernel32.GetFileAttributesW(unicode(path)) & FILE_ATTRIBUTE_REPARSE_POINT)
def get_free_space_mb(path):
if sys.version_info < (3,): # Python 2?
saved_conversion_mode = ctypes.set_conversion_mode('mbcs', 'strict')
else:
try:
path = os.fsdecode(path) # allows str or bytes (or os.PathLike in Python 3.6+)
except AttributeError: # fsdecode() not added until Python 3.2
pass
# Define variables to receive results when passed as "by reference" arguments
_, total, free = ctypes.c_ulonglong(), ctypes.c_ulonglong(), ctypes.c_ulonglong()
success = kernel32.GetDiskFreeSpaceExW(
path, ctypes.byref(_), ctypes.byref(total), ctypes.byref(free))
if not success:
error_code = ctypes.get_last_error()
if sys.version_info < (3,): # Python 2?
ctypes.set_conversion_mode(*saved_conversion_mode) # restore conversion mode
if not success:
windows_error_message = ctypes.FormatError(error_code)
raise ctypes.WinError(error_code, '{} {!r}'.format(windows_error_message, path))
used = total.value - free.value
return free.value / 1024 / 1024#for now
else:
def get_free_space_mb(dirname):
st = os.statvfs(dirname)
return st.f_bavail * st.f_frsize / 1024 / 1024
def get_iam_role_credentials(role_name):
security_metadata = None
try:
response = urllib2.urlopen(
'http://169.254.169.254/latest/meta-data/iam/security-credentials/{0}'.format(role_name)).read()
security_metadata = ast.literal_eval(response)
except:
print 'Unable to get iam role credentials'
print traceback.print_exc()
return security_metadata
def create_volume(ec2_client, availability_zone, project_name, volume_counter):
response = ec2_client.create_volume(
AvailabilityZone=availability_zone,
Size=300,
VolumeType='gp2',
TagSpecifications=
[
{
'ResourceType': 'volume',
'Tags':
[
{
'Key': 'Name',
'Value': '{0}'.format(project_name)
},
{
'Key': 'VolumeCounter',
'Value': str(volume_counter)
}
]
}
]
)
print response
volume_id = response['VolumeId']
# give some time for the creation call to complete
time.sleep(1)
response = ec2_client.describe_volumes(VolumeIds=[volume_id, ])
while (response['Volumes'][0]['State'] != 'available'):
time.sleep(1)
response = ec2_client.describe_volumes(VolumeIds=[volume_id, ])
return volume_id
def delete_volume(ec2_client, volume_id):
response = ec2_client.delete_volume(VolumeId=volume_id)
def unmount_build_volume_from_node():
if os.name == 'nt':
f = tempfile.NamedTemporaryFile(delete=False)
f.write("""
select disk 1
offline disk
""")
f.close()
subprocess.call('diskpart /s %s' % f.name)
os.unlink(f.name)
else:
subprocess.call(['umount', '/data'])
def detach_volume_from_node(ec2_client, volume, instance_id, force):
ec2_client.delete_tags(Resources=[volume.volume_id],
Tags=[
{
'Key': 'jenkins_attachment_node',
},
{
'Key': 'jenkins_attachment_time',
},
{
'Key': 'jenkins_attachment_build'
}
])
incremental_keys = ['jenkins_attachment_node', 'jenkins_attachment_time', 'jenkins_attachment_build']
volume.load()
print 'searching for keys adding during incremental build: {}'.format(incremental_keys)
while len(incremental_keys):
tag_keys = set()
for tag in volume.tags:
tag_keys.add(tag['Key'])
print 'found tags on instace {}'.format(tag_keys)
for incremental_key in list(incremental_keys):
if incremental_key not in tag_keys:
print 'incremental key {} has been successfully removed'.format(incremental_key)
incremental_keys.remove(incremental_key)
volume.load()
volume.detach_from_instance(Device='xvdf',
Force=force,
InstanceId=instance_id,
VolumeId=volume.volume_id)
while (len(volume.attachments) and volume.attachments[0]['State'] != 'detached'):
time.sleep(1)
volume.load()
volume.load()
if (len(volume.attachments)):
print 'Volume still has attachments'
for attachment in volume.attachments:
print 'Volume {} {} to instance {}'.format(attachment['VolumeId'], attachment['State'], attachment['InstanceId'])
def cleanup_node(workspace_name):
if os.name == 'nt':
jenkins_base = os.getenv('BASE')
dev_path = '{}\\workspace\\{}\\dev'.format(jenkins_base, workspace_name)
else:
dev_path = '/home/lybuilder/ly/workspace/{}/dev'.format(workspace_name)
if os.path.exists(dev_path):
if os.name == 'nt':
if is_dir_symlink(dev_path):
print "removing symlink path {}".format(dev_path)
os.rmdir(dev_path)
else:
# this shouldn't happen, but is here for sanity's sake, if we sync to the build node erroneously we want to clean it up if we can
print "given symlink path was not a symlink, deleting the full tree to prevent future build failures"
retcode = os.system('rmdir /S /Q {}'.format(dev_path))
if retcode != 0:
raise Exception("rmdir failed to remove directory: {}".format(dev_path))
return True
else:
if os.path.islink(dev_path):
print "unlinking symlink path {}".format(dev_path)
os.unlink(dev_path)
else:
print "given symlink path was not a symlink, deleting the full tree to prevent future build failures"
os.chmod(dev_path, stat.S_IWUSR)
shutil.rmtree(dev_path, ignore_errors=True)
return True
# check to make sure the directory was actually deleted
if os.path.exists(dev_path):
raise Exception("Failed to remove directory: {}".format(dev_path))
return False
def setup_volume(workspace_name, created):
if os.name == 'nt':
f = tempfile.NamedTemporaryFile(delete=False)
f.write("""
select disk 1
online disk
attribute disk clear readonly
""") # assume disk # for now
if created:
f.write("""create partition primary
select partition 1
format quick fs=ntfs
assign
active
""")
f.close()
subprocess.call(['diskpart', '/s', f.name])
time.sleep(2)
drives_after = win32api.GetLogicalDriveStrings()
drives_after = drives_after.split('\000')[:-1]
print drives_after
#drive_letter = next(item for item in drives_after if item not in drives_before)
drive_letter = 'D:\\'
os.unlink(f.name)
time.sleep(1)
dev_path = '{}ly\workspace\{}\dev'.format(drive_letter, workspace_name)
else:
subprocess.call(['file', '-s', '/dev/xvdf'])
if created:
subprocess.call(['mkfs', '-t', 'ext4', '/dev/xvdf'])
subprocess.call(['mount', '/dev/xvdf', '/data'])
dev_path = '/data/ly/workspace/{}/dev'.format(workspace_name)
return dev_path
def attach_volume_to_instance(volume, volume_id, instance_id, instance_name):
volume.attach_to_instance(Device='xvdf',
InstanceId=instance_id,
VolumeId=volume_id)
# give a little bit of time for the aws call to process
time.sleep(2)
# reload the volume just in case
volume.load()
while (len(volume.attachments) and volume.attachments[0]['State'] != 'attached'):
time.sleep(1)
volume.load()
volume.create_tags(Tags=[
{
'Key':'last_attachment_time',
'Value':datetime.datetime.utcnow().isoformat()
}
])
volume.create_tags(Tags=[
{
'Key':'jenkins_attachment_node',
'Value':instance_name,
},
{
'Key':'jenkins_attachment_time',
'Value':datetime.datetime.utcnow().isoformat()
},
{
'Key':'jenkins_attachment_build',
'Value':os.getenv('BUILD_TAG')
}
])
def prepare_incremental_build(workspace_name):
job_name = os.getenv('JOB_NAME', None)
clean_build = os.getenv('CLEAN_BUILD', 'false').lower() == 'true'
android_home = os.getenv('ANDROID_HOME', None)
if android_home is not None:
path = os.getenv('PATH').split(';')
print path
java_home = os.getenv('JAVA_HOME', None)
print java_home
os.environ['LY_NDK_PATH'] = 'C:\\ly\\3rdParty\\android-ndk\\r12'
print os.getenv('LY_NDK_PATH')
path = [x for x in path if not (java_home in x or android_home in x)]
print path
path.append(java_home)
path.append(android_home)
path.append(os.getenv('LY_NDK_PATH'))
print path
os.environ['PATH'] = ';'.join(path)
credentials = get_iam_role_credentials(IAM_ROLE_NAME)
aws_access_key_id = None
aws_secret_access_key = None
aws_session_token = None
if credentials is not None:
keys = ['AccessKeyId', 'SecretAccessKey', 'Token']
for key in keys:
if key not in credentials:
print 'Unable to find {0} in get_iam_role_credentials response {1}'.format(key, credentials)
return
aws_access_key_id = credentials['AccessKeyId']
aws_secret_access_key = credentials['SecretAccessKey']
aws_session_token = credentials['Token']
session = boto3.session.Session()
region = session.region_name
try:
instance_id = urllib2.urlopen('http://169.254.169.254/latest/meta-data/instance-id').read()
except:
# this likely means we're not an ec2 instance
raise Exception('No EC2 metadata!')
try:
availability_zone = urllib2.urlopen(
'http://169.254.169.254/latest/meta-data/placement/availability-zone').read()
except:
# also likely means we're not an ec2 instance
raise Exception('No EC2 metadata')
if region is None:
region = 'us-west-2'
client = boto3.client('ec2', region_name=region, aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
aws_session_token=aws_session_token)
project_name = job_name
ec2_resource = boto3.resource('ec2', region_name=region)
instance = ec2_resource.Instance(instance_id)
volume_counter = 0
for volume in instance.volumes.all():
for attachment in volume.attachments:
print 'attachment device: {}'.format(attachment['Device'])
if 'xvdf' in attachment['Device'] and attachment['State'] != 'detached':
print 'A device is already attached to xvdf. This likely means a previous build failed to detach it\'s' \
'build volume. This volume is considered orphaned and will be force detached from this instance.'
unmount_build_volume_from_node()
detach_volume_from_node(client, volume, instance_id, True)
if cleanup_node(workspace_name):
clean_build = True
response = client.describe_volumes(Filters=
[
{
'Name': 'tag:Name',
'Values':
[
'{0}'.format(project_name)
]
}
])
created = False
if 'Volumes' in response and not len(response['Volumes']):
print 'Volume for {0} doesn\'t exist creating it...'.format(project_name)
# volume doesn't exist, create it
volume_id = create_volume(client, availability_zone, project_name, volume_counter)
created = True
elif len(response['Volumes']) > 1:
latest_volume = None
max_counter = 0
for volume in response['Volumes']:
for tag in volume['Tags']:
if tag['Key'] == 'VolumeCounter':
if int(tag['Value']) > max_counter:
max_counter = int(tag['Value'])
latest_volume = volume
volume_counter = max_counter
volume_id = latest_volume['VolumeId']
else:
volume = response['Volumes'][0]
if len(volume['Attachments']):
# this is bad we shouldn't be attached, we should have detached at the end of a build
attachment = volume['Attachments'][0]
print ('Volume already has attachment {}'.format(attachment))
print 'Creating new volume for {} and orphaning previous volume'.format(project_name)
for tag in volume['Tags']:
if tag['Key'] == 'VolumeCounter':
volume_counter = int(tag['Value']) + 1
break
volume_id = create_volume(client, availability_zone, project_name, volume_counter)
created = True
else:
volume_id = volume['VolumeId']
if clean_build and not created:
print 'CLEAN_BUILD option was set, deleting volume {0}'.format(volume_id)
revert_workspace(job_name)
delete_volume(client, volume_id)
volume_id = create_volume(client, availability_zone, project_name, volume_counter)
created = True
print 'attaching volume {} to instance {}'.format(volume_id, instance_id)
volume = ec2_resource.Volume(volume_id)
instance_name = next(tag['Value'] for tag in instance.tags if tag['Key'] == 'Name')
if os.name == 'nt':
drives_before = win32api.GetLogicalDriveStrings()
drives_before = drives_before.split('\000')[:-1]
print drives_before
attach_volume_to_instance(volume, volume_id, instance_id, instance_name)
dev_path = setup_volume(workspace_name, created)
dev_existed = True
if os.name == 'nt':
free_space_path = 'D:\\'
else:
free_space_path = '/data/'
if get_free_space_mb(free_space_path) < 1024:
print 'Volume is running low on disk space. Recreating volume and running clean build.'
unmount_build_volume_from_node()
detach_volume_from_node(client, volume, instance_id, False)
delete_volume(client, volume_id)
volume_id = create_volume(client, availability_zone, project_name, volume_counter)
volume = ec2_resource.Volume(volume_id)
attach_volume_to_instance(volume, volume_id, instance_id, instance_name)
setup_volume(workspace_name, True)
if not os.path.exists(dev_path):
print 'creating directory structure for {}'.format(dev_path)
os.makedirs(dev_path)
if os.name != 'nt':
print 'taking ownership of {}'.format(dev_path)
subprocess.call(['chown', '-R', 'lybuilder:root', dev_path])
dev_existed = False
if os.name == 'nt':
jenkins_base = os.getenv('BASE')
try:
symlink_path = '{}\\workspace\\{}\\dev'.format(jenkins_base, workspace_name)
print 'creating symlink to path: {}'.format(symlink_path)
subprocess.call(['cmd', '/c', 'mklink', '/J', symlink_path, dev_path])
#subprocess.call(['cmd', '/c', 'mklink', '/J', '{}\\3rdParty'.format(jenkins_base), 'E:\\3rdParty'])
except Exception as e:
print e
else:
subprocess.call(['ln', '-s', '-f', dev_path, '/home/lybuilder/ly/workspace/{}'.format(workspace_name)])
subprocess.call(['ln', '-s', '-f', '/home/lybuilder/ly/workspace/3rdParty', '/data/ly/workspace'])
if not dev_existed:
print 'flushing perforce #have revision'
subprocess.call(['p4', 'trust'])
subprocess.call(['p4', 'flush', '-f', '//ly_jenkins_{}/dev/...#none'.format(job_name)])
#subprocess.call(['p4', 'sync', '-f', '//ly_jenkins_{}/dev/...'.format(job_name)])
def revert_workspace(job_name):
try:
# Workaround for LY-86789: Revert bootstrap.cfg checkout.
print "REVERTING workspace {}".format(job_name)
subprocess.check_call(['p4', 'revert', '//ly_jenkins_{}/dev/...'.format(job_name)])
except subprocess.CalledProcessError as e:
print e.output
raise e
except Exception as e:
print e
raise e
def teardown_incremental_build(workspace_name):
job_name = os.getenv('JOB_NAME', None)
if os.path.isfile('envinject.properties'):
os.remove('envinject.properties')
credentials = get_iam_role_credentials(IAM_ROLE_NAME)
aws_access_key_id = None
aws_secret_access_key = None
aws_session_token = None
if credentials is not None:
keys = ['AccessKeyId', 'SecretAccessKey', 'Token']
for key in keys:
if key not in credentials:
raise Exception('Unable to find {0} in get_iam_role_credentials response {1}'.format(key, credentials))
aws_access_key_id = credentials['AccessKeyId']
aws_secret_access_key = credentials['SecretAccessKey']
aws_session_token = credentials['Token']
session = boto3.session.Session()
region = session.region_name
try:
instance_id = urllib2.urlopen('http://169.254.169.254/latest/meta-data/instance-id').read()
except:
# this likely means we're not an ec2 instance
raise Exception('No EC2 metadata!')
if region is None:
region = 'us-west-2'
client = boto3.client('ec2', region_name=region, aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
aws_session_token=aws_session_token)
project_name = job_name
response = client.describe_volumes(Filters=
[
{
'Name': 'tag:Name',
'Values':
[
'{0}'.format(project_name)
]
}
])
ec2_resource = boto3.resource('ec2', region_name=region)
instance = ec2_resource.Instance(instance_id)
volume = None
for attached_volume in instance.volumes.all():
for attachment in attached_volume.attachments:
print 'attachment device: {}'.format(attachment['Device'])
if attachment['Device'] == 'xvdf':
volume = attached_volume
if volume is None:
# volume doesn't exist, do nothing
print 'Volume for {} does not exist or is not attached to the current instance. This probably isn\'t an issue but should be reported.'.format(project_name)
return
else:
revert_workspace(job_name)
unmount_build_volume_from_node()
detach_volume_from_node(client, volume, instance_id, False)
cleanup_node(workspace_name)
def prepare_incremental_build_mac(workspace_name):
job_name = os.getenv('JOB_NAME', None)
clean_build = os.getenv('CLEAN_BUILD', 'false').lower() == 'true'
subprocess.call(['mount', '-t', 'smbfs', '//lybuilder:Builder99@gt-sna11-nas-01.local/inc-build/ly', '/data/ly'])
dev_path = '/data/ly/workspace/{}/dev'.format(workspace_name)
dev_existed = True
if clean_build:
print 'cleaning {}'.format(dev_path)
subprocess.call(['rm', '-rf', dev_path])
if not os.path.exists(dev_path):
print 'creating directory structure for {}'.format(dev_path)
os.makedirs(dev_path)
dev_existed = False
#subprocess.call(['ln', '-s', '-f', '/data/ly/workspace', '/Users/lybuilder'])
#subprocess.call(['ln', '-s', '-f', '/Users/lybuilder/workspace/3rdParty', '/data/ly/workspace'])
if not dev_existed:
print 'flushing perforce #have revision'
subprocess.call(['p4', 'trust'])
subprocess.call(['p4', 'flush', '-f', '//ly_jenkins_{}/dev/...#none'.format(job_name)])
def main():
action = sys.argv[1]
workspace_name = sys.argv[2]
if action.lower() == 'prepare':
if platform.system().lower() == 'darwin':
prepare_incremental_build_mac(workspace_name)
else:
prepare_incremental_build(workspace_name)
elif action.lower() == 'teardown':
if platform.system().lower() == 'darwin':
pass
else:
teardown_incremental_build(workspace_name)
else:
'Invalid command. Valid actions are either "prepare" or "teardown."'
if __name__ == '__main__':
main()
@@ -0,0 +1,57 @@
"""
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.
"""
'''
All this script is doing is writing grabbing a file that was written previously marking the start of when the Perforce would run
and then getting the current time to find out how long we spent in Perforce
'''
import time
from util import *
def write_metrics():
enable_build_metrics = os.environ.get('ENABLE_BUILD_METRICS')
metrics_namespace = os.environ.get('METRICS_NAMESPACE')
if enable_build_metrics == 'true':
scm_end = int(time.time())
workspace = os.environ.get('WORKSPACE')
metrics_file_name = 'scm_start.txt'
if workspace is None:
safe_exit_with_error('{} must be run in Jenkins job.'.format(os.path.basename(__file__)))
try:
with open(os.path.join(workspace, metrics_file_name), 'r') as f:
scm_start = int(f.readline())
except:
safe_exit_with_error('Failed to read from {}'.format(metrics_file_name))
scm_total = scm_end - scm_start
script_path = os.path.join(workspace, 'dev/Tools/build/waf-1.7.13/build_metrics/write_build_metric.py')
build_tag = os.environ.get('BUILD_TAG')
p4_changelist = os.environ.get('P4_CHANGELIST')
if build_tag is not None and p4_changelist is not None:
os.environ['BUILD_ID'] = '{0}.{1}'.format(build_tag, p4_changelist)
cwd = os.getcwd()
os.chdir(os.path.join(workspace, 'dev'))
cmd = 'python {} SCMTime {} Seconds --enable-build-metrics {} --metrics-namespace {} --project-spec None'.format(script_path, scm_total, True, metrics_namespace)
# metrics call shouldn't fail the job
safe_execute_system_call(cmd, shell=True)
os.chdir(cwd)
if __name__ == "__main__":
write_metrics()
@@ -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.
"""
@@ -0,0 +1,174 @@
"""
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.
"""
"""
Filename globbing utility.
Modified using https://github.com/python/cpython/blob/3.7/Lib/glob.py to be compatible with Python2
Original file Copyright Python Software Foundation, used under license.
Modifications copyright Amazon.com, Inc. or its affiliates.
"""
import os
import re
import fnmatch
__all__ = ["glob", "iglob", "escape"]
def glob(pathname, recursive=False):
"""Return a list of paths matching a pathname pattern.
The pattern may contain simple shell-style wildcards a la
fnmatch. However, unlike fnmatch, filenames starting with a
dot are special cases that are not matched by '*' and '?'
patterns.
If recursive is true, the pattern '**' will match any files and
zero or more directories and subdirectories.
"""
return list(iglob(pathname, recursive=recursive))
def iglob(pathname, recursive=False):
"""Return an iterator which yields the paths matching a pathname pattern.
The pattern may contain simple shell-style wildcards a la
fnmatch. However, unlike fnmatch, filenames starting with a
dot are special cases that are not matched by '*' and '?'
patterns.
If recursive is true, the pattern '**' will match any files and
zero or more directories and subdirectories.
"""
it = _iglob(pathname, recursive, False)
if recursive and _isrecursive(pathname):
s = next(it) # skip empty string
assert not s
return it
def _iglob(pathname, recursive, dironly):
dirname, basename = os.path.split(pathname)
if not has_magic(pathname):
assert not dironly
if basename:
if os.path.lexists(pathname):
yield pathname
else:
# Patterns ending with a slash should match only directories
if os.path.isdir(dirname):
yield pathname
return
if not dirname:
if recursive and _isrecursive(basename):
yield _glob2(dirname, basename, dironly)
else:
yield _glob1(dirname, basename, dironly)
return
# `os.path.split()` returns the argument itself as a dirname if it is a
# drive or UNC path. Prevent an infinite recursion if a drive or UNC path
# contains magic characters (i.e. r'\\?\C:').
if dirname != pathname and has_magic(dirname):
dirs = _iglob(dirname, recursive, True)
else:
dirs = [dirname]
if has_magic(basename):
if recursive and _isrecursive(basename):
glob_in_dir = _glob2
else:
glob_in_dir = _glob1
else:
glob_in_dir = _glob0
for dirname in dirs:
for name in glob_in_dir(dirname, basename, dironly):
yield os.path.join(dirname, name)
# These 2 helper functions non-recursively glob inside a literal directory.
# They return a list of basenames. _glob1 accepts a pattern while _glob0
# takes a literal basename (so it only has to check for its existence).
def _glob1(dirname, pattern, dironly):
names = list(_iterdir(dirname, dironly))
return fnmatch.filter(names, pattern)
def _glob0(dirname, basename, dironly):
if not basename:
# `os.path.split()` returns an empty basename for paths ending with a
# directory separator. 'q*x/' should match only directories.
if os.path.isdir(dirname):
return [basename]
else:
if os.path.lexists(os.path.join(dirname, basename)):
return [basename]
return []
# Following functions are not public but can be used by third-party code.
def glob0(dirname, pattern):
return _glob0(dirname, pattern, False)
def glob1(dirname, pattern):
return _glob1(dirname, pattern, False)
# This helper function recursively yields relative pathnames inside a literal
# directory.
def _glob2(dirname, pattern, dironly):
assert _isrecursive(pattern)
return [pattern[:0]] + list(_rlistdir(dirname, dironly))
# If dironly is false, yields all file names inside a directory.
# If dironly is true, yields only directory names.
def _iterdir(dirname, dironly):
if not dirname:
if isinstance(dirname, bytes):
dirname = bytes(os.curdir, 'ASCII')
else:
dirname = os.curdir
try:
for entry in os.listdir(dirname):
yield entry
except OSError:
return
# Recursively yields relative pathnames inside a literal directory.
def _rlistdir(dirname, dironly):
if not os.path.islink(dirname):
names = list(_iterdir(dirname, dironly))
for x in names:
yield x
path = os.path.join(dirname, x) if dirname else x
for y in _rlistdir(path, dironly):
yield os.path.join(x, y)
magic_check = re.compile('([*?[])')
magic_check_bytes = re.compile(b'([*?[])')
def has_magic(s):
if isinstance(s, bytes):
match = magic_check_bytes.search(s)
else:
match = magic_check.search(s)
return match is not None
def _ishidden(path):
return path[0] in ('.', b'.'[0])
def _isrecursive(pattern):
if isinstance(pattern, bytes):
return pattern == b'**'
else:
return pattern == '**'
def escape(pathname):
"""Escape all special characters.
"""
# Escaping is done by wrapping any of "*?[" between square brackets.
# Metacharacters do not work in the drive part and shouldn't be escaped.
drive, pathname = os.path.splitdrive(pathname)
if isinstance(pathname, bytes):
pathname = magic_check_bytes.sub(br'[\1]', pathname)
else:
pathname = magic_check.sub(r'[\1]', pathname)
return drive + pathname
@@ -0,0 +1,75 @@
"""
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 sys
from validate_ly_version import _read_version_from_branch_spec
from argparse import ArgumentParser
def main(args):
waf_branch_spec_file_directory = os.path.join(os.environ['WORKSPACE'], 'dev')
waf_branch_spec_file_name = 'waf_branch_spec.py'
if not os.path.exists(os.path.join(waf_branch_spec_file_directory, waf_branch_spec_file_name)):
raise Exception("Invalid workspace directory: {}".format(waf_branch_spec_file_directory))
waf_branch_spec_version = _read_version_from_branch_spec(waf_branch_spec_file_directory, waf_branch_spec_file_name)
if waf_branch_spec_version is None:
raise Exception("Unable to read branch spec version from {}.".format(waf_branch_spec_file_name))
if args.version:
waf_branch_spec_version = args.version
if waf_branch_spec_version=='0.0.0.0' and not args.allow_unversioned:
raise Exception('Version "{}" is invalid. Please specify a valid, non-zero LUMBERYARD_VERSION in {}.'.format(
waf_branch_spec_version,
os.path.join(waf_branch_spec_file_directory, waf_branch_spec_file_name)
))
versions = waf_branch_spec_version.split('.')
if len(versions) != 4:
raise Exception("Invalid branch spec version '{}'. Must use format 'X.X.X.X'".format(waf_branch_spec_version))
major_version = versions[0]
minor_version = versions[1]
env_inject_file_path = os.path.join(os.environ['WORKSPACE'], os.environ['ENV_INJECT_FILE'])
print major_version
print minor_version
with open(env_inject_file_path, 'w') as env_inject_file:
env_inject_file.write('MAJOR_VERSION={}\n'.format(major_version))
env_inject_file.write('MINOR_VERSION={}\n'.format(minor_version))
def check_env(*vars):
missing = []
for var in vars:
if var not in os.environ:
missing += (var,)
if missing:
raise Exception("Missing one or more environment variables: {}".format(", ".join(missing)))
if __name__ == "__main__":
parser = ArgumentParser()
parser.add_argument('--allow-unversioned', default=False, action='store_true',
help="Allow version '0.0.0.0'. Default is to fail if an invalid version is found")
parser.add_argument('--version', type=str,
help="Manually specify version to use, instead of scanning dev root.")
args = parser.parse_args()
check_env("WORKSPACE", "ENV_INJECT_FILE")
main(args)
@@ -0,0 +1,212 @@
#
# 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 requests
from requests.auth import HTTPBasicAuth
from P4 import P4, P4Exception
from util import *
from zipfile import ZipFile
from download_from_s3 import s3_download_file, get_client
from botocore.exceptions import ClientError
from upload_to_s3 import s3_upload_file
import os
import shutil
import json
import urllib
PACKAGE_NAME_REGEX = r'^lumberyard-\d\.\d-\d+-\w+-\d+\.(zip|tgz)$'
P4_USER = 'lybuilder'
BUCKET = 'ly-scrubbing-test'
# Scrubber will fail if any of these files is missing, copy these files to scrubbing workspace before the test
SCRUBBING_REQUIRED_FILES = [
]
try:
JENKINS_USERNAME = os.environ['JENKINS_USERNAME']
JENKINS_API_TOKEN = os.environ['JENKINS_API_TOKEN']
JENKINS_SERVER = os.environ['JENKINS_URL']
P4_PORT = os.environ['ENV_P4_PORT']
JOB_NAME = os.environ['JOB_NAME']
BUILD_NUMBER = int(os.environ['BUILD_NUMBER'])
WORKSPACE = os.environ['WORKSPACE']
SCRUBBING_WORKSPACE = os.environ['SCRUBBING_WORKSPACE']
except KeyError:
error('This script has to run on Jenkins')
class File:
def __init__(self, path, action):
self.path = path
self.action = action
# Get the changelist numbers that trigger the build
def get_changelist_numbers():
changelist_numbers = []
changeset = []
headers = {'Content-type': 'application/json', 'Accept': 'application/json'}
try:
res = requests.get('{}/job/{}/{}/api/json'.format(JENKINS_SERVER, JOB_NAME, BUILD_NUMBER),
auth=HTTPBasicAuth(JENKINS_USERNAME, JENKINS_API_TOKEN), headers=headers, verify=False)
res = json.loads(res.content)
changeset = res.get('changeSet').get('items')
except:
print 'Error: Failed to get changes from build {} in job {}'.format(BUILD_NUMBER, JOB_NAME)
for item in changeset:
changelist_numbers.append(item.get('changeNumber'))
return changelist_numbers
# Get file list and actions that trigger the Jenkins job
def get_files():
p4 = P4()
p4.port = P4_PORT
p4.user = P4_USER
p4.connect()
files = []
changelist_numbers = get_changelist_numbers()
for changelist_number in changelist_numbers:
cmd = ['describe', '-s', changelist_number]
try:
res = p4.run(cmd)[0]
file_list = res.get('depotFile')
actions = res.get('action')
for action, file_path in zip(actions, file_list):
# P4 returns file paths that are url encoded
file_path = urllib.unquote(file_path).decode("utf8")
# Ignore files which are not in dev
p = file_path.find('dev')
if p != -1:
files.append(File(file_path[p:], action))
except P4Exception:
error('Internal error, please contact Build System')
return files
def copy_file(src, dst, overwrite=False):
if os.path.exists(dst) and not overwrite:
return
print 'Copying file from {} to {}'.format(src, dst)
dest_file_dir = os.path.dirname(dst)
if not os.path.exists(dest_file_dir):
os.makedirs(dest_file_dir)
shutil.copyfile(src, dst)
# Run scrubbing scripts
def scrub():
print 'Perform the Code Scrubbing'
scrubber_path = os.path.join(WORKSPACE, 'dev/Tools/build/JenkinsScripts/distribution/scrubbing/scrub_all.py')
scrub_params = ["-p", "-d", "-o"]
# Scrub code
args = ['python', scrubber_path, '-p', '-d', '-o', os.path.join(SCRUBBING_WORKSPACE, 'dev/Code'), os.path.join(SCRUBBING_WORKSPACE, 'dev')]
return_code = safe_execute_system_call(args)
if return_code != 0:
print 'ERROR: Code scrubbing failed.'
return False
print 'Code scrubbing complete successfully.'
return True
# Run scrubbing validator
def validate():
# Run validator
print 'Running validator'
validator_platforms = ["provo", "salem", "xenia"]
success = True
for validator_platform in validator_platforms:
validator_path = os.path.join(WORKSPACE, 'dev/Tools/build/JenkinsScripts/distribution/scrubbing/validator.py')
args = ['python', validator_path, '-p', validator_platform, os.path.join(SCRUBBING_WORKSPACE, 'dev')]
if safe_execute_system_call(args):
success = False
if not success:
print 'ERROR: Scrubbing validator failed.'
return False
print 'Scrubbing validator complete successfully.'
return True
def scrubbing_test():
if os.path.exists(SCRUBBING_WORKSPACE):
os.system('rmdir /s /q \"{}\"'.format(SCRUBBING_WORKSPACE))
os.mkdir(SCRUBBING_WORKSPACE)
client = get_client('s3')
zip_name = '{}.zip'.format(JOB_NAME)
# Check if zipfile exists in S3 bucket
try:
client.head_object(Bucket=BUCKET, Key=zip_name)
except ClientError as e:
if e.response['Error']['Code'] == '404':
print 'No previous zipfile found in S3 bucket {}'.format(BUCKET)
else:
raise
else:
# Download the zipfile from S3 bucket if the zipfile exists
if not s3_download_file(client, SCRUBBING_WORKSPACE, zip_name, BUCKET, max_retry=3):
warn('Failed to download {} from S3 bucket {}'.format(zip_name, BUCKET))
# Unzip the zipfile to SCRUBBING_WORKSPACE
zip_path = os.path.join(SCRUBBING_WORKSPACE, zip_name)
if os.path.exists(zip_path):
zip_file = ZipFile(os.path.join(SCRUBBING_WORKSPACE, zip_name), 'r')
zip_file.extractall(SCRUBBING_WORKSPACE)
zip_file.close()
# Copy scrubbing required files to SCRUBBING_WORKSPACE, no overwrite
for file in SCRUBBING_REQUIRED_FILES:
src_file = os.path.join(WORKSPACE, file)
dst_file = os.path.join(SCRUBBING_WORKSPACE, file)
copy_file(src_file, dst_file)
# Get file list and actions that trigger the Jenkins job
files = get_files()
# Copy or delete each file in SCRUBBING_WORKSPACE
for f in files:
dst_file = os.path.join(SCRUBBING_WORKSPACE, f.path)
if 'delete' in f.action:
if os.path.exists(dst_file):
print 'Deleting {}'.format(dst_file)
os.remove(dst_file)
else:
src_file = os.path.join(WORKSPACE, f.path)
copy_file(src_file, dst_file, overwrite=True)
# Backup the unmodified files and run scrubber and validator
backup_path = os.path.join(SCRUBBING_WORKSPACE, 'backup')
scrubbing_dev = os.path.join(SCRUBBING_WORKSPACE, 'dev')
success = True
if os.path.exists(scrubbing_dev):
shutil.copytree(scrubbing_dev, os.path.join(backup_path, 'dev'))
success = scrub() and validate()
if success:
# Delete zipfile from S3 if validator run successfully
try:
print 'Deleting {} from bucket {}'.format(zip_name, BUCKET)
client.delete_object(Bucket=BUCKET, Key=zip_name)
except:
warn('Failed to delete {} from bucket {}'.format(zip_name, BUCKET))
else:
# Upload backup files to S3 bucket
if os.path.exists(backup_path):
zip_path = os.path.join(SCRUBBING_WORKSPACE, JOB_NAME)
shutil.make_archive(zip_path, 'zip', backup_path)
if not s3_upload_file(client, SCRUBBING_WORKSPACE, zip_name, BUCKET, max_retry=3):
error('Failed to upload {} to S3 bucket {}'.format(zip_name, BUCKET))
exit(1)
if __name__ == "__main__":
scrubbing_test()
@@ -0,0 +1,82 @@
"""
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.
"""
'''
This script is to update the configuration in dev/bootstrap.cfg
Usage: python update_bootstrap_cfg.py --bootstrap_cfg file_path --replace key1=value1,key2=value2
'''
from optparse import OptionParser
import os
import stat
def update_bootstrap_cfg(file, replace_values):
try:
with open(file, 'r') as bootstrap_cfg:
content = bootstrap_cfg.read()
except:
error('Cannot read file {}'.format(file))
content = content.split('\n')
new_content = []
for line in content:
if not line.startswith('--'):
strs = line.split('=')
if len(strs):
key = strs[0].strip(' ')
if key in replace_values:
line = '{}={}'.format(key, replace_values[key])
new_content.append(line)
try:
with open(file, 'w') as out:
out.write('\n'.join(new_content))
except:
error('Cannot write to file {}'.format(file))
print '{} updated with value {}'.format(file, replace_values)
def error(msg):
print msg
exit(1)
def parse_args():
parser = OptionParser()
parser.add_option("--bootstrap_cfg", dest="bootstrap_cfg", default=None, help="File path of bootstrap.cfg to be updated.")
parser.add_option("--replace", dest="replace", default=None, help="Target platform to package")
(options, args) = parser.parse_args()
bootstrap_cfg = options.bootstrap_cfg
replace = options.replace
if not bootstrap_cfg:
error('bootstrap.cfg is not specified.')
if not os.path.isfile(bootstrap_cfg):
error('File {} not found.'.format(bootstrap_cfg))
replace_values = {}
if replace:
try:
replace = replace.split(',')
for r in replace:
r = r.split('=')
key = r[0].strip(' ')
value = r[1].strip(' ')
replace_values[key] = value
except IndexError:
error('Please check the format of argument --replace.')
return bootstrap_cfg, replace_values
if __name__ == "__main__":
(file, replace_values) = parse_args()
update_bootstrap_cfg(file, replace_values)
@@ -0,0 +1,174 @@
#!/usr/bin/env python
#
# 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
from datetime import datetime, timezone
import hashlib
import os
import pathlib
import platform
import re
import sys
import subprocess
import zipfile
'''
Creates zip file of <BuildDir>/BenchmarkResults folder and sends the the zip via email to team email
'''
def compute_sha256(input_filepath):
'''
Computes a SHA-2 hash using a digest that is 256 bits
Args:
input_filepath: File whose content will be hashed using the SHA-2 hash function
Returns:
bytes: byte array containing hash digest in hex
'''
hasher = hashlib.sha256()
hash_result = None
CHUNK_SIZE = 128 * (1 << 10) # Chunk Size for Sha256 hashing reads file in chunks of 128 KiB
with open(input_filepath, 'rb') as hash_file:
buf = hash_file.read(CHUNK_SIZE)
hasher.update(buf)
hash_result = hasher.hexdigest()
return hash_result
def create_sha256sums_file(input_filepath):
'''
Create a sha256sums file from the contents of the input_filepath
The sha256sums file will be named using the input_filepath path with an added
extension of .sha256sums
Args:
input_filepath: File whose content will be hashed using the SHA-2 hash function
Returns:
string: Path to sha256sums file
'''
sha256_hash = compute_sha256(input_filepath)
if not sha256_hash:
print(f'Unable to compute sha256 hash for file {input_filepath}')
return None
hash_filepath = f'{input_filepath}.sha256sums'
with open(hash_filepath, "wb") as archive_hash_file:
new_hash_contents = f'{sha256_hash} *{os.path.basename(input_filepath)}\n'
archive_hash_file.write(new_hash_contents.encode("utf8"))
return hash_filepath
def get_files_to_archive(base_dir, regex):
'''
Gathers list of filepaths to add to archive file
Files are looked up with the base directory and cross checked against the supplied regular expression
which acts as an inclusion filter
Args:
base_dir: Directory to scan for files
regex: Regular expression that is matched against each filename to determine if the file should be
added to the archive
'''
# Get all file names in base directory
with os.scandir(base_dir) as dir_entry:
filepaths = [pathlib.PurePath(entry.path) for entry in dir_entry if entry.is_file()]
# Get all file names matching the regular expression, those file will be added to zip archive
archive_files = [str(filepath) for filepath in filepaths if re.match(regex, filepath.as_posix())]
return archive_files
return None
def create_archive_file(archive_file_prefix, input_filepaths, base_dir):
'''
Creates a zip file using the supplied input files
LZMA compression is used by default for the zip file compression
Args:
archive_file_prefix: Prefix to use as the name of the zip file that should be created
input_filepaths: List of input file paths that will be added to zip file
base_dir: Directory which is used to create relative paths for each input file path from
'''
try:
zipfile_name = '{}-{:%Y%m%d_%H%M%S}.zip'.format(archive_file_prefix,datetime.now(timezone.utc))
# The lzma shared library isn't installed by default on Mac.
compression_type = zipfile.ZIP_LZMA if platform.system() != 'Darwin' else zipfile.ZIP_BZIP2
with zipfile.ZipFile(zipfile_name, mode='w', compression=compression_type) as benchmark_archive:
zipfile_name = benchmark_archive.filename
for input_filepath in input_filepaths:
# Make input files relative to base_dir when storing them as archived names
input_filepath_relpath = os.path.relpath(input_filepath, start=base_dir)
benchmark_archive.write(input_filepath, input_filepath_relpath)
except OSError as err:
print(f'Failed to write benchmark files to zip archive with error {err}')
sys.exit(1)
except RuntimeError as zip_err:
print(f'Runtime Error in zipfile module {zip_err}')
sys.exit(1)
return zipfile_name
def upload_to_s3(upload_script_path, base_dir, path_regex, bucket, key_prefix):
'''
Uploads files which located within the base directory using the upload_to_s3.py script
Args:
base_dir: The directory to pass as the --base-dir value to the upload_to_s3.py script
path_regex: The regular expression to pass to the upload_to_s3.py script --file-regex parameter
bucket: The s3 bucket to use for the --bucket argument for upload_to_s3.py
key_prefix: The prefix to store the uploaded files to within the s3 bucket,
It is passed --key-prefix argument to upload_to_s3.py
'''
try:
subprocess.run(['python', upload_script_path, '--base_dir',
base_dir, '--file_regex', path_regex,
'--bucket', bucket, '--key_prefix', key_prefix],
check=True)
except subprocess.CalledProcessError as err:
print(f'{upload_script_path} failed with error {err}')
sys.exit(1)
def upload_benchmarks(args):
'''
Main function responsible for determine which files to add to the output zip file and uploading
the results to s3
Args:
args: Parse argument list of python command line parameters using the argparse module
'''
files_to_archive = get_files_to_archive(args.base_dir, args.file_regex)
archive_zip_path = create_archive_file(args.output_prefix, files_to_archive, args.base_dir)
# Create Sha256sum hash file of zip
create_sha256sums_file(archive_zip_path)
upload_dir = str(pathlib.Path(archive_zip_path).parent)
upload_regex = fr'{pathlib.Path(archive_zip_path).name}.*'
upload_to_s3(args.upload_to_s3_script_path, upload_dir, upload_regex, args.bucket, args.key_prefix)
def parse_args():
cur_dir = os.path.dirname(os.path.abspath(__file__))
parser = argparse.ArgumentParser()
parser.add_argument("--base_dir", default=os.getcwd(), help="Base directory to files which should be archived, If not given, then current directory is used.")
parser.add_argument("--upload-to-s3-script-path", default=os.path.join(cur_dir, 'upload_to_s3.py'), help="Path to upload_to_s3.py script. Script is used for uploading benchmarks to s3")
parser.add_argument("--file_regex", default=r'.*BenchmarkResults/.+\.json', help="Regular expression that used to match file names to archive.")
parser.add_argument("-o", "--output-prefix", default='benchmarks_results', help="Prefix to use to construct the name of the zip file where the benchmark results are zipped."
" A timestamp will be added to the end of filename")
parser.add_argument("--bucket", dest="bucket", default='ly-jenkins-cmake-benchmarks', help="S3 bucket the files are uploaded to.")
parser.add_argument("-k", "--key_prefix", default='user_build', dest="key_prefix", help="Object key prefix.")
args = parser.parse_args()
return args
if __name__ == '__main__':
args = parse_args();
upload_benchmarks(args)
@@ -0,0 +1,183 @@
########################################################################################
# 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.
#
#
# Original file Copyright Crytek GMBH or its affiliates, used under license.
#
########################################################################################
import ast
import boto3
from botocore.exceptions import ClientError
from datetime import datetime
import logging
import os
import shutil
import sys
import time
import traceback
import urllib2
import uuid
KINESIS_STREAM_NAME = 'lumberyard-metrics-stream'
KINESIS_MAX_RECORD_SIZE = 1048576 # 1 MB
S3_BACKUP_BUCKET = 'infrastructure-build-metrics-backup'
IAM_ROLE_NAME = 'ec2-jenkins-node'
LOG_FILE_NAME = 'kinesis_upload.log'
MAX_RECORD_SIZE = KINESIS_MAX_RECORD_SIZE - 4 # to account for version header
MAX_RETRIES = 5
RETRY_EXCEPTIONS = ('ProvisionedThroughputExceededException',
'ThrottlingException')
# truncate the log file, eventually we need to send the logs to cloudwatch logs
with open(LOG_FILE_NAME, 'w'):
pass
logger = logging.getLogger('KinesisUploader')
fileHdlr = logging.FileHandler(LOG_FILE_NAME)
# uncomment this line and the two below to have logs go to stdout for debugging purposes
#streamHdlr = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
fileHdlr.setFormatter(formatter)
#streamHdlr.setFormatter(formatter)
logger.addHandler(fileHdlr)
#logger.addHandler(streamHdlr)
logger.setLevel(logging.DEBUG)
def backup_file_to_s3(s3_client, bucket_name, file_location, s3_file_name):
try:
s3_client.meta.client.upload_file(file_location, bucket_name, s3_file_name)
os.remove(file_location)
except:
logger.error('Failed to upload backup file to S3. This is non-fatal!')
# logger.error(traceback.print_exc())
def get_iam_role_credentials(role_name):
security_metadata = None
try:
response = urllib2.urlopen(
'http://169.254.169.254/latest/meta-data/iam/security-credentials/{0}'.format(role_name)).read()
security_metadata = ast.literal_eval(response)
except:
logger.error('Unable to get iam role credentials')
logger.error(traceback.print_exc())
return security_metadata
def splitFileByRecord(stream, maxSize):
version = 1
# currently using random GUID for partition key, but in the future we may want to partition by some build id
# or by build host
partition_key = uuid.uuid4()
entry_size = 0
put_entries = []
current_entry = ''
for line in stream:
line_size_in_bytes = len(line.encode('utf-8'))
entry_size = entry_size + line_size_in_bytes
if (entry_size > MAX_RECORD_SIZE):
put_entries.append({
'Data': str(version) + '\n' + str(current_entry),
'PartitionKey': str(partition_key)
})
current_entry = line
entry_size = line_size_in_bytes
else:
current_entry = current_entry + line
if current_entry:
put_entries.append({
'Data': str(version) + '\n' + str(current_entry),
'PartitionKey': str(partition_key)
})
return put_entries
def main():
credentials = get_iam_role_credentials(IAM_ROLE_NAME)
aws_access_key_id = None
aws_secret_access_key = None
aws_session_token = None
if credentials is not None:
keys = ['AccessKeyId', 'SecretAccessKey', 'Token']
for key in keys:
if key not in credentials:
logger.error('Unable to find {0} in get_iam_role_credentials response {1}'.format(key, credentials))
return
aws_access_key_id = credentials['AccessKeyId']
aws_secret_access_key = credentials['SecretAccessKey']
aws_session_token = credentials['Token']
kinesis_client = boto3.client('kinesis', region_name='us-west-2', aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key, aws_session_token=aws_session_token)
s3_client = boto3.resource('s3', aws_access_key_id=aws_access_key_id, aws_secret_access_key=aws_secret_access_key,
aws_session_token=aws_session_token)
file_location = sys.argv[1]
filename = os.path.basename(file_location)
backup_file_location = file_location + '.bak'
try:
if os.path.isfile(backup_file_location):
logger.info('Found pre-existing backup file. Uploading to S3.')
backup_file_to_s3(s3_client, S3_BACKUP_BUCKET, backup_file_location,
'{0}.{1}'.format(filename, datetime.now().isoformat()))
if os.path.isfile(file_location):
shutil.copyfile(file_location, backup_file_location)
backup_file_to_s3(s3_client, S3_BACKUP_BUCKET, backup_file_location,
'{0}.{1}'.format(filename, datetime.now().isoformat()))
logger.info('Opening metrics file {0}'.format(file_location))
with open(file_location, 'r+') as f:
records = splitFileByRecord(f, MAX_RECORD_SIZE)
i = 0
retries = 0
while i < len(records):
record = records[i]
try:
logger.info('Uploading {0} bytes of metrics to Kinesis...'.format(len(record)))
kinesis_client.put_record(StreamName=KINESIS_STREAM_NAME,
Data=record['Data'],
PartitionKey=record['PartitionKey'])
retries = 0
except ClientError as ex:
if ex.response['Error']['Code'] not in RETRY_EXCEPTIONS:
raise
sleep_time = 2 ** retries
logger.warn('Request throttled by Kinesis, '
'sleeping and retrying in {0} seconds'.format(2 ** retries))
time.sleep(sleep_time)
retries += 1
i -= 1
i += 1
f.truncate(0)
except:
logger.error(traceback.print_exc())
if __name__ == '__main__':
main()
@@ -0,0 +1,107 @@
#
# 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.
#
'''
Usage:
Use EC2 role to upload all .zip and .MD5 files in %WORKSPACE% folder to bucket ly-packages-mainline:
python upload_to_s3.py --base_dir %WORKSPACE% --file_regex "(.*zip$|.*MD5$)" --bucket ly-packages-mainline
Use profile to upload all .zip and .MD5 files in %WORKSPACE% folder to bucket ly-packages-mainline:
python upload_to_s3.py --base_dir %WORKSPACE% --profile profile --file_regex "(.*zip$|.*MD5$)" --bucket ly-packages-mainline
'''
import os
import re
import json
import boto3
from optparse import OptionParser
from util import error
def parse_args():
parser = OptionParser()
parser.add_option("--base_dir", dest="base_dir", default=os.getcwd(), help="Base directory to upload files, If not given, then current directory is used.")
parser.add_option("--file_regex", dest="file_regex", default=None, help="Regular expression that used to match file names to upload.")
parser.add_option("--profile", dest="profile", default=None, help="The name of a profile to use. If not given, then the default profile is used.")
parser.add_option("--bucket", dest="bucket", default=None, help="S3 bucket the files are uploaded to.")
parser.add_option("--key_prefix", dest="key_prefix", default='', help="Object key prefix.")
'''
ExtraArgs used to call s3.upload_file(), should be in json format. extra_args key must be one of: ACL, CacheControl, ContentDisposition, ContentEncoding, ContentLanguage, ContentType, Expires,
GrantFullControl, GrantRead, GrantReadACP, GrantWriteACP, Metadata, RequestPayer, ServerSideEncryption, StorageClass,
SSECustomerAlgorithm, SSECustomerKey, SSECustomerKeyMD5, SSEKMSKeyId, WebsiteRedirectLocation
'''
parser.add_option("--extra_args", dest="extra_args", default=None, help="Additional parameters used to upload file.")
parser.add_option("--max_retry", dest="max_retry", default=1, help="Maximum retry times to upload file.")
(options, args) = parser.parse_args()
if not os.path.isdir(options.base_dir):
error('{} is not a valid directory'.format(options.base_dir))
if not options.file_regex:
error('Use --file_regex to specify regular expression that used to match file names to upload.')
if not options.bucket:
error('Use --bucket to specify bucket that the files are uploaded to.')
return options
def get_client(service_name, profile_name):
session = boto3.session.Session(profile_name=profile_name)
client = session.client(service_name)
return client
def get_files_to_upload(base_dir, regex):
# Get all file names in base directory
files = [x for x in os.listdir(base_dir) if os.path.isfile(os.path.join(base_dir, x))]
# Get all file names matching the regular expression, those file will be uploaded to S3
files_to_upload = [x for x in files if re.match(regex, x)]
return files_to_upload
def s3_upload_file(client, base_dir, file, bucket, key_prefix=None, extra_args=None, max_retry=1):
print('Uploading file {} to bucket {}.'.format(file, bucket))
key = file if key_prefix is None else '{}/{}'.format(key_prefix, file)
for x in range(max_retry):
try:
client.upload_file(
os.path.join(base_dir, file), bucket, key,
ExtraArgs=extra_args
)
print('Upload succeeded')
return True
except Exception as err:
print('exception while uploading: {}'.format(err))
print('Retrying upload...')
print('Upload failed')
return False
if __name__ == "__main__":
options = parse_args()
client = get_client('s3', options.profile)
files_to_upload = get_files_to_upload(options.base_dir, options.file_regex)
extra_args = json.loads(options.extra_args) if options.extra_args else None
print('Uploading {} files to bucket {}.'.format(len(files_to_upload), options.bucket))
failure = []
success = []
for file in files_to_upload:
if not s3_upload_file(client, options.base_dir, file, options.bucket, options.key_prefix, extra_args, 2):
failure.append(file)
else:
success.append(file)
print('Upload finished.')
print('{} files are uploaded successfully:'.format(len(success)))
print('\n'.join(success))
if len(failure) > 0:
print('{} files failed to upload:'.format(len(failure)))
print('\n'.join(failure))
# Exit with error code 1 if any file is failed to upload
exit(1)
@@ -0,0 +1,65 @@
"""
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 json
import os
import re
import subprocess
class LyBuildError(Exception):
def __init__(self, message):
super(LyBuildError, self).__init__(message)
def __str__(self):
return str(self.message)
def ly_build_error(message):
raise LyBuildError(message)
def error(message):
print('Error: {}'.format(message))
exit(1)
# Exit with status code 0 means it won't fail the whole build process
def safe_exit_with_error(message):
print('Error: {}'.format(message))
exit(0)
def warn(message):
print('Warning: {}'.format(message))
def execute_system_call(command, **kwargs):
print('Executing subprocess.check_call({})'.format(command))
try:
subprocess.check_call(command, **kwargs)
except subprocess.CalledProcessError as e:
print(e.output)
error('Executing subprocess.check_call({}) failed with error {}'.format(command, e))
except FileNotFoundError as e:
error("File Not Found - Failed to call {} with error {}".format(command, e))
def safe_execute_system_call(command, **kwargs):
print('Executing subprocess.check_call({})'.format(command))
try:
subprocess.check_call(command, **kwargs)
except subprocess.CalledProcessError as e:
print(e.output)
warn('Executing subprocess.check_call({}) failed'.format(command))
return e.returncode
return 0