Simple Directmedia Layer
at main 6.7 kB view raw
1#!/usr/bin/env python3 2# 3# Simple DirectMedia Layer 4# Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org> 5# 6# This software is provided 'as-is', without any express or implied 7# warranty. In no event will the authors be held liable for any damages 8# arising from the use of this software. 9# 10# Permission is granted to anyone to use this software for any purpose, 11# including commercial applications, and to alter it and redistribute it 12# freely, subject to the following restrictions: 13# 14# 1. The origin of this software must not be misrepresented; you must not 15# claim that you wrote the original software. If you use this software 16# in a product, an acknowledgment in the product documentation would be 17# appreciated but is not required. 18# 2. Altered source versions must be plainly marked as such, and must not be 19# misrepresented as being the original software. 20# 3. This notice may not be removed or altered from any source distribution. 21# 22# This script detects use of stdlib function in SDL code 23 24import argparse 25import os 26import pathlib 27import re 28import sys 29 30SDL_ROOT = pathlib.Path(__file__).resolve().parents[1] 31 32STDLIB_SYMBOLS = [ 33 'abs', 34 'acos', 35 'acosf', 36 'asin', 37 'asinf', 38 'asprintf', 39 'atan', 40 'atan2', 41 'atan2f', 42 'atanf', 43 'atof', 44 'atoi', 45 'bsearch', 46 'calloc', 47 'ceil', 48 'ceilf', 49 'copysign', 50 'copysignf', 51 'cos', 52 'cosf', 53 'crc32', 54 'exp', 55 'expf', 56 'fabs', 57 'fabsf', 58 'floor', 59 'floorf', 60 'fmod', 61 'fmodf', 62 'free', 63 'getenv', 64 'isalnum', 65 'isalpha', 66 'isblank', 67 'iscntrl', 68 'isdigit', 69 'isgraph', 70 'islower', 71 'isprint', 72 'ispunct', 73 'isspace', 74 'isupper', 75 'isxdigit', 76 'itoa', 77 'lltoa', 78 'log10', 79 'log10f', 80 'logf', 81 'lround', 82 'lroundf', 83 'ltoa', 84 'malloc', 85 'memalign', 86 'memcmp', 87 'memcpy', 88 'memcpy4', 89 'memmove', 90 'memset', 91 'pow', 92 'powf', 93 'qsort', 94 'qsort_r', 95 'qsort_s', 96 'realloc', 97 'round', 98 'roundf', 99 'scalbn', 100 'scalbnf', 101 'setenv', 102 'sin', 103 'sinf', 104 'snprintf', 105 'sqrt', 106 'sqrtf', 107 'sscanf', 108 'strcasecmp', 109 'strchr', 110 'strcmp', 111 'strdup', 112 'strlcat', 113 'strlcpy', 114 'strlen', 115 'strlwr', 116 'strncasecmp', 117 'strncmp', 118 'strrchr', 119 'strrev', 120 'strstr', 121 'strtod', 122 'strtokr', 123 'strtol', 124 'strtoll', 125 'strtoul', 126 'strupr', 127 'tan', 128 'tanf', 129 'tolower', 130 'toupper', 131 'trunc', 132 'truncf', 133 'uitoa', 134 'ulltoa', 135 'ultoa', 136 'utf8strlcpy', 137 'utf8strlen', 138 'vasprintf', 139 'vsnprintf', 140 'vsscanf', 141 'wcscasecmp', 142 'wcscmp', 143 'wcsdup', 144 'wcslcat', 145 'wcslcpy', 146 'wcslen', 147 'wcsncasecmp', 148 'wcsncmp', 149 'wcsstr', 150] 151RE_STDLIB_SYMBOL = re.compile(rf"\b(?P<symbol>{'|'.join(STDLIB_SYMBOLS)})\b\(") 152 153 154def find_symbols_in_file(file: pathlib.Path) -> int: 155 match_count = 0 156 157 allowed_extensions = [ ".c", ".cpp", ".m", ".h", ".hpp", ".cc" ] 158 159 excluded_paths = [ 160 "src/stdlib", 161 "src/libm", 162 "src/hidapi", 163 "src/video/khronos", 164 "include/SDL3", 165 "build-scripts/gen_audio_resampler_filter.c", 166 "build-scripts/gen_audio_channel_conversion.c", 167 "test/win32/sdlprocdump.c", 168 ] 169 170 filename = pathlib.Path(file) 171 172 for ep in excluded_paths: 173 if ep in filename.as_posix(): 174 # skip 175 return 0 176 177 if filename.suffix not in allowed_extensions: 178 # skip 179 return 0 180 181 # print("Parse %s" % file) 182 183 try: 184 with file.open("r", encoding="UTF-8", newline="") as rfp: 185 parsing_comment = False 186 for line_i, original_line in enumerate(rfp, start=1): 187 line = original_line.strip() 188 189 line_comment = "" 190 191 # Get the comment block /* ... */ across several lines 192 while True: 193 if parsing_comment: 194 pos_end_comment = line.find("*/") 195 if pos_end_comment >= 0: 196 line = line[pos_end_comment+2:] 197 parsing_comment = False 198 else: 199 break 200 else: 201 pos_start_comment = line.find("/*") 202 if pos_start_comment >= 0: 203 pos_end_comment = line.find("*/", pos_start_comment+2) 204 if pos_end_comment >= 0: 205 line_comment += line[pos_start_comment:pos_end_comment+2] 206 line = line[:pos_start_comment] + line[pos_end_comment+2:] 207 else: 208 line_comment += line[pos_start_comment:] 209 line = line[:pos_start_comment] 210 parsing_comment = True 211 break 212 else: 213 break 214 if parsing_comment: 215 continue 216 pos_line_comment = line.find("//") 217 if pos_line_comment >= 0: 218 line_comment += line[pos_line_comment:] 219 line = line[:pos_line_comment] 220 221 if m := RE_STDLIB_SYMBOL.match(line): 222 override_string = f"This should NOT be SDL_{m['symbol']}()" 223 if override_string not in line_comment: 224 print(f"{filename}:{line_i}") 225 print(f" {line}") 226 print(f"") 227 match_count += 1 228 229 except UnicodeDecodeError: 230 print(f"{file} is not text, skipping", file=sys.stderr) 231 232 return match_count 233 234def find_symbols_in_dir(path: pathlib.Path) -> int: 235 match_count = 0 236 for entry in path.glob("*"): 237 if entry.is_dir(): 238 match_count += find_symbols_in_dir(entry) 239 else: 240 match_count += find_symbols_in_file(entry) 241 return match_count 242 243def main(): 244 parser = argparse.ArgumentParser(fromfile_prefix_chars="@") 245 parser.add_argument("path", default=SDL_ROOT, nargs="?", type=pathlib.Path, help="Path to look for stdlib symbols") 246 args = parser.parse_args() 247 248 print(f"Looking for stdlib usage in {args.path}...") 249 250 match_count = find_symbols_in_dir(args.path) 251 252 if match_count: 253 print("If the stdlib usage is intentional, add a '// This should NOT be SDL_<symbol>()' line comment.") 254 print("") 255 print("NOT OK") 256 else: 257 print("OK") 258 return 1 if match_count else 0 259 260if __name__ == "__main__": 261 raise SystemExit(main())