Skip to content

Commit fe3287a

Browse files
benediktibkmmahadevan108
authored andcommitted
scripts: dts: extract pickled EDT generation
Separate the pickled EDT generation from the C-Macro header generation in gen_defines.py to have a more clear responsibility of the scripts in the DTS parsing process. Signed-off-by: Benedikt Schmidt <benedikt.schmidt@embedded-solutions.at>
1 parent a575c76 commit fe3287a

File tree

7 files changed

+196
-100
lines changed

7 files changed

+196
-100
lines changed

cmake/modules/dts.cmake

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ find_package(Dtc 1.4.6)
9494
# The directory containing devicetree related scripts.
9595
set(DT_SCRIPTS ${ZEPHYR_BASE}/scripts/dts)
9696

97+
# This parses and collects the DT information
98+
set(GEN_EDT_SCRIPT ${DT_SCRIPTS}/gen_edt.py)
9799
# This generates DT information needed by the C macro APIs,
98100
# along with a few other things.
99101
set(GEN_DEFINES_SCRIPT ${DT_SCRIPTS}/gen_defines.py)
@@ -216,7 +218,7 @@ foreach(dts_root ${DTS_ROOT})
216218

217219
set(vendor_prefixes ${dts_root}/${VENDOR_PREFIXES})
218220
if(EXISTS ${vendor_prefixes})
219-
list(APPEND EXTRA_GEN_DEFINES_ARGS --vendor-prefixes ${vendor_prefixes})
221+
list(APPEND EXTRA_GEN_EDT_ARGS --vendor-prefixes ${vendor_prefixes})
220222
endif()
221223
endforeach()
222224

