diff --git a/.restyled.yaml b/.restyled.yaml index 8c77704457a15d..33db552694c1f3 100644 --- a/.restyled.yaml +++ b/.restyled.yaml @@ -78,6 +78,7 @@ exclude: - "scripts/run_codegen_targets.sh" # shellharden breaks for loops over command outputs - "src/darwin/Framework/CHIP/zap-generated/*" # already clang-formatted by our zap tooling - "zzz_generated/**/*" # already clang-formatted by our zap tooling + - "src/controller/java/generated/java/**/*" # not formatted: generated files changed_paths: diff --git a/build/chip/chip_codegen.gni b/build/chip/chip_codegen.gni index 49ba661f679e38..88658bcf14eb5c 100644 --- a/build/chip/chip_codegen.gni +++ b/build/chip/chip_codegen.gni @@ -263,7 +263,7 @@ template("_chip_build_time_zapgen") { # The ".matter" file to use to start the code generation # # generator -# Name of the generator to use (e.g. java, cpp-app) +# Name of the generator to use (e.g. java-jni, java-class, cpp-app) # # outputs # Explicit names of the expected outputs. Enforced to validate that @@ -296,7 +296,7 @@ template("_chip_build_time_zapgen") { # # chip_codegen("java-jni-generate") { # input = "controller-clusters.matter" -# generator = "java" +# generator = "java-jni" # # outputs = [ # "jni/IdentifyClient-ReadImpl.cpp", @@ -358,7 +358,7 @@ template("chip_codegen") { # The ".matter" file to use to start the code generation # # generator -# Name of the generator to use (e.g. java, cpp-app) +# Name of the generator to use (e.g. java-jni, java-class, cpp-app) # # outputs # Explicit names of the expected outputs. Enforced to validate that @@ -391,7 +391,7 @@ template("chip_codegen") { # # chip_codegen("java-jni-generate") { # input = "controller-clusters.matter" -# generator = "java" +# generator = "java-jni" # # outputs = [ # "jni/IdentifyClient-ReadImpl.cpp", diff --git a/scripts/codegen.py b/scripts/codegen.py index 60c2ee8d9b6777..564b8eac3c3f19 100755 --- a/scripts/codegen.py +++ b/scripts/codegen.py @@ -69,7 +69,7 @@ def write_new_data(self, relative_path: str, content: str): help='Determines the verbosity of script output') @click.option( '--generator', - default='JAVA', + default='java-jni', help='What code generator to run. The choices are: '+'|'.join(GENERATORS.keys())+'. ' + 'When using custom, provide the plugin path using `--generator custom::` syntax. ' + 'For example, `--generator custom:./my_plugin:my_plugin_module` will load `./my_plugin/my_plugin_module/__init.py__` ' + diff --git a/scripts/pregenerate/__init__.py b/scripts/pregenerate/__init__.py index c5033f406fdf7c..f664f5745b81dc 100644 --- a/scripts/pregenerate/__init__.py +++ b/scripts/pregenerate/__init__.py @@ -20,7 +20,8 @@ from typing import Iterator, List, Optional from .types import IdlFileType, InputIdlFile -from .using_codegen import CodegenBridgePregenerator, CodegenCppAppPregenerator, CodegenJavaPregenerator +from .using_codegen import (CodegenBridgePregenerator, CodegenCppAppPregenerator, CodegenJavaClassPregenerator, + CodegenJavaJNIPregenerator) from .using_zap import ZapApplicationPregenerator @@ -77,7 +78,8 @@ def FindPregenerationTargets(sdk_root: str, filter: TargetFilter, runner): generators = [ # Jinja-based codegen CodegenBridgePregenerator(sdk_root), - CodegenJavaPregenerator(sdk_root), + CodegenJavaJNIPregenerator(sdk_root), + CodegenJavaClassPregenerator(sdk_root), CodegenCppAppPregenerator(sdk_root), # ZAP codegen diff --git a/scripts/pregenerate/using_codegen.py b/scripts/pregenerate/using_codegen.py index 0ca159704b6f4a..1040bb7ac1ae58 100644 --- a/scripts/pregenerate/using_codegen.py +++ b/scripts/pregenerate/using_codegen.py @@ -73,7 +73,7 @@ def CreateTarget(self, idl: InputIdlFile, runner): return CodegenTarget(sdk_root=self.sdk_root, idl=idl, generator="bridge", runner=runner) -class CodegenJavaPregenerator: +class CodegenJavaJNIPregenerator: """Pregeneration logic for "java" codegen.py outputs""" def __init__(self, sdk_root): @@ -85,7 +85,22 @@ def Accept(self, idl: InputIdlFile): return idl.relative_path == "src/controller/data_model/controller-clusters.matter" def CreateTarget(self, idl: InputIdlFile, runner): - return CodegenTarget(sdk_root=self.sdk_root, idl=idl, generator="java", runner=runner) + return CodegenTarget(sdk_root=self.sdk_root, idl=idl, generator="java-jni", runner=runner) + + +class CodegenJavaClassPregenerator: + """Pregeneration logic for "java" codegen.py outputs""" + + def __init__(self, sdk_root): + self.sdk_root = sdk_root + + def Accept(self, idl: InputIdlFile): + # Java is highly specific, a single path is acceptable for dynamic + # bridge codegen + return idl.relative_path == "src/controller/data_model/controller-clusters.matter" + + def CreateTarget(self, idl: InputIdlFile, runner): + return CodegenTarget(sdk_root=self.sdk_root, idl=idl, generator="java-class", runner=runner) class CodegenCppAppPregenerator: diff --git a/scripts/py_matter_idl/BUILD.gn b/scripts/py_matter_idl/BUILD.gn index 7e3254573e9cac..aa0ba2f7899288 100644 --- a/scripts/py_matter_idl/BUILD.gn +++ b/scripts/py_matter_idl/BUILD.gn @@ -56,6 +56,7 @@ pw_python_package("matter_idl") { "matter_idl/tests/outputs/several_clusters/bridge/SecondServer.h", "matter_idl/tests/outputs/several_clusters/bridge/Third.h", "matter_idl/tests/outputs/several_clusters/bridge/ThirdServer.h", + "matter_idl/tests/outputs/several_clusters/java/ClusterWriteMapping.java", "matter_idl/tests/outputs/several_clusters/jni/FirstClient-ReadImpl.cpp", "matter_idl/tests/outputs/several_clusters/jni/SecondClient-ReadImpl.cpp", "matter_idl/tests/outputs/several_clusters/jni/ThirdClient-ReadImpl.cpp", diff --git a/scripts/py_matter_idl/files.gni b/scripts/py_matter_idl/files.gni index aa22f705caff2d..23de251a13b698 100644 --- a/scripts/py_matter_idl/files.gni +++ b/scripts/py_matter_idl/files.gni @@ -8,6 +8,7 @@ matter_idl_generator_templates = [ "${chip_root}/scripts/py_matter_idl/matter_idl/generators/bridge/BridgeClustersGlobalStructs.jinja", "${chip_root}/scripts/py_matter_idl/matter_idl/generators/java/ChipClustersCpp.jinja", "${chip_root}/scripts/py_matter_idl/matter_idl/generators/java/ChipClustersRead.jinja", + "${chip_root}/scripts/py_matter_idl/matter_idl/generators/java/ClusterWriteMapping.jinja", "${chip_root}/scripts/py_matter_idl/matter_idl/generators/cpp/application/CallbackStubSource.jinja", "${chip_root}/scripts/py_matter_idl/matter_idl/generators/cpp/application/PluginApplicationCallbacksHeader.jinja", ] diff --git a/scripts/py_matter_idl/matter_idl/generators/filters.py b/scripts/py_matter_idl/matter_idl/generators/filters.py index 594bcff7a2ecb7..b1ea94a7cd36c6 100644 --- a/scripts/py_matter_idl/matter_idl/generators/filters.py +++ b/scripts/py_matter_idl/matter_idl/generators/filters.py @@ -24,6 +24,31 @@ def normalize_acronyms(s: str) -> str: return s.replace('WiFi', 'Wifi').replace('WI_FI', 'WIFI') +def lowfirst(s: str) -> str: + """Make the first letter lowercase. """ + return s[0].lower() + s[1:] + + +def upfirst(s: str) -> str: + """Make the first letter uppercase """ + return s[0].upper() + s[1:] + + +def lowfirst_except_acronym(s: str) -> str: + """Make the first letter lowercase assuming the string is already in + CamelCase. + + Differs from lowfirst because it checks the string for starting with + several uppercase, which is the case for acronyms (HVAC, ACL, WIFI), + in which case it will NOT lowercase first + """ + if len(s) >= 2: + if s[1].isupper(): + return s + + return lowfirst(s) + + def RegisterCommonFilters(filtermap): """ Register filters that are NOT considered platform-generator specific. @@ -42,3 +67,6 @@ def RegisterCommonFilters(filtermap): filtermap['spinalcase'] = stringcase.spinalcase filtermap['normalize_acronyms'] = normalize_acronyms + filtermap['lowfirst'] = lowfirst + filtermap['lowfirst_except_acronym'] = lowfirst_except_acronym + filtermap['upfirst'] = upfirst diff --git a/scripts/py_matter_idl/matter_idl/generators/java/ClusterWriteMapping.jinja b/scripts/py_matter_idl/matter_idl/generators/java/ClusterWriteMapping.jinja new file mode 100644 index 00000000000000..836267e835d965 --- /dev/null +++ b/scripts/py_matter_idl/matter_idl/generators/java/ClusterWriteMapping.jinja @@ -0,0 +1,67 @@ +/* + * + * Copyright (c) 2023 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. + */ +package chip.devicecontroller; + +import chip.clusterinfo.CommandParameterInfo; +import chip.clusterinfo.InteractionInfo; +import chip.devicecontroller.ChipClusters.DefaultClusterCallback; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +public class ClusterWriteMapping { + public Map> getWriteAttributeMap() { + Map> writeAttributeMap = new HashMap<>(); + + {%- for cluster in clientClusters | sort(attribute='code') %} + {%- set typeLookup = idl | createLookupContext(cluster) %} + Map write{{cluster.name}}InteractionInfo = new LinkedHashMap<>(); + {%- for attribute in cluster.attributes | sort(attribute='name') | attributesWithCallback(typeLookup) %} + {#- TODO: add support for struct-typed attributes -#} + {% if not attribute.definition.is_list and attribute.is_writable %} + Map write{{cluster.name}}{{attribute.definition.name | upfirst}}CommandParams = new LinkedHashMap(); + {%- set encodable = attribute.definition | asEncodable(typeLookup) %} + CommandParameterInfo {{cluster.name | lowfirst_except_acronym}}{{attribute.definition.name | lowfirst_except_acronym}}CommandParameterInfo = + new CommandParameterInfo( + "value", + {{ encodable.boxed_java_type }}.class, {# {{asJavaType type null parent.parent.name removeGenericType=true}}.class, #} + {{ encodable.boxed_java_type }}.class {# {{asJavaType type null parent.parent.name underlyingType=true}}.class #} + ); + write{{cluster.name}}{{attribute.definition.name | upfirst}}CommandParams.put( + "value", + {{cluster.name | lowfirst_except_acronym}}{{attribute.definition.name | lowfirst_except_acronym}}CommandParameterInfo + ); + InteractionInfo write{{cluster.name}}{{attribute.definition.name | upfirst}}AttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.{{cluster.name}}Cluster) cluster).write{{attribute.definition.name | upfirst}}Attribute( + (DefaultClusterCallback) callback, + ({{ encodable.boxed_java_type }}) commandArguments.get("value") + {%- if attribute.requires_timed_write -%}, 10000 {% endif %} + ); + }, + () -> new ClusterInfoMapping.DelegatedDefaultClusterCallback(), + write{{cluster.name}}{{attribute.definition.name | upfirst}}CommandParams + ); + write{{cluster.name}}InteractionInfo.put("write{{attribute.definition.name | upfirst}}Attribute", write{{cluster.name}}{{attribute.definition.name | upfirst}}AttributeInteractionInfo); + {%- endif %} + {%- endfor %} + writeAttributeMap.put("{{cluster.name | lowfirst_except_acronym}}", write{{cluster.name}}InteractionInfo); + {%- endfor -%} + + return writeAttributeMap; + } +} diff --git a/scripts/py_matter_idl/matter_idl/generators/java/__init__.py b/scripts/py_matter_idl/matter_idl/generators/java/__init__.py index 02dda6997c5eef..562f589301d010 100644 --- a/scripts/py_matter_idl/matter_idl/generators/java/__init__.py +++ b/scripts/py_matter_idl/matter_idl/generators/java/__init__.py @@ -356,9 +356,11 @@ def CanGenerateSubscribe(attr: Attribute, lookup: TypeLookupContext) -> bool: return not lookup.is_struct_type(attr.definition.data_type.name) -class JavaGenerator(CodeGenerator): +class __JavaCodeGenerator(CodeGenerator): """ - Generation of java code for matter. + Code generation for java-specific files. + + Registers filters used by all java generators. """ def __init__(self, storage: GeneratorStorage, idl: Idl, **kargs): @@ -378,6 +380,13 @@ def __init__(self, storage: GeneratorStorage, idl: Idl, **kargs): self.jinja_env.filters['createLookupContext'] = CreateLookupContext self.jinja_env.filters['canGenerateSubscribe'] = CanGenerateSubscribe + +class JavaJNIGenerator(__JavaCodeGenerator): + """Generates JNI java files (i.e. C++ source/headers).""" + + def __init__(self, *args, **kargs): + super().__init__(*args, **kargs) + def internal_render_all(self): """ Renders .CPP files required for JNI support. @@ -406,3 +415,24 @@ def internal_render_all(self): 'typeLookup': TypeLookupContext(self.idl, cluster), } ) + + +class JavaClassGenerator(__JavaCodeGenerator): + """Generates .java files """ + + def __init__(self, *args, **kargs): + super().__init__(*args, **kargs) + + def internal_render_all(self): + """ + Renders .java files required for java matter support + """ + + self.internal_render_one_output( + template_path="java/ClusterWriteMapping.jinja", + output_file_name="java/chip/devicecontroller/ClusterWriteMapping.java", + vars={ + 'idl': self.idl, + 'clientClusters': [c for c in self.idl.clusters if c.side == ClusterSide.CLIENT], + } + ) diff --git a/scripts/py_matter_idl/matter_idl/generators/registry.py b/scripts/py_matter_idl/matter_idl/generators/registry.py index b3aab6df7b4cf0..8caf84c8fded9f 100644 --- a/scripts/py_matter_idl/matter_idl/generators/registry.py +++ b/scripts/py_matter_idl/matter_idl/generators/registry.py @@ -17,7 +17,7 @@ from matter_idl.generators.bridge import BridgeGenerator from matter_idl.generators.cpp.application import CppApplicationGenerator -from matter_idl.generators.java import JavaGenerator +from matter_idl.generators.java import JavaClassGenerator, JavaJNIGenerator class CodeGenerator(enum.Enum): @@ -26,14 +26,17 @@ class CodeGenerator(enum.Enum): the simple enum value (user friendly and can be a command line input) into underlying generators. """ - JAVA = enum.auto() + JAVA_JNI = enum.auto() + JAVA_CLASS = enum.auto() BRIDGE = enum.auto() CPP_APPLICATION = enum.auto() CUSTOM = enum.auto() def Create(self, *args, **kargs): - if self == CodeGenerator.JAVA: - return JavaGenerator(*args, **kargs) + if self == CodeGenerator.JAVA_JNI: + return JavaJNIGenerator(*args, **kargs) + elif self == CodeGenerator.JAVA_CLASS: + return JavaClassGenerator(*args, **kargs) elif self == CodeGenerator.BRIDGE: return BridgeGenerator(*args, **kargs) elif self == CodeGenerator.CPP_APPLICATION: @@ -63,7 +66,8 @@ def FromString(name): # to uniquely identify them when running command line tools or # executing tests GENERATORS = { - 'java': CodeGenerator.JAVA, + 'java-jni': CodeGenerator.JAVA_JNI, + 'java-class': CodeGenerator.JAVA_CLASS, 'bridge': CodeGenerator.BRIDGE, 'cpp-app': CodeGenerator.CPP_APPLICATION, 'custom': CodeGenerator.CUSTOM, diff --git a/scripts/py_matter_idl/matter_idl/test_generators.py b/scripts/py_matter_idl/matter_idl/test_generators.py index 39a42dbe4a0786..3a8d1f472fbe2b 100755 --- a/scripts/py_matter_idl/matter_idl/test_generators.py +++ b/scripts/py_matter_idl/matter_idl/test_generators.py @@ -33,7 +33,7 @@ from matter_idl.generators import GeneratorStorage from matter_idl.generators.bridge import BridgeGenerator from matter_idl.generators.cpp.application import CppApplicationGenerator -from matter_idl.generators.java import JavaGenerator +from matter_idl.generators.java import JavaClassGenerator, JavaJNIGenerator from matter_idl.matter_idl_types import Idl TESTS_DIR = os.path.join(os.path.dirname(__file__), "tests") @@ -116,8 +116,10 @@ def add_test_cases(self, yaml_test_case_dict): self.test_cases.append(test_case) def _create_generator(self, storage: GeneratorStorage, idl: Idl): - if self.generator_name.lower() == 'java': - return JavaGenerator(storage, idl) + if self.generator_name.lower() == 'java-jni': + return JavaJNIGenerator(storage, idl) + if self.generator_name.lower() == 'java-class': + return JavaClassGenerator(storage, idl) if self.generator_name.lower() == 'bridge': return BridgeGenerator(storage, idl) if self.generator_name.lower() == 'cpp-app': diff --git a/scripts/py_matter_idl/matter_idl/tests/available_tests.yaml b/scripts/py_matter_idl/matter_idl/tests/available_tests.yaml index 1b5483588b9b52..c1b253ec23f0f9 100644 --- a/scripts/py_matter_idl/matter_idl/tests/available_tests.yaml +++ b/scripts/py_matter_idl/matter_idl/tests/available_tests.yaml @@ -10,7 +10,7 @@ # - input_file is the input IDL # - output_file/golden_path are the expected output file names # and the expected content for those output files. -java: +java-jni: inputs/simple_attribute.matter: jni/MyClusterClient-ReadImpl.cpp: outputs/simple_attribute/jni/MyClusterClient-ReadImpl.cpp jni/MyClusterClient-InvokeSubscribeImpl.cpp: outputs/simple_attribute/jni/MyClusterClient-InvokeSubscribeImpl.cpp @@ -35,6 +35,10 @@ java: jni/MyClusterClient-ReadImpl.cpp: outputs/optional_argument/jni/MyClusterClient-ReadImpl.cpp jni/MyClusterClient-InvokeSubscribeImpl.cpp: outputs/optional_argument/jni/MyClusterClient-InvokeSubscribeImpl.cpp +java-class: + inputs/several_clusters.matter: + java/chip/devicecontroller/ClusterWriteMapping.java: outputs/several_clusters/java/ClusterWriteMapping.java + bridge: inputs/simple_attribute.matter: bridge/BridgeClustersImpl.h: outputs/simple_attribute/bridge/BridgeClustersImpl.h diff --git a/scripts/py_matter_idl/matter_idl/tests/outputs/several_clusters/java/ClusterWriteMapping.java b/scripts/py_matter_idl/matter_idl/tests/outputs/several_clusters/java/ClusterWriteMapping.java new file mode 100644 index 00000000000000..ff07bb30e4ccff --- /dev/null +++ b/scripts/py_matter_idl/matter_idl/tests/outputs/several_clusters/java/ClusterWriteMapping.java @@ -0,0 +1,102 @@ +/* + * + * Copyright (c) 2023 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. + */ +package chip.devicecontroller; + +import chip.clusterinfo.CommandParameterInfo; +import chip.clusterinfo.InteractionInfo; +import chip.devicecontroller.ChipClusters.DefaultClusterCallback; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +public class ClusterWriteMapping { + public Map> getWriteAttributeMap() { + Map> writeAttributeMap = new HashMap<>(); + Map writeFirstInteractionInfo = new LinkedHashMap<>(); + Map writeFirstSomeIntegerCommandParams = new LinkedHashMap(); + CommandParameterInfo firstsomeIntegerCommandParameterInfo = + new CommandParameterInfo( + "value", + Integer.class, + Integer.class + ); + writeFirstSomeIntegerCommandParams.put( + "value", + firstsomeIntegerCommandParameterInfo + ); + InteractionInfo writeFirstSomeIntegerAttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.FirstCluster) cluster).writeSomeIntegerAttribute( + (DefaultClusterCallback) callback, + (Integer) commandArguments.get("value") + ); + }, + () -> new ClusterInfoMapping.DelegatedDefaultClusterCallback(), + writeFirstSomeIntegerCommandParams + ); + writeFirstInteractionInfo.put("writeSomeIntegerAttribute", writeFirstSomeIntegerAttributeInteractionInfo); + writeAttributeMap.put("first", writeFirstInteractionInfo); + Map writeSecondInteractionInfo = new LinkedHashMap<>(); + writeAttributeMap.put("second", writeSecondInteractionInfo); + Map writeThirdInteractionInfo = new LinkedHashMap<>(); + Map writeThirdSomeEnumCommandParams = new LinkedHashMap(); + CommandParameterInfo thirdsomeEnumCommandParameterInfo = + new CommandParameterInfo( + "value", + Integer.class, + Integer.class + ); + writeThirdSomeEnumCommandParams.put( + "value", + thirdsomeEnumCommandParameterInfo + ); + InteractionInfo writeThirdSomeEnumAttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.ThirdCluster) cluster).writeSomeEnumAttribute( + (DefaultClusterCallback) callback, + (Integer) commandArguments.get("value") + ); + }, + () -> new ClusterInfoMapping.DelegatedDefaultClusterCallback(), + writeThirdSomeEnumCommandParams + ); + writeThirdInteractionInfo.put("writeSomeEnumAttribute", writeThirdSomeEnumAttributeInteractionInfo); + Map writeThirdOptionsCommandParams = new LinkedHashMap(); + CommandParameterInfo thirdoptionsCommandParameterInfo = + new CommandParameterInfo( + "value", + Integer.class, + Integer.class + ); + writeThirdOptionsCommandParams.put( + "value", + thirdoptionsCommandParameterInfo + ); + InteractionInfo writeThirdOptionsAttributeInteractionInfo = new InteractionInfo( + (cluster, callback, commandArguments) -> { + ((ChipClusters.ThirdCluster) cluster).writeOptionsAttribute( + (DefaultClusterCallback) callback, + (Integer) commandArguments.get("value") + ); + }, + () -> new ClusterInfoMapping.DelegatedDefaultClusterCallback(), + writeThirdOptionsCommandParams + ); + writeThirdInteractionInfo.put("writeOptionsAttribute", writeThirdOptionsAttributeInteractionInfo); + writeAttributeMap.put("third", writeThirdInteractionInfo);return writeAttributeMap; + } +} diff --git a/scripts/tools/zap_regen_all.py b/scripts/tools/zap_regen_all.py index 0da0969086f4a6..94407cae55b634 100755 --- a/scripts/tools/zap_regen_all.py +++ b/scripts/tools/zap_regen_all.py @@ -24,6 +24,8 @@ import sys import tempfile import time +import traceback +import urllib.request from dataclasses import dataclass from enum import Flag, auto from pathlib import Path @@ -41,6 +43,10 @@ class TargetType(Flag): # Global templates: generally examples and chip controller GLOBAL = auto() + # codgen.py templates (generally java templates, which cannot be built + # at compile time currently) + IDL_CODEGEN = auto() + # App-specific templates (see getSpecificTemplatesTargets) SPECIFIC = auto() @@ -48,12 +54,13 @@ class TargetType(Flag): GOLDEN_TEST_IMAGES = auto() # All possible targets. Convenience constant - ALL = TESTS | GLOBAL | SPECIFIC | GOLDEN_TEST_IMAGES + ALL = TESTS | GLOBAL | IDL_CODEGEN | SPECIFIC | GOLDEN_TEST_IMAGES __TARGET_TYPES__ = { 'tests': TargetType.TESTS, 'global': TargetType.GLOBAL, + 'idl_codegen': TargetType.IDL_CODEGEN, 'specific': TargetType.SPECIFIC, 'golden_test_images': TargetType.GOLDEN_TEST_IMAGES, 'all': TargetType.ALL, @@ -196,6 +203,61 @@ def log_command(self): logging.info(" %s" % " ".join(self.command)) +class JinjaCodegenTarget(): + def __init__(self, generator: str, output_directory: str, idl_path: str): + # This runs a test, but the important bit is we pass `--regenerate` + # to it and this will cause it to OVERWRITE golden images. + self.idl_path = idl_path + self.generator = generator + self.output_directory = output_directory + self.command = ["./scripts/codegen.py", "--output-dir", output_directory, + "--generator", generator, idl_path] + + def runJavaPrettifier(self): + try: + java_outputs = subprocess.check_output(["./scripts/codegen.py", "--name-only", "--generator", + self.generator, "--log-level", "fatal", self.idl_path]).decode("utf8").split("\n") + java_outputs = [os.path.join(self.output_directory, name) for name in java_outputs if name] + + logging.info("Prettifying %d java files:", len(java_outputs)) + for name in java_outputs: + logging.info(" %s" % name) + + # Keep this version in sync with what restyler uses (https://github.com/project-chip/connectedhomeip/blob/master/.restyled.yaml). + FORMAT_VERSION = "1.6" + URL_PREFIX = 'https://github.com/google/google-java-format/releases/download/google-java-format' + JAR_NAME = f"google-java-format-{FORMAT_VERSION}-all-deps.jar" + jar_url = f"{URL_PREFIX}-{FORMAT_VERSION}/{JAR_NAME}" + + path, http_message = urllib.request.urlretrieve(jar_url, Path.home().joinpath(JAR_NAME).as_posix()) + + subprocess.check_call(['java', '-jar', path, '--replace'] + java_outputs) + except Exception as err: + traceback.print_exception(err) + print('google-java-format error:', err) + + def generate(self) -> TargetRunStats: + generate_start = time.time() + + subprocess.check_call(self.command) + self.runJavaPrettifier() + + generate_end = time.time() + + return TargetRunStats( + generate_time=generate_end - generate_start, + config=f'codegen:{self.generator}', + template=self.idl_path, + ) + + def distinct_output(self): + # Fake output - this is a single target that generates golden images + return ZapDistinctOutput(input_template=f'{self.generator}{self.idl_path}', output_directory=self.output_directory) + + def log_command(self): + logging.info(" %s" % " ".join(self.command)) + + def checkPythonVersion(): if sys.version_info[0] < 3: print('Must use Python 3. Current version is ' + @@ -295,7 +357,18 @@ def getGlobalTemplatesTargets(): targets.append(ZAPGenerateTarget( 'src/controller/data_model/controller-clusters.zap', template="src/app/zap-templates/app-templates.json", - output_dir=os.path.join('zzz_generated/darwin/controller-clusters/zap-generated'))) + output_dir='zzz_generated/darwin/controller-clusters/zap-generated')) + + return targets + + +def getCodegenTemplates(): + targets = [] + + targets.append(JinjaCodegenTarget( + generator="java-class", + idl_path="src/controller/data_model/controller-clusters.matter", + output_directory="src/controller/java/generated")) return targets @@ -364,6 +437,9 @@ def getTargets(type, test_target): if type & TargetType.SPECIFIC: targets.extend(getSpecificTemplatesTargets()) + if type & TargetType.IDL_CODEGEN: + targets.extend(getCodegenTemplates()) + if type & TargetType.GOLDEN_TEST_IMAGES: targets.extend(getGoldenTestImageTargets()) diff --git a/src/controller/data_model/BUILD.gn b/src/controller/data_model/BUILD.gn index af701eb6fd43f1..304bd7acea4942 100644 --- a/src/controller/data_model/BUILD.gn +++ b/src/controller/data_model/BUILD.gn @@ -39,7 +39,7 @@ if (current_os == "android" || build_java_matter_controller) { chip_codegen("java-jni-generate") { input = "controller-clusters.matter" - generator = "java" + generator = "java-jni" outputs = [ "jni/IdentifyClient-ReadImpl.cpp", diff --git a/src/controller/java/BUILD.gn b/src/controller/java/BUILD.gn index 27d14372b9c959..9dc043fccc91a8 100644 --- a/src/controller/java/BUILD.gn +++ b/src/controller/java/BUILD.gn @@ -212,6 +212,7 @@ android_library("java") { data_deps = [ ":jni" ] sources = [ + "generated/java/chip/devicecontroller/ClusterWriteMapping.java", "src/chip/clusterinfo/ClusterCommandCallback.java", "src/chip/clusterinfo/ClusterInfo.java", "src/chip/clusterinfo/CommandParameterInfo.java", @@ -262,7 +263,6 @@ android_library("java") { "zap-generated/chip/devicecontroller/ChipStructs.java", "zap-generated/chip/devicecontroller/ClusterInfoMapping.java", "zap-generated/chip/devicecontroller/ClusterReadMapping.java", - "zap-generated/chip/devicecontroller/ClusterWriteMapping.java", ] if (build_java_matter_controller) { diff --git a/src/controller/java/zap-generated/chip/devicecontroller/ClusterWriteMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterWriteMapping.java similarity index 99% rename from src/controller/java/zap-generated/chip/devicecontroller/ClusterWriteMapping.java rename to src/controller/java/generated/java/chip/devicecontroller/ClusterWriteMapping.java index 571d380673d247..23a9b397567695 100644 --- a/src/controller/java/zap-generated/chip/devicecontroller/ClusterWriteMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterWriteMapping.java @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2022 Project CHIP Authors + * Copyright (c) 2023 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. @@ -14,9 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -// THIS FILE IS GENERATED BY ZAP - package chip.devicecontroller; import chip.clusterinfo.CommandParameterInfo; diff --git a/src/controller/java/templates/ClusterInfo-write-interaction.zapt b/src/controller/java/templates/ClusterInfo-write-interaction.zapt deleted file mode 100644 index 29e52ad07dde67..00000000000000 --- a/src/controller/java/templates/ClusterInfo-write-interaction.zapt +++ /dev/null @@ -1,49 +0,0 @@ -{{> header}} -{{#if (chip_has_client_clusters)}} - -package chip.devicecontroller; - -import chip.clusterinfo.CommandParameterInfo; -import chip.clusterinfo.InteractionInfo; -import chip.devicecontroller.ChipClusters.DefaultClusterCallback; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -public class ClusterWriteMapping { - public Map> getWriteAttributeMap() { - Map> writeAttributeMap = new HashMap<>(); - {{#chip_client_clusters}} - Map write{{asUpperCamelCase name}}InteractionInfo = new LinkedHashMap<>(); - {{#chip_server_cluster_attributes}} - {{! TODO: Add support for struct-typed attributes }} - {{#unless (isStrEqual chipCallback.name "Unsupported")}} - {{#if isWritableAttribute}} - {{#unless isArray}} - Map write{{asUpperCamelCase ../name}}{{asUpperCamelCase name}}CommandParams = new LinkedHashMap(); - CommandParameterInfo {{asLowerCamelCase ../name}}{{asLowerCamelCase name}}CommandParameterInfo = new CommandParameterInfo("value", {{asJavaType type null parent.parent.name removeGenericType=true}}.class, {{asJavaType type null parent.parent.name underlyingType=true}}.class); - write{{asUpperCamelCase ../name}}{{asUpperCamelCase name}}CommandParams.put("value",{{asLowerCamelCase ../name}}{{asLowerCamelCase name}}CommandParameterInfo); - InteractionInfo write{{asUpperCamelCase ../name}}{{asUpperCamelCase name}}AttributeInteractionInfo = new InteractionInfo( - (cluster, callback, commandArguments) -> { - ((ChipClusters.{{asUpperCamelCase ../name}}Cluster) cluster).write{{asUpperCamelCase name}}Attribute( - (DefaultClusterCallback) callback, - ({{asJavaBoxedType type chipType}}) - commandArguments.get("value") - {{#if mustUseTimedWrite}}, 10000{{/if}} - ); - }, - () -> new ClusterInfoMapping.DelegatedDefaultClusterCallback(), - write{{asUpperCamelCase ../name}}{{asUpperCamelCase name}}CommandParams - ); - write{{asUpperCamelCase ../name}}InteractionInfo.put("write{{asUpperCamelCase name}}Attribute", write{{asUpperCamelCase ../name}}{{asUpperCamelCase name}}AttributeInteractionInfo); - {{/unless}} - {{/if}} - {{/unless}} - {{/chip_server_cluster_attributes}} - writeAttributeMap.put("{{asLowerCamelCase name}}", write{{asUpperCamelCase name}}InteractionInfo); - {{/chip_client_clusters}} - return writeAttributeMap; - } -} - -{{/if}} \ No newline at end of file diff --git a/src/controller/java/templates/templates.json b/src/controller/java/templates/templates.json index d839cc4f211a33..744573fa9ca6cd 100644 --- a/src/controller/java/templates/templates.json +++ b/src/controller/java/templates/templates.json @@ -103,11 +103,6 @@ "path": "ClusterInfo-read-interaction.zapt", "name": "Generate read interaction for cluster information map", "output": "src/controller/java/zap-generated/chip/devicecontroller/ClusterReadMapping.java" - }, - { - "path": "ClusterInfo-write-interaction.zapt", - "name": "Generate write interaction for cluster information map", - "output": "src/controller/java/zap-generated/chip/devicecontroller/ClusterWriteMapping.java" } ] }