From 30f41c02aec763d32e62351452da9ef582bc3472 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 6 Mar 2026 13:30:59 -0800 Subject: Move contrib libraries to contrib repo --- contrib/SDL-3.2.8/src/dynapi/gendynapi.py | 547 ------------------------------ 1 file changed, 547 deletions(-) delete mode 100755 contrib/SDL-3.2.8/src/dynapi/gendynapi.py (limited to 'contrib/SDL-3.2.8/src/dynapi/gendynapi.py') diff --git a/contrib/SDL-3.2.8/src/dynapi/gendynapi.py b/contrib/SDL-3.2.8/src/dynapi/gendynapi.py deleted file mode 100755 index 0915523..0000000 --- a/contrib/SDL-3.2.8/src/dynapi/gendynapi.py +++ /dev/null @@ -1,547 +0,0 @@ -#!/usr/bin/env python3 - -# Simple DirectMedia Layer -# Copyright (C) 1997-2025 Sam Lantinga -# -# 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. - -# WHAT IS THIS? -# When you add a public API to SDL, please run this script, make sure the -# output looks sane (git diff, it adds to existing files), and commit it. -# It keeps the dynamic API jump table operating correctly. -# -# Platform-specific API: -# After running the script, you have to manually add #ifdef SDL_PLATFORM_WIN32 -# or similar around the function in 'SDL_dynapi_procs.h'. -# - -import argparse -import dataclasses -import json -import logging -import os -from pathlib import Path -import pprint -import re - - -SDL_ROOT = Path(__file__).resolve().parents[2] - -SDL_INCLUDE_DIR = SDL_ROOT / "include/SDL3" -SDL_DYNAPI_PROCS_H = SDL_ROOT / "src/dynapi/SDL_dynapi_procs.h" -SDL_DYNAPI_OVERRIDES_H = SDL_ROOT / "src/dynapi/SDL_dynapi_overrides.h" -SDL_DYNAPI_SYM = SDL_ROOT / "src/dynapi/SDL_dynapi.sym" - -RE_EXTERN_C = re.compile(r'.*extern[ "]*C[ "].*') -RE_COMMENT_REMOVE_CONTENT = re.compile(r'\/\*.*\*/') -RE_PARSING_FUNCTION = re.compile(r'(.*SDLCALL[^\(\)]*) ([a-zA-Z0-9_]+) *\((.*)\) *;.*') - -#eg: -# void (SDLCALL *callback)(void*, int) -# \1(\2)\3 -RE_PARSING_CALLBACK = re.compile(r'([^\(\)]*)\(([^\(\)]+)\)(.*)') - - -logger = logging.getLogger(__name__) - - -@dataclasses.dataclass(frozen=True) -class SdlProcedure: - retval: str - name: str - parameter: list[str] - parameter_name: list[str] - header: str - comment: str - - @property - def variadic(self) -> bool: - return "..." in self.parameter - - -def parse_header(header_path: Path) -> list[SdlProcedure]: - logger.debug("Parse header: %s", header_path) - - header_procedures = [] - - parsing_function = False - current_func = "" - parsing_comment = False - current_comment = "" - ignore_wiki_documentation = False - - with header_path.open() as f: - for line in f: - - # Skip lines if we're in a wiki documentation block. - if ignore_wiki_documentation: - if line.startswith("#endif"): - ignore_wiki_documentation = False - continue - - # Discard wiki documentations blocks. - if line.startswith("#ifdef SDL_WIKI_DOCUMENTATION_SECTION"): - ignore_wiki_documentation = True - continue - - # Discard pre-processor directives ^#.* - if line.startswith("#"): - continue - - # Discard "extern C" line - match = RE_EXTERN_C.match(line) - if match: - continue - - # Remove one line comment // ... - # eg: extern SDL_DECLSPEC SDL_hid_device * SDLCALL SDL_hid_open_path(const char *path, int bExclusive /* = false */) - line = RE_COMMENT_REMOVE_CONTENT.sub('', line) - - # Get the comment block /* ... */ across several lines - match_start = "/*" in line - match_end = "*/" in line - if match_start and match_end: - continue - if match_start: - parsing_comment = True - current_comment = line - continue - if match_end: - parsing_comment = False - current_comment += line - continue - if parsing_comment: - current_comment += line - continue - - # Get the function prototype across several lines - if parsing_function: - # Append to the current function - current_func += " " - current_func += line.strip() - else: - # if is contains "extern", start grabbing - if "extern" not in line: - continue - # Start grabbing the new function - current_func = line.strip() - parsing_function = True - - # If it contains ';', then the function is complete - if ";" not in current_func: - continue - - # Got function/comment, reset vars - parsing_function = False - func = current_func - comment = current_comment - current_func = "" - current_comment = "" - - # Discard if it doesn't contain 'SDLCALL' - if "SDLCALL" not in func: - logger.debug(" Discard, doesn't have SDLCALL: %r", func) - continue - - # Discard if it contains 'SDLMAIN_DECLSPEC' (these are not SDL symbols). - if "SDLMAIN_DECLSPEC" in func: - logger.debug(" Discard, has SDLMAIN_DECLSPEC: %r", func) - continue - - logger.debug("Raw data: %r", func) - - # Replace unusual stuff... - func = func.replace(" SDL_PRINTF_VARARG_FUNC(1)", "") - func = func.replace(" SDL_PRINTF_VARARG_FUNC(2)", "") - func = func.replace(" SDL_PRINTF_VARARG_FUNC(3)", "") - func = func.replace(" SDL_PRINTF_VARARG_FUNC(4)", "") - func = func.replace(" SDL_PRINTF_VARARG_FUNCV(1)", "") - func = func.replace(" SDL_PRINTF_VARARG_FUNCV(2)", "") - func = func.replace(" SDL_PRINTF_VARARG_FUNCV(3)", "") - func = func.replace(" SDL_PRINTF_VARARG_FUNCV(4)", "") - func = func.replace(" SDL_WPRINTF_VARARG_FUNC(3)", "") - func = func.replace(" SDL_WPRINTF_VARARG_FUNCV(3)", "") - func = func.replace(" SDL_SCANF_VARARG_FUNC(2)", "") - func = func.replace(" SDL_SCANF_VARARG_FUNCV(2)", "") - func = func.replace(" SDL_ANALYZER_NORETURN", "") - func = func.replace(" SDL_MALLOC", "") - func = func.replace(" SDL_ALLOC_SIZE2(1, 2)", "") - func = func.replace(" SDL_ALLOC_SIZE(2)", "") - func = re.sub(r" SDL_ACQUIRE\(.*\)", "", func) - func = re.sub(r" SDL_ACQUIRE_SHARED\(.*\)", "", func) - func = re.sub(r" SDL_TRY_ACQUIRE\(.*\)", "", func) - func = re.sub(r" SDL_TRY_ACQUIRE_SHARED\(.*\)", "", func) - func = re.sub(r" SDL_RELEASE\(.*\)", "", func) - func = re.sub(r" SDL_RELEASE_SHARED\(.*\)", "", func) - func = re.sub(r" SDL_RELEASE_GENERIC\(.*\)", "", func) - func = re.sub(r"([ (),])(SDL_IN_BYTECAP\([^)]*\))", r"\1", func) - func = re.sub(r"([ (),])(SDL_OUT_BYTECAP\([^)]*\))", r"\1", func) - func = re.sub(r"([ (),])(SDL_INOUT_Z_CAP\([^)]*\))", r"\1", func) - func = re.sub(r"([ (),])(SDL_OUT_Z_CAP\([^)]*\))", r"\1", func) - - # Should be a valid function here - match = RE_PARSING_FUNCTION.match(func) - if not match: - logger.error("Cannot parse: %s", func) - raise ValueError(func) - - func_ret = match.group(1) - func_name = match.group(2) - func_params = match.group(3) - - # - # Parse return value - # - func_ret = func_ret.replace('extern', ' ') - func_ret = func_ret.replace('SDLCALL', ' ') - func_ret = func_ret.replace('SDL_DECLSPEC', ' ') - func_ret, _ = re.subn('([ ]{2,})', ' ', func_ret) - # Remove trailing spaces in front of '*' - func_ret = func_ret.replace(' *', '*') - func_ret = func_ret.strip() - - # - # Parse parameters - # - func_params = func_params.strip() - if func_params == "": - func_params = "void" - - # Identify each function parameters with type and name - # (eventually there are callbacks of several parameters) - tmp = func_params.split(',') - tmp2 = [] - param = "" - for t in tmp: - if param == "": - param = t - else: - param = param + "," + t - # Identify a callback or parameter when there is same count of '(' and ')' - if param.count('(') == param.count(')'): - tmp2.append(param.strip()) - param = "" - - # Process each parameters, separation name and type - func_param_type = [] - func_param_name = [] - for t in tmp2: - if t == "void": - func_param_type.append(t) - func_param_name.append("") - continue - - if t == "...": - func_param_type.append(t) - func_param_name.append("") - continue - - param_name = "" - - # parameter is a callback - if '(' in t: - match = RE_PARSING_CALLBACK.match(t) - if not match: - logger.error("cannot parse callback: %s", t) - raise ValueError(t) - a = match.group(1).strip() - b = match.group(2).strip() - c = match.group(3).strip() - - try: - (param_type, param_name) = b.rsplit('*', 1) - except: - param_type = t - param_name = "param_name_not_specified" - - # bug rsplit ?? - if param_name == "": - param_name = "param_name_not_specified" - - # reconstruct a callback name for future parsing - func_param_type.append(a + " (" + param_type.strip() + " *REWRITE_NAME)" + c) - func_param_name.append(param_name.strip()) - - continue - - # array like "char *buf[]" - has_array = False - if t.endswith("[]"): - t = t.replace("[]", "") - has_array = True - - # pointer - if '*' in t: - try: - (param_type, param_name) = t.rsplit('*', 1) - except: - param_type = t - param_name = "param_name_not_specified" - - # bug rsplit ?? - if param_name == "": - param_name = "param_name_not_specified" - - val = param_type.strip() + "*REWRITE_NAME" - - # Remove trailing spaces in front of '*' - tmp = "" - while val != tmp: - tmp = val - val = val.replace(' ', ' ') - val = val.replace(' *', '*') - # first occurrence - val = val.replace('*', ' *', 1) - val = val.strip() - - else: # non pointer - # cut-off last word on - try: - (param_type, param_name) = t.rsplit(' ', 1) - except: - param_type = t - param_name = "param_name_not_specified" - - val = param_type.strip() + " REWRITE_NAME" - - # set back array - if has_array: - val += "[]" - - func_param_type.append(val) - func_param_name.append(param_name.strip()) - - new_proc = SdlProcedure( - retval=func_ret, # Return value type - name=func_name, # Function name - comment=comment, # Function comment - header=header_path.name, # Header file - parameter=func_param_type, # List of parameters (type + anonymized param name 'REWRITE_NAME') - parameter_name=func_param_name, # Real parameter name, or 'param_name_not_specified' - ) - - header_procedures.append(new_proc) - - if logger.getEffectiveLevel() <= logging.DEBUG: - logger.debug("%s", pprint.pformat(new_proc)) - - return header_procedures - - -# Dump API into a json file -def full_API_json(path: Path, procedures: list[SdlProcedure]): - with path.open('w', newline='') as f: - json.dump([dataclasses.asdict(proc) for proc in procedures], f, indent=4, sort_keys=True) - logger.info("dump API to '%s'", path) - - -class CallOnce: - def __init__(self, cb): - self._cb = cb - self._called = False - def __call__(self, *args, **kwargs): - if self._called: - return - self._called = True - self._cb(*args, **kwargs) - - -# Check public function comments are correct -def print_check_comment_header(): - logger.warning("") - logger.warning("Please fix following warning(s):") - logger.warning("--------------------------------") - - -def check_documentations(procedures: list[SdlProcedure]) -> None: - - check_comment_header = CallOnce(print_check_comment_header) - - warning_header_printed = False - - # Check \param - for proc in procedures: - expected = len(proc.parameter) - if expected == 1: - if proc.parameter[0] == 'void': - expected = 0 - count = proc.comment.count("\\param") - if count != expected: - # skip SDL_stdinc.h - if proc.header != 'SDL_stdinc.h': - # Warning mismatch \param and function prototype - check_comment_header() - logger.warning(" In file %s: function %s() has %d '\\param' but expected %d", proc.header, proc.name, count, expected) - - # Warning check \param uses the correct parameter name - # skip SDL_stdinc.h - if proc.header != 'SDL_stdinc.h': - for n in proc.parameter_name: - if n != "" and "\\param " + n not in proc.comment and "\\param[out] " + n not in proc.comment: - check_comment_header() - logger.warning(" In file %s: function %s() missing '\\param %s'", proc.header, proc.name, n) - - # Check \returns - for proc in procedures: - expected = 1 - if proc.retval == 'void': - expected = 0 - - count = proc.comment.count("\\returns") - if count != expected: - # skip SDL_stdinc.h - if proc.header != 'SDL_stdinc.h': - # Warning mismatch \param and function prototype - check_comment_header() - logger.warning(" In file %s: function %s() has %d '\\returns' but expected %d" % (proc.header, proc.name, count, expected)) - - # Check \since - for proc in procedures: - expected = 1 - count = proc.comment.count("\\since") - if count != expected: - # skip SDL_stdinc.h - if proc.header != 'SDL_stdinc.h': - # Warning mismatch \param and function prototype - check_comment_header() - logger.warning(" In file %s: function %s() has %d '\\since' but expected %d" % (proc.header, proc.name, count, expected)) - - -# Parse 'sdl_dynapi_procs_h' file to find existing functions -def find_existing_proc_names() -> list[str]: - reg = re.compile(r'SDL_DYNAPI_PROC\([^,]*,([^,]*),.*\)') - ret = [] - - with SDL_DYNAPI_PROCS_H.open() as f: - for line in f: - match = reg.match(line) - if not match: - continue - existing_func = match.group(1) - ret.append(existing_func) - return ret - -# Get list of SDL headers -def get_header_list() -> list[Path]: - ret = [] - - for f in SDL_INCLUDE_DIR.iterdir(): - # Only *.h files - if f.is_file() and f.suffix == ".h": - ret.append(f) - else: - logger.debug("Skip %s", f) - - # Order headers for reproducible behavior - ret.sort() - - return ret - -# Write the new API in files: _procs.h _overrivides.h and .sym -def add_dyn_api(proc: SdlProcedure) -> None: - decl_args: list[str] = [] - call_args = [] - for i, argtype in enumerate(proc.parameter): - # Special case, void has no parameter name - if argtype == "void": - assert len(decl_args) == 0 - assert len(proc.parameter) == 1 - decl_args.append("void") - continue - - # Var name: a, b, c, ... - varname = chr(ord('a') + i) - - decl_args.append(argtype.replace("REWRITE_NAME", varname)) - if argtype != "...": - call_args.append(varname) - - macro_args = ( - proc.retval, - proc.name, - "({})".format(",".join(decl_args)), - "({})".format(",".join(call_args)), - "" if proc.retval == "void" else "return", - ) - - # File: SDL_dynapi_procs.h - # - # Add at last - # SDL_DYNAPI_PROC(SDL_EGLConfig,SDL_EGL_GetCurrentConfig,(void),(),return) - with SDL_DYNAPI_PROCS_H.open("a", newline="") as f: - if proc.variadic: - f.write("#ifndef SDL_DYNAPI_PROC_NO_VARARGS\n") - f.write(f"SDL_DYNAPI_PROC({','.join(macro_args)})\n") - if proc.variadic: - f.write("#endif\n") - - # File: SDL_dynapi_overrides.h - # - # Add at last - # "#define SDL_DelayNS SDL_DelayNS_REAL - f = open(SDL_DYNAPI_OVERRIDES_H, "a", newline="") - f.write(f"#define {proc.name} {proc.name}_REAL\n") - f.close() - - # File: SDL_dynapi.sym - # - # Add before "extra symbols go here" line - with SDL_DYNAPI_SYM.open() as f: - new_input = [] - for line in f: - if "extra symbols go here" in line: - new_input.append(f" {proc.name};\n") - new_input.append(line) - - with SDL_DYNAPI_SYM.open('w', newline='') as f: - for line in new_input: - f.write(line) - - -def main(): - parser = argparse.ArgumentParser() - parser.set_defaults(loglevel=logging.INFO) - parser.add_argument('--dump', nargs='?', default=None, const="sdl.json", metavar="JSON", help='output all SDL API into a .json file') - parser.add_argument('--debug', action='store_const', const=logging.DEBUG, dest="loglevel", help='add debug traces') - args = parser.parse_args() - - logging.basicConfig(level=args.loglevel, format='[%(levelname)s] %(message)s') - - # Get list of SDL headers - sdl_list_includes = get_header_list() - procedures = [] - for filename in sdl_list_includes: - header_procedures = parse_header(filename) - procedures.extend(header_procedures) - - # Parse 'sdl_dynapi_procs_h' file to find existing functions - existing_proc_names = find_existing_proc_names() - for procedure in procedures: - if procedure.name not in existing_proc_names: - logger.info("NEW %s", procedure.name) - add_dyn_api(procedure) - - if args.dump: - # Dump API into a json file - full_API_json(path=Path(args.dump), procedures=procedures) - - # Check comment formatting - check_documentations(procedures) - - -if __name__ == '__main__': - raise SystemExit(main()) -- cgit v1.2.3