diff --git a/scripts/py_matter_idl/matter/idl/generators/cpp/sdk/SharedEnumsCheck.jinja b/scripts/py_matter_idl/matter/idl/generators/cpp/sdk/SharedEnumsCheck.jinja new file mode 100644 index 00000000000000..d05db500f0fee3 --- /dev/null +++ b/scripts/py_matter_idl/matter/idl/generators/cpp/sdk/SharedEnumsCheck.jinja @@ -0,0 +1,29 @@ +// DO NOT EDIT MANUALLY - Generated file +// +// Generated based on {{input_name}} +#pragma once + +#include + +namespace chip { +namespace app { +namespace Clusters { +{% for item in items -%} +static auto __attribute__((unused)) EnsureKnownEnumValue({{item.namespace}}::{{item.enum.name}} val) +{ + using EnumType = {{item.namespace}}::{{item.enum.name}}; + switch (val) + { + {% for entry in item.enum.entries -%} + case EnumType::{{entry.specification_name | enum_constant_from_spec_name | default(entry.name, true)}}: + {% endfor -%} + return val; + default: + return EnumType::kUnknownEnumValue; + } +} +{% endfor -%} + +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/scripts/py_matter_idl/matter/idl/generators/cpp/sdk/sdk_generator.py b/scripts/py_matter_idl/matter/idl/generators/cpp/sdk/sdk_generator.py index e934ba5a2e4d1d..9f8e0c2ea6f8d4 100644 --- a/scripts/py_matter_idl/matter/idl/generators/cpp/sdk/sdk_generator.py +++ b/scripts/py_matter_idl/matter/idl/generators/cpp/sdk/sdk_generator.py @@ -13,11 +13,14 @@ # limitations under the License. # import os +import re +from dataclasses import dataclass +from typing import Optional from matter.idl.generators import CodeGenerator from matter.idl.generators.storage import GeneratorStorage -from matter.idl.matter_idl_types import (AccessPrivilege, Attribute, AttributeQuality, Command, CommandQuality, FieldQuality, Idl, - Struct) +from matter.idl.matter_idl_types import (AccessPrivilege, Attribute, AttributeQuality, Command, CommandQuality, Enum, FieldQuality, + Idl, Struct) def as_privilege(privilege: AccessPrivilege) -> str: @@ -36,13 +39,13 @@ def extract_attribute_quality_flags(attribute: Attribute) -> list[str]: result = [] if attribute.qualities & AttributeQuality.TIMED_WRITE: - result.append('DataModel::AttributeQualityFlags::kTimed') + result.append("DataModel::AttributeQualityFlags::kTimed") if attribute.definition.is_list: - result.append('DataModel::AttributeQualityFlags::kListAttribute') + result.append("DataModel::AttributeQualityFlags::kListAttribute") if attribute.definition.qualities & FieldQuality.FABRIC_SENSITIVE: - result.append('DataModel::AttributeQualityFlags::kFabricSensitive') + result.append("DataModel::AttributeQualityFlags::kFabricSensitive") # TODO: kChangesOmitted is not available # TODO: kFabricScoped is not available here (it is a struct/field quality...) @@ -54,10 +57,10 @@ def extract_command_quality_flags(command: Command) -> list[str]: result = [] if command.qualities & CommandQuality.FABRIC_SCOPED: - result.append('DataModel::CommandQualityFlags::kFabricScoped') + result.append("DataModel::CommandQualityFlags::kFabricScoped") if command.qualities & CommandQuality.TIMED_INVOKE: - result.append('DataModel::CommandQualityFlags::kTimed') + result.append("DataModel::CommandQualityFlags::kTimed") # TODO: kLargeMessage is not available @@ -85,6 +88,56 @@ def response_struct(s: Struct) -> bool: return s.code is not None +@dataclass +class EnumEntry: + namespace: str + enum: Enum + + +def extract_shared_enums(idl: Idl) -> list[EnumEntry]: + """ + Retunrs global/shared enumerations, distinguished by namespace + """ + result = [] + for g in idl.global_enums: + result.append(EnumEntry(namespace="Globals", enum=g)) + + # Code generation relies that shared enums are unique by name. + # XMLs could be written to duplicate things, however that would break our codegen + # significantly as we use a single namespace for shared things: `details` and + # name clashes would not work. + # + # For now rely that the names are unique. + found_enums = set() + for c in idl.clusters: + for e in c.enums: + if not e.is_shared: + continue + if e.name in found_enums: + continue + + result.append(EnumEntry(namespace="detail", enum=e)) + found_enums.add(e.name) + + result.sort(key=lambda e: (e.namespace.lower(), e.enum.name)) + + return result + + +def enum_constant_from_spec_name(spec_name: Optional[str]) -> Optional[str]: + if spec_name is None: + return None + + # C++ uses asUpperCampeCase names, without acronym preservation. Logic is: + # - split by words + # - CamelCase words + result = "k" + for word in re.split(r"[- _/\\]", spec_name): + result += word[0].upper() + word[1:].lower() + + return result + + class SdkGenerator(CodeGenerator): """ Generation of cpp code for application implementation for matter. @@ -96,12 +149,17 @@ def __init__(self, storage: GeneratorStorage, idl: Idl, **kargs): filters as required by the java .jinja templates to function. """ super().__init__(storage, idl, fs_loader_searchpath=os.path.dirname(__file__)) - self.jinja_env.filters['as_privilege'] = as_privilege - self.jinja_env.filters['extract_attribute_quality_flags'] = extract_attribute_quality_flags - self.jinja_env.filters['extract_command_quality_flags'] = extract_command_quality_flags - self.jinja_env.filters['name_for_id_usage'] = name_for_id_usage - self.jinja_env.tests['global_attribute'] = global_attribute - self.jinja_env.tests['response_struct'] = response_struct + self.jinja_env.filters["as_privilege"] = as_privilege + self.jinja_env.filters["extract_attribute_quality_flags"] = ( + extract_attribute_quality_flags + ) + self.jinja_env.filters["extract_command_quality_flags"] = ( + extract_command_quality_flags + ) + self.jinja_env.filters["name_for_id_usage"] = name_for_id_usage + self.jinja_env.filters["enum_constant_from_spec_name"] = enum_constant_from_spec_name + self.jinja_env.tests["global_attribute"] = global_attribute + self.jinja_env.tests["response_struct"] = response_struct def internal_render_all(self): """ @@ -117,14 +175,22 @@ def internal_render_all(self): }, ) + # render shared ... + self.internal_render_one_output( + template_path="SharedEnumsCheck.jinja", + output_file_name="shared/EnumsCheck.h", + vars={ + "items": extract_shared_enums(self.idl), + "input_name": self.idl.parse_file_name, + }, + ) + for cluster in self.idl.clusters: build_targets = { "Build.jinja": "BUILD.gn", - # contains `*Entry` items for attributes and commands "ClusterMetadataHeader.jinja": "Metadata.h", - # contains id definitions "AttributeIds.jinja": "AttributeIds.h", "ClusterId.jinja": "ClusterId.h", diff --git a/src/app/common/templates/templates.json b/src/app/common/templates/templates.json index 6e6753283df58e..9d154e431c6fc2 100644 --- a/src/app/common/templates/templates.json +++ b/src/app/common/templates/templates.json @@ -104,11 +104,6 @@ "name": "Shared enumerations header", "output": "../../clusters/shared/Enums.h" }, - { - "path": "../../zap-templates/templates/app/shared-cluster-enums-check.zapt", - "name": "Shared enumeration check header", - "output": "../../clusters/shared/EnumsCheck.h" - }, { "path": "../../zap-templates/templates/app/shared-cluster-structs.zapt", "name": "Shared/global structures", diff --git a/src/app/zap-templates/templates/app/shared-cluster-enums-check.zapt b/src/app/zap-templates/templates/app/shared-cluster-enums-check.zapt deleted file mode 100644 index b96a85a451080c..00000000000000 --- a/src/app/zap-templates/templates/app/shared-cluster-enums-check.zapt +++ /dev/null @@ -1,25 +0,0 @@ -{{> header}} -#pragma once - -#include - -namespace chip { -namespace app { -namespace Clusters { -{{! zcl_enums ordering is non-deterministic when a global and shared enum have - the same name: ZAP has no way to consistently order those same-named things - against each other. To have deterministic codegen, just always output the - shared things first, then the global things. }} -{{#zcl_enums}} -{{#if has_more_than_one_cluster}} -{{> cluster_enums_ensure_known_value ns="detail"}} -{{/if}} -{{/zcl_enums}} -{{#zcl_enums}} -{{#if has_no_clusters}} -{{> cluster_enums_ensure_known_value ns="Globals"}} -{{/if}} -{{/zcl_enums}} -} // namespace Clusters -} // namespace app -} // namespace chip diff --git a/zzz_generated/app-common/clusters/shared/EnumsCheck.h b/zzz_generated/app-common/clusters/shared/EnumsCheck.h index 2efd79e695b855..86eb4f8e088073 100644 --- a/zzz_generated/app-common/clusters/shared/EnumsCheck.h +++ b/zzz_generated/app-common/clusters/shared/EnumsCheck.h @@ -1,21 +1,6 @@ -/* - * - * Copyright (c) 2022 Project CHIP Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// THIS FILE IS GENERATED BY ZAP +// DO NOT EDIT MANUALLY - Generated file +// +// Generated based on src/controller/data_model/controller-clusters.matter #pragma once #include