Skip to content

Commit 15488be

Browse files
57300kartben
authored andcommitted
scripts: dts: Refactor gen_driver_kconfig_dts
The motivation for this patch was to improve the script's performance, but some stylistic changes and cleanups are included as well. The main optimization concerns the use of PyYAML, as it offers multiple functions for interpreting YAML. The commonly used `load`/`safe_load` converts a YAML stream to a dictionary. There are also `scan`, `parse`, and `compose`, which return intermediate representations, the last one being a graph. [1] Since `gen_driver_kconfig_dts` scans DT bindings for compatible strings, it only needs to look through top level keys in YAML. The intermediate PyYAML graph is sufficient for this, and using it reduces the script's execution time by about 30%, without making the code too complicated. [1] - https://pyyaml.org/wiki/PyYAMLDocumentation Signed-off-by: Grzegorz Swiderski <[email protected]>
1 parent d46546e commit 15488be

File tree

1 file changed

+37
-45
lines changed

1 file changed

+37
-45
lines changed

scripts/dts/gen_driver_kconfig_dts.py

Lines changed: 37 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
#!/usr/bin/env python3
22
#
33
# Copyright (c) 2022 Kumar Gala <[email protected]>
4+
# Copyright (c) 2025 Nordic Semiconductor ASA
45
#
56
# SPDX-License-Identifier: Apache-2.0
67

78
import argparse
89
import os
9-
import sys
10-
import re
1110

1211
import yaml
1312
try:
@@ -16,22 +15,33 @@
1615
except ImportError:
1716
from yaml import SafeLoader # type: ignore
1817

19-
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'python-devicetree',
20-
'src'))
2118

22-
def binding_paths(bindings_dirs):
23-
# Returns a list with the paths to all bindings (.yaml files) in
24-
# 'bindings_dirs'
19+
HEADER = """\
20+
# Generated devicetree Kconfig
21+
#
22+
# SPDX-License-Identifier: Apache-2.0"""
23+
24+
25+
KCONFIG_TEMPLATE = """
26+
DT_COMPAT_{COMPAT} := {compat}
27+
28+
config DT_HAS_{COMPAT}_ENABLED
29+
\tdef_bool $(dt_compat_enabled,$(DT_COMPAT_{COMPAT}))"""
2530

26-
binding_paths = []
31+
32+
# Character translation table used to derive Kconfig symbol names
33+
TO_UNDERSCORES = str.maketrans("-,.@/+", "______")
34+
35+
36+
def binding_paths(bindings_dirs):
37+
# Yields paths to all bindings (.yaml files) in 'bindings_dirs'
2738

2839
for bindings_dir in bindings_dirs:
2940
for root, _, filenames in os.walk(bindings_dir):
3041
for filename in filenames:
31-
if filename.endswith(".yaml") or filename.endswith(".yml"):
32-
binding_paths.append(os.path.join(root, filename))
42+
if filename.endswith((".yaml", ".yml")):
43+
yield os.path.join(root, filename)
3344

34-
return binding_paths
3545

3646
def parse_args():
3747
# Returns parsed command-line arguments
@@ -45,56 +55,38 @@ def parse_args():
4555

4656
return parser.parse_args()
4757

48-
def printfile(s):
49-
print(s, file=kconfig_file)
50-
51-
def str2ident(s):
52-
# Converts 's' to a form suitable for (part of) an identifier
53-
54-
return re.sub('[-,.@/+]', '_', s.upper())
55-
56-
def compat2kconfig(compat):
57-
compat_ident = str2ident(compat)
58-
59-
printfile(f'')
60-
printfile(f'DT_COMPAT_{compat_ident} := {compat}')
61-
printfile(f'')
62-
printfile(f'config DT_HAS_{compat_ident}_ENABLED')
63-
printfile(f'\tdef_bool $(dt_compat_enabled,$(DT_COMPAT_{compat_ident}))')
6458

6559
def main():
66-
global kconfig_file
6760
args = parse_args()
6861

69-
compat_list = []
62+
compats = set()
7063

7164
for binding_path in binding_paths(args.bindings_dirs):
7265
with open(binding_path, encoding="utf-8") as f:
73-
contents = f.read()
74-
7566
try:
76-
# Parsed PyYAML output (Python lists/dictionaries/strings/etc.,
77-
# representing the file)
78-
raw = yaml.load(contents, Loader=SafeLoader)
67+
# Parsed PyYAML representation graph. For our purpose,
68+
# we don't need the whole file converted into a dict.
69+
root = yaml.compose(f, Loader=SafeLoader)
7970
except yaml.YAMLError as e:
8071
print(f"WARNING: '{binding_path}' appears in binding "
8172
f"directories but isn't valid YAML: {e}")
8273
continue
83-
if raw is None or 'compatible' not in raw:
84-
continue
8574

86-
compat_list.append(raw['compatible'])
87-
88-
# Remove any duplicates and sort the list
89-
compat_list = sorted(set(compat_list))
75+
if not isinstance(root, yaml.MappingNode):
76+
continue
77+
for key, node in root.value:
78+
if key.value == "compatible" and isinstance(node, yaml.ScalarNode):
79+
compats.add(node.value)
80+
break
9081

9182
with open(args.kconfig_out, "w", encoding="utf-8") as kconfig_file:
92-
printfile(f'# Generated devicetree Kconfig')
93-
printfile(f'#')
94-
printfile(f'# SPDX-License-Identifier: Apache-2.0')
83+
print(HEADER, file=kconfig_file)
9584

96-
for c in compat_list:
97-
compat2kconfig(c)
85+
for c in sorted(compats):
86+
out = KCONFIG_TEMPLATE.format(
87+
compat=c, COMPAT=c.upper().translate(TO_UNDERSCORES)
88+
)
89+
print(out, file=kconfig_file)
9890

9991

10092
if __name__ == "__main__":

0 commit comments

Comments
 (0)