Updated Several Engine Gem's CMakeLists.txt to add themselves as required Gems (#1262)

* Fixed organization of the AssetProcessor SourceAssetBrowser

Assets within the Engine Root were grouped under a '/' entry.
That has been fixed to use the relative path within the engine root for
those assets
Assets outside of the Engine Root, but on the same drive were using
absolute paths before. Now there are child entries that navigate up the
directory hierarchy to those asset locations

* Added ly_enable_gems call to Atom gems targets that are required

The DefaultLevel.prefab contains several Atom components, that require
the Atom RHI, RPI, Common_Feature, ShaderBuilder and AtomLyIntegration CommonFeatures
gems to be enabled in order to successfully process in the
AssetProcessor.

* Added ly_enable_gems call to make the Camera gem required in Tools,
Builders and Clients.

This is needed as the DefaultLevel.prefab contains an Editor Camera
Component

* Adding the ly_enable_gem call to make the Maestro gem required

CrySystem currently requires Maestro to be enabled in order to
initialize

* Added ly_enable_gems call to the SceneProcessing gem to make it required

The SceneCore and SceneData libraries that are part of the core engine
Code folder requires the SceneProcessing gem to be enabled in order to
invoke the InitializeDynamicModule hooks in DllMain.cpp in order to
initialize those libraries.

* Fixed bad argument in comment for Prefab CMakeLists.txt

* Fixed Assert in Asset Builders due to the Atom RPI Builder

The Atom RPI Builder was enabling the Asset Catalog for the ScriptAsset a second time

The Atom Feature Common EditorSystemCommonComponent.cpp which also loads
in the AssetBuilder is enabling the Asset Catalog for the ScriptAsset

Added BehaviorContext reflection to the OutputDeviceTransformType enum
to fix the BehaviorContext errors about reflecting a method that returns
such an enum

* Added TypeId output to the JsonDeserializer report message about missing
ClassData

Previously the report callback would indicate that the target type was
missing Serialization class data, but didn't indicate the TypeId of the
target type

* Added support to the ly_enable_gems function to be able to support
0 gems being enabled.

Updated the Install step for CMake to propagate any ly_enable_gems
within a CMakeLists.txt for a target into the generated CMakeLists.txt
that is made for each installed IMPORTED target

* Adding newline to the end of the Camera Gem CMakeLists.txt

* Fixing target TYPE parameter for actual Gem Modules to use the GEM_MODULE tag instead of MODULE

* Reverting change to the DESTINATION directory for the installed CMakeLists.txt to use the relative path to the installed directory

* Adding the Atom_Bootstrap gem as a required gem

The Client and GameLaunchers required the Atom_Bootstrap gem in order to create the NativeWindow
Added Atom_Feature_Common client module as a runtime dependency of the AtomLyIntegration CommonsFeature client module

* Fixed register.py --all-projects-path and --all-gems-path arguments to
NOT register projects or gems that are within a template folder
Fixed reading of old pre-1.0 o3de_manifest.json files where the
"engines" key was a json array

* Changed how the relative target source directory is calculated when that source directroy resides outside of the engine root.
The final dirname component is used with a unique SHA256 has to form a <dirname>-<8 char SHA256> folder for installing files into

* Adding newline to the end of Atom_Bootstrap CMakeLists.txt

* Moving ly_enable_gems variants for Tools and Builders inside of PAL_TRAIT_BUILD_HOST_TOOLS block

* Adding a comment to AWSCore.ResourceMappingTool target to indicate that it is not a GEM_MODULE.
Furthermore it cannot be loaded with the Gem system because the library is in a different directory the executable
main
lumberyard-employee-dm 5 years ago committed by GitHub
parent 4be9ce56c4
commit 7dabe8b6e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -19,6 +19,7 @@
#include <AzCore/Serialization/Json/RegistrationContext.h> #include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Serialization/Json/StackedString.h> #include <AzCore/Serialization/Json/StackedString.h>
#include <AzCore/std/string/conversions.h> #include <AzCore/std/string/conversions.h>
#include <AzCore/std/string/fixed_string.h>
#include <AzCore/std/string/string.h> #include <AzCore/std/string/string.h>
namespace AZ namespace AZ
@ -595,7 +596,9 @@ namespace AZ
} }
else else
{ {
status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown, "Serialization information for target type not found."); using ReporterString = AZStd::fixed_string<1024>;
status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown,
ReporterString::format("Serialization information for target type %s not found.", loadedTypeId.m_typeId.ToString<ReporterString>().c_str()));
return ResolvePointerResult::FullyProcessed; return ResolvePointerResult::FullyProcessed;
} }
objectType = loadedTypeId.m_typeId; objectType = loadedTypeId.m_typeId;

@ -10,5 +10,6 @@
"version_number" : 1, "version_number" : 1,
"version_name" : "1.0.0.0", "version_name" : "1.0.0.0",
"orientation" : "landscape" "orientation" : "landscape"
} },
"engine" : "o3de"
} }

@ -63,7 +63,7 @@ namespace AssetProcessor
} }
AZ::IO::Path fullPath = AZ::IO::Path(scanFolder.m_scanFolder, AZ::IO::PosixPathSeparator) / source.m_sourceName; AZ::IO::Path fullPath = AZ::IO::Path(scanFolder.m_scanFolder) / source.m_sourceName;
// It's common for Open 3D Engine game projects and scan folders to be in a subfolder // It's common for Open 3D Engine game projects and scan folders to be in a subfolder
// of the engine install. To improve readability of the source files, strip out // of the engine install. To improve readability of the source files, strip out
@ -74,7 +74,7 @@ namespace AssetProcessor
} }
if (m_assetRootSet) if (m_assetRootSet)
{ {
AzFramework::StringFunc::Replace(fullPath.Native(), m_assetRoot.absolutePath().toUtf8(), ""); fullPath = fullPath.LexicallyProximate(m_assetRoot.absolutePath().toUtf8().constData());
} }
if (fullPath.empty()) if (fullPath.empty())
@ -88,11 +88,12 @@ namespace AssetProcessor
QModelIndex newIndicesStart; QModelIndex newIndicesStart;
AssetTreeItem* parentItem = m_root.get(); AssetTreeItem* parentItem = m_root.get();
AZ::IO::Path currentFullFolderPath; // Use posix path separator for each child item
const AZ::IO::PathView filename = fullPath.Filename(); AZ::IO::Path currentFullFolderPath(AZ::IO::PosixPathSeparator);
const AZ::IO::PathView fullPathWithoutFilename = fullPath.RemoveFilename(); const AZ::IO::FixedMaxPath filename = fullPath.Filename();
fullPath.RemoveFilename();
AZStd::fixed_string<AZ::IO::MaxPathLength> currentPath; AZStd::fixed_string<AZ::IO::MaxPathLength> currentPath;
for (auto pathIt = fullPathWithoutFilename.begin(); pathIt != fullPathWithoutFilename.end(); ++pathIt) for (auto pathIt = fullPath.begin(); pathIt != fullPath.end(); ++pathIt)
{ {
currentPath = pathIt->FixedMaxPathString(); currentPath = pathIt->FixedMaxPathString();
currentFullFolderPath /= currentPath; currentFullFolderPath /= currentPath;

@ -74,7 +74,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
) )
ly_add_target( ly_add_target(
NAME AWSCore.Editor MODULE NAME AWSCore.Editor GEM_MODULE
NAMESPACE Gem NAMESPACE Gem
FILES_CMAKE FILES_CMAKE
awscore_editor_shared_files.cmake awscore_editor_shared_files.cmake
@ -89,6 +89,8 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
Gem::AWSCore Gem::AWSCore
) )
# This target is not a real gem module
# It is not meant to be loaded by the ModuleManager in C++
ly_add_target( ly_add_target(
NAME AWSCore.ResourceMappingTool MODULE NAME AWSCore.ResourceMappingTool MODULE
NAMESPACE Gem NAMESPACE Gem

@ -102,6 +102,11 @@ ly_add_target(
3rdParty::azslc 3rdParty::azslc
) )
# The Atom_Asset_Shader is a required gem for Builders in order to process the assets that come WITHOUT
# the Atom_Feature_Common required gem
ly_enable_gems(GEMS Atom_Asset_Shader VARIANTS Builders
TARGETS AssetBuilder AssetProcessor AssetProcessorBatch)
################################################################################ ################################################################################
# Tests # Tests
################################################################################ ################################################################################

@ -37,3 +37,20 @@ ly_add_target(
Legacy::CryCommon Legacy::CryCommon
Gem::Atom_RPI.Public Gem::Atom_RPI.Public
) )
# Atom_Bootstrap is only used in Launchers
ly_create_alias(NAME Atom_Bootstrap.Clients NAMESPACE Gem TARGETS Gem::Atom_Bootstrap)
ly_create_alias(NAME Atom_Bootstrap.Servers NAMESPACE Gem TARGETS Gem::Atom_Bootstrap)
# The Atom_Bootstrap gem is responsible for making the NativeWindow handle in the launcher applications
# Loop over each Project name to allow the ${ProjectName}.GameLauncher and ${ProjectName}.ServerLauncher
# target to add the gem the Clients and Servers variant
get_property(LY_PROJECTS_TARGET_NAME GLOBAL PROPERTY LY_PROJECTS_TARGET_NAME)
foreach(project_name IN LISTS LY_PROJECTS_TARGET_NAME)
# Add gem as a dependency of the Clients Launcher
ly_enable_gems(PROJECT_NAME ${project_name} GEMS Atom_Bootstrap VARIANTS Clients TARGETS ${project_name}.GameLauncher)
# Add gem as a dependency of the Servers Launcher
if(PAL_TRAIT_BUILD_SERVER_SUPPORTED)
ly_enable_gems(PROJECT_NAME ${project_name} GEMS Atom_Bootstrap VARIANTS Servers TARGETS ${project_name}.ServerLauncher)
endif()
endforeach()

@ -246,8 +246,8 @@ float ProjectedShadow::GetVisibilityEsm()
const float occluder = shadowmap.Sample( const float occluder = shadowmap.Sample(
PassSrg::LinearSampler, PassSrg::LinearSampler,
float3(atlasPosition.xy * invAtlasSize, atlasPosition.z)).r; float3(atlasPosition.xy * invAtlasSize, atlasPosition.z)).r;
const float exponent = -ViewSrg::m_projectedShadows[m_shadowIndex].m_esmExponent * (depth - occluder); const float exponent = -ViewSrg::m_projectedShadows[m_shadowIndex].m_esmExponent * (depth - occluder);
const float ratio = exp(exponent); const float ratio = exp(exponent);
// pow() mitigates light bleeding to shadows from near shadow casters. // pow() mitigates light bleeding to shadows from near shadow casters.
return saturate( pow(ratio, 8) ); return saturate( pow(ratio, 8) );
@ -287,8 +287,8 @@ float ProjectedShadow::GetVisibilityEsmPcf()
const float occluder = shadowmap.Sample( const float occluder = shadowmap.Sample(
PassSrg::LinearSampler, PassSrg::LinearSampler,
float3(atlasPosition.xy * invAtlasSize, atlasPosition.z)).r; float3(atlasPosition.xy * invAtlasSize, atlasPosition.z)).r;
const float exponent = -ViewSrg::m_projectedShadows[m_shadowIndex].m_esmExponent * (depth - occluder); const float exponent = -ViewSrg::m_projectedShadows[m_shadowIndex].m_esmExponent * (depth - occluder);
float ratio = exp(exponent); float ratio = exp(exponent);
static const float pcfFallbackThreshold = 1.04; static const float pcfFallbackThreshold = 1.04;