@@ -266,23 +268,44 @@ toolchain_parse_make_rule(${DTS_DEPS}
266268
set_property(DIRECTORY APPEND PROPERTY
267269
CMAKE_CONFIGURE_DEPENDS
268270
${DTS_INCLUDE_FILES}
271+
${GEN_EDT_SCRIPT}
269272
${GEN_DEFINES_SCRIPT}
270273
${GEN_DRIVER_KCONFIG_SCRIPT}
271274
${GEN_DTS_CMAKE_SCRIPT}
272275
)
273276

274277
#
275-
# Run GEN_DEFINES_SCRIPT.
278+
# Run GEN_EDT_SCRIPT.
276279
#
277280

278281
string(REPLACE ";" " " EXTRA_DTC_FLAGS_RAW "${EXTRA_DTC_FLAGS}")
279-
set(CMD_GEN_DEFINES ${PYTHON_EXECUTABLE} ${GEN_DEFINES_SCRIPT}
282+
set(CMD_GEN_EDT ${PYTHON_EXECUTABLE} ${GEN_EDT_SCRIPT}
280283
--dts ${DTS_POST_CPP}
281284
--dtc-flags '${EXTRA_DTC_FLAGS_RAW}'
282285
--bindings-dirs ${DTS_ROOT_BINDINGS}
283-
--header-out ${DEVICETREE_GENERATED_H}.new
284286
--dts-out ${ZEPHYR_DTS}.new # for debugging and dtc
285-
--edt-pickle-out ${EDT_PICKLE}
287+
--edt-pickle-out ${EDT_PICKLE}.new
288+
${EXTRA_GEN_EDT_ARGS}
289+
)
290+
291+
execute_process(
292+
COMMAND ${CMD_GEN_EDT}
293+
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
294+
COMMAND_ERROR_IS_FATAL ANY
295+
)
296+
zephyr_file_copy(${ZEPHYR_DTS}.new ${ZEPHYR_DTS} ONLY_IF_DIFFERENT)
297+
zephyr_file_copy(${EDT_PICKLE}.new ${EDT_PICKLE} ONLY_IF_DIFFERENT)
298+
file(REMOVE ${ZEPHYR_DTS}.new ${EDT_PICKLE}.new)
299+
message(STATUS "Generated zephyr.dts: ${ZEPHYR_DTS}")
300+
message(STATUS "Generated pickled edt: ${EDT_PICKLE}")
301+
302+
#
303+
# Run GEN_DEFINES_SCRIPT.
304+
#
305+
306+
set(CMD_GEN_DEFINES ${PYTHON_EXECUTABLE} ${GEN_DEFINES_SCRIPT}
307+
--header-out ${DEVICETREE_GENERATED_H}.new
308+
--edt-pickle ${EDT_PICKLE}
286309
${EXTRA_GEN_DEFINES_ARGS}
287310
)
288311

@@ -291,7 +314,6 @@ execute_process(
291314
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
292315
COMMAND_ERROR_IS_FATAL ANY
293316
)
294-
zephyr_file_copy(${ZEPHYR_DTS}.new ${ZEPHYR_DTS} ONLY_IF_DIFFERENT)
295317
zephyr_file_copy(${DEVICETREE_GENERATED_H}.new ${DEVICETREE_GENERATED_H} ONLY_IF_DIFFERENT)
296318
file(REMOVE ${ZEPHYR_DTS}.new ${DEVICETREE_GENERATED_H}.new)
297319
message(STATUS "Generated zephyr.dts: ${ZEPHYR_DTS}")

doc/releases/release-notes-4.0.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,17 @@ Build system and Infrastructure
133133

134134
* Added support for .elf files to the west flash command for jlink, pyocd and linkserver runners.
135135

136+
* Extracted pickled EDT generation from gen_defines.py into gen_edt.py. This moved the following
137+
parameters from the cmake variable ``EXTRA_GEN_DEFINES_ARGS`` to ``EXTRA_GEN_EDT_ARGS``:
138+
139+
* ``--dts``
140+
* ``--dtc-flags``
141+
* ``--bindings-dirs``
142+
* ``--dts-out``
143+
* ``--edt-pickle-out``
144+
* ``--vendor-prefixes``
145+
* ``--edtlib-Werror``
146+
136147
Documentation
137148
*************
138149

scripts/dts/edtlib_logger.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright (c) 2019 - 2020 Nordic Semiconductor ASA
4+
# Copyright (c) 2019 Linaro Limited
5+
# Copyright (c) 2024 SILA Embedded Solutions GmbH
6+
7+
import logging
8+
import sys
9+
10+
11+
class LogFormatter(logging.Formatter):
12+
'''A log formatter that prints the level name in lower case,
13+
for compatibility with earlier versions of edtlib.'''
14+
15+
def __init__(self):
16+
super().__init__(fmt='%(levelnamelower)s: %(message)s')
17+
18+
def format(self, record):
19+
record.levelnamelower = record.levelname.lower()
20+
return super().format(record)
21+
22+
23+
def setup_edtlib_logging() -> None:
24+
# The edtlib module emits logs using the standard 'logging' module.
25+
# Configure it so that warnings and above are printed to stderr,
26+
# using the LogFormatter class defined above to format each message.
27+
28+
handler = logging.StreamHandler(sys.stderr)
29+
handler.setFormatter(LogFormatter())
30+
31+
logger = logging.getLogger('edtlib')
32+
logger.setLevel(logging.WARNING)
33+
logger.addHandler(handler)

scripts/dts/gen_defines.py

Lines changed: 9 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,11 @@
22

33
# Copyright (c) 2019 - 2020 Nordic Semiconductor ASA
44
# Copyright (c) 2019 Linaro Limited
5+
# Copyright (c) 2024 SILA Embedded Solutions GmbH
56
# SPDX-License-Identifier: BSD-3-Clause
67

7-
# This script uses edtlib to generate a header file from a devicetree
8-
# (.dts) file. Information from binding files in YAML format is used
9-
# as well.
10-
#
11-
# Bindings are files that describe devicetree nodes. Devicetree nodes are
12-
# usually mapped to bindings via their 'compatible = "..."' property.
13-
#
14-
# See Zephyr's Devicetree user guide for details.
8+
# This script uses edtlib to generate a header file from a pickled
9+
# edt file.
1510
#
1611
# Note: Do not access private (_-prefixed) identifiers from edtlib here (and
1712
# also note that edtlib is not meant to expose the dtlib API directly).
@@ -20,7 +15,6 @@
2015

2116
import argparse
2217
from collections import defaultdict
23-
import logging
2418
import os
2519
import pathlib
2620
import pickle
@@ -31,49 +25,23 @@
3125
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'python-devicetree',
3226
'src'))
3327

28+
import edtlib_logger
3429
from devicetree import edtlib
3530

36-
class LogFormatter(logging.Formatter):
37-
'''A log formatter that prints the level name in lower case,
38-
for compatibility with earlier versions of edtlib.'''
39-
40-
def __init__(self):
41-
super().__init__(fmt='%(levelnamelower)s: %(message)s')
42-
43-
def format(self, record):
44-
record.levelnamelower = record.levelname.lower()
45-
return super().format(record)
4631

4732
def main():
4833
global header_file
4934
global flash_area_num
5035

5136
args = parse_args()
5237

53-
setup_edtlib_logging()
38+
edtlib_logger.setup_edtlib_logging()
5439

