mirror of
				https://github.com/libsdl-org/SDL.git
				synced 2025-11-04 01:34:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			262 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			262 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env python3
 | 
						|
#
 | 
						|
#  Simple DirectMedia Layer
 | 
						|
#  Copyright (C) 1997-2024 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",
 | 
						|
        "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())
 |