@ -71,7 +71,7 @@ void MainCS(uint3 dispatchId: SV_DispatchThreadID)
// Todo: Expose Esm exponent slider for directional lights // Todo: Expose Esm exponent slider for directional lights
// This would remove the exp calculation below, collapsing it into a subtraction in DirectionalLightShadow.azsli // This would remove the exp calculation below, collapsing it into a subtraction in DirectionalLightShadow.azsli
// ATOM-15775 // ATOM-15775
const float outValue = exp(EsmExponentialShift * depth); const float outValue = exp(EsmExponentialShift * depth);
PassSrg::m_outputShadowmap[dispatchId].r = outValue; PassSrg::m_outputShadowmap[dispatchId].r = outValue;
break; break;

@ -76,6 +76,8 @@ ly_add_target(
FILES_CMAKE FILES_CMAKE
atom_feature_common_shared_files.cmake atom_feature_common_shared_files.cmake
../Assets/atom_feature_common_asset_files.cmake ../Assets/atom_feature_common_asset_files.cmake
PLATFORM_INCLUDE_FILES
${pal_source_dir}/runtime_dependencies_clients.cmake
INCLUDE_DIRECTORIES INCLUDE_DIRECTORIES
PRIVATE PRIVATE
Source Source
@ -99,6 +101,8 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
NAMESPACE Gem NAMESPACE Gem
FILES_CMAKE FILES_CMAKE
atom_feature_common_editor_files.cmake atom_feature_common_editor_files.cmake
PLATFORM_INCLUDE_FILES
${pal_source_dir}/runtime_dependencies_tools.cmake
INCLUDE_DIRECTORIES INCLUDE_DIRECTORIES
PRIVATE PRIVATE
. .
@ -130,12 +134,16 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
NAMESPACE Gem NAMESPACE Gem
FILES_CMAKE FILES_CMAKE
atom_feature_common_builders_files.cmake atom_feature_common_builders_files.cmake
PLATFORM_INCLUDE_FILES
${pal_source_dir}/runtime_dependencies_tools.cmake
INCLUDE_DIRECTORIES INCLUDE_DIRECTORIES
PRIVATE PRIVATE
Source/Builders Source/Builders
BUILD_DEPENDENCIES BUILD_DEPENDENCIES
PRIVATE PRIVATE
AZ::AzCore AZ::AzCore
RUNTIME_DEPENDENCIES
Gem::Atom_RHI.Private
) )
endif() endif()

@ -50,6 +50,19 @@ namespace AZ
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context)) if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{ {
behaviorContext->Class<OutputDeviceTransformType>()
->Enum<OutputDeviceTransformType::OutputDeviceTransformType_48Nits>("OutputDeviceTransformType_48Nits")
->Attribute(AZ::Script::Attributes::Module, "atom")
->Enum<OutputDeviceTransformType::OutputDeviceTransformType_1000Nits>("OutputDeviceTransformType_100Nits")
->Attribute(AZ::Script::Attributes::Module, "atom")
->Enum<OutputDeviceTransformType::OutputDeviceTransformType_2000Nits>("OutputDeviceTransformType_2000Nits")
->Attribute(AZ::Script::Attributes::Module, "atom")
->Enum<OutputDeviceTransformType::OutputDeviceTransformType_4000Nits>("OutputDeviceTransformType_4000Nits")
->Attribute(AZ::Script::Attributes::Module, "atom")
->Enum<OutputDeviceTransformType::NumOutputDeviceTransformTypes>("OutputDeviceTransformType_NumOutputDeviceTransformTypes")
->Attribute(AZ::Script::Attributes::Module, "atom")
;
behaviorContext->Class<AcesParameterOverrides>("AcesParameterOverrides") behaviorContext->Class<AcesParameterOverrides>("AcesParameterOverrides")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "render") ->Attribute(AZ::Script::Attributes::Category, "render")

@ -0,0 +1,14 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(LY_RUNTIME_DEPENDENCIES
Gem::Atom_RHI_Vulkan.Private
)

@ -9,7 +9,5 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# #
set(LY_COMPILE_OPTIONS set(LY_RUNTIME_DEPENDENCIES
PRIVATE
/EHsc
) )

@ -0,0 +1,14 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(LY_RUNTIME_DEPENDENCIES
Gem::Atom_RHI_Vulkan.Private
)

@ -0,0 +1,19 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(LY_RUNTIME_DEPENDENCIES
Gem::Atom_RHI_Vulkan.Private
Gem::Atom_RHI_DX12.Private
Gem::Atom_RHI_Metal.Private
Gem::Atom_RHI_Vulkan.Builders
Gem::Atom_RHI_DX12.Builders
Gem::Atom_RHI_Metal.Builders
)

@ -0,0 +1,14 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(LY_RUNTIME_DEPENDENCIES
Gem::Atom_RHI_Metal.Private
)

@ -0,0 +1,19 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(LY_RUNTIME_DEPENDENCIES
Gem::Atom_RHI_Metal.Private
Gem::Atom_RHI_Vulkan.Private
Gem::Atom_RHI_DX12.Private
Gem::Atom_RHI_Metal.Builders
Gem::Atom_RHI_Vulkan.Builders
Gem::Atom_RHI_DX12.Builders
)

@ -0,0 +1,16 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(LY_RUNTIME_DEPENDENCIES
Gem::Atom_RHI_Vulkan.Private
Gem::Atom_RHI_DX12.Private
Gem::Atom_RHI_Null.Private
)

@ -0,0 +1,19 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(LY_RUNTIME_DEPENDENCIES
Gem::Atom_RHI_Vulkan.Private
Gem::Atom_RHI_DX12.Private
Gem::Atom_RHI_Metal.Private
Gem::Atom_RHI_Vulkan.Builders
Gem::Atom_RHI_DX12.Builders
Gem::Atom_RHI_Metal.Builders
)

@ -0,0 +1,14 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(LY_RUNTIME_DEPENDENCIES
Gem::Atom_RHI_Metal.Private
)

@ -9,7 +9,5 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# #
set(LY_COMPILE_OPTIONS set(LY_RUNTIME_DEPENDENCIES
PRIVATE
-fexceptions
) )

@ -486,7 +486,7 @@ namespace AZ
AZ_Warning("ProfilingCaptureSystemComponent", false, captureInfo.c_str()); AZ_Warning("ProfilingCaptureSystemComponent", false, captureInfo.c_str());
} }
else else
{ {
AZ_Printf("ProfilingCaptureSystemComponent", "Cpu profiling statistics was saved to file [%s]\n", outputFilePath.c_str()); AZ_Printf("ProfilingCaptureSystemComponent", "Cpu profiling statistics was saved to file [%s]\n", outputFilePath.c_str());
} }
@ -500,7 +500,7 @@ namespace AZ
ProfilingCaptureNotificationBus::Broadcast(&ProfilingCaptureNotificationBus::Events::OnCaptureCpuProfilingStatisticsFinished, ProfilingCaptureNotificationBus::Broadcast(&ProfilingCaptureNotificationBus::Events::OnCaptureCpuProfilingStatisticsFinished,
saveResult.IsSuccess(), saveResult.IsSuccess(),
captureInfo); captureInfo);
}); });
// Start the TickBus. // Start the TickBus.

