diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 2a01bd0..f00cb3a 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -88,3 +88,159 @@ jobs: OUTPUT=$(python -c "from cuvis_il import cuvis_il; print(cuvis_il.cuvis_version_swig())") echo "Version output: $OUTPUT" echo "$OUTPUT" | grep -q "CUBERT SDK" || (echo "Version check failed!" && exit 1) + + setup-windows-deps: + needs: prepare + runs-on: windows-latest + steps: + - name: Cache CUVIS SDK zip + id: cache-cuvis-zip + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}\cuvis_sdk.zip + key: cuvis-sdk-zip-windows-${{ needs.prepare.outputs.cuvis_version }} + + - name: Download CUVIS SDK zip + if: steps.cache-cuvis-zip.outputs.cache-hit != 'true' + shell: pwsh + env: + CUVIS_VERSION: ${{ needs.prepare.outputs.cuvis_version }} + run: | + $v = $env:CUVIS_VERSION + $vn = $v -replace '\.', '' + Invoke-WebRequest "https://cloud.cubert-gmbh.de/s/cuvis_sdk_release_ver_$vn/download" -OutFile cuvis_sdk.zip + + build-and-test-windows: + needs: [prepare, setup-windows-deps] + strategy: + fail-fast: false + matrix: + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + include: + - { python: "3.10", numpy: "2.0.0" } + - { python: "3.11", numpy: "2.0.0" } + - { python: "3.12", numpy: "2.0.0" } + - { python: "3.13", numpy: "2.1.0" } + - { python: "3.14", numpy: "2.3.2" } + runs-on: windows-latest + + steps: + - name: Install SWIG 4.2.1 + shell: pwsh + run: | + winget install --id SWIG.SWIG --version 4.2.1 --silent --accept-package-agreements --accept-source-agreements + $swigPkg = Get-ChildItem "$env:LOCALAPPDATA\Microsoft\WinGet\Packages" -Directory | Where-Object { $_.Name -like "SWIG.SWIG_*" } | Select-Object -First 1 + $swigExe = Get-ChildItem $swigPkg.FullName -Recurse -Filter "swig.exe" | Select-Object -First 1 + echo "SWIG_EXECUTABLE=$($swigExe.FullName)" >> $env:GITHUB_ENV + echo "SWIG_DIR=$(Join-Path $swigExe.DirectoryName 'Lib')" >> $env:GITHUB_ENV + + - name: Restore CUVIS SDK zip + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}\cuvis_sdk.zip + key: cuvis-sdk-zip-windows-${{ needs.prepare.outputs.cuvis_version }} + fail-on-cache-miss: true + + - name: Install CUVIS SDK + shell: pwsh + env: + CUVIS_VERSION: ${{ needs.prepare.outputs.cuvis_version }} + run: | + $v = $env:CUVIS_VERSION + Expand-Archive "${{ github.workspace }}\cuvis_sdk.zip" -DestinationPath cuvis_sdk + $installer = Get-ChildItem "cuvis_sdk\Cuvis $v\" -Recurse -Filter "Cuvis_C_SDK_Installer_*.exe" | Select-Object -First 1 + Write-Output "Installing: $($installer.FullName)" + + $logDir = Join-Path $env:RUNNER_TEMP 'cuvis_install' + New-Item -ItemType Directory -Force -Path $logDir | Out-Null + $nsisLog = Join-Path $logDir 'install.log' + $cuvisDir = 'C:\Program Files\Cuvis' + $cuvisBin = Join-Path $cuvisDir 'bin' + + # WORKAROUND for a bug in the current Cuvis_C_SDK_Installer (NSIS): $SILENT is never set under /S, + # so its bundled runtime driver MSIs (C:\Program Files\Cuvis\Runtime\*.msi, e.g. WavesSetup222.msi) + # launch via `msiexec /i ... /passive` instead of /quiet. /passive pops a GUI that nobody dismisses + # on a headless runner, so the installer's ExecWait hangs forever. We kill those stuck interactive + # msiexec processes so the installer finishes, then re-install the runtime MSIs ourselves with /qn. + # Those driver components are secondary -> their failure must not fail the job. + # Upstream fix for future installer versions: branch the runtime-MSI calls on ${Silent}, use /quiet. + $proc = Start-Process $installer.FullName -ArgumentList '/S', "/LOG=$nsisLog", '/NORESTART' -PassThru + $timeoutSec = 600; $elapsed = 0; $grace = 20; $firstSeen = @{} + while (-not $proc.HasExited -and $elapsed -lt $timeoutSec) { + Start-Sleep -Seconds 10; $elapsed += 10 + Get-CimInstance Win32_Process | Where-Object { + $_.Name -eq 'msiexec.exe' -and $_.CommandLine -and + $_.CommandLine -match 'Cuvis' -and $_.CommandLine -match '\.msi' -and $_.CommandLine -notmatch '/q' } | + ForEach-Object { + if (-not $firstSeen.ContainsKey($_.ProcessId)) { + $firstSeen[$_.ProcessId] = $elapsed + Write-Output "Detected interactive runtime sub-installer (PID $($_.ProcessId)): $($_.CommandLine)" + } elseif ($elapsed - $firstSeen[$_.ProcessId] -ge $grace) { + Write-Output "Killing stuck sub-installer PID $($_.ProcessId) so the main installer can continue." + Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue + } + } + Write-Output "== t=${elapsed}s : installer running ==" + } + + if ($proc.HasExited) { Write-Output "Installer exit code: $($proc.ExitCode)" } + else { Write-Output "::warning::Installer still running at ${timeoutSec}s - terminating."; Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue } + + if (-not (Test-Path "$cuvisBin\*.dll")) { + Write-Output "::error::core CUVIS SDK missing under $cuvisBin" + if (Test-Path $nsisLog) { Write-Output '----- NSIS install.log -----'; Get-Content $nsisLog } + exit 1 + } + + Get-ChildItem (Join-Path $cuvisDir 'Runtime') -Filter *.msi -ErrorAction SilentlyContinue | ForEach-Object { + $msiLog = Join-Path $logDir ($_.BaseName + '.log') + $p = Start-Process msiexec -Wait -PassThru -ArgumentList '/i', "`"$($_.FullName)`"", '/qn', '/norestart', '/l*v', "`"$msiLog`"" + Write-Output "Runtime component $($_.Name) -> msiexec exit $($p.ExitCode)" + } + + Write-Output "CUVIS SDK installed:"; Get-ChildItem $cuvisBin | Select-Object -First 20 | Format-Table -Auto | Out-String | Write-Output + + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Python ${{ matrix.python }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + + - name: Create venv and install Python build dependencies + shell: pwsh + run: | + python -m venv venv + .\venv\Scripts\Activate.ps1 + python -m pip install --upgrade pip setuptools wheel build + python -m pip install numpy==${{ matrix.numpy }} + + - name: CMake configure and build + shell: pwsh + env: + CUVIS: C:\Program Files\Cuvis + run: | + $env:PATH += ";C:\Program Files\Cuvis\bin" + cmake "-DCMAKE_BUILD_TYPE=Release" "-DDOXYGEN_BUILD_DOCUMENTATION=FALSE" "-DSWIG_DIR=$env:SWIG_DIR" "-DSWIG_EXECUTABLE=$env:SWIG_EXECUTABLE" "-DPython_ROOT_DIR=${{ github.workspace }}\venv" "-DCMAKE_PREFIX_PATH=$env:CUVIS" -B build . + cmake --build build --target cuvis_pyil --config Release + + - name: Copy built files and install package + shell: bash + run: | + chmod +x ./copy_pyil_files.sh + ./copy_pyil_files.sh build/Release + source venv/Scripts/activate + pip install . + + - name: Smoke test - verify import and version + shell: pwsh + env: + CUVIS: C:\Program Files\Cuvis\bin + run: | + .\venv\Scripts\Activate.ps1 + $output = python -c "from cuvis_il import cuvis_il; print(cuvis_il.cuvis_version_swig())" + Write-Output "Version output: $output" + if ($output -notmatch "CUBERT SDK") { Write-Output "Version check failed!"; exit 1 } diff --git a/.github/workflows/publish_version.yml b/.github/workflows/publish_version.yml index 68f0ba8..93521a6 100644 --- a/.github/workflows/publish_version.yml +++ b/.github/workflows/publish_version.yml @@ -141,8 +141,183 @@ jobs: name: wheel-ubuntu${{ matrix.ubuntu }}-py${{ matrix.python }}-${{ matrix.arch }} path: dist/*.whl + setup-windows-deps: + needs: prepare + runs-on: windows-latest + steps: + - name: Cache CUVIS SDK zip + id: cache-cuvis-zip + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}\cuvis_sdk.zip + key: cuvis-sdk-zip-windows-${{ needs.prepare.outputs.cuvis_version }} + + - name: Download CUVIS SDK zip + if: steps.cache-cuvis-zip.outputs.cache-hit != 'true' + shell: pwsh + env: + CUVIS_VERSION: ${{ needs.prepare.outputs.cuvis_version }} + run: | + $v = $env:CUVIS_VERSION + $vn = $v -replace '\.', '' + Invoke-WebRequest "https://cloud.cubert-gmbh.de/s/cuvis_sdk_release_ver_$vn/download" -OutFile cuvis_sdk.zip + + build-windows: + needs: [prepare, setup-windows-deps] + strategy: + fail-fast: false + matrix: + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + include: + - { python: "3.10", numpy: "2.0.0" } + - { python: "3.11", numpy: "2.0.0" } + - { python: "3.12", numpy: "2.0.0" } + - { python: "3.13", numpy: "2.1.0" } + - { python: "3.14", numpy: "2.3.2" } + runs-on: windows-latest + + permissions: + contents: read + + steps: + - name: Install SWIG 4.2.1 + shell: pwsh + run: | + winget install --id SWIG.SWIG --version 4.2.1 --silent --accept-package-agreements --accept-source-agreements + $swigPkg = Get-ChildItem "$env:LOCALAPPDATA\Microsoft\WinGet\Packages" -Directory | Where-Object { $_.Name -like "SWIG.SWIG_*" } | Select-Object -First 1 + $swigExe = Get-ChildItem $swigPkg.FullName -Recurse -Filter "swig.exe" | Select-Object -First 1 + echo "SWIG_EXECUTABLE=$($swigExe.FullName)" >> $env:GITHUB_ENV + echo "SWIG_DIR=$(Join-Path $swigExe.DirectoryName 'Lib')" >> $env:GITHUB_ENV + + - name: Restore CUVIS SDK zip + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}\cuvis_sdk.zip + key: cuvis-sdk-zip-windows-${{ needs.prepare.outputs.cuvis_version }} + fail-on-cache-miss: true + + - name: Install CUVIS SDK + shell: pwsh + env: + CUVIS_VERSION: ${{ needs.prepare.outputs.cuvis_version }} + run: | + $v = $env:CUVIS_VERSION + Expand-Archive "${{ github.workspace }}\cuvis_sdk.zip" -DestinationPath cuvis_sdk + $installer = Get-ChildItem "cuvis_sdk\Cuvis $v\" -Recurse -Filter "Cuvis_C_SDK_Installer_*.exe" | Select-Object -First 1 + Write-Output "Installing: $($installer.FullName)" + + $logDir = Join-Path $env:RUNNER_TEMP 'cuvis_install' + New-Item -ItemType Directory -Force -Path $logDir | Out-Null + $nsisLog = Join-Path $logDir 'install.log' + $cuvisDir = 'C:\Program Files\Cuvis' + $cuvisBin = Join-Path $cuvisDir 'bin' + + # WORKAROUND for a bug in the current Cuvis_C_SDK_Installer (NSIS): $SILENT is never set under /S, + # so its bundled runtime driver MSIs (C:\Program Files\Cuvis\Runtime\*.msi, e.g. WavesSetup222.msi) + # launch via `msiexec /i ... /passive` instead of /quiet. /passive pops a GUI that nobody dismisses + # on a headless runner, so the installer's ExecWait hangs forever. We kill those stuck interactive + # msiexec processes so the installer finishes, then re-install the runtime MSIs ourselves with /qn. + # Those driver components are secondary -> their failure must not fail the job. + # Upstream fix for future installer versions: branch the runtime-MSI calls on ${Silent}, use /quiet. + $proc = Start-Process $installer.FullName -ArgumentList '/S', "/LOG=$nsisLog", '/NORESTART' -PassThru + $timeoutSec = 600; $elapsed = 0; $grace = 20; $firstSeen = @{} + while (-not $proc.HasExited -and $elapsed -lt $timeoutSec) { + Start-Sleep -Seconds 10; $elapsed += 10 + Get-CimInstance Win32_Process | Where-Object { + $_.Name -eq 'msiexec.exe' -and $_.CommandLine -and + $_.CommandLine -match 'Cuvis' -and $_.CommandLine -match '\.msi' -and $_.CommandLine -notmatch '/q' } | + ForEach-Object { + if (-not $firstSeen.ContainsKey($_.ProcessId)) { + $firstSeen[$_.ProcessId] = $elapsed + Write-Output "Detected interactive runtime sub-installer (PID $($_.ProcessId)): $($_.CommandLine)" + } elseif ($elapsed - $firstSeen[$_.ProcessId] -ge $grace) { + Write-Output "Killing stuck sub-installer PID $($_.ProcessId) so the main installer can continue." + Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue + } + } + Write-Output "== t=${elapsed}s : installer running ==" + } + + if ($proc.HasExited) { Write-Output "Installer exit code: $($proc.ExitCode)" } + else { Write-Output "::warning::Installer still running at ${timeoutSec}s - terminating."; Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue } + + if (-not (Test-Path "$cuvisBin\*.dll")) { + Write-Output "::error::core CUVIS SDK missing under $cuvisBin" + if (Test-Path $nsisLog) { Write-Output '----- NSIS install.log -----'; Get-Content $nsisLog } + exit 1 + } + + Get-ChildItem (Join-Path $cuvisDir 'Runtime') -Filter *.msi -ErrorAction SilentlyContinue | ForEach-Object { + $msiLog = Join-Path $logDir ($_.BaseName + '.log') + $p = Start-Process msiexec -Wait -PassThru -ArgumentList '/i', "`"$($_.FullName)`"", '/qn', '/norestart', '/l*v', "`"$msiLog`"" + Write-Output "Runtime component $($_.Name) -> msiexec exit $($p.ExitCode)" + } + + Write-Output "CUVIS SDK installed:"; Get-ChildItem $cuvisBin | Select-Object -First 20 | Format-Table -Auto | Out-String | Write-Output + + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref || github.ref }} + submodules: recursive + + - name: Setup Python ${{ matrix.python }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + + - name: Create venv and install Python build dependencies + shell: pwsh + run: | + python -m venv venv + .\venv\Scripts\Activate.ps1 + python -m pip install --upgrade pip setuptools wheel build + python -m pip install numpy==${{ matrix.numpy }} + + - name: CMake configure and build + shell: pwsh + env: + CUVIS: C:\Program Files\Cuvis + run: | + $env:PATH += ";C:\Program Files\Cuvis\bin" + cmake "-DCMAKE_BUILD_TYPE=Release" "-DDOXYGEN_BUILD_DOCUMENTATION=FALSE" "-DSWIG_DIR=$env:SWIG_DIR" "-DSWIG_EXECUTABLE=$env:SWIG_EXECUTABLE" "-DPython_ROOT_DIR=${{ github.workspace }}\venv" "-DCMAKE_PREFIX_PATH=$env:CUVIS" -B build . + cmake --build build --target cuvis_pyil --config Release + + - name: Copy built files and package wheel + shell: bash + run: | + rm -rf ./cuvis_il/*cuvis* + rm -rf ./dist/*.whl + chmod +x ./copy_pyil_files.sh + ./copy_pyil_files.sh build/Release + source venv/Scripts/activate + python -m build --wheel --no-isolation + + - name: Tag wheel with correct Python and platform tags + shell: bash + run: | + source venv/Scripts/activate + PYTHON_TAG="py$(echo '${{ matrix.python }}' | tr -d '.')" + WHEELS=$(find dist -type f -name "*.whl") + WHEEL_COUNT=$(echo "$WHEELS" | wc -l) + if [ "$WHEEL_COUNT" -ne 1 ]; then + echo "Expected exactly 1 wheel, found $WHEEL_COUNT" + exit 1 + fi + echo "Tagging $WHEELS with --python-tag=$PYTHON_TAG --platform-tag=win_amd64" + python -m wheel tags --remove \ + --python-tag="$PYTHON_TAG" \ + --platform-tag=win_amd64 \ + "$WHEELS" + + - name: Upload wheel artifact + uses: actions/upload-artifact@v4 + with: + name: wheel-windows-py${{ matrix.python }} + path: dist/*.whl + preflight: - needs: [prepare, build] + needs: [prepare, build, build-windows] runs-on: ubuntu-latest permissions: diff --git a/pyproject.toml b/pyproject.toml index 33ab0e3..c77b7b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "cuvis_il" -version = "3.5.3.1" +version = "3.5.3.2" description = "Compiled Python Bindings for the CUVIS SDK." readme = "README.md" requires-python = ">=3.9"