Merge branch 'TIF/Runtime' into TIF/Jenkins

This commit is contained in:
jonawals
2021-06-03 12:45:13 +01:00
4925 changed files with 124342 additions and 303053 deletions
+1
View File
@@ -11,5 +11,6 @@
add_subdirectory(detect_file_changes)
add_subdirectory(commit_validation)
add_subdirectory(o3de)
add_subdirectory(project_manager)
add_subdirectory(ctest)
+45 -15
View File
@@ -60,7 +60,7 @@ def palRm(path) {
} else {
def win_path = path.replace('/','\\')
bat label: "Removing ${win_path}",
script: "del ${win_path}"
script: "del /Q ${win_path}"
}
}
@@ -190,12 +190,14 @@ def CheckoutBootstrapScripts(String branchName) {
doGenerateSubmoduleConfigurations: false,
extensions: [
[$class: 'PruneStaleBranch'],
[$class: 'AuthorInChangelog'],
[$class: 'SparseCheckoutPaths', sparseCheckoutPaths: [
[ $class: 'SparseCheckoutPath', path: 'scripts/build/Jenkins/' ],
[ $class: 'SparseCheckoutPath', path: 'scripts/build/bootstrap/' ],
[ $class: 'SparseCheckoutPath', path: 'scripts/build/Platform' ]
]],
[$class: 'CloneOption', depth: 1, noTags: false, reference: '', shallow: true]
// Shallow checkouts break changelog computation. Do not enable.
[$class: 'CloneOption', noTags: false, reference: '', shallow: false]
],
submoduleCfg: [],
userRemoteConfigs: scm.userRemoteConfigs
@@ -234,6 +236,7 @@ def CheckoutRepo(boolean disableSubmodules = false) {
branches: scm.branches,
extensions: [
[$class: 'PruneStaleBranch'],
[$class: 'AuthorInChangelog'],
[$class: 'SubmoduleOption', disableSubmodules: disableSubmodules, recursiveSubmodules: true],
[$class: 'CheckoutOption', timeout: 60]
],
@@ -339,14 +342,17 @@ def TestMetrics(Map pipelineConfig, String workspace, String branchName, String
checkout scm: [
$class: 'GitSCM',
branches: [[name: '*/main']],
extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'mars']],
extensions: [
[$class: 'AuthorInChangelog'],
[$class: 'RelativeTargetDirectory', relativeTargetDir: 'mars']
],
userRemoteConfigs: [[url: "${env.MARS_REPO}", name: 'mars', credentialsId: "${env.GITHUB_USER}"]]
]
withCredentials([usernamePassword(credentialsId: "${env.SERVICE_USER}", passwordVariable: 'apitoken', usernameVariable: 'username')]) {
def command = "${pipelineConfig.PYTHON_DIR}/python.cmd -u mars/scripts/python/ctest_test_metric_scraper.py " +
'-e jenkins.creds.user %username% -e jenkins.creds.pass %apitoken% ' +
"-e jenkins.base_url ${env.JENKINS_URL} " +
"${cmakeBuildDir} ${branchName} %BUILD_NUMBER% AR ${configuration} ${repoName} "
"${cmakeBuildDir} ${branchName} %BUILD_NUMBER% AR ${configuration} ${repoName} --url ${env.BUILD_URL}"
bat label: "Publishing ${buildJobName} Test Metrics",
script: command
}
@@ -354,6 +360,16 @@ def TestMetrics(Map pipelineConfig, String workspace, String branchName, String
}
}
def ExportTestResults(Map options, String platform, String type, String workspace, Map params) {
catchError(message: "Error exporting tests results (this won't fail the build)", buildResult: 'SUCCESS', stageResult: 'FAILURE') {
def o3deroot = "${workspace}/${ENGINE_REPOSITORY_NAME}"
dir("${o3deroot}/${params.OUTPUT_DIRECTORY}") {
junit testResults: "Testing/**/*.xml"
palRmDir("Testing")
}
}
}
def PostBuildCommonSteps(String workspace, boolean mount = true) {
echo 'Starting post-build common steps...'
@@ -396,6 +412,14 @@ def CreateTestMetricsStage(Map pipelineConfig, String branchName, Map environmen
}
}
def CreateExportTestResultsStage(Map pipelineConfig, String platformName, String jobName, Map environmentVars, Map params) {
return {
stage("${jobName}_results") {
ExportTestResults(pipelineConfig, platformName, jobName, environmentVars['WORKSPACE'], params)
}
}
}
def CreateTeardownStage(Map environmentVars) {
return {
stage('Teardown') {
@@ -511,11 +535,15 @@ try {
}
}
finally {
if (env.MARS_REPO && platform.value.build_types[build_job_name].PARAMETERS.containsKey('TEST_METRICS') && platform.value.build_types[build_job_name].PARAMETERS.TEST_METRICS == 'True') {
def output_directory = platform.value.build_types[build_job_name].PARAMETERS.OUTPUT_DIRECTORY
def configuration = platform.value.build_types[build_job_name].PARAMETERS.CONFIGURATION
def params = platform.value.build_types[build_job_name].PARAMETERS
if (env.MARS_REPO && params && params.containsKey('TEST_METRICS') && params.TEST_METRICS == 'True') {
def output_directory = params.OUTPUT_DIRECTORY
def configuration = params.CONFIGURATION
CreateTestMetricsStage(pipelineConfig, branchName, envVars, build_job_name, output_directory, configuration).call()
}
if (params && params.containsKey('TEST_RESULTS') && params.TEST_RESULTS == 'True') {
CreateExportTestResultsStage(pipelineConfig, platform.key, build_job_name, envVars, params).call()
}
CreateTeardownStage(envVars).call()
}
}
@@ -549,15 +577,17 @@ finally {
message:"${currentBuild.currentResult}:${BUILD_URL}:${env.RECREATE_VOLUME}:${env.CLEAN_OUTPUT_DIRECTORY}:${env.CLEAN_ASSETS}"
)
}
step([
$class: 'Mailer',
notifyEveryUnstableBuild: true,
sendToIndividuals: true,
recipients: emailextrecipients([
[$class: 'CulpritsRecipientProvider'],
[$class: 'RequesterRecipientProvider']
node('controller') {
emailRecipients = [[$class: 'RequesterRecipientProvider']]
if (env.WATCHED_BRANCHES.tokenize(',').contains(branchName)) {
emailRecipients.add([$class: 'CulpritsRecipientProvider'])
}
step([
$class: 'Mailer',
notifyEveryUnstableBuild: true,
recipients: emailextrecipients(emailRecipients)
])
])
}
} catch(Exception e) {
}
}
@@ -27,7 +27,8 @@
},
"debug": {
"TAGS":[
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND":"../Windows/build_ninja_windows.cmd",
@@ -67,7 +68,8 @@
},
"profile_nounity": {
"TAGS":[
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND":"../Windows/build_ninja_windows.cmd",
@@ -83,7 +85,9 @@
"asset_profile": {
"TAGS":[
"default",
"weekly-build-metrics"
"weekly-build-metrics",
"nightly-incremental",
"nightly-clean"
],
"COMMAND":"../Windows/build_asset_windows.cmd",
"PARAMETERS": {
@@ -95,24 +99,13 @@
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo",
"ASSET_PROCESSOR_BINARY": "bin\\profile\\AssetProcessorBatch.exe",
"ASSET_PROCESSOR_OPTIONS": "/zeroAnalysisMode --regset=\"/Amazon/AssetProcessor/Settings/Exclude Android/pattern=.*/DiffuseGlobalIllumination/.*precompiledshader\"",
"ASSET_PROCESSOR_PLATFORMS":"es3"
"ASSET_PROCESSOR_PLATFORMS":"android"
}
},
"asset_clean_profile": {
"TAGS":[
"nightly"
],
"PIPELINE_ENV": {
"CLEAN_ASSETS": "1"
},
"steps": [
"clean",
"asset_profile"
]
},
"release": {
"TAGS":[
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND":"../Windows/build_ninja_windows.cmd",
@@ -127,7 +120,8 @@
},
"monolithic_release": {
"TAGS":[
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND":"../Windows/build_ninja_windows.cmd",
@@ -142,15 +136,14 @@
},
"gradle": {
"TAGS":[
"default",
"weekly-build-metrics"
],
"COMMAND":"gradle_windows.cmd",
"PARAMETERS": {
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\android_gradle",
"OUTPUT_DIRECTORY":"build\\ad_grd",
"GAME_PROJECT": "AutomatedTesting",
"ANDROID_NDK_PLATFORM": "21",
"ANDROID_SDK_PLATFORM": "29",
"SIGN_APK": "false",
"GRADLE_BUILD_CMD": "build",
"ADDITIONAL_GENERATE_ARGS": ""
@@ -164,8 +157,6 @@
"CONFIGURATION":"profile",
"OUTPUT_DIRECTORY":"build\\android_unittest",
"GAME_PROJECT": "AutomatedTesting",
"ANDROID_NDK_PLATFORM": "21",
"ANDROID_SDK_PLATFORM": "29",
"SIGN_APK": "true",
"GRADLE_BUILD_CMD": "assemble",
"ADDITIONAL_GENERATE_ARGS": "--unit-test"
@@ -17,20 +17,12 @@ IF NOT EXIST "%LY_3RDPARTY_PATH%" (
GOTO :error
)
IF NOT EXIST "%GRADLE_HOME%" (
IF NOT EXIST "%GRADLE_BUILD_HOME%" (
REM This is the default for developers
SET GRADLE_HOME=C:\Gradle\gradle-5.6.4
SET GRADLE_BUILD_HOME=C:\Gradle\gradle-7.0
)
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%
IF NOT EXIST "%GRADLE_BUILD_HOME%" (
ECHO [ci_build] FAIL: GRADLE_BUILD_HOME=%GRADLE_BUILD_HOME%
GOTO :error
)
@@ -50,20 +42,9 @@ ECHO Ninja wasnt in the call path, add the value set by LY_NINJA_PATH
SET PATH=%PATH%;%LY_NINJA_PATH%
:ninja_on_path
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
IF NOT "%ANDROID_GRADLE_PLUGIN%" == "" (
set ANDROID_GRADLE_PLUGIN_OPTION=--gradle-plugin-version=%ANDROID_GRADLE_PLUGIN%
)
IF NOT EXIST %OUTPUT_DIRECTORY% (
@@ -154,11 +135,11 @@ IF "%GENERATE_SIGNED_APK%"=="true" (
ECHO Using keystore file at %CI_ANDROID_KEYSTORE_FILE_ABS%
)
ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-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% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %OPTIONAL_TEST_FLAG% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-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% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_BUILD_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-sdk-path=%ANDROID_HOME% %ANDROID_GRADLE_PLUGIN_OPTION% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_BUILD_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% --android-sdk-path=%ANDROID_HOME% %ANDROID_GRADLE_PLUGIN_OPTION% --signconfig-store-file %CI_ANDROID_KEYSTORE_FILE_ABS% --signconfig-store-password %CI_ANDROID_KEYSTORE_PASSWORD% --signconfig-key-alias %CI_ANDROID_KEYSTORE_ALIAS% --signconfig-key-password %CI_ANDROID_KEYSTORE_PASSWORD% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
) ELSE (
ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-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% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-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% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
ECHO [ci_build] %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% %GRADLE_OVERRIDE_OPTION% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% %ANDROID_GRADLE_PLUGIN_OPTION% --android-sdk-path=%ANDROID_HOME% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
CALL %PYTHON% cmake\Tools\Platform\Android\generate_android_project.py --engine-root=. --build-dir=%OUTPUT_DIRECTORY% -g %GAME_PROJECT% --gradle-install-path=%GRADLE_BUILD_HOME% --ninja-install-path=%LY_NINJA_PATH% --third-party-path=%LY_3RDPARTY_PATH% %ANDROID_GRADLE_PLUGIN_OPTION% --android-sdk-path=%ANDROID_HOME% %ADDITIONAL_GENERATE_ARGS% --overwrite-existing
)
REM Validate the android project generation
+5 -2
View File
@@ -1,7 +1,7 @@
{
"ENV": {
"GRADLE_HOME": "C:/Gradle/gradle-5.6.4",
"NODE_LABEL": "windows-047e5cdf",
"GRADLE_HOME": "C:/Gradle/gradle-7.0",
"NODE_LABEL": "windows-b3c8994f1",
"LY_3RDPARTY_PATH": "C:/ly/3rdParty",
"TIMEOUT": 30,
"WORKSPACE": "D:/workspace",
@@ -13,6 +13,9 @@
},
"packaging": {
"CLEAN_WORKSPACE": true
},
"nightly-clean": {
"CLEAN_WORKSPACE": true
}
}
}
+40 -25
View File
@@ -29,7 +29,8 @@
},
"debug": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_linux.sh",
@@ -43,9 +44,10 @@
},
"profile": {
"TAGS": [
"nightly",
"daily-pipeline-metrics",
"weekly-build-metrics"
"nightly-incremental",
"nightly-clean",
"daily-pipeline-metrics",
"weekly-build-metrics"
],
"COMMAND": "build_linux.sh",
"PARAMETERS": {
@@ -81,7 +83,7 @@
"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": "AutomatedTesting",
"CMAKE_TARGET": "all",
"CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -L FRAMEWORK_googletest"
"CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -LE SUITE_sandbox -L FRAMEWORK_googletest"
}
},
"test_profile_nounity": {
@@ -93,12 +95,14 @@
"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": "AutomatedTesting",
"CMAKE_TARGET": "all",
"CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -L FRAMEWORK_googletest"
"CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -LE SUITE_sandbox -L FRAMEWORK_googletest"
}
},
"asset_profile": {
"TAGS": [
"weekly-build-metrics"
"weekly-build-metrics",
"nightly-incremental",
"nightly-clean"
],
"COMMAND": "build_asset_linux.sh",
"PARAMETERS": {
@@ -126,21 +130,10 @@
"ASSET_PROCESSOR_PLATFORMS": "pc,server"
}
},
"asset_clean_profile": {
"TAGS": [
"nightly"
],
"PIPELINE_ENV": {
"CLEAN_ASSETS": "1"
},
"steps": [
"clean",
"asset_profile"
]
},
"periodic_test_profile": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_test_linux.sh",
@@ -150,12 +143,32 @@
"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": "AutomatedTesting",
"CMAKE_TARGET": "TEST_SUITE_periodic",
"CTEST_OPTIONS": "-L \"(SUITE_periodic)\""
"CTEST_OPTIONS": "-L (SUITE_periodic)"
}
},
"sandbox_test_profile": {
"TAGS": [
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"PIPELINE_ENV": {
"ON_FAILURE_MARK": "UNSTABLE"
},
"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 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE",
"CMAKE_LY_PROJECTS": "AutomatedTesting",
"CMAKE_TARGET": "all",
"CTEST_OPTIONS": "-L (SUITE_sandbox)"
}
},
"benchmark_test_profile": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_test_linux.sh",
@@ -165,12 +178,13 @@
"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": "AutomatedTesting",
"CMAKE_TARGET": "TEST_SUITE_benchmark",
"CTEST_OPTIONS": "-L \"(SUITE_benchmark)\""
"CTEST_OPTIONS": "-L (SUITE_benchmark)"
}
},
"release": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_linux.sh",
@@ -184,7 +198,8 @@
},
"monolithic_release": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_linux.sh",
@@ -12,6 +12,9 @@
},
"packaging": {
"CLEAN_WORKSPACE": true
},
"nightly-clean": {
"CLEAN_WORKSPACE": true
}
}
}
+18 -21
View File
@@ -9,7 +9,8 @@
},
"profile_pipe": {
"TAGS": [
"nightly"
"nightly-incremental",
"nightly-clean"
],
"steps": [
"profile",
@@ -28,7 +29,8 @@
},
"debug": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_mac.sh",
@@ -56,7 +58,8 @@
},
"profile_nounity": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_mac.sh",
@@ -70,7 +73,9 @@
},
"asset_profile": {
"TAGS": [
"weekly-build-metrics"
"weekly-build-metrics",
"nightly-incremental",
"nightly-clean"
],
"COMMAND": "build_asset_mac.sh",
"PARAMETERS": {
@@ -81,24 +86,13 @@
"CMAKE_TARGET": "AssetProcessorBatch",
"ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch",
"ASSET_PROCESSOR_OPTIONS": "/zeroAnalysisMode",
"ASSET_PROCESSOR_PLATFORMS": "osx_gl"
"ASSET_PROCESSOR_PLATFORMS": "mac"
}
},
"asset_clean_profile": {
"TAGS": [
"nightly"
],
"PIPELINE_ENV": {
"CLEAN_ASSETS": "1"
},
"steps": [
"clean",
"asset_profile"
]
},
"periodic_test_profile": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_test_mac.sh",
@@ -113,7 +107,8 @@
},
"benchmark_test_profile": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_test_mac.sh",
@@ -128,7 +123,8 @@
},
"release": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_mac.sh",
@@ -142,7 +138,8 @@
},
"monolithic_release": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_mac.sh",
+4 -1
View File
@@ -1,6 +1,6 @@
{
"ENV": {
"NODE_LABEL": "mac",
"NODE_LABEL": "mac-catalina-7ad2e45b",
"LY_3RDPARTY_PATH": "/Users/lybuilder/3rdParty",
"TIMEOUT": 30,
"WORKSPACE": "/Users/lybuilder/workspace",
@@ -12,6 +12,9 @@
},
"packaging": {
"CLEAN_WORKSPACE": true
},
"nightly-clean": {
"CLEAN_WORKSPACE": true
}
}
}
@@ -17,7 +17,8 @@
},
"debug_vs2019_pipe": {
"TAGS": [
"nightly"
"nightly-incremental",
"nightly-clean"
],
"steps": [
"debug_vs2019",
@@ -125,7 +126,8 @@
"CMAKE_TARGET": "TEST_SUITE_smoke TEST_SUITE_main",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo",
"CTEST_OPTIONS": "-L \"(SUITE_smoke|SUITE_main)\" -LE \"(REQUIRES_gpu)\" -T Test",
"TEST_METRICS": "True"
"TEST_METRICS": "True",
"TEST_RESULTS": "True"
}
},
"profile_vs2019": {
@@ -145,7 +147,8 @@
},
"profile_vs2019_nounity": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_windows.cmd",
@@ -172,15 +175,17 @@
"CMAKE_TARGET": "TEST_SUITE_smoke TEST_SUITE_main",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo",
"CTEST_OPTIONS": "-L \"(SUITE_smoke|SUITE_main)\" -LE \"(REQUIRES_gpu)\" -T Test",
"TEST_METRICS": "True"
"TEST_METRICS": "True",
"TEST_RESULTS": "True"
}
},
"test_gpu_profile_vs2019": {
"TAGS":[
"nightly"
"nightly-incremental",
"nightly-clean"
],
"PIPELINE_ENV":{
"NODE_LABEL":"windows-gpu"
"NODE_LABEL":"windows-gpu"
},
"COMMAND": "build_test_windows.cmd",
"PARAMETERS": {
@@ -191,12 +196,15 @@
"CMAKE_TARGET": "TEST_SUITE_smoke TEST_SUITE_main",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo",
"CTEST_OPTIONS": "-L \"(SUITE_smoke_REQUIRES_gpu|SUITE_main_REQUIRES_gpu)\" -T Test",
"TEST_METRICS": "True"
"TEST_METRICS": "True",
"TEST_RESULTS": "True"
}
},
"asset_profile_vs2019": {
"TAGS": [
"weekly-build-metrics"
"weekly-build-metrics",
"nightly-incremental",
"nightly-clean"
],
"COMMAND": "build_asset_windows.cmd",
"PARAMETERS": {
@@ -211,21 +219,10 @@
"ASSET_PROCESSOR_PLATFORMS": "pc,server"
}
},
"asset_clean_profile_vs2019": {
"TAGS": [
"nightly"
],
"PIPELINE_ENV": {
"CLEAN_ASSETS": "1"
},
"steps": [
"clean",
"asset_profile_vs2019"
]
},
"periodic_test_profile_vs2019": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_test_windows.cmd",
@@ -237,12 +234,14 @@
"CMAKE_TARGET": "TEST_SUITE_periodic",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo",
"CTEST_OPTIONS": "-L \"(SUITE_periodic)\" -T Test",
"TEST_METRICS": "True"
"TEST_METRICS": "True",
"TEST_RESULTS": "True"
}
},
"sandbox_test_profile_vs2019": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"PIPELINE_ENV": {
@@ -257,12 +256,14 @@
"CMAKE_TARGET": "TEST_SUITE_sandbox",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo",
"CTEST_OPTIONS": "-L \"(SUITE_sandbox)\" -T Test",
"TEST_METRICS": "True"
"TEST_METRICS": "True",
"TEST_RESULTS": "True"
}
},
"benchmark_test_profile_vs2019": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_test_windows.cmd",
@@ -274,12 +275,14 @@
"CMAKE_TARGET": "TEST_SUITE_benchmark",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo",
"CTEST_OPTIONS": "-L \"(SUITE_benchmark)\" -T Test",
"TEST_METRICS": "True"
"TEST_METRICS": "True",
"TEST_RESULTS": "True"
}
},
"release_vs2019": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_windows.cmd",
@@ -294,7 +297,8 @@
},
"monolithic_release_vs2019": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "build_windows.cmd",
@@ -309,13 +313,14 @@
},
"install_profile_vs2019": {
"TAGS": [
"nightly"
"nightly-incremental",
"nightly-clean"
],
"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 -DLY_DISABLE_TEST_MODULES=TRUE -DCMAKE_INSTALL_PREFIX=install",
"CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_DISABLE_TEST_MODULES=TRUE",
"CMAKE_LY_PROJECTS": "",
"CMAKE_TARGET": "INSTALL",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo"
@@ -347,7 +352,7 @@
"PARAMETERS": {
"CONFIGURATION": "profile",
"OUTPUT_DIRECTORY": "build\\windows_vs2019",
"CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DCMAKE_MODULE_PATH=!WORKSPACE!/o3de/build/windows_vs2019/install/cmake",
"CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DCMAKE_MODULE_PATH=!WORKSPACE!/o3de/install/cmake",
"CMAKE_LY_PROJECTS": "",
"CMAKE_TARGET": "ALL_BUILD",
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo"
+4 -1
View File
@@ -1,6 +1,6 @@
{
"ENV": {
"NODE_LABEL": "windows-047e5cdf",
"NODE_LABEL": "windows-b3c8994f1",
"LY_3RDPARTY_PATH": "C:/ly/3rdParty",
"TIMEOUT": 30,
"WORKSPACE": "D:/workspace",
@@ -12,6 +12,9 @@
},
"packaging": {
"CLEAN_WORKSPACE": true
},
"nightly-clean": {
"CLEAN_WORKSPACE": true
}
}
}
+11 -18
View File
@@ -19,7 +19,8 @@
},
"debug": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "../Mac/build_mac.sh",
@@ -34,7 +35,8 @@
},
"profile": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"daily-pipeline-metrics",
"weekly-build-metrics"
],
@@ -50,7 +52,8 @@
},
"profile_nounity": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "../Mac/build_mac.sh",
@@ -65,14 +68,15 @@
},
"asset_profile": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "../Mac/build_asset_mac.sh",
"PARAMETERS": {
"CONFIGURATION": "profile",
"OUTPUT_DIRECTORY": "build/mac",
"CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE",
"CMAKE_OPTIONS": "-G Xcode -DLY_UNITY_BUILD=TRUE -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE",
"CMAKE_LY_PROJECTS": "AutomatedTesting",
"CMAKE_TARGET": "AssetProcessorBatch",
"ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch",
@@ -80,21 +84,10 @@
"ASSET_PROCESSOR_PLATFORMS": "ios"
}
},
"asset_clean_profile": {
"TAGS": [
"nightly"
],
"PIPELINE_ENV": {
"CLEAN_ASSETS": "true"
},
"steps": [
"clean",
"asset_profile"
]
},
"release": {
"TAGS": [
"nightly",
"nightly-incremental",
"nightly-clean",
"weekly-build-metrics"
],
"COMMAND": "../Mac/build_mac.sh",
+4 -1
View File
@@ -1,6 +1,6 @@
{
"ENV": {
"NODE_LABEL": "mac",
"NODE_LABEL": "mac-catalina-7ad2e45b",
"LY_3RDPARTY_PATH": "/Users/lybuilder/3rdParty",
"TIMEOUT": 30,
"WORKSPACE": "/Users/lybuilder/workspace",
@@ -12,6 +12,9 @@
},
"packaging": {
"CLEAN_WORKSPACE": true
},
"nightly-clean": {
"CLEAN_WORKSPACE": true
}
}
}
@@ -38,7 +38,7 @@ then
./aws/install
rm -rf ./aws
else
AWS_CLI_VERSION=`aws --version | awk '{print $1}' | awk -F/ '{print $2}'`
AWS_CLI_VERSION=$(aws --version | awk '{print $1}' | awk -F/ '{print $2}')
echo AWS CLI \(version $AWS_CLI_VERSION\) already installed
fi
@@ -26,7 +26,7 @@ then
exit 1
fi
UBUNTU_DISTRO="`lsb_release -c | awk '{print $2}'`"
UBUNTU_DISTRO="$(lsb_release -c | awk '{print $2}')"
if [ "$UBUNTU_DISTRO" == "bionic" ]
then
echo "Setup for Ubuntu 18.04 LTS ($UBUNTU_DISTRO)"
@@ -53,7 +53,7 @@ fi
# will install it from the bionic distro manually into focal. This is needed since Ubuntu 20.04 supports
# python 3.8 out of the box, but we are using 3.7
#
LIBFFI6_COUNT=`apt list --installed 2>/dev/null | grep libffi6 | wc -l`
LIBFFI6_COUNT=$(apt list --installed 2>/dev/null | grep libffi6 | wc -l)
if [ "$UBUNTU_DISTRO" == "focal" ] && [ $LIBFFI6_COUNT -eq 0 ]
then
echo "Installing libffi for Ubuntu 20.04"
@@ -90,7 +90,7 @@ fi
# Add the kitware repository for cmake if necessary
#
KITWARE_REPO_COUNT=`cat /etc/apt/sources.list | grep ^deb | grep https://apt.kitware.com/ubuntu/ | wc -l`
KITWARE_REPO_COUNT=$(cat /etc/apt/sources.list | grep ^deb | grep https://apt.kitware.com/ubuntu/ | wc -l)
if [ $KITWARE_REPO_COUNT -eq 0 ]
then
@@ -121,33 +121,34 @@ PACKAGE_FILE_LIST=package-list.ubuntu-$UBUNTU_DISTRO.txt
echo Reading package list $PACKAGE_FILE_LIST
# Read each line (strip out comment tags)
for LINE in `cat $PACKAGE_FILE_LIST | sed 's/#.*$//g'`
for PREPROC_LINE in $(cat $PACKAGE_FILE_LIST | sed 's/#.*$//g')
do
PACKAGE=`echo $LINE | awk -F / '{print $1}'`
LINE=$(echo $PREPROC_LINE | tr -d '\r\n')
PACKAGE=$(echo $LINE | awk -F / '{$1=$1;print $1}')
if [ "$PACKAGE" != "" ] # Skip blank lines
then
PACKAGE_VER=`echo $LINE | awk -F / '{print $2}'`
PACKAGE_VER=$(echo $LINE | awk -F / '{$2=$2;print $2}')
if [ "$PACKAGE_VER" == "" ]
then
# Process non-versioned packages
INSTALLED_COUNT=`apt list --installed 2>/dev/null | grep ^$PACKAGE/ | wc -l`
INSTALLED_COUNT=$(apt list --installed 2>/dev/null | grep ^$PACKAGE/ | wc -l)
if [ $INSTALLED_COUNT -eq 0 ]
then
echo Installing $PACKAGE
apt-get install $PACKAGE -y
else
INSTALLED_VERSION=`apt list --installed 2>/dev/null | grep ^$PACKAGE/ | awk '{print $2}'`
INSTALLED_VERSION=$(apt list --installed 2>/dev/null | grep ^$PACKAGE/ | awk '{print $2}')
echo $PACKAGE already installed \(version $INSTALLED_VERSION\)
fi
else
# Process versioned packages
INSTALLED_COUNT=`apt list --installed 2>/dev/null | grep ^$PACKAGE/ | wc -l`
INSTALLED_COUNT=$(apt list --installed 2>/dev/null | grep ^$PACKAGE/ | wc -l)
if [ $INSTALLED_COUNT -eq 0 ]
then
echo Installing $PACKAGE \( $PACKAGE_VER \)
apt-get install $PACKAGE=$PACKAGE_VER -y
else
INSTALLED_VERSION=`apt list --installed 2>/dev/null | grep ^$PACKAGE/ | awk '{print $2}'`
INSTALLED_VERSION=$(apt list --installed 2>/dev/null | grep ^$PACKAGE/ | awk '{print $2}')
if [ "$INSTALLED_VERSION" != "$PACKAGE_VER" ]
then
echo $PACKAGE already installed but with the wrong version. Purging the package
@@ -26,7 +26,7 @@ then
exit 1
fi
UBUNTU_DISTRO="`lsb_release -c | awk '{print $2}'`"
UBUNTU_DISTRO="$(lsb_release -c | awk '{print $2}')"
if [ "$UBUNTU_DISTRO" == "bionic" ]
then
echo "Setup for Ubuntu 18.04 LTS ($UBUNTU_DISTRO)"
@@ -49,14 +49,14 @@ then
apt-get update
apt-get install git -y
else
GIT_VERSION=`git --version | awk '{print $3}'`
GIT_VERSION=$(git --version | awk '{print $3}')
echo Git $GIT_VERSION already Installed. Skipping Git installation
fi
#
# Setup Git-LFS if needed
#
GIT_LFS_PACKAGE_COUNT=`apt list --installed 2>/dev/null | grep git-lfs/ | wc -l`
GIT_LFS_PACKAGE_COUNT=$(apt list --installed 2>/dev/null | grep git-lfs/ | wc -l)
if [ $GIT_LFS_PACKAGE_COUNT -eq 0 ]
then
echo Setting up Git-LFS
@@ -87,7 +87,7 @@ then
dpkg -i $GCM_PACKAGE_NAME
popd
else
GCM_VERSION=`git-credential-manager-core --version`
GCM_VERSION=$(git-credential-manager-core --version)
echo Git Credential Manager \(GCM\) version $GCM_VERSION already installed. Skipping GCM installation
fi
@@ -0,0 +1,68 @@
#!/bin/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.
# This script must be run as root
if [[ $EUID -ne 0 ]]
then
echo "This script must be run as root (sudo)"
exit 1
fi
# Install python3 if necessary
python3 --version >/dev/null 2>&1
if [ $? -ne 0 ]
then
echo Installing Python3
apt-get install python3
if [ $? -ne 0 ]
then
echo Error installing python3
exit 1
fi
else
PYTHON_VERSION=$(python3 --version)
echo Python3 already installed \($PYTHON_VERSION\)
fi
# Install python3 pip if necessary
pip3 --version >/dev/null 2>&1
if [ $? -ne 0 ]
then
echo Installing Python3 PIP
apt-get install -y python3-pip
if [ $? -ne 0 ]
then
echo Error installing python3
exit 1
fi
else
PYTHON_VERSION=$(pip3 --version | awk '{print $2}')
echo Python3 Pip already installed \($PYTHON_VERSION\)
fi
# Read from the package list and process each package
PIP_REQUIREMENTS_FILE=requirements.txt
pip3 install -r $PIP_REQUIREMENTS_FILE
if [ $? -ne 0 ]
then
echo Error installing python3
exit 1
fi
echo Python3 setup complete
exit 0
+48
View File
@@ -0,0 +1,48 @@
#!/bin/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.
# This script must be run as root
if [[ $EUID -ne 0 ]]
then
echo "This script must be run as root (sudo)"
exit 1
fi
echo Installing packages and tools for O3DE development
# Install awscli
./install-ubuntu-awscli.sh
if [ $? -ne 0 ]
then
echo Error installing AWSCLI
exit 1
fi
# Install git
./install-ubuntu-git.sh
if [ $? -ne 0 ]
then
echo Error installing Git
exit 1
fi
# Install the necessary build tools
./install-ubuntu-build-tools.sh
if [ $? -ne 0 ]
then
echo Error installing ubuntu tools
exit 1
fi
echo Packages and tools for O3DE setup complete
exit 0
@@ -0,0 +1,43 @@
boto3==1.16.18 \
--hash=sha256:51c419d890ae216b9b031be31f3182739dc3deb5b64351f286bffca2818ddb35 \
--hash=sha256:d70d21ea137d786e84124639a62be42f92f4b09472ebfb761156057c92dc5366
psutil==5.8.0 \
--hash=sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64 \
--hash=sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131 \
--hash=sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c \
--hash=sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6 \
--hash=sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023 \
--hash=sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df \
--hash=sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394 \
--hash=sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4 \
--hash=sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b \
--hash=sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2 \
--hash=sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d \
--hash=sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65 \
--hash=sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d \
--hash=sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef \
--hash=sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7 \
--hash=sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60 \
--hash=sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6 \
--hash=sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8 \
--hash=sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b \
--hash=sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d \
--hash=sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac \
--hash=sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935 \
--hash=sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d \
--hash=sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28 \
--hash=sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876 \
--hash=sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0 \
--hash=sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3 \
--hash=sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563
requests==2.25.1 \
--hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \
--hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e
traceback2==1.4.0 \
--hash=sha256:05acc67a09980c2ecfedd3423f7ae0104839eccb55fc645773e1caa0951c3030 \
--hash=sha256:8253cebec4b19094d67cc5ed5af99bf1dba1285292226e98a31929f87a5d6b23
urllib3==1.26.4 \
--hash=sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df \
--hash=sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937
tempfile2==0.1.1 \
--hash=sha256:77fdd256c16804053d3d588168b79595099ea5e874c3fb171893b0ababd10340
@@ -28,8 +28,8 @@ Start-Process -FilePath $sdkmanager -ArgumentList $build_tools -NoNewWindow -Wai
Write-Host "Installing Gradle and Ninja"
Import-Module C:\ProgramData\chocolatey\helpers\chocolateyInstaller.psm1 #Grade needs a custom installer due to being hardcoded to C:\Programdata in Chocolatey
$packageName = 'gradle'
$version = '5.6.4'
$checksum = 'ABC10BCEDB58806E8654210F96031DB541BCD2D6FC3161E81CB0572D6A15E821'
$version = '7.0'
$checksum = '81003F83B0056D20EEDF48CDDD4F52A9813163D4BA185BCF8ABD34B8EEEA4CBD'
$url = "https://services.gradle.org/distributions/gradle-$version-all.zip"
$installDir = "C:\Gradle"
@@ -38,6 +38,6 @@ Install-ChocolateyZipPackage $packageName $url $installDir -Checksum $checksum -
$gradle_home = Join-Path $installDir "$packageName-$version"
$gradle_bat = Join-Path $gradle_home 'bin/gradle.bat'
Install-ChocolateyEnvironmentVariable "GRADLE_HOME" $gradle_home 'Machine'
Install-ChocolateyEnvironmentVariable "GRADLE_BUILD_HOME" $gradle_home 'Machine'
choco install -y ninja --version=1.10.0 --package-parameters="/installDir:C:\Ninja"
+1 -1
View File
@@ -53,7 +53,7 @@ def lambda_handler(event, context):
backoff = 30
status_list = [404] # Retry if the branch doesn't exist yet and provide time for Jenkins to discover it.
method_list = ['POST']
retry_config = Retry(total=retries, backoff_factor=backoff, status_forcelist=status_list, method_whitelist=method_list)
retry_config = Retry(total=retries, backoff_factor=backoff, status_forcelist=status_list, allowed_methods=method_list)
session = requests.Session()
session.mount('https://', HTTPAdapter(max_retries=retry_config))
@@ -37,25 +37,13 @@
"AssetBundler/**": "#include",
"AzTestRunner/**": "#include",
"CrashHandler/**": "#include",
"CryCommonTools/**": "#include",
"CrySCompileServer/**": "#include",
"CryXML/**": "#include",
"DeltaCataloger/**": "#include",
"GemRegistry/**": "#include",
"GridHub/**": "#include",
"HLSLCrossCompiler/**": "#include",
"HLSLCrossCompilerMETAL/**": "#include",
"LyIdentity/**": "#include",
"LyMetrics/**": "#include",
"News/**": "#include",
"PythonBindingsExample/**": "#include",
"RC/**": "#include",
"RemoteConsole/**": "#include",
"SceneAPI/**": "#include",
"SerializeContextTools/**": "#include",
"ShaderCacheGen/**": "#include",
"SharedQMLResource/**": "#include",
"Woodpecker/**": "#include",
"CMakeLists.txt": "#include"
},
"CMakeLists.txt": "#include"
@@ -93,7 +81,6 @@
"GraphCanvas": "#include",
"GraphModel": "#include",
"HttpRequestor": "#include",
"ImageProcessing": "#include",
"ImGui": "#include",
"InAppPurchases": "#include",
"LandscapeCanvas": "#include",
@@ -151,7 +138,6 @@
"3dsmax/**": "#include",
"7za.exe": "#include",
"7za_legal_notice.txt": "#include",
"CrySCompileServer/**": "#include",
"PakShaders/**": "#include",
"Python/**": "#include",
"Redistributables": {
-31
View File
@@ -25,9 +25,6 @@ from glob3 import glob
def package(options):
package_env = PackageEnv(options.platform, options.type, options.package_env)
# Override values in bootstrap.cfg for PC package
override_bootstrap_cfg(package_env)
if not package_env.get('SKIP_BUILD'):
print(package_env.get('SKIP_BUILD'))
print('SKIP_BUILD is False, running CMake build...')
@@ -51,34 +48,6 @@ def get_python_path(package_env):
return os.path.join(package_env.get('ENGINE_ROOT'), 'python', 'python.sh')
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 = {'project_path':'{}'.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 cmake_build(package_env):
build_targets = package_env.get('BUILD_TARGETS')
for build_target in build_targets:
@@ -1,76 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
import os
from incremental_build_util import get_iam_role_credentials
try:
import requests
except ImportError:
import pip
pip.main(['install', 'requests', '--ignore-installed', '-q'])
import requests
try:
from requests_aws4auth import AWS4Auth
except ImportError:
import pip
pip.main(['install', 'requests_aws4auth', '--ignore-installed', '-q'])
from requests_aws4auth import AWS4Auth
IAM_ROLE_NAME = 'ec2-jenkins-node'
TEAM = 'lumberyard-build'
CHIME_ROOM_WEB_HOOK = "https://hooks.chime.aws/incomingwebhooks/2a6018c9-3bf5-4e03-851c-32ba82c7c4e2?token=YWhTVXZsWVJ8MXxaLTg5RkVZNlA5Q1NiMVdfQndGeS1TSHNnYW5VREVha3pjX1pUUEd5b1JF"
def find_curr_oncalls_for_team(team):
host = "who-is-oncall-pdx.corp.amazon.com"
service_name = "who-is-oncall"
aws_region = "us-west-2"
headers = {"content-type": "application/json", "host": host}
credentials = get_iam_role_credentials(IAM_ROLE_NAME)
try:
aws_access_key_id = credentials['AccessKeyId']
aws_secret_access_key = credentials['SecretAccessKey']
aws_session_token = credentials['Token']
except Exception as e:
print(f'ERROR: Cannot get AWS credentials.\n{e}')
return ['All']
auth = AWS4Auth(aws_access_key_id, aws_secret_access_key, aws_region, service_name, session_token=aws_session_token)
r = requests.get(f"https://who-is-oncall-pdx.corp.amazon.com/teams/{team}", headers=headers, auth=auth, verify=False)
if r.ok:
res = r.json()
try:
return res['currOncalls']
except KeyError:
return ['All']
return ['All']
def send_alert_to_chime_room(web_hook, content):
data = '{"Content":"' + content + '"}'
headers = {'Content-Type': 'application/json'}
requests.post(web_hook, headers=headers, data=data)
def create_content():
content = ''
oncalls = find_curr_oncalls_for_team(TEAM)
for oncall in oncalls:
content += f'@{oncall} '
job_name = os.environ['JOB_NAME']
build_url = os.environ['BUILD_URL']
content += fr'\nJob {job_name} failed\nBuild URL: {build_url}\n'
return content
send_alert_to_chime_room(CHIME_ROOM_WEB_HOOK, create_content())
-128
View File
@@ -1,128 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
"""
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))
+2 -2
View File
@@ -163,11 +163,11 @@ def add_shaders_types():
shaders.append(gl4)
gles3 = _ShaderType('GLES3', 'GLSL_HLSLcc')
gles3.add_configuration('Android', 'es3')
gles3.add_configuration('Android', 'android')
shaders.append(gles3)
metal = _ShaderType('METAL', 'METAL_LLVM_DXC')
metal.add_configuration('Mac', 'osx_gl')
metal.add_configuration('Mac', 'mac')
metal.add_configuration('iOS', 'ios')
shaders.append(metal)
@@ -59,7 +59,6 @@
*/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/Plugins/StandardPlugins/Source/StandardPluginsConfig.h
*/Gems/EMotionFX/Code/MCore/Source/Config.h
*/Gems/GameStateSamples/Code/Include/GameStateSamples/GameStateLocalUserLobby.inl
*/Gems/ImageProcessing/Code/Tests/AtlasBuilderTest.cpp
*/Gems/PhysX/Code/Source/System/PhysXSystem.cpp
*/Gems/SaveData/Code/Tests/SaveDataTest.cpp
*/Gems/WhiteBox/Code/Source/Rendering/Legacy/WhiteBoxLegacyRenderMesh.cpp
@@ -118,8 +118,10 @@ def _merge_xml_results(xml_results_path, prefix, merged_xml_name, parent_element
def _aggregate_attributes(nodes):
for node in nodes:
for attribute in attributes_to_aggregate:
value = node.attrib[attribute.name]
temp_dict[attribute.name] += attribute.func(value)
if attribute.name in node.attrib:
temp_dict[attribute.name] += attribute.func(node.attrib[attribute.name])
else:
print("Failed to find key {} in {}, continuing...".format(attribute.name, node.tag))
base_tree = xet.parse(xml_files[0])
base_tree_root = base_tree.getroot()
+5 -3
View File
@@ -12,15 +12,15 @@ REM
pushd %~dp0%
CD %~dp0..
SET BASE_PATH=%CD%
SET "BASE_PATH=%CD%"
CD %~dp0
SET PYTHON_DIRECTORY=%BASE_PATH%\python
SET "PYTHON_DIRECTORY=%BASE_PATH%\python"
IF EXIST "%PYTHON_DIRECTORY%" GOTO pythonPathAvailable
GOTO pythonDirNotFound
:pythonPathAvailable
SET PYTHON_EXECUTABLE=%PYTHON_DIRECTORY%\python.cmd
IF NOT EXIST "%PYTHON_EXECUTABLE%" GOTO pythonExeNotFound
CALL "%PYTHON_EXECUTABLE%" %BASE_PATH%\scripts\o3de.py %*
CALL "%PYTHON_EXECUTABLE%" "%BASE_PATH%\scripts\o3de.py" %*
GOTO end
:pythonDirNotFound
ECHO Python directory not found: %PYTHON_DIRECTORY%
@@ -33,4 +33,6 @@ popd
EXIT /b 1
:end
popd
EXIT /b %ERRORLEVEL%
+44 -15
View File
@@ -10,25 +10,54 @@
#
import argparse
import pathlib
import sys
import os
# Resolve the common python module
ROOT_DEV_PATH = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
if ROOT_DEV_PATH not in sys.path:
sys.path.append(ROOT_DEV_PATH)
from cmake.Tools import engine_template
from cmake.Tools import current_project
from cmake.Tools import add_remove_gem
from cmake.Tools import registration
def add_args(parser, subparsers) -> None:
current_project.add_args(parser, subparsers)
engine_template.add_args(parser, subparsers)
add_remove_gem.add_args(parser, subparsers)
registration.add_args(parser, subparsers)
"""
add_args is called to add expected parser arguments and subparsers arguments to each command such that it can be
invoked by o3de.py
Ex o3de.py can invoke the register downloadable commands by importing register,
call add_args and execute: python o3de.py register --gem-path "C:/TestGem"
:param parser: the caller instantiates a parser and passes it in here
:param subparsers: the caller instantiates subparsers and passes it in here
"""
# As o3de.py shares the same name as the o3de package attempting to use a regular
# from o3de import <module> line tries to import from the current o3de.py script and not the package
# So the {current script directory} / 'o3de' is added to the front of the sys.path
script_dir = pathlib.Path(__file__).parent
o3de_package_dir = (script_dir / 'o3de').resolve()
# add the scripts/o3de directory to the front of the sys.path
sys.path.insert(0, str(o3de_package_dir))
from o3de import engine_template, global_project, register, print_registration, get_registration, \
enable_gem, disable_gem, sha256
# Remove the temporarily added path
sys.path = sys.path[1:]
# global_project
global_project.add_args(subparsers)
# engine templaate
engine_template.add_args(subparsers)
# register
register.add_args(subparsers)
# show
print_registration.add_args(subparsers)
# get-registered
get_registration.add_args(subparsers)
# add a gem to a project
enable_gem.add_args(subparsers)
# remove a gem from a project
disable_gem.add_args(subparsers)
# sha256
sha256.add_args(subparsers)
if __name__ == "__main__":
+5 -6
View File
@@ -15,11 +15,9 @@
#Note this does not actually change the working directory
SCRIPT_DIR=$(cd `dirname $0` && pwd)
#The engine root is always the parent of the scripts directory, so $(dirname "$SCRIPT_DIR") should get us engine root
ENGINE_ROOT=$(dirname "$SCRIPT_DIR")
#The engines python is in the engineroot/python
PYTHON_DIRECTORY="$ENGINE_ROOT/python"
#python should be in the base path
BASE_PATH=$(dirname "$SCRIPT_DIR")
PYTHON_DIRECTORY="$BASE_PATH/python"
#If engine python exists use it, if not try the system python
if [ ! -d "$PYTHON_DIRECTORY" ]; then
@@ -33,4 +31,5 @@ if [ ! -f "$PYTHON_EXECUTABLE" ]; then
fi
#run the o3de.py pass along the command
$PYTHON_EXECUTABLE "$SCRIPT_DIR/o3de.py" $*
$PYTHON_EXECUTABLE "$SCRIPT_DIR/o3de.py" $*
exit $?
+12
View File
@@ -0,0 +1,12 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
add_subdirectory(tests)
+41
View File
@@ -0,0 +1,41 @@
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
INTRODUCTION
------------
o3de is a package of scripts containing functionality to register engine, projects, gems,
templates and download repositories with the o3de manifests
It also contains functionality for creating new projects, gems and templates as well
as querying existing gems and templates
REQUIREMENTS
------------
* Python 3.7.10 (64-bit)
INSTALL
-----------
It is recommended to set up these these tools with O3DE's CMake build commands.
Assuming CMake is already setup on your operating system, below are some sample build commands:
cd /path/to/od3e/
cmake -B windows_vs2019 -S . -G"Visual Studio 16" -DLY_3RDPARTY_PATH="%LY_3RDPARTY_PATH%"
To manually install the project in development mode using your own installed Python interpreter:
cd /path/to/od3e/o3de
/path/to/your/python -m pip install -e .
UNINSTALLATION
--------------
The preferred way to uninstall the project is:
/path/to/your/python -m pip uninstall o3de
+10
View File
@@ -0,0 +1,10 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
+193
View File
@@ -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.
#
"""
Contains methods for query CMake gem target information
"""
import logging
import os
import pathlib
from o3de import manifest
logger = logging.getLogger()
logging.basicConfig()
enable_gem_start_marker = 'set(ENABLED_GEMS'
enable_gem_end_marker = ')'
def add_gem_dependency(cmake_file: pathlib.Path,
gem_name: str) -> int:
"""
adds a gem dependency to a cmake file
:param cmake_file: path to the cmake file
:param gem_name: name of the gem
:return: 0 for success or non 0 failure code
"""
if not cmake_file.is_file():
logger.error(f'Failed to locate cmake file {str(cmake_file)}')
return 1
# on a line by basis, see if there already is {gem_name}
# find the first occurrence of a gem, copy its formatting and replace
# the gem name with the new one and append it
# if the gem is already present fail
t_data = []
added = False
line_index_to_append = None
with open(cmake_file, 'r') as s:
line_index = 0
for line in s:
if line.strip().startswith(enable_gem_start_marker):
line_index_to_append = line_index
if f'{gem_name}' == line.strip():
logger.warning(f'{gem_name} is already enabled in file {str(cmake_file)}.')
return 0
t_data.append(line)
line_index += 1
indent = 4
if line_index_to_append:
# Insert the gem after the 'set(ENABLED_GEMS)...` line
t_data.insert(line_index_to_append + 1, f'{" " * indent}{gem_name}\n')
added = True
# if we didn't add, then create a new set(ENABLED_GEMS) variable
# add a new gem, if empty the correct format is 1 tab=4spaces
if not added:
t_data.append('\n')
t_data.append(f'{enable_gem_start_marker}\n')
t_data.append(f'{" " * indent}{gem_name}\n')
t_data.append(f'{enable_gem_end_marker}\n')
# write the cmake
with open(cmake_file, 'w') as s:
s.writelines(t_data)
return 0
def remove_gem_dependency(cmake_file: pathlib.Path,
gem_name: str) -> int:
"""
removes a gem dependency from a cmake file
:param cmake_file: path to the cmake file
:param gem_name: name of the gem
:return: 0 for success or non 0 failure code
"""
if not cmake_file.is_file():
logger.error(f'Failed to locate cmake file {cmake_file}')
return 1
# on a line by basis, remove any line with {gem_name}
t_data = []
# Remove the gem from the enabled_gem file by skipping the gem name entry
removed = False
with open(cmake_file, 'r') as s:
for line in s:
if gem_name == line.strip():
removed = True
else:
t_data.append(line)
if not removed:
logger.error(f'Failed to remove {gem_name} from cmake file {cmake_file}')
return 1
# write the cmake
with open(cmake_file, 'w') as s:
s.writelines(t_data)
return 0
def get_project_gems(project_path: pathlib.Path,
platform: str = 'Common') -> set:
return get_gems_from_cmake_file(get_enabled_gem_cmake_file(project_path=project_path, platform=platform))
def get_enabled_gems(cmake_file: pathlib.Path) -> set:
"""
Gets a list of enabled gems from the cmake file
:param cmake_file: path to the cmake file
:return: set of gem targets found
"""
cmake_file = pathlib.Path(cmake_file).resolve()
if not cmake_file.is_file():
logger.error(f'Failed to locate cmake file {cmake_file}')
return set()
gem_target_set = set()
with cmake_file.open('r') as s:
in_gem_list = False
for line in s:
line = line.strip()
if line.startswith(enable_gem_start_marker):
# Set the flag to indicate that we are in the ENABLED_GEMS variable
in_gem_list = True
# Skip pass the 'set(ENABLED_GEMS' marker just in case their are gems declared on the same line
line = line[len(enable_gem_start_marker):]
if in_gem_list:
# Since we are inside the ENABLED_GEMS variable determine if the line has the end_marker of ')'
if line.endswith(enable_gem_end_marker):
# Strip away the line end marker
line = line[:-len(enable_gem_end_marker)]
# Set the flag to indicate that we are no longer in the ENABLED_GEMS variable after this line
in_gem_list = False
# Split the rest of the line on whitespace just in case there are multiple gems in a line
gem_name_list = line.split()
gem_target_set.update(gem_name_list)
return gem_target_set
def get_project_gem_paths(project_path: pathlib.Path,
platform: str = 'Common') -> set:
gem_names = get_project_gems(project_path, platform)
gem_paths = set()
for gem_name in gem_names:
gem_paths.add(manifest.get_registered(gem_name=gem_name))
return gem_paths
def get_enabled_gem_cmake_file(project_name: str = None,
project_path: str or pathlib.Path = None,
platform: str = 'Common') -> pathlib.Path or None:
"""
get the standard cmake file name for a particular type of dependency
:param gem_name: name of the gem, resolves gem_path
:param gem_path: path of the gem
:return: list of gem targets
"""
if not project_name and not project_path:
logger.error(f'Must supply either a Project Name or Project Path.')
return None
if project_name and not project_path:
project_path = manifest.get_registered(project_name=project_name)
project_path = pathlib.Path(project_path).resolve()
enable_gem_filename = "enabled_gems.cmake"
if platform == 'Common':
project_code_dir = project_path / 'Gem/Code'
if project_code_dir.is_dir():
dependencies_file_path = project_code_dir / enable_gem_filename
return dependencies_file_path.resolve()
return (project_path / 'Code' / enable_gem_filename).resolve()
else:
project_code_dir = project_path / 'Gem/Code/Platform' / platform
if project_code_dir.is_dir():
dependencies_file_path = project_code_dir / enable_gem_filename
return dependencies_file_path.resolve()
return (project_path / 'Code/Platform' / platform / enable_gem_filename).resolve()
+177
View File
@@ -0,0 +1,177 @@
#
# 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.
#
"""
Contains methods for removing a gem from a project
"""
import argparse
import logging
import os
import pathlib
import sys
from o3de import cmake, manifest
logger = logging.getLogger()
logging.basicConfig()
def disable_gem_in_project(gem_name: str = None,
gem_path: pathlib.Path = None,
project_name: str = None,
project_path: pathlib.Path = None,
enabled_gem_file: pathlib.Path = None) -> int:
"""
disable a gem in a projects enabled_gems.cmake file
:param gem_name: name of the gem to add
:param gem_path: path to the gem to add
:param project_name: name of the project to add the gem to
:param project_path: path to the project to add the gem to
:param enabled_gem_file: File to remove enabled gem from
:return: 0 for success or non 0 failure code
"""
# we need either a project name or path
if not project_name and not project_path:
logger.error(f'Must either specify a Project path or Project Name.')
return 1
# if project name resolve it into a path
if project_name and not project_path:
project_path = manifest.get_registered(project_name=project_name)
if not project_path:
logger.error(f'Unable to locate project path from the registered manifest.json files:'
f' {str(pathlib.Path("~/.o3de/o3de_manifest.json").expanduser())}, engine.json')
return 1
project_path = pathlib.Path(project_path).resolve()
if not project_path.is_dir():
logger.error(f'Project path {project_path} is not a folder.')
return 1
# We need either a gem name or path
if not gem_name and not gem_path:
logger.error(f'Must either specify a Gem path or Gem Name.')
return 1
# if gem name resolve it into a path
if gem_name and not gem_path:
gem_path = manifest.get_registered(gem_name=gem_name)
if not gem_path:
logger.error(f'Unable to locate gem path from the registered manifest.json files:'
f' {str(pathlib.Path.home() / ".o3de/manifest.json")},'
f' {project_path / "project.json"}, engine.json')
return 1
gem_path = pathlib.Path(gem_path).resolve()
# make sure this gem already exists if we're adding. We can always remove a gem.
if not gem_path.is_dir():
logger.error(f'Gem Path {gem_path} does not exist.')
return 1
# Read gem.json from the gem path
gem_json_data = manifest.get_gem_json_data(gem_path=gem_path)
if not gem_json_data:
logger.error(f'Could not read gem.json content under {gem_path}.')
return 1
# when removing we will try to do as much as possible even with failures so ret_val will be the last error code
ret_val = 0
if not enabled_gem_file:
enabled_gem_file = cmake.get_enabled_gem_cmake_file(project_path=project_path)
# make sure this is a project has an enabled gems file
if not enabled_gem_file.is_file():
logger.error(f'Enabled gem file {enabled_gem_file} is not present.')
return 1
# remove the gem
error_code = cmake.remove_gem_dependency(enabled_gem_file, gem_json_data['gem_name'])
if error_code:
ret_val = error_code
return ret_val
def _run_disable_gem_in_project(args: argparse) -> int:
if args.override_home_folder:
manifest.override_home_folder = args.override_home_folder
return disable_gem_in_project(args.gem_name,
args.gem_path,
args.project_name,
args.project_path,
args.enabled_gem_file)
def add_parser_args(parser):
"""
add_parser_args is called to add arguments to each command such that it can be
invoked locally or added by a central python file.
Ex. Directly run from this file alone with: python disable_gem.py --project-path D:/Test --gem-name Atom
:param parser: the caller passes an argparse parser like instance to this method
"""
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-pp', '--project-path', type=pathlib.Path, required=False,
help='The path to the project.')
group.add_argument('-pn', '--project-name', type=str, required=False,
help='The name of the project.')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-gp', '--gem-path', type=pathlib.Path, required=False,
help='The path to the gem.')
group.add_argument('-gn', '--gem-name', type=str, required=False,
help='The name of the gem.')
parser.add_argument('-egf', '--enabled-gem-file', type=pathlib.Path, required=False,
help='The cmake enabled gem file in which gem names are to be removed from.'
'If not specified it will assume ')
parser.add_argument('-ohf', '--override-home-folder', type=pathlib.Path, required=False,
help='By default the home folder is the user folder, override it to this folder.')
parser.set_defaults(func=_run_disable_gem_in_project)
def add_args(subparsers) -> None:
"""
add_args is called to add subparsers arguments to each command such that it can be
a central python file such as o3de.py.
It can be run from the o3de.py script as follows
call add_args and execute: python o3de.py disable-gem-from-cmake --project-path D:/Test --gem-name Atom
:param subparsers: the caller instantiates subparsers and passes it in here
"""
disable_gem_project_subparser = subparsers.add_parser('disable-gem')
add_parser_args(disable_gem_project_subparser)
def main():
"""
Runs disable_gem_project.py script as standalone script
"""
# parse the command line args
the_parser = argparse.ArgumentParser()
# add subparsers
# add args to the parser
add_parser_args(the_parser)
# parse args
the_args = the_parser.parse_args()
# run
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
# return
sys.exit(ret)
if __name__ == "__main__":
main()
+240
View File
@@ -0,0 +1,240 @@
#
# 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.
#
"""
Implements functionality for downloading o3de objecs either locally or from a URI
"""
import argparse
import hashlib
import json
import logging
import pathlib
import shutil
import sys
import urllib.parse
import urllib.request
from o3de import manifest, repo, utils, validation
logger = logging.getLogger()
logging.basicConfig()
def unzip_manifest_json_data(download_zip_path: pathlib.Path, zip_file_name: str) -> dict:
json_data = {}
with zipfile.ZipFile(download_zip_path, 'r') as zip_data:
with zip_data.open(zip_file_name) as manifest_json_file:
try:
json_data = json.load(manifest_json_file)
except json.JSONDecodeError as e:
logger.error(f'UnZip exception:{str(e)}')
return json_data
def validate_downloaded_zip_sha256(download_uri_json_data: dict, download_zip_path: pathlib.Path,
manifest_json_name) -> int:
# if the engine.json has a sha256 check it against a sha256 of the zip
try:
sha256A = download_uri_json_data['sha256']
except KeyError as e:
logger.warn(f'SECURITY WARNING: The advertised o3de object you downloaded has no "sha256"!!! Be VERY careful!!!'
f' We cannot verify this is the actually the advertised object!!!')
else:
sha256B = hashlib.sha256(download_zip_path.open('rb').read()).hexdigest()
if sha256A != sha256B:
logger.error(f'SECURITY VIOLATION: Downloaded zip sha256 {sha256B} does not match'
f' the advertised "sha256":{sha256A} in the f{manifest_json_name}. Deleting unzipped files!!!')
shutil.rmtree(dest_path)
return 1
manifest_json_data = unzip_manifest_json_data(download_zip_path, manifest_json_name)
# remove the sha256 if present in the advertised downloadable manifest json
# then compare it to the json in the zip, they should now be identical
try:
del download_uri_json_data['sha256']
except KeyError as e:
pass
sha256A = hashlib.sha256(json.dumps(download_uri_json_data, indent=4).encode('utf8')).hexdigest()
with unzipped_manifest_json.open('r') as s:
try:
unzipped_manifest_json_data = json.load(s)
except json.JSONDecodeError as e:
logger.error(f'Failed to read manifest json {unzipped_manifest_json}. Unable to confirm this'
f' is the same template that was advertised.')
return 1
sha256B = hashlib.sha256(json.dumps(unzipped_manifest_json_data, indent=4).encode('utf8')).hexdigest()
if sha256A != sha256B:
logger.error(f'SECURITY VIOLATION: Downloaded manifest json does not match'
f' the advertised manifest json. Deleting unzipped files!!!')
shutil.rmtree(dest_path)
return 1
return 0
def get_downloadable(engine_name: str = None,
project_name: str = None,
gem_name: str = None,
template_name: str = None,
restricted_name: str = None) -> dict or None:
json_data = manifest.load_o3de_manifest()
try:
o3de_object_uris = json_data['repos']
except KeyError as key_err:
logger.error(f'Unable to load repos from o3de manifest: {str(key_err)}')
return None
manifest_json = 'repo.json'
search_func = lambda: repo.search_repo(manifest_json, engine_name, project_name, gem_name, template_name)
return repo.search_o3de_object(manifest_json, o3de_object_uris, search_func)
def download_o3de_object(object_name: str, default_folder_name: str, dest_path: str or pathlib.Path,
object_type: str, downloadable_kwarg_key) -> int:
if not dest_path:
dest_path = manifest.get_registered(default_folder=default_folder_name)
if not dest_path:
logger.error(f'Destination path not cannot be empty.')
return 1
dest_path = pathlib.Path(dest_path).resolve()
dest_path.mkdir(exist_ok=True)
download_path = manifest.get_o3de_download_folder() / default_folder_name / object_name
download_path.mkdir(exist_ok=True)
download_zip_path = download_path / f'{object_type}.zip'
downloadable_object_data = get_downloadable(**{downloadable_kwarg_key : object_name})
if not downloadable_object_data:
logger.error(f'Downloadable o3de object {object_name} not found.')
return 1
origin = downloadable_json_data['origin']
url = f'{origin}/object_type.zip'
parsed_uri = urllib.parse.urlparse(url)
download_zip_result = utils.download_zip_file(parsed_uri, download_zip_path)
if download_zip_result != 0:
return download_zip_result
return validate_downloaded_zip_sha256(downloadable_object_data, download_zip_path)
def download_engine(engine_name: str,
dest_path: str or pathlib.Path) -> int:
return download_o3de_object(engine_name, 'engines', dest_path, 'engine', 'engine_name')
def download_project(project_name: str,
dest_path: str or pathlib.Path) -> int:
return download_o3de_object(project_name, 'projects', dest_path, 'project', 'project_name')
def download_gem(gem_name: str,
dest_path: str or pathlib.Path) -> int:
return download_o3de_object(gem_name, 'gems', dest_path, 'gem', 'gem_name')
def download_template(template_name: str,
dest_path: str or pathlib.Path) -> int:
return download_o3de_object(template_name, 'templates', dest_path, 'template', 'template_name')
def download_restricted(restricted_name: str,
dest_path: str or pathlib.Path) -> int:
return download_o3de_object(restricted_name, 'restricted', dest_path, 'restricted', 'restricted_name')
def _run_download(args: argparse) -> int:
if args.override_home_folder:
manifest.override_home_folder = args.override_home_folder
if args.engine_name:
return download_engine(args.engine_name,
args.dest_path)
elif args.project_name:
return download_project(args.project_name,
args.dest_path)
elif args.gem_nanme:
return download_gem(args.gem_name,
args.dest_path)
elif args.template_name:
return download_template(args.template_name,
args.dest_path)
return 1
def add_parser_args(parser):
"""
add_parser_args is called to add arguments to each command such that it can be
invoked locally or added by a central python file.
Ex. Directly run from this file alone with: python download.py --engine-name "o3de"
:param parser: the caller passes an argparse parser like instance to this method
"""
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-e', '--engine-name', type=str, required=False,
help='Downloadable engine name.')
group.add_argument('-p', '--project-name', type=str, required=False,
help='Downloadable project name.')
group.add_argument('-g', '--gem-name', type=str, required=False,
help='Downloadable gem name.')
group.add_argument('-t', '--template-name', type=str, required=False,
help='Downloadable template name.')
parser.add_argument('-dp', '--dest-path', type=str, required=False,
default=None,
help='Optional destination folder to download into.'
' i.e. download --project-name "AstomSamplerViewer" --dest-path "C:/projects"'
' will result in C:/projects/AtomSampleViewer'
' If blank will download to default object type folder')
parser.add_argument('-ohf', '--override-home-folder', type=str, required=False,
help='By default the home folder is the user folder, override it to this folder.')
parser.set_defaults(func=_run_download)
def add_args(subparsers) -> None:
"""
add_args is called to add subparsers arguments to each command such that it can be
a central python file such as o3de.py.
It can be run from the o3de.py script as follows
call add_args and execute: python o3de.py download --engine-name "o3de"
:param subparsers: the caller instantiates subparsers and passes it in here
"""
download_subparser = subparsers.add_parser('download')
add_parser_args(download_subparser)
def main():
"""
Runs download.py script as standalone script
"""
# parse the command line args
the_parser = argparse.ArgumentParser()
# add subparsers
# add args to the parser
add_parser_args(the_parser)
# parse args
the_args = the_parser.parse_args()
# run
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
# return
sys.exit(ret)
# Do not allow running the download.py script as a standalone script until it is reviewed by app-sec
+181
View File
@@ -0,0 +1,181 @@
#
# 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.
#
"""
Contains command to add a gem to a project's enabled_gem.cmake file
"""
import argparse
import json
import logging
import os
import pathlib
import sys
from o3de import cmake, manifest, validation
logger = logging.getLogger()
logging.basicConfig()
def enable_gem_in_project(gem_name: str = None,
gem_path: pathlib.Path = None,
project_name: str = None,
project_path: pathlib.Path = None,
enabled_gem_file: pathlib.Path = None) -> int:
"""
enable a gem in a projects enabled_gems.cmake file
:param gem_name: name of the gem to add
:param gem_path: path to the gem to add
:param project_name: name of to the project to add the gem to
:param project_path: path to the project to add the gem to
:param enabled_gem_file_file: if this dependency goes/is in a specific file
:return: 0 for success or non 0 failure code
"""
# we need either a project name or path
if not project_name and not project_path:
logger.error(f'Must either specify a Project path or Project Name.')
return 1
# if project name resolve it into a path
if project_name and not project_path:
project_path = manifest.get_registered(project_name=project_name)
if not project_path:
logger.error(f'Unable to locate project path from the registered manifest.json files:'
f' {str(pathlib.Path.home() / ".o3de/manifest.json")}, engine.json')
return 1
project_path = pathlib.Path(project_path).resolve()
if not project_path.is_dir():
logger.error(f'Project path {project_path} is not a folder.')
return 1
# we need either a gem name or path
if not gem_name and not gem_path:
logger.error(f'Must either specify a Gem path or Gem Name.')
return 1
# if gem name resolve it into a path
if gem_name and not gem_path:
gem_path = manifest.get_registered(gem_name=gem_name)
if not gem_path:
logger.error(f'Unable to locate gem path from the registered manifest.json files:'
f' {str(pathlib.Path( "~/.o3de/o3de_manifest.json").expanduser())},'
f' {project_path / "project.json"}, engine.json')
return 1
gem_path = pathlib.Path(gem_path).resolve()
# make sure this gem already exists if we're adding. We can always remove a gem.
if not gem_path.is_dir():
logger.error(f'Gem Path {gem_path} does not exist.')
return 1
# Read gem.json from the gem path
gem_json_data = manifest.get_gem_json_data(gem_path=gem_path)
if not gem_json_data:
logger.error(f'Could not read gem.json content under {gem_path}.')
return 1
ret_val = 0
if enabled_gem_file:
# make sure this is a project has an enabled gems file
if not enabled_gem_file.is_file():
logger.error(f'Enabled gem file {enabled_gem_file} is not present.')
return 1
# add the gem
ret_val = cmake.add_gem_dependency(enabled_gem_file, gem_json_data['gem_name'])
else:
# Find the path to enabled gem file.
# It will be created if it doesn't exist
project_enabled_gem_file = cmake.get_enabled_gem_cmake_file(project_path=project_path)
if not project_enabled_gem_file.is_file():
project_enabled_gem_file.touch()
# add the gem
ret_val = cmake.add_gem_dependency(project_enabled_gem_file, gem_json_data['gem_name'])
return ret_val
def _run_enable_gem_in_project(args: argparse) -> int:
if args.override_home_folder:
manifest.override_home_folder = args.override_home_folder
return enable_gem_in_project(args.gem_name,
args.gem_path,
args.project_name,
args.project_path,
args.enabled_gem_file)
def add_parser_args(parser):
"""
add_parser_args is called to add arguments to each command such that it can be
invoked locally or added by a central python file.
Ex. Directly run from this file alone with: python enable_gem.py --project-path "D:/TestProject" --gem-path "D:/TestGem"
:param parser: the caller passes an argparse parser like instance to this method
"""
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-pp', '--project-path', type=pathlib.Path, required=False,
help='The path to the project.')
group.add_argument('-pn', '--project-name', type=str, required=False,
help='The name of the project.')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-gp', '--gem-path', type=pathlib.Path, required=False,
help='The path to the gem.')
group.add_argument('-gn', '--gem-name', type=str, required=False,
help='The name of the gem.')
parser.add_argument('-egf', '--enabled-gem-file', type=pathlib.Path, required=False,
help='The cmake enabled_gem file in which the gem names are specified.'
'If not specified it will assume enabled_gems.cmake')
parser.add_argument('-ohf', '--override-home-folder', type=pathlib.Path, required=False,
help='By default the home folder is the user folder, override it to this folder.')
parser.set_defaults(func=_run_enable_gem_in_project)
def add_args(subparsers) -> None:
"""
add_args is called to add subparsers arguments to each command such that it can be
a central python file such as o3de.py.
It can be run from the o3de.py script as follows
call add_args and execute: python o3de.py add-gem-to-project --project-path "D:/TestProject" --gem-path "D:/TestGem"
:param subparsers: the caller instantiates subparsers and passes it in here
"""
enable_gem_project_subparser = subparsers.add_parser('enable-gem')
add_parser_args(enable_gem_project_subparser)
def main():
"""
Runs enable_gem.py script as standalone script
"""
# parse the command line args
the_parser = argparse.ArgumentParser()
# add subparsers
# add args to the parser
add_parser_args(the_parser)
# parse args
the_args = the_parser.parse_args()
# run
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
# return
sys.exit(ret)
if __name__ == "__main__":
main()
+2465
View File
File diff suppressed because it is too large Load Diff
+96
View File
@@ -0,0 +1,96 @@
#
# 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 pathlib
import sys
from o3de import manifest
def _run_get_registered(args: argparse) -> str or pathlib.Path:
if args.override_home_folder:
manifest.override_home_folder = args.override_home_folder
return manifest.get_registered(args.engine_name,
args.project_name,
args.gem_name,
args.template_name,
args.default_folder,
args.repo_name,
args.restricted_name)
def add_parser_args(parser):
"""
add_parser_args is called to add arguments to each command such that it can be
invoked locally or added by a central python file.
Ex. Directly run from this file alone with: python get_registration.py --engine-name "o3de"
:param parser: the caller passes an argparse parser like instance to this method
"""
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-en', '--engine-name', type=str, required=False,
help='Engine name.')
group.add_argument('-pn', '--project-name', type=str, required=False,
help='Project name.')
group.add_argument('-gn', '--gem-name', type=str, required=False,
help='Gem name.')
group.add_argument('-tn', '--template-name', type=str, required=False,
help='Template name.')
group.add_argument('-df', '--default-folder', type=str, required=False,
choices=['engines', 'projects', 'gems', 'templates', 'restricted'],
help='The default folders for o3de.')
group.add_argument('-rn', '--repo-name', type=str, required=False,
help='Repo name.')
group.add_argument('-rsn', '--restricted-name', type=str, required=False,
help='Restricted name.')
parser.add_argument('-ohf', '--override-home-folder', type=str, required=False,
help='By default the home folder is the user folder, override it to this folder.')
parser.set_defaults(func=_run_get_registered)
def add_args(subparsers) -> None:
"""
add_args is called to add subparsers arguments to each command such that it can be
a central python file such as o3de.py.
It can be run from the o3de.py script as follows
call add_args and execute: python o3de.py get-registered --engine-name "o3de"
:param subparsers: the caller instantiates subparsers and passes it in here
"""
get_registered_subparser = subparsers.add_parser('get-registered')
add_parser_args(get_registered_subparser)
def main():
"""
Runs get_registration.py script as standalone script
"""
# parse the command line args
the_parser = argparse.ArgumentParser()
# add subparsers
# add args to the parser
add_parser_args(the_parser)
# parse args
the_args = the_parser.parse_args()
# run
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
# return
sys.exit(ret)
if __name__ == "__main__":
main()
+214
View File
@@ -0,0 +1,214 @@
#
# 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 os
import sys
import re
import pathlib
import json
from o3de import manifest, validation
logger = logging.getLogger()
logging.basicConfig()
DEFAULT_BOOTSTRAP_SETREG = pathlib.Path('~/.o3de/Registry/bootstrap.setreg').expanduser()
PROJECT_PATH_KEY = ('Amazon', 'AzCore', 'Bootstrap', 'project_path')
def get_json_data(input_path: pathlib.Path):
setreg_json_data = {}
# If the output_path exist validate that it is a valid json file
if input_path.is_file():
with input_path.open('r') as f:
try:
setreg_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.error(f'The file: {input_path} is not a valid json file: {str(e)}')
return setreg_json_data
def set_global_project(output_path: pathlib.Path,
project_name: str = None,
project_path: pathlib.Path = None,
force: bool = False) -> int:
"""
Adds a project path the a settings registry file in the users ~/.o3de/Registry directory
:param output_path: path to .setreg file to store project_path value into
:param project_name: name of the project to lookup path for
:param project_path: path to the project to add to .setreg file
:param force: if set, the project path will be set within the .setreg file regardless of if the path doesn't exist
:return: 0 for success or non 0 failure code
"""
# we need either a project name or path
if not project_name and not project_path:
logger.error(f'Must either specify a Project path or Project Name.')
return 1
# if project name resolve it into a path
if project_name and not project_path:
project_path = manifest.get_registered(project_name=project_name)
if not project_path:
logger.error(
f'The project name has been supplied. Unable to locate project path from the registered manifest.json files:'
f' {str(pathlib.Path("~/.o3de/o3de_manifest.json").expanduser())}, engine.json\n'
'A The --project-path parameter can be used directly to skip checking the manifest')
return 1
# Only perform project path validations when force=False
if not force:
if not project_path.is_dir():
logger.error(f'Project path {project_path} is not a folder.')
return 1
# Validate that the supplied path points contains a valid project.json
if not validation.valid_o3de_project_json(project_path / 'project.json'):
logger.error(f'The supplied project path does not contain a valid project.json.\n'
f'The Path will not be set')
return 1
# If the output_path exist validate that it is a valid json file and read it's json data
setreg_json_data = get_json_data(output_path)
if output_path.is_file():
with output_path.open('r') as f:
try:
setreg_json_data = json.load(f)
except (json.JSONDecodeError) as e:
logger.error(f'The output file: {output_path} is not a valid json file: {str(e)}')
return 1
# Add a json dictionary that will be merged with any existing json data from the .setreg file
merge_json_data = {}
json_object_iter = merge_json_data
for json_key in PROJECT_PATH_KEY[:-1]:
# Add the parent json object for the key to update
json_object_iter = json_object_iter.setdefault(json_key, {})
# Set the project path value here
json_object_iter[PROJECT_PATH_KEY[-1]] = project_path.as_posix()
setreg_json_data.update(merge_json_data)
# Create the parent directories
if output_path.parent:
output_path.parent.mkdir(parents=True, exist_ok=True)
try:
with output_path.open('w') as s:
s.write(json.dumps(setreg_json_data, indent=4) + '\n')
except OSError as e:
logger.error(f'Failed to write project path {project_path} to file {output_path}: {str(e)}')
return 1
return 0
def get_global_project(input_path: pathlib.Path) -> pathlib.Path or None:
"""
Retrieves the /Amazon/AzCore/Bootstrap/project_path key from the supplied file path
:return: project_path or None on failure
"""
setreg_json_data = get_json_data(input_path)
try:
# Iterate over each element of the tuple and read the json key from each successive json object
json_object_iter = setreg_json_data
for json_key in PROJECT_PATH_KEY:
json_object_iter = json_object_iter[json_key]
except KeyError as e:
logger.error(f'Cannot read key /{"/".join(PROJECT_PATH_KEY)} from file {input_path.as_posix()}: {str(e)}')
else:
project_path = json_object_iter
return pathlib.Path(project_path).resolve()
return None
def _run_get_global_project(args: argparse) -> int:
project_path = get_global_project(args.input_path)
if project_path:
print(project_path.as_posix())
return 0
return 1
def _run_set_global_project(args: argparse) -> int:
return set_global_project(args.output_path,
args.project_name,
args.project_path,
args.force)
def add_parser_args(get_project_parser, set_project_parser):
"""
add_parser_args is called to add arguments to each command such that it can be
invoked locally or added by a central python file.
Ex. Directly run from this file alone with: python global_project.py --project-path "D:/TestProject"
:param parser: the caller passes an argparse parser like instance to this method
"""
# get-current-project
get_project_parser.add_argument('-i', '--input-path', type=pathlib.Path, required=False, default=DEFAULT_BOOTSTRAP_SETREG,
help=f'Optional path to file to read /{"/".join(PROJECT_PATH_KEY)} key from.'
f' If not supplied, then {DEFAULT_BOOTSTRAP_SETREG} is used instead')
get_project_parser.set_defaults(func=_run_get_global_project)
# set-current-project
group = set_project_parser.add_mutually_exclusive_group(required=True)
group.add_argument('-pp', '--project-path', type=pathlib.Path, required=False,
help='The path to the project.')
group.add_argument('-pn', '--project-name', type=str, required=False,
help='The name of the project.')
set_project_parser.add_argument('-o', '--output-path', type=pathlib.Path, required=False,
default=DEFAULT_BOOTSTRAP_SETREG,
help=f'Optional path to output file to write project_path key to. '
f'If not supplied, then {DEFAULT_BOOTSTRAP_SETREG} is used instead')
set_project_parser.add_argument('-f', '--force', action='store_true', default=False,
help=f'Force the setting of the project path in the supplied setreg file')
set_project_parser.set_defaults(func=_run_set_global_project)
def add_args(subparsers) -> None:
"""
add_args is called to add subparsers arguments to each command such that it can be
a central python file such as o3de.py.
It can be run from the o3de.py script as follows
call add_args and execute: python o3de.py set-global-project --project-path "D:/TestProject"
:param subparsers: the caller instantiates subparsers and passes it in here
"""
get_project_subparser = subparsers.add_parser('get-global-project')
set_project_subparser = subparsers.add_parser('set-global-project')
add_parser_args(get_project_subparser, set_project_subparser)
def main():
"""
Runs this script as standalone script
"""
# parse the command line args
the_parser = argparse.ArgumentParser()
# add subparsers
project_subparsers = the_parser.add_subparsers(help="Commands for modifying the project path in the user's home"
" setreg files")
# add args to the parser
add_args(project_subparsers)
# parse args
the_args = the_parser.parse_args()
# run
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
# return
sys.exit(ret)
if __name__ == "__main__":
main()
+675
View File
@@ -0,0 +1,675 @@
#
# 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.
#
"""
Contains functions for data from json files such as the o3de_manifests.json, engine.json, project.json, etc...
"""
import json
import logging
import os
import pathlib
from o3de import validation
logger = logging.getLogger()
logging.basicConfig()
# Directory methods
override_home_folder = None
def get_this_engine_path() -> pathlib.Path:
return pathlib.Path(os.path.realpath(__file__)).parents[3].resolve()
def get_home_folder() -> pathlib.Path:
if override_home_folder:
return pathlib.Path(override_home_folder).resolve()
else:
return pathlib.Path(os.path.expanduser("~")).resolve()
def get_o3de_folder() -> pathlib.Path:
o3de_folder = get_home_folder() / '.o3de'
o3de_folder.mkdir(parents=True, exist_ok=True)
return o3de_folder
def get_o3de_registry_folder() -> pathlib.Path:
registry_folder = get_o3de_folder() / 'Registry'
registry_folder.mkdir(parents=True, exist_ok=True)
return registry_folder
def get_o3de_cache_folder() -> pathlib.Path:
cache_folder = get_o3de_folder() / 'Cache'
cache_folder.mkdir(parents=True, exist_ok=True)
return cache_folder
def get_o3de_download_folder() -> pathlib.Path:
download_folder = get_o3de_folder() / 'Download'
download_folder.mkdir(parents=True, exist_ok=True)
return download_folder
def get_o3de_engines_folder() -> pathlib.Path:
engines_folder = get_o3de_folder() / 'Engines'
engines_folder.mkdir(parents=True, exist_ok=True)
return engines_folder
def get_o3de_projects_folder() -> pathlib.Path:
projects_folder = get_o3de_folder() / 'Projects'
projects_folder.mkdir(parents=True, exist_ok=True)
return projects_folder
def get_o3de_gems_folder() -> pathlib.Path:
gems_folder = get_o3de_folder() / 'Gems'
gems_folder.mkdir(parents=True, exist_ok=True)
return gems_folder
def get_o3de_templates_folder() -> pathlib.Path:
templates_folder = get_o3de_folder() / 'Templates'
templates_folder.mkdir(parents=True, exist_ok=True)
return templates_folder
def get_o3de_restricted_folder() -> pathlib.Path:
restricted_folder = get_o3de_folder() / 'Restricted'
restricted_folder.mkdir(parents=True, exist_ok=True)
return restricted_folder
def get_o3de_logs_folder() -> pathlib.Path:
logs_folder = get_o3de_folder() / 'Logs'
logs_folder.mkdir(parents=True, exist_ok=True)
return logs_folder
# o3de manifest file methods
def get_o3de_manifest() -> pathlib.Path:
manifest_path = get_o3de_folder() / 'o3de_manifest.json'
if not manifest_path.is_file():
username = os.path.split(get_home_folder())[-1]
o3de_folder = get_o3de_folder()
default_registry_folder = get_o3de_registry_folder()
default_cache_folder = get_o3de_cache_folder()
default_downloads_folder = get_o3de_download_folder()
default_logs_folder = get_o3de_logs_folder()
default_engines_folder = get_o3de_engines_folder()
default_projects_folder = get_o3de_projects_folder()
default_gems_folder = get_o3de_gems_folder()
default_templates_folder = get_o3de_templates_folder()
default_restricted_folder = get_o3de_restricted_folder()
default_projects_restricted_folder = default_projects_folder / 'Restricted'
default_projects_restricted_folder.mkdir(parents=True, exist_ok=True)
default_gems_restricted_folder = default_gems_folder / 'Restricted'
default_gems_restricted_folder.mkdir(parents=True, exist_ok=True)
default_templates_restricted_folder = default_templates_folder / 'Restricted'
default_templates_restricted_folder.mkdir(parents=True, exist_ok=True)
json_data = {}
json_data.update({'o3de_manifest_name': f'{username}'})
json_data.update({'origin': o3de_folder.as_posix()})
json_data.update({'default_engines_folder': default_engines_folder.as_posix()})
json_data.update({'default_projects_folder': default_projects_folder.as_posix()})
json_data.update({'default_gems_folder': default_gems_folder.as_posix()})
json_data.update({'default_templates_folder': default_templates_folder.as_posix()})
json_data.update({'default_restricted_folder': default_restricted_folder.as_posix()})
json_data.update({'projects': []})
json_data.update({'external_subdirectories': []})
json_data.update({'templates': []})
json_data.update({'restricted': []})
json_data.update({'repos': []})
json_data.update({'engines': []})
default_restricted_folder_json = default_restricted_folder / 'restricted.json'
if not default_restricted_folder_json.is_file():
with default_restricted_folder_json.open('w') as s:
restricted_json_data = {}
restricted_json_data.update({'restricted_name': 'o3de'})
s.write(json.dumps(restricted_json_data, indent=4) + '\n')
json_data.update({'default_restricted_folder': default_restricted_folder.as_posix()})
default_projects_restricted_folder_json = default_projects_restricted_folder / 'restricted.json'
if not default_projects_restricted_folder_json.is_file():
with default_projects_restricted_folder_json.open('w') as s:
restricted_json_data = {}
restricted_json_data.update({'restricted_name': 'projects'})
s.write(json.dumps(restricted_json_data, indent=4) + '\n')
default_gems_restricted_folder_json = default_gems_restricted_folder / 'restricted.json'
if not default_gems_restricted_folder_json.is_file():
with default_gems_restricted_folder_json.open('w') as s:
restricted_json_data = {}
restricted_json_data.update({'restricted_name': 'gems'})
s.write(json.dumps(restricted_json_data, indent=4) + '\n')
default_templates_restricted_folder_json = default_templates_restricted_folder / 'restricted.json'
if not default_templates_restricted_folder_json.is_file():
with default_templates_restricted_folder_json.open('w') as s:
restricted_json_data = {}
restricted_json_data.update({'restricted_name': 'templates'})
s.write(json.dumps(restricted_json_data, indent=4) + '\n')
with manifest_path.open('w') as s:
s.write(json.dumps(json_data, indent=4) + '\n')
return manifest_path
def load_o3de_manifest(manifest_path: pathlib.Path = None) -> dict:
"""
Loads supplied manifest file or ~/.o3de/o3de_manifest.json if None
:param manifest_path: optional path to manifest file to load
"""
if not manifest_path:
manifest_path = get_o3de_manifest()
with manifest_path.open('r') as f:
try:
json_data = json.load(f)
except json.JSONDecodeError as e:
logger.error(f'Manifest json failed to load: {str(e)}')
return {}
else:
return json_data
def save_o3de_manifest(json_data: dict, manifest_path: pathlib.Path = None) -> None:
"""
Save the json dictionary to the supplied manifest file or ~/.o3de/o3de_manifest.json if None
:param json_data: dictionary to save in json format at the file path
:param manifest_path: optional path to manifest file to save
"""
if not manifest_path:
manifest_path = get_o3de_manifest()
with manifest_path.open('w') as s:
try:
s.write(json.dumps(json_data, indent=4) + '\n')
except OSError as e:
logger.error(f'Manifest json failed to save: {str(e)}')
# Data query methods
def get_this_engine() -> dict:
json_data = load_o3de_manifest()
engine_data = find_engine_data(json_data)
return engine_data
def get_engines() -> list:
json_data = load_o3de_manifest()
return json_data['engines'] if 'engines' in json_data else []
def get_projects() -> list:
json_data = load_o3de_manifest()
return json_data['projects'] if 'projects' in json_data else []
def get_gems() -> list:
def is_gem_subdirectory(subdir):
return (pathlib.Path(subdir) / 'gem.json').exists()
external_subdirs = get_external_subdirectories()
return list(filter(is_gem_subdirectory, external_subdirs)) if external_subdirs else []
def get_external_subdirectories() -> list:
json_data = load_o3de_manifest()
return json_data['external_subdirectories'] if 'external_subdirectories' in json_data else []
def get_templates() -> list:
json_data = load_o3de_manifest()
return json_data['templates'] if 'templates' in json_data else []
def get_restricted() -> list:
json_data = load_o3de_manifest()
return json_data['restricted'] if 'restricted' in json_data else []
def get_repos() -> list:
json_data = load_o3de_manifest()
return json_data['repos'] if 'repos' in json_data else []
# engine.json queries
def get_engine_projects() -> list:
engine_path = get_this_engine_path()
engine_object = get_engine_json_data(engine_path=engine_path)
return list(map(lambda rel_path: (pathlib.Path(engine_path) / rel_path).as_posix(),
engine_object['projects'])) if 'projects' in engine_object else []
def get_engine_gems() -> list:
def is_gem_subdirectory(subdir):
return (pathlib.Path(subdir) / 'gem.json').exists()
external_subdirs = get_engine_external_subdirectories()
return list(filter(is_gem_subdirectory, external_subdirs)) if external_subdirs else []
def get_engine_external_subdirectories() -> list:
engine_path = get_this_engine_path()
engine_object = get_engine_json_data(engine_path=engine_path)
return list(map(lambda rel_path: (pathlib.Path(engine_path) / rel_path).as_posix(),
engine_object['external_subdirectories'])) if 'external_subdirectories' in engine_object else []
def get_engine_templates() -> list:
engine_path = get_this_engine_path()
engine_object = get_engine_json_data(engine_path=engine_path)
return list(map(lambda rel_path: (pathlib.Path(engine_path) / rel_path).as_posix(),
engine_object['templates']))
def get_engine_restricted() -> list:
engine_path = get_this_engine_path()
engine_object = get_engine_json_data(engine_path=engine_path)
return list(map(lambda rel_path: (pathlib.Path(engine_path) / rel_path).as_posix(),
engine_object['restricted'])) if 'restricted' in engine_object else []
# project.json queries
def get_project_gems(project_path: pathlib.Path) -> list:
def is_gem_subdirectory(subdir):
return (pathlib.Path(subdir) / 'gem.json').exists()
external_subdirs = get_project_external_subdirectories(project_path)
return list(filter(is_gem_subdirectory, external_subdirs)) if external_subdirs else []
def get_project_external_subdirectories(project_path: pathlib.Path) -> list:
project_object = get_project_json_data(project_path=project_path)
return list(map(lambda rel_path: (pathlib.Path(project_path) / rel_path).as_posix(),
project_object['external_subdirectories'])) if 'external_subdirectories' in project_object else []
# Combined manifest queries
def get_all_projects() -> list:
projects_data = set(get_projects())
projects_data.update(get_engine_projects())
return list(projects_data)
def get_all_gems(project_path: pathlib.Path = None) -> list:
gems_data = set(get_gems())
gems_data.update(get_engine_gems())
if project_path:
gems_data.update(get_project_gems(project_path))
return list(gems_data)
def get_all_external_subdirectories(project_path: pathlib.Path = None) -> list:
external_subdirectories_data = set(get_external_subdirectories())
external_subdirectories_data.update(get_engine_external_subdirectories())
if project_path:
external_subdirectories_data.update(get_project_external_subdirectories(project_path))
return list(templates_data)
def get_all_templates() -> list:
templates_data = set(get_templates())
templates_data.update(get_engine_templates())
return list(templates_data)
def get_all_restricted() -> list:
restricted_data = set(get_restricted())
restricted_data.update(get_engine_restricted())
return list(gems_data)
# Template functions
def get_project_templates(): # temporary until we have a better way to do this... maybe template_type element
project_templates = []
for template in get_all_templates():
if 'Project' in template:
project_templates.append(template)
return project_templates
def get_gem_templates(): # temporary until we have a better way to do this... maybe template_type element
gem_templates = []
for template in get_all_templates():
if 'Gem' in template:
gem_templates.append(template)
return gem_templates
def get_generic_templates(): # temporary until we have a better way to do this... maybe template_type element
generic_templates = []
for template in get_all_templates():
if 'Project' not in template and 'Gem' not in template:
generic_templates.append(template)
return generic_templates
def get_all_restricted() -> list:
engine_restricted = get_engine_restricted()
restricted_data = get_restricted()
restricted_data.extend(engine_restricted)
return restricted_data
def find_engine_data(json_data: dict,
engine_path: str or pathlib.Path = None) -> dict or None:
if not engine_path:
engine_path = get_this_engine_path()
engine_path = pathlib.Path(engine_path).resolve()
for engine_object in json_data['engines']:
engine_object_path = pathlib.Path(engine_object['path']).resolve()
if engine_path == engine_object_path:
return engine_object
return None
def get_engine_json_data(engine_name: str = None,
engine_path: str or pathlib.Path = None) -> dict or None:
if not engine_name and not engine_path:
logger.error('Must specify either a Engine name or Engine Path.')
return None
if engine_name and not engine_path:
engine_path = get_registered(engine_name=engine_name)
if not engine_path:
logger.error(f'Engine Path {engine_path} has not been registered.')
return None
engine_path = pathlib.Path(engine_path).resolve()
engine_json = engine_path / 'engine.json'
if not engine_json.is_file():
logger.error(f'Engine json {engine_json} is not present.')
return None
if not validation.valid_o3de_engine_json(engine_json):
logger.error(f'Engine json {engine_json} is not valid.')
return None
with engine_json.open('r') as f:
try:
engine_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{engine_json} failed to load: {str(e)}')
else:
return engine_json_data
return None
def get_project_json_data(project_name: str = None,
project_path: str or pathlib.Path = None) -> dict or None:
if not project_name and not project_path:
logger.error('Must specify either a Project name or Project Path.')
return None
if project_name and not project_path:
project_path = get_registered(project_name=project_name)
if not project_path:
logger.error(f'Project Path {project_path} has not been registered.')
return None
project_path = pathlib.Path(project_path).resolve()
project_json = project_path / 'project.json'
if not project_json.is_file():
logger.error(f'Project json {project_json} is not present.')
return None
if not validation.valid_o3de_project_json(project_json):
logger.error(f'Project json {project_json} is not valid.')
return None
with project_json.open('r') as f:
try:
project_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{project_json} failed to load: {str(e)}')
else:
return project_json_data
return None
def get_gem_json_data(gem_name: str = None,
gem_path: str or pathlib.Path = None) -> dict or None:
if not gem_name and not gem_path:
logger.error('Must specify either a Gem name or Gem Path.')
return None
if gem_name and not gem_path:
gem_path = get_registered(gem_name=gem_name)
if not gem_path:
logger.error(f'Gem Path {gem_path} has not been registered.')
return None
gem_path = pathlib.Path(gem_path).resolve()
gem_json = gem_path / 'gem.json'
if not gem_json.is_file():
logger.error(f'Gem json {gem_json} is not present.')
return None
if not validation.valid_o3de_gem_json(gem_json):
logger.error(f'Gem json {gem_json} is not valid.')
return None
with gem_json.open('r') as f:
try:
gem_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{gem_json} failed to load: {str(e)}')
else:
return gem_json_data
return None
def get_template_json_data(template_name: str = None,
template_path: str or pathlib.Path = None) -> dict or None:
if not template_name and not template_path:
logger.error('Must specify either a Template name or Template Path.')
return None
if template_name and not template_path:
template_path = get_registered(template_name=template_name)
if not template_path:
logger.error(f'Template Path {template_path} has not been registered.')
return None
template_path = pathlib.Path(template_path).resolve()
template_json = template_path / 'template.json'
if not template_json.is_file():
logger.error(f'Template json {template_json} is not present.')
return None
if not validation.valid_o3de_template_json(template_json):
logger.error(f'Template json {template_json} is not valid.')
return None
with template_json.open('r') as f:
try:
template_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{template_json} failed to load: {str(e)}')
else:
return template_json_data
return None
def get_restricted_data(restricted_name: str = None,
restricted_path: str or pathlib.Path = None) -> dict or None:
if not restricted_name and not restricted_path:
logger.error('Must specify either a Restricted name or Restricted Path.')
return None
if restricted_name and not restricted_path:
restricted_path = get_registered(restricted_name=restricted_name)
if not restricted_path:
logger.error(f'Restricted Path {restricted_path} has not been registered.')
return None
restricted_path = pathlib.Path(restricted_path).resolve()
restricted_json = restricted_path / 'restricted.json'
if not restricted_json.is_file():
logger.error(f'Restricted json {restricted_json} is not present.')
return None
if not validation.valid_o3de_restricted_json(restricted_json):
logger.error(f'Restricted json {restricted_json} is not valid.')
return None
with restricted_json.open('r') as f:
try:
restricted_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{restricted_json} failed to load: {str(e)}')
else:
return restricted_json_data
return None
def get_registered(engine_name: str = None,
project_name: str = None,
gem_name: str = None,
template_name: str = None,
default_folder: str = None,
repo_name: str = None,
restricted_name: str = None) -> pathlib.Path or None:
json_data = load_o3de_manifest()
# check global first then this engine
if isinstance(engine_name, str):
for engine in json_data['engines']:
engine_path = pathlib.Path(engine['path']).resolve()
engine_json = engine_path / 'engine.json'
with engine_json.open('r') as f:
try:
engine_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{engine_json} failed to load: {str(e)}')
else:
this_engines_name = engine_json_data['engine_name']
if this_engines_name == engine_name:
return engine_path
elif isinstance(project_name, str):
enging_projects = get_engine_projects()
projects = json_data['projects'].copy()
projects.extend(engine_object['projects'])
for project_path in projects:
project_path = pathlib.Path(project_path).resolve()
project_json = project_path / 'project.json'
with project_json.open('r') as f:
try:
project_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{project_json} failed to load: {str(e)}')
else:
this_projects_name = project_json_data['project_name']
if this_projects_name == project_name:
return project_path
elif isinstance(gem_name, str):
gems = get_all_gems()
for gem_path in gems:
gem_path = pathlib.Path(gem_path).resolve()
gem_json = gem_path / 'gem.json'
with gem_json.open('r') as f:
try:
gem_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{gem_json} failed to load: {str(e)}')
else:
this_gems_name = gem_json_data['gem_name']
if this_gems_name == gem_name:
return gem_path
elif isinstance(template_name, str):
engine_templates = get_engine_templates()
templates = json_data['templates'].copy()
templates.extend(engine_templates)
for template_path in templates:
template_path = pathlib.Path(template_path).resolve()
template_json = template_path / 'template.json'
with template_json.open('r') as f:
try:
template_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{template_path} failed to load: {str(e)}')
else:
this_templates_name = template_json_data['template_name']
if this_templates_name == template_name:
return template_path
elif isinstance(restricted_name, str):
engine_restricted = get_engine_restricted()
restricted = json_data['restricted'].copy()
restricted.extend(engine_restricted)
for restricted_path in restricted:
restricted_path = pathlib.Path(restricted_path).resolve()
restricted_json = restricted_path / 'restricted.json'
with restricted_json.open('r') as f:
try:
restricted_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{restricted_json} failed to load: {str(e)}')
else:
this_restricted_name = restricted_json_data['restricted_name']
if this_restricted_name == restricted_name:
return restricted_path
elif isinstance(default_folder, str):
if default_folder == 'engines':
default_engines_folder = pathlib.Path(json_data['default_engines_folder']).resolve()
return default_engines_folder
elif default_folder == 'projects':
default_projects_folder = pathlib.Path(json_data['default_projects_folder']).resolve()
return default_projects_folder
elif default_folder == 'gems':
default_gems_folder = pathlib.Path(json_data['default_gems_folder']).resolve()
return default_gems_folder
elif default_folder == 'templates':
default_templates_folder = pathlib.Path(json_data['default_templates_folder']).resolve()
return default_templates_folder
elif default_folder == 'restricted':
default_restricted_folder = pathlib.Path(json_data['default_restricted_folder']).resolve()
return default_restricted_folder
elif isinstance(repo_name, str):
cache_folder = get_o3de_cache_folder()
for repo_uri in json_data['repos']:
repo_uri = pathlib.Path(repo_uri).resolve()
repo_sha256 = hashlib.sha256(repo_uri.encode())
cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json')
if cache_file.is_file():
repo = pathlib.Path(cache_file).resolve()
with repo.open('r') as f:
try:
repo_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{cache_file} failed to load: {str(e)}')
else:
this_repos_name = repo_json_data['repo_name']
if this_repos_name == repo_name:
return repo_uri
return None
+490
View File
@@ -0,0 +1,490 @@
#
# 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 hashlib
import logging
import sys
import urllib.parse
from o3de import manifest, validation
logger = logging.getLogger()
logging.basicConfig()
def print_this_engine(verbose: int) -> None:
engine_data = manifest.get_this_engine()
print(json.dumps(engine_data, indent=4))
if verbose > 0:
print_engines_data(engine_data)
def print_engines(verbose: int) -> None:
engines_data = manifest.get_engines()
print(json.dumps(engines_data, indent=4))
if verbose > 0:
print_engines_data(engines_data)
def print_projects(verbose: int) -> None:
projects_data = manifest.get_projects()
print(json.dumps(projects_data, indent=4))
if verbose > 0:
print_projects_data(projects_data)
def print_gems(verbose: int) -> None:
gems_data = manifest.get_gems()
print(json.dumps(gems_data, indent=4))
if verbose > 0:
print_gems_data(gems_data)
def print_templates(verbose: int) -> None:
templates_data = manifest.get_templates()
print(json.dumps(templates_data, indent=4))
if verbose > 0:
print_templates_data(templates_data)
def print_restricted(verbose: int) -> None:
restricted_data = manifest.get_restricted()
print(json.dumps(restricted_data, indent=4))
if verbose > 0:
print_restricted_data(restricted_data)
def print_engine_projects(verbose: int) -> None:
engine_projects_data = manifest.get_engine_projects()
print(json.dumps(engine_projects_data, indent=4))
if verbose > 0:
print_projects_data(engine_projects_data)
def print_engine_gems(verbose: int) -> None:
engine_gems_data = manifest.get_engine_gems()
print(json.dumps(engine_gems_data, indent=4))
if verbose > 0:
print_gems_data(engine_gems_data)
def print_engine_templates(verbose: int) -> None:
engine_templates_data = manifest.get_engine_templates()
print(json.dumps(engine_templates_data, indent=4))
if verbose > 0:
print_templates_data(engine_templates_data)
def print_engine_restricted(verbose: int) -> None:
engine_restricted_data = manifest.get_engine_restricted()
print(json.dumps(engine_restricted_data, indent=4))
if verbose > 0:
print_restricted_data(engine_restricted_data)
def print_engine_external_subdirectories(verbose: int) -> None:
external_subdirs_data = manifest.get_engine_external_subdirectories()
print(json.dumps(external_subdirs_data, indent=4))
def print_all_projects(verbose: int) -> None:
all_projects_data = manifest.get_all_projects()
print(json.dumps(all_projects_data, indent=4))
if verbose > 0:
print_projects_data(all_projects_data)
def print_all_gems(verbose: int) -> None:
all_gems_data = manifest.get_all_gems()
print(json.dumps(all_gems_data, indent=4))
if verbose > 0:
print_gems_data(all_gems_data)
def print_all_templates(verbose: int) -> None:
all_templates_data = manifest.get_all_templates()
print(json.dumps(all_templates_data, indent=4))
if verbose > 0:
print_templates_data(all_templates_data)
def print_all_restricted(verbose: int) -> None:
all_restricted_data = manifest.get_all_restricted()
print(json.dumps(all_restricted_data, indent=4))
if verbose > 0:
print_restricted_data(all_restricted_data)
def print_engines_data(engines_data: dict) -> None:
print('\n')
print("Engines================================================")
for engine_object in engines_data:
# if it's not local it should be in the cache
engine_uri = engine_object['path']
parsed_uri = urllib.parse.urlparse(engine_uri)
if parsed_uri.scheme == 'http' or \
parsed_uri.scheme == 'https' or \
parsed_uri.scheme == 'ftp' or \
parsed_uri.scheme == 'ftps':
repo_sha256 = hashlib.sha256(engine_uri.encode())
cache_folder = manifest.get_o3de_cache_folder()
engine = cache_folder / str(repo_sha256.hexdigest() + '.json')
print(f'{engine_uri}/engine.json cached as:')
else:
engine_json = pathlib.Path(engine_uri).resolve() / 'engine.json'
with engine_json.open('r') as f:
try:
engine_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{engine_json} failed to load: {str(e)}')
else:
print(engine_json)
print(json.dumps(engine_json_data, indent=4))
print('\n')
def print_projects_data(projects_data: dict) -> None:
print('\n')
print("Projects================================================")
for project_uri in projects_data:
# if it's not local it should be in the cache
parsed_uri = urllib.parse.urlparse(project_uri)
if parsed_uri.scheme == 'http' or \
parsed_uri.scheme == 'https' or \
parsed_uri.scheme == 'ftp' or \
parsed_uri.scheme == 'ftps':
repo_sha256 = hashlib.sha256(project_uri.encode())
cache_folder = manifest.get_o3de_cache_folder()
project_json = cache_folder / str(repo_sha256.hexdigest() + '.json')
else:
project_json = pathlib.Path(project_uri).resolve() / 'project.json'
with project_json.open('r') as f:
try:
project_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{project_json} failed to load: {str(e)}')
else:
print(project_json)
print(json.dumps(project_json_data, indent=4))
print('\n')
def print_gems_data(gems_data: dict) -> None:
print('\n')
print("Gems================================================")
for gem_uri in gems_data:
# if it's not local it should be in the cache
parsed_uri = urllib.parse.urlparse(gem_uri)
if parsed_uri.scheme == 'http' or \
parsed_uri.scheme == 'https' or \
parsed_uri.scheme == 'ftp' or \
parsed_uri.scheme == 'ftps':
repo_sha256 = hashlib.sha256(gem_uri.encode())
cache_folder = manifest.get_o3de_cache_folder()
gem_json = cache_folder / str(repo_sha256.hexdigest() + '.json')
else:
gem_json = pathlib.Path(gem_uri).resolve() / 'gem.json'
with gem_json.open('r') as f:
try:
gem_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{gem_json} failed to load: {str(e)}')
else:
print(gem_json)
print(json.dumps(gem_json_data, indent=4))
print('\n')
def print_templates_data(templates_data: dict) -> None:
print('\n')
print("Templates================================================")
for template_uri in templates_data:
# if it's not local it should be in the cache
parsed_uri = urllib.parse.urlparse(template_uri)
if parsed_uri.scheme == 'http' or \
parsed_uri.scheme == 'https' or \
parsed_uri.scheme == 'ftp' or \
parsed_uri.scheme == 'ftps':
repo_sha256 = hashlib.sha256(template_uri.encode())
cache_folder = manifest.get_o3de_cache_folder()
template_json = cache_folder / str(repo_sha256.hexdigest() + '.json')
else:
template_json = pathlib.Path(template_uri).resolve() / 'template.json'
with template_json.open('r') as f:
try:
template_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{template_json} failed to load: {str(e)}')
else:
print(template_json)
print(json.dumps(template_json_data, indent=4))
print('\n')
def print_repos_data(repos_data: dict) -> None:
print('\n')
print("Repos================================================")
cache_folder = manifest.get_o3de_cache_folder()
for repo_uri in repos_data:
repo_sha256 = hashlib.sha256(repo_uri.encode())
cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json')
if validation.valid_o3de_repo_json(cache_file):
with cache_file.open('r') as s:
try:
repo_json_data = json.load(s)
except json.JSONDecodeError as e:
logger.warn(f'{cache_file} failed to load: {str(e)}')
else:
print(f'{repo_uri}/repo.json cached as:')
print(cache_file)
print(json.dumps(repo_json_data, indent=4))
print('\n')
def print_restricted_data(restricted_data: dict) -> None:
print('\n')
print("Restricted================================================")
for restricted_path in restricted_data:
restricted_json = pathlib.Path(restricted_path).resolve() / 'restricted.json'
with restricted_json.open('r') as f:
try:
restricted_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{restricted_json} failed to load: {str(e)}')
else:
print(restricted_json)
print(json.dumps(restricted_json_data, indent=4))
print('\n')
def register_show_repos(verbose: int) -> None:
repos_data = get_repos()
print(json.dumps(repos_data, indent=4))
if verbose > 0:
print_repos_data(repos_data)
def register_show(verbose: int) -> None:
json_data = manifest.load_o3de_manifest()
print(f"{manifest.get_o3de_manifest()}:")
print(json.dumps(json_data, indent=4))
if verbose > 0:
print_engines_data(manifest.get_engines())
print_projects_data(manifest.get_all_projects())
print_gems_data(manifest.get_gems())
print_templates_data(manifest.get_all_templates())
print_restricted_data(manifest.get_all_restricted())
print_repos_data(manifest.get_repos())
def _run_register_show(args: argparse) -> int:
if args.override_home_folder:
manifest.override_home_folder = args.override_home_folder
if args.this_engine:
print_this_engine(args.verbose)
return 0
elif args.engines:
print_engines(args.verbose)
return 0
elif args.projects:
print_projects(args.verbose)
return 0
elif args.gems:
print_gems(args.verbose)
return 0
elif args.templates:
print_templates(args.verbose)
return 0
elif args.repos:
register_show_repos(args.verbose)
return 0
elif args.restricted:
print_restricted(args.verbose)
return 0
elif args.engine_projects:
print_engine_projects(args.verbose)
return 0
elif args.engine_gems:
print_engine_gems(args.verbose)
return 0
elif args.engine_templates:
print_engine_templates(args.verbose)
return 0
elif args.engine_restricted:
print_engine_restricted(args.verbose)
return 0
elif args.engine_external_subdirectories:
print_engine_external_subdirectories(args.verbose)
return 0
elif args.all_projects:
print_all_projects(args.verbose)
return 0
elif args.all_gems:
print_all_gems(args.verbose)
return 0
elif args.all_templates:
print_all_templates(args.verbose)
return 0
elif args.all_restricted:
print_all_restricted(args.verbose)
return 0
elif args.downloadables:
print_downloadables(args.verbose)
return 0
if args.downloadable_engines:
print_downloadable_engines(args.verbose)
return 0
elif args.downloadable_projects:
print_downloadable_projects(args.verbose)
return 0
elif args.downloadable_gems:
print_downloadable_gems(args.verbose)
return 0
elif args.downloadable_templates:
print_downloadable_templates(args.verbose)
return 0
else:
register_show(args.verbose)
return 0
def add_parser_args(parser):
"""
add_parser_args is called to add arguments to each command such that it can be
invoked locally or added by a central python file.
Ex. Directly run from this file alone with: python print_registration.py --engine-projects
:param parser: the caller passes an argparse parser like instance to this method
"""
group = parser.add_mutually_exclusive_group(required=False)
group.add_argument('-te', '--this-engine', action='store_true', required=False,
default=False,
help='Just the local engines.')
group.add_argument('-e', '--engines', action='store_true', required=False,
default=False,
help='Just the local engines.')
group.add_argument('-p', '--projects', action='store_true', required=False,
default=False,
help='Just the local projects.')
group.add_argument('-g', '--gems', action='store_true', required=False,
default=False,
help='Just the local gems.')
group.add_argument('-t', '--templates', action='store_true', required=False,
default=False,
help='Just the local templates.')
group.add_argument('-r', '--repos', action='store_true', required=False,
default=False,
help='Just the local repos. Ignores repos.')
group.add_argument('-rs', '--restricted', action='store_true', required=False,
default=False,
help='The local restricted folders.')
group.add_argument('-ep', '--engine-projects', action='store_true', required=False,
default=False,
help='Just the local projects. Ignores repos.')
group.add_argument('-eg', '--engine-gems', action='store_true', required=False,
default=False,
help='Just the local gems. Ignores repos')
group.add_argument('-et', '--engine-templates', action='store_true', required=False,
default=False,
help='Just the local templates. Ignores repos.')
group.add_argument('-ers', '--engine-restricted', action='store_true', required=False,
default=False,
help='The restricted folders.')
group.add_argument('-x', '--engine-external-subdirectories', action='store_true', required=False,
default=False,
help='The external subdirectories.')
group.add_argument('-ap', '--all-projects', action='store_true', required=False,
default=False,
help='Just the local projects. Ignores repos.')
group.add_argument('-ag', '--all-gems', action='store_true', required=False,
default=False,
help='Just the local gems. Ignores repos')
group.add_argument('-at', '--all-templates', action='store_true', required=False,
default=False,
help='Just the local templates. Ignores repos.')
group.add_argument('-ars', '--all-restricted', action='store_true', required=False,
default=False,
help='The restricted folders.')
group.add_argument('-d', '--downloadables', action='store_true', required=False,
default=False,
help='Combine all repos into a single list of resources.')
group.add_argument('-de', '--downloadable-engines', action='store_true', required=False,
default=False,
help='Combine all repos engines into a single list of resources.')
group.add_argument('-dp', '--downloadable-projects', action='store_true', required=False,
default=False,
help='Combine all repos projects into a single list of resources.')
group.add_argument('-dg', '--downloadable-gems', action='store_true', required=False,
default=False,
help='Combine all repos gems into a single list of resources.')
group.add_argument('-dt', '--downloadable-templates', action='store_true', required=False,
default=False,
help='Combine all repos templates into a single list of resources.')
parser.add_argument('-v', '--verbose', action='count', required=False,
default=0,
help='How verbose do you want the output to be.')
parser.add_argument('-ohf', '--override-home-folder', type=str, required=False,
help='By default the home folder is the user folder, override it to this folder.')
parser.set_defaults(func=_run_register_show)
def add_args(subparsers) -> None:
"""
add_args is called to add subparsers arguments to each command such that it can be
a central python file such as o3de.py.
It can be run from the o3de.py script as follows
call add_args and execute: python o3de.py register-show --engine-projects
:param subparsers: the caller instantiates subparsers and passes it in here
"""
register_show_subparser = subparsers.add_parser('register-show')
add_parser_args(register_show_subparser)
def main():
"""
Runs print_registration.py script as standalone script
"""
# parse the command line args
the_parser = argparse.ArgumentParser()
# add subparsers
# add args to the parser
add_parser_args(the_parser)
# parse args
the_args = the_parser.parse_args()
# run
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
# return
sys.exit(ret)
if __name__ == "__main__":
main()
+864
View File
@@ -0,0 +1,864 @@
#
# 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 file contains all the code that has to do with registering engines, projects, gems and templates
"""
import argparse
import hashlib
import logging
import json
import os
import pathlib
import shutil
import sys
import urllib.parse
import urllib.request
from o3de import get_registration, manifest, repo, utils, validation
logger = logging.getLogger()
logging.basicConfig()
def register_shipped_engine_o3de_objects(force: bool = False) -> int:
engine_path = manifest.get_this_engine_path()
ret_val = 0
# register anything in the users default folders globally
error_code = register_all_engines_in_folder(manifest.get_registered(default_folder='engines'), force=force)
if error_code:
ret_val = error_code
error_code = register_all_projects_in_folder(manifest.get_registered(default_folder='projects'))
if error_code:
ret_val = error_code
error_code = register_all_gems_in_folder(manifest.get_registered(default_folder='gems'))
if error_code:
ret_val = error_code
error_code = register_all_templates_in_folder(manifest.get_registered(default_folder='templates'))
if error_code:
ret_val = error_code
error_code = register_all_restricted_in_folder(manifest.get_registered(default_folder='restricted'))
if error_code:
ret_val = error_code
error_code = register_all_restricted_in_folder(manifest.get_registered(default_folder='projects'))
if error_code:
ret_val = error_code
error_code = register_all_restricted_in_folder(manifest.get_registered(default_folder='gems'))
if error_code:
ret_val = error_code
error_code = register_all_restricted_in_folder(manifest.get_registered(default_folder='templates'))
if error_code:
ret_val = error_code
return ret_val
def register_all_in_folder(folder_path: str or pathlib.Path,
remove: bool = False,
engine_path: str or pathlib.Path = None,
exclude: list = None) -> int:
if not folder_path:
logger.error(f'Folder path cannot be empty.')
return 1
folder_path = pathlib.Path(folder_path).resolve()
if not folder_path.is_dir():
logger.error(f'Folder path is not dir.')
return 1
engines_set = set()
projects_set = set()
gems_set = set()
templates_set = set()
restricted_set = set()
repo_set = set()
ret_val = 0
for root, dirs, files in os.walk(folder_path):
if root in exclude:
continue
for name in files:
if name == 'engine.json':
engines_set.add(root)
elif name == 'project.json':
projects_set.add(root)
elif name == 'gem.json':
gems_set.add(root)
elif name == 'template.json':
templates_set.add(root)
elif name == 'restricted.json':
restricted_set.add(root)
elif name == 'repo.json':
repo_set.add(root)
for engine in sorted(engines_set, reverse=True):
error_code = register(engine_path=engine, remove=remove)
if error_code:
ret_val = error_code
for project in sorted(projects_set, reverse=True):
error_code = register(engine_path=engine_path, project_path=project, remove=remove)
if error_code:
ret_val = error_code
for gem in sorted(gems_set, reverse=True):
error_code = register(engine_path=engine_path, gem_path=gem, remove=remove)
if error_code:
ret_val = error_code
for template in sorted(templates_set, reverse=True):
error_code = register(engine_path=engine_path, template_path=template, remove=remove)
if error_code:
ret_val = error_code
for restricted in sorted(restricted_set, reverse=True):
error_code = register(engine_path=engine_path, restricted_path=restricted, remove=remove)
if error_code:
ret_val = error_code
for repo in sorted(repo_set, reverse=True):
error_code = register(engine_path=engine_path, repo_uri=repo, remove=remove)
if error_code:
ret_val = error_code
return ret_val
def register_all_o3de_objects_of_type_in_folder(o3de_object_path: str or pathlib.Path,
o3de_object_type: str,
remove: bool,
force: bool,
**register_kwargs) -> int:
if not o3de_object_path:
logger.error(f'Engines path cannot be empty.')
return 1
o3de_object_path = pathlib.Path(o3de_object_path).resolve()
if not o3de_object_path.is_dir():
logger.error(f'Engines path is not dir.')
return 1
o3de_object_type_set = set()
register_path_kwarg = f'{o3de_object_type}_path' if o3de_object_type != 'repo' else f'{o3de_object_type}_uri'
ret_val = 0
for root, dirs, files in os.walk(o3de_object_path):
if f'{o3de_object_type}.json' in files:
o3de_object_type_set.add(root)
# Stop iteration of any subdirectories
# Nested o3de objects of the same type aren't supported(i.e an engine cannot be inside of a engine).
dirs[:] = []
for o3de_object_type_root in sorted(o3de_object_type_set, reverse=True):
error_code = register(**{register_path_kwarg: o3de_object_type_root},
remove=remove, force=force, **register_kwargs)
if error_code:
ret_val = error_code
return ret_val
def register_all_engines_in_folder(engines_path: str or pathlib.Path,
remove: bool = False,
force: bool = False) -> int:
return register_all_o3de_objects_of_type_in_folder(engines_path, 'engine', remove, force)
def register_all_projects_in_folder(projects_path: str or pathlib.Path,
remove: bool = False,
engine_path: str or pathlib.Path = None) -> int:
return register_all_o3de_objects_of_type_in_folder(projects_path, 'project', remove, False, engine_path=engine_path)
def register_all_gems_in_folder(gems_path: str or pathlib.Path,
remove: bool = False,
engine_path: pathlib.Path = None,
project_path: pathlib.Path = None) -> int:
return register_all_o3de_objects_of_type_in_folder(gems_path, 'gem', remove, False, engine_path=engine_path)
def register_all_templates_in_folder(templates_path: str or pathlib.Path,
remove: bool = False,
engine_path: str or pathlib.Path = None) -> int:
return register_all_o3de_objects_of_type_in_folder(templates_path, 'template', remove, False, engine_path=engine_path)
def register_all_restricted_in_folder(restricted_path: str or pathlib.Path,
remove: bool = False,
engine_path: str or pathlib.Path = None) -> int:
return register_all_o3de_objects_of_type_in_folder(restricted_path, 'restricted', remove, False, engine_path=engine_path)
def register_all_repos_in_folder(repos_path: str or pathlib.Path,
remove: bool = False,
engine_path: str or pathlib.Path = None) -> int:
return register_all_o3de_objects_of_type_in_folder(repos_path, 'repo', remove, force, engine_path=engine_path)
def remove_engine_name_to_path(json_data: dict,
engine_path: pathlib.Path) -> int:
"""
Remove the engine at the specified path if it exist in the o3de manifest
:param json_data in-memory json view of the o3de_manifest.json data
:param engine_path path to engine to remove from the manifest data
returns 0 to indicate no issues has occurred with removal
"""
if engine_path.is_dir() and validation.valid_o3de_engine_json(engine_path):
engine_json_data = manifest.get_engine_json_data(engine_path=engine_path)
if 'engine_name' in engine_json_data and 'engines_path' in json_data:
engine_name = engine_json_data['engine_name']
try:
del json_data['engines_path'][engine_name]
except KeyError:
# Attempting to remove a non-existent engine_name is fine
pass
return 0
def add_engine_name_to_path(json_data: dict, engine_path: pathlib.Path, force: bool):
# Add an engine path JSON object which maps the "engine_name" -> "engine_path"
engine_json_data = manifest.get_engine_json_data(engine_path=engine_path)
if not engine_json_data:
logger.error(f'Unable to retrieve json data from engine.json at path {engine_path.as_posix()}')
return 1
engines_path_json = json_data.setdefault('engines_path', {})
if 'engine_name' not in engine_json_data:
logger.error(f'engine.json at path {engine_path.as_posix()} is missing "engine_name" key')
return 1
engine_name = engine_json_data['engine_name']
if not force and engine_name in engines_path_json and \
pathlib.PurePath(engines_path_json[engine_name]) != engine_path:
logger.error(
f'Attempting to register existing engine "{engine_name}" with a new path of {engine_path.as_posix()}.'
f' The current path is {pathlib.Path(engines_path_json[engine_name]).as_posix()}.'
f' To force registration of a new engine path, specify the -f/--force option.')
return 1
engines_path_json[engine_name] = engine_path.as_posix()
return 0
def register_engine_path(json_data: dict,
engine_path: str or pathlib.Path,
remove: bool = False,
force: bool = False) -> int:
if not engine_path:
logger.error(f'Engine path cannot be empty.')
return 1
engine_path = pathlib.Path(engine_path).resolve()
for engine_object in json_data.get('engines', {}):
engine_object_path = pathlib.Path(engine_object['path']).resolve()
if engine_object_path == engine_path:
json_data['engines'].remove(engine_object)
if remove:
return remove_engine_name_to_path(json_data, engine_path)
if not engine_path.is_dir():
logger.error(f'Engine path {engine_path} does not exist.')
return 1
engine_json = engine_path / 'engine.json'
if not validation.valid_o3de_engine_json(engine_json):
logger.error(f'Engine json {engine_json} is not valid.')
return 1
engine_object = {}
engine_object.update({'path': engine_path.as_posix()})
json_data.setdefault('engines', []).insert(0, engine_object)
return add_engine_name_to_path(json_data, engine_path, force)
def register_o3de_object_path(json_data: dict,
o3de_object_path: str or pathlib.Path,
o3de_object_key: str,
o3de_json_filename: str,
validation_func: callable,
remove: bool = False,
engine_path: pathlib.Path = None,
project_path: pathlib.Path = None) -> int:
# save_path variable is used to save the changes to the store the path to the file to save
# if the registration is for the project or engine
save_path = None
if not o3de_object_path:
logger.error(f'o3de object path cannot be empty.')
return 1
o3de_object_path = pathlib.Path(o3de_object_path).resolve()
if engine_path and project_path:
logger.error(f'Both a project path: {project_path} and engine path: {engine_path} has been supplied.'
'A subdirectory can only be registered to either the engine path or project in one command')
manifest_data = None
if engine_path:
manifest_data = manifest.get_engine_json_data(json_data, engine_path)
if not manifest_data:
logger.error(f'Cannot load engine.json data at path {engine_path}')
return 1
save_path = engine_path / 'engine.json'
elif project_path:
manifest_data = manifest.get_project_json_data(json_data, project_path)
if not manifest_data:
logger.error(f'Cannot load project.json data at path {project_path}')
return 1
save_path = project_path / 'project.json'
else:
manifest_data = json_data
paths_to_remove = [o3de_object_path]
if save_path:
try:
paths_to_remove.append(o3de_object_path.relative_to(save_path.parent))
except ValueError:
pass # It is OK relative path cannot be formed
manifest_data[o3de_object_key] = list(filter(lambda p: pathlib.Path(p) not in paths_to_remove,
manifest_data.setdefault(o3de_object_key, [])))
if remove:
if save_path:
manifest.save_o3de_manifest(manifest_data, save_path)
return 0
if not o3de_object_path.is_dir():
logger.error(f'o3de object path {o3de_object_path} does not exist.')
return 1
manifest_json_path = o3de_object_path / o3de_json_filename
if validation_func and not validation_func(manifest_json_path):
logger.error(f'o3de json {manifest_json_path} is not valid.')
return 1
# if there is a save path make it relative the directory containing o3de object json file
if save_path:
try:
o3de_object_path = o3de_object_path.relative_to(save_path.parent)
except ValueError:
pass # It is OK relative path cannot be formed
manifest_data[o3de_object_key].insert(0, o3de_object_path.as_posix())
if save_path:
manifest.save_o3de_manifest(manifest_data, save_path)
return 0
def register_external_subdirectory(json_data: dict,
external_subdir_path: str or pathlib.Path,
remove: bool = False,
engine_path: pathlib.Path = None,
project_path: pathlib.Path = None) -> int:
"""
:return An integer return code indicating whether registration or removal of the external subdirectory
completed successfully
"""
return register_o3de_object_path(json_data, external_subdir_path, 'external_subdirectories', '', None, remove,
engine_path, project_path)
def register_gem_path(json_data: dict,
gem_path: str or pathlib.Path,
remove: bool = False,
engine_path: pathlib.Path = None,
project_path: pathlib.Path = None) -> int:
return register_o3de_object_path(json_data, gem_path, 'external_subdirectories', 'gem.json',
validation.valid_o3de_gem_json, remove, engine_path, project_path)
def register_project_path(json_data: dict,
project_path: str or pathlib.Path,
remove: bool = False,
engine_path: str or pathlib.Path = None) -> int:
result = register_o3de_object_path(json_data, project_path, 'projects', 'project.json',
validation.valid_o3de_project_json, remove, engine_path, None)
if result != 0:
return result
# registering a project has the additional step of setting the project.json 'engine' field
this_engine_json = manifest.get_engine_json_data(engine_path=manifest.get_this_engine_path())
if not this_engine_json:
return 1
project_json_data = manifest.get_project_json_data(project_path=project_path)
if not project_json_data:
return 1
update_project_json = False
try:
update_project_json = project_json_data['engine'] != this_engine_json['engine_name']
except KeyError as e:
update_project_json = True
if update_project_json:
project_json_data['engine'] = this_engine_json['engine_name']
utils.backup_file(project_json)
if not manifest.save_o3de_manifest(project_json_data, project_path):
return 1
return 0
def register_template_path(json_data: dict,
template_path: str or pathlib.Path,
remove: bool = False,
engine_path: str or pathlib.Path = None) -> int:
return register_o3de_object_path(json_data, template_path, 'templates', 'template.json',
validation.valid_o3de_template_json, remove, engine_path, None)
def register_restricted_path(json_data: dict,
restricted_path: str or pathlib.Path,
remove: bool = False,
engine_path: str or pathlib.Path = None) -> int:
return register_o3de_object_path(json_data, restricted_path, 'restricted', 'restricted.json',
validation.valid_o3de_restricted_json, remove, engine_path, None)
def register_repo(json_data: dict,
repo_uri: str or pathlib.Path,
remove: bool = False) -> int:
if not repo_uri:
logger.error(f'Repo URI cannot be empty.')
return 1
url = f'{repo_uri}/repo.json'
parsed_uri = urllib.parse.urlparse(url)
if parsed_uri.scheme in ['http', 'https', 'ftp', 'ftps']:
while repo_uri in json_data['repos']:
json_data['repos'].remove(repo_uri)
else:
repo_uri = pathlib.Path(repo_uri).resolve()
while repo_uri.as_posix() in json_data['repos']:
json_data['repos'].remove(repo_uri.as_posix())
if remove:
logger.warn(f'Removing repo uri {repo_uri}.')
return 0
repo_sha256 = hashlib.sha256(url.encode())
cache_file = manifest.get_o3de_cache_folder() / str(repo_sha256.hexdigest() + '.json')
result = utils.download_file(url, cache_file)
if result == 0:
json_data['repos'].insert(0, repo_uri.as_posix())
result = repo.process_add_o3de_repo(cache_file, repo_set)
return result
def register_default_o3de_object_folder(json_data: dict,
default_o3de_object_folder: str or pathlib.Path,
o3de_object_key: str) -> int:
# make sure the path exists
default_o3de_object_folder = pathlib.Path(default_o3de_object_folder).resolve()
if not default_o3de_object_folder.is_dir():
logger.error(f'Default o3de object folder {default_o3de_object_folder} does not exist.')
return 1
json_data[o3de_object_key] = default_o3de_object_folder.as_posix()
return 0
def register_default_engines_folder(json_data: dict,
default_engines_folder: str or pathlib.Path,
remove: bool = False) -> int:
return register_default_o3de_object_folder(json_data,
manifest.get_o3de_engines_folder() if remove else default_engines_folder,
'default_engines_folder', remove)
def register_default_projects_folder(json_data: dict,
default_projects_folder: str or pathlib.Path,
remove: bool = False) -> int:
return register_default_o3de_object_folder(json_data,
manifest.get_o3de_projects_folder() if remove else default_projects_folder,
'default_projects_folder', remove)
def register_default_gems_folder(json_data: dict,
default_gems_folder: str or pathlib.Path,
remove: bool = False) -> int:
return register_default_o3de_object_folder(json_data,
manifest.get_o3de_gems_folder() if remove else default_gems_folder,
'default_gems_folder', remove)
def register_default_templates_folder(json_data: dict,
default_templates_folder: str or pathlib.Path,
remove: bool = False) -> int:
return register_default_o3de_object_folder(json_data,
manifest.get_o3de_templates_folder() if remove else default_templates_folder,
'default_templates_folder', remove)
def register_default_restricted_folder(json_data: dict,
default_restricted_folder: str or pathlib.Path,
reset_to_default: bool = False) -> int:
return register_default_o3de_object_folder(json_data,
manifest.get_o3de_restricted_folder() if remove else default_restricted_folder,
'default_restricted_folder', remove)
def register(engine_path: str or pathlib.Path = None,
project_path: str or pathlib.Path = None,
gem_path: str or pathlib.Path = None,
external_subdir_path: str or pathlib.Path = None,
template_path: str or pathlib.Path = None,
restricted_path: str or pathlib.Path = None,
repo_uri: str or pathlib.Path = None,
default_engines_folder: str or pathlib.Path = None,
default_projects_folder: str or pathlib.Path = None,
default_gems_folder: str or pathlib.Path = None,
default_templates_folder: str or pathlib.Path = None,
default_restricted_folder: str or pathlib.Path = None,
external_subdir_engine_path: pathlib.Path = None,
external_subdir_project_path: pathlib.Path = None,
remove: bool = False,
force: bool = False
) -> int:
"""
Adds/Updates entries to the ~/.o3de/o3de_manifest.json
:param engine_path: if engine folder is supplied the path will be added to the engine if it can, if not global
:param project_path: project folder
:param gem_path: gem folder
:param external_subdir_path: external subdirectory
:param template_path: template folder
:param restricted_path: restricted folder
:param repo_uri: repo uri
:param default_engines_folder: default engines folder
:param default_projects_folder: default projects folder
:param default_gems_folder: default gems folder
:param default_templates_folder: default templates folder
:param default_restricted_folder: default restricted code folder
:param external_subdir_engine_path: Path to the engine to use when registering an external subdirectory.
The registration occurs in the engine.json file in this case
:param external_subdir_engine_path: Path to the project to use when registering an external subdirectory.
The registrations occurs in the project.json in this case
:param remove: add/remove the entries
:param force: force update of the engine_path for specified "engine_name" from the engine.json file
:return: 0 for success or non 0 failure code
"""
json_data = manifest.load_o3de_manifest()
result = 0
# do anything that could require a engine context first
if isinstance(project_path, str) or isinstance(project_path, pathlib.PurePath):
if not project_path:
logger.error(f'Project path cannot be empty.')
return 1
result = register_project_path(json_data, project_path, remove, engine_path)
elif isinstance(gem_path, str) or isinstance(gem_path, pathlib.PurePath):
if not gem_path:
logger.error(f'Gem path cannot be empty.')
return 1
result = register_gem_path(json_data, gem_path, remove,
external_subdir_engine_path, external_subdir_project_path)
elif isinstance(external_subdir_path, str) or isinstance(external_subdir_path, pathlib.PurePath):
if not external_subdir_path:
logger.error(f'External Subdirectory path is None.')
return 1
result = register_external_subdirectory(json_data, external_subdir_path, remove,
external_subdir_engine_path, external_subdir_project_path)
elif isinstance(template_path, str) or isinstance(template_path, pathlib.PurePath):
if not template_path:
logger.error(f'Template path cannot be empty.')
return 1
result = register_template_path(json_data, template_path, remove, engine_path)
elif isinstance(restricted_path, str) or isinstance(restricted_path, pathlib.PurePath):
if not restricted_path:
logger.error(f'Restricted path cannot be empty.')
return 1
result = register_restricted_path(json_data, restricted_path, remove, engine_path)
elif isinstance(repo_uri, str) or isinstance(repo_uri, pathlib.PurePath):
if not repo_uri:
logger.error(f'Repo URI cannot be empty.')
return 1
result = register_repo(json_data, repo_uri, remove)
elif isinstance(default_engines_folder, str) or isinstance(default_engines_folder, pathlib.PurePath):
result = register_default_engines_folder(json_data, default_engines_folder, remove)
elif isinstance(default_projects_folder, str) or isinstance(default_projects_folder, pathlib.PurePath):
result = register_default_projects_folder(json_data, default_projects_folder, remove)
elif isinstance(default_gems_folder, str) or isinstance(default_gems_folder, pathlib.PurePath):
result = register_default_gems_folder(json_data, default_gems_folder, remove)
elif isinstance(default_templates_folder, str) or isinstance(default_templates_folder, pathlib.PurePath):
result = register_default_templates_folder(json_data, default_templates_folder, remove)
elif isinstance(default_restricted_folder, str) or isinstance(default_restricted_folder, pathlib.PurePath):
result = register_default_restricted_folder(json_data, default_restricted_folder, remove)
# engine is done LAST
# Now that everything that could have an engine context is done, if the engine is supplied that means this is
# registering the engine itself
elif isinstance(engine_path, str) or isinstance(engine_path, pathlib.PurePath):
if not engine_path:
logger.error(f'Engine path cannot be empty.')
return 1
result = register_engine_path(json_data, engine_path, remove, force)
if not result:
manifest.save_o3de_manifest(json_data)
return result
def remove_invalid_o3de_objects() -> None:
json_data = manifest.load_o3de_manifest()
for engine_object in json_data['engines']:
engine_path = engine_object['path']
if not validation.valid_o3de_engine_json(pathlib.Path(engine_path).resolve() / 'engine.json'):
logger.warn(f"Engine path {engine_path} is invalid.")
register(engine_path=engine_path, remove=True)
for project in json_data['projects']:
if not validation.valid_o3de_project_json(pathlib.Path(project).resolve() / 'project.json'):
logger.warn(f"Project path {project} is invalid.")
register(project_path=project, remove=True)
for gem in json_data['gems']:
if not validation.valid_o3de_gem_json(pathlib.Path(gem).resolve() / 'gem.json'):
logger.warn(f"Gem path {gem} is invalid.")
register(gem_path=gem, remove=True)
for external in json_data['external_subdirectories']:
external = pathlib.Path(external).resolve()
if not external.is_dir():
logger.warn(f"External subdirectory {external} is invalid.")
register(engine_path=engine_path, external_subdir_path=external, remove=True)
for template in json_data['templates']:
if not validation.valid_o3de_template_json(pathlib.Path(template).resolve() / 'template.json'):
logger.warn(f"Template path {template} is invalid.")
register(template_path=template, remove=True)
for restricted in json_data['restricted']:
if not validation.valid_o3de_restricted_json(pathlib.Path(restricted).resolve() / 'restricted.json'):
logger.warn(f"Restricted path {restricted} is invalid.")
register(restricted_path=restricted, remove=True)
default_engines_folder = pathlib.Path(json_data['default_engines_folder']).resolve()
if not default_engines_folder.is_dir():
new_default_engines_folder = manifest.get_o3de_folder() / 'Engines'
new_default_engines_folder.mkdir(parents=True, exist_ok=True)
logger.warn(
f"Default engines folder {default_engines_folder} is invalid. Set default {new_default_engines_folder}")
register(default_engines_folder=new_default_engines_folder.as_posix())
default_projects_folder = pathlib.Path(json_data['default_projects_folder']).resolve()
if not default_projects_folder.is_dir():
new_default_projects_folder = manifest.get_o3de_folder() / 'Projects'
new_default_projects_folder.mkdir(parents=True, exist_ok=True)
logger.warn(
f"Default projects folder {default_projects_folder} is invalid. Set default {new_default_projects_folder}")
register(default_projects_folder=new_default_projects_folder.as_posix())
default_gems_folder = pathlib.Path(json_data['default_gems_folder']).resolve()
if not default_gems_folder.is_dir():
new_default_gems_folder = manifest.get_o3de_folder() / 'Gems'
new_default_gems_folder.mkdir(parents=True, exist_ok=True)
logger.warn(f"Default gems folder {default_gems_folder} is invalid."
f" Set default {new_default_gems_folder}")
register(default_gems_folder=new_default_gems_folder.as_posix())
default_templates_folder = pathlib.Path(json_data['default_templates_folder']).resolve()
if not default_templates_folder.is_dir():
new_default_templates_folder = manifest.get_o3de_folder() / 'Templates'
new_default_templates_folder.mkdir(parents=True, exist_ok=True)
logger.warn(
f"Default templates folder {default_templates_folder} is invalid."
f" Set default {new_default_templates_folder}")
register(default_templates_folder=new_default_templates_folder.as_posix())
default_restricted_folder = pathlib.Path(json_data['default_restricted_folder']).resolve()
if not default_restricted_folder.is_dir():
default_restricted_folder = manifest.get_o3de_folder() / 'Restricted'
default_restricted_folder.mkdir(parents=True, exist_ok=True)
logger.warn(
f"Default restricted folder {default_restricted_folder} is invalid."
f" Set default {default_restricted_folder}")
register(default_restricted_folder=default_restricted_folder.as_posix())
def _run_register(args: argparse) -> int:
if args.override_home_folder:
manifest.override_home_folder = args.override_home_folder
if args.update:
remove_invalid_o3de_objects()
return repo.refresh_repos()
elif args.this_engine:
ret_val = register(engine_path=manifest.get_this_engine_path(), force=args.force)
error_code = register_shipped_engine_o3de_objects(force=args.force)
if error_code:
ret_val = error_code
return ret_val
elif args.all_engines_path:
return register_all_engines_in_folder(args.all_engines_path, args.remove, args.force)
elif args.all_projects_path:
return register_all_projects_in_folder(args.all_projects_path, args.remove)
elif args.all_gems_path:
return register_all_gems_in_folder(args.all_gems_path, args.remove)
elif args.all_templates_path:
return register_all_templates_in_folder(args.all_templates_path, args.remove)
elif args.all_restricted_path:
return register_all_restricted_in_folder(args.all_restricted_path, args.remove)
elif args.all_repo_uri:
return register_all_repos_in_folder(args.all_restricted_path, args.remove)
else:
return register(engine_path=args.engine_path,
project_path=args.project_path,
gem_path=args.gem_path,
external_subdir_path=args.external_subdirectory,
template_path=args.template_path,
restricted_path=args.restricted_path,
repo_uri=args.repo_uri,
default_engines_folder=args.default_engines_folder,
default_projects_folder=args.default_projects_folder,
default_gems_folder=args.default_gems_folder,
default_templates_folder=args.default_templates_folder,
default_restricted_folder=args.default_restricted_folder,
external_subdir_engine_path=args.external_subdirectory_engine_path,
external_subdir_project_path=args.external_subdirectory_project_path,
remove=args.remove,
force=args.force)
def add_parser_args(parser):
"""
add_parser_args is called to add arguments to each command such that it can be
invoked locally or added by a central python file.
Ex. Directly run from this file alone with: python register.py --engine-path "C:/o3de"
:param parser: the caller passes an argparse parser like instance to this method
"""
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--this-engine', action='store_true', required=False,
default=False,
help='Registers the engine this script is running from.')
group.add_argument('-ep', '--engine-path', type=str, required=False,
help='Engine path to register/remove.')
group.add_argument('-pp', '--project-path', type=str, required=False,
help='Project path to register/remove.')
group.add_argument('-gp', '--gem-path', type=str, required=False,
help='Gem path to register/remove.')
group.add_argument('-es', '--external-subdirectory', type=str, required=False,
help='External subdirectory path to register/remove.')
group.add_argument('-tp', '--template-path', type=str, required=False,
help='Template path to register/remove.')
group.add_argument('-rp', '--restricted-path', type=str, required=False,
help='A restricted folder to register/remove.')
group.add_argument('-ru', '--repo-uri', type=str, required=False,
help='A repo uri to register/remove.')
group.add_argument('-aep', '--all-engines-path', type=str, required=False,
help='All engines under this folder to register/remove.')
group.add_argument('-app', '--all-projects-path', type=str, required=False,
help='All projects under this folder to register/remove.')
group.add_argument('-agp', '--all-gems-path', type=str, required=False,
help='All gems under this folder to register/remove.')
group.add_argument('-atp', '--all-templates-path', type=str, required=False,
help='All templates under this folder to register/remove.')
group.add_argument('-arp', '--all-restricted-path', type=str, required=False,
help='All templates under this folder to register/remove.')
group.add_argument('-aru', '--all-repo-uri', type=str, required=False,
help='All repos under this folder to register/remove.')
group.add_argument('-def', '--default-engines-folder', type=str, required=False,
help='The default engines folder to register/remove.')
group.add_argument('-dpf', '--default-projects-folder', type=str, required=False,
help='The default projects folder to register/remove.')
group.add_argument('-dgf', '--default-gems-folder', type=str, required=False,
help='The default gems folder to register/remove.')
group.add_argument('-dtf', '--default-templates-folder', type=str, required=False,
help='The default templates folder to register/remove.')
group.add_argument('-drf', '--default-restricted-folder', type=str, required=False,
help='The default restricted folder to register/remove.')
group.add_argument('-u', '--update', action='store_true', required=False,
default=False,
help='Refresh the repo cache.')
parser.add_argument('-ohf', '--override-home-folder', type=str, required=False,
help='By default the home folder is the user folder, override it to this folder.')
parser.add_argument('-r', '--remove', action='store_true', required=False,
default=False,
help='Remove entry.')
parser.add_argument('-f', '--force', action='store_true', default=False,
help='For the update of the registration field being modified.')
external_subdir_group = parser.add_argument_group(title='external-subdirectory',
description='path arguments to use with the --external-subdirectory option')
external_subdir_path_group = external_subdir_group.add_mutually_exclusive_group()
external_subdir_path_group.add_argument('-esep', '--external-subdirectory-engine-path', type=pathlib.Path,
help='If supplied, registers the external subdirectory with the engine.json at' \
' the engine-path location')
external_subdir_path_group.add_argument('-espp', '--external-subdirectory-project-path', type=pathlib.Path)
parser.set_defaults(func=_run_register)
def add_args(subparsers) -> None:
"""
add_args is called to add subparsers arguments to each command such that it can be
a central python file such as o3de.py.
It can be run from the o3de.py script as follows
call add_args and execute: python o3de.py register --engine-path "C:/o3de"
:param subparsers: the caller instantiates subparsers and passes it in here
"""
register_subparser = subparsers.add_parser('register')
add_parser_args(register_subparser)
def main():
"""
Runs register.py script as standalone script
"""
# parse the command line args
the_parser = argparse.ArgumentParser()
# add subparsers
# add args to the parser
add_parser_args(the_parser)
# parse args
the_args = the_parser.parse_args()
# run
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
# return
sys.exit(ret)
if __name__ == "__main__":
main()
+160
View File
@@ -0,0 +1,160 @@
#
# 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 logging
import pathlib
import shutil
import urllib.parse
import urllib.request
from o3de import manifest, utils, validation
logger = logging.getLogger()
logging.basicConfig()
def process_add_o3de_repo(file_name: str or pathlib.Path,
repo_set: set) -> int:
file_name = pathlib.Path(file_name).resolve()
if not validation.valid_o3de_repo_json(file_name):
return 1
cache_folder = manifest.get_o3de_cache_folder()
with file_name.open('r') as f:
try:
repo_data = json.load(f)
except json.JSONDecodeError as e:
logger.error(f'{file_name} failed to load: {str(e)}')
return 1
for o3de_object_uris, manifest_json in [(repo_data['engines'], 'engine.json'),
(repo_data['projects'], 'project.json'),
(repo_data['gems'], 'gem.json'),
(repo_data['template'], 'template.json'),
(repo_data['restricted'], 'restricted.json')]:
for o3de_object_uri in o3de_object_uris:
manifest_json_uri = f'{o3de_object_uri}/{manifest_json}'
manifest_json_sha256 = hashlib.sha256(manifest_json_uri.encode())
cache_file = cache_folder / str(manifest_json_sha256.hexdigest() + '.json')
if not cache_file.is_file():
parsed_uri = urllib.parse.urlparse(manifest_json_uri)
download_file_result = utils.download_file(parsed_uri, cache_file)
if download_file_result != 0:
return download_file_result
repo_set |= repo_data['repos']
return 0
def refresh_repos() -> int:
json_data = manifest.load_o3de_manifest()
# clear the cache
cache_folder = manifest.get_o3de_cache_folder()
shutil.rmtree(cache_folder)
cache_folder = manifest.get_o3de_cache_folder() # will recreate it
result = 0
# set will stop circular references
repo_set = set()
for repo_uri in json_data['repos']:
if repo_uri not in repo_set:
repo_set.add(repo_uri)
repo_uri = f'{repo_uri}/repo.json'
repo_sha256 = hashlib.sha256(repo_uri.encode())
cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json')
if not cache_file.is_file():
parsed_uri = urllib.parse.urlparse(repo_uri)
download_file_result = utils.download_file(parsed_uri, cache_file)
if download_file_result != 0:
return download_file_result
if not validation.valid_o3de_repo_json(cache_file):
logger.error(f'Repo json {repo_uri} is not valid.')
cache_file.unlink()
return 1
last_failure = process_add_o3de_repo(cache_file, repo_set)
if last_failure:
result = last_failure
return result
def search_repo(repo_json_data: dict,
engine_name: str = None,
project_name: str = None,
gem_name: str = None,
template_name: str = None,
restricted_name: str = None) -> dict or None:
if isinstance(engine_name, str) or isinstance(engine_name, pathlib.PurePath):
o3de_object_uris = repo_json_data['engines']
manifest_json = 'engine.json'
json_key = 'engine_name'
search_func = lambda: None if manifest_json_data.get(json_key, '') == engine_name else manifest_json_data
elif isinstance(project_name, str) or isinstance(project_name, pathlib.PurePath):
o3de_object_uris = repo_json_data['projects']
manifest_json = 'project.json'
json_key = 'project_name'
search_func = lambda: None if manifest_json_data.get(json_key, '') == project_name else manifest_json_data
elif isinstance(gem_name, str) or isinstance(gem_name, pathlib.PurePath):
o3de_object_uris = repo_json_data['gems']
manifest_json = 'gem.json'
json_key = 'gem_name'
search_func = lambda: None if manifest_json_data.get(json_key, '') == gem_name else manifest_json_data
elif isinstance(template_name, str) or isinstance(template_name, pathlib.PurePath):
o3de_object_uris = repo_json_data['template']
manifest_json = 'template.json'
json_key = 'template_name'
search_func = lambda: None if manifest_json_data.get(json_key, '') == template_name_name else manifest_json_data
elif isinstance(restricted_name, str) or isinstance(restricted_name, pathlib.PurePath):
o3de_object_uris = repo_json_data['restricted']
manifest_json = 'restricted.json'
json_key = 'restricted_name'
search_func = lambda: None if manifest_json_data.get(json_key, '') == restricted_name else manifest_json_data
else:
return None
o3de_object = search_o3de_object(manifest_json, o3de_object_uris, search_func)
if o3de_object:
return o3de_object
# recurse into the repos object to search for the o3de object
o3de_object_uris = repo_json_data['repos']
manifest_json = 'repo.json'
search_func = lambda: search_repo(manifest_json, engine_name, project_name, gem_name, template_name)
return search_o3de_object(manifest_json, o3de_object_uris, search_func)
def search_o3de_object(manifest_json, o3de_object_uris, search_func):
# Search for the o3de object based on the supplied object name in the current repo
cache_folder = manifest.get_o3de_cache_folder()
for o3de_object_uri in o3de_object_uris:
manifest_json_uri = f'{o3de_object_uri}/{manifest_json}'
manifest_json_sha256 = hashlib.sha256(manifest_json_uri.encode())
cache_file = cache_folder / str(manifest_json_sha256.hexdigest() + '.json')
if cache_file.is_file():
with cache_file.open('r') as f:
try:
manifest_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{cache_file} failed to load: {str(e)}')
else:
result_json_data = search_func()
if result_json_data:
return result_json_data
return None
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6d6204c6730e5675791765ca194e9b1cbec282208e280507de830afc2805e5fa
size 41127
+117
View File
@@ -0,0 +1,117 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
import argparse
import json
import logging
import hashlib
import pathlib
import sys
from o3de import utils
logger = logging.getLogger()
logging.basicConfig()
def sha256(file_path: str or pathlib.Path,
json_path: str or pathlib.Path = None) -> int:
if not file_path:
logger.error(f'File path cannot be empty.')
return 1
file_path = pathlib.Path(file_path).resolve()
if not file_path.is_file():
logger.error(f'File path {file_path} does not exist.')
return 1
if json_path:
json_path = pathlib.Path(json_path).resolve()
if not json_path.is_file():
logger.error(f'Json path {json_path} does not exist.')
return 1
sha256 = hashlib.sha256(file_path.open('rb').read()).hexdigest()
if json_path:
with json_path.open('r') as s:
try:
json_data = json.load(s)
except json.JSONDecodeError as e:
logger.error(f'Failed to read Json path {json_path}: {str(e)}')
return 1
json_data.update({"sha256": sha256})
utils.backup_file(json_path)
with json_path.open('w') as s:
try:
s.write(json.dumps(json_data, indent=4) + '\n')
except OSError as e:
logger.error(f'Failed to write Json path {json_path}: {str(e)}')
return 1
else:
print(sha256)
return 0
def _run_sha256(args: argparse) -> int:
return sha256(args.file_path,
args.json_path)
def add_parser_args(parser):
"""
add_parser_args is called to add arguments to each command such that it can be
invoked locally or added by a central python file.
Ex. Directly run from this file alone with: python sha256.py --file-path "C:/TestGem"
:param parser: the caller passes an argparse parser like instance to this method
"""
parser.add_argument('-f', '--file-path', type=str, required=True,
help='The path to the file you want to sha256.')
parser.add_argument('-j', '--json-path', type=str, required=False,
help='optional path to an o3de json file to add the "sha256" element to.')
parser.set_defaults(func=_run_sha256)
def add_args(subparsers) -> None:
"""
add_args is called to add subparsers arguments to each command such that it can be
a central python file such as o3de.py.
It can be run from the o3de.py script as follows
call add_args and execute: python o3de.py sha256 --file-path "C:/TestGem"
:param subparsers: the caller instantiates subparsers and passes it in here
"""
sha256_subparser = subparsers.add_parser('sha256')
add_parser_args(sha256_subparser)
def main():
"""
Runs sha256.py script as standalone script
"""
# parse the command line args
the_parser = argparse.ArgumentParser()
# add subparsers
# add args to the parser
add_parser_args(the_parser)
# parse args
the_args = the_parser.parse_args()
# run
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
# return
sys.exit(ret)
if __name__ == "__main__":
main()
+112
View File
@@ -0,0 +1,112 @@
#
# 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 file contains utility functions
"""
import uuid
import pathlib
import shutil
import urllib.request
def validate_identifier(identifier: str) -> bool:
"""
Determine if the identifier supplied is valid.
:param identifier: the name which needs to to checked
:return: bool: if the identifier is valid or not
"""
if not identifier:
return False
elif len(identifier) > 64:
return False
elif not identifier[0].isalpha():
return False
else:
for character in identifier:
if not (character.isalnum() or character == '_' or character == '-'):
return False
return True
def validate_uuid4(uuid_string: str) -> bool:
"""
Determine if the uuid supplied is valid.
:param uuid_string: the uuid which needs to to checked
:return: bool: if the uuid is valid or not
"""
try:
val = uuid.UUID(uuid_string, version=4)
except ValueError:
return False
return str(val) == uuid_string
def backup_file(file_name: str or pathlib.Path) -> None:
index = 0
renamed = False
while not renamed:
backup_file_name = pathlib.Path(str(file_name) + '.bak' + str(index)).resolve()
index += 1
if not backup_file_name.is_file():
file_name = pathlib.Path(file_name).resolve()
file_name.rename(backup_file_name)
if backup_file_name.is_file():
renamed = True
def backup_folder(folder: str or pathlib.Path) -> None:
index = 0
renamed = False
while not renamed:
backup_folder_name = pathlib.Path(str(folder) + '.bak' + str(index)).resolve()
index += 1
if not backup_folder_name.is_dir():
folder = pathlib.Path(folder).resolve()
folder.rename(backup_folder_name)
if backup_folder_name.is_dir():
renamed = True
def download_file(parsed_uri, download_path: pathlib.Path) -> int:
"""
:param parsed_uri: uniform resource identifier to zip file to download
:param download_path: location path on disk to download file
"""
if download_path.is_file():
logger.warn(f'File already downloaded to {download_path}.')
elif parsed_uri.scheme in ['http', 'https', 'ftp', 'ftps']:
with urllib.request.urlopen(url) as s:
with download_path.open('wb') as f:
shutil.copyfileobj(s, f)
else:
origin_file = pathlib.Path(url).resolve()
if not origin_file.is_file():
return 1
shutil.copy(origin_file, download_path)
return 0
def download_zip_file(parsed_uri, download_zip_path: pathlib.Path) -> int:
"""
:param parsed_uri: uniform resource identifier to zip file to download
:param download_zip_path: path to output zip file
"""
download_file_result = download_file(parsed_uri, download_zip_path)
if download_file_result != 0:
return download_file_result
if not zipfile.is_zipfile(download_zip_path):
logger.error(f"File zip {download_zip_path} is invalid.")
download_zip_path.unlink()
return 1
return 0
+102
View File
@@ -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.
#
"""
This file validating o3de object json files
"""
import json
import pathlib
def valid_o3de_json_dict(json_data: dict, key: str) -> bool:
return key in json_data
def valid_o3de_repo_json(file_name: str or pathlib.Path) -> bool:
file_name = pathlib.Path(file_name).resolve()
if not file_name.is_file():
return False
with file_name.open('r') as f:
try:
json_data = json.load(f)
test = json_data['repo_name']
test = json_data['origin']
except (json.JSONDecodeError, KeyError) as e:
return False
return True
def valid_o3de_engine_json(file_name: str or pathlib.Path) -> bool:
file_name = pathlib.Path(file_name).resolve()
if not file_name.is_file():
return False
with file_name.open('r') as f:
try:
json_data = json.load(f)
test = json_data['engine_name']
except (json.JSONDecodeError, KeyError) as e:
return False
return True
def valid_o3de_project_json(file_name: str or pathlib.Path) -> bool:
file_name = pathlib.Path(file_name).resolve()
if not file_name.is_file():
return False
with file_name.open('r') as f:
try:
json_data = json.load(f)
test = json_data['project_name']
except (json.JSONDecodeError, KeyError) as e:
return False
return True
def valid_o3de_gem_json(file_name: str or pathlib.Path) -> bool:
file_name = pathlib.Path(file_name).resolve()
if not file_name.is_file():
return False
with file_name.open('r') as f:
try:
json_data = json.load(f)
test = json_data['gem_name']
except (json.JSONDecodeError, KeyError) as e:
return False
return True
def valid_o3de_template_json(file_name: str or pathlib.Path) -> bool:
file_name = pathlib.Path(file_name).resolve()
if not file_name.is_file():
return False
with file_name.open('r') as f:
try:
json_data = json.load(f)
test = json_data['template_name']
except (json.JSONDecodeError, KeyError) as e:
return False
return True
def valid_o3de_restricted_json(file_name: str or pathlib.Path) -> bool:
file_name = pathlib.Path(file_name).resolve()
if not file_name.is_file():
return False
with file_name.open('r') as f:
try:
json_data = json.load(f)
test = json_data['restricted_name']
except (json.JSONDecodeError, KeyError) as e:
return False
return True
+42
View File
@@ -0,0 +1,42 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import os
import platform
from setuptools import setup, find_packages
from setuptools.command.develop import develop
from setuptools.command.build_py import build_py
PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__))
PYTHON_64 = platform.architecture()[0] == '64bit'
if __name__ == '__main__':
if not PYTHON_64:
raise RuntimeError("32-bit Python is not a supported platform.")
with open(os.path.join(PACKAGE_ROOT, 'README.txt')) as f:
long_description = f.read()
setup(
name="o3de",
version="1.0.0",
description='O3DE editor Python bindings test tools',
long_description=long_description,
packages=find_packages(where='o3de', exclude=['tests']),
install_requires=[
],
tests_require=[
],
entry_points={
},
)
+36
View File
@@ -0,0 +1,36 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(NOT PAL_TRAIT_BUILD_TESTS_SUPPORTED)
return()
endif()
# Add a test to test out the o3de package `o3de.py register` command
ly_add_pytest(
NAME o3de_register
PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_register.py
TEST_SUITE smoke
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
)
ly_add_pytest(
NAME o3de_cmake
PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_cmake.py
TEST_SUITE smoke
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
)
ly_add_pytest(
NAME o3de_global_project
PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_global_project.py
TEST_SUITE smoke
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
)
+10
View File
@@ -0,0 +1,10 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
+69
View File
@@ -0,0 +1,69 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
import io
import json
import logging
import pytest
import pathlib
from unittest.mock import patch
from o3de import cmake
class TestGetEnabledGems:
@pytest.mark.parametrize(
"enable_gems_cmake_data, expected_set", [
pytest.param("""
# Comment
set(ENABLED_GEMS foo bar baz)
""", set(['foo', 'bar', 'baz'])),
pytest.param("""
# Comment
set(ENABLED_GEMS
foo
bar
baz
)
""", set(['foo', 'bar', 'baz'])),
pytest.param("""
# Comment
set(ENABLED_GEMS
foo
bar
baz)
""", set(['foo', 'bar', 'baz'])),
pytest.param("""
# Comment
set(ENABLED_GEMS
foo bar
baz)
""", set(['foo', 'bar', 'baz'])),
pytest.param("""
# Comment
set(RANDOM_VARIABLE TestGame, TestProject Test Engine)
set(ENABLED_GEMS HelloWorld IceCream
foo
baz bar
baz baz baz baz baz morebaz lessbaz
)
Random Text
""", set(['HelloWorld', 'IceCream', 'foo', 'bar', 'baz', 'morebaz', 'lessbaz'])),
]
)
def test_get_enabled_gems(self, enable_gems_cmake_data, expected_set):
enabled_gems_set = set()
with patch('pathlib.Path.resolve', return_value=pathlib.Path('enabled_gems.cmake')) as pathlib_is_resolve_mock,\
patch('pathlib.Path.is_file', return_value=True) as pathlib_is_file_mock,\
patch('pathlib.Path.open', return_value=io.StringIO(enable_gems_cmake_data)) as pathlib_open_mock:
enabled_gems_set = cmake.get_enabled_gems(pathlib.Path('enabled_gems.cmake'))
assert enabled_gems_set == expected_set
+748
View File
@@ -0,0 +1,748 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
import os
import pytest
from . import engine_template
TEST_TEMPLATED_CONTENT_WITH_LICENSE = """\
// {BEGIN_LICENSE}
/*
* 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.
*
*/
// {END_LICENSE}
#pragma once
#include <AzCore/EBus/EBus.h>
namespace ${Name}
{
class ${Name}Requests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
// Put your public methods here
};
using ${Name}RequestsBus = AZ::EBus<${Name}Requests>;
} // namespace ${Name}
"""
TEST_TEMPLATED_CONTENT_WITHOUT_LICENSE = """\
#pragma once
#include <AzCore/EBus/EBus.h>
namespace ${Name}
{
class ${Name}Requests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
// Put your public methods here
};
using ${Name}RequestsBus = AZ::EBus<${Name}Requests>;
} // namespace ${Name}
"""
TEST_CONCRETE_TESTTEMPLATE_CONTENT_WITHOUT_LICENSE = """\
#pragma once
#include <AzCore/EBus/EBus.h>
namespace TestTemplate
{
class TestTemplateRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
// Put your public methods here
};
using TestTemplateRequestsBus = AZ::EBus<TestTemplateRequests>;
} // namespace TestTemplate
"""
TEST_CONCRETE_TESTTEMPLATE_CONTENT_WITH_LICENSE = """\
// {BEGIN_LICENSE}
/*
* 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.
*
*/
// {END_LICENSE}
#pragma once
#include <AzCore/EBus/EBus.h>
namespace TestTemplate
{
class TestTemplateRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
// Put your public methods here
};
using TestTemplateRequestsBus = AZ::EBus<TestTemplateRequests>;
} // namespace TestTemplate
"""
TEST_CONCRETE_TESTPROJECT_TEMPLATE_CONTENT_WITHOUT_LICENSE = """\
#pragma once
#include <AzCore/EBus/EBus.h>
namespace TestProject
{
class TestProjectRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
// Put your public methods here
};
using TestProjectRequestsBus = AZ::EBus<TestProjectRequests>;
} // namespace TestProject
"""
TEST_CONCRETE_TESTPROJECT_TEMPLATE_CONTENT_WITH_LICENSE = """\
// {BEGIN_LICENSE}
/*
* 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.
*
*/
// {END_LICENSE}
#pragma once
#include <AzCore/EBus/EBus.h>
namespace TestProject
{
class TestProjectRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
// Put your public methods here
};
using TestProjectRequestsBus = AZ::EBus<TestProjectRequests>;
} // namespace TestProject
"""
TEST_CONCRETE_TESTGEM_TEMPLATE_CONTENT_WITHOUT_LICENSE = """\
#pragma once
#include <AzCore/EBus/EBus.h>
namespace TestGem
{
class TestGemRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
// Put your public methods here
};
using TestGemRequestsBus = AZ::EBus<TestGemRequests>;
} // namespace TestGem
"""
TEST_CONCRETE_TESTGEM_TEMPLATE_CONTENT_WITH_LICENSE = """\
// {BEGIN_LICENSE}
/*
* 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.
*
*/
// {END_LICENSE}
#pragma once
#include <AzCore/EBus/EBus.h>
namespace TestGem
{
class TestGemRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
// Put your public methods here
};
using TestGemRequestsBus = AZ::EBus<TestGemRequests>;
} // namespace TestGem
"""
TEST_DEFAULTTEMPLATE_JSON_CONTENTS = """\
{
"inputPath": "Templates/Default/Template",
"copyFiles": [
{
"inFile": "Code/Include/${Name}/${Name}Bus.h",
"outFile": "Code/Include/${Name}/${Name}Bus.h",
"isTemplated": true,
"isOptional": false
}
],
"createDirectories": [
{
"outDir": "Code"
},
{
"outDir": "Code/Include"
},
{
"outDir": "Code/Include/Platform"
},
{
"outDir": "Code/Include/${Name}"
}
]
}\
"""
TEST_DEFAULTTEMPLATE_RESTRICTED_JSON_CONTENTS = """\
{
"inputPath": "restricted/Salem/Templates/Default/Template",
"copyFiles": [
{
"inFile": "Code/Include/Platform/Salem/${Name}Bus.h",
"outFile": "Code/Include/Platform/Salem/${Name}Bus.h",
"isTemplated": true,
"isOptional": false
}
],
"createDirectories": [
{
"outDir": "Code/Include/Platform/Salem"
}
]
}\
"""
TEST_DEFAULTPROJECT_TEMPLATE_JSON_CONTENTS = """\
{
"inputPath": "Templates/DefaultProject/Template",
"copyFiles": [
{
"inFile": "Code/Include/${Name}/${Name}Bus.h",
"outFile": "Code/Include/${Name}/${Name}Bus.h",
"isTemplated": true,
"isOptional": false
}
],
"createDirectories": [
{
"outDir": "Code"
},
{
"outDir": "Code/Include"
},
{
"outDir": "Code/Include/Platform"
},
{
"outDir": "Code/Include/${Name}"
}
]
}\
"""
TEST_DEFAULTPROJECT_TEMPLATE_RESTRICTED_JSON_CONTENTS = """\
{
"inputPath": "restricted/Salem/Templates/DefaultProject/Template",
"copyFiles": [
{
"inFile": "Code/Include/Platform/Salem/${Name}Bus.h",
"outFile": "Code/Include/Platform/Salem/${Name}Bus.h",
"isTemplated": true,
"isOptional": false
}
],
"createDirectories": [
{
"outDir": "Code/Include/Platform/Salem"
}
]
}\
"""
TEST_DEFAULTGEM_TEMPLATE_JSON_CONTENTS = """\
{
"inputPath": "Templates/DefaultGem/Template",
"copyFiles": [
{
"inFile": "Code/Include/${Name}/${Name}Bus.h",
"outFile": "Code/Include/${Name}/${Name}Bus.h",
"isTemplated": true,
"isOptional": false
}
],
"createDirectories": [
{
"outDir": "Code"
},
{
"outDir": "Code/Include"
},
{
"outDir": "Code/Include/Platform"
},
{
"outDir": "Code/Include/${Name}"
}
]
}\
"""
TEST_DEFAULTGEM_TEMPLATE_RESTRICTED_JSON_CONTENTS = """\
{
"inputPath": "restricted/Salem/Templates/DefaultGem/Template",
"copyFiles": [
{
"inFile": "Code/Include/Platform/Salem/${Name}Bus.h",
"outFile": "Code/Include/Platform/Salem/${Name}Bus.h",
"isTemplated": true,
"isOptional": false
}
],
"createDirectories": [
{
"outDir": "Code/Include/Platform/Salem"
}
]
}\
"""
@pytest.mark.parametrize(
"concrete_contents,"
" templated_contents_with_license, templated_contents_without_license,"
" keep_license_text, expect_failure,"
" template_json_contents, restricted_template_json_contents", [
pytest.param(TEST_CONCRETE_TESTTEMPLATE_CONTENT_WITH_LICENSE,
TEST_TEMPLATED_CONTENT_WITH_LICENSE, TEST_TEMPLATED_CONTENT_WITHOUT_LICENSE,
True, False,
TEST_DEFAULTTEMPLATE_JSON_CONTENTS, TEST_DEFAULTTEMPLATE_RESTRICTED_JSON_CONTENTS),
pytest.param(TEST_CONCRETE_TESTTEMPLATE_CONTENT_WITH_LICENSE,
TEST_TEMPLATED_CONTENT_WITH_LICENSE, TEST_TEMPLATED_CONTENT_WITHOUT_LICENSE,
False, False,
TEST_DEFAULTTEMPLATE_JSON_CONTENTS, TEST_DEFAULTTEMPLATE_RESTRICTED_JSON_CONTENTS)
]
)
def test_create_template(tmpdir,
concrete_contents,
templated_contents_with_license, templated_contents_without_license,
keep_license_text, expect_failure,
template_json_contents, restricted_template_json_contents):
dev_root = str(tmpdir.join('dev').realpath()).replace('\\', '/')
os.makedirs(dev_root, exist_ok=True)
dev_gem_code_include_testgem = f'{dev_root}/TestTemplate/Code/Include/TestTemplate'
os.makedirs(dev_gem_code_include_testgem, exist_ok=True)
gem_bus_file = f'{dev_gem_code_include_testgem}/TestTemplateBus.h'
if os.path.isfile(gem_bus_file):
os.unlink(gem_bus_file)
with open(gem_bus_file, 'w') as s:
s.write(concrete_contents)
dev_gem_code_include_platform_salem = f'{dev_root}/TestTemplate/Code/Include/Platform/Salem'
os.makedirs(dev_gem_code_include_platform_salem, exist_ok=True)
restricted_gem_bus_file = f'{dev_gem_code_include_platform_salem}/TestTemplateBus.h'
if os.path.isfile(restricted_gem_bus_file):
os.unlink(restricted_gem_bus_file)
with open(restricted_gem_bus_file, 'w') as s:
s.write(concrete_contents)
template_folder = f'{dev_root}/Templates'
os.makedirs(template_folder, exist_ok=True)
restricted_folder = f'{dev_root}/restricted'
os.makedirs(restricted_folder, exist_ok=True)
result = engine_template.create_template(dev_root, 'TestTemplate', 'Default', keep_license_text=keep_license_text)
if expect_failure:
assert result != 0
else:
assert result == 0
new_template_folder = f'{template_folder}/Default'
assert os.path.isdir(new_template_folder)
new_template_json = f'{new_template_folder}/template.json'
assert os.path.isfile(new_template_json)
with open(new_template_json, 'r') as s:
s_data = s.read()
assert s_data == template_json_contents
new_default_name_bus_file = f'{new_template_folder}/Template/Code/Include/' + '${Name}/${Name}Bus.h'
assert os.path.isfile(new_default_name_bus_file)
with open(new_default_name_bus_file, 'r') as s:
s_data = s.read()
if keep_license_text:
assert s_data == templated_contents_with_license
else:
assert s_data == templated_contents_without_license
restricted_template_folder = f'{dev_root}/restricted/Salem/Templates'
new_restricted_template_folder = f'{restricted_template_folder}/Default'
assert os.path.isdir(new_restricted_template_folder)
new_restricted_template_json = f'{new_restricted_template_folder}/template.json'
assert os.path.isfile(new_restricted_template_json)
with open(new_restricted_template_json, 'r') as s:
s_data = s.read()
assert s_data == restricted_template_json_contents
new_restricted_default_name_bus_file = f'{restricted_template_folder}' \
f'/Default/Template/Code/Include/Platform/Salem/' + '${Name}Bus.h'
assert os.path.isfile(new_restricted_default_name_bus_file)
with open(new_restricted_default_name_bus_file, 'r') as s:
s_data = s.read()
if keep_license_text:
assert s_data == templated_contents_with_license
else:
assert s_data == templated_contents_without_license
@pytest.mark.parametrize(
"concrete_contents, templated_contents,"
" keep_license_text, expect_failure,"
" template_json_contents, restricted_template_json_contents", [
pytest.param(TEST_CONCRETE_TESTTEMPLATE_CONTENT_WITH_LICENSE, TEST_TEMPLATED_CONTENT_WITH_LICENSE,
True, False,
TEST_DEFAULTTEMPLATE_JSON_CONTENTS, TEST_DEFAULTTEMPLATE_RESTRICTED_JSON_CONTENTS),
pytest.param(TEST_CONCRETE_TESTTEMPLATE_CONTENT_WITHOUT_LICENSE, TEST_TEMPLATED_CONTENT_WITH_LICENSE,
False, False,
TEST_DEFAULTTEMPLATE_JSON_CONTENTS, TEST_DEFAULTTEMPLATE_RESTRICTED_JSON_CONTENTS)
]
)
def test_create_from_template(tmpdir,
concrete_contents, templated_contents,
keep_license_text, expect_failure,
template_json_contents, restricted_template_json_contents):
dev_root = str(tmpdir.join('dev').realpath()).replace('\\', '/')
os.makedirs(dev_root, exist_ok=True)
template_default_folder = f'{dev_root}/Templates/Default'
os.makedirs(template_default_folder, exist_ok=True)
template_json = f'{template_default_folder}/template.json'
if os.path.isfile(template_json):
os.unlink(template_json)
with open(template_json, 'w') as s:
s.write(template_json_contents)
default_name_bus_dir = f'{template_default_folder}/Template/Code/Include/' + '${Name}'
os.makedirs(default_name_bus_dir, exist_ok=True)
default_name_bus_file = f'{default_name_bus_dir}/' + '${Name}Bus.h'
if os.path.isfile(default_name_bus_file):
os.unlink(default_name_bus_file)
with open(default_name_bus_file, 'w') as s:
s.write(templated_contents)
restricted_template_default_folder = f'{dev_root}/restricted/Salem/Templates/Default'
os.makedirs(restricted_template_default_folder, exist_ok=True)
restricted_template_json = f'{restricted_template_default_folder}/template.json'
if os.path.isfile(restricted_template_json):
os.unlink(restricted_template_json)
with open(restricted_template_json, 'w') as s:
s.write(restricted_template_json_contents)
restricted_default_name_bus_dir = f'{restricted_template_default_folder}/Template/Code/Include/Platform/Salem'
os.makedirs(restricted_default_name_bus_dir, exist_ok=True)
restricted_default_name_bus_file = f'{restricted_default_name_bus_dir}/' + '${Name}Bus.h'
if os.path.isfile(restricted_default_name_bus_file):
os.unlink(restricted_default_name_bus_file)
with open(restricted_default_name_bus_file, 'w') as s:
s.write(templated_contents)
result = engine_template.create_from_template(dev_root, 'TestTemplate', 'Default',
keep_license_text=keep_license_text)
if expect_failure:
assert result != 0
else:
assert result == 0
test_folder = f'{dev_root}/TestTemplate'
assert os.path.isdir(test_folder)
test_bus_file = f'{test_folder}/Code/Include/TestTemplate/TestTemplateBus.h'
assert os.path.isfile(test_bus_file)
with open(test_bus_file, 'r') as s:
s_data = s.read()
assert s_data == concrete_contents
restricted_test_bus_folder = f'{dev_root}/restricted/Salem/TestTemplate/Code/Include/Platform/Salem'
assert os.path.isdir(restricted_test_bus_folder)
restricted_default_name_bus_file = f'{restricted_test_bus_folder}/TestTemplateBus.h'
assert os.path.isfile(restricted_default_name_bus_file)
with open(restricted_default_name_bus_file, 'r') as s:
s_data = s.read()
assert s_data == concrete_contents
@pytest.mark.parametrize(
"concrete_contents, templated_contents,"
" keep_license_text, expect_failure,"
" template_json_contents, restricted_template_json_contents", [
pytest.param(TEST_CONCRETE_TESTPROJECT_TEMPLATE_CONTENT_WITH_LICENSE, TEST_TEMPLATED_CONTENT_WITH_LICENSE,
True, False,
TEST_DEFAULTPROJECT_TEMPLATE_JSON_CONTENTS, TEST_DEFAULTPROJECT_TEMPLATE_RESTRICTED_JSON_CONTENTS),
pytest.param(TEST_CONCRETE_TESTPROJECT_TEMPLATE_CONTENT_WITHOUT_LICENSE, TEST_TEMPLATED_CONTENT_WITH_LICENSE,
False, False,
TEST_DEFAULTPROJECT_TEMPLATE_JSON_CONTENTS, TEST_DEFAULTPROJECT_TEMPLATE_RESTRICTED_JSON_CONTENTS)
]
)
def test_create_project(tmpdir,
concrete_contents, templated_contents,
keep_license_text, expect_failure,
template_json_contents, restricted_template_json_contents):
dev_root = str(tmpdir.join('dev').realpath()).replace('\\', '/')
os.makedirs(dev_root, exist_ok=True)
template_default_folder = f'{dev_root}/Templates/DefaultProject'
os.makedirs(template_default_folder, exist_ok=True)
template_json = f'{template_default_folder}/template.json'
if os.path.isfile(template_json):
os.unlink(template_json)
with open(template_json, 'w') as s:
s.write(template_json_contents)
default_name_bus_dir = f'{template_default_folder}/Template/Code/Include/' + '${Name}'
os.makedirs(default_name_bus_dir, exist_ok=True)
default_name_bus_file = f'{default_name_bus_dir}/' + '${Name}Bus.h'
if os.path.isfile(default_name_bus_file):
os.unlink(default_name_bus_file)
with open(default_name_bus_file, 'w') as s:
s.write(templated_contents)
restricted_template_default_folder = f'{dev_root}/restricted/Salem/Templates/DefaultProject'
os.makedirs(restricted_template_default_folder, exist_ok=True)
restricted_template_json = f'{restricted_template_default_folder}/template.json'
if os.path.isfile(restricted_template_json):
os.unlink(restricted_template_json)
with open(restricted_template_json, 'w') as s:
s.write(restricted_template_json_contents)
restricted_default_name_bus_dir = f'{restricted_template_default_folder}/Template/Code/Include/Platform/Salem'
os.makedirs(restricted_default_name_bus_dir, exist_ok=True)
restricted_default_name_bus_file = f'{restricted_default_name_bus_dir}/' + '${Name}Bus.h'
if os.path.isfile(restricted_default_name_bus_file):
os.unlink(restricted_default_name_bus_file)
with open(restricted_default_name_bus_file, 'w') as s:
s.write(templated_contents)
result = engine_template.create_project(dev_root, 'TestProject', keep_license_text=keep_license_text)
if expect_failure:
assert result != 0
else:
assert result == 0
test_project_folder = f'{dev_root}/TestProject'
assert os.path.isdir(test_project_folder)
test_project_bus_file = f'{test_project_folder}/Code/Include/TestProject/TestProjectBus.h'
assert os.path.isfile(test_project_bus_file)
with open(test_project_bus_file, 'r') as s:
s_data = s.read()
assert s_data == concrete_contents
restricted_test_project_bus_folder = f'{dev_root}/restricted/Salem/TestProject/Code/Include/Platform/Salem'
assert os.path.isdir(restricted_test_project_bus_folder)
restricted_default_name_bus_file = f'{restricted_test_project_bus_folder}/TestProjectBus.h'
assert os.path.isfile(restricted_default_name_bus_file)
with open(restricted_default_name_bus_file, 'r') as s:
s_data = s.read()
assert s_data == concrete_contents
@pytest.mark.parametrize(
"concrete_contents, templated_contents,"
" keep_license_text, expect_failure,"
" template_json_contents, restricted_template_json_contents", [
pytest.param(TEST_CONCRETE_TESTGEM_TEMPLATE_CONTENT_WITH_LICENSE, TEST_TEMPLATED_CONTENT_WITH_LICENSE,
True, False,
TEST_DEFAULTGEM_TEMPLATE_JSON_CONTENTS, TEST_DEFAULTGEM_TEMPLATE_RESTRICTED_JSON_CONTENTS),
pytest.param(TEST_CONCRETE_TESTGEM_TEMPLATE_CONTENT_WITHOUT_LICENSE, TEST_TEMPLATED_CONTENT_WITH_LICENSE,
False, False,
TEST_DEFAULTGEM_TEMPLATE_JSON_CONTENTS, TEST_DEFAULTGEM_TEMPLATE_RESTRICTED_JSON_CONTENTS)
]
)
def test_create_gem(tmpdir,
concrete_contents, templated_contents,
keep_license_text, expect_failure,
template_json_contents, restricted_template_json_contents):
dev_root = str(tmpdir.join('dev').realpath()).replace('\\', '/')
os.makedirs(dev_root, exist_ok=True)
template_default_folder = f'{dev_root}/Templates/DefaultGem'
os.makedirs(template_default_folder, exist_ok=True)
template_json = f'{template_default_folder}/template.json'
if os.path.isfile(template_json):
os.unlink(template_json)
with open(template_json, 'w') as s:
s.write(template_json_contents)
default_name_bus_dir = f'{template_default_folder}/Template/Code/Include/' + '${Name}'
os.makedirs(default_name_bus_dir, exist_ok=True)
default_name_bus_file = f'{default_name_bus_dir}/' + '${Name}Bus.h'
if os.path.isfile(default_name_bus_file):
os.unlink(default_name_bus_file)
with open(default_name_bus_file, 'w') as s:
s.write(templated_contents)
restricted_template_default_folder = f'{dev_root}/restricted/Salem/Templates/DefaultGem'
os.makedirs(restricted_template_default_folder, exist_ok=True)
restricted_template_json = f'{restricted_template_default_folder}/template.json'
if os.path.isfile(restricted_template_json):
os.unlink(restricted_template_json)
with open(restricted_template_json, 'w') as s:
s.write(restricted_template_json_contents)
restricted_default_name_bus_dir = f'{restricted_template_default_folder}/Template/Code/Include/Platform/Salem'
os.makedirs(restricted_default_name_bus_dir, exist_ok=True)
restricted_default_name_bus_file = f'{restricted_default_name_bus_dir}/' + '${Name}Bus.h'
if os.path.isfile(restricted_default_name_bus_file):
os.unlink(restricted_default_name_bus_file)
with open(restricted_default_name_bus_file, 'w') as s:
s.write(templated_contents)
result = engine_template.create_gem(dev_root, 'TestGem', keep_license_text=keep_license_text)
if expect_failure:
assert result != 0
else:
assert result == 0
test_gem_folder = f'{dev_root}/Gems/TestGem'
assert os.path.isdir(test_gem_folder)
test_gem_bus_file = f'{test_gem_folder}/Code/Include/TestGem/TestGemBus.h'
assert os.path.isfile(test_gem_bus_file)
with open(test_gem_bus_file, 'r') as s:
s_data = s.read()
assert s_data == concrete_contents
restricted_test_gem_bus_folder = f'{dev_root}/restricted/Salem/Gems/TestGem/Code/Include/Platform/Salem'
assert os.path.isdir(restricted_test_gem_bus_folder)
restricted_default_name_bus_file = f'{restricted_test_gem_bus_folder}/TestGemBus.h'
assert os.path.isfile(restricted_default_name_bus_file)
with open(restricted_default_name_bus_file, 'r') as s:
s_data = s.read()
assert s_data == concrete_contents
@@ -0,0 +1,40 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
import io
import json
import logging
import pytest
import pathlib
from unittest.mock import patch
from o3de import global_project
logger = logging.getLogger()
logging.basicConfig()
DEFAULT_BOOTSTRAP_SETREG = pathlib.Path('~/.o3de/Registry/bootstrap.setreg').expanduser()
PROJECT_PATH_KEY = ('Amazon', 'AzCore', 'Bootstrap', 'project_path')
class TestSetGlobalProject:
@pytest.mark.parametrize(
"output_path, project_path, force, expected_result", [
pytest.param(pathlib.Path('~/.o3de/Registry/bootstrap.setreg'), pathlib.Path('A:/'), False, False),
pytest.param(pathlib.Path('~/.o3de/Registry/bootstrap.setreg'), pathlib.Path('A:/'), True, True)
]
)
def test_set_global_project_non_existent_project_path(self, output_path, project_path, force, expected_result):
with patch('pathlib.Path.open', return_value=io.StringIO()) as pathlib_open_mock:
result = global_project.set_global_project(output_path, project_path=project_path, force=force) == 0
assert result == expected_result
+117
View File
@@ -0,0 +1,117 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
import argparse
import json
import logging
import pytest
import pathlib
from unittest.mock import patch
from o3de import register
string_manifest_data = '{}'
@pytest.mark.parametrize(
"engine_path, engine_name, force, expected_result", [
pytest.param(pathlib.PurePath('D:/o3de/o3de'), "o3de", False, 0),
# Same engine_name and path should result in valid registration
pytest.param(pathlib.PurePath('D:/o3de/o3de'), "o3de", False, 0),
# Same engine_name and but different path should fail
pytest.param(pathlib.PurePath('D:/o3de/engine-path'), "o3de", False, 1),
# New engine_name should result in valid registration
pytest.param(pathlib.PurePath('D:/o3de/engine-path'), "o3de-other", False, 0),
# Same engine_name and but different path with --force should result in valid registration
pytest.param(pathlib.PurePath('F:/Open3DEngine'), "o3de", True, 0),
]
)
def test_register_engine_path(engine_path, engine_name, force, expected_result):
parser = argparse.ArgumentParser()
# Register the registration script subparsers with the current argument parser
register.add_parser_args(parser)
arg_list = ['--engine-path', str(engine_path)]
if force:
arg_list += ['--force']
args = parser.parse_args(arg_list)
def load_manifest_from_string() -> dict:
try:
manifest_json = json.loads(string_manifest_data)
except json.JSONDecodeError as err:
logging.error("Error decoding Json from Manifest file")
else:
return manifest_json
def save_manifest_to_string(manifest_json: dict) -> None:
global string_manifest_data
string_manifest_data = json.dumps(manifest_json)
engine_json_data = {'engine_name': engine_name}
with patch('o3de.manifest.load_o3de_manifest', side_effect=load_manifest_from_string) as load_manifest_mock, \
patch('o3de.manifest.save_o3de_manifest', side_effect=save_manifest_to_string) as save_manifest_mock, \
patch('o3de.manifest.get_engine_json_data', return_value=engine_json_data) as engine_paths_mock, \
patch('o3de.validation.valid_o3de_engine_json', return_value=True) as valid_engine_mock, \
patch('pathlib.Path.is_dir', return_value=True) as pathlib_is_dir_mock:
result = register._run_register(args)
assert result == expected_result
@pytest.fixture(scope='class')
def init_manifest_data(request):
class ManifestData:
def __init__(self):
self.json_string = json.dumps({'default_engines_folder': '',
'default_projects_folder': '', 'default_gems_folder': '',
'default_templates_folder': '', 'default_restricted_folder': ''})
request.cls.manifest_data = ManifestData()
@pytest.mark.usefixtures('init_manifest_data')
class TestRegisterThisEngine:
@pytest.mark.parametrize(
"engine_path, engine_name, force, expected_result", [
pytest.param(pathlib.PurePath('D:/o3de/o3de'), "o3de", False, 0),
pytest.param(pathlib.PurePath('F:/Open3DEngine'), "o3de", False, 1),
pytest.param(pathlib.PurePath('F:/Open3DEngine'), "o3de", True, 0)
]
)
def test_register_this_engine(self, engine_path, engine_name, force, expected_result):
parser = argparse.ArgumentParser()
# Register the registration script subparsers with the current argument parser
register.add_parser_args(parser)
arg_list = ['--this-engine']
if force:
arg_list += ['--force']
args = parser.parse_args(arg_list)
def load_manifest_from_string() -> dict:
try:
manifest_json = json.loads(self.manifest_data.json_string)
except json.JSONDecodeError as err:
logging.error("Error decoding Json from Manifest file")
else:
return manifest_json
def save_manifest_to_string(manifest_json: dict) -> None:
self.manifest_data.json_string = json.dumps(manifest_json)
engine_json_data = {'engine_name': engine_name}
with patch('o3de.manifest.load_o3de_manifest', side_effect=load_manifest_from_string) as load_manifest_mock, \
patch('o3de.manifest.save_o3de_manifest', side_effect=save_manifest_to_string) as save_manifest_mock, \
patch('o3de.manifest.get_engine_json_data', return_value=engine_json_data) as engine_paths_mock, \
patch('o3de.manifest.get_this_engine_path', return_value=engine_path) as engine_paths_mock, \
patch('o3de.validation.valid_o3de_engine_json', return_value=True) as valid_engine_mock, \
patch('pathlib.Path.is_dir', return_value=True) as pathlib_is_dir_mock:
result = register._run_register(args)
assert result == expected_result
+42
View File
@@ -0,0 +1,42 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
import pytest
from . import utils
@pytest.mark.parametrize(
"value, expected_result", [
pytest.param('Game1', True),
pytest.param('0Game1', False),
pytest.param('the/Game1', False),
pytest.param('', False),
pytest.param('-test', False),
pytest.param('test-', True),
]
)
def test_validate_identifier(value, expected_result):
result = utils.validate_identifier(value)
assert result == expected_result
@pytest.mark.parametrize(
"value, expected_result", [
pytest.param('{018427ae-cd08-4ff1-ad3b-9b95256c17ca}', False),
pytest.param('', False),
pytest.param('{018427aecd084ff1ad3b9b95256c17ca}', False),
pytest.param('018427ae-cd08-4ff1-ad3b-9b95256c17ca', True),
pytest.param('018427aecd084ff1ad3b9b95256c17ca', False),
pytest.param('018427aecd084ff1ad3b9', False),
]
)
def test_validate_uuid4(value, expected_result):
result = utils.validate_uuid4(value)
assert result == expected_result
File diff suppressed because it is too large Load Diff
+78 -57
View File
@@ -9,54 +9,67 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
import pytest
'''
import os
import sys
import tempfile
import logging
import pathlib
from unittest.mock import MagicMock
logger = logging.getLogger()
# Code lives one folder above
projects_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.append(projects_path)
project_manager_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.append(project_manager_path)
from pyside import add_pyside_environment, is_configuration_valid
from ly_test_tools import WINDOWS
project_marker_file = "project.json"
sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..', '..')))
executable_path = ''
from cmake.Tools import registration
from cmake.Tools import engine_template
class ProjectHelper:
def __init__(self):
self._temp_directory = tempfile.TemporaryDirectory()
self.temp_project_root = self._temp_directory.name
self.temp_file_dir = os.path.join(self.temp_project_root, "o3de")
self._temp_directory = pathlib.Path(tempfile.TemporaryDirectory().name).resolve()
self._temp_directory.mkdir(parents=True, exist_ok=True)
self.home_path = self._temp_directory
registration.override_home_folder = self.home_path
self.engine_path = registration.get_this_engine_path()
if registration.register(engine_path=self.engine_path):
assert True, f"Failed to register the engine."
if registration.register_shipped_engine_o3de_objects():
assert True, f"Failed to register shipped engine objects."
self.projects_folder = registration.get_o3de_projects_folder()
if not self.projects_folder.is_dir():
assert True
self.application = None
self.dialog = None
if not os.path.exists(self.temp_file_dir):
os.makedirs(self.temp_file_dir)
def create_empty_projects(self):
self.project_1_dir = os.path.join(self.temp_project_root, "Project1")
if not os.path.exists(self.project_1_dir):
os.makedirs(self.project_1_dir)
with open(os.path.join(self.project_1_dir,project_marker_file), 'w') as marker_file:
marker_file.write("{}")
self.project_2_dir = os.path.join(self.temp_project_root, "Project2")
if not os.path.exists(self.project_2_dir):
os.makedirs(self.project_2_dir)
with open(os.path.join(self.project_2_dir, project_marker_file), 'w') as marker_file:
marker_file.write("{}")
self.project_3_dir = os.path.join(self.temp_project_root, "Project3")
if not os.path.exists(self.project_3_dir):
os.makedirs(self.project_3_dir)
with open(os.path.join(self.project_3_dir, project_marker_file), 'w') as marker_file:
marker_file.write("{}")
self.invalid_project_dir = os.path.join(self.temp_project_root, "InvalidProject")
self.project_1_dir = self.projects_folder / "Project1"
if engine_template.create_project(project_manager_path=self.project_1_dir):
assert True, f"Failed to create Project1."
self.project_2_dir = self.projects_folder / "Project2"
if engine_template.create_project(project_manager_path=self.project_2_dir):
assert True, f"Failed to create Project2."
self.project_3_dir = self.projects_folder / "Project3"
if engine_template.create_project(project_manager_path=self.project_3_dir):
assert True, f"Failed to create Project3."
self.invalid_project_dir = self.projects_folder / "InvalidProject"
self.invalid_project_dir.mkdir(parents=True, exist_ok=True)
def setup_dialog_test(self, workspace):
add_pyside_environment(workspace.paths.build_directory())
@@ -66,6 +79,7 @@ class ProjectHelper:
# need to use the profile version of PySide which works with the profile QT libs which aren't in the debug
# folder we've built.
return None
from PySide2.QtWidgets import QApplication, QMessageBox
if QApplication.instance():
@@ -74,13 +88,13 @@ class ProjectHelper:
self.application = QApplication(sys.argv)
assert self.application
from projects import ProjectDialog
from projects import ProjectManagerDialog
try:
self.dialog = ProjectDialog(settings_folder=self.temp_file_dir)
self.dialog = ProjectManagerDialog(settings_folder=self.home_path)
return self.dialog
except Exception as e:
logger.error(f'Failed to create ProjectDialog with error {e}')
logger.error(f'Failed to create ProjectManagerDialog with error {e}')
return None
def create_project_from_template(self, project_name) -> bool:
@@ -90,21 +104,24 @@ class ProjectHelper:
:return: True for Success, False for failure
"""
from PySide2.QtWidgets import QWidget, QFileDialog
from projects import ProjectDialog
from projects import ProjectManagerDialog
QWidget.exec = MagicMock()
self.dialog.create_project_handler()
QWidget.exec.assert_called_once()
assert len(self.dialog.project_templates), 'Failed to find any project templates'
ProjectDialog.get_selected_project_template = MagicMock(return_value=self.dialog.project_templates[0])
ProjectManagerDialog.get_selected_project_template = MagicMock(return_value=self.dialog.project_templates[0])
QFileDialog.exec = MagicMock()
create_project_dir = os.path.join(self.temp_project_root, project_name)
QFileDialog.selectedFiles = MagicMock(return_value=[create_project_dir])
create_project_path = self.projects_folder / project_name
QFileDialog.selectedFiles = MagicMock(return_value=[create_project_path])
self.dialog.create_project_accepted_handler()
assert os.path.isdir(create_project_dir), f"Expected project folder not found at {create_project_dir}"
assert QWidget.exec.call_count == 2, "Message box confirming project creation failed to show"
if create_project_path.is_dir():
assert True, f"Expected project creation folder not found at {create_project_path}"
if QWidget.exec.call_count == 2:
assert True, "Message box confirming project creation failed to show"
@pytest.fixture
@@ -113,7 +130,7 @@ def project_helper():
@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently")
@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent
@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent
def test_logger_handler(workspace, project_helper):
my_dialog = project_helper.setup_dialog_test(workspace)
if not my_dialog:
@@ -126,7 +143,7 @@ def test_logger_handler(workspace, project_helper):
@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently")
@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent
@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent
def test_mru_list(workspace, project_helper):
my_dialog = project_helper.setup_dialog_test(workspace)
if not my_dialog:
@@ -140,16 +157,16 @@ def test_mru_list(workspace, project_helper):
assert len(mru_list) == 0, f'MRU list unexpectedly had entries: {mru_list}'
QMessageBox.warning = MagicMock()
my_dialog.add_new_project('TestProjectInvalid')
my_dialog.add_project(project_helper.invalid_project_dir)
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 0, f'MRU list unexpectedly added an invalid project : {mru_list}'
QMessageBox.warning.assert_called_once()
my_dialog.add_new_project(project_helper.project_1_dir)
my_dialog.add_project(project_helper.project_1_dir)
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 1, f'MRU list failed to add project at {project_helper.project_1_dir}'
my_dialog.add_new_project(project_helper.project_1_dir)
my_dialog.add_project(project_helper.project_1_dir)
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 1, f'MRU list added project at {project_helper.project_1_dir} a second time : {mru_list}'
@@ -157,7 +174,7 @@ def test_mru_list(workspace, project_helper):
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 1, f'MRU list added project at {project_helper.project_1_dir} a second time : {mru_list}'
my_dialog.add_new_project(project_helper.project_2_dir)
my_dialog.add_project(project_helper.project_2_dir)
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 2, f'MRU list failed to add project at {project_helper.project_2_dir}'
@@ -170,13 +187,13 @@ def test_mru_list(workspace, project_helper):
assert mru_list[0] == project_helper.project_1_dir, f"{project_helper.project_1_dir} wasn't first item"
assert mru_list[1] == project_helper.project_2_dir, f"{project_helper.project_2_dir} wasn't second item"
my_dialog.add_new_project(project_helper.invalid_project_dir)
my_dialog.add_project(project_helper.invalid_project_dir)
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 2, f'MRU list added invalid item {mru_list}'
assert mru_list[0] == project_helper.project_1_dir, f"{project_helper.project_1_dir} wasn't first item"
assert mru_list[1] == project_helper.project_2_dir, f"{project_helper.project_2_dir} wasn't second item"
my_dialog.add_new_project(project_helper.project_3_dir)
my_dialog.add_project(project_helper.project_3_dir)
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 3, f'MRU list failed to add {project_helper.project_3_dir} : {mru_list}'
assert mru_list[0] == project_helper.project_3_dir, f"{project_helper.project_3_dir} wasn't first item"
@@ -185,7 +202,7 @@ def test_mru_list(workspace, project_helper):
@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently")
@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent
@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent
def test_create_project(workspace, project_helper):
my_dialog = project_helper.setup_dialog_test(workspace)
if not my_dialog:
@@ -195,7 +212,7 @@ def test_create_project(workspace, project_helper):
@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently")
@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent
@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent
def test_add_remove_gems(workspace, project_helper):
my_dialog = project_helper.setup_dialog_test(workspace)
if not my_dialog:
@@ -203,32 +220,36 @@ def test_add_remove_gems(workspace, project_helper):
my_project_name = "TestAddRemoveGems"
project_helper.create_project_from_template(my_project_name)
my_project_path = os.path.join(project_helper.temp_project_root, my_project_name)
project_helper.create_project_from_template(project_manager_path=my_project_name)
my_project_path = project_helper.projects_folder / my_project_name
from PySide2.QtWidgets import QWidget, QFileDialog
from projects import ProjectDialog
from projects import ProjectManagerDialog
assert my_dialog.path_for_selection() == my_project_path, "Gems project not selected"
assert my_dialog.get_selected_project_path() == my_project_path, "TestAddRemoveGems project not selected"
QWidget.exec = MagicMock()
my_dialog.manage_gems_handler()
assert my_dialog.manage_gems_dialog, "No gem management dialog created"
assert my_dialog.manage_gem_targets_dialog, "No gem management dialog created"
QWidget.exec.assert_called_once()
assert len(my_dialog.gems_list), 'Failed to find any gems'
my_test_gem = my_dialog.gems_list[0]
my_test_gem_name = my_test_gem.get("Name")
my_test_gem_path = my_test_gem.get("Path")
my_test_gem_selection = (my_test_gem_name, my_test_gem_path)
ProjectDialog.get_selected_add_gems = MagicMock(return_value=[my_test_gem_selection])
if not len(my_dialog.all_gems_list):
assert True, 'Failed to find any gems'
my_test_gem_path = my_dialog.all_gems_list[0]
gem_data = registration.get_gem_data(my_test_gem_path)
my_test_gem_selection = (my_test_gem_name, my_test_gem_path)
ProjectManagerDialog.get_selected_add_gems = MagicMock(return_value=[my_test_gem_selection])
assert my_test_gem_name, "No Name set in test gem"
assert my_test_gem_name not in my_dialog.project_gem_list, f'Gem {my_test_gem_name} already in project gem list'
my_dialog.add_gems_handler()
assert my_test_gem_name in my_dialog.project_gem_list, f'Gem {my_test_gem_name} failed to add to gem list'
ProjectDialog.get_selected_project_gems = MagicMock(return_value=[my_test_gem_name])
ProjectManagerDialog.get_selected_project_gems = MagicMock(return_value=[my_test_gem_name])
my_dialog.remove_gems_handler()
assert my_test_gem_name not in my_dialog.project_gem_list, f'Gem {my_test_gem_name} still in project gem list'
'''
def test_project_place_holder():
pass
@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>createFromTemplateDialog</class>
<widget class="QDialog" name="createFromTemplateDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>467</width>
<height>288</height>
</rect>
</property>
<property name="windowTitle">
<string>Create From Template</string>
</property>
<widget class="QDialogButtonBox" name="okCancel">
<property name="geometry">
<rect>
<x>50</x>
<y>250</y>
<width>400</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QListView" name="genericTemplates">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>449</width>
<height>221</height>
</rect>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>10</x>
<y>0</y>
<width>300</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Available Templates</string>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>okCancel</sender>
<signal>accepted()</signal>
<receiver>createFromTemplateDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>okCancel</sender>
<signal>rejected()</signal>
<receiver>createFromTemplateDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
+94
View File
@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>createGemDialog</class>
<widget class="QDialog" name="createGemDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>467</width>
<height>288</height>
</rect>
</property>
<property name="windowTitle">
<string>Create Gem</string>
</property>
<widget class="QDialogButtonBox" name="okCancel">
<property name="geometry">
<rect>
<x>50</x>
<y>250</y>
<width>400</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QListView" name="gemTemplates">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>449</width>
<height>221</height>
</rect>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>10</x>
<y>0</y>
<width>300</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Available Templates</string>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>okCancel</sender>
<signal>accepted()</signal>
<receiver>createGemDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>okCancel</sender>
<signal>rejected()</signal>
<receiver>createGemDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
+4 -4
View File
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>226</width>
<width>467</width>
<height>288</height>
</rect>
</property>
@@ -18,7 +18,7 @@
<rect>
<x>50</x>
<y>250</y>
<width>171</width>
<width>400</width>
<height>32</height>
</rect>
</property>
@@ -34,7 +34,7 @@
<rect>
<x>10</x>
<y>20</y>
<width>211</width>
<width>449</width>
<height>221</height>
</rect>
</property>
@@ -47,7 +47,7 @@
<rect>
<x>10</x>
<y>0</y>
<width>101</width>
<width>300</width>
<height>16</height>
</rect>
</property>
@@ -1,24 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>addGemsDialog</class>
<widget class="QDialog" name="addGemsDialog">
<class>manageGemTargetsDialog</class>
<widget class="QDialog" name="manageGemTargetsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>635</width>
<height>327</height>
<width>702</width>
<height>297</height>
</rect>
</property>
<property name="windowTitle">
<string>Manage Gems</string>
<string>Manage Gem Targets</string>
</property>
<widget class="QDialogButtonBox" name="close">
<property name="geometry">
<rect>
<x>460</x>
<y>290</y>
<width>171</width>
<x>310</x>
<y>260</y>
<width>71</width>
<height>32</height>
</rect>
</property>
@@ -29,10 +29,10 @@
<set>QDialogButtonBox::Close</set>
</property>
</widget>
<widget class="QListView" name="addGemsList">
<widget class="QListView" name="availableGemTargetsList">
<property name="geometry">
<rect>
<x>380</x>
<x>440</x>
<y>50</y>
<width>250</width>
<height>221</height>
@@ -48,7 +48,7 @@
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
<widget class="QListView" name="projectGems">
<widget class="QListView" name="enabledGemTargetsList">
<property name="geometry">
<rect>
<x>10</x>
@@ -64,14 +64,14 @@
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>380</x>
<x>440</x>
<y>30</y>
<width>91</width>
<width>251</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Available Gems</string>
<string>Available Gem Targets</string>
</property>
</widget>
<widget class="QLabel" name="label_4">
@@ -79,38 +79,38 @@
<rect>
<x>10</x>
<y>30</y>
<width>71</width>
<width>251</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Enabled Gems</string>
<string>Enabled Gem Targets</string>
</property>
</widget>
<widget class="QPushButton" name="removeGemsButton">
<widget class="QPushButton" name="removeGemTargetsButton">
<property name="geometry">
<rect>
<x>267</x>
<x>264</x>
<y>130</y>
<width>105</width>
<width>171</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Remove Gems &gt;&gt;</string>
<string>Remove Gem Targets &gt;&gt;</string>
</property>
</widget>
<widget class="QPushButton" name="addGemsButton">
<widget class="QPushButton" name="addGemTargetsButton">
<property name="geometry">
<rect>
<x>267</x>
<x>264</x>
<y>100</y>
<width>105</width>
<width>171</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>&lt;&lt; Add Gems</string>
<string>&lt;&lt; Add Gem Targets</string>
</property>
</widget>
<widget class="QLabel" name="label">
@@ -118,12 +118,12 @@
<rect>
<x>10</x>
<y>5</y>
<width>241</width>
<height>16</height>
<width>400</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Adding new Gems may require rebuilding</string>
<string>Adding new Gem Targets may require rebuilding</string>
</property>
</widget>
</widget>
@@ -132,7 +132,7 @@
<connection>
<sender>close</sender>
<signal>accepted()</signal>
<receiver>addGemsDialog</receiver>
<receiver>manageGemTargetsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
@@ -148,7 +148,7 @@
<connection>
<sender>close</sender>
<signal>rejected()</signal>
<receiver>addGemsDialog</receiver>
<receiver>manageGemTargetsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
@@ -0,0 +1,407 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>712</width>
<height>395</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>O3DE</string>
</property>
<property name="whatsThis">
<string>Select and manage your projects for O3DE</string>
</property>
<widget class="QDialogButtonBox" name="okCancel">
<property name="geometry">
<rect>
<x>540</x>
<y>360</y>
<width>161</width>
<height>31</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QComboBox" name="projectListBox">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>691</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Current project to launch or manage gems for.</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>47</width>
<height>13</height>
</rect>
</property>
<property name="text">
<string>Project</string>
</property>
</widget>
<widget class="QLabel" name="logDisplay">
<property name="geometry">
<rect>
<x>270</x>
<y>220</y>
<width>431</width>
<height>141</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
<widget class="QGroupBox" name="createGroupBox">
<property name="geometry">
<rect>
<x>10</x>
<y>70</y>
<width>241</width>
<height>151</height>
</rect>
</property>
<property name="title">
<string>Create</string>
</property>
<widget class="QPushButton" name="createFromTemplateButton">
<property name="geometry">
<rect>
<x>10</x>
<y>110</y>
<width>221</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Create a new O3DE object from a pre configured template.</string>
</property>
<property name="text">
<string>Create From Template</string>
</property>
</widget>
<widget class="QPushButton" name="createProjectButton">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>221</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Create a new O3DE object from a pre configured template.</string>
</property>
<property name="text">
<string>Create Project</string>
</property>
</widget>
<widget class="QPushButton" name="createGemButton">
<property name="geometry">
<rect>
<x>11</x>
<y>50</y>
<width>221</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Create a new O3DE object from a pre configured template.</string>
</property>
<property name="text">
<string>Create Gem</string>
</property>
</widget>
<widget class="QPushButton" name="createTemplateButton">
<property name="geometry">
<rect>
<x>11</x>
<y>80</y>
<width>221</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Create a new O3DE object from a pre configured template.</string>
</property>
<property name="text">
<string>Create Template</string>
</property>
</widget>
</widget>
<widget class="QGroupBox" name="addRemoveGroupBox">
<property name="geometry">
<rect>
<x>260</x>
<y>70</y>
<width>451</width>
<height>141</height>
</rect>
</property>
<property name="title">
<string>Registration</string>
</property>
<widget class="QPushButton" name="addTemplateButton">
<property name="geometry">
<rect>
<x>10</x>
<y>80</y>
<width>211</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Browse for an existing O3DE project.</string>
</property>
<property name="text">
<string>Add Template</string>
</property>
</widget>
<widget class="QPushButton" name="addProjectButton">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>211</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Browse for an existing O3DE project.</string>
</property>
<property name="text">
<string>Add Project</string>
</property>
</widget>
<widget class="QPushButton" name="addGemButton">
<property name="geometry">
<rect>
<x>10</x>
<y>50</y>
<width>211</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Browse for an existing O3DE project.</string>
</property>
<property name="text">
<string>Add Gem</string>
</property>
</widget>
<widget class="QPushButton" name="addRestrictedButton">
<property name="geometry">
<rect>
<x>10</x>
<y>110</y>
<width>211</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Browse for an existing O3DE project.</string>
</property>
<property name="text">
<string>Add Restricted</string>
</property>
</widget>
<widget class="QPushButton" name="removeRestrictedButton">
<property name="geometry">
<rect>
<x>230</x>
<y>110</y>
<width>211</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Browse for an existing O3DE project.</string>
</property>
<property name="text">
<string>Remove Restricted</string>
</property>
</widget>
<widget class="QPushButton" name="removeProjectButton">
<property name="geometry">
<rect>
<x>230</x>
<y>20</y>
<width>211</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Browse for an existing O3DE project.</string>
</property>
<property name="text">
<string>Remove Project</string>
</property>
</widget>
<widget class="QPushButton" name="removeGemButton">
<property name="geometry">
<rect>
<x>230</x>
<y>50</y>
<width>211</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Browse for an existing O3DE project.</string>
</property>
<property name="text">
<string>Remove Gem</string>
</property>
</widget>
<widget class="QPushButton" name="removeTemplateButton">
<property name="geometry">
<rect>
<x>230</x>
<y>80</y>
<width>211</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Browse for an existing O3DE project.</string>
</property>
<property name="text">
<string>Remove Template</string>
</property>
</widget>
</widget>
<widget class="QGroupBox" name="manageProjectGroupBox">
<property name="geometry">
<rect>
<x>10</x>
<y>230</y>
<width>241</width>
<height>121</height>
</rect>
</property>
<property name="title">
<string>Manage Project</string>
</property>
<widget class="QPushButton" name="manageServerGemTargetsButton">
<property name="geometry">
<rect>
<x>10</x>
<y>80</y>
<width>221</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Add or remove gems from your selected project. Gems add and remove additional assets and features to projects.</string>
</property>
<property name="text">
<string>Manage Server Gem Targets</string>
</property>
</widget>
<widget class="QPushButton" name="manageToolGemTargetsButton">
<property name="geometry">
<rect>
<x>10</x>
<y>50</y>
<width>221</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Add or remove gems from your selected project. Gems add and remove additional assets and features to projects.</string>
</property>
<property name="text">
<string>Manage Tool Gem Targets</string>
</property>
</widget>
<widget class="QPushButton" name="manageRuntimeGemTargetsButton">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>221</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Add or remove gems from your selected project. Gems add and remove additional assets and features to projects.</string>
</property>
<property name="text">
<string>Manage Runtime Gem Targets</string>
</property>
</widget>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>okCancel</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>okCancel</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
-167
View File
@@ -1,167 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>464</width>
<height>136</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>O3DE</string>
</property>
<property name="whatsThis">
<string>Select and manage your projects for O3DE</string>
</property>
<widget class="QDialogButtonBox" name="okCancel">
<property name="geometry">
<rect>
<x>290</x>
<y>100</y>
<width>171</width>
<height>31</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QComboBox" name="projectListBox">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>451</width>
<height>31</height>
</rect>
</property>
<property name="whatsThis">
<string>Current project to launch or manage gems for.</string>
</property>
</widget>
<widget class="QPushButton" name="createProjectButton">
<property name="geometry">
<rect>
<x>10</x>
<y>70</y>
<width>91</width>
<height>23</height>
</rect>
</property>
<property name="whatsThis">
<string>Create a new O3DE project from a pre configured template.</string>
</property>
<property name="text">
<string>Create New</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>47</width>
<height>13</height>
</rect>
</property>
<property name="text">
<string>Project</string>
</property>
</widget>
<widget class="QPushButton" name="browseProjectsButton">
<property name="geometry">
<rect>
<x>110</x>
<y>70</y>
<width>91</width>
<height>23</height>
</rect>
</property>
<property name="whatsThis">
<string>Browse for an existing O3DE project.</string>
</property>
<property name="text">
<string>Browse</string>
</property>
</widget>
<widget class="QPushButton" name="manageGemsButton">
<property name="geometry">
<rect>
<x>350</x>
<y>70</y>
<width>91</width>
<height>23</height>
</rect>
</property>
<property name="whatsThis">
<string>Add or remove gems from your selected project. Gems add and remove additional assets and features to projects.</string>
</property>
<property name="text">
<string>Manage Gems</string>
</property>
</widget>
<widget class="QLabel" name="logDisplay">
<property name="geometry">
<rect>
<x>5</x>
<y>110</y>
<width>275</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>okCancel</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>okCancel</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
+1 -1
View File
@@ -243,7 +243,7 @@ class Validator(object):
validations += 1
counter += 1
# Trim out whitelisted subdirectories in the current directory if allowed
# Trim out allowlisted subdirectories in the current directory if allowed
for name in bypassed_directories:
if name in dirnames:
dirnames.remove(name)