1"""
2Utility script to migrate Zephyr-based projects to the new <zephyr/...> include
3prefix.
4
5.. note::
6    The script will also migrate <zephyr.h> or <zephyr/zephyr.h> to
7    <zephyr/kernel.h>.
8
9Usage::
10
11    python $ZEPHYR_BASE/scripts/utils/migrate_includes.py \
12           -p path/to/zephyr-based-project
13
14Copyright (c) 2022 Nordic Semiconductor ASA
15SPDX-License-Identifier: Apache-2.0
16"""
17
18import argparse
19import re
20import sys
21from pathlib import Path
22
23ZEPHYR_BASE = Path(__file__).parents[2]
24
25EXTENSIONS = ("c", "cpp", "h", "hpp", "dts", "dtsi", "rst", "S", "overlay", "ld")
26
27
28def update_includes(project, dry_run):
29    for p in project.glob("**/*"):
30        if not p.is_file() or not p.suffix or p.suffix[1:] not in EXTENSIONS:
31            continue
32
33        try:
34            with open(p) as f:
35                lines = f.readlines()
36        except UnicodeDecodeError:
37            print(f"File with invalid encoding: {p}, skipping", file=sys.stderr)
38            continue
39
40        content = ""
41        migrate = False
42        for line in lines:
43            m = re.match(r"^(.*)#include <(.*\.h)>(.*)$", line)
44            if m and m.group(2) in ("zephyr.h", "zephyr/zephyr.h"):
45                content += (
46                    m.group(1)
47                    + "#include <zephyr/kernel.h>"
48                    + m.group(3)
49                    + "\n"
50                )
51                migrate = True
52            elif (
53                m
54                and not m.group(2).startswith("zephyr/")
55                and (ZEPHYR_BASE / "include" / "zephyr" / m.group(2)).exists()
56            ):
57                content += (
58                    m.group(1)
59                    + "#include <zephyr/"
60                    + m.group(2)
61                    + ">"
62                    + m.group(3)
63                    + "\n"
64                )
65                migrate = True
66            else:
67                content += line
68
69        if migrate:
70            print(f"Updating {p}{' (dry run)' if dry_run else ''}")
71            if not dry_run:
72                with open(p, "w") as f:
73                    f.write(content)
74
75
76if __name__ == "__main__":
77    parser = argparse.ArgumentParser(allow_abbrev=False)
78    parser.add_argument(
79        "-p", "--project", type=Path, required=True, help="Zephyr-based project path"
80    )
81    parser.add_argument("--dry-run", action="store_true", help="Dry run")
82    args = parser.parse_args()
83
84    update_includes(args.project, args.dry_run)
85