isle-portable/tools/gendynsdl3api.py
2026-01-08 02:10:10 +01:00

277 lines
9.3 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import contextlib
import json
from pathlib import Path
import textwrap
class Sdl3SymbolIterator:
def __init__(self, sdl3_json, skip_non_partable_symbols=True):
self.sdl3_json = sdl3_json
self.next_index = 0
self.skip_non_partable_symbols = skip_non_partable_symbols
@property
def symbols_remaining(self):
return len(self.sdl3_json) - self.next_index
def __iter__(self):
return self
def __next__(self):
while True:
if self.next_index >= len(self.sdl3_json):
raise StopIteration
symbol_json = self.sdl3_json[self.next_index]
self.next_index += 1
name = symbol_json["name"]
rettype = symbol_json["retval"]
parameter_types = symbol_json["parameter"]
parameter_names = symbol_json["parameter_name"]
if parameter_types[0] == "void":
parameter_types = parameter_types[1:]
parameter_names = parameter_names[1:]
assert len(parameter_types) == 0
type_decl = ", ".join(parameter_type.replace("REWRITE_NAME", parameter_name) for parameter_type, parameter_name in zip(parameter_types, parameter_names))
if self.skip_non_partable_symbols:
if name.startswith("Vk") or "vulkan" in name.lower():
continue
if "android" in name.lower() or "iOS" in name:
continue
if "XTask" in type_decl or "XUser" in type_decl:
continue
if "SDL_WindowsMessageHook" in type_decl:
continue
return rettype, name, type_decl
@contextlib.contextmanager
def iterate_sdl3_symbols(sdl3_json):
try:
yield Sdl3SymbolIterator(sdl3_json)
finally:
pass
def main():
parser = argparse.ArgumentParser(allow_abbrev=False)
parser.add_argument("--api", required=True, type=Path, help="Path of SDL3 json api (generated by SDL/src/dynapi/gendynapi.py --dump)")
parser.add_argument("--used", required=True, type=Path, help="Path of used SDL3 symbols as a json file")
args = parser.parse_args()
with args.api.open() as f_sdl3:
sdl3_json = json.load(f_sdl3)
with args.used.open() as f_used:
sdl3_used = set(json.load(f_used))
print("#pragma once")
print()
print(f"// This file is auto-generated by {Path(__file__).name}. Do not edit.")
print()
print("#ifdef SDL_h_")
print("#error This file must be included BEFORE SDL3/SDL.h")
print("#endif")
print()
print("#include <SDL3/SDL.h>")
print("#include <SDL3/SDL_revision.h>")
print("#ifndef MORTAR_IMPLEMENT_SDL_MAIN")
print("#define SDL_MAIN_HANDLED")
print("#endif")
print("#include <SDL3/SDL_main.h>")
print("#include <cstdio>")
print()
print("#ifdef SDL3_DYNAMIC_LOAD")
print()
print("#define FOREACH_SDL3_SYMBOL(X) \\")
seen_symbols = set()
with iterate_sdl3_symbols(sdl3_json) as sdl3_symbol_iterator:
for rettype, name, type_decl in sdl3_symbol_iterator:
if name in sdl3_used:
print(f" X({rettype}, {name}, ({type_decl}))" + ("" if sdl3_symbol_iterator.symbols_remaining == 0 else " \\"))
seen_symbols.add(name)
if seen_symbols != sdl3_used:
missing = sdl3_used - seen_symbols
parser.error(f"sdl3 api does not provide all required SDL3 symbols (missing={missing})")
print()
print("#define X_SDL3_SYMBOL_TYPEDEF(RETTYPE, NAME, TYPES) typedef RETTYPE SDLCALL NAME ## _cbfn TYPES;")
print("#define X_SDL3_STRUCT_MEMBER(RETTYPE, NAME, TYPES) NAME ## _cbfn *NAME##_symbol;")
print()
print("FOREACH_SDL3_SYMBOL(X_SDL3_SYMBOL_TYPEDEF)")
print("typedef struct SDL3_Symbols {")
print(" int refcount;")
print(" void *handle;")
print(" FOREACH_SDL3_SYMBOL(X_SDL3_STRUCT_MEMBER)")
print("} SDL3_Symbols;")
print()
print("#undef X_SDL3_SYMBOL_TYPEDEF")
print("#undef X_SDL3_STRUCT_MEMBER")
print()
print("extern SDL3_Symbols SDL3;")
print()
print("#ifdef IMPLEMENT_DYN_SDL3")
print()
print(textwrap.dedent("""\
#ifdef _WIN32
static const char * const sdl3_locations[] = {
"SDL3.dll",
};
#elif defined(__APPLE__)
#define SDL3_LIBNAME "libSDL3.dylib"
#define SDL3_FRAMEWORK "SDL3.framework/Versions/A/SDL3"
static const char * const sdl3_locations[] = {
"@loader_path/" SDL3_LIBNAME, /* MyApp.app/Contents/MacOS/libSDL3_dylib */
"@loader_path/../Frameworks/" SDL3_FRAMEWORK, /* MyApp.app/Contents/Frameworks/SDL3_framework */
"@executable_path/" SDL3_LIBNAME, /* MyApp.app/Contents/MacOS/libSDL3_dylib */
"@executable_path/../Frameworks/" SDL3_FRAMEWORK, /* MyApp.app/Contents/Frameworks/SDL3_framework */
NULL, /* /Users/username/Library/Frameworks/SDL3_framework */
"/Library/Frameworks" SDL3_FRAMEWORK, /* /Library/Frameworks/SDL3_framework */
SDL3_LIBNAME /* oh well, anywhere the system can see the .dylib (/usr/local/lib or whatever) */
};
#undef SDL3_LIBNAME
#undef SDL3_FRAMEWORK
#else
static const char * const sdl3_locations[] = {
"libSDL3.so.0",
"libSDL3.so",
};
#endif
#ifdef _WIN32
#include <windows.h>
static void *open_object(const char *name) {
return (void *)LoadLibraryA(name);
}
static void close_object(void *obj) {
FreeLibrary((HMODULE)obj);
}
static void *load_symbol(void *obj, const char *name) {
return (void *)GetProcAddress((HMODULE)obj, name);
}
static const char *load_error(void) {
static char buffer[512];
DWORD cchMsg = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buffer,
sizeof(buffer)-1,
NULL);
if (cchMsg == 0) {
strncpy(buffer, "GetProcAddress failed", sizeof(buffer));
buffer[sizeof(buffer)-1] = '\\0';
}
return buffer;
}
#else
#include <dlfcn.h>
static void *open_object(const char *name) {
return dlopen(name, RTLD_NOW | RTLD_LOCAL);
}
static void close_object(void *obj) {
dlclose(obj);
}
static void *load_symbol(void *obj, const char *name) {
return dlsym(obj, name);
}
static const char *load_error(void) {
return dlerror();
}
#endif
"""))
print()
print(textwrap.dedent("""\
#define X_SDL3_LOAD_SYMBOL(RETTYPE, NAME, TYPES) \\
SDL3.NAME##_symbol = (NAME##_cbfn*)load_symbol(SDL3.handle, #NAME); \\
if (!SDL3.NAME##_symbol) { \\
missing_symbol = #NAME; \\
goto error; \\
}
static bool load_sdl3_api() {
if (SDL3.refcount) {
SDL3.refcount += 1;
return true;
}
SDL_zero(SDL3);
for (size_t i = 0; i < SDL_arraysize(sdl3_locations); i++) {
SDL3.handle = open_object(sdl3_locations[i]);
if (SDL3.handle != NULL) {
break;
}
}
if (!SDL3.handle) {
fputs("Could not find SDL3 library\\n", stderr);
return false;
}
const char *missing_symbol = NULL;
FOREACH_SDL3_SYMBOL(X_SDL3_LOAD_SYMBOL)
SDL3.refcount = 1;
return true;
error:
fprintf(stderr, "Could not find SDL3 symbol: %s\\n", missing_symbol);
close_object(SDL3.handle);
SDL_zero(SDL3);
return false;
}"""))
print()
print(textwrap.dedent("""\
static void unload_sdl3_api() {
SDL3.refcount -= 1;
if (!SDL3.refcount) {
close_object(SDL3.handle);
SDL_zero(SDL3.handle);
}
}"""))
print("#endif // IMPLEMENT_DYN_SDL3")
print()
slow_symbols = ("SDL_memset", "SDL_memmove", "SDL_memcpy")
print()
with iterate_sdl3_symbols(sdl3_json) as sdl3_symbol_iterator:
for rettype, name, type_decl in sdl3_symbol_iterator:
if name in slow_symbols:
print(f"#ifndef SDL_SLOW_{name.removeprefix('SDL_').upper()}")
print(f"#ifdef {name}")
print(f"#undef {name}")
print(f"#endif // {name}")
if name in sdl3_used:
print(f"#define {name} SDL3.{name}_symbol")
else:
print(f"#define {name} SDL3_symbol_{name}_is_marked_unused")
if name in slow_symbols:
print(f"#endif // SDL_SLOW_{name.removeprefix('SDL_').upper()}")
print()
print("#else // SDL3_DYNAMIC_LOAD")
print()
print("#define load_sdl3_api() true")
print("#define unload_sdl3_api() do {} while (0)")
print()
print("#endif // SDL3_DYNAMIC_LOAD")
if __name__ == "__main__":
raise SystemExit(main())