55-
vendor_prefixes = {}
56-
for prefixes_file in args.vendor_prefixes:
57-
vendor_prefixes.update(edtlib.load_vendor_prefixes_txt(prefixes_file))
58-
59-
try:
60-
edt = edtlib.EDT(args.dts, args.bindings_dirs,
61-
# Suppress this warning if it's suppressed in dtc
62-
warn_reg_unit_address_mismatch=
63-
"-Wno-simple_bus_reg" not in args.dtc_flags,
64-
default_prop_types=True,
65-
infer_binding_for_paths=["/zephyr,user"],
66-
werror=args.edtlib_Werror,
67-
vendor_prefixes=vendor_prefixes)
68-
except edtlib.EDTError as e:
69-
sys.exit(f"devicetree error: {e}")
40+
with open(args.edt_pickle, 'rb') as f:
41+
edt = pickle.load(f)
7042

7143
flash_area_num = 0
7244

73-
# Save merged DTS source, as a debugging aid
74-
with open(args.dts_out, "w", encoding="utf-8") as f:
75-
print(edt.dts_source, file=f)
76-
7745
# Create the generated header.
7846
with open(args.header_out, "w", encoding="utf-8") as header_file:
7947
write_top_comment(edt)
@@ -133,22 +101,6 @@ def main():
133101
write_chosen(edt)
134102
write_global_macros(edt)
135103

136-
if args.edt_pickle_out:
137-
write_pickled_edt(edt, args.edt_pickle_out)
138-
139-
140-
def setup_edtlib_logging() -> None:
141-
# The edtlib module emits logs using the standard 'logging' module.
142-
# Configure it so that warnings and above are printed to stderr,
143-
# using the LogFormatter class defined above to format each message.
144-
145-
handler = logging.StreamHandler(sys.stderr)
146-
handler.setFormatter(LogFormatter())
147-
148-
logger = logging.getLogger('edtlib')
149-
logger.setLevel(logging.WARNING)
150-
logger.addHandler(handler)
151-
152104

153105
def node_z_path_id(node: edtlib.Node) -> str:
154106
# Return the node specific bit of the node's path identifier:
@@ -173,27 +125,10 @@ def parse_args() -> argparse.Namespace:
173125
# Returns parsed command-line arguments
174126

175127
parser = argparse.ArgumentParser(allow_abbrev=False)
176-
parser.add_argument("--dts", required=True, help="DTS file")
177-
parser.add_argument("--dtc-flags",
178-
help="'dtc' devicetree compiler flags, some of which "
179-
"might be respected here")
180-
parser.add_argument("--bindings-dirs", nargs='+', required=True,
181-
help="directory with bindings in YAML format, "
182-
"we allow multiple")
183128
parser.add_argument("--header-out", required=True,
184129
help="path to write header to")
185-
parser.add_argument("--dts-out", required=True,
186-
help="path to write merged DTS source code to (e.g. "
187-
"as a debugging aid)")
188-
parser.add_argument("--edt-pickle-out",
189-
help="path to write pickled edtlib.EDT object to")
190-
parser.add_argument("--vendor-prefixes", action='append', default=[],
191-
help="vendor-prefixes.txt path; used for validation; "
192-
"may be given multiple times")
193-
parser.add_argument("--edtlib-Werror", action="store_true",
194-
help="if set, edtlib-specific warnings become errors. "
195-
"(this does not apply to warnings shared "
196-
"with dtc.)")
130+
parser.add_argument("--edt-pickle",
131+
help="path to read pickled edtlib.EDT object from")
197132

198133
return parser.parse_args()
199134

@@ -1099,21 +1034,6 @@ def quote_str(s: str) -> str:
10991034
return f'"{escape(s)}"'
11001035

11011036

1102-
def write_pickled_edt(edt: edtlib.EDT, out_file: str) -> None:
1103-
# Writes the edt object in pickle format to out_file.
1104-
1105-
with open(out_file, 'wb') as f:
1106-
# Pickle protocol version 4 is the default as of Python 3.8
1107-
# and was introduced in 3.4, so it is both available and
1108-
# recommended on all versions of Python that Zephyr supports
1109-
# (at time of writing, Python 3.6 was Zephyr's minimum
1110-
# version, and 3.8 the most recent CPython release).
1111-
#
1112-
# Using a common protocol version here will hopefully avoid
1113-
# reproducibility issues in different Python installations.
1114-
pickle.dump(edt, f, protocol=4)
1115-
1116-
11171037
def err(s: str) -> NoReturn:
11181038
raise Exception(s)
11191039

