1"""
2Utility script to migrate SYS_INIT() calls from the old signature
3``int (*init_fn)(const struct device *dev)`` to ``int (*init_fn)(void)``.
4
5.. warning::
6    Review the output carefully! This script may not cover all cases!
7
8Usage::
9
10    python $ZEPHYR_BASE/scripts/utils/migrate_sys_init.py \
11           -p path/to/zephyr-based-project
12
13Copyright (c) 2022 Nordic Semiconductor ASA
14SPDX-License-Identifier: Apache-2.0
15"""
16
17import argparse
18import re
19from pathlib import Path
20
21
22def update_sys_init(project, dry_run):
23    for p in project.glob("**/*"):
24        if not p.is_file() or not p.suffix or p.suffix[1:] not in ("c", "cpp"):
25            continue
26
27        with open(p) as f:
28            lines = f.readlines()
29
30        sys_inits = []
31        for line in lines:
32            m = re.match(r"^SYS_INIT\(([A-Za-z0-9_]+),.*", line)
33            if m:
34                sys_inits.append(m.group(1))
35                continue
36
37            m = re.match(r"^SYS_INIT_NAMED\([A-Za-z0-9_]+,\s?([A-Za-z0-9_]+).*", line)
38            if m:
39                sys_inits.append(m.group(1))
40                continue
41
42        if not sys_inits:
43            continue
44
45        arg = None
46        content = ""
47        update = False
48        unused = False
49        for line in lines:
50            m = re.match(
51                r"(.*)int ("
52                + "|".join(sys_inits)
53                + r")\(const\s+struct\s+device\s+\*\s?(.*)\)(.*)",
54                line,
55            )
56            if m:
57                b, sys_init, arg, e = m.groups()
58                content += f"{b}int {sys_init}(void){e}\n"
59                update = True
60            elif arg:
61                m = re.match(r"^\s?ARG_UNUSED\(" + arg + r"\);.*$", line)
62                if m:
63                    arg = None
64                    unused = True
65                else:
66                    content += line
67            elif unused:
68                m = re.match(r"^\s?\n$", line)
69                if not m:
70                    content += line
71                unused = False
72            else:
73                content += line
74
75        if update:
76            print(f"Updating {p}{' (dry run)' if dry_run else ''}")
77            if not dry_run:
78                with open(p, "w") as f:
79                    f.write(content)
80
81
82if __name__ == "__main__":
83    parser = argparse.ArgumentParser(allow_abbrev=False)
84    parser.add_argument(
85        "-p", "--project", type=Path, required=True, help="Zephyr-based project path"
86    )
87    parser.add_argument("--dry-run", action="store_true", help="Dry run")
88    args = parser.parse_args()
89
90    update_sys_init(args.project, args.dry_run)
91