Skip to content

Commit 8426004

Browse files
andy31415andreilitvin
authored andcommitted
Create a codegen that converts idl back into .matter formats (#29867)
* Start creating a IDL codegen so we can self-test parsed output * Start with listing clusters * Enum listing * A lot more things supported * Attribute rendering * Support for string and octet string sizes * Timed command support * Restyle * Add descriptions to clusters * Attempt to fix up alignment of things * Alignment looks slightly better * Better command separation * Align comments * Align and output descriptions including clusters * More work regarding loop structures * Apply hex formatting to bitmaps. output now seems identical except one whitespace change * Identical output for now * Support API maturity. Notice that doccomments are lost on maturity :( * Fix doxygen parsing for api maturity at the cluster level * Restyle * Support endpoints, although that is not 1:1 as hex encoding and ordering for events is lost * Restyle * Add todo note that default value does not string escaping * Default rendering and add to files * More updates on file dependencies * Unit test IDL generator * Add the IDL unit test as a standard unit test * Update for python compatibility * Fix unit testing of builds when GSDK root is defined * Added a readme file * Restyle * Make xml parser use the idl codegen * Restyle * look to fix misspell warnings * Undo repo update * Fix linter errors --------- Co-authored-by: Andrei Litvin <andreilitvin@google.com>
1 parent e210bd9 commit 8426004

12 files changed

+536
-7
lines changed

scripts/build/test.py

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ def build_actual_output(root: str, out: str, args: List[str]) -> List[str]:
4848
'IMX_SDK_ROOT': 'IMX_SDK_ROOT',
4949
'TI_SYSCONFIG_ROOT': 'TEST_TI_SYSCONFIG_ROOT',
5050
'JAVA_PATH': 'TEST_JAVA_PATH',
51+
'GSDK_ROOT': 'TEST_GSDK_ROOT',
5152
})
5253

