can-api-generator 2.0.0__tar.gz → 2.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/PKG-INFO +25 -1
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/README.md +24 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/__about__.py +1 -1
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/code_generator.py +18 -5
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/main.py +17 -2
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/msg_processor.py +9 -4
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/templates/declaration_macros.jinja +0 -3
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/templates/header_template.jinja +6 -6
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/templates/rx_macros.jinja +7 -7
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/templates/source_template.jinja +3 -3
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/templates/tx_macros.jinja +7 -7
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/utils.py +7 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_cli.py +144 -9
- can_api_generator-2.1.0/tests/test_utils.py +15 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/.gitignore +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/LICENSE.txt +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/manual_cag_test.py +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/pyproject.toml +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/__init__.py +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/cbdb_generator.py +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/templates/canframe.jinja +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/templates/dyndec_header.jinja +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/templates/dyndec_source.jinja +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/templates/encoding_macros.jinja +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/types.py +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/__init__.py +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/cantool_test_utils.jinja +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/compat_shim.jinja +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/dyndec_test_harness.c +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/multiple_busses_test.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_1.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_basic_multiplex.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_big_endian.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_can_db_model_dep.py +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_cbdb_generator.py +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_cross_interface_compat.jinja +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_cross_interface_compat.py +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_dyndec_round_trip.py +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_float_wrong_length.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_message_parser.py +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_missing_consumer.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_missing_producer.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_mux_group_count_out_of_order.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_mux_with_labeled_index.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_mux_with_partially_labled_index.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_mux_with_same_signal_names.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_mux_with_same_signal_names_labeled_mux_variants.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_mux_with_scale_and_offset.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_non_encodable_0.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_overlapping_signals.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_periodic_tx_over_can.jinja +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_procedures.jinja +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_prog_source_template.jinja +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_rx_over_can.jinja +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_direction.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/double_plain.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/double_scaled.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/double_scaled_offset.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/float_plain.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/float_scaled.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/float_scaled_offset.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/sint_matching_fractional_scaled.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/sint_matching_fractional_scaled_offset.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/sint_matching_plain.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/sint_matching_scaled.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/sint_matching_scaled_offset.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/sint_nonmatching_fractional_scaled.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/sint_nonmatching_plain.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/sint_nonmatching_scaled.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/sint_nonmatching_scaled_offset.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/test_decoding_numerical_value.jinja +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/test_encoding_numerical_value.jinja +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/test_signal_encoding_wrong_float_size.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/uint_matching_fractional_scaled.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/uint_matching_plain.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/uint_matching_scaled.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/uint_matching_scaled_offset.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/uint_nonmatching_fractional_scaled.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/uint_nonmatching_plain.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/uint_nonmatching_scaled.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/uint_nonmatching_scaled_offset.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_info.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_too_large_message.kcd +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_tx_over_can.jinja +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_vcan_round_trip.py +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_yaml_cbdb.py +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_yaml_input.py +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_yaml_kcd_equivalence.py +0 -0
- {can_api_generator-2.0.0 → can_api_generator-2.1.0}/todo.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: can-api-generator
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Program to generate a 'signal' style CAN API in C
|
|
5
5
|
Project-URL: Documentation, https://sr.ht/~laplace/can-api-generator#readme
|
|
6
6
|
Project-URL: Issues, https://sr.ht/~laplace/can-api-generator/issues
|
|
@@ -283,6 +283,26 @@ translation unit. This is sufficient for a single-core MCU where the dispatcher
|
|
|
283
283
|
case. On weakly-ordered multicore hosts, user code is responsible for inserting the appropriate memory barrier between the decode writes and the pointer
|
|
284
284
|
publish, and between reading the pointer and dereferencing it — the generator does not emit any barriers itself.
|
|
285
285
|
|
|
286
|
+
### Coexisting APIs and struct-name prefixes
|
|
287
|
+
|
|
288
|
+
The per-message signal structs (`struct <Message>`) are emitted **without** an include-guard. Two headers that share a message name therefore cannot be included in
|
|
289
|
+
one translation unit unless their struct names are made distinct. (An earlier release briefly wrapped these structs in a `#ifndef STRUCT_<NAME>` guard; that was never
|
|
290
|
+
an intended feature — it silently reused the first header's struct layout when two headers defined the same struct name, hiding real layout mismatches — and has been
|
|
291
|
+
removed.)
|
|
292
|
+
|
|
293
|
+
To include two generated headers that share message names in one translation unit — for example the API for two versions of the same database — generate them with
|
|
294
|
+
`--prefix-signal-struct-names` and `--prefix-context-struct-names`. Each prefixes the relevant struct type names (and, for the context structs, their include-guard
|
|
295
|
+
tokens) with the CamelCase API prefix already used for `<Prefix>RxSignals` / `<Prefix>TxSignals`, so the two APIs get non-colliding struct names:
|
|
296
|
+
|
|
297
|
+
```c
|
|
298
|
+
/* generated with LIBRARY_NAME "battery_v2" and both prefix flags */
|
|
299
|
+
struct BatteryV2Message1 { /* ... */ };
|
|
300
|
+
struct BatteryV2MsgRxContext { /* ... */ }; /* guard: #ifndef BATTERYV2MSGRXCONTEXT */
|
|
301
|
+
struct BatteryV2MsgTxContext { /* ... */ }; /* guard: #ifndef BATTERYV2MSGTXCONTEXT */
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Both flags are off by default and reuse the existing API prefix — they change struct type *names* only, never layout or field order.
|
|
305
|
+
|
|
286
306
|
### RX/TX callback context
|
|
287
307
|
|
|
288
308
|
Both the [Global RX](#global-rx) and [Periodic TX](#periodic-tx) paths let you register a per-message callback. Without a context flag those callbacks take no arguments
|
|
@@ -565,6 +585,10 @@ Options:
|
|
|
565
585
|
generated and decoded signals reach the application only through the RX callback. See [Global RX](#global-rx). Only meaningful together with `-r`.
|
|
566
586
|
* `--with-rx-valid` — in callback-only mode, add a `valid` field to `MsgRxContext` and set it on each matched frame, for struct-layout compatibility with
|
|
567
587
|
store-based APIs. No effect with `--rx-signal-store`. Only meaningful together with `-r`.
|
|
588
|
+
* `--prefix-signal-struct-names` — prefix every per-message signal struct type name with the CamelCase API prefix (the same prefix used for `<Prefix>RxSignals` /
|
|
589
|
+
`<Prefix>TxSignals`). Lets two similar APIs (e.g. two versions of one database) coexist in a single translation unit with distinct struct names. Off by default.
|
|
590
|
+
* `--prefix-context-struct-names` — prefix the `MsgRxContext` / `MsgTxContext` type names and their include-guard tokens with the CamelCase API prefix, so the
|
|
591
|
+
context structs of two coexisting APIs do not share a single definition. Off by default.
|
|
568
592
|
* `-im`, `--id-member-name TEXT` — override the name of the `CanFrame` member that carries the arbitration id (default `arbitration_id`). Used to adapt the API
|
|
569
593
|
to an existing project's frame struct naming.
|
|
570
594
|
* `-lm`, `--length-member-name TEXT` — override the name of the `CanFrame` length member (default `length`).
|
|
@@ -258,6 +258,26 @@ translation unit. This is sufficient for a single-core MCU where the dispatcher
|
|
|
258
258
|
case. On weakly-ordered multicore hosts, user code is responsible for inserting the appropriate memory barrier between the decode writes and the pointer
|
|
259
259
|
publish, and between reading the pointer and dereferencing it — the generator does not emit any barriers itself.
|
|
260
260
|
|
|
261
|
+
### Coexisting APIs and struct-name prefixes
|
|
262
|
+
|
|
263
|
+
The per-message signal structs (`struct <Message>`) are emitted **without** an include-guard. Two headers that share a message name therefore cannot be included in
|
|
264
|
+
one translation unit unless their struct names are made distinct. (An earlier release briefly wrapped these structs in a `#ifndef STRUCT_<NAME>` guard; that was never
|
|
265
|
+
an intended feature — it silently reused the first header's struct layout when two headers defined the same struct name, hiding real layout mismatches — and has been
|
|
266
|
+
removed.)
|
|
267
|
+
|
|
268
|
+
To include two generated headers that share message names in one translation unit — for example the API for two versions of the same database — generate them with
|
|
269
|
+
`--prefix-signal-struct-names` and `--prefix-context-struct-names`. Each prefixes the relevant struct type names (and, for the context structs, their include-guard
|
|
270
|
+
tokens) with the CamelCase API prefix already used for `<Prefix>RxSignals` / `<Prefix>TxSignals`, so the two APIs get non-colliding struct names:
|
|
271
|
+
|
|
272
|
+
```c
|
|
273
|
+
/* generated with LIBRARY_NAME "battery_v2" and both prefix flags */
|
|
274
|
+
struct BatteryV2Message1 { /* ... */ };
|
|
275
|
+
struct BatteryV2MsgRxContext { /* ... */ }; /* guard: #ifndef BATTERYV2MSGRXCONTEXT */
|
|
276
|
+
struct BatteryV2MsgTxContext { /* ... */ }; /* guard: #ifndef BATTERYV2MSGTXCONTEXT */
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Both flags are off by default and reuse the existing API prefix — they change struct type *names* only, never layout or field order.
|
|
280
|
+
|
|
261
281
|
### RX/TX callback context
|
|
262
282
|
|
|
263
283
|
Both the [Global RX](#global-rx) and [Periodic TX](#periodic-tx) paths let you register a per-message callback. Without a context flag those callbacks take no arguments
|
|
@@ -540,6 +560,10 @@ Options:
|
|
|
540
560
|
generated and decoded signals reach the application only through the RX callback. See [Global RX](#global-rx). Only meaningful together with `-r`.
|
|
541
561
|
* `--with-rx-valid` — in callback-only mode, add a `valid` field to `MsgRxContext` and set it on each matched frame, for struct-layout compatibility with
|
|
542
562
|
store-based APIs. No effect with `--rx-signal-store`. Only meaningful together with `-r`.
|
|
563
|
+
* `--prefix-signal-struct-names` — prefix every per-message signal struct type name with the CamelCase API prefix (the same prefix used for `<Prefix>RxSignals` /
|
|
564
|
+
`<Prefix>TxSignals`). Lets two similar APIs (e.g. two versions of one database) coexist in a single translation unit with distinct struct names. Off by default.
|
|
565
|
+
* `--prefix-context-struct-names` — prefix the `MsgRxContext` / `MsgTxContext` type names and their include-guard tokens with the CamelCase API prefix, so the
|
|
566
|
+
context structs of two coexisting APIs do not share a single definition. Off by default.
|
|
543
567
|
* `-im`, `--id-member-name TEXT` — override the name of the `CanFrame` member that carries the arbitration id (default `arbitration_id`). Used to adapt the API
|
|
544
568
|
to an existing project's frame struct naming.
|
|
545
569
|
* `-lm`, `--length-member-name TEXT` — override the name of the `CanFrame` length member (default `length`).
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
from can_api_generator.msg_processor import CAPIMessage
|
|
2
|
+
from can_api_generator.utils import to_struct_prefix
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
import can_db_model
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
def build_msgs_data_dicts_from_kcd(
|
|
7
|
-
db_path: Path, api_name: str, node_name: str
|
|
8
|
+
db_path: Path, api_name: str, node_name: str,
|
|
9
|
+
prefix_signal_struct_names: bool = False):
|
|
8
10
|
"""Load a KCD or YAML database and build CAPIMessage dicts for NODE_NAME.
|
|
9
11
|
|
|
10
12
|
Returns (list-of-message-dicts, CanDb). The path may be .kcd or .yaml/.yml;
|
|
@@ -16,7 +18,8 @@ def build_msgs_data_dicts_from_kcd(
|
|
|
16
18
|
msg,
|
|
17
19
|
api_name=api_name,
|
|
18
20
|
node_name=node_name,
|
|
19
|
-
bus_name=bus_name
|
|
21
|
+
bus_name=bus_name,
|
|
22
|
+
prefix_signal_struct_names=prefix_signal_struct_names)
|
|
20
23
|
for msg in db.messages]
|
|
21
24
|
for msg in msgs:
|
|
22
25
|
for sig in msg.signals:
|
|
@@ -42,12 +45,18 @@ def generate_api_implementation(msgs: list[dict],
|
|
|
42
45
|
rx_signal_store: bool = False,
|
|
43
46
|
with_rx_valid: bool = False,
|
|
44
47
|
msg_as_tx_callback_arg: bool = False,
|
|
45
|
-
tx_signal_store: bool = False
|
|
48
|
+
tx_signal_store: bool = False,
|
|
49
|
+
prefix_context_struct_names: bool = False):
|
|
46
50
|
header_template = jinja_env.get_template("header_template.jinja")
|
|
47
51
|
source_template = jinja_env.get_template("source_template.jinja")
|
|
48
52
|
canframe_template = jinja_env.get_template("canframe.jinja")
|
|
49
|
-
lib_name_struct_prefix =
|
|
50
|
-
|
|
53
|
+
lib_name_struct_prefix = to_struct_prefix(api_name)
|
|
54
|
+
if prefix_context_struct_names:
|
|
55
|
+
rx_context_struct_name = lib_name_struct_prefix + "MsgRxContext"
|
|
56
|
+
tx_context_struct_name = lib_name_struct_prefix + "MsgTxContext"
|
|
57
|
+
else:
|
|
58
|
+
rx_context_struct_name = "MsgRxContext"
|
|
59
|
+
tx_context_struct_name = "MsgTxContext"
|
|
51
60
|
# Callback-only global RX (no store) can only deliver signals through the
|
|
52
61
|
# callback, so the message-as-callback-argument behavior is implied.
|
|
53
62
|
if not rx_signal_store:
|
|
@@ -72,6 +81,8 @@ def generate_api_implementation(msgs: list[dict],
|
|
|
72
81
|
with_rx_valid=with_rx_valid,
|
|
73
82
|
msg_as_tx_callback_arg=msg_as_tx_callback_arg,
|
|
74
83
|
tx_signal_store=tx_signal_store,
|
|
84
|
+
rx_context_struct_name=rx_context_struct_name,
|
|
85
|
+
tx_context_struct_name=tx_context_struct_name,
|
|
75
86
|
)
|
|
76
87
|
source_code = source_template.render(
|
|
77
88
|
messages=msgs,
|
|
@@ -95,6 +106,8 @@ def generate_api_implementation(msgs: list[dict],
|
|
|
95
106
|
with_rx_valid=with_rx_valid,
|
|
96
107
|
msg_as_tx_callback_arg=msg_as_tx_callback_arg,
|
|
97
108
|
tx_signal_store=tx_signal_store,
|
|
109
|
+
rx_context_struct_name=rx_context_struct_name,
|
|
110
|
+
tx_context_struct_name=tx_context_struct_name,
|
|
98
111
|
)
|
|
99
112
|
canframe_header_code = canframe_template.render(
|
|
100
113
|
arb_id_member_name=arb_id_member_name,
|
|
@@ -100,6 +100,17 @@ def main():
|
|
|
100
100
|
"periodic TX is callback-only: each scheduled message's "
|
|
101
101
|
"callback returns the struct to encode, and a NULL return or "
|
|
102
102
|
"missing callback sends nothing. Only meaningful with -p.")
|
|
103
|
+
@click.option("--prefix-signal-struct-names", is_flag=True, default=False,
|
|
104
|
+
help="Prefix every per-message signal struct type name with the "
|
|
105
|
+
"CamelCase API prefix (the same prefix used for "
|
|
106
|
+
"<Prefix>RxSignals / <Prefix>TxSignals), so two similar APIs "
|
|
107
|
+
"(e.g. two versions of a database) can coexist in one "
|
|
108
|
+
"translation unit with distinct struct names.")
|
|
109
|
+
@click.option("--prefix-context-struct-names", is_flag=True, default=False,
|
|
110
|
+
help="Prefix the MsgRxContext / MsgTxContext struct type names (and "
|
|
111
|
+
"their include-guard tokens) with the CamelCase API prefix, so "
|
|
112
|
+
"context structs from two coexisting APIs do not share one "
|
|
113
|
+
"definition.")
|
|
103
114
|
@click.option("-v", "--verbose", is_flag=True, default=False,
|
|
104
115
|
help="Print the Data that is passed to the template that "
|
|
105
116
|
"generates the code")
|
|
@@ -108,7 +119,8 @@ def api(db_file, library_name, node_name, force, encode_all_signals_func,
|
|
|
108
119
|
periodic_tx, ti_compatible, send_now, double_buffered,
|
|
109
120
|
rx_callback_context, tx_callback_context, msg_as_rx_callback_arg,
|
|
110
121
|
rx_signal_store, with_rx_valid, msg_as_tx_callback_arg,
|
|
111
|
-
tx_signal_store,
|
|
122
|
+
tx_signal_store, prefix_signal_struct_names,
|
|
123
|
+
prefix_context_struct_names, verbose):
|
|
112
124
|
"""
|
|
113
125
|
Generate the static C API for NODE_NAME from DB_FILE (.kcd or .yaml)
|
|
114
126
|
"""
|
|
@@ -127,7 +139,9 @@ def api(db_file, library_name, node_name, force, encode_all_signals_func,
|
|
|
127
139
|
except FileExistsError as e:
|
|
128
140
|
click.echo(e)
|
|
129
141
|
try:
|
|
130
|
-
msgs, kcd_db = build_msgs_data_dicts_from_kcd(
|
|
142
|
+
msgs, kcd_db = build_msgs_data_dicts_from_kcd(
|
|
143
|
+
db_path, library_name, node_name,
|
|
144
|
+
prefix_signal_struct_names=prefix_signal_struct_names)
|
|
131
145
|
except MessageProcessingException as e:
|
|
132
146
|
click.echo(f"Got Error while processing message {e.can_message_name}"
|
|
133
147
|
f" for {e.node_name}: {e}")
|
|
@@ -164,6 +178,7 @@ def api(db_file, library_name, node_name, force, encode_all_signals_func,
|
|
|
164
178
|
with_rx_valid=with_rx_valid,
|
|
165
179
|
msg_as_tx_callback_arg=msg_as_tx_callback_arg,
|
|
166
180
|
tx_signal_store=tx_signal_store,
|
|
181
|
+
prefix_context_struct_names=prefix_context_struct_names,
|
|
167
182
|
)
|
|
168
183
|
with (header_path.parent / 'canframe.h').open('w+') as canframe_header_file:
|
|
169
184
|
canframe_header_file.write(canframe_header)
|
|
@@ -10,7 +10,7 @@ import operator
|
|
|
10
10
|
import copy
|
|
11
11
|
import random
|
|
12
12
|
from can_db_model.model import MuxMember
|
|
13
|
-
from .utils import is_valid_c_identifier, converrt_to_c_identifier
|
|
13
|
+
from .utils import is_valid_c_identifier, converrt_to_c_identifier, to_struct_prefix
|
|
14
14
|
from .utils import convert_to_cantools_signal_name
|
|
15
15
|
from .utils import get_signal_type_from_range, get_bit_length_from_type
|
|
16
16
|
from .types import SignalType, SignalDirection, ByteOrder, SignalProcessingException, MessageProcessingException
|
|
@@ -18,7 +18,8 @@ from .types import SignalType, SignalDirection, ByteOrder, SignalProcessingExcep
|
|
|
18
18
|
|
|
19
19
|
class CAPIMessage:
|
|
20
20
|
def __init__(self, msg, api_name: str, node_name: str,
|
|
21
|
-
bus_name: str, require_endpoints: bool = True
|
|
21
|
+
bus_name: str, require_endpoints: bool = True,
|
|
22
|
+
prefix_signal_struct_names: bool = False):
|
|
22
23
|
"""
|
|
23
24
|
msg is a can_db_model.model.Message. bus_name is the database's bus
|
|
24
25
|
name (the model carries the bus at the top level, not per-message).
|
|
@@ -34,6 +35,7 @@ class CAPIMessage:
|
|
|
34
35
|
self.is_extended = msg.extended
|
|
35
36
|
# prefix for the struct names
|
|
36
37
|
self.api_prefix = api_name
|
|
38
|
+
self.prefix_signal_struct_names = prefix_signal_struct_names
|
|
37
39
|
if msg.length > 8:
|
|
38
40
|
raise MessageProcessingException(
|
|
39
41
|
"Message too large for v2.0B spec",
|
|
@@ -115,9 +117,12 @@ class CAPIMessage:
|
|
|
115
117
|
|
|
116
118
|
def struct_name(self):
|
|
117
119
|
if ' ' not in self.name:
|
|
118
|
-
|
|
120
|
+
base = ''.join(map(lambda s: s.capitalize(), self.name.split('_')))
|
|
119
121
|
else:
|
|
120
|
-
|
|
122
|
+
base = ''.join(map(lambda s: s.capitalize(), self.name.split()))
|
|
123
|
+
if self.prefix_signal_struct_names:
|
|
124
|
+
return to_struct_prefix(self.api_prefix) + base
|
|
125
|
+
return base
|
|
121
126
|
|
|
122
127
|
def to_dict(self):
|
|
123
128
|
return {
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
{% macro declare_message_struct(message) -%}
|
|
2
|
-
#ifndef STRUCT_{{ message.struct_name | upper }}
|
|
3
|
-
#define STRUCT_{{ message.struct_name | upper }}
|
|
4
2
|
struct {{ message.struct_name }} {
|
|
5
3
|
{%- for signal in message.all_signals %}
|
|
6
4
|
{{ signal.api_data_type }} {{ signal.name }};
|
|
7
5
|
{%- endfor %}
|
|
8
6
|
};
|
|
9
|
-
#endif
|
|
10
7
|
{%- endmacro %}
|
|
11
8
|
|
|
12
9
|
{% macro generate_msg_defines(message) -%}
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
{{ generate_label_defines(message) }}{{ declare_message_struct(message) }}
|
|
36
36
|
{% endfor %}
|
|
37
37
|
{%- if global_rx %}
|
|
38
|
-
{{ define_rx_context_struct(true, 'uint64_t', rx_callback_context=rx_callback_context, msg_as_rx_callback_arg=msg_as_rx_callback_arg, rx_signal_store=rx_signal_store, with_rx_valid=with_rx_valid) }}
|
|
38
|
+
{{ define_rx_context_struct(true, 'uint64_t', rx_callback_context=rx_callback_context, msg_as_rx_callback_arg=msg_as_rx_callback_arg, rx_signal_store=rx_signal_store, with_rx_valid=with_rx_valid, ctx_name=rx_context_struct_name) }}
|
|
39
39
|
|
|
40
|
-
{{ generate_global_rx_struct(messages, api_name_struct_prefix, double_buffered, rx_signal_store=rx_signal_store) }}
|
|
40
|
+
{{ generate_global_rx_struct(messages, api_name_struct_prefix, double_buffered, rx_signal_store=rx_signal_store, ctx_name=rx_context_struct_name) }}
|
|
41
41
|
|
|
42
42
|
{{ declare_global_rx_frame_process_function(api_name , api_name_struct_prefix, 'CanFrame', 'uint16_t', 'uint64_t') }};
|
|
43
43
|
{%- if double_buffered and rx_signal_store %}
|
|
@@ -46,15 +46,15 @@
|
|
|
46
46
|
{%- endif %}
|
|
47
47
|
{% endif %}
|
|
48
48
|
{%- if periodic_tx %}
|
|
49
|
-
{{ define_tx_context_struct(true, 'uint16_t', 'uint64_t', send_now, tx_callback_context=tx_callback_context, msg_as_tx_callback_arg=msg_as_tx_callback_arg) }}
|
|
49
|
+
{{ define_tx_context_struct(true, 'uint16_t', 'uint64_t', send_now, tx_callback_context=tx_callback_context, msg_as_tx_callback_arg=msg_as_tx_callback_arg, ctx_name=tx_context_struct_name) }}
|
|
50
50
|
|
|
51
|
-
{{ generate_global_tx_struct(messages, api_name_struct_prefix, double_buffered, tx_signal_store=tx_signal_store) }}
|
|
51
|
+
{{ generate_global_tx_struct(messages, api_name_struct_prefix, double_buffered, tx_signal_store=tx_signal_store, ctx_name=tx_context_struct_name) }}
|
|
52
52
|
|
|
53
53
|
{{ declare_periodic_tx_init_function(api_name, api_name_struct_prefix, 'uint64_t') }};
|
|
54
54
|
|
|
55
|
-
{{ declare_get_msg_tx_context_by_id(messages, api_name, api_name_struct_prefix, 'struct
|
|
55
|
+
{{ declare_get_msg_tx_context_by_id(messages, api_name, api_name_struct_prefix, 'struct ' ~ tx_context_struct_name) }};
|
|
56
56
|
|
|
57
|
-
{{ declare_period_update_function(api_name, 'struct
|
|
57
|
+
{{ declare_period_update_function(api_name, 'struct ' ~ tx_context_struct_name) }}
|
|
58
58
|
|
|
59
59
|
{{ declare_periodic_tx_function(api_name, api_name_struct_prefix, 'CanFrame', 'uint16_t', 'uint64_t', 'uint16_t') }};
|
|
60
60
|
{%- if double_buffered and tx_signal_store %}
|
{can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/templates/rx_macros.jinja
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{% from "encoding_macros.jinja" import message_match_function_call %}
|
|
2
2
|
{% from "encoding_macros.jinja" import message_decoding_function_call %}
|
|
3
|
-
{%- macro define_rx_context_struct(rx_callback, time_type, rx_callback_context=false, msg_as_rx_callback_arg=false, rx_signal_store=false, with_rx_valid=false) -%}
|
|
4
|
-
#ifndef
|
|
5
|
-
#define
|
|
6
|
-
struct
|
|
3
|
+
{%- macro define_rx_context_struct(rx_callback, time_type, rx_callback_context=false, msg_as_rx_callback_arg=false, rx_signal_store=false, with_rx_valid=false, ctx_name='MsgRxContext') -%}
|
|
4
|
+
#ifndef {{ ctx_name | upper }}
|
|
5
|
+
#define {{ ctx_name | upper }}
|
|
6
|
+
struct {{ ctx_name }} {
|
|
7
7
|
uint16_t ignore;
|
|
8
8
|
{%- if rx_signal_store or with_rx_valid %}
|
|
9
9
|
uint16_t valid;
|
|
@@ -26,11 +26,11 @@ struct MsgRxContext {
|
|
|
26
26
|
#endif
|
|
27
27
|
{%- endmacro %}
|
|
28
28
|
|
|
29
|
-
{% macro generate_global_rx_struct(messages, api_struct_prefix, double_buffered=False, rx_signal_store=False) -%}
|
|
29
|
+
{% macro generate_global_rx_struct(messages, api_struct_prefix, double_buffered=False, rx_signal_store=False, ctx_name='MsgRxContext') -%}
|
|
30
30
|
struct {{ api_struct_prefix }}RxSignals {
|
|
31
31
|
{%- for message in messages %}
|
|
32
32
|
{%- if message.direction == 'rx' or message.direction == 'rt' %}
|
|
33
|
-
struct
|
|
33
|
+
struct {{ ctx_name }} {{ message.name }}_context;
|
|
34
34
|
{%- if rx_signal_store %}
|
|
35
35
|
{%- if double_buffered %}
|
|
36
36
|
struct {{ message.struct_name }} {{ message.name }}_signals_storage[2];
|
|
@@ -73,7 +73,7 @@ struct {{ api_struct_prefix }}RxSignals {{ var_name }};
|
|
|
73
73
|
{{ api_name }}_process_received_frame({{ rx_signals_ptr_name }}, {{ can_frame_ptr_name }}, {{ time_var_name }})
|
|
74
74
|
{% endmacro %}
|
|
75
75
|
|
|
76
|
-
{% macro generate_global_rx_frame_process_function(messages, api_name, api_struct_prefix, can_frame_type, return_type, time_type, rx_callback, double_buffered=False, rx_callback_context=false, msg_as_rx_callback_arg=false, rx_signal_store=false, with_rx_valid=false) -%}
|
|
76
|
+
{% macro generate_global_rx_frame_process_function(messages, api_name, api_struct_prefix, can_frame_type, return_type, time_type, rx_callback, double_buffered=False, rx_callback_context=false, msg_as_rx_callback_arg=false, rx_signal_store=false, with_rx_valid=false, ctx_name='MsgRxContext') -%}
|
|
77
77
|
{{ declare_global_rx_frame_process_function(api_name, api_struct_prefix, can_frame_type, return_type, time_type) }} {
|
|
78
78
|
{%- for message in messages %}
|
|
79
79
|
{%- if message.direction == 'rx' or message.direction == 'rt' %}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
#include <string.h>
|
|
15
15
|
{%- if global_rx %}
|
|
16
16
|
|
|
17
|
-
{{ generate_global_rx_frame_process_function(messages, api_name, api_name_struct_prefix, 'CanFrame', 'uint16_t', 'uint64_t', true, double_buffered, rx_callback_context=rx_callback_context, msg_as_rx_callback_arg=msg_as_rx_callback_arg, rx_signal_store=rx_signal_store, with_rx_valid=with_rx_valid) }}
|
|
17
|
+
{{ generate_global_rx_frame_process_function(messages, api_name, api_name_struct_prefix, 'CanFrame', 'uint16_t', 'uint64_t', true, double_buffered, rx_callback_context=rx_callback_context, msg_as_rx_callback_arg=msg_as_rx_callback_arg, rx_signal_store=rx_signal_store, with_rx_valid=with_rx_valid, ctx_name=rx_context_struct_name) }}
|
|
18
18
|
{%- if double_buffered and rx_signal_store %}
|
|
19
19
|
|
|
20
20
|
{{ define_global_rx_init_function(messages, api_name, api_name_struct_prefix) }}
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
{%- endif %}
|
|
23
23
|
{%- if periodic_tx %}
|
|
24
24
|
|
|
25
|
-
{{ define_periodic_tx_function(messages, api_name, api_name_struct_prefix, 'CanFrame', 'uint16_t', 'uint64_t', 'uint16_t', true, send_now, double_buffered, tx_callback_context=tx_callback_context, msg_as_tx_callback_arg=msg_as_tx_callback_arg, tx_signal_store=tx_signal_store) }}
|
|
25
|
+
{{ define_periodic_tx_function(messages, api_name, api_name_struct_prefix, 'CanFrame', 'uint16_t', 'uint64_t', 'uint16_t', true, send_now, double_buffered, tx_callback_context=tx_callback_context, msg_as_tx_callback_arg=msg_as_tx_callback_arg, tx_signal_store=tx_signal_store, ctx_name=tx_context_struct_name) }}
|
|
26
26
|
|
|
27
|
-
{{ define_get_msg_tx_context_by_id(messages, api_name, api_name_struct_prefix, 'struct
|
|
27
|
+
{{ define_get_msg_tx_context_by_id(messages, api_name, api_name_struct_prefix, 'struct ' ~ tx_context_struct_name) }}
|
|
28
28
|
|
|
29
29
|
{{ define_periodic_tx_init_function(api_name, api_name_struct_prefix, 'uint64_t', messages, '1000', double_buffered, tx_signal_store=tx_signal_store) }}
|
|
30
30
|
{%- if double_buffered and tx_signal_store %}
|
{can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/templates/tx_macros.jinja
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{% from "encoding_macros.jinja" import message_encoding_function_call %}
|
|
2
2
|
{% from "encoding_macros.jinja" import rotate_multiplex_func_call %}
|
|
3
3
|
|
|
4
|
-
{% macro define_tx_context_struct(tx_callback, flag_type, time_type, send_now, tx_callback_context=false, msg_as_tx_callback_arg=false) -%}
|
|
5
|
-
#ifndef
|
|
6
|
-
#define
|
|
7
|
-
struct
|
|
4
|
+
{% macro define_tx_context_struct(tx_callback, flag_type, time_type, send_now, tx_callback_context=false, msg_as_tx_callback_arg=false, ctx_name='MsgTxContext') -%}
|
|
5
|
+
#ifndef {{ ctx_name | upper }}
|
|
6
|
+
#define {{ ctx_name | upper }}
|
|
7
|
+
struct {{ ctx_name }} {
|
|
8
8
|
uint32_t arb_id;
|
|
9
9
|
{{ flag_type }} enable;
|
|
10
10
|
{{ time_type }} last_tx;
|
|
@@ -58,11 +58,11 @@ static inline void {{ api_name }}_update_tx_period({{ context_type }} *txctx, ui
|
|
|
58
58
|
}
|
|
59
59
|
{%- endmacro %}
|
|
60
60
|
|
|
61
|
-
{% macro generate_global_tx_struct(messages, api_struct_prefix, double_buffered=False, tx_signal_store=False) -%}
|
|
61
|
+
{% macro generate_global_tx_struct(messages, api_struct_prefix, double_buffered=False, tx_signal_store=False, ctx_name='MsgTxContext') -%}
|
|
62
62
|
struct {{ api_struct_prefix }}TxSignals {
|
|
63
63
|
{%- for message in messages %}
|
|
64
64
|
{%- if message.direction == 'tx' or message.direction == 'rt' %}
|
|
65
|
-
struct
|
|
65
|
+
struct {{ ctx_name }} {{ message.name }}_context;
|
|
66
66
|
{%- if tx_signal_store %}
|
|
67
67
|
{%- if double_buffered %}
|
|
68
68
|
struct {{ message.struct_name }} {{ message.name }}_signals_storage[2];
|
|
@@ -93,7 +93,7 @@ void {{ api_name }}_{{ message.name }}_commit(struct {{ api_struct_prefix }}TxSi
|
|
|
93
93
|
{{ return_type }} {{ api_name }}_periodic_tx(struct {{ api_struct_prefix }}TxSignals *tx_signals, {{ can_frame_type }} *output_frame, {{ time_type }} now)
|
|
94
94
|
{%- endmacro %}
|
|
95
95
|
|
|
96
|
-
{% macro define_periodic_tx_function(messages, api_name, api_struct_prefix, can_frame_type, return_type, time_type, flag_type, tx_callback, send_now, double_buffered=False, tx_callback_context=false, msg_as_tx_callback_arg=false, tx_signal_store=false) -%}
|
|
96
|
+
{% macro define_periodic_tx_function(messages, api_name, api_struct_prefix, can_frame_type, return_type, time_type, flag_type, tx_callback, send_now, double_buffered=False, tx_callback_context=false, msg_as_tx_callback_arg=false, tx_signal_store=false, ctx_name='MsgTxContext') -%}
|
|
97
97
|
{{ declare_periodic_tx_function(api_name, api_struct_prefix, can_frame_type, return_type, time_type, flag_type) }} {
|
|
98
98
|
{%- for message in messages %}
|
|
99
99
|
{%- if message.direction == 'tx' or message.direction == 'rt' %}
|
|
@@ -24,6 +24,13 @@ def is_valid_c_identifier(s: str) -> bool:
|
|
|
24
24
|
def converrt_to_c_identifier(input_str: str) -> str:
|
|
25
25
|
return '_'.join(map(lambda s: s.lower(), input_str.split()))
|
|
26
26
|
|
|
27
|
+
def to_struct_prefix(api_name: str) -> str:
|
|
28
|
+
"""CamelCase an api/library name for use as a C struct-name prefix.
|
|
29
|
+
|
|
30
|
+
Mirrors the transform already applied to <Prefix>RxSignals / TxSignals.
|
|
31
|
+
"""
|
|
32
|
+
return ''.join(map(lambda s: s.capitalize(), api_name.split('_')))
|
|
33
|
+
|
|
27
34
|
def convert_to_cantools_signal_name(c_signal_identifier: str) -> str:
|
|
28
35
|
"""
|
|
29
36
|
Take a C identifier and adapt it to match the cantools name
|
|
@@ -622,7 +622,9 @@ def _gen_api_files(tmp_path, api_name, node="DUT", kcd="tests/test_1.kcd",
|
|
|
622
622
|
here = Path(__file__).resolve().parent
|
|
623
623
|
env = Environment(loader=FileSystemLoader(
|
|
624
624
|
[str(here.parent / "src" / "can_api_generator" / "templates")]))
|
|
625
|
-
msgs, _ = build_msgs_data_dicts_from_kcd(
|
|
625
|
+
msgs, _ = build_msgs_data_dicts_from_kcd(
|
|
626
|
+
Path(kcd), api_name, node,
|
|
627
|
+
prefix_signal_struct_names=flags.get("prefix_signal_struct_names", False))
|
|
626
628
|
header, source, canframe = generate_api_implementation(
|
|
627
629
|
msgs, env, api_name, True,
|
|
628
630
|
generate_everything=False,
|
|
@@ -635,6 +637,7 @@ def _gen_api_files(tmp_path, api_name, node="DUT", kcd="tests/test_1.kcd",
|
|
|
635
637
|
tx_signal_store=flags.get("tx_signal_store", False),
|
|
636
638
|
msg_as_tx_callback_arg=flags.get("msg_as_tx_callback_arg", False),
|
|
637
639
|
tx_callback_context=flags.get("tx_callback_context", False),
|
|
640
|
+
prefix_context_struct_names=flags.get("prefix_context_struct_names", False),
|
|
638
641
|
)
|
|
639
642
|
(tmp_path / f"{api_name}.h").write_text(header)
|
|
640
643
|
(tmp_path / f"{api_name}.c").write_text(source)
|
|
@@ -661,20 +664,152 @@ def test_callback_only_rx_zeroes_decode_temp(tmp_path):
|
|
|
661
664
|
assert decl < memz < decode, "decode temp must be memset before decode"
|
|
662
665
|
|
|
663
666
|
|
|
664
|
-
def
|
|
665
|
-
"""
|
|
666
|
-
in one translation unit without redefining the shared message structs."""
|
|
667
|
-
import os
|
|
668
|
-
import subprocess
|
|
667
|
+
def test_signal_struct_has_no_include_guard(tmp_path):
|
|
668
|
+
"""The per-message signal struct is emitted bare (no STRUCT_ guard)."""
|
|
669
669
|
header, _ = _gen_api_files(tmp_path, "prod", global_rx=True,
|
|
670
670
|
periodic_tx=True, rx_signal_store=True,
|
|
671
671
|
tx_signal_store=True)
|
|
672
|
+
assert "struct Message1 {" in header
|
|
673
|
+
assert "STRUCT_MESSAGE1" not in header
|
|
674
|
+
assert "#ifndef STRUCT_" not in header
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
def test_prefix_signal_struct_names_prefixes_struct_type(tmp_path):
|
|
678
|
+
header, source = _gen_api_files(
|
|
679
|
+
tmp_path, "prod", global_rx=True, periodic_tx=True,
|
|
680
|
+
rx_signal_store=True, tx_signal_store=True,
|
|
681
|
+
prefix_signal_struct_names=True)
|
|
682
|
+
# struct definition and RX/TX storage members use the prefixed type
|
|
683
|
+
assert "struct ProdMessage1 {" in header
|
|
684
|
+
assert "struct ProdMessage2 {" in header
|
|
685
|
+
assert "struct ProdMessage2 message_2_signals;" in header
|
|
686
|
+
# encode/decode signatures use the prefixed type
|
|
687
|
+
assert "struct ProdMessage2 *msg" in source or "struct ProdMessage2 * msg" in source
|
|
688
|
+
# function names are NOT prefixed with the struct prefix (still api_prefix)
|
|
689
|
+
assert "prod_message_2_decode" in source
|
|
690
|
+
assert "struct Message1 {" not in header
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
def test_prefix_signal_struct_names_absent_by_default(tmp_path):
|
|
694
|
+
header, _ = _gen_api_files(
|
|
695
|
+
tmp_path, "prod", global_rx=True, periodic_tx=True,
|
|
696
|
+
rx_signal_store=True, tx_signal_store=True)
|
|
697
|
+
assert "struct Message1 {" in header
|
|
698
|
+
assert "ProdMessage1" not in header
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def test_prefix_signal_struct_names_cli_flag(tmp_path):
|
|
702
|
+
runner = CliRunner()
|
|
703
|
+
lib_base = tmp_path / "prod"
|
|
704
|
+
result = runner.invoke(main, [
|
|
705
|
+
"api", "tests/test_1.kcd", str(lib_base), "DUT",
|
|
706
|
+
"-r", "-p", "--rx-signal-store", "--tx-signal-store",
|
|
707
|
+
"--prefix-signal-struct-names",
|
|
708
|
+
])
|
|
709
|
+
assert result.exit_code == 0, result.output
|
|
710
|
+
header = (tmp_path / "prod.h").read_text()
|
|
711
|
+
# The CLI passes the (tmp) library path as api_name, so the exact prefix is
|
|
712
|
+
# path-derived; assert the flag applied a prefix rather than a specific one.
|
|
713
|
+
assert "Message1 {" in header # prefixed struct still ends in Message1
|
|
714
|
+
assert "struct Message1 {" not in header # ...but not the bare name
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
def test_prefix_context_struct_names_prefixes_both_contexts(tmp_path):
|
|
718
|
+
header, source = _gen_api_files(
|
|
719
|
+
tmp_path, "prod", global_rx=True, periodic_tx=True,
|
|
720
|
+
rx_signal_store=True, tx_signal_store=True,
|
|
721
|
+
prefix_context_struct_names=True)
|
|
722
|
+
# struct definitions + guard tokens follow the prefixed names
|
|
723
|
+
assert "struct ProdMsgRxContext {" in header
|
|
724
|
+
assert "struct ProdMsgTxContext {" in header
|
|
725
|
+
assert "#ifndef PRODMSGRXCONTEXT" in header
|
|
726
|
+
assert "#ifndef PRODMSGTXCONTEXT" in header
|
|
727
|
+
# members and signatures use the prefixed context types
|
|
728
|
+
assert "struct ProdMsgRxContext message_2_context;" in header
|
|
729
|
+
assert "struct ProdMsgTxContext message_1_context;" in header
|
|
730
|
+
assert "struct ProdMsgTxContext *prod_get_msg_tx_context_by_id" in header
|
|
731
|
+
assert "struct MsgRxContext {" not in header
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
def test_prefix_context_struct_names_absent_by_default(tmp_path):
|
|
735
|
+
header, _ = _gen_api_files(
|
|
736
|
+
tmp_path, "prod", global_rx=True, periodic_tx=True,
|
|
737
|
+
rx_signal_store=True, tx_signal_store=True)
|
|
738
|
+
assert "struct MsgRxContext {" in header
|
|
739
|
+
assert "struct MsgTxContext {" in header
|
|
740
|
+
assert "#ifndef MSGRXCONTEXT" in header
|
|
741
|
+
assert "#ifndef MSGTXCONTEXT" in header
|
|
742
|
+
assert "ProdMsgRxContext" not in header
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
def test_prefix_context_struct_names_cli_flag(tmp_path):
|
|
746
|
+
runner = CliRunner()
|
|
747
|
+
lib_base = tmp_path / "prod"
|
|
748
|
+
result = runner.invoke(main, [
|
|
749
|
+
"api", "tests/test_1.kcd", str(lib_base), "DUT",
|
|
750
|
+
"-r", "-p", "--rx-signal-store", "--tx-signal-store",
|
|
751
|
+
"--prefix-context-struct-names",
|
|
752
|
+
])
|
|
753
|
+
assert result.exit_code == 0, result.output
|
|
754
|
+
header = (tmp_path / "prod.h").read_text()
|
|
755
|
+
# api_name is the tmp path, so assert a prefix was applied, not a specific one.
|
|
756
|
+
assert "MsgRxContext {" in header
|
|
757
|
+
assert "struct MsgRxContext {" not in header
|
|
758
|
+
assert "MsgTxContext {" in header
|
|
759
|
+
assert "struct MsgTxContext {" not in header
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
# These compile checks pin -std=c11: the generator targets pre-C23 / embedded
|
|
763
|
+
# compilers (e.g. TI C2000), where two same-tag struct definitions in one TU are
|
|
764
|
+
# a redefinition error. C23 (the host gcc default) legalized identical same-tag
|
|
765
|
+
# redefinitions, which would mask the collision the prefixes exist to prevent.
|
|
766
|
+
def test_two_prefixed_builds_coexist_in_one_tu(tmp_path):
|
|
767
|
+
"""Two APIs from one KCD, different names, both prefix flags set, compile
|
|
768
|
+
together in one translation unit with no redefinition."""
|
|
769
|
+
import os
|
|
770
|
+
import subprocess
|
|
771
|
+
_gen_api_files(tmp_path, "prod", global_rx=True, periodic_tx=True,
|
|
772
|
+
rx_signal_store=True, tx_signal_store=True,
|
|
773
|
+
prefix_signal_struct_names=True,
|
|
774
|
+
prefix_context_struct_names=True)
|
|
775
|
+
_gen_api_files(tmp_path, "cons", global_rx=True, periodic_tx=True,
|
|
776
|
+
rx_signal_store=True, tx_signal_store=True,
|
|
777
|
+
prefix_signal_struct_names=True,
|
|
778
|
+
prefix_context_struct_names=True)
|
|
779
|
+
(tmp_path / "both.c").write_text(
|
|
780
|
+
'#include "prod.h"\n#include "cons.h"\nint main(void) { return 0; }\n')
|
|
781
|
+
c = subprocess.run(["gcc", "-std=c11", "-c", "-o", os.devnull, "both.c"],
|
|
782
|
+
cwd=tmp_path, capture_output=True, text=True)
|
|
783
|
+
assert c.returncode == 0, c.stderr
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
def test_unprefixed_signal_structs_collide_in_one_tu(tmp_path):
|
|
787
|
+
"""Guard removal is intentional: without --prefix-signal-struct-names, two
|
|
788
|
+
headers that share a message name no longer coexist in one TU."""
|
|
789
|
+
import os
|
|
790
|
+
import subprocess
|
|
791
|
+
_gen_api_files(tmp_path, "prod", global_rx=True, periodic_tx=True,
|
|
792
|
+
rx_signal_store=True, tx_signal_store=True)
|
|
672
793
|
_gen_api_files(tmp_path, "cons", global_rx=True, periodic_tx=True,
|
|
673
794
|
rx_signal_store=True, tx_signal_store=True)
|
|
674
|
-
assert "#ifndef STRUCT_MESSAGE1" in header
|
|
675
|
-
assert "#define STRUCT_MESSAGE1" in header
|
|
676
795
|
(tmp_path / "both.c").write_text(
|
|
677
796
|
'#include "prod.h"\n#include "cons.h"\nint main(void) { return 0; }\n')
|
|
678
|
-
c = subprocess.run(["gcc", "-c", "-o", os.devnull, "both.c"],
|
|
797
|
+
c = subprocess.run(["gcc", "-std=c11", "-c", "-o", os.devnull, "both.c"],
|
|
798
|
+
cwd=tmp_path, capture_output=True, text=True)
|
|
799
|
+
assert c.returncode != 0
|
|
800
|
+
assert "redefinition" in c.stderr and "Message1" in c.stderr
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
def test_prefix_flags_do_not_change_layout_callback_only(tmp_path):
|
|
804
|
+
"""Prefix flags only rename types: a callback-only RX/TX build still
|
|
805
|
+
compiles and keeps the same field-access shape."""
|
|
806
|
+
import os
|
|
807
|
+
import subprocess
|
|
808
|
+
_gen_api_files(tmp_path, "prod", global_rx=True, periodic_tx=True,
|
|
809
|
+
prefix_signal_struct_names=True,
|
|
810
|
+
prefix_context_struct_names=True)
|
|
811
|
+
(tmp_path / "u.c").write_text(
|
|
812
|
+
'#include "prod.h"\nint main(void) { return 0; }\n')
|
|
813
|
+
c = subprocess.run(["gcc", "-std=c11", "-c", "-o", os.devnull, "u.c"],
|
|
679
814
|
cwd=tmp_path, capture_output=True, text=True)
|
|
680
815
|
assert c.returncode == 0, c.stderr
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from src.can_api_generator.utils import to_struct_prefix
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_to_struct_prefix_camelcases_underscore_parts():
|
|
5
|
+
assert to_struct_prefix("battery_v2") == "BatteryV2"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_to_struct_prefix_single_token():
|
|
9
|
+
assert to_struct_prefix("prod") == "Prod"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_to_struct_prefix_matches_legacy_inline_transform():
|
|
13
|
+
api_name = "dut_can_a"
|
|
14
|
+
legacy = "".join(map(lambda s: s.capitalize(), api_name.split("_")))
|
|
15
|
+
assert to_struct_prefix(api_name) == legacy
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{can_api_generator-2.0.0 → can_api_generator-2.1.0}/src/can_api_generator/templates/canframe.jinja
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_mux_group_count_out_of_order.kcd
RENAMED
|
File without changes
|
|
File without changes
|
{can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_mux_with_partially_labled_index.kcd
RENAMED
|
File without changes
|
{can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_mux_with_same_signal_names.kcd
RENAMED
|
File without changes
|
|
File without changes
|
{can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_mux_with_scale_and_offset.kcd
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/double_plain.kcd
RENAMED
|
File without changes
|
{can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/double_scaled.kcd
RENAMED
|
File without changes
|
|
File without changes
|
{can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/float_plain.kcd
RENAMED
|
File without changes
|
{can_api_generator-2.0.0 → can_api_generator-2.1.0}/tests/test_signal_encoding/float_scaled.kcd
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|