diff --git a/docker/Dockerfile b/docker/Dockerfile index 63bec2d..4fc1c42 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -12,6 +12,14 @@ RUN set -eux; \ # container run RUN wine wineboot --init +RUN set -eux; \ + curl -fsSL "https://api.github.com/repos/mon/windows-dll-compat-checker/releases/latest" \ + | jq -r '.assets[] | select(.name | contains("linux-x86_64")) | .browser_download_url' \ + | xargs curl -fsSL -o /tmp/dll_compat_checker.zip; \ + unzip /tmp/dll_compat_checker.zip -d /usr/local/bin/; \ + chmod +x /usr/local/bin/windows_dll_compat_checker; \ + rm /tmp/dll_compat_checker.zip; + ENV CROSS_32=/opt/llvm-mingw/toolchain-files/meson/i686-mingw32-clang.ini ENV CROSS_64=/opt/llvm-mingw/toolchain-files/meson/x86_64-mingw32-clang.ini diff --git a/ensure_xp_compatible.py b/ensure_xp_compatible.py deleted file mode 100644 index d65e43b..0000000 --- a/ensure_xp_compatible.py +++ /dev/null @@ -1,122 +0,0 @@ -import argparse -import functools -import os -from collections.abc import Callable -from glob import glob -from typing import TypeAlias - -import pefile -from requests.utils import CaseInsensitiveDict - -parser = argparse.ArgumentParser() -parser.add_argument("dll_path", nargs="+") - -args = parser.parse_args() - -Imports: TypeAlias = dict[str, list[tuple[bytes | None, int | None]]] -Exports: TypeAlias = list[tuple[bytes | None, int | None]] - - -class MissingDLL(Exception): - pass - - -class MissingFunction(Exception): - pass - - -def load_imports(paths: list[str]) -> CaseInsensitiveDict[Imports]: - dlls = CaseInsensitiveDict() - for path in paths: - print(f"Loading imports from {path}") - pe = pefile.PE(path) - imports: Imports = {} - total = 0 - for entry in pe.DIRECTORY_ENTRY_IMPORT: - these = [] - for imp in entry.imports: - these.append((imp.name, imp.ordinal)) - - imports[entry.dll.decode()] = these - total += len(these) - - print(f" ...{total} imports from {len(imports)} DLLs") - - dlls[os.path.basename(path)] = imports - - return dlls - - -def load_exports(path: str) -> Callable[[], Exports]: - @functools.cache - def load(): - print(f"Loading exports from {path}...") - pe = pefile.PE(path) - exports: Exports = [] - for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols: - exports.append((exp.name, exp.ordinal)) - - print(f" ...{len(exports)} exports") - - return exports - - return load - - -available_functions: CaseInsensitiveDict[Callable[[], Exports]] = CaseInsensitiveDict() -for dll in glob("./xp_dlls/*.dll"): - available_functions[os.path.basename(dll)] = load_exports(dll) - -loaded_dlls = load_imports(args.dll_path) - -# https://www.geoffchappell.com/studies/windows/win32/kernel32/api/index.htm -# my test .dll is 32-bit and these are only in 64 -ignore_functions = [ - b"RtlLookupFunctionEntry", - b"RtlUnwindEx", - b"RtlVirtualUnwind", - b"RtlRestoreContext", - b"__C_specific_handler", -] - -exceptions = [] -for path, needed_functions in loaded_dlls.items(): - for dll_name, imports in needed_functions.items(): - if (dll := available_functions.get(dll_name)) is None: - if dll_name not in loaded_dlls: - exceptions.append( - MissingDLL( - f"{path}: Need {dll_name} but it's not in the XP DLLs folder" - ) - ) - continue - - dll = dll() - - for name, ordinal in imports: - if name is not None: - if name in ignore_functions: - continue - - if next((fn for fn in dll if fn[0] == name), None) is None: - exceptions.append( - MissingFunction( - f'{path}: Function "{name}" not present in XP {dll_name}' - ) - ) - continue - elif ordinal is not None: - if next((fn for fn in dll if fn[1] == ordinal), None) is None: - exceptions.append( - MissingFunction( - f"{path}: Function with ordinal {ordinal} not present in XP {dll_name}" - ) - ) - continue - else: - raise RuntimeError(f"{path}: Import with no name or ordinal???") - -if exceptions: - raise ExceptionGroup("Failures", exceptions) - -print("All functions present!") diff --git a/meson.build b/meson.build index 581097e..c20b6f3 100644 --- a/meson.build +++ b/meson.build @@ -111,7 +111,12 @@ if host_machine.cpu_family() == 'x86' ) endif -python = import('python').find_installation() +windows_dll_compat_checker = find_program('windows_dll_compat_checker', required: false) +if host_machine.cpu_family() == 'x86_64' + xp_check_ini = 'PREMADE/winxp_x86_64.ini' +else + xp_check_ini = 'PREMADE/winxp_x86_64_32bit_dlls.ini' +endif foreach cfg : special_cfgs folder_name = cfg[0] @@ -139,14 +144,16 @@ foreach cfg : special_cfgs install_dir: install_dir, install: true, ) - test(lib_name + '_is_xp', - python, - args: [ - files('ensure_xp_compatible.py'), - ifs_dll, - ], - workdir: meson.current_source_dir(), - ) + if windows_dll_compat_checker.found() + test(lib_name + '_is_xp', + windows_dll_compat_checker, + args: [ + ifs_dll, + '-s', xp_check_ini, + ], + workdir: meson.current_source_dir(), + ) + endif endforeach injector_cfgs = [ @@ -183,14 +190,16 @@ foreach cfg : injector_cfgs install: true, ) - test(dll_name + '_is_xp', - python, - args: [ - files('ensure_xp_compatible.py'), - inject_dll, - ], - workdir: meson.current_source_dir(), - ) + if windows_dll_compat_checker.found() + test(dll_name + '_is_xp', + windows_dll_compat_checker, + args: [ + inject_dll, + '-s', xp_check_ini, + ], + workdir: meson.current_source_dir(), + ) + endif endforeach if host_machine.cpu_family() == 'x86_64' diff --git a/test.sh b/test.sh index f3f25ce..eecb8e6 100644 --- a/test.sh +++ b/test.sh @@ -1,3 +1,6 @@ #!/usr/bin/env bash +set -eu + +meson test -C build32 --print-errorlogs "$@" meson test -C build64 --print-errorlogs "$@" diff --git a/xp_dlls/README.md b/xp_dlls/README.md deleted file mode 100644 index d6b9ecb..0000000 --- a/xp_dlls/README.md +++ /dev/null @@ -1 +0,0 @@ -Taken from my Jubeat cab's XP image diff --git a/xp_dlls/kernel32.dll b/xp_dlls/kernel32.dll deleted file mode 100644 index 56bae30..0000000 Binary files a/xp_dlls/kernel32.dll and /dev/null differ diff --git a/xp_dlls/msvcrt.dll b/xp_dlls/msvcrt.dll deleted file mode 100644 index ed68df5..0000000 Binary files a/xp_dlls/msvcrt.dll and /dev/null differ diff --git a/xp_dlls/shell32.dll b/xp_dlls/shell32.dll deleted file mode 100644 index 277aff1..0000000 Binary files a/xp_dlls/shell32.dll and /dev/null differ