scripts/dts/gen_edt.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright (c) 2019 - 2020 Nordic Semiconductor ASA
4+
# Copyright (c) 2019 Linaro Limited
5+
# Copyright (c) 2024 SILA Embedded Solutions GmbH
6+
# SPDX-License-Identifier: Apache-2.0
7+
8+
# This script uses edtlib to generate a pickled edt from a devicetree
9+
# (.dts) file. Information from binding files in YAML format is used
10+
# as well.
11+
#
12+
# Bindings are files that describe devicetree nodes. Devicetree nodes are
13+
# usually mapped to bindings via their 'compatible = "..."' property.
14+
#
15+
# See Zephyr's Devicetree user guide for details.
16+
#
17+
# Note: Do not access private (_-prefixed) identifiers from edtlib here (and
18+
# also note that edtlib is not meant to expose the dtlib API directly).
19+
# Instead, think of what API you need, and add it as a public documented API in
20+
# edtlib. This will keep this script simple.
21+
22+
import argparse
23+
import os
24+
import pickle
25+
import sys
26+
from typing import NoReturn
27+
28+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'python-devicetree',
29+
'src'))
30+
31+
import edtlib_logger
32+
from devicetree import edtlib
33+
34+
35+
def main():
36+
args = parse_args()
37+
38+
edtlib_logger.setup_edtlib_logging()
39+
40+
vendor_prefixes = {}
41+
for prefixes_file in args.vendor_prefixes:
42+
vendor_prefixes.update(edtlib.load_vendor_prefixes_txt(prefixes_file))
43+
44+
try:
45+
edt = edtlib.EDT(args.dts, args.bindings_dirs,
46+
# Suppress this warning if it's suppressed in dtc
47+
warn_reg_unit_address_mismatch=
48+
"-Wno-simple_bus_reg" not in args.dtc_flags,
49+
default_prop_types=True,
50+
infer_binding_for_paths=["/zephyr,user"],
51+
werror=args.edtlib_Werror,
52+
vendor_prefixes=vendor_prefixes)
53+
except edtlib.EDTError as e:
54+
sys.exit(f"devicetree error: {e}")
55+
56+
# Save merged DTS source, as a debugging aid
57+
with open(args.dts_out, "w", encoding="utf-8") as f:
58+
print(edt.dts_source, file=f)
59+
60+
write_pickled_edt(edt, args.edt_pickle_out)
61+
62+
63+
def parse_args() -> argparse.Namespace:
64+
# Returns parsed command-line arguments
65+
66+
parser = argparse.ArgumentParser(allow_abbrev=False)
67+
parser.add_argument("--dts", required=True, help="DTS file")
68+
parser.add_argument("--dtc-flags",
69+
help="'dtc' devicetree compiler flags, some of which "
70+
"might be respected here")
71+
parser.add_argument("--bindings-dirs", nargs='+', required=True,
72+
help="directory with bindings in YAML format, "
73+
"we allow multiple")
74+
parser.add_argument("--dts-out", required=True,
75+
help="path to write merged DTS source code to (e.g. "
76+
"as a debugging aid)")
77+
parser.add_argument("--edt-pickle-out",
78+
help="path to write pickled edtlib.EDT object to", required=True)
79+
parser.add_argument("--vendor-prefixes", action='append', default=[],
80+
help="vendor-prefixes.txt path; used for validation; "
81+
"may be given multiple times")
82+
parser.add_argument("--edtlib-Werror", action="store_true",
83+
help="if set, edtlib-specific warnings become errors. "
84+
"(this does not apply to warnings shared "
85+
"with dtc.)")
86+
87+
return parser.parse_args()
88+
89+
90+
def write_pickled_edt(edt: edtlib.EDT, out_file: str) -> None:
91+
# Writes the edt object in pickle format to out_file.
92+
93+
with open(out_file, 'wb') as f:
94+
# Pickle protocol version 4 is the default as of Python 3.8
95+
# and was introduced in 3.4, so it is both available and
96+
# recommended on all versions of Python that Zephyr supports
97+
# (at time of writing, Python 3.6 was Zephyr's minimum
98+
# version, and 3.10 the most recent CPython release).
99+
#
100+
# Using a common protocol version here will hopefully avoid
101+
# reproducibility issues in different Python installations.
102+
pickle.dump(edt, f, protocol=4)
103+
104+
105+
def err(s: str) -> NoReturn:
106+
raise Exception(s)
107+
108+
109+
if __name__ == "__main__":
110+
main()

0 commit comments

Comments
 (0)