From ee0ecc2fa03b73cb95e3987d56a63441cfc8fe0a Mon Sep 17 00:00:00 2001 From: mnaumov Date: Wed, 2 Jun 2021 11:49:08 -0700 Subject: [PATCH 1/3] [ATOM-15711] Changing thumbmail sphere to polar sphere --- Gems/Atom/Feature/Common/Assets/Models/sphere.fbx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Models/sphere.fbx b/Gems/Atom/Feature/Common/Assets/Models/sphere.fbx index 5c8f550c8c..eb02c68394 100644 --- a/Gems/Atom/Feature/Common/Assets/Models/sphere.fbx +++ b/Gems/Atom/Feature/Common/Assets/Models/sphere.fbx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a476e99b55cf2a76fef6775c5a57dad29f8ffcb942c625bab04c89051a72a560 -size 62626 +oid sha256:838830c99f344f5b68e5e85c9bc52751350caf48e662c9c2b767ab77039bbd8f +size 103472 From 0a9d6f5f0f54419b8bab035379b1f631c97a4a53 Mon Sep 17 00:00:00 2001 From: chcurran Date: Thu, 3 Jun 2021 13:13:22 -0700 Subject: [PATCH 2/3] Bug fixes and improvements brought over from demo work. * Generic Multi Function Call ability added to extensible nodes * Code gen improvements, including allowing for more manually codewritten extension of codegen facilities * CVAR to disable automatic update of deprecated node * Fixed variable sorting error that can apply to parser/runtime added variables * Made Edit/SerializeContext ClassBuilder public, as it was needlessly private * Fixed dangerous Datum::GetValueAddress(), it now checks for an empty storage AZSTd::any, as does Datum::Empty() --- .../AzCore/AzCore/Serialization/EditContext.h | 5 +- .../AzCore/Serialization/SerializeContext.h | 10 +- .../Code/Editor/Components/EditorGraph.cpp | 5 +- .../AutoGen/ScriptCanvasNodeable_Header.jinja | 40 +- .../AutoGen/ScriptCanvasNodeable_Source.jinja | 160 +- .../ScriptCanvas_Nodeable_Macros.jinja | 5 +- .../Code/Include/ScriptCanvas/Core/Core.h | 1 - .../Code/Include/ScriptCanvas/Core/Datum.cpp | 8 +- .../Code/Include/ScriptCanvas/Core/Datum.h | 2 +- .../Code/Include/ScriptCanvas/Core/Node.cpp | 11 +- .../Code/Include/ScriptCanvas/Core/Node.h | 2 + .../Include/ScriptCanvas/Core/NodeableNode.h | 2 + .../Code/Include/ScriptCanvas/Core/PureData.h | 2 + .../Grammar/AbstractCodeModel.cpp | 193 +- .../ScriptCanvas/Grammar/AbstractCodeModel.h | 2 + .../ScriptCanvas/Grammar/ParsingUtilities.cpp | 10 +- .../Include/ScriptCanvas/Grammar/Primitives.h | 28 + .../Grammar/PrimitivesExecution.cpp | 38 +- .../Grammar/PrimitivesExecution.h | 8 + .../Include/ScriptCanvas/Results/ErrorText.h | 6 + ...UnitTest_RunAllTransformNodes.scriptcanvas | 9124 +++++++---------- 21 files changed, 4416 insertions(+), 5246 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Serialization/EditContext.h b/Code/Framework/AzCore/AzCore/Serialization/EditContext.h index ed19130579..394a6f047c 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/EditContext.h +++ b/Code/Framework/AzCore/AzCore/Serialization/EditContext.h @@ -127,13 +127,14 @@ namespace AZ */ class EditContext { + public: /// @cond EXCLUDE_DOCS class ClassBuilder; class EnumBuilder; using ClassInfo = ClassBuilder; ///< @deprecated Use EditContext::ClassBuilder using EnumInfo = EnumBuilder; ///< @deprecated Use EditContext::EnumBuilder /// @endcond - public: + AZ_CLASS_ALLOCATOR(EditContext, SystemAllocator, 0); /** @@ -186,6 +187,7 @@ namespace AZ * look at the unit tests and example to see use cases. * */ + public: class ClassBuilder { friend EditContext; @@ -399,6 +401,7 @@ namespace AZ EnumBuilder* Value(const char* name, E value); }; + private: typedef AZStd::list ClassDataListType; typedef AZStd::unordered_map EnumDataMapType; diff --git a/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.h b/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.h index 6806232337..279ce01039 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.h +++ b/Code/Framework/AzCore/AzCore/Serialization/SerializeContext.h @@ -101,6 +101,9 @@ namespace AZ class SerializeContext : public ReflectContext { + static const unsigned int VersionClassDeprecated = (unsigned int)-1; + + public: /// @cond EXCLUDE_DOCS friend class EditContext; class ClassBuilder; @@ -108,9 +111,6 @@ namespace AZ /// @endcond class EnumBuilder; - static const unsigned int VersionClassDeprecated = (unsigned int)-1; - - public: class ClassData; struct EnumerateInstanceCallContext; struct ClassElement; @@ -1131,6 +1131,7 @@ namespace AZ * ->Version(3,&MyVersionConverter) * ->Field("data",&MyStruct::m_data); */ + public: class ClassBuilder { friend class SerializeContext; @@ -1330,7 +1331,8 @@ namespace AZ AZStd::vector* m_currentAttributes = nullptr; }; - EditContext* m_editContext; ///< Pointer to optional edit context. + private: + EditContext* m_editContext; ///< Pointer to optional edit context. UuidToClassMap m_uuidMap; ///< Map for all class in this serialize context AZStd::unordered_multimap m_classNameToUuid; /// Map all class names to their uuid AZStd::unordered_multimap m_uuidGenericMap; ///< Uuid to ClassData map of reflected classes with GenericTypeInfo diff --git a/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp b/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp index 6f28cbfe19..616be11dd0 100644 --- a/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Components/EditorGraph.cpp @@ -91,7 +91,8 @@ AZ_POP_DISABLE_WARNING #include #include -//// + AZ_CVAR(bool, g_disableDeprecatedNodeUpdates, false, {}, AZ::ConsoleFunctorFlags::Null, + "Disables automatic update attempts of deprecated nodes, so that graphs that require and update can be viewed in their original form"); namespace EditorGraphCpp { @@ -3642,7 +3643,7 @@ namespace ScriptCanvasEditor if (scriptCanvasNode) { - if (scriptCanvasNode->IsDeprecated()) + if (scriptCanvasNode->IsDeprecated() && !g_disableDeprecatedNodeUpdates) { ScriptCanvas::NodeConfiguration nodeConfig = scriptCanvasNode->GetReplacementNodeConfiguration(); if (nodeConfig.IsValid()) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Header.jinja b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Header.jinja index 202fad5b60..313fd196b0 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Header.jinja +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Header.jinja @@ -22,6 +22,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #pragma once +#include +#include +#include +#include + #include #include @@ -43,6 +48,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. {% endif %} {% endif %} +{%- set attribute_Base = Class.attrib['Base'] %} +{% if not Class.attrib['Base'] is defined %} +{% set attribute_Base = "ScriptCanvas::Nodeable" %} +{% endif %} + {% if attribute_Namespace is defined %} namespace {{attribute_Namespace}} { @@ -66,6 +76,9 @@ namespace {{attribute_Namespace}} public: \ AZ_RTTI({{className}}, "{{nodeableClassName|createHashGuid}}"{% if Class.attrib['Base'] is defined %}, {{ Class.attrib['Base'] }}{% endif %}); \ static void Reflect(AZ::ReflectContext* reflection); \ + static void ExtendReflectionSerialize([[maybe_unused]] AZ::SerializeContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionSerialize'] is defined %};{% else %}{}{% endif %} \ + static void ExtendReflectionEdit([[maybe_unused]] AZ::EditContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionEdit'] is defined %};{% else %}{}{% endif %} \ + static void ExtendReflectionBehavior([[maybe_unused]] AZ::BehaviorContext::ClassBuilder<{{className}}>* builder){% if Class.attrib['ExtendReflectionBehavior'] is defined %};{% else %}{}{% endif %} \ static const char* GetDescription() { return "{{ macros.GetAttributeAsString(Class.attrib, 'Description') }}"; } \ ScriptCanvas::NodePropertyInterface* GetPropertyInterface(AZ::Crc32 propertyId) override; \ bool IsActive() const override { return false; } \ @@ -83,13 +96,33 @@ public: \ AZ_COMPONENT({{nodeableNodeName}}, {% if Class.attrib['NodeableUuid'] is defined %}"{{Class.attrib['NodeableUuid']}}"{% else %}"{{nodeableNodeName|createHashGuid}}"{% endif %}, ScriptCanvas::Nodes::NodeableNode); static void Reflect(AZ::ReflectContext* context); + + static void ExtendReflectionSerialize([[maybe_unused]] AZ::SerializeContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionSerialize'] is defined %};{% else %}{}{% endif %} + + static void ExtendReflectionEdit([[maybe_unused]] AZ::EditContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionEdit'] is defined %};{% else %}{}{% endif %} + + static void ExtendReflectionBehavior([[maybe_unused]] AZ::BehaviorContext::ClassBuilder<{{nodeableNodeName}}>* builder){% if Class.attrib['ExtendReflectionBehavior'] is defined %};{% else %}{}{% endif %} + void ConfigureSlots() override; + +{% if Class.attrib['ExtendConfigureSlots'] is defined %} + void ExtendConfigureSlots([[maybe_unused]] SlotExecution::Ins& ins, [[maybe_unused]] SlotExecution::Outs& latents); + +{% else %} + /* no slot configuration extension, Use Class attribute 'ExtendConfigureSlots' to extend them */ + +{% endif %} void ConfigureVisualExtensions() override; + size_t GenerateFingerprint() const override; -{% if Class.attrib['EntryPoint'] is defined and Class.attrib['EntryPoint'] == "true" %} + +{% if Class.attrib['EntryPoint'] is defined and Class.attrib['EntryPoint'] == "true" %} bool IsEntryPoint() const override { return true; } -{% endif %} + +{% endif %} {{nodeableNodeName}}(); + + {{Class.attrib['NodeDeclarations']}} }; } {% endif %} @@ -100,6 +133,5 @@ public: \ {{ macros.ReportErrors() }} - {% endfor %} -{% endfor %} +{% endfor %} \ No newline at end of file diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja index 71681ed73b..b977daf43b 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja @@ -19,15 +19,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#include -#include -#include - #include #include #include #include - #include {% for xml in dataFiles %} @@ -47,13 +42,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. {%- set attribute_Category = Class.attrib['Category'] %} {%- set attribute_Uuid = Class.attrib['Uuid'] %} {%- set attribute_Icon = Class.attrib['Icon'] %} -{%- set attribute_Base = Class.attrib['Base'] %} {%- set attribute_GeneratePropertyFriend = Class.attrib['GeneratePropertyFriend'] %} {%- set attribute_Version = Class.attrib['Version'] %} {%- set attribute_VersionConverter = Class.attrib['VersionConverter'] %} {%- set attribute_EventHandler = Class.attrib['EventHandler'] %} {%- set attribute_Deprecated = Class.attrib['Deprecated'] %} + +{%- set attribute_Base = Class.attrib['Base'] %} +{% if not Class.attrib['Base'] is defined %} +{% set attribute_Base = "ScriptCanvas::Nodeable" %} +{% endif %} + {% set attribute_Namespace = undefined %} {%- if Class.attrib['Namespace'] is defined %} {% if Class.attrib['Namespace'] != "None" %} @@ -95,19 +95,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. {{CollectDisplayGroups('Output')}} {{CollectDisplayGroups('Parameter')}} -{# FOR DEBUGGING / DIAGNOSTIC - -// Standalone (No DisplayGroup) {{ global_standaloneTagMap }} -{% for key, value in global_standaloneTagMap.items() %} -// {{key}} : {{value}} -{% endfor %} - -// DisplayGrouped {{ global_displayGroupMap }} -{% for key, value in global_displayGroupMap.items() %} -// {{key}} : {{value}} -{% endfor %} -#} - {# ----------------------------------------------------------------------------------------- #} {% if attribute_Namespace is defined %} @@ -115,33 +102,6 @@ namespace {{attribute_Namespace}} { {% endif %} -{# Standard "In" function } -{{nodemacro.FunctionSignature(attribute_QualifiedName, Class)}} -{ -{%- for parameter in Class.findall('Parameter') -%} -{% if parameter.attrib['Input'] is defined and parameter.attrib['Input'] == "True" %} - -// this->{{parameter.attrib['Name']}} = arg{{loop.index0}}; - -{%- endif -%} -{% endfor %} - -{% set returnNames = [] %} -{% set returnTypes = [] %} -{%- for return in Class.findall('Parameter') -%} -{%- if return.attrib['Output'] is defined and return.attrib['Output'] == "True" -%} -{% if returnTypes.append(return.attrib['Type']) %}{% endif %} -{% if returnNames.append("this->" + return.attrib['Name']) %}{% endif %} -{%- endif -%} -{%- endfor -%} - -{% if returnNames|length() == 1 %} -return {{returnNames[0]}}; -{% elif returnNames|length() > 1 %} - return AZStd::tuple<{{returnTypes|join(", ")}}>({{returnNames|join(", ")}}); -{% endif %} -} -#} {%- set nodeableNodeName = attribute_Name + 'Node' %} {% set list_outputs = [] %} {% for output in Class.iter('Output') %} @@ -156,43 +116,81 @@ return {{returnNames[0]}}; {% for item in Class.iter('Output') %} {% if item.attrib['DisplayGroup'] is defined %}{% set displayGroup = item.attrib['DisplayGroup'] %}{% endif %} {% endfor %} +{% set branches = [] %} +{% for method in Class.findall('Input') %} +{% for branch in method.findall('Branch') %} +{% if branches.append(branch) %}{% endif %} +{% endfor %} +{% endfor %} {# ExecutionOuts #} // ExecutionOuts begin {{ nodemacro.ExecutionOutDefinitions(Class, attribute_QualifiedName)}} +{% if not Class.attrib['ExtendConfigureSlots'] is defined %} size_t {{attribute_QualifiedName}}::GetRequiredOutCount() const { return {{Class.findall('Output')|length + branches|length}}; }{% endif %} // ExecutionOuts end {# Reflect #} + +{% if Class.attrib['ExtendReflectionSerialize'] is defined %} +{% set ExtendReflectionSerialize = "defined" %} +{% set preSerialize = "serializeBuilder" %} +{% set postSerialize = ";" %} +{% else %} +{% set preSerialize = "" %} +{% set postSerialize = "" %} +{% endif %} + +{% if Class.attrib['ExtendReflectionEdit'] is defined %} +{% set ExtendReflectionEdit = "defined" %} +{% set preEdit = "editorBuilder" %} +{% set postEdit = ";" %} +{% else %} +{% set preEdit = "" %} +{% set postEdit = "" %} +{% endif %} + +{% if Class.attrib['ExtendReflectionBehavior'] is defined %} +{% set ExtendReflectionBehavior = "defined" %} +{% set preBehavior = "behaviorBuilder" %} +{% set postBehavior = ";" %} +{% else %} +{% set preBehavior = "" %} +{% set postBehavior = "" %} +{% endif %} + void {{attribute_QualifiedName}}::Reflect(AZ::ReflectContext* context) { using namespace ScriptCanvas; if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) { - serializeContext->Class<{{ attribute_Name }}{% if attribute_Base is defined %}, {{ attribute_Base }}{% endif %}>() +{% if ExtendReflectionSerialize is defined %} auto {{preSerialize}} = {% else %} {% endif %}serializeContext->Class<{{ attribute_Name }}{% if attribute_Base is defined %}, {{ attribute_Base }}{% endif %}>(){{postSerialize}} {% if attribute_EventHandler is defined %} - ->EventHandler<{{ attribute_EventHandler }}>() + {{preSerialize}}->EventHandler<{{ attribute_EventHandler }}>(){{postSerialize}} {% endif %} {# Serialized Properties #} {% for Property in Class.iter('Property') %} {% set property_Name = Property.attrib['Name'] %} - ->Field("{{ property_Name }}", &{{ attribute_Name }}::{{ property_Name | replace(' ','') }}) + {{preSerialize}}->Field("{{ property_Name }}", &{{ attribute_Name }}::{{ property_Name | replace(' ','') }}){{postSerialize}} {% endfor %} ; +{% if ExtendReflectionSerialize is defined %} + ExtendReflectionSerialize(&{{preSerialize}}); +{% endif %} + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { - editContext->Class<{{ attribute_QualifiedName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - +{% if ExtendReflectionEdit is defined %} auto {{preEdit}} = {% else %} {% endif %}editContext->Class<{{ attribute_QualifiedName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}"){{postEdit}} + {{preEdit}}->ClassElement(AZ::Edit::ClassElements::EditorData, ""){{postEdit}} + {{preEdit}}->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly){{postEdit}} {% if attribute_Category is defined %} - ->Attribute(AZ::Edit::Attributes::Category, "{{ attribute_Category }}") + {{preEdit}}->Attribute(AZ::Edit::Attributes::Category, "{{ attribute_Category }}"){{postEdit}} {%- endif %} {% if attribute_Icon is defined %} - ->Attribute(AZ::Edit::Attributes::Icon, "{{ attribute_Icon }}") + {{preEdit}}->Attribute(AZ::Edit::Attributes::Icon, "{{ attribute_Icon }}"){{postEdit}} {%- endif %} {% if attribute_Deprecated is defined %} - ->Attribute(AZ::Edit::Attributes::Deprecated, "{{ attribute_Deprecated }}") + {{preEdit}}->Attribute(AZ::Edit::Attributes::Deprecated, "{{ attribute_Deprecated }}"){{postEdit}} {%- endif %} {% set uihandler = 'AZ::Edit::UIHandlers::Default' %} {% for item in Class.iter('Property') %} @@ -203,29 +201,34 @@ void {{attribute_QualifiedName}}::Reflect(AZ::ReflectContext* context) {% if item.attrib['Description'] is defined %} {% set description = item.attrib['Description'] %} {% endif %} - // {{ item.attrib['Name'] }} - ->DataElement({{ uihandler }}, &{{ attribute_Name }}::{{ item.attrib['Name'] }}, "{{ item.attrib['Name'] }}", "{{ description }}") + // {{ item.attrib['Name'] }} + {{preEdit}}->DataElement({{ uihandler }}, &{{ attribute_Name }}::{{ item.attrib['Name'] }}, "{{ item.attrib['Name'] }}", "{{ description }}"){{postEdit}} {% for EditAttribute in item.iter('EditAttribute') %} - ->Attribute({{ EditAttribute.attrib['Key'] }}, {{ EditAttribute.attrib['Value'] }}) + {{preEdit}}->Attribute({{ EditAttribute.attrib['Key'] }}, {{ EditAttribute.attrib['Value'] }}){{postEdit}} {% endfor %} {% endfor %} ; +{% if ExtendReflectionEdit is defined %} + ExtendReflectionEdit(&{{preEdit}}); +{% endif %} } } // Behavior Context Reflection if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { - behaviorContext->Class<{{ attribute_Name }}>("{{ attribute_Name }}") - ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::List) - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) +{% if ExtendReflectionBehavior is defined %} auto {{preBehavior}} = {% else %} {% endif %}behaviorContext->Class<{{ attribute_Name }}>("{{ attribute_Name }}"){{postBehavior}} + {{preBehavior}}->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::List){{postBehavior}} + {{preBehavior}}->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common){{postBehavior}} {% for inputMethod in Class.iter('Input') %} {% set methodName = inputMethod.attrib['Name'] %} // {{ inputMethod.attrib['Name'] }} - ->Method(Grammar::ToIdentifier("{{ macros.SlotName(methodName) }}").c_str(), &{{ attribute_Name }}::{{ macros.CleanName(methodName) }}) + {{preBehavior}}->Method(Grammar::ToIdentifier("{{ macros.SlotName(methodName) }}").c_str(), &{{ attribute_Name }}::{{ macros.CleanName(methodName) }}){{postBehavior}} {% endfor %} - ; +{% if ExtendReflectionBehavior is defined %} + ExtendReflectionBehavior(&{{preBehavior}}); +{% endif %} } } @@ -253,27 +256,32 @@ Nodes::{{ nodeableNodeName }}::{{ nodeableNodeName }}() {# NodeableNode Reflection #} void Nodes::{{ nodeableNodeName }}::Reflect(AZ::ReflectContext* context) { - {{ attribute_QualifiedName }}::Reflect(context); // Serialization Context Reflection if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) { - serializeContext->Class<{{ nodeableNodeName }}, NodeableNode>() + {%if ExtendReflectionSerialize is defined%}auto {{preSerialize}} = {%endif%}serializeContext->Class<{{ nodeableNodeName }}, NodeableNode>(){{postSerialize}} {% if attribute_Version is defined %} - ->Version({{ attribute_Version }}{% if attribute_VersionConverter is defined %}, &{{ attribute_VersionConverter }}{% endif %}) + {{preSerialize}}->Version({{ attribute_Version }}{% if attribute_VersionConverter is defined %}, &{{ attribute_VersionConverter }}{% endif %}){{postSerialize}} {% else %} - ->Version(0) + {{preSerialize}}->Version(0){{postSerialize}} +{% endif %} + ; +{% if ExtendReflectionSerialize is defined %} + ExtendReflectionSerialize(&{{preSerialize}}); {% endif %} - ; if (AZ::EditContext* editContext = serializeContext->GetEditContext()) { - editContext->Class<{{ nodeableNodeName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ; + {% if ExtendReflectionEdit is defined %}auto {{preEdit}} = {%endif%}editContext->Class<{{ nodeableNodeName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}"){{postEdit}} + {{preEdit}}->ClassElement(AZ::Edit::ClassElements::EditorData, ""){{postEdit}} + {{preEdit}}->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly){{postEdit}} + {{preEdit}}->Attribute(AZ::Edit::Attributes::AutoExpand, true){{postEdit}} + ; +{% if ExtendReflectionEdit is defined %} + ExtendReflectionEdit(&{{preEdit}}); +{% endif %} } } } @@ -324,6 +332,7 @@ void Nodes::{{ nodeableNodeName }}::ConfigureVisualExtensions() RegisterExtension(visualExtensions); } {% endfor %} + OnConfigureVisualExtensions(); } {# ConfigureSlots #} @@ -457,6 +466,9 @@ void Nodes::{{ nodeableNodeName }}::ConfigureSlots() {% endfor %} #} +{% if Class.attrib['ExtendConfigureSlots'] is defined %} + ExtendConfigureSlots(ins, outs); +{% endif %} // Generate the execution map m_slotExecutionMap = SlotExecution::Map(AZStd::move(ins), AZStd::move(outs)); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvas_Nodeable_Macros.jinja b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvas_Nodeable_Macros.jinja index addd24f5d3..a7300791e8 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvas_Nodeable_Macros.jinja +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvas_Nodeable_Macros.jinja @@ -349,7 +349,4 @@ void {{qualifiedName}}::Call{{CleanName(outName)}}({{ExecutionOutReturnDefinitio {%- for executionOut in Class.findall('Output') -%} {{ ExecutionOutDefinition(Class, qualifiedName, executionOut, loop.index0 + branches|length) }} {%- endfor %} - -size_t {{qualifiedName}}::GetRequiredOutCount() const { return {{Class.findall('Output')|length + branches|length}}; } - -{% endmacro %} +{% endmacro %} \ No newline at end of file diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h index 5b1e5eee76..8f3cefa138 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Core.h @@ -250,7 +250,6 @@ namespace ScriptCanvas }; using ScriptCanvasSettingsRequestBus = AZ::EBus; - } namespace AZStd diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp index 30e55cee49..64c9f2497d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.cpp @@ -1476,9 +1476,11 @@ namespace ScriptCanvas const void* Datum::GetValueAddress() const { - return m_type.GetType() != Data::eType::BehaviorContextObject - ? AZStd::any_cast(&m_storage) - : (*AZStd::any_cast(&m_storage))->Get(); + return !m_storage.empty() + ? m_type.GetType() != Data::eType::BehaviorContextObject + ? AZStd::any_cast(&m_storage) + : (*AZStd::any_cast(&m_storage))->Get() + : nullptr; } bool Datum::Initialize(const Data::Type& type, eOriginality originality, const void* source, const AZ::Uuid& sourceTypeID) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.h index 781dcb9ed8..be6f3ee37e 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Datum.h @@ -389,7 +389,7 @@ namespace ScriptCanvas bool Datum::Empty() const { - return GetValueAddress() == nullptr; + return m_storage.empty() || GetValueAddress() == nullptr; } template diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.cpp index 4a936c8824..98d5e6b121 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.cpp @@ -3577,12 +3577,12 @@ namespace ScriptCanvas } if (targetSlotType == CombinedSlotType::DataOut - && executionSlot.GetType() == CombinedSlotType::ExecutionIn - && executionInCount > 1) + && executionSlot.GetType() == CombinedSlotType::ExecutionIn + && executionInCount > 1) { if (!executionChildSlot || executionChildSlot->GetType() != CombinedSlotType::ExecutionOut) { - return AZ::Failure(AZStd::string("Data out by ExcutionIn must have child out slot")); + return AZ::Failure(AZStd::string("Data out by ExecutionIn must have child out slot")); } } @@ -3626,6 +3626,11 @@ namespace ScriptCanvas return {}; } + Grammar::MultipleFunctionCallFromSingleSlotInfo Node::GetMultipleFunctionCallFromSingleSlotInfo([[maybe_unused]] const Slot& slot) const + { + return {}; + } + VariableId Node::GetVariableIdRead(const Slot*) const { return {}; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h index a2d06686e9..42ffaec8da 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/Node.h @@ -715,6 +715,8 @@ namespace ScriptCanvas virtual PropertyFields GetPropertyFields() const; + virtual Grammar::MultipleFunctionCallFromSingleSlotInfo GetMultipleFunctionCallFromSingleSlotInfo(const Slot& slot) const; + virtual VariableId GetVariableIdRead(const Slot*) const; virtual VariableId GetVariableIdWritten(const Slot*) const; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/NodeableNode.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/NodeableNode.h index 0193641d97..c1c9db6f8b 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/NodeableNode.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/NodeableNode.h @@ -73,6 +73,8 @@ namespace ScriptCanvas void ConfigureSlots() override; + virtual void OnConfigureVisualExtensions() {} + AZ::Outcome GetBehaviorContextClass() const; ConstSlotsOutcome GetBehaviorContextOutName(const Slot& inSlot) const; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/PureData.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/PureData.h index 8426e90515..7b74f3dd81 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/PureData.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Core/PureData.h @@ -46,6 +46,8 @@ namespace ScriptCanvas const AZStd::unordered_map>& GetPropertyNameSlotMap() const; + AZ_INLINE AZ::Outcome GetDependencies() const override { return AZ::Success(DependencyReport{}); } + ~PureData() override; protected: diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp index 732455857e..db45f6bfe3 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp @@ -2249,6 +2249,10 @@ namespace ScriptCanvas } #endif AddAllVariablesPreParse(); + if (!IsErrorFree()) + { + return; + } for (auto& nodeEntity : m_source.m_graphData->m_nodes) { @@ -2270,6 +2274,16 @@ namespace ScriptCanvas { AddError(nullptr, ValidationConstPtr(aznew NullEntityInGraph())); } + + if (!IsErrorFree()) + { + return; + } + } + + if (!IsErrorFree()) + { + return; } ParseAutoConnectedEBusHandlerVariables(); @@ -2638,7 +2652,8 @@ namespace ScriptCanvas AZStd::vector inputVariableIds; AZStd::unordered_map inputVariablesById; - for (auto variable : GetVariables()) + auto& variables = GetVariables(); + for (auto variable : variables) { auto constructionRequirement = ParseConstructionRequirement(variable); @@ -2655,18 +2670,30 @@ namespace ScriptCanvas case VariableConstructionRequirement::InputNodeable: { + if (variable->m_datum.Empty()) + { + AddError(nullptr, aznew Internal::ParseError(AZ::EntityId{}, "Empty nodeable datum in variable, probably due to a problem with azrtti declarations")); + break; + } + // I solemnly swear no harm shall come to the nodeable const Nodeable* nodeableSource = reinterpret_cast(variable->m_datum.GetAsDanger()); - AZ_Assert(nodeableSource != nullptr, "the must be a raw nodeable held by this pointer"); - AZ_Assert(azrtti_typeid(nodeableSource) != azrtti_typeid(), "type problem with nodeable"); + + if (!nodeableSource) + { + AddError(nullptr, aznew Internal::ParseError(AZ::EntityId{}, "No raw nodeable held by variable")); + break; + } + nodeablesById.push_back({ variable->m_nodeableNodeId, const_cast(nodeableSource) }); } break; case VariableConstructionRequirement::InputVariable: { - inputVariableIds.push_back(variable->m_sourceVariableId); - inputVariablesById.insert({ variable->m_sourceVariableId, variable }); + auto variableID = variable->m_sourceVariableId.IsValid() ? variable->m_sourceVariableId : VariableId::MakeVariableId(); + inputVariableIds.push_back(variableID); + inputVariablesById.insert({ variableID, variable }); // sort revealed a datum copy issue: type is not preserved, workaround below // m_runtimeInputs.m_variables.emplace_back(variable->m_sourceVariableId, variable->m_datum); } @@ -4310,6 +4337,7 @@ namespace ScriptCanvas { if (auto variable = FindVariable(execution->GetNodeId())) { + execution->MarkInputHasThisPointer(); execution->AddInput({ nullptr, variable, DebugDataSource::FromInternal() }); } else @@ -4327,6 +4355,7 @@ namespace ScriptCanvas { auto variable = AZStd::make_shared(); variable->m_datum = Datum(eventHandling->m_handlerName); + execution->MarkInputHasThisPointer(); execution->AddInput({ nullptr, variable, DebugDataSource::FromInternal() }); } else @@ -4338,6 +4367,7 @@ namespace ScriptCanvas { if (auto variable = FindVariable(execution->GetNodeId())) { + execution->MarkInputHasThisPointer(); execution->AddInput({ nullptr, variable, DebugDataSource::FromInternal() }); } else @@ -4367,12 +4397,159 @@ namespace ScriptCanvas void AbstractCodeModel::ParseMultiExecutionPost(ExecutionTreePtr execution) { ParsePropertyExtractionsPost(execution); + ParseMultipleFunctionCallPost(execution); } void AbstractCodeModel::ParseMultiExecutionPre(ExecutionTreePtr execution) { ParsePropertyExtractionsPre(execution); - } + } + + void AbstractCodeModel::ParseMultipleFunctionCallPost(ExecutionTreePtr execution) + { + auto& id = execution->GetId(); + MultipleFunctionCallFromSingleSlotInfo info = id.m_node->GetMultipleFunctionCallFromSingleSlotInfo(*id.m_slot); + + if (info.functionCalls.empty()) + { + return; + } + + auto parent = execution->ModParent(); + + if (!parent) + { + AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), "Null parent in MultipleFunctionCall")); + return; + } + + size_t indexInParentCall = parent->FindChildIndex(execution); + if (indexInParentCall >= parent->GetChildrenCount()) + { + AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNoChildren)); + return; + } + + ExecutionChild* executionChildInParent = &parent->ModChild(indexInParentCall); + + const size_t executionInputCount = execution->GetInputCount(); + const size_t thisInputOffset = execution->InputHasThisPointer() ? 1 : 0; + + // the original index has ALL the input from the slots on the node + // create multiple calls with separate function call nodes, but ONLY take the inputs required + // as indicated by the function call info + + AZStd::unordered_set usedSlots; + bool variadicIsFound = false; + + auto createChild = [&](auto parentCall, ExecutionChild* childInParent, auto& functionCallInfo) + { + auto child = CreateChild(parentCall, id.m_node, id.m_slot); + child->SetSymbol(Symbol::FunctionCall); + child->SetName(functionCallInfo.functionName); + child->SetNameLexicalScope(functionCallInfo.lexicalScope); + childInParent->m_execution = child; + return child; + }; + + auto addThisInput = [&](auto functionCall) + { + if (thisInputOffset != 0) + { + if (executionInputCount == 0) + { + AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNotEnoughInputForThis)); + return; + } + + const ExecutionInput& input = execution->GetInput(0); + usedSlots.insert(input.m_slot); + functionCall->AddInput(input); + } + }; + + auto addSlotInput = [&](auto functionCall, size_t inputIndex) + { + if (inputIndex >= executionInputCount) + { + AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNotEnoughInput)); + return; + } + + const ExecutionInput& input = execution->GetInput(inputIndex); + + if (usedSlots.contains(input.m_slot)) + { + AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNotEnoughInput)); + return; + } + + usedSlots.insert(input.m_slot); + + if (input.m_value->m_source == execution) + { + input.m_value->m_source = functionCall; + } + + functionCall->AddInput(input); + }; + + auto addCall = [&](auto& functionCallInfo, auto childInParent, size_t startingIndex, size_t sentinel, size_t variadicOffset = 0) + { + auto child = createChild(parent, childInParent, functionCallInfo); + addThisInput(child); + + for (size_t index = startingIndex; index < sentinel; ++index) + { + const size_t inputIndex = index + thisInputOffset + variadicOffset; + addSlotInput(child, inputIndex); + } + + child->AddChild({}); + childInParent = &child->ModChild(0); + return AZStd::make_pair(childInParent, child); + }; + + // loop through each call... + for (auto& functionCallInfo : info.functionCalls) + { + // ...first add any pre-variadic calls, using the starting index and the number of args, since they could come in any order, not input slot order... + if (!functionCallInfo.isVariadic) + { + AZStd::pair childInParentAndParent = addCall(functionCallInfo, executionChildInParent, functionCallInfo.startingIndex, functionCallInfo.startingIndex + functionCallInfo.numArguments); + executionChildInParent = childInParentAndParent.first; + parent = childInParentAndParent.second; + } + else + { + // ...then add only one variadic call if there is one... + if (variadicIsFound) + { + AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotMultipleVariadic)); + return; + } + + variadicIsFound = true; + const size_t sentinel = executionInputCount == 0 ? 0 : executionInputCount - thisInputOffset; + // ... by looping through the remaining slots, striding by functionCallInfo.numArguments, making repeated calls to the function + for (size_t slotInputIndex = functionCallInfo.startingIndex; slotInputIndex < sentinel; slotInputIndex += functionCallInfo.numArguments) + { + AZStd::pair childInParentAndParent = addCall(functionCallInfo, executionChildInParent, 0, functionCallInfo.numArguments, slotInputIndex); + executionChildInParent = childInParentAndParent.first; + parent = childInParentAndParent.second; + } + } + } + + if (info.errorOnUnusedSlot && usedSlots.size() != executionInputCount) + { + AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotUnused)); + } + + // parent now refers to the last child call created + parent->SwapChildren(execution); + execution->Clear(); + } void AbstractCodeModel::ParseNodelingVariables(const Node& node, NodelingType nodelingType) { @@ -5146,6 +5323,6 @@ namespace ScriptCanvas return type == Data::eType::BehaviorContextObject; } - } + } -} +} diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.h index f1ad7b986b..8b8be27fab 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.h @@ -387,6 +387,8 @@ namespace ScriptCanvas void ParseMultiExecutionPre(ExecutionTreePtr execution); + void ParseMultipleFunctionCallPost(ExecutionTreePtr execution); + void ParseNodelingVariables(const Node& node, NodelingType nodelingType); void ParseOperatorArithmetic(ExecutionTreePtr execution); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.cpp index e16a3bc815..131209a521 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/ParsingUtilities.cpp @@ -1132,14 +1132,14 @@ namespace ScriptCanvas } else if (variable->m_isExposedToConstruction) { - if (variable->m_sourceVariableId.IsValid()) - { - return VariableConstructionRequirement::InputVariable; - } - else if (variable->m_nodeableNodeId.IsValid()) + if (variable->m_nodeableNodeId.IsValid()) { return VariableConstructionRequirement::InputNodeable; } + else if (variable->m_sourceVariableId.IsValid()) + { + return VariableConstructionRequirement::InputVariable; + } else { AZ_Assert(false, "A member variable in the model has no valid id"); diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/Primitives.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/Primitives.h index ab047fb580..d7e4fa0f72 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/Primitives.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/Primitives.h @@ -164,6 +164,34 @@ namespace ScriptCanvas virtual void PostParseExecutionTreeBody(AbstractCodeModel& /*model*/, ExecutionTreePtr /*execution*/) {} }; + // for now, no return values supported + struct MultipleFunctionCallFromSingleSlotEntry + { + AZ_TYPE_INFO(MultipleFunctionCallFromSingleSlotEntry, "{360A23A3-C490-4047-B71E-64E290E441D3}"); + AZ_CLASS_ALLOCATOR(MultipleFunctionCallFromSingleSlotEntry, AZ::SystemAllocator, 0); + + bool isVariadic = false; + AZStd::string functionName; + LexicalScope lexicalScope; + size_t numArguments = 0; // stride in case isVariadic == true + size_t startingIndex = 0; // the index of the slot order + }; + + // for now, no return values supported + struct MultipleFunctionCallFromSingleSlotInfo + { + AZ_TYPE_INFO(MultipleFunctionCallFromSingleSlotInfo, "{DF51F08A-8B28-4851-9888-9AB7CC0B90D2}"); + AZ_CLASS_ALLOCATOR(MultipleFunctionCallFromSingleSlotInfo, AZ::SystemAllocator, 0); + + // this could likely be implemented, but needs care to duplicate input that the execution-slot created + // bool errorOnReusedSlot = false; + + bool errorOnUnusedSlot = false; + + // calls are executed in the order they arrive in the vector + AZStd::vector functionCalls; + }; + struct NodeableParse : public AZStd::enable_shared_from_this { diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.cpp index ed61d7e838..66f609768d 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.cpp @@ -313,6 +313,11 @@ namespace ScriptCanvas return !m_returnValues.empty(); } + bool ExecutionTree::InputHasThisPointer() const + { + return m_inputHasThisPointer; + } + bool ExecutionTree::IsInfiniteLoopDetectionPoint() const { return m_isInfiniteLoopDetectionPoint; @@ -377,6 +382,11 @@ namespace ScriptCanvas m_isInfiniteLoopDetectionPoint = true; } + void ExecutionTree::MarkInputHasThisPointer() + { + m_inputHasThisPointer = true; + } + void ExecutionTree::MarkInputOutputPreprocessed() { m_isInputOutputPreprocessed = true; @@ -587,6 +597,32 @@ namespace ScriptCanvas m_symbol = val; } - } + void ExecutionTree::SwapChildren(ExecutionTreePtr execution) + { + if (execution) + { + m_children.swap(execution->m_children); + for (auto& child : m_children) + { + if (child.m_execution) + { + child.m_execution->SetParent(shared_from_this()); + } + } + + for (auto& orphan : execution->m_children) + { + if (orphan.m_execution) + { + orphan.m_execution->SetParent(execution); + } + } + } + else + { + ClearChildren(); + } + } + } } diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.h index 3d0d75b313..17b86c8da4 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/PrimitivesExecution.h @@ -190,6 +190,8 @@ namespace ScriptCanvas bool HasReturnValues() const; + bool InputHasThisPointer() const; + bool IsInfiniteLoopDetectionPoint() const; void InsertChild(size_t index, const ExecutionChild& child); @@ -208,6 +210,8 @@ namespace ScriptCanvas void MarkInfiniteLoopDetectionPoint(); + void MarkInputHasThisPointer(); + void MarkInputOutputPreprocessed(); void MarkInternalOut(); @@ -262,6 +266,8 @@ namespace ScriptCanvas void SetSymbol(Symbol val); + void SwapChildren(ExecutionTreePtr execution); + private: // the (possible) slot(s) through which execution exited, along with associated output AZStd::vector m_children; @@ -275,6 +281,8 @@ namespace ScriptCanvas bool m_isInfiniteLoopDetectionPoint = false; + bool m_inputHasThisPointer = false; + bool m_isInputOutputPreprocessed = false; bool m_isInternalOut = false; diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h index def4fa7761..68c6a8442a 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Results/ErrorText.h @@ -57,6 +57,12 @@ namespace ScriptCanvas constexpr const char* MissingVariableForEBusHandlerAddress = "missing variable for ebus handler address"; constexpr const char* MissingVariableForEBusHandlerAddressConnected = "missing variable for ebus handler address"; constexpr const char* MultipleExecutionOutConnections = "This node has multiple, unordered execution Out connections"; + constexpr const char* MultipleFunctionCallFromSingleSlotMultipleVariadic = "Only one variadic call (the last one) is supported in the multi-call per single slot."; + constexpr const char* MultipleFunctionCallFromSingleSlotNoChildren = "Node missing from parent children."; + constexpr const char* MultipleFunctionCallFromSingleSlotNotEnoughInput = "Not enough input to support multi call input information."; + constexpr const char* MultipleFunctionCallFromSingleSlotNotEnoughInputForThis = "Node doesn't have enough input for a parsed this pointer."; + constexpr const char* MultipleFunctionCallFromSingleSlotReused = "Multiple function slot reused an input slot"; + constexpr const char* MultipleFunctionCallFromSingleSlotUnused = "Multiple function slot left an input slot unused."; constexpr const char* MultipleSimulaneousInputValues = "Multiple values routed to the same single input with no way to discern which to take."; constexpr const char* MultipleStartNodes = "Multiple Start nodes in a single graph. Only one is allowed."; constexpr const char* NoChildrenAfterRoot = "No children after parsing function root"; diff --git a/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_RunAllTransformNodes.scriptcanvas b/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_RunAllTransformNodes.scriptcanvas index 45b24bad8d..c3b3599325 100644 --- a/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_RunAllTransformNodes.scriptcanvas +++ b/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_RunAllTransformNodes.scriptcanvas @@ -3,34 +3,34 @@ - + - + - + - + - + - + - + @@ -62,12 +62,13 @@ + - + @@ -99,12 +100,13 @@ + - + @@ -141,12 +143,13 @@ + - + @@ -178,6 +181,7 @@ + @@ -189,7 +193,7 @@ - + @@ -199,26 +203,26 @@ - + - + - + - + - + - + @@ -250,12 +254,13 @@ + - + @@ -287,12 +292,13 @@ + - + @@ -329,12 +335,646 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -371,12 +1011,13 @@ + - + @@ -408,6 +1049,7 @@ + @@ -419,7 +1061,7 @@ - + @@ -431,7 +1073,7 @@ - + @@ -441,26 +1083,26 @@ - + - + - + - + - + - + @@ -492,12 +1134,13 @@ + - + @@ -529,484 +1172,13 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -1043,12 +1215,13 @@ + - + @@ -1080,21 +1253,10 @@ + - - - - - - - - - - - - @@ -1103,7 +1265,7 @@ - + @@ -1113,26 +1275,26 @@ - + - + - + - + - + - + @@ -1164,12 +1326,13 @@ + - + @@ -1201,200 +1364,13 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -1431,12 +1407,56 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1446,10 +1466,10 @@ - + - + @@ -1468,6 +1488,7 @@ + @@ -1479,9 +1500,21 @@ - + - + + + + + + + + + + + + + @@ -1489,26 +1522,26 @@ - + - + - + - + - + - + @@ -1540,12 +1573,338 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1577,12 +1936,205 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1619,12 +2171,13 @@ + - + @@ -1639,7 +2192,7 @@ - + @@ -1661,12 +2214,13 @@ + - + @@ -1676,10 +2230,10 @@ - + - + @@ -1698,6 +2252,7 @@ + @@ -1709,19 +2264,19 @@ - + - + - + @@ -1731,26 +2286,26 @@ - + - + - + - + - + - + @@ -1782,12 +2337,13 @@ + - + @@ -1819,12 +2375,13 @@ + - + @@ -1839,7 +2396,7 @@ - + @@ -1861,12 +2418,56 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1898,6 +2499,7 @@ + @@ -1909,36 +2511,107 @@ - + + + + + + + + + + + + + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1970,12 +2643,13 @@ + - + @@ -2007,12 +2681,13 @@ + - + @@ -2049,12 +2724,205 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2091,12 +2959,13 @@ + - + @@ -2128,6 +2997,7 @@ + @@ -2139,7 +3009,7 @@ - + @@ -2151,7 +3021,7 @@ - + @@ -2161,26 +3031,26 @@ - + - + - + - + - + - + @@ -2212,12 +3082,13 @@ + - + @@ -2249,12 +3120,13 @@ + - + @@ -2291,12 +3163,56 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2328,6 +3244,7 @@ + @@ -2339,36 +3256,1196 @@ - + + + + + + + + + + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + @@ -2405,12 +4482,13 @@ + - + @@ -2447,12 +4525,13 @@ + - + @@ -2484,12 +4563,13 @@ + - + @@ -2521,6 +4601,7 @@ + @@ -2565,1709 +4646,26 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -4299,12 +4697,13 @@ + - + @@ -4336,12 +4735,13 @@ + - + @@ -4378,12 +4778,13 @@ + - + @@ -4420,12 +4821,13 @@ + - + @@ -4457,6 +4859,7 @@ + @@ -4480,7 +4883,7 @@ - + @@ -4490,26 +4893,26 @@ - + - + - + - + - + - + @@ -4541,12 +4944,13 @@ + - + @@ -4578,12 +4982,13 @@ + - + @@ -4620,850 +5025,13 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -5495,194 +5063,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -5694,9 +5075,9 @@ - + - + @@ -5704,703 +5085,35 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + @@ -6410,28 +5123,28 @@ - + - + - + - + - + - + - + @@ -6441,28 +5154,28 @@ - + - + - + - + - + - + - + @@ -6472,28 +5185,28 @@ - + - + - + - + - + - + - + @@ -6503,28 +5216,28 @@ - + - + - + - + - + - + - + @@ -6534,28 +5247,28 @@ - + - + - + - + - + - + - + @@ -6565,28 +5278,28 @@ - + - + - + - + - + - + - + @@ -6596,28 +5309,28 @@ - + - + - + - + - + - + - + @@ -6627,28 +5340,28 @@ - + - + - + - + - + - + - + @@ -6658,28 +5371,28 @@ - + - + - + - + - + - + - + @@ -6689,28 +5402,28 @@ - + - + - + - + - + - + @@ -6720,28 +5433,28 @@ - + - + - + - + - + - + - + @@ -6751,28 +5464,307 @@ - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6788,7 +5780,7 @@ - + @@ -6796,7 +5788,7 @@ - + @@ -6804,7 +5796,7 @@ - + @@ -6822,15 +5814,15 @@ - - - + + + - - - + + + @@ -6838,7 +5830,7 @@ - + @@ -6846,7 +5838,7 @@ - + @@ -6863,46 +5855,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -6911,18 +5867,12 @@ - - - - - - - + @@ -6930,637 +5880,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -7586,7 +5906,7 @@ - + @@ -7594,7 +5914,7 @@ - + @@ -7602,7 +5922,595 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -7620,15 +6528,15 @@ - - - + + + - - - + + + @@ -7636,7 +6544,7 @@ - + @@ -7644,7 +6552,7 @@ - + @@ -7661,46 +6569,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -7709,18 +6581,12 @@ - - - - - - - + @@ -7728,7 +6594,7 @@ - + @@ -7746,15 +6612,15 @@ - - - + + + - - - + + + @@ -7762,7 +6628,7 @@ - + @@ -7771,9 +6637,9 @@ - - - + + + @@ -7783,7 +6649,7 @@ - + @@ -7791,49 +6657,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -7851,15 +6675,15 @@ - - - + + + - - - + + + @@ -7867,7 +6691,7 @@ - + @@ -7875,7 +6699,49 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -7893,15 +6759,15 @@ - - - + + + - - - + + + @@ -7915,60 +6781,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -7980,7 +6798,35 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -7988,25 +6834,33 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + @@ -8014,7 +6868,7 @@ - + From 816d05ef2d85843c86fd47265cc8cf1a3b55c3c5 Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Thu, 3 Jun 2021 18:16:22 -0500 Subject: [PATCH 3/3] Updating manifest.py template query functions (#1113) * Updating manifest.py template query functions The get_project_templates, get_gem_templates and get_generic_templates methods have been renamed to indicate that the methods return the templates that can be used in a create-project, create-gem and create-from-template command of the engine_template.py Updated the print_registration.py script to support outputing project specific gems and templates. Add a unit test script for the manifest.py script. Added unit test to validate the new functions: `get_templates_for_project_creation` `get_templates_for_gem_creation` `get_templates_for_generic_creation` * Implementing the project print registration methods Added implementations of the project print registration methods and tested them locally Removed implementations of the download print registration methods, since they have not went through app-sec review. * Renaming get_restricted_data to get_restricted_json_data Fixed the get_registered method in manifest.py when looking up projects * Updated the print_manifest_json_data calls to return the result --- .../ProjectManager/Source/PythonBindings.cpp | 215 ++++---- scripts/o3de/o3de/manifest.py | 97 ++-- scripts/o3de/o3de/print_registration.py | 496 +++++++++--------- scripts/o3de/tests/CMakeLists.txt | 7 + scripts/o3de/tests/unit_test_manifest.py | 111 ++++ scripts/o3de/tests/unit_test_utils.py | 2 +- 6 files changed, 542 insertions(+), 386 deletions(-) create mode 100644 scripts/o3de/tests/unit_test_manifest.py diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 7062d886da..18b29c496f 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -294,7 +294,8 @@ namespace O3DE::ProjectManager RegisterThisEngine(); return result == 0 && !PyErr_Occurred(); - } catch ([[maybe_unused]] const std::exception& e) + } + catch ([[maybe_unused]] const std::exception& e) { AZ_Warning("ProjectManagerWindow", false, "Py_Initialize() failed with %s", e.what()); return false; @@ -320,25 +321,25 @@ namespace O3DE::ProjectManager bool registrationResult = true; // already registered is considered successful bool pythonResult = ExecuteWithLock( [&] + { + // check current engine path against all other registered engines + // to see if we are already registered + auto allEngines = m_manifest.attr("get_engines")(); + if (pybind11::isinstance(allEngines)) { - // check current engine path against all other registered engines - // to see if we are already registered - auto allEngines = m_manifest.attr("get_engines")(); - if (pybind11::isinstance(allEngines)) + for (auto engine : allEngines) { - for (auto engine : allEngines) + AZ::IO::FixedMaxPath enginePath(Py_To_String(engine["path"])); + if (enginePath.Compare(m_enginePath) == 0) { - AZ::IO::FixedMaxPath enginePath(Py_To_String(engine["path"])); - if (enginePath.Compare(m_enginePath) == 0) - { - return; - } + return; } } + } - auto result = m_register.attr("register")(m_enginePath.c_str()); - registrationResult = (result.cast() == 0); - }); + auto result = m_register.attr("register")(m_enginePath.c_str()); + registrationResult = (result.cast() == 0); + }); bool finalResult = (registrationResult && pythonResult); AZ_Assert(finalResult, "Registration of this engine failed!"); @@ -378,12 +379,12 @@ namespace O3DE::ProjectManager auto o3deData = m_manifest.attr("load_o3de_manifest")(); if (pybind11::isinstance(o3deData)) { - engineInfo.m_path = Py_To_String(enginePath); - engineInfo.m_defaultGemsFolder = Py_To_String(o3deData["default_gems_folder"]); - engineInfo.m_defaultProjectsFolder = Py_To_String(o3deData["default_projects_folder"]); + engineInfo.m_path = Py_To_String(enginePath); + engineInfo.m_defaultGemsFolder = Py_To_String(o3deData["default_gems_folder"]); + engineInfo.m_defaultProjectsFolder = Py_To_String(o3deData["default_projects_folder"]); engineInfo.m_defaultRestrictedFolder = Py_To_String(o3deData["default_restricted_folder"]); - engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]); - engineInfo.m_thirdPartyPath = Py_To_String_Optional(o3deData,"third_party_path",""); + engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]); + engineInfo.m_thirdPartyPath = Py_To_String_Optional(o3deData, "third_party_path", ""); } auto engineData = m_manifest.attr("get_engine_json_data")(pybind11::none(), enginePath); @@ -391,8 +392,8 @@ namespace O3DE::ProjectManager { try { - engineInfo.m_version = Py_To_String_Optional(engineData,"O3DEVersion","0.0.0.0"); - engineInfo.m_name = Py_To_String_Optional(engineData,"engine_name","O3DE"); + engineInfo.m_version = Py_To_String_Optional(engineData, "O3DEVersion", "0.0.0.0"); + engineInfo.m_name = Py_To_String_Optional(engineData, "engine_name", "O3DE"); } catch ([[maybe_unused]] const std::exception& e) { @@ -416,19 +417,19 @@ namespace O3DE::ProjectManager bool PythonBindings::SetEngineInfo(const EngineInfo& engineInfo) { bool result = ExecuteWithLock([&] { - pybind11::str enginePath = engineInfo.m_path.toStdString(); - pybind11::str defaultProjectsFolder = engineInfo.m_defaultProjectsFolder.toStdString(); - pybind11::str defaultGemsFolder = engineInfo.m_defaultGemsFolder.toStdString(); + pybind11::str enginePath = engineInfo.m_path.toStdString(); + pybind11::str defaultProjectsFolder = engineInfo.m_defaultProjectsFolder.toStdString(); + pybind11::str defaultGemsFolder = engineInfo.m_defaultGemsFolder.toStdString(); pybind11::str defaultTemplatesFolder = engineInfo.m_defaultTemplatesFolder.toStdString(); auto registrationResult = m_register.attr("register")( - enginePath, // engine_path - pybind11::none(), // project_path + enginePath, // engine_path + pybind11::none(), // project_path pybind11::none(), // gem_path - pybind11::none(), // external_subdir_path - pybind11::none(), // template_path - pybind11::none(), // restricted_path - pybind11::none(), // repo_uri + pybind11::none(), // external_subdir_path + pybind11::none(), // template_path + pybind11::none(), // restricted_path + pybind11::none(), // repo_uri pybind11::none(), // default_engines_folder defaultProjectsFolder, defaultGemsFolder, @@ -477,12 +478,12 @@ namespace O3DE::ProjectManager QVector gems; auto result = ExecuteWithLockErrorHandling([&] + { + for (auto path : m_manifest.attr("get_engine_gems")()) { - for (auto path : m_manifest.attr("get_engine_gems")()) - { - gems.push_back(GemInfoFromPath(path)); - } - }); + gems.push_back(GemInfoFromPath(path)); + } + }); if (!result.IsSuccess()) { return AZ::Failure(result.GetError().c_str()); @@ -497,13 +498,13 @@ namespace O3DE::ProjectManager QVector gems; auto result = ExecuteWithLockErrorHandling([&] + { + pybind11::str pyProjectPath = projectPath.toStdString(); + for (auto path : m_manifest.attr("get_all_gems")(pyProjectPath)) { - pybind11::str pyProjectPath = projectPath.toStdString(); - for (auto path : m_manifest.attr("get_all_gems")(pyProjectPath)) - { - gems.push_back(GemInfoFromPath(path)); - } - }); + gems.push_back(GemInfoFromPath(path)); + } + }); if (!result.IsSuccess()) { return AZ::Failure(result.GetError().c_str()); @@ -518,12 +519,12 @@ namespace O3DE::ProjectManager // Retrieve the path to the cmake file that lists the enabled gems. pybind11::str enabledGemsFilename; auto result = ExecuteWithLockErrorHandling([&] - { - const pybind11::str pyProjectPath = projectPath.toStdString(); - enabledGemsFilename = m_cmake.attr("get_enabled_gem_cmake_file")( - pybind11::none(), // project_name - pyProjectPath); // project_path - }); + { + const pybind11::str pyProjectPath = projectPath.toStdString(); + enabledGemsFilename = m_cmake.attr("get_enabled_gem_cmake_file")( + pybind11::none(), // project_name + pyProjectPath); // project_path + }); if (!result.IsSuccess()) { return AZ::Failure(result.GetError().c_str()); @@ -532,13 +533,13 @@ namespace O3DE::ProjectManager // Retrieve the actual list of names from the cmake file. QVector gemNames; result = ExecuteWithLockErrorHandling([&] + { + const auto pyGemNames = m_cmake.attr("get_enabled_gems")(enabledGemsFilename); + for (auto gemName : pyGemNames) { - const auto pyGemNames = m_cmake.attr("get_enabled_gems")(enabledGemsFilename); - for (auto gemName : pyGemNames) - { - gemNames.push_back(Py_To_String(gemName)); - } - }); + gemNames.push_back(Py_To_String(gemName)); + } + }); if (!result.IsSuccess()) { return AZ::Failure(result.GetError().c_str()); @@ -552,13 +553,13 @@ namespace O3DE::ProjectManager bool registrationResult = false; bool result = ExecuteWithLock( [&] - { - pybind11::str projectPath = path.toStdString(); - auto pythonRegistrationResult = m_register.attr("register")(pybind11::none(), projectPath); + { + pybind11::str projectPath = path.toStdString(); + auto pythonRegistrationResult = m_register.attr("register")(pybind11::none(), projectPath); - // Returns an exit code so boolify it then invert result - registrationResult = !pythonRegistrationResult.cast(); - }); + // Returns an exit code so boolify it then invert result + registrationResult = !pythonRegistrationResult.cast(); + }); return result && registrationResult; } @@ -568,30 +569,30 @@ namespace O3DE::ProjectManager bool registrationResult = false; bool result = ExecuteWithLock( [&] - { - pybind11::str projectPath = path.toStdString(); - auto pythonRegistrationResult = m_register.attr("register")( - pybind11::none(), // engine_path - projectPath, // project_path - pybind11::none(), // gem_path - pybind11::none(), // external_subdir_path - pybind11::none(), // template_path - pybind11::none(), // restricted_path - pybind11::none(), // repo_uri - pybind11::none(), // default_engines_folder - pybind11::none(), // default_projects_folder - pybind11::none(), // default_gems_folder - pybind11::none(), // default_templates_folder - pybind11::none(), // default_restricted_folder - pybind11::none(), // external_subdir_engine_path - pybind11::none(), // external_subdir_project_path - true, // remove - false // force + { + pybind11::str projectPath = path.toStdString(); + auto pythonRegistrationResult = m_register.attr("register")( + pybind11::none(), // engine_path + projectPath, // project_path + pybind11::none(), // gem_path + pybind11::none(), // external_subdir_path + pybind11::none(), // template_path + pybind11::none(), // restricted_path + pybind11::none(), // repo_uri + pybind11::none(), // default_engines_folder + pybind11::none(), // default_projects_folder + pybind11::none(), // default_gems_folder + pybind11::none(), // default_templates_folder + pybind11::none(), // default_restricted_folder + pybind11::none(), // external_subdir_engine_path + pybind11::none(), // external_subdir_project_path + true, // remove + false // force ); - - // Returns an exit code so boolify it then invert result - registrationResult = !pythonRegistrationResult.cast(); - }); + + // Returns an exit code so boolify it then invert result + registrationResult = !pythonRegistrationResult.cast(); + }); return result && registrationResult; } @@ -649,12 +650,12 @@ namespace O3DE::ProjectManager try { // required - gemInfo.m_name = Py_To_String(data["gem_name"]); + gemInfo.m_name = Py_To_String(data["gem_name"]); // optional gemInfo.m_displayName = Py_To_String_Optional(data, "DisplayName", gemInfo.m_name); - gemInfo.m_summary = Py_To_String_Optional(data, "Summary", ""); - gemInfo.m_version = Py_To_String_Optional(data, "Version", ""); + gemInfo.m_summary = Py_To_String_Optional(data, "Summary", ""); + gemInfo.m_version = Py_To_String_Optional(data, "Version", ""); if (data.contains("Tags")) { @@ -685,7 +686,7 @@ namespace O3DE::ProjectManager try { projectInfo.m_projectName = Py_To_String(projectData["project_name"]); - projectInfo.m_displayName = Py_To_String_Optional(projectData,"display_name", projectInfo.m_projectName); + projectInfo.m_displayName = Py_To_String_Optional(projectData, "display_name", projectInfo.m_projectName); } catch ([[maybe_unused]] const std::exception& e) { @@ -727,33 +728,33 @@ namespace O3DE::ProjectManager AZ::Outcome PythonBindings::AddGemToProject(const QString& gemPath, const QString& projectPath) { return ExecuteWithLockErrorHandling([&] - { - pybind11::str pyGemPath = gemPath.toStdString(); - pybind11::str pyProjectPath = projectPath.toStdString(); + { + pybind11::str pyGemPath = gemPath.toStdString(); + pybind11::str pyProjectPath = projectPath.toStdString(); - m_enableGemProject.attr("enable_gem_in_project")( - pybind11::none(), // gem name not needed as path is provided - pyGemPath, - pybind11::none(), // project name not needed as path is provided - pyProjectPath + m_enableGemProject.attr("enable_gem_in_project")( + pybind11::none(), // gem name not needed as path is provided + pyGemPath, + pybind11::none(), // project name not needed as path is provided + pyProjectPath ); - }); + }); } AZ::Outcome PythonBindings::RemoveGemFromProject(const QString& gemPath, const QString& projectPath) { return ExecuteWithLockErrorHandling([&] - { - pybind11::str pyGemPath = gemPath.toStdString(); - pybind11::str pyProjectPath = projectPath.toStdString(); + { + pybind11::str pyGemPath = gemPath.toStdString(); + pybind11::str pyProjectPath = projectPath.toStdString(); - m_disableGemProject.attr("disable_gem_in_project")( - pybind11::none(), // gem name not needed as path is provided - pyGemPath, - pybind11::none(), // project name not needed as path is provided - pyProjectPath + m_disableGemProject.attr("disable_gem_in_project")( + pybind11::none(), // gem name not needed as path is provided + pyGemPath, + pybind11::none(), // project name not needed as path is provided + pyProjectPath ); - }); + }); } bool PythonBindings::UpdateProject([[maybe_unused]] const ProjectInfo& projectInfo) @@ -773,8 +774,8 @@ namespace O3DE::ProjectManager { // required templateInfo.m_displayName = Py_To_String(data["display_name"]); - templateInfo.m_name = Py_To_String(data["template_name"]); - templateInfo.m_summary = Py_To_String(data["summary"]); + templateInfo.m_name = Py_To_String(data["template_name"]); + templateInfo.m_summary = Py_To_String(data["summary"]); // optional if (data.contains("canonical_tags")) @@ -806,7 +807,7 @@ namespace O3DE::ProjectManager QVector templates; bool result = ExecuteWithLock([&] { - for (auto path : m_manifest.attr("get_project_templates")()) + for (auto path : m_manifest.attr("get_templates_for_project_creation")()) { templates.push_back(ProjectTemplateInfoFromPath(path)); } diff --git a/scripts/o3de/o3de/manifest.py b/scripts/o3de/o3de/manifest.py index edcd44c525..bcb331d9f4 100644 --- a/scripts/o3de/o3de/manifest.py +++ b/scripts/o3de/o3de/manifest.py @@ -302,64 +302,95 @@ def get_project_external_subdirectories(project_path: pathlib.Path) -> list: project_object['external_subdirectories'])) if 'external_subdirectories' in project_object else [] +def get_project_templates(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['templates'])) + + +def get_project_restricted(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['restricted'])) if 'restricted' 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) + projects_data = get_projects() + projects_data.extend(get_engine_projects()) + # Remove duplicates from the list + return list(dict.fromkeys(projects_data)) def get_all_gems(project_path: pathlib.Path = None) -> list: - gems_data = set(get_gems()) - gems_data.update(get_engine_gems()) + gems_data = get_gems() + gems_data.extend(get_engine_gems()) if project_path: - gems_data.update(get_project_gems(project_path)) - return list(gems_data) + gems_data.extend(get_project_gems(project_path)) + return list(dict.fromkeys(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()) + external_subdirectories_data = get_external_subdirectories() + external_subdirectories_data.extend(get_engine_external_subdirectories()) if project_path: - external_subdirectories_data.update(get_project_external_subdirectories(project_path)) - return list(templates_data) + external_subdirectories_data.extend(get_project_external_subdirectories(project_path)) + return list(dict.fromkeys(external_subdirectories_data)) -def get_all_templates() -> list: - templates_data = set(get_templates()) - templates_data.update(get_engine_templates()) - return list(templates_data) +def get_all_templates(project_path: pathlib.Path = None) -> list: + templates_data = get_templates() + templates_data.extend(get_engine_templates()) + if project_path: + templates_data.extend(get_project_templates(project_path)) + return list(dict.fromkeys(templates_data)) def get_all_restricted() -> list: - restricted_data = set(get_restricted()) - restricted_data.update(get_engine_restricted()) - return list(gems_data) + restricted_data = get_restricted() + restricted_data.extend(get_engine_restricted()) + if project_path: + restricted_data.extend(get_project_restricted(project_path)) + return list(dict.fromkeys(restricted_data)) # Template functions -def get_project_templates(): # temporary until we have a better way to do this... maybe template_type element +def get_templates_for_project_creation(): project_templates = [] - for template in get_all_templates(): - if 'Project' in template: - project_templates.append(template) + for template_path in get_all_templates(): + template_path = pathlib.Path(template_path) + template_json_path = pathlib.Path(template_path) / 'template.json' + if not validation.valid_o3de_template_json(template_json_path): + continue + + project_json_path = template_path / 'Template' / 'project.json' + if validation.valid_o3de_project_json(project_json_path): + project_templates.append(template_path) return project_templates -def get_gem_templates(): # temporary until we have a better way to do this... maybe template_type element +def get_templates_for_gem_creation(): gem_templates = [] - for template in get_all_templates(): - if 'Gem' in template: - gem_templates.append(template) + for template_path in get_all_templates(): + template_path = pathlib.Path(template_path) + template_json_path = pathlib.Path(template_path) / 'template.json' + if not validation.valid_o3de_template_json(template_json_path): + continue + + gem_json_path = template_path / 'Template' / 'gem.json' + if validation.valid_o3de_gem_json(gem_json_path): + gem_templates.append(template_path) 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_templates_for_generic_creation(): # temporary until we have a better way to do this... maybe template_type element + def filter_project_and_gem_templates_out(template_path, + templates_for_project_creation = get_templates_for_project_creation(), + templates_for_gem_creation = get_templates_for_gem_creation()): + template_path = pathlib.Path(template_path) + return template_path not in templates_for_project_creation and template_path not in templates_for_gem_creation + + return list(filter(filter_project_and_gem_templates_out, get_all_templates())) def get_all_restricted() -> list: @@ -515,7 +546,7 @@ def get_template_json_data(template_name: str = None, return None -def get_restricted_data(restricted_name: str = None, +def get_restricted_json_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.') diff --git a/scripts/o3de/o3de/print_registration.py b/scripts/o3de/o3de/print_registration.py index 292f2224bc..e5c7a1afc0 100644 --- a/scripts/o3de/o3de/print_registration.py +++ b/scripts/o3de/o3de/print_registration.py @@ -13,6 +13,7 @@ import argparse import json import hashlib import logging +import pathlib import sys import urllib.parse @@ -21,219 +22,251 @@ from o3de import manifest, validation logger = logging.getLogger() logging.basicConfig() -def print_this_engine(verbose: int) -> None: + +def get_project_path(project_path: pathlib.Path, project_name: str) -> pathlib.Path: + if not project_name and not project_path: + logger.error(f'Must either specify a Project path or Project Name.') + return None + + if 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 None + + if not project_path.is_dir(): + logger.error(f'Project path {project_path} is not a folder.') + return None + + return project_path + + +def print_this_engine(verbose: int) -> int: engine_data = manifest.get_this_engine() print(json.dumps(engine_data, indent=4)) + result = True if verbose > 0: - print_engines_data(engine_data) + result = print_manifest_json_data(engine_data, 'engine.json', 'This Engine', + manifest.get_engine_json_data, 'engine_path') + return 0 if result else 1 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) + return print_manifest_json_data(engines_data, 'engine.json', 'Engines', + manifest.get_engine_json_data, 'engine_path') + return 0 -def print_projects(verbose: int) -> None: +def print_projects(verbose: int) -> int: projects_data = manifest.get_projects() print(json.dumps(projects_data, indent=4)) + if verbose > 0: - print_projects_data(projects_data) + return print_manifest_json_data(projects_data, 'project.json', 'Projects', + manifest.get_project_json_data, 'project_path') + return 0 -def print_gems(verbose: int) -> None: +def print_gems(verbose: int) -> int: gems_data = manifest.get_gems() print(json.dumps(gems_data, indent=4)) + if verbose > 0: - print_gems_data(gems_data) + return print_manifest_json_data(gems_data, 'gem.json', 'Gems', + manifest.get_gem_json_data, 'gem_path') + return 0 -def print_templates(verbose: int) -> None: +def print_templates(verbose: int) -> int: templates_data = manifest.get_templates() print(json.dumps(templates_data, indent=4)) + if verbose > 0: - print_templates_data(templates_data) + return print_manifest_json_data(templates_data, 'template.json', 'Templates', + manifest.get_template_json_data, 'template_path') + return 0 -def print_restricted(verbose: int) -> None: +def print_restricted(verbose: int) -> int: 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: + if verbose > 0: + return print_manifest_json_data(restricted_data, 'restricted.json', 'Restricted', + manifest.get_restricted_json_data, 'restricted_path') + return 0 + + +# Engine output methods +def print_engine_projects(verbose: int) -> int: engine_projects_data = manifest.get_engine_projects() print(json.dumps(engine_projects_data, indent=4)) + if verbose > 0: - print_projects_data(engine_projects_data) + return print_manifest_json_data(engine_projects_data, 'project.json', 'Projects', + manifest.get_project_json_data, 'project_path') + return 0 -def print_engine_gems(verbose: int) -> None: +def print_engine_gems(verbose: int) -> int: engine_gems_data = manifest.get_engine_gems() print(json.dumps(engine_gems_data, indent=4)) + if verbose > 0: - print_gems_data(engine_gems_data) + return print_manifest_json_data(engine_gems_data, 'gem.json', 'Gems', + manifest.get_gem_json_data, 'gem_path') + return 0 -def print_engine_templates(verbose: int) -> None: +def print_engine_templates(verbose: int) -> int: engine_templates_data = manifest.get_engine_templates() print(json.dumps(engine_templates_data, indent=4)) + if verbose > 0: - print_templates_data(engine_templates_data) + return print_manifest_json_data(engine_templates_data, 'template.json', 'Templates', + manifest.get_template_json_data, 'template_path') + return 0 -def print_engine_restricted(verbose: int) -> None: +def print_engine_restricted(verbose: int) -> int: engine_restricted_data = manifest.get_engine_restricted() print(json.dumps(engine_restricted_data, indent=4)) + if verbose > 0: - print_restricted_data(engine_restricted_data) + return print_manifest_json_data(engine_restricted_data, 'restricted.json', 'Restricted', + manifest.get_restricted_json_data, 'restricted_path') + return 0 -def print_engine_external_subdirectories(verbose: int) -> None: +def print_engine_external_subdirectories() -> int: external_subdirs_data = manifest.get_engine_external_subdirectories() print(json.dumps(external_subdirs_data, indent=4)) + return 0 -def print_all_projects(verbose: int) -> None: +# Project output methods +def print_project_gems(verbose: int, project_path: pathlib.Path, project_name: str) -> int: + project_path = get_project_path(project_path, project_name) + if not project_path: + return 1 + + project_gems_data = manifest.get_project_gems(project_path) + print(json.dumps(project_gems_data, indent=4)) + + if verbose > 0: + return print_manifest_json_data(project_gems_data, 'gem.json', 'Gems', + manifest.get_gem_json_data, 'gem_path') + return 0 + + +def print_project_external_subdirectories(project_path: pathlib.Path, project_name: str) -> int: + project_path = get_project_path(project_path, project_name) + if not project_path: + return 1 + + external_subdirs_data = manifest.get_project_external_subdirectories(project_path) + print(json.dumps(external_subdirs_data, indent=4)) + return 0 + + +def print_project_templates(verbose: int, project_path: pathlib.Path, project_name: str) -> int: + project_path = get_project_path(project_path, project_name) + if not project_path: + return 1 + + project_templates_data = manifest.get_project_templates(project_path) + print(json.dumps(project_templates_data, indent=4)) + if verbose > 0: + return print_manifest_json_data(project_templates_data, 'template.json', 'Templates', + manifest.get_template_json_data, 'template_path') + return 0 + + +def print_project_restricted(verbose: int, project_path: pathlib.Path, project_name: str) -> int: + project_path = get_project_path(project_path, project_name) + if not project_path: + return 1 + + project_restricted_data = manifest.get_project_restricted(project_path) + print(json.dumps(project_restricted_data, indent=4)) + if verbose > 0: + return print_manifest_json_data(project_restricted_data, 'restricted.json', 'Restricted', + manifest.get_restricted_json_data, 'restricted_path') + return 0 + + +def print_all_projects(verbose: int) -> int: all_projects_data = manifest.get_all_projects() print(json.dumps(all_projects_data, indent=4)) + if verbose > 0: - print_projects_data(all_projects_data) + return print_manifest_json_data(all_projects_data, 'project.json', 'Projects', + manifest.get_project_json_data, 'project_path') + return 0 -def print_all_gems(verbose: int) -> None: +def print_all_gems(verbose: int) -> int: all_gems_data = manifest.get_all_gems() print(json.dumps(all_gems_data, indent=4)) + if verbose > 0: - print_gems_data(all_gems_data) + return print_manifest_json_data(all_gems_data, 'gem.json', 'Gems', + manifest.get_gem_json_data, 'gem_path') + return 0 -def print_all_templates(verbose: int) -> None: +def print_all_external_subdirectories() -> int: + all_external_subdirectories_data = manifest.get_all_external_subdirectories() + print(json.dumps(all_external_subdirectories_data, indent=4)) + return 0 + +def print_all_templates(verbose: int) -> int: all_templates_data = manifest.get_all_templates() print(json.dumps(all_templates_data, indent=4)) + if verbose > 0: - print_templates_data(all_templates_data) + return print_manifest_json_data(all_templates_data, 'template.json', 'Templates', + manifest.get_template_json_data, 'template_path') + return 0 -def print_all_restricted(verbose: int) -> None: +def print_all_restricted(verbose: int) -> int: all_restricted_data = manifest.get_all_restricted() print(json.dumps(all_restricted_data, indent=4)) + if verbose > 0: - print_restricted_data(all_restricted_data) + return print_manifest_json_data(all_restricted_data, 'restricted.json', 'Restricted', + manifest.get_restricted_json_data, 'restricted_path') + return 0 -def print_engines_data(engines_data: dict) -> None: +def print_manifest_json_data(uri_json_data: dict, json_filename: str, + print_prefix: str, get_json_func: callable, get_json_data_kw: str) -> int: print('\n') - print("Engines================================================") - for engine_object in engines_data: + print(f"{print_prefix}================================================") + for manifest_uri in uri_json_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()) + parsed_uri = urllib.parse.urlparse(manifest_uri) + if parsed_uri.scheme in ['http', 'https', 'ftp', 'ftps']: + repo_sha256 = hashlib.sha256(manifest_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:') + manifest_json_path = cache_folder / str(repo_sha256.hexdigest() + '.json') else: - engine_json = pathlib.Path(engine_uri).resolve() / 'engine.json' + manifest_json_path = pathlib.Path(manifest_uri).resolve() / json_filename - 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') + json_data = get_json_func(**{get_json_data_kwargs: manifest_json_path}) + if json_data: + print(manifest_json_path) + print(json.dumps(json_data, indent=4) + '\n') + return 0 -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: +def print_repos_data(repos_data: dict) -> int: print('\n') print("Repos================================================") cache_folder = manifest.get_o3de_cache_folder() @@ -251,29 +284,16 @@ def print_repos_data(repos_data: dict) -> None: 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') + return 0 def register_show_repos(verbose: int) -> None: - repos_data = get_repos() + repos_data = manifest.get_repos() print(json.dumps(repos_data, indent=4)) + if verbose > 0: - print_repos_data(repos_data) + return print_repos_data(repos_data) == 0 + return 0 def register_show(verbose: int) -> None: @@ -281,13 +301,15 @@ def register_show(verbose: int) -> None: print(f"{manifest.get_o3de_manifest()}:") print(json.dumps(json_data, indent=4)) + result = True 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()) + result = print_manifest_json_data(manifest.get_engines()) == 0 and result + result = print_manifest_json_data(manifest.get_all_projects()) == 0 and result + result = print_manifest_json_data(manifest.get_gems()) == 0 and result + result = print_manifest_json_data(manifest.get_all_templates()) == 0 and result + result = print_manifest_json_data(manifest.get_all_restricted()) == 0 and result + result = print_repos_data(manifest.get_repos()) == 0 and result + return 0 if result else 1 def _run_register_show(args: argparse) -> int: @@ -295,75 +317,53 @@ def _run_register_show(args: argparse) -> int: manifest.override_home_folder = args.override_home_folder if args.this_engine: - print_this_engine(args.verbose) - return 0 - + return print_this_engine(args.verbose) elif args.engines: - print_engines(args.verbose) - return 0 + return print_engines(args.verbose) elif args.projects: - print_projects(args.verbose) - return 0 + return print_projects(args.verbose) elif args.gems: - print_gems(args.verbose) - return 0 + return print_gems(args.verbose) elif args.templates: - print_templates(args.verbose) - return 0 + return print_templates(args.verbose) elif args.repos: - register_show_repos(args.verbose) - return 0 + return register_show_repos(args.verbose) elif args.restricted: - print_restricted(args.verbose) - return 0 + return print_restricted(args.verbose) elif args.engine_projects: - print_engine_projects(args.verbose) - return 0 + return print_engine_projects(args.verbose) 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 + return print_engine_gems(args.verbose) elif args.engine_external_subdirectories: - print_engine_external_subdirectories(args.verbose) - return 0 + return print_engine_external_subdirectories() + elif args.engine_templates: + return print_engine_templates(args.verbose) + elif args.engine_restricted: + return print_engine_restricted(args.verbose) + + elif args.project_gems: + return print_project_gems(args.verbose, args.project_path, args.project_name) + elif args.project_external_subdirectories: + return print_project_external_subdirectories(args.project_path, args.project_name) + elif args.project_templates: + return print_project_templates(args.verbose, args.project_path, args.project_name) + elif args.project_restricted: + return print_project_restricted(args.verbose, args.project_path, args.project_name) elif args.all_projects: - print_all_projects(args.verbose) - return 0 + return print_all_projects(args.verbose) elif args.all_gems: - print_all_gems(args.verbose) - return 0 + return print_all_gems(args.verbose) + elif args.all_external_subdirectories: + return print_all_external_subdirectories() elif args.all_templates: - print_all_templates(args.verbose) - return 0 + return print_all_templates(args.verbose) elif args.all_restricted: - print_all_restricted(args.verbose) - return 0 + return print_all_restricted(args.verbose) - 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 + return register_show(args.verbose) def add_parser_args(parser): @@ -376,78 +376,84 @@ def add_parser_args(parser): 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.') + help='Output the current engine path.') group.add_argument('-e', '--engines', action='store_true', required=False, default=False, - help='Just the local engines.') + help='Output the engines registered in the global ~/.o3de/o3de_manifest.json.') group.add_argument('-p', '--projects', action='store_true', required=False, default=False, - help='Just the local projects.') + help='Output the projects registered in the global ~/.o3de/o3de_manifest.json.') group.add_argument('-g', '--gems', action='store_true', required=False, default=False, - help='Just the local gems.') + help='Output the gems registered in the global ~/.o3de/o3de_manifest.json.') group.add_argument('-t', '--templates', action='store_true', required=False, default=False, - help='Just the local templates.') + help='Output the templates registered in the global ~/.o3de/o3de_manifest.json.') group.add_argument('-r', '--repos', action='store_true', required=False, default=False, - help='Just the local repos. Ignores repos.') + help='Output the repos registered in the global ~/.o3de/o3de_manifest.json. Ignores repos.') group.add_argument('-rs', '--restricted', action='store_true', required=False, default=False, - help='The local restricted folders.') + help='Output the restricted directories registered in the global ~/.o3de/o3de_manifest.json.') group.add_argument('-ep', '--engine-projects', action='store_true', required=False, default=False, - help='Just the local projects. Ignores repos.') + help='Output the projects registered in the current engine engine.json. Ignores repos.') group.add_argument('-eg', '--engine-gems', action='store_true', required=False, default=False, - help='Just the local gems. Ignores repos') + help='Output the gems registered in the current engine engine.json. Ignores repos') group.add_argument('-et', '--engine-templates', action='store_true', required=False, default=False, - help='Just the local templates. Ignores repos.') + help='Output the templates registered in the current engine engine.json. 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, + help='Output the restricted directories registered in the current engine engine.json.') + group.add_argument('-ees', '--engine-external-subdirectories', action='store_true', required=False, default=False, - help='The external subdirectories.') + help='Output the external subdirectories registered in the current engine engine.json.') + + group.add_argument('-pg', '--project-gems', action='store_true', + default=False, + help='Returns the gems registered with the project.json.') + group.add_argument('-pt', '--project-templates', action='store_true', + default=False, + help='Returns the templates registered with the project.json.') + group.add_argument('-prs', '--project-restricted', action='store_true', + default=False, + help='Returns the restricted directories registered with the project.json.') + group.add_argument('-pes', '--project-external-subdirectories', action='store_true', + default=False, + help='Returns the external subdirectories register with the project.json.') group.add_argument('-ap', '--all-projects', action='store_true', required=False, default=False, - help='Just the local projects. Ignores repos.') + help='Output all projects registered in the ~/.o3de/o3de_manifest.json and the current engine.json. Ignores repos.') group.add_argument('-ag', '--all-gems', action='store_true', required=False, default=False, - help='Just the local gems. Ignores repos') + help='Output all gems registered in the ~/.o3de/o3de_manifest.json and the current engine.json. 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, + help='Output all templates registered in the ~/.o3de/o3de_manifest.json and the current engine.json. Ignores repos.') + group.add_argument('-ares', '--all-restricted', action='store_true', required=False, default=False, - help='The restricted folders.') - - group.add_argument('-d', '--downloadables', action='store_true', required=False, + help='Output all restricted directory registered in the ~/.o3de/o3de_manifest.json and the current engine.json.') + group.add_argument('-aes', '--all-external-subdirectories', action='store_true', 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.') + help='Output all external subdirectories registered in the ~/.o3de/o3de_manifest.json and the current engine.json.') parser.add_argument('-v', '--verbose', action='count', required=False, - default=0, - help='How verbose do you want the output to be.') + default=0, + help='How verbose do you want the output to be.') + + project_group = parser.add_mutually_exclusive_group(required=False) + project_group.add_argument('-pp', '--project-path', type=pathlib.Path, + help='The path to a project.') + project_group.add_argument('-pn', '--project-name', type=str, + help='The name of a project.') 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.') + help='By default the home folder is the user folder, override it to this folder.') parser.set_defaults(func=_run_register_show) diff --git a/scripts/o3de/tests/CMakeLists.txt b/scripts/o3de/tests/CMakeLists.txt index 0526c7740d..82b994e387 100644 --- a/scripts/o3de/tests/CMakeLists.txt +++ b/scripts/o3de/tests/CMakeLists.txt @@ -34,3 +34,10 @@ ly_add_pytest( TEST_SUITE smoke EXCLUDE_TEST_RUN_TARGET_FROM_IDE ) + +ly_add_pytest( + NAME o3de_manifest + PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_manifest.py + TEST_SUITE smoke + EXCLUDE_TEST_RUN_TARGET_FROM_IDE +) diff --git a/scripts/o3de/tests/unit_test_manifest.py b/scripts/o3de/tests/unit_test_manifest.py new file mode 100644 index 0000000000..98430c227b --- /dev/null +++ b/scripts/o3de/tests/unit_test_manifest.py @@ -0,0 +1,111 @@ +# +# 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 manifest + + +@pytest.mark.parametrize("valid_project_json_paths, valid_gem_json_paths", [ + pytest.param([pathlib.Path('D:/o3de/Templates/DefaultProject/Template/project.json')], + [pathlib.Path('D:/o3de/Templates/DefaultGem/Template/gem.json')]) +]) +class TestGetTemplatesForCreation: + @staticmethod + def get_templates() -> list: + return [] + + @staticmethod + def get_project_templates() -> list: + return [] + + @staticmethod + def get_engine_templates() -> list: + return [pathlib.Path('D:/o3de/Templates/DefaultProject'), pathlib.Path('D:/o3de/Templates/DefaultGem')] + + + @pytest.mark.parametrize("expected_template_paths", [ + pytest.param([]) + ] + ) + def test_get_templates_for_generic_creation(self, valid_project_json_paths, valid_gem_json_paths, + expected_template_paths): + def validate_project_json(template_path) -> bool: + return pathlib.Path(template_path) in valid_project_json_paths + + def validate_gem_json(template_path) -> bool: + return pathlib.Path(template_path) in valid_gem_json_paths + + with patch('o3de.manifest.get_templates', side_effect=self.get_templates) as get_templates_patch, \ + patch('o3de.manifest.get_project_templates', side_effect=self.get_project_templates)\ + as get_project_templates_patch, \ + patch('o3de.manifest.get_engine_templates', side_effect=self.get_engine_templates)\ + as get_engine_templates_patch, \ + patch('o3de.validation.valid_o3de_template_json', return_value=True) as validate_template_json,\ + patch('o3de.validation.valid_o3de_project_json', side_effect=validate_project_json) as validate_project_json,\ + patch('o3de.validation.valid_o3de_gem_json', side_effect=validate_gem_json) as validate_gem_json: + templates = manifest.get_templates_for_generic_creation() + assert templates == expected_template_paths + + + @pytest.mark.parametrize("expected_template_paths", [ + pytest.param([pathlib.Path('D:/o3de/Templates/DefaultProject')]) + ] + ) + def test_get_templates_for_gem_creation(self, valid_project_json_paths, valid_gem_json_paths, + expected_template_paths): + def validate_project_json(template_path) -> bool: + return pathlib.Path(template_path) in valid_project_json_paths + + def validate_gem_json(template_path) -> bool: + return pathlib.Path(template_path) in valid_gem_json_paths + + with patch('o3de.manifest.get_templates', side_effect=self.get_templates) as get_templates_patch, \ + patch('o3de.manifest.get_project_templates', side_effect=self.get_project_templates) \ + as get_project_templates_patch, \ + patch('o3de.manifest.get_engine_templates', side_effect=self.get_engine_templates) \ + as get_engine_templates_patch, \ + patch('o3de.validation.valid_o3de_template_json', return_value=True) as validate_template_json, \ + patch('o3de.validation.valid_o3de_project_json', + side_effect=validate_project_json) as validate_project_json, \ + patch('o3de.validation.valid_o3de_gem_json', side_effect=validate_gem_json) as validate_gem_json: + templates = manifest.get_templates_for_project_creation() + assert templates == expected_template_paths + + + @pytest.mark.parametrize("expected_template_paths", [ + pytest.param([pathlib.Path('D:/o3de/Templates/DefaultGem')]) + ] + ) + def test_get_templates_for_project_creation(self, valid_project_json_paths, valid_gem_json_paths, + expected_template_paths): + def validate_project_json(template_path) -> bool: + return pathlib.Path(template_path) in valid_project_json_paths + + def validate_gem_json(template_path) -> bool: + return pathlib.Path(template_path) in valid_gem_json_paths + + with patch('o3de.manifest.get_templates', side_effect=self.get_templates) as get_templates_patch, \ + patch('o3de.manifest.get_project_templates', side_effect=self.get_project_templates) \ + as get_project_templates_patch, \ + patch('o3de.manifest.get_engine_templates', side_effect=self.get_engine_templates) \ + as get_engine_templates_patch, \ + patch('o3de.validation.valid_o3de_template_json', return_value=True) as validate_template_json, \ + patch('o3de.validation.valid_o3de_project_json', + side_effect=validate_project_json) as validate_project_json, \ + patch('o3de.validation.valid_o3de_gem_json', side_effect=validate_gem_json) as validate_gem_json: + templates = manifest.get_templates_for_gem_creation() + assert templates == expected_template_paths \ No newline at end of file diff --git a/scripts/o3de/tests/unit_test_utils.py b/scripts/o3de/tests/unit_test_utils.py index e7d59b507b..4fe22b1f72 100755 --- a/scripts/o3de/tests/unit_test_utils.py +++ b/scripts/o3de/tests/unit_test_utils.py @@ -11,7 +11,7 @@ import pytest -from . import utils +from o3de import utils @pytest.mark.parametrize( "value, expected_result", [