5354
retval = subprocess.run([

scripts/build/testdata/dry_run_efr32-brd4161a-light-rpc-no-version.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
cd "{root}"
33

44
# Generating efr32-brd4161a-light-rpc-no-version
5-
gn gen --check --fail-on-unused-args --export-compile-commands --root={root}/examples/lighting-app/silabs '--args=silabs_board="BRD4161A" is_debug=false import("//with_pw_rpc.gni")' {out}/efr32-brd4161a-light-rpc-no-version
5+
gn gen --check --fail-on-unused-args --export-compile-commands --root={root}/examples/lighting-app/silabs '--args=silabs_board="BRD4161A" is_debug=false import("//with_pw_rpc.gni") efr32_sdk_root="TEST_GSDK_ROOT" openthread_root="TEST_GSDK_ROOT/util/third_party/openthread"' {out}/efr32-brd4161a-light-rpc-no-version
66

77
# Building efr32-brd4161a-light-rpc-no-version
88
ninja -C {out}/efr32-brd4161a-light-rpc-no-version

scripts/py_matter_idl/BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ pw_python_package("matter_idl") {
6262
"matter_idl/test_backwards_compatibility.py",
6363
"matter_idl/test_matter_idl_parser.py",
6464
"matter_idl/test_generators.py",
65+
"matter_idl/test_idl_generator.py",
6566
"matter_idl/test_xml_parser.py",
6667
]
6768

scripts/py_matter_idl/files.gni

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ matter_idl_generator_templates = [
77
"${chip_root}/scripts/py_matter_idl/matter_idl/generators/cpp/application/PluginApplicationCallbacksHeader.jinja",
88
"${chip_root}/scripts/py_matter_idl/matter_idl/generators/cpp/tlvmeta/TLVMetaData_cpp.jinja",
99
"${chip_root}/scripts/py_matter_idl/matter_idl/generators/cpp/tlvmeta/TLVMetaData_h.jinja",
10+
"${chip_root}/scripts/py_matter_idl/matter_idl/generators/idl/MatterIdl.jinja",
1011
"${chip_root}/scripts/py_matter_idl/matter_idl/generators/java/CHIPCallbackTypes.jinja",
1112
"${chip_root}/scripts/py_matter_idl/matter_idl/generators/java/ChipClustersCpp.jinja",
1213
"${chip_root}/scripts/py_matter_idl/matter_idl/generators/java/ChipClustersRead.jinja",
@@ -27,6 +28,7 @@ matter_idl_generator_sources = [
2728
"${chip_root}/scripts/py_matter_idl/matter_idl/generators/cpp/application/__init__.py",
2829
"${chip_root}/scripts/py_matter_idl/matter_idl/generators/cpp/tlvmeta/__init__.py",
2930
"${chip_root}/scripts/py_matter_idl/matter_idl/generators/filters.py",
31+
"${chip_root}/scripts/py_matter_idl/matter_idl/generators/idl/__init__.py",
3032
"${chip_root}/scripts/py_matter_idl/matter_idl/generators/java/__init__.py",
3133
"${chip_root}/scripts/py_matter_idl/matter_idl/generators/registry.py",
3234
"${chip_root}/scripts/py_matter_idl/matter_idl/generators/types.py",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
{% macro render_field(field) -%}{#
2+
Macro for the output of a single field entry such as:
3+
4+
int16u identifyTime = 0;
5+
optional int16u transitionTime = 3;
6+
optional nullable int16u transitionTime = 2;
7+
optional ExtensionFieldSet extensionFieldSets[] = 5;
8+
#}
9+
10+
{%- if field.qualities %}{{field.qualities | idltxt}} {% endif -%}
11+
{{field.data_type.name}}
12+
{%- if field.data_type.max_length -%} <{{field.data_type.max_length}}> {%- endif -%}
13+
{##} {{field.name}}
14+
{%- if field.is_list %}[]{% endif -%}
15+
{##} = {{field.code}};
16+
{%- endmacro -%}
17+
18+
{% macro render_struct(s) -%}{#
19+
Macro for the output of a complete struct
20+
#}
21+
{%- if s.tag %}{{s.tag | idltxt}} {% endif -%}
22+
{% if s.qualities %}{{s.qualities | idltxt}} {% endif -%}
23+
struct {{s.name}} {##}
24+
{%- if s.code is not none %}= {{s.code}} {% endif -%}
25+
{
26+
{% for field in s.fields %}
27+
{{render_field(field)}}
28+
{% endfor %}
29+
}
30+
{%- endmacro -%}
31+
32+
33+
// This IDL was auto-generated from a parsed data structure
34+
35+
{% for cluster in idl.clusters %}
36+
{% if cluster.description %}/** {{cluster.description}} */
37+
{% endif %}
38+
{{cluster.api_maturity | idltxt}}{{cluster.side | idltxt}} cluster {{cluster.name}} = {{cluster.code}} {
39+
{%- for enum in cluster.enums %}
40+
41+
enum {{enum.name}} : {{ enum.base_type}} {
42+
{% for entry in enum.entries %}
43+
{{entry.name}} = {{entry.code}};
44+
{% endfor %}
45+
}
46+
{% endfor %}
47+
48+
{%- for bitmap in cluster.bitmaps %}
49+
50+
bitmap {{bitmap.name}} : {{ bitmap.base_type}} {
51+
{% for entry in bitmap.entries %}
52+
{{entry.name}} = 0x{{"%X" | format(entry.code)}};
53+
{% endfor %}
54+
}
55+
{% endfor %}
56+
57+
{%- for s in cluster.structs | rejectattr("tag") %}
58+
{% if loop.first %}
59+
60+
{% endif %}
61+
{{render_struct(s)}}
62+
{% if not loop.last %}
63+
64+
{% endif %}
65+
{% endfor %}
66+
67+
{%- for e in cluster.events %}
68+
{% if loop.first %}
69+
70+
{% endif %}
71+
{##} {##}{% if e.qualities %}{{e.qualities | idltxt}} {% endif -%}
72+
{{e.priority | idltxt}} event {{e | event_access}}{{e.name}} = {{e.code}} {
73+
{% for field in e.fields %}
74+
{{render_field(field)}}
75+
{% endfor %}
76+
}
77+
{% if not loop.last %}
78+
79+
{% endif %}
80+
{% endfor %}
81+
82+
{%- for a in cluster.attributes %}
83+
{% if loop.first %}
84+
85+
{% endif %}
86+
{{a.qualities | idltxt}}attribute {{a | attribute_access}}{{render_field(a.definition)}}
87+
{% endfor %}
88+
89+
{%- for s in cluster.structs | selectattr("tag") %}
90+
91+
{{render_struct(s)}}
92+
{% endfor %}
93+
94+
{%- for c in cluster.commands %}
95+
{% if loop.first %}
96+
97+
{% endif %}
98+
{% if c.description %}
99+
/** {{c.description}} */
100+
{% endif %}
101+
{{c.qualities | idltxt}}command {{c | command_access}}{{c.name}}(
102+
{%- if c.input_param %}{{c.input_param}}{% endif -%}
103+
): {{c.output_param}} = {{c.code}};
104+
{% endfor %}
105+
}
106+
107+
{% endfor %}
108+
109+
{%- if idl.endpoints %}
110+
{%- for endpoint in idl.endpoints %}
111+
endpoint {{endpoint.number}} {
112+
{% for t in endpoint.device_types %}
113+
device type {{t.name}} = {{t.code}}, version {{t.version}};
114+
{% endfor%}
115+
116+
{%-for b in endpoint.client_bindings %}
117+
{% if loop.first %}
118+
119+
{% endif %}
120+
binding cluster {{b}};
121+
{% endfor %}
122+
123+
{%-for c in endpoint.server_clusters %}
124+
125+
server cluster {{c.name}} {
126+
{% for e in c.events_emitted %}
127+
emits event {{e}};
128+
{% if loop.last %}
129+
130+
{% endif %}
131+
{% endfor %}
132+
{% for a in c.attributes %}
133+
{{"%-8s" | format(a.storage|idltxt) }} attribute {{a.name}}
134+
{%- if a.default is not none %} default = {{a.default|render_default}} {%- endif %};
135+
{% endfor %}
136+
{% for cmd in c.commands %}
137+
{% if loop.first %}
138+
139+
{% endif %}
140+
handle command {{cmd.name}};
141+
{% endfor %}
142+
}
143+
{% endfor %}
144+
145+
}
146+
{% if not loop.last %}
147+
148+
{% endif %}
149+
{% endfor %}
150+
{% endif %}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
## Generator description
2+
3+
Generates a structured `Idl` data type into a human-readable text format
4+
(`.matter` file).
5+
6+
It is useful for tools that ingest non-idl data but convert into idl data (e.g.
7+
`zapxml` or CSA data model XML data.)
8+
9+
### Usage
10+
11+
A no-op usage can be:
12+
13+
```
14+
./scripts/codegen.py -g idl --output-dir out/idlgen examples/all-clusters-app/all-clusters-common/all-clusters-app.matter
15+
```
16+
17+
which would re-generate the entire `all-clusters-app.matter` into
18+
`out/idlgen/idl.matter`
19+
20+
This generation is useful for testing/validating that both parsing and
21+
generation works. Actual usage of this generator would be inside XML tools.
22+
23+
### Within XML parsing
24+
25+
A XML parser will use this code generator to output a human readable view of the
26+
parsed data:
27+
28+
```
29+
./scripts/py_matter_idl/matter_idl/xml_parser.py \
30+
./src/app/zap-templates/zcl/data-model/chip/onoff-cluster.xml \
31+
./src/app/zap-templates/zcl/data-model/chip/global-attributes.xm
32+
```

0 commit comments

Comments
 (0)