@ -219,7 +219,7 @@ namespace AZ
{ {
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzRender, "main per-frame work"); AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzRender, "main per-frame work");
m_frameScheduler.BeginFrame(); m_frameScheduler.BeginFrame();
frameGraphCallback(m_frameScheduler); frameGraphCallback(m_frameScheduler);
/** /**

@ -66,6 +66,8 @@ ly_add_target(
Gem::Atom_RPI.Public Gem::Atom_RPI.Public
Gem::Atom_RHI.Public Gem::Atom_RHI.Public
Gem::Atom_RHI.Reflect Gem::Atom_RHI.Reflect
RUNTIME_DEPENDENCIES
Gem::Atom_RHI.Private
) )
if(PAL_TRAIT_BUILD_HOST_TOOLS) if(PAL_TRAIT_BUILD_HOST_TOOLS)
@ -129,6 +131,8 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
Gem::Atom_RPI.Editor.Static Gem::Atom_RPI.Editor.Static
Gem::Atom_RPI.Edit Gem::Atom_RPI.Edit
Gem::Atom_RPI.Public Gem::Atom_RPI.Public
RUNTIME_DEPENDENCIES
Gem::Atom_RHI.Private
) )
endif() endif()

@ -24,7 +24,7 @@ namespace AZ
//! Connect to this EBus to get notifications whenever material objects reload. //! Connect to this EBus to get notifications whenever material objects reload.
//! The bus address is the AssetId of the MaterialAsset or MaterialTypeAsset. //! The bus address is the AssetId of the MaterialAsset or MaterialTypeAsset.
//! //!
//! Be careful when using the parameters provided by these functions. The bus ID is an AssetId, and it's possible for the system to have //! Be careful when using the parameters provided by these functions. The bus ID is an AssetId, and it's possible for the system to have
//! both *old* versions and *new reloaded* versions of the asset in memory at the same time, and they will have the same AssetId. Therefore //! both *old* versions and *new reloaded* versions of the asset in memory at the same time, and they will have the same AssetId. Therefore
//! your bus Handlers could receive Reinitialized messages from multiple sources. It may be necessary to check the memory addresses of these //! your bus Handlers could receive Reinitialized messages from multiple sources. It may be necessary to check the memory addresses of these

@ -27,7 +27,7 @@ namespace AZ
/** /**
* Connect to this EBus to get notifications whenever a shader system class reinitializes itself. * Connect to this EBus to get notifications whenever a shader system class reinitializes itself.
* The bus address is the AssetId of the ShaderAsset, even when the thing being reinitialized is a ShaderVariant or other shader related class. * The bus address is the AssetId of the ShaderAsset, even when the thing being reinitialized is a ShaderVariant or other shader related class.
* *
* Be careful when using the parameters provided by these functions. The bus ID is an AssetId, and it's possible for the system to have * Be careful when using the parameters provided by these functions. The bus ID is an AssetId, and it's possible for the system to have
* both *old* versions and *new reloaded* versions of the asset in memory at the same time, and they will have the same AssetId. Therefore * both *old* versions and *new reloaded* versions of the asset in memory at the same time, and they will have the same AssetId. Therefore
* your bus Handlers could receive Reinitialized messages from multiple sources. It may be necessary to check the memory addresses of these * your bus Handlers could receive Reinitialized messages from multiple sources. It may be necessary to check the memory addresses of these

@ -56,7 +56,7 @@ namespace AZ
bool IsRootVariant() const { return m_shaderVariantAsset->IsRootVariant(); } bool IsRootVariant() const { return m_shaderVariantAsset->IsRootVariant(); }
ShaderVariantStableId GetStableId() const { return m_shaderVariantAsset->GetStableId(); } ShaderVariantStableId GetStableId() const { return m_shaderVariantAsset->GetStableId(); }
const Data::Asset<ShaderAsset>& GetShaderAsset() const { return m_shaderAsset; } const Data::Asset<ShaderAsset>& GetShaderAsset() const { return m_shaderAsset; }
const Data::Asset<ShaderVariantAsset>& GetShaderVariantAsset() const { return m_shaderVariantAsset; } const Data::Asset<ShaderVariantAsset>& GetShaderVariantAsset() const { return m_shaderVariantAsset; }
@ -65,10 +65,10 @@ namespace AZ
bool Init( bool Init(
const Data::Asset<ShaderAsset>& shaderAsset, const Data::Asset<ShaderAsset>& shaderAsset,
const Data::Asset<ShaderVariantAsset>& shaderVariantAsset); const Data::Asset<ShaderVariantAsset>& shaderVariantAsset);
// AssetBus overrides... // AssetBus overrides...
void OnAssetReloaded(Data::Asset<Data::AssetData> asset) override; void OnAssetReloaded(Data::Asset<Data::AssetData> asset) override;
//! A reference to the shader asset that this is a variant of. //! A reference to the shader asset that this is a variant of.
Data::Asset<ShaderAsset> m_shaderAsset; Data::Asset<ShaderAsset> m_shaderAsset;

@ -105,20 +105,6 @@ namespace AZ
m_assetHandlers.emplace_back(MakeAssetHandler<ShaderVariantTreeAssetHandler>()); m_assetHandlers.emplace_back(MakeAssetHandler<ShaderVariantTreeAssetHandler>());
m_assetHandlers.emplace_back(MakeAssetHandler<SkinMetaAssetHandler>()); m_assetHandlers.emplace_back(MakeAssetHandler<SkinMetaAssetHandler>());
m_assetHandlers.emplace_back(MakeAssetHandler<MorphTargetMetaAssetHandler>()); m_assetHandlers.emplace_back(MakeAssetHandler<MorphTargetMetaAssetHandler>());
RPI::MaterialFunctorSourceDataRegistration* materialFunctorRegistration = RPI::MaterialFunctorSourceDataRegistration::Get();
AZ_Assert(materialFunctorRegistration,
"MaterialFunctorSourceDataRegistration must be added to a component of the current module, "
"and initialize it in the component's Init() call.");
materialFunctorRegistration->RegisterMaterialFunctor("Lua", azrtti_typeid<LuaMaterialFunctorSourceData>());
// Add asset types and extensions to AssetCatalog. Uses "AssetCatalogService".
auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler();
if (assetCatalog)
{
assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo<AZ::ScriptAsset>::Uuid());
}
} }
void BuilderComponent::Deactivate() void BuilderComponent::Deactivate()

@ -91,7 +91,7 @@ namespace AZ
{ {
return m_shaderVariantAsset->GetOutputContract(); return m_shaderVariantAsset->GetOutputContract();
} }
void ShaderVariant::OnAssetReloaded(Data::Asset<Data::AssetData> asset) void ShaderVariant::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
{ {
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderVariant::OnAssetReloaded %s", this, asset.GetHint().c_str()); ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->ShaderVariant::OnAssetReloaded %s", this, asset.GetHint().c_str());
@ -102,7 +102,7 @@ namespace AZ
Init(m_shaderAsset, shaderVariantAsset); Init(m_shaderAsset, shaderVariantAsset);
ShaderReloadNotificationBus::Event(m_shaderAsset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderVariantReinitialized, *this); ShaderReloadNotificationBus::Event(m_shaderAsset.GetId(), &ShaderReloadNotificationBus::Events::OnShaderVariantReinitialized, *this);
} }
if (asset.GetAs<ShaderAsset>()) if (asset.GetAs<ShaderAsset>())
{ {
Data::Asset<ShaderAsset> shaderAsset = { asset.GetAs<ShaderAsset>(), AZ::Data::AssetLoadBehavior::PreLoad }; Data::Asset<ShaderAsset> shaderAsset = { asset.GetAs<ShaderAsset>(), AZ::Data::AssetLoadBehavior::PreLoad };

@ -119,7 +119,7 @@ namespace AZ
{ {
// When reloads occur, it's possible for old Asset objects to hang around and report reinitialization, // When reloads occur, it's possible for old Asset objects to hang around and report reinitialization,
// so we can reduce unnecessary reinitialization in that case. // so we can reduce unnecessary reinitialization in that case.
if (materialTypeAsset.Get() == m_materialTypeAsset.Get()) if (materialTypeAsset.Get() == m_materialTypeAsset.Get())
{ {
ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialAsset::OnMaterialTypeAssetReinitialized %s", this, materialTypeAsset.GetHint().c_str()); ShaderReloadDebugTracker::ScopedSection reloadSection("{%p}->MaterialAsset::OnMaterialTypeAssetReinitialized %s", this, materialTypeAsset.GetHint().c_str());

@ -12,7 +12,7 @@
#include <Atom/Feature/Utils/ProfilingCaptureBus.h> #include <Atom/Feature/Utils/ProfilingCaptureBus.h>
#include <Atom/RPI.Public/RPISystemInterface.h> #include <Atom/RPI.Public/RPISystemInterface.h>
#include <AzCore/IO/SystemFile.h> // For AZ_MAX_PATH_LEN #include <AzCore/IO/Path/Path_fwd.h>
#include <AzCore/std/time.h> #include <AzCore/std/time.h>
namespace AZ namespace AZ
@ -48,10 +48,10 @@ namespace AZ
if (ImGui::Begin("Cpu Profiler", &keepDrawing, ImGuiWindowFlags_None)) if (ImGui::Begin("Cpu Profiler", &keepDrawing, ImGuiWindowFlags_None))
{ {
m_paused = !AZ::RHI::CpuProfiler::Get()->IsProfilerEnabled(); m_paused = !AZ::RHI::CpuProfiler::Get()->IsProfilerEnabled();
if (ImGui::Button(m_paused?"Resume":"Pause")) if (ImGui::Button(m_paused ? "Resume" : "Pause"))
{ {
m_paused = !m_paused; m_paused = !m_paused;
AZ::RHI::CpuProfiler::Get()->SetProfilerEnabled(!m_paused); AZ::RHI::CpuProfiler::Get()->SetProfilerEnabled(!m_paused);
} }
// Update region map and cache the input cpu timing statistics when the profiling is not paused // Update region map and cache the input cpu timing statistics when the profiling is not paused
@ -194,8 +194,8 @@ namespace AZ
AZStd::to_string(timeString, timeNow); AZStd::to_string(timeString, timeNow);
u64 currentTick = AZ::RPI::RPISystemInterface::Get()->GetCurrentTick(); u64 currentTick = AZ::RPI::RPISystemInterface::Get()->GetCurrentTick();
AZStd::string frameDataFilePath = AZStd::string::format("@user@/CpuProfiler/%s_%llu.json", timeString.c_str(), currentTick); AZStd::string frameDataFilePath = AZStd::string::format("@user@/CpuProfiler/%s_%llu.json", timeString.c_str(), currentTick);
char resolvedPath[AZ_MAX_PATH_LEN]; char resolvedPath[AZ::IO::MaxPathLength];
AZ::IO::FileIOBase::GetInstance()->ResolvePath(frameDataFilePath.c_str(), resolvedPath, AZ_MAX_PATH_LEN); AZ::IO::FileIOBase::GetInstance()->ResolvePath(frameDataFilePath.c_str(), resolvedPath, AZ::IO::MaxPathLength);
m_lastCapturedFilePath = resolvedPath; m_lastCapturedFilePath = resolvedPath;
AZ::Render::ProfilingCaptureRequestBus::Broadcast(&AZ::Render::ProfilingCaptureRequestBus::Events::CaptureCpuProfilingStatistics, AZ::Render::ProfilingCaptureRequestBus::Broadcast(&AZ::Render::ProfilingCaptureRequestBus::Events::CaptureCpuProfilingStatistics,
frameDataFilePath); frameDataFilePath);

@ -61,6 +61,21 @@ ly_add_target(
BUILD_DEPENDENCIES BUILD_DEPENDENCIES
PRIVATE PRIVATE
Gem::AtomLyIntegration_CommonFeatures.Static Gem::AtomLyIntegration_CommonFeatures.Static
RUNTIME_DEPENDENCIES
Gem::Atom_RPI.Private
Gem::Atom_Feature_Common
)
# The AtomLyIntegration_CommonFeatures module is used for Clients and Servers
ly_create_alias(NAME AtomLyIntegration_CommonFeatures.Clients NAMESPACE Gem
TARGETS
Gem::AtomLyIntegration_CommonFeatures
Gem::GradientSignal.Clients
)
ly_create_alias(NAME AtomLyIntegration_CommonFeatures.Servers NAMESPACE Gem
TARGETS
Gem::AtomLyIntegration_CommonFeatures
Gem::GradientSignal.Servers
) )
if(PAL_TRAIT_BUILD_HOST_TOOLS) if(PAL_TRAIT_BUILD_HOST_TOOLS)
@ -94,5 +109,44 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
Legacy::Editor.Headers Legacy::Editor.Headers
Legacy::EditorCommon Legacy::EditorCommon
Legacy::CryCommon Legacy::CryCommon
RUNTIME_DEPENDENCIES
Gem::Atom_RPI.Editor
Gem::Atom_Feature_Common.Editor
)
# The AtomLyIntegration_CommonFeatures.Editor module is used for Builders and Tools
ly_create_alias(NAME AtomLyIntegration_CommonFeatures.Builders NAMESPACE Gem
TARGETS
Gem::AtomLyIntegration_CommonFeatures.Editor
Gem::Atom_RPI.Builders
Gem::GradientSignal.Builders
) )
ly_create_alias(NAME AtomLyIntegration_CommonFeatures.Tools NAMESPACE Gem
TARGETS
Gem::AtomLyIntegration_CommonFeatures.Editor
Gem::GradientSignal.Tools
)
# AtomLyIntergration_CommonFeatures gem targets are required as part of the Editor and AssetProcessor
# due to the AZ::Render::EditorDirectionalLightComponent, AZ::Render::EditorMeshComponent,
# AZ::Render::EditorGridComponent, AZ::Render::EditorHDRiSkyboxComponent,
# AZ::Render::EditorImageBasedLightComponent being saved as part of the DefaultLevel.prefab
ly_enable_gems(GEMS AtomLyIntegration_CommonFeatures VARIANTS Tools
TARGETS Editor)
ly_enable_gems(GEMS AtomLyIntegration_CommonFeatures VARIANTS Builders
TARGETS AssetBuilder AssetProcessor AssetProcessorBatch)
endif() endif()
# Added dependencies to the Client and Server Launchers
get_property(LY_PROJECTS_TARGET_NAME GLOBAL PROPERTY LY_PROJECTS_TARGET_NAME)
foreach(project_name IN LISTS LY_PROJECTS_TARGET_NAME)
# Add gem as a dependency of the Clients Launcher
ly_enable_gems(PROJECT_NAME ${project_name} GEMS AtomLyIntegration_CommonFeatures VARIANTS Clients
TARGETS ${project_name}.GameLauncher)
# Add gem as a dependency of the Servers Launcher
if(PAL_TRAIT_BUILD_SERVER_SUPPORTED)
ly_enable_gems(PROJECT_NAME ${project_name} GEMS AtomLyIntegration_CommonFeatures VARIANTS Servers
TARGETS ${project_name}.ServerLauncher)
endif()
endforeach()

@ -179,18 +179,18 @@ namespace AZ
->EnumAttribute(PcfMethod::BoundarySearch, "Boundary search") ->EnumAttribute(PcfMethod::BoundarySearch, "Boundary search")
->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows)
->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::IsShadowPcfDisabled) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::IsShadowPcfDisabled)
->DataElement( ->DataElement(
Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_esmExponent, "Esm Exponent", Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_esmExponent, "Esm Exponent",
"Exponent used by Esm shadows. " "Exponent used by Esm shadows. "
"Larger values increase the sharpness of the border between lit and unlit areas.") "Larger values increase the sharpness of the border between lit and unlit areas.")
->Attribute(Edit::Attributes::Min, 50.0f) ->Attribute(Edit::Attributes::Min, 50.0f)
->Attribute(Edit::Attributes::Max, 5000.0f) ->Attribute(Edit::Attributes::Max, 5000.0f)
->Attribute(AZ::Edit::Attributes::Decimals, 0) ->Attribute(AZ::Edit::Attributes::Decimals, 0)
->Attribute(AZ::Edit::Attributes::SliderCurveMidpoint, 0.05f) ->Attribute(AZ::Edit::Attributes::SliderCurveMidpoint, 0.05f)
->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows)
->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::IsEsmDisabled) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::IsEsmDisabled)
; ;
} }
} }

@ -69,4 +69,22 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
# tools and builders use the above module. # tools and builders use the above module.
ly_create_alias(NAME Camera.Tools NAMESPACE Gem TARGETS Gem::Camera.Editor) ly_create_alias(NAME Camera.Tools NAMESPACE Gem TARGETS Gem::Camera.Editor)
ly_create_alias(NAME Camera.Builders NAMESPACE Gem TARGETS Gem::Camera.Editor) ly_create_alias(NAME Camera.Builders NAMESPACE Gem TARGETS Gem::Camera.Editor)
# The DefaultPrefab contains an EditorCameraComponent which makes this gem required
ly_enable_gems(GEMS Camera VARIANTS Tools TARGETS Editor)
ly_enable_gems(GEMS Camera VARIANTS Builders TARGETS AssetBuilder AssetProcessor AssetProcessorBatch)
endif() endif()
# Added dependencies to the Client and Server Launchers
get_property(LY_PROJECTS_TARGET_NAME GLOBAL PROPERTY LY_PROJECTS_TARGET_NAME)
foreach(project_name IN LISTS LY_PROJECTS_TARGET_NAME)
# Add gem as a dependency of the Clients Launcher
ly_enable_gems(PROJECT_NAME ${project_name} GEMS Camera VARIANTS Clients
TARGETS ${project_name}.GameLauncher)
# Add gem as a dependency of the Servers Launcher
if(PAL_TRAIT_BUILD_SERVER_SUPPORTED)
ly_enable_gems(PROJECT_NAME ${project_name} GEMS Camera VARIANTS Servers
TARGETS ${project_name}.ServerLauncher)
endif()
endforeach()

@ -78,8 +78,25 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
ly_create_alias(NAME Maestro.Tools NAMESPACE Gem TARGETS Gem::Maestro.Editor) ly_create_alias(NAME Maestro.Tools NAMESPACE Gem TARGETS Gem::Maestro.Editor)
ly_create_alias(NAME Maestro.Builders NAMESPACE Gem TARGETS Gem::Maestro.Editor) ly_create_alias(NAME Maestro.Builders NAMESPACE Gem TARGETS Gem::Maestro.Editor)
# Maestro is still used by the CrySystem Level System and SystemInit and TrackView
# It is required by the GameLauncher, ServerLauncher and Editor applications
ly_enable_gems(GEMS Maestro VARIANTS Tools TARGETS Editor)
endif() endif()
# Loop over each Project name to allow the ${ProjectName}.GameLauncher and ${ProjectName}.ServerLauncher
# target to add the gem the Clients and Servers variant
get_property(LY_PROJECTS_TARGET_NAME GLOBAL PROPERTY LY_PROJECTS_TARGET_NAME)
foreach(project_name IN LISTS LY_PROJECTS_TARGET_NAME)
# Add gem as a dependency of the Clients Launcher
ly_enable_gems(PROJECT_NAME ${project_name} GEMS Maestro VARIANTS Clients TARGETS ${project_name}.GameLauncher)
# Add gem as a dependency of the Servers Launcher
if(PAL_TRAIT_BUILD_SERVER_SUPPORTED)
ly_enable_gems(PROJECT_NAME ${project_name} GEMS Maestro VARIANTS Servers TARGETS ${project_name}.ServerLauncher)
endif()
endforeach()
################################################################################ ################################################################################
# Tests # Tests
################################################################################ ################################################################################

@ -26,7 +26,7 @@ ly_add_target(
) )
ly_add_target( ly_add_target(
NAME PrefabBuilder MODULE NAME PrefabBuilder GEM_MODULE
NAMESPACE Gem NAMESPACE Gem
INCLUDE_DIRECTORIES INCLUDE_DIRECTORIES
PRIVATE PRIVATE
@ -47,7 +47,7 @@ ly_enable_gems(GEMS PrefabBuilder VARIANTS Builders TARGETS AssetProcessor Asset
# if you have a custom builder application in your project, then use ly_enable_gems() to # if you have a custom builder application in your project, then use ly_enable_gems() to
# add it to that application for your project, like this to make YOUR_TARGET_NAME load it automatically # add it to that application for your project, like this to make YOUR_TARGET_NAME load it automatically
# ly_enable_gems(PROJECT (YOUR_PROJECT_NAME) GEMS PrefabBuilder VARIANTS Builders TARGETS (YOUR_TARGET_NAME) ) # ly_enable_gems(PROJECT_NAME (YOUR_PROJECT_NAME) GEMS PrefabBuilder VARIANTS Builders TARGETS (YOUR_TARGET_NAME) )
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_target( ly_add_target(

@ -45,7 +45,7 @@ ly_add_target(
) )
ly_add_target( ly_add_target(
NAME QtForPython.Editor MODULE NAME QtForPython.Editor GEM_MODULE
NAMESPACE Gem NAMESPACE Gem
FILES_CMAKE FILES_CMAKE
qtforpython_shared_files.cmake qtforpython_shared_files.cmake

@ -70,8 +70,14 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
ly_create_alias(NAME SceneProcessing.Builders NAMESPACE Gem TARGETS Gem::SceneProcessing.Editor) ly_create_alias(NAME SceneProcessing.Builders NAMESPACE Gem TARGETS Gem::SceneProcessing.Editor)
ly_create_alias(NAME SceneProcessing.Tools NAMESPACE Gem TARGETS Gem::SceneProcessing.Editor) ly_create_alias(NAME SceneProcessing.Tools NAMESPACE Gem TARGETS Gem::SceneProcessing.Editor)
# SceneProcessing Gem is only used in Tools and builders and is a requirement for the Editor
ly_enable_gems(GEMS SceneProcessing VARIANTS Tools
TARGETS Editor)
ly_enable_gems(GEMS SceneProcessing VARIANTS Builders
TARGETS AssetBuilder AssetProcessor AssetProcessorBatch)
endif() endif()
################################################################################ ################################################################################
# Tests # Tests
################################################################################ ################################################################################

@ -85,7 +85,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
) )
ly_add_target( ly_add_target(
NAME ${Name}.Editor MODULE NAME ${Name}.Editor GEM_MODULE
NAMESPACE Gem NAMESPACE Gem
AUTOMOC AUTOMOC
OUTPUT_NAME Gem.${Name}.Editor OUTPUT_NAME Gem.${Name}.Editor

@ -84,7 +84,7 @@ function(ly_create_alias)
# now add the final alias: # now add the final alias:
add_library(${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME} ALIAS ${ly_create_alias_NAME}) add_library(${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME} ALIAS ${ly_create_alias_NAME})
# Store off the arguments needed used ly_create_alias into a DIRECTORY property # Store off the arguments used by ly_create_alias into a DIRECTORY property
# This will be used to re-create the calls in the generated CMakeLists.txt in the INSTALL step # This will be used to re-create the calls in the generated CMakeLists.txt in the INSTALL step
# Replace the CMake list separator with a space to replicate the space separated TARGETS arguments # Replace the CMake list separator with a space to replicate the space separated TARGETS arguments
@ -136,8 +136,8 @@ function(ly_enable_gems)
if(NOT was_able_to_load_the_file) if(NOT was_able_to_load_the_file)
message(FATAL_ERROR "could not load the GEM_FILE ${ly_enable_gems_GEM_FILE}") message(FATAL_ERROR "could not load the GEM_FILE ${ly_enable_gems_GEM_FILE}")
endif() endif()
if(NOT ENABLED_GEMS) if(NOT DEFINED ENABLED_GEMS)
message(FATAL_ERROR "GEM_FILE ${ly_enable_gems_GEM_FILE} did not set the value of ENABLED_GEMS.\n" message(WARNING "GEM_FILE ${ly_enable_gems_GEM_FILE} did not set the value of ENABLED_GEMS.\n"
"Gem Files should contain set(ENABLED_GEMS ... <list of gem names>)") "Gem Files should contain set(ENABLED_GEMS ... <list of gem names>)")
endif() endif()
set(ly_enable_gems_GEMS ${ENABLED_GEMS}) set(ly_enable_gems_GEMS ${ENABLED_GEMS})
@ -148,9 +148,19 @@ function(ly_enable_gems)
foreach(target_name ${ly_enable_gems_TARGETS}) foreach(target_name ${ly_enable_gems_TARGETS})
foreach(variant_name ${ly_enable_gems_VARIANTS}) foreach(variant_name ${ly_enable_gems_VARIANTS})
set_property(GLOBAL APPEND PROPERTY LY_DELAYED_ENABLE_GEMS "${ly_enable_gems_PROJECT_NAME},${target_name},${variant_name}") set_property(GLOBAL APPEND PROPERTY LY_DELAYED_ENABLE_GEMS "${ly_enable_gems_PROJECT_NAME},${target_name},${variant_name}")
define_property(GLOBAL PROPERTY LY_DELAYED_ENABLE_GEMS_"${ly_enable_gems_PROJECT_NAME},${target_name},${variant_name}"
BRIEF_DOCS "List of gem names to evaluate variants against" FULL_DOCS "Names of gems that will be paired with the variant name
to determine if it is valid target that should be added as an application dynamic load dependency")
set_property(GLOBAL APPEND PROPERTY LY_DELAYED_ENABLE_GEMS_"${ly_enable_gems_PROJECT_NAME},${target_name},${variant_name}" ${ly_enable_gems_GEMS}) set_property(GLOBAL APPEND PROPERTY LY_DELAYED_ENABLE_GEMS_"${ly_enable_gems_PROJECT_NAME},${target_name},${variant_name}" ${ly_enable_gems_GEMS})
endforeach() endforeach()
endforeach() endforeach()
# Store off the arguments used by ly_enable_gems into a DIRECTORY property
# This will be used to re-create the ly_enable_gems call in the generated CMakeLists.txt at the INSTALL step
# Replace the CMake list separator with a space to replicate the space separated TARGETS arguments
string(REPLACE ";" " " enable_gems_args "${ly_enable_gems_PROJECT_NAME},${ly_enable_gems_GEMS},${ly_enable_gems_GEM_FILE},${ly_enable_gems_VARIANTS},${ly_enable_gems_TARGETS}")
set_property(DIRECTORY APPEND PROPERTY LY_ENABLE_GEMS_ARGUMENTS "${enable_gems_args}")
endfunction() endfunction()
# call this before runtime dependencies are used to add any relevant targets # call this before runtime dependencies are used to add any relevant targets
@ -176,6 +186,20 @@ function(ly_enable_gems_delayed)
get_property(gem_dependencies GLOBAL PROPERTY LY_DELAYED_ENABLE_GEMS_"${project_target_variant}") get_property(gem_dependencies GLOBAL PROPERTY LY_DELAYED_ENABLE_GEMS_"${project_target_variant}")
if (NOT gem_dependencies) if (NOT gem_dependencies)
get_property(gem_dependencies_defined GLOBAL PROPERTY LY_DELAYED_ENABLE_GEMS_"${project_target_variant}" DEFINED)
if (gem_dependencies_defined)
# special case, if the LY_DELAYED_ENABLE_GEMS_"${project_target_variant}" property is DEFINED
# but empty, add an entry to the LY_DELAYED_LOAD_DEPENDENCIES to have the
# cmake_dependencies.*.setreg file for the (project, target) tuple to be regenerated
# This is needed if the ENABLED_GEMS list for a project goes from >0 to 0. In this case
# the cmake_dependencies would have a stale list of gems to load unless it is regenerated
get_property(delayed_load_target_set GLOBAL PROPERTY LY_DELAYED_LOAD_"${project},${target}" SET)
if(NOT delayed_load_target_set)
set_property(GLOBAL APPEND PROPERTY LY_DELAYED_LOAD_DEPENDENCIES "${project},${target}")
set_property(GLOBAL APPEND PROPERTY LY_DELAYED_LOAD_"${project},${target}" "")
endif()
endif()
# Continue to the next iteration loop regardless as there are no gem dependencies
continue() continue()
endif() endif()

@ -13,8 +13,8 @@ set(CMAKE_INSTALL_MESSAGE NEVER) # Simplify messages to reduce output noise
ly_set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Core) ly_set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Core)
file(RELATIVE_PATH runtime_output_directory ${CMAKE_BINARY_DIR} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) cmake_path(RELATIVE_PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE runtime_output_directory)
file(RELATIVE_PATH library_output_directory ${CMAKE_BINARY_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) cmake_path(RELATIVE_PATH CMAKE_LIBRARY_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE library_output_directory)
# Anywhere CMAKE_INSTALL_PREFIX is used, it has to be escaped so it is baked into the cmake_install.cmake script instead # Anywhere CMAKE_INSTALL_PREFIX is used, it has to be escaped so it is baked into the cmake_install.cmake script instead
# of baking the path. This is needed so `cmake --install --prefix <someprefix>` works regardless of the CMAKE_INSTALL_PREFIX # of baking the path. This is needed so `cmake --install --prefix <someprefix>` works regardless of the CMAKE_INSTALL_PREFIX
# used to generate the solution. # used to generate the solution.
@ -22,11 +22,34 @@ file(RELATIVE_PATH library_output_directory ${CMAKE_BINARY_DIR} ${CMAKE_LIBRARY_
set(install_output_folder "\${CMAKE_INSTALL_PREFIX}/${runtime_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>") set(install_output_folder "\${CMAKE_INSTALL_PREFIX}/${runtime_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>")
function(ly_get_engine_relative_source_dir absolute_target_source_dir output_source_dir)
# Get a relative target source directory to the LY root folder if possible
# Otherwise use the final component name
cmake_path(IS_PREFIX LY_ROOT_FOLDER ${absolute_target_source_dir} is_target_prefix_of_engine_root)
if(is_target_prefix_of_engine_root)
cmake_path(RELATIVE_PATH absolute_target_source_dir BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE relative_target_source_dir)
else()
# In this case the target source directory is outside of the engine root of the target source directory and concatenate the first
# is used first 8 characters of the absolute path SHA256 hash to make a unique relative directory
# that can be used to install the generated CMakeLists.txt
# of a SHA256 hash
string(SHA256 target_source_hash ${absolute_target_source_dir})
string(SUBSTRING ${target_source_hash} 0 8 target_source_hash)
cmake_path(GET absolute_target_source_dir FILENAME target_source_dirname)
cmake_path(SET relative_target_source_dir "${target_source_dirname}-${target_source_hash}")
endif()
set(${output_source_dir} ${relative_target_source_dir} PARENT_SCOPE)
endfunction()
#! ly_setup_target: Setup the data needed to re-create the cmake target commands for a single target #! ly_setup_target: Setup the data needed to re-create the cmake target commands for a single target
function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME) function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME absolute_target_source_dir)
# De-alias target name # De-alias target name
ly_de_alias_target(${ALIAS_TARGET_NAME} TARGET_NAME) ly_de_alias_target(${ALIAS_TARGET_NAME} TARGET_NAME)
# Get the target source directory relative to the LY root folder
ly_get_engine_relative_source_dir(${absolute_target_source_dir} relative_target_source_dir)
# get the component ID. if the property isn't set for the target, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME # get the component ID. if the property isn't set for the target, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME
get_property(install_component TARGET ${TARGET_NAME} PROPERTY INSTALL_COMPONENT) get_property(install_component TARGET ${TARGET_NAME} PROPERTY INSTALL_COMPONENT)
@ -67,16 +90,16 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME)
endif() endif()
# Get the output folders, archive is always the same, but runtime/library can be in subfolders defined per target # Get the output folders, archive is always the same, but runtime/library can be in subfolders defined per target
file(RELATIVE_PATH archive_output_directory ${CMAKE_BINARY_DIR} ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}) cmake_path(RELATIVE_PATH CMAKE_ARCHIVE_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE archive_output_directory)
get_target_property(target_runtime_output_directory ${TARGET_NAME} RUNTIME_OUTPUT_DIRECTORY) get_target_property(target_runtime_output_directory ${TARGET_NAME} RUNTIME_OUTPUT_DIRECTORY)
if(target_runtime_output_directory) if(target_runtime_output_directory)
file(RELATIVE_PATH target_runtime_output_subdirectory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${target_runtime_output_directory}) cmake_path(RELATIVE_PATH target_runtime_output_directory BASE_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} OUTPUT_VARIABLE target_runtime_output_subdirectory)
endif() endif()
get_target_property(target_library_output_directory ${TARGET_NAME} LIBRARY_OUTPUT_DIRECTORY) get_target_property(target_library_output_directory ${TARGET_NAME} LIBRARY_OUTPUT_DIRECTORY)
if(target_library_output_directory) if(target_library_output_directory)
file(RELATIVE_PATH target_library_output_subdirectory ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${target_library_output_directory}) cmake_path(RELATIVE_PATH target_library_output_directory BASE_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} OUTPUT_VARIABLE target_library_output_subdirectory)
endif() endif()
install( install(
@ -127,9 +150,9 @@ function(ly_setup_target OUTPUT_CONFIGURED_TARGET ALIAS_TARGET_NAME)
foreach(include ${include_directories}) foreach(include ${include_directories})
string(GENEX_STRIP ${include} include_genex_expr) string(GENEX_STRIP ${include} include_genex_expr)
if(include_genex_expr STREQUAL include) # only for cases where there are no generation expressions if(include_genex_expr STREQUAL include) # only for cases where there are no generation expressions
file(RELATIVE_PATH relative_include ${absolute_target_source_dir} ${include}) cmake_path(RELATIVE_PATH include BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE target_include)
cmake_path(APPEND include_location "${target_source_dir}" "${relative_include}" OUTPUT_VARIABLE target_include)
cmake_path(NORMAL_PATH target_include) cmake_path(NORMAL_PATH target_include)
# Escape the LY_ROOT_FOLDER variable so that it isn't resolved during the install step
string(APPEND INCLUDE_DIRECTORIES_PLACEHOLDER "\${LY_ROOT_FOLDER}/${target_include}\n") string(APPEND INCLUDE_DIRECTORIES_PLACEHOLDER "\${LY_ROOT_FOLDER}/${target_include}\n")
endif() endif()
endforeach() endforeach()
@ -207,21 +230,10 @@ set_property(TARGET ${TARGET_NAME}
endif() endif()
endif() endif()
if(IS_ABSOLUTE ${target_source_dir}) set(target_install_source_dir ${CMAKE_CURRENT_BINARY_DIR}/install/${relative_target_source_dir})
# This normally applies the target_source_dir is outside of the engine root
# such as when invoking ly_setup_subdirectory from the project
# Therefore the final directory component of the target source directory is used first 8 characters
# of a SHA256 hash
string(SHA256 target_source_hash ${target_source_dir})
string(SUBSTRING ${target_source_hash} 0 8 target_source_hash)
get_filename_component(target_source_folder_name ${target_source_dir} NAME)
set(target_source_dir "${target_source_folder_name}-${target_source_hash}")
endif()
set(target_install_source_dir ${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir})
file(GENERATE OUTPUT "${target_install_source_dir}/${NAME_PLACEHOLDER}_$<CONFIG>.cmake" CONTENT "${target_file_contents}") file(GENERATE OUTPUT "${target_install_source_dir}/${NAME_PLACEHOLDER}_$<CONFIG>.cmake" CONTENT "${target_file_contents}")
install(FILES "${target_install_source_dir}/${NAME_PLACEHOLDER}_$<CONFIG>.cmake" install(FILES "${target_install_source_dir}/${NAME_PLACEHOLDER}_$<CONFIG>.cmake"
DESTINATION ${target_source_dir} DESTINATION ${relative_target_source_dir}
COMPONENT ${install_component} COMPONENT ${install_component}
) )
@ -231,24 +243,26 @@ set_property(TARGET ${TARGET_NAME}
set(${OUTPUT_CONFIGURED_TARGET} ${output_cmakelists_data} PARENT_SCOPE) set(${OUTPUT_CONFIGURED_TARGET} ${output_cmakelists_data} PARENT_SCOPE)
endfunction() endfunction()
#! ly_setup_subdirectories: setups all targets on a per directory basis #! ly_setup_subdirectories: setups all targets on a per directory basis
function(ly_setup_subdirectories) function(ly_setup_subdirectories)
get_property(all_subdirectories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES) get_property(all_subdirectories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES)
foreach(target IN LISTS all_subdirectories) foreach(target_subdirectory IN LISTS all_subdirectories)
ly_setup_subdirectory(${target}) ly_setup_subdirectory(${target_subdirectory})
endforeach() endforeach()
endfunction() endfunction()
#! ly_setup_subdirectory: setup all targets in the subdirectory #! ly_setup_subdirectory: setup all targets in the subdirectory
function(ly_setup_subdirectory absolute_target_source_dir) function(ly_setup_subdirectory absolute_target_source_dir)
# Get the target source directory relative to the LY roo folder
ly_get_engine_relative_source_dir(${absolute_target_source_dir} relative_target_source_dir)
# The builtin BUILDSYSTEM_TARGETS property isn't being used here as that returns the de-alised # The builtin BUILDSYSTEM_TARGETS property isn't being used here as that returns the de-alised
# TARGET and we need the alias namespace for recreating the CMakeLists.txt in the install layout # TARGET and we need the alias namespace for recreating the CMakeLists.txt in the install layout
get_property(ALIAS_TARGETS_NAME DIRECTORY ${absolute_target_source_dir} PROPERTY LY_DIRECTORY_TARGETS) get_property(ALIAS_TARGETS_NAME DIRECTORY ${absolute_target_source_dir} PROPERTY LY_DIRECTORY_TARGETS)
file(RELATIVE_PATH target_source_dir ${LY_ROOT_FOLDER} ${absolute_target_source_dir})
foreach(ALIAS_TARGET_NAME IN LISTS ALIAS_TARGETS_NAME) foreach(ALIAS_TARGET_NAME IN LISTS ALIAS_TARGETS_NAME)
ly_setup_target(configured_target ${ALIAS_TARGET_NAME}) ly_setup_target(configured_target ${ALIAS_TARGET_NAME} ${absolute_target_source_dir})
string(APPEND all_configured_targets "${configured_target}") string(APPEND all_configured_targets "${configured_target}")
endforeach() endforeach()
@ -271,34 +285,55 @@ function(ly_setup_subdirectory absolute_target_source_dir)
string(APPEND CREATE_ALIASES_PLACEHOLDER ${create_alias_command}) string(APPEND CREATE_ALIASES_PLACEHOLDER ${create_alias_command})
endforeach() endforeach()
file(READ ${LY_ROOT_FOLDER}/cmake/install/Copyright.in cmake_copyright_comment)
if(IS_ABSOLUTE ${target_source_dir}) # Reproduce the ly_enable_gems() calls made in the the SOURCE_DIR for this target into the CMakeLists.txt that
# This normally applies the target_source_dir is outside of the engine root # is about to be generated
# such as when invoking ly_setup_subdirectory from the project string(JOIN "\n" enable_gems_template
# Therefore the final directory component of the target source directory is used first 8 characters " ly_enable_gems(@enable_gem_PROJECT_NAME@ @enable_gem_GEM@ @enable_gem_GEM_FILE@ @enable_gem_VARIANTS@ @enable_gem_TARGETS@)"
# of a SHA256 hash "endif()"
string(SHA256 target_source_hash ${target_source_dir}) ""
string(SUBSTRING ${target_source_hash} 0 8 target_source_hash) )
get_filename_component(target_source_folder_name ${target_source_dir} NAME) get_property(enable_gems_commands_arg_list DIRECTORY ${absolute_target_source_dir} PROPERTY LY_ENABLE_GEMS_ARGUMENTS)
set(target_source_dir "${target_source_folder_name}-${target_source_hash}") foreach(enable_gems_single_command_arg_list ${enable_gems_commands_arg_list})
endif() # Split the ly_enable_gems arguments back out based on commas
string(REPLACE "," ";" ly_enable_gems_single_command_arg_list "${enable_gems_single_command_arg_list}")
list(POP_FRONT enable_gems_single_command_arg_list enable_gem_PROJECT_NAME)
list(POP_FRONT enable_gems_single_command_arg_list enable_gem_GEM)
list(POP_FRONT enable_gems_single_command_arg_list enable_gem_GEM_FILE)
list(POP_FRONT enable_gems_single_command_arg_list enable_gem_GEM)
list(POP_FRONT enable_gems_single_command_arg_list enable_gem_VARIANTS)
list(POP_FRONT enable_gems_single_command_arg_list enable_gem_TARGETS)
foreach(enable_gem_arg_kw IN ITEMS PROJECT_NAME GEM GEM_FILE GEM VARIANTS TARGETS)
list(POP_FRONT enable_gems_single_command_arg_list enable_gem_${enable_gem_arg_kw})
if(enable_gem_${enable_gem_arg_kw})
# if the argument exist append to argument keyword to the front
string(PREPEND enable_gem_${enable_gem_arg_kw} "${enable_gem_arg_kw} ")
endif()
endforeach()
string(CONFIGURE "${enable_gems_template}" enable_gems_command @ONLY)
string(APPEND ENABLE_GEMS_PLACEHOLDER ${enable_gems_command})
endforeach()
file(READ ${LY_ROOT_FOLDER}/cmake/install/Copyright.in cmake_copyright_comment)
# Initialize the target install source directory to path underneath the current binary directory # Initialize the target install source directory to path underneath the current binary directory
set(target_install_source_dir ${CMAKE_CURRENT_BINARY_DIR}/install/${target_source_dir}) set(target_install_source_dir ${CMAKE_CURRENT_BINARY_DIR}/install/${relative_target_source_dir})
# Write out all the aggregated ly_add_target function calls and the final ly_create_alias() calls to the target CMakeLists.txt # Write out all the aggregated ly_add_target function calls and the final ly_create_alias() calls to the target CMakeLists.txt
file(WRITE ${target_install_source_dir}/CMakeLists.txt file(WRITE ${target_install_source_dir}/CMakeLists.txt
"${cmake_copyright_comment}" "${cmake_copyright_comment}"
"${all_configured_targets}" "${all_configured_targets}"
"\n" "\n"
"${CREATE_ALIASES_PLACEHOLDER}" "${CREATE_ALIASES_PLACEHOLDER}"
"${ENABLE_GEMS_PLACEHOLDER}"
) )
# get the component ID. if the property isn't set for the directory, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME # get the component ID. if the property isn't set for the directory, it will auto fallback to use CMAKE_INSTALL_DEFAULT_COMPONENT_NAME
get_property(install_component DIRECTORY ${absolute_target_source_dir} PROPERTY INSTALL_COMPONENT) get_property(install_component DIRECTORY ${absolute_target_source_dir} PROPERTY INSTALL_COMPONENT)
install(FILES "${target_install_source_dir}/CMakeLists.txt" install(FILES "${target_install_source_dir}/CMakeLists.txt"
DESTINATION ${target_source_dir} DESTINATION ${relative_target_source_dir}
COMPONENT ${install_component} COMPONENT ${install_component}
) )
@ -328,7 +363,7 @@ function(ly_setup_cmake_install)
# Transform the LY_EXTERNAL_SUBDIRS list into a json array # Transform the LY_EXTERNAL_SUBDIRS list into a json array
set(indent " ") set(indent " ")
foreach(external_subdir ${LY_EXTERNAL_SUBDIRS}) foreach(external_subdir ${LY_EXTERNAL_SUBDIRS})
file(RELATIVE_PATH engine_rel_external_subdir ${LY_ROOT_FOLDER} ${external_subdir}) cmake_path(RELATIVE_PATH external_subdir BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE engine_rel_external_subdir)
list(APPEND relative_external_subdirs "\"${engine_rel_external_subdir}\"") list(APPEND relative_external_subdirs "\"${engine_rel_external_subdir}\"")
endforeach() endforeach()
list(JOIN relative_external_subdirs ",\n${indent}" LY_INSTALL_EXTERNAL_SUBDIRS) list(JOIN relative_external_subdirs ",\n${indent}" LY_INSTALL_EXTERNAL_SUBDIRS)
@ -368,8 +403,8 @@ function(ly_setup_cmake_install)
# Add to the FIND_PACKAGES_PLACEHOLDER all directories in which ly_add_target were called in # Add to the FIND_PACKAGES_PLACEHOLDER all directories in which ly_add_target were called in
get_property(all_subdirectories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES) get_property(all_subdirectories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES)
foreach(target_subdirectory IN LISTS all_subdirectories) foreach(target_subdirectory IN LISTS all_subdirectories)
file(RELATIVE_PATH target_source_dir_relative ${LY_ROOT_FOLDER} ${target_subdirectory}) cmake_path(RELATIVE_PATH target_subdirectory BASE_DIRECTORY ${LY_ROOT_FOLDER} OUTPUT_VARIABLE relative_target_subdirectory)
string(APPEND FIND_PACKAGES_PLACEHOLDER " add_subdirectory(${target_source_dir_relative})\n") string(APPEND FIND_PACKAGES_PLACEHOLDER " add_subdirectory(${relative_target_subdirectory})\n")
endforeach() endforeach()
configure_file(${LY_ROOT_FOLDER}/cmake/install/Findo3de.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake @ONLY) configure_file(${LY_ROOT_FOLDER}/cmake/install/Findo3de.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake/Findo3de.cmake @ONLY)
@ -430,7 +465,7 @@ endfunction()"
get_target_property(target_runtime_output_directory ${target} RUNTIME_OUTPUT_DIRECTORY) get_target_property(target_runtime_output_directory ${target} RUNTIME_OUTPUT_DIRECTORY)
if(target_runtime_output_directory) if(target_runtime_output_directory)
file(RELATIVE_PATH target_runtime_output_subdirectory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${target_runtime_output_directory}) cmake_path(RELATIVE_PATH target_runtime_output_directory BASE_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} OUTPUT_VARIABLE target_runtime_output_subdirectory)
endif() endif()
# Qt # Qt

@ -64,9 +64,9 @@ def register_shipped_engine_o3de_objects(force: bool = False) -> int:
return ret_val return ret_val
def register_all_in_folder(folder_path: str or pathlib.Path, def register_all_in_folder(folder_path: pathlib.Path,
remove: bool = False, remove: bool = False,
engine_path: str or pathlib.Path = None, engine_path: pathlib.Path = None,
exclude: list = None) -> int: exclude: list = None) -> int:
if not folder_path: if not folder_path:
logger.error(f'Folder path cannot be empty.') logger.error(f'Folder path cannot be empty.')
@ -136,10 +136,11 @@ def register_all_in_folder(folder_path: str or pathlib.Path,
return ret_val return ret_val
def register_all_o3de_objects_of_type_in_folder(o3de_object_path: str or pathlib.Path, def register_all_o3de_objects_of_type_in_folder(o3de_object_path: pathlib.Path,
o3de_object_type: str, o3de_object_type: str,
remove: bool, remove: bool,
force: bool, force: bool,
stop_iteration_callable: callable,
**register_kwargs) -> int: **register_kwargs) -> int:
if not o3de_object_path: if not o3de_object_path:
logger.error(f'Engines path cannot be empty.') logger.error(f'Engines path cannot be empty.')
@ -155,8 +156,11 @@ def register_all_o3de_objects_of_type_in_folder(o3de_object_path: str or pathlib
ret_val = 0 ret_val = 0
for root, dirs, files in os.walk(o3de_object_path): for root, dirs, files in os.walk(o3de_object_path):
# Skip subdirectories where the stop iteration callback is true
if stop_iteration_callable and stop_iteration_callable(dirs, files):
dirs[:] = []
if f'{o3de_object_type}.json' in files: if f'{o3de_object_type}.json' in files:
o3de_object_type_set.add(root) o3de_object_type_set.add(pathlib.Path(root))
# Stop iteration of any subdirectories # 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). # Nested o3de objects of the same type aren't supported(i.e an engine cannot be inside of a engine).
dirs[:] = [] dirs[:] = []
@ -170,41 +174,49 @@ def register_all_o3de_objects_of_type_in_folder(o3de_object_path: str or pathlib
return ret_val return ret_val
def register_all_engines_in_folder(engines_path: str or pathlib.Path, def stop_on_template_folders(dirs: list, files: list) -> bool:
return 'template.json' in files
def register_all_engines_in_folder(engines_path: pathlib.Path,
remove: bool = False, remove: bool = False,
force: bool = False) -> int: force: bool = False) -> int:
return register_all_o3de_objects_of_type_in_folder(engines_path, 'engine', remove, force) return register_all_o3de_objects_of_type_in_folder(engines_path, 'engine', remove, force, None)
def register_all_projects_in_folder(projects_path: str or pathlib.Path, def register_all_projects_in_folder(projects_path: pathlib.Path,
remove: bool = False, remove: bool = False,
engine_path: str or pathlib.Path = None) -> int: engine_path: pathlib.Path = None) -> int:
return register_all_o3de_objects_of_type_in_folder(projects_path, 'project', remove, False, engine_path=engine_path) return register_all_o3de_objects_of_type_in_folder(projects_path, 'project', remove, False,
stop_on_template_folders, engine_path=engine_path)
def register_all_gems_in_folder(gems_path: str or pathlib.Path, def register_all_gems_in_folder(gems_path: pathlib.Path,
remove: bool = False, remove: bool = False,
engine_path: pathlib.Path = None, engine_path: pathlib.Path = None,
project_path: pathlib.Path = None) -> int: project_path: pathlib.Path = None) -> int:
return register_all_o3de_objects_of_type_in_folder(gems_path, 'gem', remove, False, engine_path=engine_path) return register_all_o3de_objects_of_type_in_folder(gems_path, 'gem', remove, False, stop_on_template_folders,
engine_path=engine_path)
def register_all_templates_in_folder(templates_path: str or pathlib.Path, def register_all_templates_in_folder(templates_path: pathlib.Path,
remove: bool = False, remove: bool = False,
engine_path: str or pathlib.Path = None) -> int: engine_path: pathlib.Path = None) -> int:
return register_all_o3de_objects_of_type_in_folder(templates_path, 'template', remove, False, engine_path=engine_path) return register_all_o3de_objects_of_type_in_folder(templates_path, 'template', remove, False, None,
engine_path=engine_path)
def register_all_restricted_in_folder(restricted_path: str or pathlib.Path, def register_all_restricted_in_folder(restricted_path: pathlib.Path,
remove: bool = False, remove: bool = False,
engine_path: str or pathlib.Path = None) -> int: engine_path: pathlib.Path = None) -> int:
return register_all_o3de_objects_of_type_in_folder(restricted_path, 'restricted', remove, False, engine_path=engine_path) return register_all_o3de_objects_of_type_in_folder(restricted_path, 'restricted', remove, False, None,
engine_path=engine_path)
def register_all_repos_in_folder(repos_path: str or pathlib.Path, def register_all_repos_in_folder(repos_path: pathlib.Path,
remove: bool = False, remove: bool = False,
engine_path: str or pathlib.Path = None) -> int: engine_path: pathlib.Path = None) -> int:
return register_all_o3de_objects_of_type_in_folder(repos_path, 'repo', remove, force, engine_path=engine_path) return register_all_o3de_objects_of_type_in_folder(repos_path, 'repo', remove, force, None, engine_path=engine_path)
def remove_engine_name_to_path(json_data: dict, def remove_engine_name_to_path(json_data: dict,
@ -252,7 +264,7 @@ def add_engine_name_to_path(json_data: dict, engine_path: pathlib.Path, force: b
def register_engine_path(json_data: dict, def register_engine_path(json_data: dict,
engine_path: str or pathlib.Path, engine_path: pathlib.Path,
remove: bool = False, remove: bool = False,
force: bool = False) -> int: force: bool = False) -> int:
if not engine_path: if not engine_path:
@ -260,8 +272,11 @@ def register_engine_path(json_data: dict,
return 1 return 1
engine_path = pathlib.Path(engine_path).resolve() engine_path = pathlib.Path(engine_path).resolve()
for engine_object in json_data.get('engines', {}): for engine_object in json_data.get('engines', []):
engine_object_path = pathlib.Path(engine_object['path']).resolve() if isinstance(engine_object, dict):
engine_object_path = pathlib.Path(engine_object['path']).resolve()
else:
engine_object_path = pathlib.Path(engine_object).resolve()
if engine_object_path == engine_path: if engine_object_path == engine_path:
json_data['engines'].remove(engine_object) json_data['engines'].remove(engine_object)
@ -362,7 +377,7 @@ def register_o3de_object_path(json_data: dict,
def register_external_subdirectory(json_data: dict, def register_external_subdirectory(json_data: dict,
external_subdir_path: str or pathlib.Path, external_subdir_path: pathlib.Path,
remove: bool = False, remove: bool = False,
engine_path: pathlib.Path = None, engine_path: pathlib.Path = None,
project_path: pathlib.Path = None) -> int: project_path: pathlib.Path = None) -> int:
@ -375,7 +390,7 @@ def register_external_subdirectory(json_data: dict,
def register_gem_path(json_data: dict, def register_gem_path(json_data: dict,
gem_path: str or pathlib.Path, gem_path: pathlib.Path,
remove: bool = False, remove: bool = False,
engine_path: pathlib.Path = None, engine_path: pathlib.Path = None,
project_path: pathlib.Path = None) -> int: project_path: pathlib.Path = None) -> int:
@ -384,9 +399,9 @@ def register_gem_path(json_data: dict,
def register_project_path(json_data: dict, def register_project_path(json_data: dict,
project_path: str or pathlib.Path, project_path: pathlib.Path,
remove: bool = False, remove: bool = False,
engine_path: str or pathlib.Path = None) -> int: engine_path: pathlib.Path = None) -> int:
result = register_o3de_object_path(json_data, project_path, 'projects', 'project.json', result = register_o3de_object_path(json_data, project_path, 'projects', 'project.json',
validation.valid_o3de_project_json, remove, engine_path, None) validation.valid_o3de_project_json, remove, engine_path, None)
@ -408,9 +423,10 @@ def register_project_path(json_data: dict,
update_project_json = True update_project_json = True
if update_project_json: if update_project_json:
project_json_path = project_path / 'project.json'
project_json_data['engine'] = this_engine_json['engine_name'] project_json_data['engine'] = this_engine_json['engine_name']
utils.backup_file(project_json) utils.backup_file(project_json_path)
if not manifest.save_o3de_manifest(project_json_data, project_path): if not manifest.save_o3de_manifest(project_json_data, project_json_path):
return 1 return 1
@ -418,17 +434,17 @@ def register_project_path(json_data: dict,
def register_template_path(json_data: dict, def register_template_path(json_data: dict,
template_path: str or pathlib.Path, template_path: pathlib.Path,
remove: bool = False, remove: bool = False,
engine_path: str or pathlib.Path = None) -> int: engine_path: pathlib.Path = None) -> int:
return register_o3de_object_path(json_data, template_path, 'templates', 'template.json', return register_o3de_object_path(json_data, template_path, 'templates', 'template.json',
validation.valid_o3de_template_json, remove, engine_path, None) validation.valid_o3de_template_json, remove, engine_path, None)
def register_restricted_path(json_data: dict, def register_restricted_path(json_data: dict,
restricted_path: str or pathlib.Path, restricted_path: pathlib.Path,
remove: bool = False, remove: bool = False,
engine_path: str or pathlib.Path = None) -> int: engine_path: pathlib.Path = None) -> int:
return register_o3de_object_path(json_data, restricted_path, 'restricted', 'restricted.json', return register_o3de_object_path(json_data, restricted_path, 'restricted', 'restricted.json',
validation.valid_o3de_restricted_json, remove, engine_path, None) validation.valid_o3de_restricted_json, remove, engine_path, None)
@ -468,7 +484,7 @@ def register_repo(json_data: dict,
def register_default_o3de_object_folder(json_data: dict, def register_default_o3de_object_folder(json_data: dict,
default_o3de_object_folder: str or pathlib.Path, default_o3de_object_folder: pathlib.Path,
o3de_object_key: str) -> int: o3de_object_key: str) -> int:
# make sure the path exists # make sure the path exists
default_o3de_object_folder = pathlib.Path(default_o3de_object_folder).resolve() default_o3de_object_folder = pathlib.Path(default_o3de_object_folder).resolve()
@ -482,7 +498,7 @@ def register_default_o3de_object_folder(json_data: dict,
def register_default_engines_folder(json_data: dict, def register_default_engines_folder(json_data: dict,
default_engines_folder: str or pathlib.Path, default_engines_folder: pathlib.Path,
remove: bool = False) -> int: remove: bool = False) -> int:
return register_default_o3de_object_folder(json_data, return register_default_o3de_object_folder(json_data,
manifest.get_o3de_engines_folder() if remove else default_engines_folder, manifest.get_o3de_engines_folder() if remove else default_engines_folder,
@ -490,7 +506,7 @@ def register_default_engines_folder(json_data: dict,
def register_default_projects_folder(json_data: dict, def register_default_projects_folder(json_data: dict,
default_projects_folder: str or pathlib.Path, default_projects_folder: pathlib.Path,
remove: bool = False) -> int: remove: bool = False) -> int:
return register_default_o3de_object_folder(json_data, return register_default_o3de_object_folder(json_data,
manifest.get_o3de_projects_folder() if remove else default_projects_folder, manifest.get_o3de_projects_folder() if remove else default_projects_folder,
@ -498,7 +514,7 @@ def register_default_projects_folder(json_data: dict,
def register_default_gems_folder(json_data: dict, def register_default_gems_folder(json_data: dict,
default_gems_folder: str or pathlib.Path, default_gems_folder: pathlib.Path,
remove: bool = False) -> int: remove: bool = False) -> int:
return register_default_o3de_object_folder(json_data, return register_default_o3de_object_folder(json_data,
manifest.get_o3de_gems_folder() if remove else default_gems_folder, manifest.get_o3de_gems_folder() if remove else default_gems_folder,
@ -506,7 +522,7 @@ def register_default_gems_folder(json_data: dict,
def register_default_templates_folder(json_data: dict, def register_default_templates_folder(json_data: dict,
default_templates_folder: str or pathlib.Path, default_templates_folder: pathlib.Path,
remove: bool = False) -> int: remove: bool = False) -> int:
return register_default_o3de_object_folder(json_data, return register_default_o3de_object_folder(json_data,
manifest.get_o3de_templates_folder() if remove else default_templates_folder, manifest.get_o3de_templates_folder() if remove else default_templates_folder,
@ -514,7 +530,7 @@ def register_default_templates_folder(json_data: dict,
def register_default_restricted_folder(json_data: dict, def register_default_restricted_folder(json_data: dict,
default_restricted_folder: str or pathlib.Path, default_restricted_folder: pathlib.Path,
remove: bool = False) -> int: remove: bool = False) -> int:
return register_default_o3de_object_folder(json_data, return register_default_o3de_object_folder(json_data,
manifest.get_o3de_restricted_folder() if remove else default_restricted_folder, manifest.get_o3de_restricted_folder() if remove else default_restricted_folder,
@ -527,18 +543,18 @@ def register_default_third_party_folder(json_data: dict,
manifest.get_o3de_third_party_folder() if remove else default_third_party_folder, manifest.get_o3de_third_party_folder() if remove else default_third_party_folder,
'default_third_party_folder') 'default_third_party_folder')
def register(engine_path: str or pathlib.Path = None, def register(engine_path: pathlib.Path = None,
project_path: str or pathlib.Path = None, project_path: pathlib.Path = None,
gem_path: str or pathlib.Path = None, gem_path: pathlib.Path = None,
external_subdir_path: str or pathlib.Path = None, external_subdir_path: pathlib.Path = None,
template_path: str or pathlib.Path = None, template_path: pathlib.Path = None,
restricted_path: str or pathlib.Path = None, restricted_path: pathlib.Path = None,
repo_uri: str or pathlib.Path = None, repo_uri: str or pathlib.Path = None,
default_engines_folder: str or pathlib.Path = None, default_engines_folder: pathlib.Path = None,
default_projects_folder: str or pathlib.Path = None, default_projects_folder: pathlib.Path = None,
default_gems_folder: str or pathlib.Path = None, default_gems_folder: pathlib.Path = None,
default_templates_folder: str or pathlib.Path = None, default_templates_folder: pathlib.Path = None,
default_restricted_folder: str or pathlib.Path = None, default_restricted_folder: pathlib.Path = None,
default_third_party_folder: pathlib.Path = None, default_third_party_folder: pathlib.Path = None,
external_subdir_engine_path: pathlib.Path = None, external_subdir_engine_path: pathlib.Path = None,
external_subdir_project_path: pathlib.Path = None, external_subdir_project_path: pathlib.Path = None,
@ -576,32 +592,32 @@ def register(engine_path: str or pathlib.Path = None,
result = 0 result = 0
# do anything that could require a engine context first # do anything that could require a engine context first
if isinstance(project_path, str) or isinstance(project_path, pathlib.PurePath): if isinstance(project_path, pathlib.PurePath):
if not project_path: if not project_path:
logger.error(f'Project path cannot be empty.') logger.error(f'Project path cannot be empty.')
return 1 return 1
result = result or register_project_path(json_data, project_path, remove, engine_path) result = result or register_project_path(json_data, project_path, remove, engine_path)
if isinstance(gem_path, str) or isinstance(gem_path, pathlib.PurePath): if isinstance(gem_path, pathlib.PurePath):
if not gem_path: if not gem_path:
logger.error(f'Gem path cannot be empty.') logger.error(f'Gem path cannot be empty.')
return 1 return 1
result = result or register_gem_path(json_data, gem_path, remove, result = result or register_gem_path(json_data, gem_path, remove,
external_subdir_engine_path, external_subdir_project_path) external_subdir_engine_path, external_subdir_project_path)
if isinstance(external_subdir_path, str) or isinstance(external_subdir_path, pathlib.PurePath): if isinstance(external_subdir_path, pathlib.PurePath):
if not external_subdir_path: if not external_subdir_path:
logger.error(f'External Subdirectory path is None.') logger.error(f'External Subdirectory path is None.')
return 1 return 1
result = result or register_external_subdirectory(json_data, external_subdir_path, remove, result = result or register_external_subdirectory(json_data, external_subdir_path, remove,
external_subdir_engine_path, external_subdir_project_path) external_subdir_engine_path, external_subdir_project_path)
if isinstance(template_path, str) or isinstance(template_path, pathlib.PurePath): if isinstance(template_path, pathlib.PurePath):
if not template_path: if not template_path:
logger.error(f'Template path cannot be empty.') logger.error(f'Template path cannot be empty.')
return 1 return 1
result = result or register_template_path(json_data, template_path, remove, engine_path) result = result or register_template_path(json_data, template_path, remove, engine_path)
if isinstance(restricted_path, str) or isinstance(restricted_path, pathlib.PurePath): if isinstance(restricted_path, pathlib.PurePath):
if not restricted_path: if not restricted_path:
logger.error(f'Restricted path cannot be empty.') logger.error(f'Restricted path cannot be empty.')
return 1 return 1
@ -613,28 +629,28 @@ def register(engine_path: str or pathlib.Path = None,
return 1 return 1
result = result or register_repo(json_data, repo_uri, remove) result = result or register_repo(json_data, repo_uri, remove)
if isinstance(default_engines_folder, str) or isinstance(default_engines_folder, pathlib.PurePath): if isinstance(default_engines_folder, pathlib.PurePath):
result = result or register_default_engines_folder(json_data, default_engines_folder, remove) result = result or register_default_engines_folder(json_data, default_engines_folder, remove)
if isinstance(default_projects_folder, str) or isinstance(default_projects_folder, pathlib.PurePath): if isinstance(default_projects_folder, pathlib.PurePath):
result = result or register_default_projects_folder(json_data, default_projects_folder, remove) result = result or register_default_projects_folder(json_data, default_projects_folder, remove)
if isinstance(default_gems_folder, str) or isinstance(default_gems_folder, pathlib.PurePath): if isinstance(default_gems_folder, pathlib.PurePath):
result = result or register_default_gems_folder(json_data, default_gems_folder, remove) result = result or register_default_gems_folder(json_data, default_gems_folder, remove)
if isinstance(default_templates_folder, str) or isinstance(default_templates_folder, pathlib.PurePath): if isinstance(default_templates_folder, pathlib.PurePath):
result = result or register_default_templates_folder(json_data, default_templates_folder, remove) result = result or register_default_templates_folder(json_data, default_templates_folder, remove)
if isinstance(default_restricted_folder, str) or isinstance(default_restricted_folder, pathlib.PurePath): if isinstance(default_restricted_folder, pathlib.PurePath):
result = result or register_default_restricted_folder(json_data, default_restricted_folder, remove) result = result or register_default_restricted_folder(json_data, default_restricted_folder, remove)
if isinstance(default_third_party_folder, str) or isinstance(default_third_party_folder, pathlib.PurePath): if isinstance(default_third_party_folder, pathlib.PurePath):
result = result or register_default_third_party_folder(json_data, default_third_party_folder, remove) result = result or register_default_third_party_folder(json_data, default_third_party_folder, remove)
# engine is done LAST # engine is done LAST
# Now that everything that could have an engine context is done, if the engine is supplied that means this is # Now that everything that could have an engine context is done, if the engine is supplied that means this is
# registering the engine itself # registering the engine itself
if isinstance(engine_path, str) or isinstance(engine_path, pathlib.PurePath): if isinstance(engine_path, pathlib.PurePath):
if not engine_path: if not engine_path:
logger.error(f'Engine path cannot be empty.') logger.error(f'Engine path cannot be empty.')
return 1 return 1
@ -789,41 +805,41 @@ def add_parser_args(parser):
group.add_argument('--this-engine', action='store_true', required=False, group.add_argument('--this-engine', action='store_true', required=False,
default=False, default=False,
help='Registers the engine this script is running from.') help='Registers the engine this script is running from.')
group.add_argument('-ep', '--engine-path', type=str, required=False, group.add_argument('-ep', '--engine-path', type=pathlib.Path, required=False,
help='Engine path to register/remove.') help='Engine path to register/remove.')
group.add_argument('-pp', '--project-path', type=str, required=False, group.add_argument('-pp', '--project-path', type=pathlib.Path, required=False,
help='Project path to register/remove.') help='Project path to register/remove.')
group.add_argument('-gp', '--gem-path', type=str, required=False, group.add_argument('-gp', '--gem-path', type=pathlib.Path, required=False,
help='Gem path to register/remove.') help='Gem path to register/remove.')
group.add_argument('-es', '--external-subdirectory', type=str, required=False, group.add_argument('-es', '--external-subdirectory', type=pathlib.Path, required=False,
help='External subdirectory path to register/remove.') help='External subdirectory path to register/remove.')
group.add_argument('-tp', '--template-path', type=str, required=False, group.add_argument('-tp', '--template-path', type=pathlib.Path, required=False,
help='Template path to register/remove.') help='Template path to register/remove.')
group.add_argument('-rp', '--restricted-path', type=str, required=False, group.add_argument('-rp', '--restricted-path', type=pathlib.Path, required=False,
help='A restricted folder to register/remove.') help='A restricted folder to register/remove.')
group.add_argument('-ru', '--repo-uri', type=str, required=False, group.add_argument('-ru', '--repo-uri', type=str, required=False,
help='A repo uri to register/remove.') help='A repo uri to register/remove.')
group.add_argument('-aep', '--all-engines-path', type=str, required=False, group.add_argument('-aep', '--all-engines-path', type=pathlib.Path, required=False,
help='All engines under this folder to register/remove.') help='All engines under this folder to register/remove.')
group.add_argument('-app', '--all-projects-path', type=str, required=False, group.add_argument('-app', '--all-projects-path', type=pathlib.Path, required=False,
help='All projects under this folder to register/remove.') help='All projects under this folder to register/remove.')
group.add_argument('-agp', '--all-gems-path', type=str, required=False, group.add_argument('-agp', '--all-gems-path', type=pathlib.Path, required=False,
help='All gems under this folder to register/remove.') help='All gems under this folder to register/remove.')
group.add_argument('-atp', '--all-templates-path', type=str, required=False, group.add_argument('-atp', '--all-templates-path', type=pathlib.Path, required=False,
help='All templates under this folder to register/remove.') help='All templates under this folder to register/remove.')
group.add_argument('-arp', '--all-restricted-path', type=str, required=False, group.add_argument('-arp', '--all-restricted-path', type=pathlib.Path, required=False,
help='All templates under this folder to register/remove.') help='All templates under this folder to register/remove.')
group.add_argument('-aru', '--all-repo-uri', type=str, required=False, group.add_argument('-aru', '--all-repo-uri', type=pathlib.Path, required=False,
help='All repos under this folder to register/remove.') help='All repos under this folder to register/remove.')
group.add_argument('-def', '--default-engines-folder', type=str, required=False, group.add_argument('-def', '--default-engines-folder', type=pathlib.Path, required=False,
help='The default engines folder to register/remove.') help='The default engines folder to register/remove.')
group.add_argument('-dpf', '--default-projects-folder', type=str, required=False, group.add_argument('-dpf', '--default-projects-folder', type=pathlib.Path, required=False,
help='The default projects folder to register/remove.') help='The default projects folder to register/remove.')
group.add_argument('-dgf', '--default-gems-folder', type=str, required=False, group.add_argument('-dgf', '--default-gems-folder', type=pathlib.Path, required=False,
help='The default gems folder to register/remove.') help='The default gems folder to register/remove.')
group.add_argument('-dtf', '--default-templates-folder', type=str, required=False, group.add_argument('-dtf', '--default-templates-folder', type=pathlib.Path, required=False,
help='The default templates folder to register/remove.') help='The default templates folder to register/remove.')
group.add_argument('-drf', '--default-restricted-folder', type=str, required=False, group.add_argument('-drf', '--default-restricted-folder', type=pathlib.Path, required=False,
help='The default restricted folder to register/remove.') help='The default restricted folder to register/remove.')
group.add_argument('-dtpf', '--default-third-party-folder', type=pathlib.Path, required=False, group.add_argument('-dtpf', '--default-third-party-folder', type=pathlib.Path, required=False,
help='The default 3rd Party folder to register/remove.') help='The default 3rd Party folder to register/remove.')
@ -831,7 +847,7 @@ def add_parser_args(parser):
default=False, default=False,
help='Refresh the repo cache.') help='Refresh the repo cache.')
parser.add_argument('-ohf', '--override-home-folder', type=str, required=False, 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.') 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, parser.add_argument('-r', '--remove', action='store_true', required=False,
default=False, default=False,

Loading…
Cancel
Save