123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- #!/usr/bin/env python3
- #
- # Simple DirectMedia Layer
- # Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
- #
- # This software is provided 'as-is', without any express or implied
- # warranty. In no event will the authors be held liable for any damages
- # arising from the use of this software.
- #
- # Permission is granted to anyone to use this software for any purpose,
- # including commercial applications, and to alter it and redistribute it
- # freely, subject to the following restrictions:
- #
- # 1. The origin of this software must not be misrepresented; you must not
- # claim that you wrote the original software. If you use this software
- # in a product, an acknowledgment in the product documentation would be
- # appreciated but is not required.
- # 2. Altered source versions must be plainly marked as such, and must not be
- # misrepresented as being the original software.
- # 3. This notice may not be removed or altered from any source distribution.
- #
- # This script detects use of stdlib function in SDL code
- import argparse
- import os
- import pathlib
- import re
- import sys
- SDL_ROOT = pathlib.Path(__file__).resolve().parents[1]
- STDLIB_SYMBOLS = [
- 'abs',
- 'acos',
- 'acosf',
- 'asin',
- 'asinf',
- 'asprintf',
- 'atan',
- 'atan2',
- 'atan2f',
- 'atanf',
- 'atof',
- 'atoi',
- 'bsearch',
- 'calloc',
- 'ceil',
- 'ceilf',
- 'copysign',
- 'copysignf',
- 'cos',
- 'cosf',
- 'crc32',
- 'exp',
- 'expf',
- 'fabs',
- 'fabsf',
- 'floor',
- 'floorf',
- 'fmod',
- 'fmodf',
- 'free',
- 'getenv',
- 'isalnum',
- 'isalpha',
- 'isblank',
- 'iscntrl',
- 'isdigit',
- 'isgraph',
- 'islower',
- 'isprint',
- 'ispunct',
- 'isspace',
- 'isupper',
- 'isxdigit',
- 'itoa',
- 'lltoa',
- 'log10',
- 'log10f',
- 'logf',
- 'lround',
- 'lroundf',
- 'ltoa',
- 'malloc',
- 'memalign',
- 'memcmp',
- 'memcpy',
- 'memcpy4',
- 'memmove',
- 'memset',
- 'pow',
- 'powf',
- 'qsort',
- 'qsort_r',
- 'qsort_s',
- 'realloc',
- 'round',
- 'roundf',
- 'scalbn',
- 'scalbnf',
- 'setenv',
- 'sin',
- 'sinf',
- 'snprintf',
- 'sqrt',
- 'sqrtf',
- 'sscanf',
- 'strcasecmp',
- 'strchr',
- 'strcmp',
- 'strdup',
- 'strlcat',
- 'strlcpy',
- 'strlen',
- 'strlwr',
- 'strncasecmp',
- 'strncmp',
- 'strrchr',
- 'strrev',
- 'strstr',
- 'strtod',
- 'strtokr',
- 'strtol',
- 'strtoll',
- 'strtoul',
- 'strupr',
- 'tan',
- 'tanf',
- 'tolower',
- 'toupper',
- 'trunc',
- 'truncf',
- 'uitoa',
- 'ulltoa',
- 'ultoa',
- 'utf8strlcpy',
- 'utf8strlen',
- 'vasprintf',
- 'vsnprintf',
- 'vsscanf',
- 'wcscasecmp',
- 'wcscmp',
- 'wcsdup',
- 'wcslcat',
- 'wcslcpy',
- 'wcslen',
- 'wcsncasecmp',
- 'wcsncmp',
- 'wcsstr',
- ]
- RE_STDLIB_SYMBOL = re.compile(rf"\b(?P<symbol>{'|'.join(STDLIB_SYMBOLS)})\b\(")
- def find_symbols_in_file(file: pathlib.Path) -> int:
- match_count = 0
- allowed_extensions = [ ".c", ".cpp", ".m", ".h", ".hpp", ".cc" ]
- excluded_paths = [
- "src/stdlib",
- "src/libm",
- "src/hidapi",
- "src/video/khronos",
- "src/video/stb_image.h",
- "include/SDL3",
- "build-scripts/gen_audio_resampler_filter.c",
- "build-scripts/gen_audio_channel_conversion.c",
- "test/win32/sdlprocdump.c",
- ]
- filename = pathlib.Path(file)
- for ep in excluded_paths:
- if ep in filename.as_posix():
- # skip
- return 0
- if filename.suffix not in allowed_extensions:
- # skip
- return 0
- # print("Parse %s" % file)
- try:
- with file.open("r", encoding="UTF-8", newline="") as rfp:
- parsing_comment = False
- for line_i, original_line in enumerate(rfp, start=1):
- line = original_line.strip()
- line_comment = ""
- # Get the comment block /* ... */ across several lines
- while True:
- if parsing_comment:
- pos_end_comment = line.find("*/")
- if pos_end_comment >= 0:
- line = line[pos_end_comment+2:]
- parsing_comment = False
- else:
- break
- else:
- pos_start_comment = line.find("/*")
- if pos_start_comment >= 0:
- pos_end_comment = line.find("*/", pos_start_comment+2)
- if pos_end_comment >= 0:
- line_comment += line[pos_start_comment:pos_end_comment+2]
- line = line[:pos_start_comment] + line[pos_end_comment+2:]
- else:
- line_comment += line[pos_start_comment:]
- line = line[:pos_start_comment]
- parsing_comment = True
- break
- else:
- break
- if parsing_comment:
- continue
- pos_line_comment = line.find("//")
- if pos_line_comment >= 0:
- line_comment += line[pos_line_comment:]
- line = line[:pos_line_comment]
- if m := RE_STDLIB_SYMBOL.match(line):
- override_string = f"This should NOT be SDL_{m['symbol']}()"
- if override_string not in line_comment:
- print(f"{filename}:{line_i}")
- print(f" {line}")
- print(f"")
- match_count += 1
- except UnicodeDecodeError:
- print(f"{file} is not text, skipping", file=sys.stderr)
- return match_count
- def find_symbols_in_dir(path: pathlib.Path) -> int:
- match_count = 0
- for entry in path.glob("*"):
- if entry.is_dir():
- match_count += find_symbols_in_dir(entry)
- else:
- match_count += find_symbols_in_file(entry)
- return match_count
- def main():
- parser = argparse.ArgumentParser(fromfile_prefix_chars="@")
- parser.add_argument("path", default=SDL_ROOT, nargs="?", type=pathlib.Path, help="Path to look for stdlib symbols")
- args = parser.parse_args()
- print(f"Looking for stdlib usage in {args.path}...")
- match_count = find_symbols_in_dir(args.path)
- if match_count:
- print("If the stdlib usage is intentional, add a '// This should NOT be SDL_<symbol>()' line comment.")
- print("")
- print("NOT OK")
- else:
- print("OK")
- return 1 if match_count else 0
- if __name__ == "__main__":
- raise SystemExit(main())
|