1#!/usr/bin/env python3
2#
3# Copyright (c) 2022 Kumar Gala <galak@kernel.org>
4# Copyright (c) 2025 Nordic Semiconductor ASA
5#
6# SPDX-License-Identifier: Apache-2.0
7
8import argparse
9import os
10
11import yaml
12try:
13    # Use the C LibYAML parser if available, rather than the Python parser.
14    from yaml import CSafeLoader as SafeLoader
15except ImportError:
16    from yaml import SafeLoader     # type: ignore
17
18
19HEADER = """\
20# Generated devicetree Kconfig
21#
22# SPDX-License-Identifier: Apache-2.0"""
23
24
25KCONFIG_TEMPLATE = """
26DT_COMPAT_{COMPAT} := {compat}
27
28config DT_HAS_{COMPAT}_ENABLED
29\tdef_bool $(dt_compat_enabled,$(DT_COMPAT_{COMPAT}))"""
30
31
32# Character translation table used to derive Kconfig symbol names
33TO_UNDERSCORES = str.maketrans("-,.@/+", "______")
34
35
36def binding_paths(bindings_dirs):
37    # Yields paths to all bindings (.yaml files) in 'bindings_dirs'
38
39    for bindings_dir in bindings_dirs:
40        for root, _, filenames in os.walk(bindings_dir):
41            for filename in filenames:
42                if filename.endswith((".yaml", ".yml")):
43                    yield os.path.join(root, filename)
44
45
46def parse_args():
47    # Returns parsed command-line arguments
48
49    parser = argparse.ArgumentParser(allow_abbrev=False)
50    parser.add_argument("--kconfig-out", required=True,
51                        help="path to write the Kconfig file")
52    parser.add_argument("--bindings-dirs", nargs='+', required=True,
53                        help="directory with bindings in YAML format, "
54                        "we allow multiple")
55
56    return parser.parse_args()
57
58
59def main():
60    args = parse_args()
61
62    compats = set()
63
64    for binding_path in binding_paths(args.bindings_dirs):
65        with open(binding_path, encoding="utf-8") as f:
66            try:
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)
70            except yaml.YAMLError as e:
71                print(f"WARNING: '{binding_path}' appears in binding "
72                      f"directories but isn't valid YAML: {e}")
73                continue
74
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
81
82    with open(args.kconfig_out, "w", encoding="utf-8") as kconfig_file:
83        print(HEADER, file=kconfig_file)
84
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)
90
91
92if __name__ == "__main__":
93    main()
94