#!/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 ") print("#include ") print("#ifndef MORTAR_IMPLEMENT_SDL_MAIN") print("#define SDL_MAIN_HANDLED") print("#endif") print("#include ") print("#include ") 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 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 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())