1""" 2Utility script to migrate Zephyr-based projects to normative POSIX Kconfig options. 3 4This script should be used for migrating from versions of Zephyr older than v3.7.0 to Zephyr 5version v3.7.0 or later. 6 7Usage:: 8 9 python $ZEPHYR_BASE/scripts/utils/migrate_posix_kconfigs.py --root root_path --dry-run 10 11The utility will process c, cpp, h, hpp, rst, conf, CMakeLists.txt, 12yml, yaml and Kconfig files. 13 14 15Copyright (c) 2022 Nordic Semiconductor ASA 16Copyright (c) 2024 Tenstorrent AI ULC 17SPDX-License-Identifier: Apache-2.0 18""" 19 20import argparse 21import re 22import sys 23from pathlib import Path 24 25ZEPHYR_BASE = Path(__file__).parents[2] 26 27FILE_PATTERNS = ( 28 r".+\.c", r".+\.cpp", r".+\.hpp", r".+\.h", r".+\.rst", r".+\.conf", 29 r".+\.yml", r".+\.yaml", r"CMakeLists.txt", r"Kconfig(\..+)?" 30) 31 32REPLACEMENTS = { 33 "EVENTFD_MAX": "ZVFS_EVENTFD_MAX", 34 "FNMATCH": "POSIX_C_LIB_EXT", 35 "GETENTROPY": "POSIX_C_LIB_EXT", 36 "GETOPT": "POSIX_C_LIB_EXT", 37 "MAX_PTHREAD_COUNT": "POSIX_THREAD_THREADS_MAX", 38 "MAX_PTHREAD_KEY_COUNT": "POSIX_THREAD_KEYS_MAX", 39 "MAX_TIMER_COUNT": "POSIX_TIMER_MAX", 40 "MSG_COUNT_MAX": "POSIX_MQ_OPEN_MAX", 41 "POSIX_CLOCK": "POSIX_TIMERS", 42 "POSIX_CONFSTR": "POSIX_SINGLE_PROCESS", 43 "POSIX_ENV": "POSIX_SINGLE_PROCESS", 44 "POSIX_FS": "POSIX_FILE_SYSTEM", 45 "POSIX_LIMITS_RTSIG_MAX": "POSIX_RTSIG_MAX", 46 "POSIX_MAX_FDS": "ZVFS_OPEN_MAX", 47 "POSIX_MAX_OPEN_FILES": "ZVFS_OPEN_MAX", 48 "POSIX_MQUEUE": "POSIX_MESSAGE_PASSING", 49 "POSIX_PUTMSG": "XOPEN_STREAMS", 50 "POSIX_SIGNAL": "POSIX_SIGNALS", 51 "POSIX_SYSCONF": "POSIX_SINGLE_PROCESS", 52 "POSIX_SYSLOG": "XSI_SYSTEM_LOGGING", 53 "POSIX_UNAME": "POSIX_SINGLE_PROCESS", 54 "PTHREAD": "POSIX_THREADS", 55 "PTHREAD_BARRIER": "POSIX_BARRIERS", 56 "PTHREAD_COND": "POSIX_THREADS", 57 "PTHREAD_IPC": "POSIX_THREADS", 58 "PTHREAD_KEY": "POSIX_THREADS", 59 "PTHREAD_MUTEX": "POSIX_THREADS", 60 "PTHREAD_RWLOCK": "POSIX_READER_WRITER_LOCKS", 61 "PTHREAD_SPINLOCK": "POSIX_SPIN_LOCKS", 62 "TIMER": "POSIX_TIMERS", 63 "TIMER_DELAYTIMER_MAX": "POSIX_DELAYTIMER_MAX", 64 "SEM_NAMELEN_MAX": "POSIX_SEM_NAME_MAX", 65 "SEM_VALUE_MAX": "POSIX_SEM_VALUE_MAX", 66} 67 68MESSAGES = { 69 "POSIX_CLOCK": 70 "POSIX_CLOCK is a one-to-many replacement. If this simple substitution is not " 71 "sufficient, it's best to try a combination of POSIX_CLOCK_SELECTION, POSIX_CPUTIME, " 72 "POSIX_MONOTONIC_CLOCK, POSIX_TIMERS, and POSIX_TIMEOUTS.", 73 "POSIX_MAX_FDS": 74 "A read-only version of this symbol is POSIX_OPEN_MAX, which is of course, the standard " 75 "symbol. ZVFS_OPEN_MAX may be set by the user. Consider using POSIX_MAX_FDS if the " 76 "use-case is read-only.", 77} 78 79 80def process_file(path): 81 modified = False 82 output = [] 83 84 try: 85 with open(path) as f: 86 lines = f.readlines() 87 88 lineno = 1 89 for line in lines: 90 longest = "" 91 length = 0 92 for m in REPLACEMENTS: 93 if re.match(".*" + m + ".*", line) and len(m) > length: 94 length = len(m) 95 longest = m 96 97 # TIMER is just way too frequent an occurrence. Skip it. 98 skip = {"TIMER"} 99 if length != 0: 100 if longest in skip: 101 pass 102 else: 103 modified = True 104 old = line 105 line = line.replace(longest, REPLACEMENTS[longest]) 106 msg = MESSAGES.get(longest) 107 if msg: 108 print(f"{path}:{lineno}:old:{old.strip()}") 109 print(f"{path}:{lineno}:new:{line.strip()}") 110 111 lineno += 1 112 output.append(line) 113 114 if modified is False or args.dry_run: 115 return 116 117 with open(path, "w") as f: 118 f.writelines(output) 119 120 except UnicodeDecodeError: 121 print(f"Unable to read lines from {path}", file=sys.stderr) 122 except Exception as e: 123 print(f"Failed with exception {e}", e) 124 125 126def process_tree(project): 127 for p in project.glob("**/*"): 128 for fp in FILE_PATTERNS: 129 cfp = re.compile(".+/" + fp + "$") 130 if re.match(cfp, str(p)) is not None: 131 process_file(p) 132 133 134if __name__ == "__main__": 135 parser = argparse.ArgumentParser(allow_abbrev=False) 136 parser.add_argument( 137 "-r", 138 "--root", 139 type=Path, 140 required=True, 141 help="Zephyr-based project path") 142 parser.add_argument( 143 "-d", 144 "--dry-run", 145 action="store_true", 146 help="log potential changes without modifying files") 147 args = parser.parse_args() 148 149 process_tree(args.root) 150