1"""
2Utility to autogenerate generic LL HAL headers.
3
4Usage::
5
6    python3 genllheaders.py [-p /path/to/HAL] [-o /path/to/output_dir]
7
8Copyright (c) 2020 Teslabs Engineering S.L.
9
10SPDX-License-Identifier: Apache-2.0
11"""
12
13import argparse
14import logging
15from pathlib import Path
16import re
17import shutil
18
19from jinja2 import Environment, FileSystemLoader
20
21
22logger = logging.getLogger(__name__)
23
24
25SCRIPT_DIR = Path(__file__).absolute().parent
26"""Script directory."""
27
28README_TEMPLATE_FILE = "readme-template.j2"
29"""Readme template file."""
30
31HEADER_TEMPLATE_FILE = "header-template.j2"
32"""Header template file."""
33
34REPO_ROOT = SCRIPT_DIR / ".." / ".."
35"""Repository root (used for input/output default folders)."""
36
37LL_API_IGNORE = ["usb"]
38"""List of LL APIs to be ignored."""
39
40
41def main(hal_path, output):
42    """Entry point.
43
44    Args:
45        hal_path: Zephyr CubeMX HAL path.
46        output: Output directory.
47    """
48
49    # obtain all available LL APIs for each series
50    ll_apis = dict()
51    versions = dict()
52    for entry in sorted(hal_path.iterdir()):
53        if not entry.is_dir() or not entry.name.startswith("stm32"):
54            continue
55
56        series = entry.name
57
58        # collect version
59        with open(entry / "README") as f:
60            readme = f.read()
61            m = re.search(r"version v?([0-9\.]+)", readme)
62            if not m:
63                logger.error(f"Could not determine version for {series}")
64                continue
65
66            versions[series] = m.group(1)
67
68        # obtain series LL headers
69        series_headers = entry / "drivers" / "include"
70        for header in series_headers.iterdir():
71            m = re.match(r"^stm32[a-z0-9]+_ll_([a-z0-9]+)\.h$", header.name)
72            if not m:
73                continue
74
75            ll_api = m.group(1)
76
77            if ll_api in LL_API_IGNORE:
78                continue
79
80            if ll_api not in ll_apis:
81                ll_apis[ll_api] = list()
82            ll_apis[ll_api].append(series)
83
84    # write header files
85    env = Environment(
86        trim_blocks=True, lstrip_blocks=True, loader=FileSystemLoader(SCRIPT_DIR)
87    )
88    readme_template = env.get_template(README_TEMPLATE_FILE)
89    header_template = env.get_template(HEADER_TEMPLATE_FILE)
90
91    if output.exists():
92        shutil.rmtree(output)
93    output.mkdir(parents=True)
94
95    readme_file = output / "README.rst"
96    with open(readme_file, "w") as f:
97        f.write(readme_template.render(versions=versions))
98
99    output_headers = output / "include"
100    output_headers.mkdir()
101
102    for ll_api, all_series in ll_apis.items():
103        header_file = output_headers / ("stm32_ll_" + ll_api + ".h")
104        with open(header_file, "w") as f:
105            f.write(header_template.render(ll_api=ll_api, all_series=all_series))
106
107
108if __name__ == "__main__":
109    parser = argparse.ArgumentParser()
110    parser.add_argument(
111        "-p",
112        "--hal-path",
113        type=Path,
114        default=REPO_ROOT / "stm32cube",
115        help="Zephyr CubeMX HAL path",
116    )
117    parser.add_argument(
118        "-o",
119        "--output",
120        type=Path,
121        default=REPO_ROOT / "stm32cube" / "common_ll",
122        help="Output directory",
123    )
124    args = parser.parse_args()
125
126    main(args.hal_path, args.output)
127