From b1079b9c1767420569eacf38ab25ce863bd81a9c Mon Sep 17 00:00:00 2001 From: Karim Date: Tue, 3 Mar 2026 15:05:16 +0000 Subject: [PATCH 01/14] windows: first attempt --- .github/workflows/build-wheels.yml | 103 +++++++++++++++++++++++++---- .github/workflows/coverage.yml | 81 ++++++++++++++++++++--- MANIFEST.in | 4 ++ pyproject.toml | 5 ++ rayforce/__init__.py | 8 ++- rayforce/capi/rayforce_c.h | 2 + rayforce/cli.py | 12 ++-- scripts/prepare_build_windows.ps1 | 94 ++++++++++++++++++++++++++ setup.py | 12 +++- 9 files changed, 288 insertions(+), 33 deletions(-) create mode 100644 scripts/prepare_build_windows.ps1 diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 64a24d5..3d01fdf 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -17,6 +17,9 @@ jobs: - os: macos-14 platform: macos arch: arm64 + - os: windows-2022 + platform: windows + arch: x86_64 runs-on: ${{ matrix.os }} env: @@ -48,7 +51,8 @@ jobs: restore-keys: | cibw-${{ runner.os }}-${{ matrix.platform }}-${{ matrix.arch }}- - - name: Build wheels + - name: Build wheels (Linux) + if: matrix.platform == 'linux' env: CIBW_BUILD: "cp311-* cp312-* cp313-* cp314-*" CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux_*" @@ -56,17 +60,39 @@ jobs: CIBW_BEFORE_BUILD: "bash {project}/scripts/prepare_build.sh" CIBW_TEST_SKIP: "*" CIBW_ENVIRONMENT: "RAYFORCE_GITHUB=https://github.com/RayforceDB/rayforce.git" - CIBW_MACOS_ENVIRONMENT: "MACOSX_DEPLOYMENT_TARGET=11.0" CIBW_CACHE_PATH: ".cibw" CIBW_MANYLINUX_X86_64_IMAGE: "quay.io/pypa/manylinux_2_28_x86_64:latest" CIBW_BEFORE_ALL_LINUX: "yum install -y git gcc clang gcc-c++ make" + run: cibuildwheel --platform linux --archs x86_64 --output-dir wheelhouse - run: | - if [ "${{ matrix.platform }}" = "linux" ]; then - cibuildwheel --platform linux --archs x86_64 --output-dir wheelhouse - else - cibuildwheel --platform macos --archs arm64 --output-dir wheelhouse - fi + - name: Build wheels (macOS) + if: matrix.platform == 'macos' + env: + CIBW_BUILD: "cp311-* cp312-* cp313-* cp314-*" + CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux_*" + CIBW_ARCHS: "auto64" + CIBW_BEFORE_BUILD: "bash {project}/scripts/prepare_build.sh" + CIBW_TEST_SKIP: "*" + CIBW_ENVIRONMENT: "RAYFORCE_GITHUB=https://github.com/RayforceDB/rayforce.git" + CIBW_MACOS_ENVIRONMENT: "MACOSX_DEPLOYMENT_TARGET=11.0" + CIBW_CACHE_PATH: ".cibw" + run: cibuildwheel --platform macos --archs arm64 --output-dir wheelhouse + + - name: Build wheels (Windows) + if: matrix.platform == 'windows' + env: + CIBW_BUILD: "cp311-* cp312-* cp313-* cp314-*" + CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux_*" + CIBW_ARCHS: "auto64" + CIBW_BEFORE_ALL_WINDOWS: >- + C:\msys64\usr\bin\bash -lc "pacman -S --noconfirm mingw-w64-x86_64-clang mingw-w64-x86_64-lld mingw-w64-x86_64-make" + CIBW_BEFORE_BUILD_WINDOWS: "powershell -ExecutionPolicy Bypass -File {project}\\scripts\\prepare_build_windows.ps1" + CIBW_TEST_SKIP: "*" + CIBW_ENVIRONMENT_WINDOWS: >- + RAYFORCE_GITHUB=https://github.com/RayforceDB/rayforce.git + PATH="C:\\msys64\\mingw64\\bin;C:\\msys64\\usr\\bin;$PATH" + CIBW_CACHE_PATH: ".cibw" + run: cibuildwheel --platform windows --archs AMD64 --output-dir wheelhouse - name: Upload wheel artifacts uses: actions/upload-artifact@v4 @@ -113,6 +139,22 @@ jobs: python-version: '3.14' platform: macos arch: arm64 + - os: windows-2022 + python-version: '3.11' + platform: windows + arch: x86_64 + - os: windows-2022 + python-version: '3.12' + platform: windows + arch: x86_64 + - os: windows-2022 + python-version: '3.13' + platform: windows + arch: x86_64 + - os: windows-2022 + python-version: '3.14' + platform: windows + arch: x86_64 runs-on: ${{ matrix.os }} @@ -122,7 +164,7 @@ jobs: with: submodules: false - - name: Verify AVX2 support (required for rayforce) + - name: Verify AVX2 support (Linux) if: runner.os == 'Linux' run: | echo "Checking CPU features..." @@ -133,6 +175,15 @@ jobs: exit 1 fi + - name: Verify AVX2 support (Windows) + if: runner.os == 'Windows' + shell: powershell + run: | + $cpu = Get-CimInstance Win32_Processor + Write-Host "CPU: $($cpu.Name)" + # GitHub Actions Windows runners use modern Xeon/EPYC CPUs with AVX2 + Write-Host "AVX2 support assumed on GitHub Actions Windows runners" + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: @@ -150,18 +201,27 @@ jobs: python -m pip install --upgrade pip pip install pytest pytest-asyncio pytest-timeout websockets pandas>=2.0.0 polars>=0.19.0 pyarrow sqlglot - - name: Remove local rayforce package to force pip installation + - name: Remove local rayforce package (Unix) + if: runner.os != 'Windows' run: | - # Remove local rayforce directory to ensure we test against installed wheel rm -rf rayforce/__pycache__ - rm -rf rayforce/*.so rayforce/*.dylib rayforce/*.dll + rm -rf rayforce/*.so rayforce/*.dylib rayforce/*.dll rayforce/*.pyd rm -rf rayforce/plugins/*.so rayforce/plugins/*.dylib rayforce/plugins/*.dll - # Move rayforce directory out of Python path temporarily if [ -d "rayforce" ]; then mv rayforce rayforce.local fi - - name: Install and test wheel + - name: Remove local rayforce package (Windows) + if: runner.os == 'Windows' + shell: powershell + run: | + if (Test-Path "rayforce\__pycache__") { Remove-Item -Recurse -Force "rayforce\__pycache__" } + Get-ChildItem -Path "rayforce" -Include "*.so","*.dylib","*.dll","*.pyd" -ErrorAction SilentlyContinue | Remove-Item -Force + Get-ChildItem -Path "rayforce\plugins" -Include "*.so","*.dylib","*.dll" -ErrorAction SilentlyContinue | Remove-Item -Force + if (Test-Path "rayforce") { Rename-Item "rayforce" "rayforce.local" } + + - name: Install and test wheel (Unix) + if: runner.os != 'Windows' run: | PYVER=$(echo '${{ matrix.python-version }}' | tr -d '.') WHEEL_FILE=$(find wheels -name "*cp${PYVER}*.whl" | head -1) @@ -174,6 +234,21 @@ jobs: pip install "$WHEEL_FILE" --force-reinstall --no-deps python -m pytest -m "" -x -vv --durations=20 tests/ + - name: Install and test wheel (Windows) + if: runner.os == 'Windows' + shell: powershell + run: | + $pyver = '${{ matrix.python-version }}' -replace '\.','' + $wheel = Get-ChildItem -Path wheels -Filter "*cp${pyver}*win_amd64*.whl" | Select-Object -First 1 + if (-not $wheel) { + Write-Error "No wheel file found for Python ${{ matrix.python-version }}" + Get-ChildItem -Path wheels -Filter "*.whl" + exit 1 + } + Write-Host "Installing wheel: $($wheel.FullName)" + pip install $wheel.FullName --force-reinstall --no-deps + python -m pytest -m "" -x -vv --durations=20 tests/ + publish: name: Publish to PyPI needs: test_wheels diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 8fd38d8..dbf1640 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,13 +1,55 @@ name: Coverage on: + pull_request: push: branches: [master] jobs: coverage: - name: Coverage - runs-on: ubuntu-latest + name: Coverage (${{ matrix.os }}, Python ${{ matrix.python-version }}) + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-22.04 + python-version: '3.11' + platform: linux + - os: ubuntu-22.04 + python-version: '3.12' + platform: linux + - os: ubuntu-22.04 + python-version: '3.13' + platform: linux + - os: ubuntu-22.04 + python-version: '3.14' + platform: linux + - os: macos-14 + python-version: '3.11' + platform: macos + - os: macos-14 + python-version: '3.12' + platform: macos + - os: macos-14 + python-version: '3.13' + platform: macos + - os: macos-14 + python-version: '3.14' + platform: macos + - os: windows-2022 + python-version: '3.11' + platform: windows + - os: windows-2022 + python-version: '3.12' + platform: windows + - os: windows-2022 + python-version: '3.13' + platform: windows + - os: windows-2022 + python-version: '3.14' + platform: windows + + runs-on: ${{ matrix.os }} env: RAYFORCE_GITHUB: "https://github.com/RayforceDB/rayforce.git" @@ -18,25 +60,46 @@ jobs: with: submodules: false - - name: Set up Python 3.14 + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: - python-version: '3.14' + python-version: ${{ matrix.python-version }} cache: 'pip' - - name: Install system dependencies + - name: Install system dependencies (Linux) + if: matrix.platform == 'linux' run: | sudo apt-get update -qq sudo apt-get install -y -qq git gcc clang g++ make + - name: Install system dependencies (macOS) + if: matrix.platform == 'macos' + run: | + echo "Xcode CLI tools provide clang and make" + + - name: Install system dependencies (Windows) + if: matrix.platform == 'windows' + shell: powershell + run: | + C:\msys64\usr\bin\bash -lc "pacman -S --noconfirm mingw-w64-x86_64-clang mingw-w64-x86_64-lld mingw-w64-x86_64-make" + - name: Install build dependencies run: | python -m pip install --upgrade pip pip install setuptools wheel setuptools-scm - - name: Build rayforce binaries + - name: Build rayforce binaries (Unix) + if: matrix.platform != 'windows' run: make app + - name: Build rayforce binaries (Windows) + if: matrix.platform == 'windows' + shell: powershell + env: + PATH: "C:\\msys64\\mingw64\\bin;C:\\msys64\\usr\\bin;${{ env.PATH }}" + run: | + powershell -ExecutionPolicy Bypass -File scripts/prepare_build_windows.ps1 + - name: Install test dependencies run: | pip install -e ".[dev,test-plugins]" @@ -54,13 +117,13 @@ jobs: uses: codecov/codecov-action@v4 with: file: ./coverage.xml - flags: unittests - name: codecov-umbrella + flags: ${{ matrix.platform }}-py${{ matrix.python-version }} + name: coverage-${{ matrix.platform }}-py${{ matrix.python-version }} fail_ci_if_error: false - name: Upload coverage HTML report uses: actions/upload-artifact@v4 with: - name: coverage-report + name: coverage-report-${{ matrix.platform }}-py${{ matrix.python-version }} path: htmlcov/ retention-days: 30 diff --git a/MANIFEST.in b/MANIFEST.in index 3dd36ff..6abeb21 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,7 +2,11 @@ include README.md include LICENSE recursive-include rayforce *.so recursive-include rayforce *.dylib +recursive-include rayforce *.dll +recursive-include rayforce *.pyd recursive-include rayforce/plugins *.so recursive-include rayforce/plugins *.dylib +recursive-include rayforce/plugins *.dll recursive-include rayforce *.pyi include rayforce/bin/rayforce +include rayforce/bin/rayforce.exe diff --git a/pyproject.toml b/pyproject.toml index 745c6a6..2b1a97d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,6 +73,11 @@ before-all = [ "yum install -y clang llvm", ] +[tool.cibuildwheel.windows] +before-all = "C:\\msys64\\usr\\bin\\bash -lc \"pacman -S --noconfirm mingw-w64-x86_64-clang mingw-w64-x86_64-lld mingw-w64-x86_64-make\"" +before-build = "powershell -ExecutionPolicy Bypass -File {project}\\scripts\\prepare_build_windows.ps1" +environment = { PATH = "C:\\msys64\\mingw64\\bin;C:\\msys64\\usr\\bin;$PATH", RAYFORCE_GITHUB = "https://github.com/RayforceDB/rayforce.git" } + [tool.ruff] show-fixes = true target-version = "py314" diff --git a/rayforce/__init__.py b/rayforce/__init__.py index 080e7ac..f8aaf04 100644 --- a/rayforce/__init__.py +++ b/rayforce/__init__.py @@ -19,7 +19,8 @@ lib_name = "_rayforce_c.so" raykx_lib_name = "libraykx.dylib" elif sys.platform == "win32": - lib_name = "rayforce.dll" + lib_name = "_rayforce_c.pyd" + raykx_lib_name = "raykx.dll" else: raise ImportError(f"Platform not supported: {sys.platform}") @@ -27,8 +28,9 @@ raykx_lib_path = Path(__file__).resolve().parent / "plugins" / raykx_lib_name if lib_path.exists() and raykx_lib_path.exists(): try: - ctypes.CDLL(str(lib_path), mode=ctypes.RTLD_GLOBAL) - ctypes.CDLL(str(raykx_lib_path), mode=ctypes.RTLD_GLOBAL) + load_mode = 0 if sys.platform == "win32" else ctypes.RTLD_GLOBAL + ctypes.CDLL(str(lib_path), mode=load_mode) + ctypes.CDLL(str(raykx_lib_path), mode=load_mode) except Exception as e: raise ImportError(f"Error loading CDLL: {e}") from e else: diff --git a/rayforce/capi/rayforce_c.h b/rayforce/capi/rayforce_c.h index 92602eb..3c30a7c 100644 --- a/rayforce/capi/rayforce_c.h +++ b/rayforce/capi/rayforce_c.h @@ -39,7 +39,9 @@ #include "vary.h" #include #include +#ifndef _WIN32 #include +#endif #ifndef memcpy extern void *memcpy(void *dest, const void *src, size_t n); diff --git a/rayforce/cli.py b/rayforce/cli.py index e9de2e4..ff7e1c6 100644 --- a/rayforce/cli.py +++ b/rayforce/cli.py @@ -6,15 +6,17 @@ def find_rayforce_executable(): package_dir = Path(__file__).parent - bundled_executable = package_dir / "bin" / "rayforce" + exe_name = "rayforce.exe" if sys.platform == "win32" else "rayforce" - if bundled_executable.exists() and os.access(bundled_executable, os.X_OK): + bundled_executable = package_dir / "bin" / exe_name + if bundled_executable.exists() and ( + sys.platform == "win32" or os.access(bundled_executable, os.X_OK) + ): return str(bundled_executable) project_root = package_dir.parent - dev_executable = project_root / "tmp" / "rayforce-c" / "rayforce" - - if dev_executable.exists() and os.access(dev_executable, os.X_OK): + dev_executable = project_root / "tmp" / "rayforce-c" / exe_name + if dev_executable.exists() and (sys.platform == "win32" or os.access(dev_executable, os.X_OK)): return str(dev_executable) raise FileNotFoundError("Rayforce executable not found. Try to reinstall the library") diff --git a/scripts/prepare_build_windows.ps1 b/scripts/prepare_build_windows.ps1 new file mode 100644 index 0000000..618e88d --- /dev/null +++ b/scripts/prepare_build_windows.ps1 @@ -0,0 +1,94 @@ +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest + +$EXEC_DIR = Get-Location +$RAYFORCE_GITHUB = if ($env:RAYFORCE_GITHUB) { $env:RAYFORCE_GITHUB } else { "https://github.com/RayforceDB/rayforce.git" } + +$PYTHON_BIN = if ($env:PYTHON_BIN) { $env:PYTHON_BIN } else { "python" } +$PYTHON_VERSION = & $PYTHON_BIN --version 2>&1 | ForEach-Object { $_.ToString().Split(" ")[1] } + +Write-Host "Python version: $PYTHON_VERSION" +Write-Host "Using rayforce repo: $RAYFORCE_GITHUB" + +# --- Clean previous build artifacts --- +Write-Host "Cleaning previous build artifacts..." +$cleanPaths = @( + "$EXEC_DIR\tmp\rayforce-c", + "$EXEC_DIR\rayforce\rayforce", + "$EXEC_DIR\rayforce\bin", + "$EXEC_DIR\rayforce\_rayforce_c.pyd", + "$EXEC_DIR\rayforce\_rayforce_c.so", + "$EXEC_DIR\rayforce\rayforce.dll", + "$EXEC_DIR\build" +) +foreach ($p in $cleanPaths) { + if (Test-Path $p) { Remove-Item -Recurse -Force $p } +} +Get-ChildItem -Path $EXEC_DIR -Filter "*.egg-info" -Directory -Recurse -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force +Get-ChildItem -Path $EXEC_DIR -Filter "__pycache__" -Directory -Recurse -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force +# Clean plugin DLLs +Get-ChildItem -Path "$EXEC_DIR\rayforce\plugins" -Filter "libraykx.*" -ErrorAction SilentlyContinue | Remove-Item -Force +Get-ChildItem -Path "$EXEC_DIR\rayforce\plugins" -Filter "raykx.*" -ErrorAction SilentlyContinue | Remove-Item -Force + +# --- Clone rayforce repo --- +Write-Host "Cloning rayforce repo from GitHub..." +git clone $RAYFORCE_GITHUB "$EXEC_DIR\tmp\rayforce-c" +Copy-Item -Recurse "$EXEC_DIR\tmp\rayforce-c\core" "$EXEC_DIR\rayforce\rayforce" + +# --- Get Python build info --- +$PYTHON_INCLUDE = & $PYTHON_BIN -c "import sysconfig; print(sysconfig.get_config_var('INCLUDEPY'))" +$PYTHON_PYVER = & $PYTHON_BIN -c "import sys; print(f'{sys.version_info.major}{sys.version_info.minor}')" +$PYTHON_LIB_DIR = & $PYTHON_BIN -c "import sysconfig, os; print(os.path.dirname(sysconfig.get_config_var('INCLUDEPY'))+'\\libs')" + +Write-Host "Python include: $PYTHON_INCLUDE" +Write-Host "Python version tag: $PYTHON_PYVER" +Write-Host "Python lib dir: $PYTHON_LIB_DIR" + +# --- Patch Makefile for Python target --- +Write-Host "Patching Makefile for Python support..." +$pythonTarget = @" + +PY_OBJECTS = core/rayforce_c.o core/raypy_init_from_py.o core/raypy_init_from_buffer.o core/raypy_read_from_rf.o core/raypy_queries.o core/raypy_io.o core/raypy_binary.o core/raypy_dynlib.o core/raypy_eval.o core/raypy_iter.o core/raypy_serde.o +PY_APP_OBJECTS = app/term.o +python: CFLAGS = `$(RELEASE_CFLAGS) -DPY_SSIZE_T_CLEAN -I$PYTHON_INCLUDE -Wno-macro-redefined +python: LDFLAGS = `$(RELEASE_LDFLAGS) -L$PYTHON_LIB_DIR -lpython$PYTHON_PYVER +python: `$(CORE_OBJECTS) `$(PY_OBJECTS) `$(PY_APP_OBJECTS) + `$(CC) -shared -o _rayforce_c.pyd `$(CFLAGS) `$(CORE_OBJECTS) `$(PY_OBJECTS) `$(PY_APP_OBJECTS) `$(LIBS) `$(LDFLAGS) +"@ +Add-Content -Path "$EXEC_DIR\tmp\rayforce-c\Makefile" -Value $pythonTarget + +# --- Copy C API source files --- +Write-Host "Copying C API source files..." +$capiFiles = @( + "rayforce_c.c", "rayforce_c.h", + "raypy_init_from_py.c", "raypy_init_from_buffer.c", "raypy_read_from_rf.c", + "raypy_queries.c", "raypy_io.c", "raypy_binary.c", + "raypy_dynlib.c", "raypy_eval.c", "raypy_iter.c", "raypy_serde.c" +) +foreach ($f in $capiFiles) { + Copy-Item "$EXEC_DIR\rayforce\capi\$f" "$EXEC_DIR\tmp\rayforce-c\core\$f" +} + +# --- Build Python extension --- +Write-Host "Building Rayforce Python extension..." +Push-Location "$EXEC_DIR\tmp\rayforce-c" +make python +Pop-Location +Copy-Item "$EXEC_DIR\tmp\rayforce-c\_rayforce_c.pyd" "$EXEC_DIR\rayforce\_rayforce_c.pyd" + +# --- Build Raykx plugin --- +Write-Host "Building Raykx plugin..." +Push-Location "$EXEC_DIR\tmp\rayforce-c\ext\raykx" +make release +Pop-Location +Copy-Item "$EXEC_DIR\tmp\rayforce-c\ext\raykx\raykx.dll" "$EXEC_DIR\rayforce\plugins\raykx.dll" + +# --- Build Rayforce executable --- +Write-Host "Building Rayforce executable..." +Push-Location "$EXEC_DIR\tmp\rayforce-c" +make release +Pop-Location +New-Item -ItemType Directory -Force -Path "$EXEC_DIR\rayforce\bin" | Out-Null +Copy-Item "$EXEC_DIR\tmp\rayforce-c\rayforce.exe" "$EXEC_DIR\rayforce\bin\rayforce.exe" + +Write-Host "Build preparation complete!" diff --git a/setup.py b/setup.py index fd45d8e..9627c45 100644 --- a/setup.py +++ b/setup.py @@ -12,8 +12,16 @@ def has_ext_modules(self): version="0.6.1", packages=find_packages(), package_data={ - "rayforce": ["*.so", "*.dylib", "*.pyi", "bin/rayforce"], - "rayforce.plugins": ["*.so", "*.dylib"], + "rayforce": [ + "*.so", + "*.dylib", + "*.dll", + "*.pyd", + "*.pyi", + "bin/rayforce", + "bin/rayforce.exe", + ], + "rayforce.plugins": ["*.so", "*.dylib", "*.dll"], }, include_package_data=True, zip_safe=False, From e282c18762129f0ca28ad2d4f04d7e6d6b8e92f5 Mon Sep 17 00:00:00 2001 From: Karim Date: Tue, 3 Mar 2026 15:08:17 +0000 Subject: [PATCH 02/14] 2 attempt --- .github/workflows/build-wheels.yml | 2 +- .github/workflows/coverage.yml | 2 +- Makefile | 2 +- pyproject.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 3d01fdf..3a69b1d 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -86,7 +86,7 @@ jobs: CIBW_ARCHS: "auto64" CIBW_BEFORE_ALL_WINDOWS: >- C:\msys64\usr\bin\bash -lc "pacman -S --noconfirm mingw-w64-x86_64-clang mingw-w64-x86_64-lld mingw-w64-x86_64-make" - CIBW_BEFORE_BUILD_WINDOWS: "powershell -ExecutionPolicy Bypass -File {project}\\scripts\\prepare_build_windows.ps1" + CIBW_BEFORE_BUILD_WINDOWS: "powershell -ExecutionPolicy Bypass -File {project}/scripts/prepare_build_windows.ps1" CIBW_TEST_SKIP: "*" CIBW_ENVIRONMENT_WINDOWS: >- RAYFORCE_GITHUB=https://github.com/RayforceDB/rayforce.git diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index dbf1640..4bce7a9 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -98,7 +98,7 @@ jobs: env: PATH: "C:\\msys64\\mingw64\\bin;C:\\msys64\\usr\\bin;${{ env.PATH }}" run: | - powershell -ExecutionPolicy Bypass -File scripts/prepare_build_windows.ps1 + & scripts/prepare_build_windows.ps1 - name: Install test dependencies run: | diff --git a/Makefile b/Makefile index e6ff890..85f3242 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ ifeq ($(UNAME_S),Darwin) RAYKX_LIB_NAME = libraykx.dylib RELEASE_LDFLAGS = $(shell python3 -c "import sysconfig; print(sysconfig.get_config_var('LDFLAGS') or '')") PYTHON_VERSION = $(shell python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')") - SHARED_COMPILE_FLAGS = -lpython$(PYTHON_VERSION) + SHARED_COMPILE_FLAGS = -undefined dynamic_lookup else ifeq ($(UNAME_S),Linux) RAYKX_LIB_NAME = libraykx.so RELEASE_LDFLAGS = $$(RELEASE_LDFLAGS) diff --git a/pyproject.toml b/pyproject.toml index 2b1a97d..6dfed58 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,7 +75,7 @@ before-all = [ [tool.cibuildwheel.windows] before-all = "C:\\msys64\\usr\\bin\\bash -lc \"pacman -S --noconfirm mingw-w64-x86_64-clang mingw-w64-x86_64-lld mingw-w64-x86_64-make\"" -before-build = "powershell -ExecutionPolicy Bypass -File {project}\\scripts\\prepare_build_windows.ps1" +before-build = "powershell -ExecutionPolicy Bypass -File {project}/scripts/prepare_build_windows.ps1" environment = { PATH = "C:\\msys64\\mingw64\\bin;C:\\msys64\\usr\\bin;$PATH", RAYFORCE_GITHUB = "https://github.com/RayforceDB/rayforce.git" } [tool.ruff] From 8d7351ed3b37a3e6ad3ccae4858f840940116217 Mon Sep 17 00:00:00 2001 From: Karim Date: Tue, 3 Mar 2026 15:10:05 +0000 Subject: [PATCH 03/14] 3 attempt --- scripts/prepare_build_windows.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/prepare_build_windows.ps1 b/scripts/prepare_build_windows.ps1 index 618e88d..8e945b9 100644 --- a/scripts/prepare_build_windows.ps1 +++ b/scripts/prepare_build_windows.ps1 @@ -1,6 +1,9 @@ $ErrorActionPreference = "Stop" Set-StrictMode -Version Latest +# Ensure MSYS2/MinGW and Git for Windows are on PATH +$env:PATH = "C:\msys64\mingw64\bin;C:\msys64\usr\bin;C:\Program Files\Git\cmd;$env:PATH" + $EXEC_DIR = Get-Location $RAYFORCE_GITHUB = if ($env:RAYFORCE_GITHUB) { $env:RAYFORCE_GITHUB } else { "https://github.com/RayforceDB/rayforce.git" } From 9d17d13ee61d698a895a86b9cb677f24841f6681 Mon Sep 17 00:00:00 2001 From: Karim Date: Tue, 3 Mar 2026 15:12:24 +0000 Subject: [PATCH 04/14] 4 attempt --- .github/workflows/build-wheels.yml | 2 +- .github/workflows/coverage.yml | 2 +- pyproject.toml | 2 +- scripts/prepare_build_windows.ps1 | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 3a69b1d..b924384 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -85,7 +85,7 @@ jobs: CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux_*" CIBW_ARCHS: "auto64" CIBW_BEFORE_ALL_WINDOWS: >- - C:\msys64\usr\bin\bash -lc "pacman -S --noconfirm mingw-w64-x86_64-clang mingw-w64-x86_64-lld mingw-w64-x86_64-make" + C:\msys64\usr\bin\bash -lc "pacman -S --noconfirm mingw-w64-x86_64-clang mingw-w64-x86_64-lld make" CIBW_BEFORE_BUILD_WINDOWS: "powershell -ExecutionPolicy Bypass -File {project}/scripts/prepare_build_windows.ps1" CIBW_TEST_SKIP: "*" CIBW_ENVIRONMENT_WINDOWS: >- diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 4bce7a9..7c6ed05 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -81,7 +81,7 @@ jobs: if: matrix.platform == 'windows' shell: powershell run: | - C:\msys64\usr\bin\bash -lc "pacman -S --noconfirm mingw-w64-x86_64-clang mingw-w64-x86_64-lld mingw-w64-x86_64-make" + C:\msys64\usr\bin\bash -lc "pacman -S --noconfirm mingw-w64-x86_64-clang mingw-w64-x86_64-lld make" - name: Install build dependencies run: | diff --git a/pyproject.toml b/pyproject.toml index 6dfed58..cc251f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ before-all = [ ] [tool.cibuildwheel.windows] -before-all = "C:\\msys64\\usr\\bin\\bash -lc \"pacman -S --noconfirm mingw-w64-x86_64-clang mingw-w64-x86_64-lld mingw-w64-x86_64-make\"" +before-all = "C:\\msys64\\usr\\bin\\bash -lc \"pacman -S --noconfirm mingw-w64-x86_64-clang mingw-w64-x86_64-lld make\"" before-build = "powershell -ExecutionPolicy Bypass -File {project}/scripts/prepare_build_windows.ps1" environment = { PATH = "C:\\msys64\\mingw64\\bin;C:\\msys64\\usr\\bin;$PATH", RAYFORCE_GITHUB = "https://github.com/RayforceDB/rayforce.git" } diff --git a/scripts/prepare_build_windows.ps1 b/scripts/prepare_build_windows.ps1 index 8e945b9..97cdf0e 100644 --- a/scripts/prepare_build_windows.ps1 +++ b/scripts/prepare_build_windows.ps1 @@ -75,21 +75,21 @@ foreach ($f in $capiFiles) { # --- Build Python extension --- Write-Host "Building Rayforce Python extension..." Push-Location "$EXEC_DIR\tmp\rayforce-c" -make python +C:\msys64\usr\bin\make.exe python Pop-Location Copy-Item "$EXEC_DIR\tmp\rayforce-c\_rayforce_c.pyd" "$EXEC_DIR\rayforce\_rayforce_c.pyd" # --- Build Raykx plugin --- Write-Host "Building Raykx plugin..." Push-Location "$EXEC_DIR\tmp\rayforce-c\ext\raykx" -make release +C:\msys64\usr\bin\make.exe release Pop-Location Copy-Item "$EXEC_DIR\tmp\rayforce-c\ext\raykx\raykx.dll" "$EXEC_DIR\rayforce\plugins\raykx.dll" # --- Build Rayforce executable --- Write-Host "Building Rayforce executable..." Push-Location "$EXEC_DIR\tmp\rayforce-c" -make release +C:\msys64\usr\bin\make.exe release Pop-Location New-Item -ItemType Directory -Force -Path "$EXEC_DIR\rayforce\bin" | Out-Null Copy-Item "$EXEC_DIR\tmp\rayforce-c\rayforce.exe" "$EXEC_DIR\rayforce\bin\rayforce.exe" From a6313c0b085b4fa3f9f7e5b9bb03f01fb45d24ab Mon Sep 17 00:00:00 2001 From: Karim Date: Tue, 3 Mar 2026 15:16:03 +0000 Subject: [PATCH 05/14] 5 attempt --- scripts/prepare_build_windows.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/prepare_build_windows.ps1 b/scripts/prepare_build_windows.ps1 index 97cdf0e..fe13325 100644 --- a/scripts/prepare_build_windows.ps1 +++ b/scripts/prepare_build_windows.ps1 @@ -54,7 +54,7 @@ $pythonTarget = @" PY_OBJECTS = core/rayforce_c.o core/raypy_init_from_py.o core/raypy_init_from_buffer.o core/raypy_read_from_rf.o core/raypy_queries.o core/raypy_io.o core/raypy_binary.o core/raypy_dynlib.o core/raypy_eval.o core/raypy_iter.o core/raypy_serde.o PY_APP_OBJECTS = app/term.o python: CFLAGS = `$(RELEASE_CFLAGS) -DPY_SSIZE_T_CLEAN -I$PYTHON_INCLUDE -Wno-macro-redefined -python: LDFLAGS = `$(RELEASE_LDFLAGS) -L$PYTHON_LIB_DIR -lpython$PYTHON_PYVER +python: LDFLAGS = -fuse-ld=lld -L$PYTHON_LIB_DIR -lpython$PYTHON_PYVER python: `$(CORE_OBJECTS) `$(PY_OBJECTS) `$(PY_APP_OBJECTS) `$(CC) -shared -o _rayforce_c.pyd `$(CFLAGS) `$(CORE_OBJECTS) `$(PY_OBJECTS) `$(PY_APP_OBJECTS) `$(LIBS) `$(LDFLAGS) "@ From 6b4840921a588ceaca273be4bd34ce3be162943a Mon Sep 17 00:00:00 2001 From: Karim Date: Tue, 3 Mar 2026 15:19:09 +0000 Subject: [PATCH 06/14] 6 attempt --- scripts/prepare_build_windows.ps1 | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/scripts/prepare_build_windows.ps1 b/scripts/prepare_build_windows.ps1 index fe13325..d617bd6 100644 --- a/scripts/prepare_build_windows.ps1 +++ b/scripts/prepare_build_windows.ps1 @@ -72,20 +72,26 @@ foreach ($f in $capiFiles) { Copy-Item "$EXEC_DIR\rayforce\capi\$f" "$EXEC_DIR\tmp\rayforce-c\core\$f" } -# --- Build Python extension --- -Write-Host "Building Rayforce Python extension..." +# --- Build shared library (produces rayforce.dll + rayforce.lib needed by raykx) --- +Write-Host "Building Rayforce shared library..." Push-Location "$EXEC_DIR\tmp\rayforce-c" -C:\msys64\usr\bin\make.exe python +C:\msys64\usr\bin\make.exe shared Pop-Location -Copy-Item "$EXEC_DIR\tmp\rayforce-c\_rayforce_c.pyd" "$EXEC_DIR\rayforce\_rayforce_c.pyd" -# --- Build Raykx plugin --- +# --- Build Raykx plugin (depends on rayforce.lib from shared build) --- Write-Host "Building Raykx plugin..." Push-Location "$EXEC_DIR\tmp\rayforce-c\ext\raykx" C:\msys64\usr\bin\make.exe release Pop-Location Copy-Item "$EXEC_DIR\tmp\rayforce-c\ext\raykx\raykx.dll" "$EXEC_DIR\rayforce\plugins\raykx.dll" +# --- Build Python extension --- +Write-Host "Building Rayforce Python extension..." +Push-Location "$EXEC_DIR\tmp\rayforce-c" +C:\msys64\usr\bin\make.exe python +Pop-Location +Copy-Item "$EXEC_DIR\tmp\rayforce-c\_rayforce_c.pyd" "$EXEC_DIR\rayforce\_rayforce_c.pyd" + # --- Build Rayforce executable --- Write-Host "Building Rayforce executable..." Push-Location "$EXEC_DIR\tmp\rayforce-c" From 1d65f8c60b562470d87451a076ff16dfd20d2a7a Mon Sep 17 00:00:00 2001 From: Karim Date: Tue, 3 Mar 2026 15:24:15 +0000 Subject: [PATCH 07/14] 7 attempt --- scripts/prepare_build_windows.ps1 | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/prepare_build_windows.ps1 b/scripts/prepare_build_windows.ps1 index d617bd6..609a97b 100644 --- a/scripts/prepare_build_windows.ps1 +++ b/scripts/prepare_build_windows.ps1 @@ -73,15 +73,19 @@ foreach ($f in $capiFiles) { } # --- Build shared library (produces rayforce.dll + rayforce.lib needed by raykx) --- +# Override RELEASE_LDFLAGS: upstream uses MSVC-style /DEF:/IMPLIB: flags which MinGW lld doesn't understand. +# Use MinGW-compatible --out-implib to generate the import library. Write-Host "Building Rayforce shared library..." Push-Location "$EXEC_DIR\tmp\rayforce-c" -C:\msys64\usr\bin\make.exe shared +C:\msys64\usr\bin\make.exe shared "RELEASE_LDFLAGS=-fuse-ld=lld -Wl,--out-implib,rayforce.lib" +if ($LASTEXITCODE -ne 0) { throw "make shared failed with exit code $LASTEXITCODE" } Pop-Location # --- Build Raykx plugin (depends on rayforce.lib from shared build) --- Write-Host "Building Raykx plugin..." Push-Location "$EXEC_DIR\tmp\rayforce-c\ext\raykx" C:\msys64\usr\bin\make.exe release +if ($LASTEXITCODE -ne 0) { throw "make raykx failed with exit code $LASTEXITCODE" } Pop-Location Copy-Item "$EXEC_DIR\tmp\rayforce-c\ext\raykx\raykx.dll" "$EXEC_DIR\rayforce\plugins\raykx.dll" @@ -89,13 +93,15 @@ Copy-Item "$EXEC_DIR\tmp\rayforce-c\ext\raykx\raykx.dll" "$EXEC_DIR\rayforce\plu Write-Host "Building Rayforce Python extension..." Push-Location "$EXEC_DIR\tmp\rayforce-c" C:\msys64\usr\bin\make.exe python +if ($LASTEXITCODE -ne 0) { throw "make python failed with exit code $LASTEXITCODE" } Pop-Location Copy-Item "$EXEC_DIR\tmp\rayforce-c\_rayforce_c.pyd" "$EXEC_DIR\rayforce\_rayforce_c.pyd" # --- Build Rayforce executable --- Write-Host "Building Rayforce executable..." Push-Location "$EXEC_DIR\tmp\rayforce-c" -C:\msys64\usr\bin\make.exe release +C:\msys64\usr\bin\make.exe release "RELEASE_LDFLAGS=-fuse-ld=lld -Wl,--out-implib,rayforce.lib" +if ($LASTEXITCODE -ne 0) { throw "make release failed with exit code $LASTEXITCODE" } Pop-Location New-Item -ItemType Directory -Force -Path "$EXEC_DIR\rayforce\bin" | Out-Null Copy-Item "$EXEC_DIR\tmp\rayforce-c\rayforce.exe" "$EXEC_DIR\rayforce\bin\rayforce.exe" From 970382bf4140688b3a58c7f561fbd6890c3a4175 Mon Sep 17 00:00:00 2001 From: Karim Date: Tue, 3 Mar 2026 15:28:39 +0000 Subject: [PATCH 08/14] 8 attempt --- .github/workflows/coverage.yml | 49 ++++++++++++++++--------------- scripts/prepare_build_windows.ps1 | 14 +++++++-- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 7c6ed05..7ed2e94 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -12,30 +12,31 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-22.04 - python-version: '3.11' - platform: linux - - os: ubuntu-22.04 - python-version: '3.12' - platform: linux - - os: ubuntu-22.04 - python-version: '3.13' - platform: linux - - os: ubuntu-22.04 - python-version: '3.14' - platform: linux - - os: macos-14 - python-version: '3.11' - platform: macos - - os: macos-14 - python-version: '3.12' - platform: macos - - os: macos-14 - python-version: '3.13' - platform: macos - - os: macos-14 - python-version: '3.14' - platform: macos + # TODO: re-enable linux and macos after Windows support is confirmed + # - os: ubuntu-22.04 + # python-version: '3.11' + # platform: linux + # - os: ubuntu-22.04 + # python-version: '3.12' + # platform: linux + # - os: ubuntu-22.04 + # python-version: '3.13' + # platform: linux + # - os: ubuntu-22.04 + # python-version: '3.14' + # platform: linux + # - os: macos-14 + # python-version: '3.11' + # platform: macos + # - os: macos-14 + # python-version: '3.12' + # platform: macos + # - os: macos-14 + # python-version: '3.13' + # platform: macos + # - os: macos-14 + # python-version: '3.14' + # platform: macos - os: windows-2022 python-version: '3.11' platform: windows diff --git a/scripts/prepare_build_windows.ps1 b/scripts/prepare_build_windows.ps1 index 609a97b..69dd3c9 100644 --- a/scripts/prepare_build_windows.ps1 +++ b/scripts/prepare_build_windows.ps1 @@ -75,10 +75,20 @@ foreach ($f in $capiFiles) { # --- Build shared library (produces rayforce.dll + rayforce.lib needed by raykx) --- # Override RELEASE_LDFLAGS: upstream uses MSVC-style /DEF:/IMPLIB: flags which MinGW lld doesn't understand. # Use MinGW-compatible --out-implib to generate the import library. +# Append shared-win target (includes app/term.o — Windows requires all symbols resolved at link time) Write-Host "Building Rayforce shared library..." +$sharedWinTarget = @" + +shared-win: CFLAGS = `$(RELEASE_CFLAGS) +shared-win: LDFLAGS = -fuse-ld=lld -Wl,--out-implib,rayforce.lib +shared-win: `$(CORE_OBJECTS) app/term.o + `$(CC) -shared -o `$(LIBNAME) `$(CFLAGS) `$(CORE_OBJECTS) app/term.o `$(LIBS) `$(LDFLAGS) +"@ +Add-Content -Path "$EXEC_DIR\tmp\rayforce-c\Makefile" -Value $sharedWinTarget + Push-Location "$EXEC_DIR\tmp\rayforce-c" -C:\msys64\usr\bin\make.exe shared "RELEASE_LDFLAGS=-fuse-ld=lld -Wl,--out-implib,rayforce.lib" -if ($LASTEXITCODE -ne 0) { throw "make shared failed with exit code $LASTEXITCODE" } +C:\msys64\usr\bin\make.exe shared-win +if ($LASTEXITCODE -ne 0) { throw "make shared-win failed with exit code $LASTEXITCODE" } Pop-Location # --- Build Raykx plugin (depends on rayforce.lib from shared build) --- From 7b95689f4b0af9210a3e40018f711e693cfe72da Mon Sep 17 00:00:00 2001 From: Karim Date: Tue, 3 Mar 2026 15:32:55 +0000 Subject: [PATCH 09/14] 9 attempt --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9627c45..dc1f5a6 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ def has_ext_modules(self): zip_safe=False, python_requires=">=3.11", description="Python bindings for RayforceDB", - long_description=open("README.md").read(), # noqa: SIM115 + long_description=open("README.md", encoding="utf-8").read(), # noqa: SIM115 long_description_content_type="text/markdown", author="FalsePublicEnemy", license="MIT", From 9da3c5df9f5e404d13dd52a04155db0010ecd7b1 Mon Sep 17 00:00:00 2001 From: Karim Date: Tue, 3 Mar 2026 15:38:01 +0000 Subject: [PATCH 10/14] 10 attempt --- scripts/prepare_build_windows.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/prepare_build_windows.ps1 b/scripts/prepare_build_windows.ps1 index 69dd3c9..f50f521 100644 --- a/scripts/prepare_build_windows.ps1 +++ b/scripts/prepare_build_windows.ps1 @@ -54,7 +54,7 @@ $pythonTarget = @" PY_OBJECTS = core/rayforce_c.o core/raypy_init_from_py.o core/raypy_init_from_buffer.o core/raypy_read_from_rf.o core/raypy_queries.o core/raypy_io.o core/raypy_binary.o core/raypy_dynlib.o core/raypy_eval.o core/raypy_iter.o core/raypy_serde.o PY_APP_OBJECTS = app/term.o python: CFLAGS = `$(RELEASE_CFLAGS) -DPY_SSIZE_T_CLEAN -I$PYTHON_INCLUDE -Wno-macro-redefined -python: LDFLAGS = -fuse-ld=lld -L$PYTHON_LIB_DIR -lpython$PYTHON_PYVER +python: LDFLAGS = -fuse-ld=lld -static -L$PYTHON_LIB_DIR -lpython$PYTHON_PYVER python: `$(CORE_OBJECTS) `$(PY_OBJECTS) `$(PY_APP_OBJECTS) `$(CC) -shared -o _rayforce_c.pyd `$(CFLAGS) `$(CORE_OBJECTS) `$(PY_OBJECTS) `$(PY_APP_OBJECTS) `$(LIBS) `$(LDFLAGS) "@ @@ -80,7 +80,7 @@ Write-Host "Building Rayforce shared library..." $sharedWinTarget = @" shared-win: CFLAGS = `$(RELEASE_CFLAGS) -shared-win: LDFLAGS = -fuse-ld=lld -Wl,--out-implib,rayforce.lib +shared-win: LDFLAGS = -fuse-ld=lld -static -Wl,--out-implib,rayforce.lib shared-win: `$(CORE_OBJECTS) app/term.o `$(CC) -shared -o `$(LIBNAME) `$(CFLAGS) `$(CORE_OBJECTS) app/term.o `$(LIBS) `$(LDFLAGS) "@ From 1154570e0bf888a0980fe5a1c264adfae2b418bd Mon Sep 17 00:00:00 2001 From: Karim Date: Tue, 3 Mar 2026 15:41:52 +0000 Subject: [PATCH 11/14] 11 attempt --- scripts/prepare_build_windows.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/prepare_build_windows.ps1 b/scripts/prepare_build_windows.ps1 index f50f521..84615fd 100644 --- a/scripts/prepare_build_windows.ps1 +++ b/scripts/prepare_build_windows.ps1 @@ -97,6 +97,8 @@ Push-Location "$EXEC_DIR\tmp\rayforce-c\ext\raykx" C:\msys64\usr\bin\make.exe release if ($LASTEXITCODE -ne 0) { throw "make raykx failed with exit code $LASTEXITCODE" } Pop-Location +# Copy rayforce.dll (runtime dependency of raykx.dll) and the plugin +Copy-Item "$EXEC_DIR\tmp\rayforce-c\rayforce.dll" "$EXEC_DIR\rayforce\rayforce.dll" Copy-Item "$EXEC_DIR\tmp\rayforce-c\ext\raykx\raykx.dll" "$EXEC_DIR\rayforce\plugins\raykx.dll" # --- Build Python extension --- From 5720dba829af867bc2287bd0a2a5636b7ebf1ea4 Mon Sep 17 00:00:00 2001 From: Karim Date: Tue, 3 Mar 2026 15:45:56 +0000 Subject: [PATCH 12/14] 12 attempt --- rayforce/__init__.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/rayforce/__init__.py b/rayforce/__init__.py index f8aaf04..a29e8aa 100644 --- a/rayforce/__init__.py +++ b/rayforce/__init__.py @@ -3,6 +3,7 @@ """ import ctypes +import os from pathlib import Path import sys @@ -24,11 +25,18 @@ else: raise ImportError(f"Platform not supported: {sys.platform}") -lib_path = Path(__file__).resolve().parent / lib_name -raykx_lib_path = Path(__file__).resolve().parent / "plugins" / raykx_lib_name +_pkg_dir = Path(__file__).resolve().parent +lib_path = _pkg_dir / lib_name +raykx_lib_path = _pkg_dir / "plugins" / raykx_lib_name if lib_path.exists() and raykx_lib_path.exists(): try: - load_mode = 0 if sys.platform == "win32" else ctypes.RTLD_GLOBAL + if sys.platform == "win32": + # Add package dirs to DLL search path (Python 3.8+ restricts DLL loading) + os.add_dll_directory(str(_pkg_dir)) + os.add_dll_directory(str(_pkg_dir / "plugins")) + load_mode = 0 + else: + load_mode = ctypes.RTLD_GLOBAL ctypes.CDLL(str(lib_path), mode=load_mode) ctypes.CDLL(str(raykx_lib_path), mode=load_mode) except Exception as e: From 3f420e68fac7dc6fac3f504ed7fbea73b2cffebd Mon Sep 17 00:00:00 2001 From: Karim Date: Tue, 3 Mar 2026 15:50:07 +0000 Subject: [PATCH 13/14] 13 attempt --- rayforce/plugins/raykx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rayforce/plugins/raykx.py b/rayforce/plugins/raykx.py index bac7b4c..194a961 100644 --- a/rayforce/plugins/raykx.py +++ b/rayforce/plugins/raykx.py @@ -12,7 +12,7 @@ if sys.platform == "darwin": raykx_lib_name = "libraykx.dylib" elif sys.platform == "win32": - raykx_lib_name = "libraykx.dll" + raykx_lib_name = "raykx.dll" else: raykx_lib_name = "libraykx.so" From 994b25257316bd848bc500fb985548ec9a3795b2 Mon Sep 17 00:00:00 2001 From: Karim Date: Tue, 3 Mar 2026 15:56:58 +0000 Subject: [PATCH 14/14] 14 attempt --- tests/plugins/test_raykx.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/plugins/test_raykx.py b/tests/plugins/test_raykx.py index 5850257..065dccc 100644 --- a/tests/plugins/test_raykx.py +++ b/tests/plugins/test_raykx.py @@ -1,10 +1,16 @@ +import sys from unittest.mock import MagicMock, patch import pytest from rayforce import _rayforce_c as r from rayforce.plugins import errors -from rayforce.plugins.raykx import KDBConnection, KDBEngine + +# raykx plugin uses FFI.loadfn_from_file() at import time which requires +# the Rayforce runtime's dynlib loader to support the platform +raykx = pytest.importorskip("rayforce.plugins.raykx", reason="raykx plugin not available on this platform") +KDBConnection = raykx.KDBConnection +KDBEngine = raykx.KDBEngine pytestmark = pytest.mark.plugin