Android: don't link libpython into dart_bridge.abi3.so #28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Test bridge (no publish, no slow platform tests) | |
| # Throwaway workflow that exercises just the new serious_python_bridge work — | |
| # the cibuildwheel matrix, the Android NDK cross-build, and the macOS bridge | |
| # example integration test — without triggering the slow flet_example | |
| # platform matrix or the publish/release steps in ci.yml. Delete before | |
| # merging to main. | |
| on: | |
| push: | |
| branches: [dart-bridge] | |
| workflow_dispatch: | |
| env: | |
| # Mirrors ci.yml's workflow-level env: serious_python_darwin's | |
| # sync_site_packages.sh and the Linux CMakeLists.txt both read this to know | |
| # where package_command.dart staged the bundled site-packages. | |
| SERIOUS_PYTHON_SITE_PACKAGES: "${{ github.workspace }}/site-packages" | |
| jobs: | |
| test_wheel_build: | |
| name: cibuildwheel (${{ matrix.os }}) | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-24.04, ubuntu-24.04-arm, windows-latest] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Build wheels | |
| uses: pypa/cibuildwheel@v2.21.3 | |
| with: | |
| package-dir: src/serious_python_bridge/python | |
| # Windows ships two CPython variants (Release pythonXY.dll and Debug | |
| # pythonXY_d.dll). The cibuildwheel-built abi3 .pyd is Release-CRT; a | |
| # Debug Flutter app loads Debug Python and tries to import | |
| # dart_bridge_d.cp<XY>-win_amd64.pyd (CPython appends _d to EXT_SUFFIX | |
| # when built in Debug mode). Compile a Debug-CRT variant of the same | |
| # dart_bridge_shim.c and pack it alongside the Release .pyd in each | |
| # wheel. Released wheels then work in both `flutter build` (Release) | |
| # and `fvm flutter run` (Debug) without a serious_python_windows split. | |
| - name: Build Debug-variant dart_bridge.pyd and inject into Windows wheels | |
| if: matrix.os == 'windows-latest' | |
| shell: pwsh | |
| run: | | |
| $ErrorActionPreference = 'Stop' | |
| # Use 3.12 headers — abi3 (Py_LIMITED_API=0x030c0000) means one .pyd | |
| # built against the minimum supported version works for every | |
| # 3.12+ runtime. | |
| $pyver = '3.12' | |
| $pyverNodot = '312' | |
| $pywinZip = "python-windows-for-dart-$pyver.zip" | |
| $pywinUrl = "https://github.com/flet-dev/python-build/releases/download/v$pyver/$pywinZip" | |
| Write-Host "Downloading $pywinUrl" | |
| Invoke-WebRequest -Uri $pywinUrl -OutFile $pywinZip | |
| Expand-Archive -Path $pywinZip -DestinationPath "pywin-$pyver" -Force | |
| $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" | |
| $vsPath = & $vswhere -latest -property installationPath | |
| $vcvars = "$vsPath\VC\Auxiliary\Build\vcvars64.bat" | |
| Write-Host "vcvars: $vcvars" | |
| $shimSrc = (Resolve-Path 'src/serious_python_bridge/native/dart_bridge_shim.c').Path | |
| $includeDir = (Resolve-Path "pywin-$pyver/include").Path | |
| $libDir = (Resolve-Path "pywin-$pyver/libs").Path | |
| $outDir = "$PWD\debug_build" | |
| New-Item -ItemType Directory -Force -Path $outDir | Out-Null | |
| $outPyd = "$outDir\dart_bridge_d.pyd" | |
| # /LD = build DLL. /MDd = link Debug CRT (vcruntime140d/msvcp140d). | |
| # /D_DEBUG = trigger pyconfig.h's Debug branch + match Debug CRT. | |
| # /DPy_LIMITED_API = mirror the Release wheel's abi3 contract. | |
| # Link the Debug abi3 stub python3_d.lib (forwards to python3_d.dll | |
| # which serious_python_windows ships in Debug Flutter builds). | |
| # /LIBPATH puts python-windows-for-dart's libs/ folder on the | |
| # linker search path so pyconfig.h's auto-link `#pragma comment( | |
| # lib, "pythonXY_d.lib")` resolves (it asks for python312_d.lib by | |
| # name; that file ships in the same libs/ folder). | |
| $clCmd = "cl /nologo /LD /MDd /D_DEBUG /DPy_LIMITED_API=0x030c0000 /I `"$includeDir`" `"$shimSrc`" /link /LIBPATH:`"$libDir`" /OUT:`"$outPyd`" `"$libDir\python3_d.lib`"" | |
| Write-Host "Running: $clCmd" | |
| cmd /c "`"$vcvars`" >NUL && $clCmd" | |
| if (-not (Test-Path $outPyd)) { | |
| throw "Debug .pyd not produced: $outPyd" | |
| } | |
| Write-Host "Built: $outPyd" | |
| Get-Item $outPyd | Format-List FullName, Length | |
| # Inject the Debug .pyd into each Windows wheel. | |
| python -m pip install --quiet wheel | |
| foreach ($whl in Get-ChildItem "wheelhouse\dart_bridge-*-cp312-abi3-win_amd64.whl") { | |
| $unpackDir = Join-Path $env:RUNNER_TEMP ("unpack_" + [IO.Path]::GetFileNameWithoutExtension($whl.Name)) | |
| if (Test-Path $unpackDir) { Remove-Item -Recurse -Force $unpackDir } | |
| python -m wheel unpack -d $unpackDir $whl.FullName | |
| $unpackedRoot = Get-ChildItem $unpackDir -Directory | Select-Object -First 1 | |
| Write-Host "Unpacked tree:" | |
| Get-ChildItem -Recurse -Path $unpackedRoot.FullName | ForEach-Object { | |
| Write-Host " $($_.FullName.Substring($unpackedRoot.FullName.Length))" | |
| } | |
| # Find any existing dart_bridge*.pyd to know where to place the Debug | |
| # sibling. setuptools may name it dart_bridge.pyd, dart_bridge.abi3.pyd, | |
| # dart_bridge.cp312-win_amd64.pyd, etc., depending on the build setup. | |
| $existing = Get-ChildItem -Recurse -Path $unpackedRoot.FullName -Filter 'dart_bridge*.pyd' | Select-Object -First 1 | |
| if (-not $existing) { throw "No dart_bridge*.pyd found inside $($whl.FullName)" } | |
| Write-Host "Found Release pyd: $($existing.Name)" | |
| $debugDest = Join-Path $existing.DirectoryName "dart_bridge_d.pyd" | |
| Copy-Item $outPyd $debugDest | |
| Write-Host "Injected: $debugDest" | |
| Remove-Item $whl.FullName | |
| python -m wheel pack -d wheelhouse $unpackedRoot.FullName | |
| } | |
| - name: List built wheels | |
| shell: bash | |
| run: | | |
| ls -la wheelhouse/ | |
| echo | |
| echo "Wheel filenames (expect cp312-abi3-<plat>):" | |
| ls wheelhouse/*.whl | |
| if [ "${{ matrix.os }}" = "windows-latest" ]; then | |
| echo | |
| echo "Wheel contents (verify Debug + Release .pyd both present):" | |
| for w in wheelhouse/*.whl; do | |
| echo "--- $w ---" | |
| python -m zipfile -l "$w" | grep -E "dart_bridge.*\.pyd" || true | |
| done | |
| fi | |
| - name: Upload wheel artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: bridge-wheels-${{ matrix.os }} | |
| path: wheelhouse/*.whl | |
| if-no-files-found: error | |
| test_android_build: | |
| name: Android cross-build (${{ matrix.abi }}) | |
| runs-on: ubuntu-24.04 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| abi: [arm64-v8a, armeabi-v7a, x86_64] | |
| env: | |
| PYTHON_VERSION: "3.12" | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Download python-android-mobile-forge tarball | |
| run: | | |
| set -euo pipefail | |
| VER="$PYTHON_VERSION" | |
| ABI="${{ matrix.abi }}" | |
| ARCHIVE="python-android-mobile-forge-${VER}.tar.gz" | |
| curl -fL -o "$ARCHIVE" \ | |
| "https://github.com/flet-dev/python-build/releases/download/v${VER}/${ARCHIVE}" | |
| mkdir -p pydist | |
| tar -xzf "$ARCHIVE" -C pydist \ | |
| "install/android/${ABI}/python-${VER}.13/include/" \ | |
| "install/android/${ABI}/python-${VER}.13/lib/" | |
| echo "Python.h candidates:" | |
| find pydist -name "Python.h" | |
| echo "libpython candidates:" | |
| find pydist -name "libpython*.so" | |
| - name: Cross-compile dart_bridge.abi3.so | |
| run: | | |
| set -euxo pipefail | |
| VER="$PYTHON_VERSION" | |
| ABI="${{ matrix.abi }}" | |
| case "$ABI" in | |
| arm64-v8a) TARGET=aarch64-linux-android ;; | |
| armeabi-v7a) TARGET=armv7a-linux-androideabi ;; | |
| x86_64) TARGET=x86_64-linux-android ;; | |
| *) echo "unsupported ABI: $ABI" >&2 ; exit 1 ;; | |
| esac | |
| API=21 | |
| NDK="${ANDROID_NDK_HOME:-${ANDROID_NDK_LATEST_HOME}}" | |
| TOOLCHAIN="$NDK/toolchains/llvm/prebuilt/linux-x86_64" | |
| CC="$TOOLCHAIN/bin/${TARGET}${API}-clang" | |
| test -x "$CC" | |
| INCLUDE_DIR=$(find pydist -path "*/python-${VER}.13/include/python${VER}" | head -n1) | |
| LIBPYTHON=$(find pydist -path "*/python-${VER}.13/lib/libpython${VER}.so" | head -n1) | |
| test -n "$INCLUDE_DIR" -a -n "$LIBPYTHON" | |
| OUT="dart_bridge.abi3-android-${ABI}.so" | |
| # Compile dart_bridge_shim.c (the Python-callable module) — same | |
| # source used by the Linux/Windows wheels. Symbols from the core | |
| # (dart_bridge_global_enqueue_handler_func, dart_bridge_post_to_dart) | |
| # are resolved at PyInit time via dlsym/dlopen against | |
| # libflet_bridge.so, which the Flutter plugin builds + bundles | |
| # separately. Linking dart_bridge.c directly into this .so would | |
| # produce a second, isolated copy of the global handler cell — | |
| # Dart's side and Python's side would never see the same value. | |
| # -ldl gives dart_bridge_shim.c its dlsym/dlopen. | |
| # | |
| # Intentionally NOT linking $LIBPYTHON: hardcoding it would emit | |
| # DT_NEEDED for the version-specific libpython3.X.so used at build | |
| # time (we build once with 3.12), and Android's linker would then | |
| # fail to load this .so under 3.13/3.14 Python (which ship | |
| # libpython3.13.so / libpython3.14.so but not libpython3.12.so). | |
| # `-shared` allows undefined Python symbols; they resolve at import | |
| # time because dlopen flags propagate from the already-loaded | |
| # libpython into the new module's symbol lookup. | |
| # `-Wl,--allow-shlib-undefined` makes the linker tolerate the | |
| # unresolved Python C API references during the link itself. | |
| $CC -shared -fPIC -fvisibility=hidden \ | |
| -DPy_LIMITED_API=0x030c0000 \ | |
| -I"$INCLUDE_DIR" \ | |
| -I"src/serious_python_bridge/native" \ | |
| src/serious_python_bridge/native/dart_bridge_shim.c \ | |
| -ldl \ | |
| -Wl,--allow-shlib-undefined \ | |
| -Wl,-z,max-page-size=16384 \ | |
| -o "$OUT" | |
| - name: Inspect output | |
| run: | | |
| ls -lh dart_bridge.abi3-android-*.so | |
| file dart_bridge.abi3-android-*.so | |
| - name: Upload .so artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: bridge-android-${{ matrix.abi }} | |
| path: dart_bridge.abi3-android-*.so | |
| if-no-files-found: error | |
| test_bridge_example_macos: | |
| name: Bridge example macOS round-trip (Python ${{ matrix.python_version }}) | |
| runs-on: macos-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python_version: ['3.12', '3.13', '3.14'] | |
| env: | |
| SERIOUS_PYTHON_VERSION: ${{ matrix.python_version }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Flutter | |
| uses: kuhnroyal/flutter-fvm-config-action/setup@v3 | |
| with: | |
| path: '.fvmrc' | |
| cache: true | |
| - name: Package + run integration test | |
| working-directory: "src/serious_python_bridge/example" | |
| run: | | |
| dart run serious_python:main package app/src --platform Darwin --python-version ${{ matrix.python_version }} | |
| flutter test integration_test -d macos | |
| test_bridge_example_ios: | |
| name: Bridge example iOS round-trip (Python ${{ matrix.python_version }}) | |
| runs-on: macos-latest | |
| timeout-minutes: 25 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python_version: ['3.12', '3.13', '3.14'] | |
| env: | |
| SERIOUS_PYTHON_VERSION: ${{ matrix.python_version }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Flutter | |
| uses: kuhnroyal/flutter-fvm-config-action/setup@v3 | |
| with: | |
| path: '.fvmrc' | |
| cache: true | |
| - name: Setup iOS Simulator | |
| id: simulator | |
| uses: futureware-tech/simulator-action@v4 | |
| with: | |
| model: 'iPhone 16 Pro Max' | |
| os: "iOS" | |
| os_version: "^18.6" | |
| shutdown_after_job: true | |
| wait_for_boot: true | |
| - name: Package + run integration test | |
| working-directory: "src/serious_python_bridge/example" | |
| run: | | |
| # certifi is a placeholder requirement: serious_python_darwin's | |
| # sync_site_packages.sh only populates dist_ios/site-xcframeworks | |
| # (which bundle-python-frameworks-ios.sh then requires at build | |
| # time) when iOS-specific site-packages subdirs exist. Empty | |
| # --requirements skips that branch and the build fails. | |
| dart run serious_python:main package app/src --platform iOS --python-version ${{ matrix.python_version }} --requirements certifi | |
| flutter test integration_test --device-id ${{ steps.simulator.outputs.udid }} | |
| test_bridge_example_linux: | |
| name: Bridge example Linux ${{ matrix.title }} round-trip (Python ${{ matrix.python_version }}) | |
| runs-on: ${{ matrix.runner }} | |
| needs: test_wheel_build | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python_version: ['3.12', '3.13', '3.14'] | |
| arch: [arm64, amd64] | |
| include: | |
| - arch: arm64 | |
| runner: ubuntu-24.04-arm | |
| title: ARM64 | |
| wheel_artifact: bridge-wheels-ubuntu-24.04-arm | |
| - arch: amd64 | |
| runner: ubuntu-24.04 | |
| title: AMD64 | |
| wheel_artifact: bridge-wheels-ubuntu-24.04 | |
| env: | |
| SERIOUS_PYTHON_VERSION: ${{ matrix.python_version }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup uv | |
| uses: astral-sh/setup-uv@v6 | |
| - name: Get Flutter version from .fvmrc | |
| uses: kuhnroyal/flutter-fvm-config-action/config@v3 | |
| id: fvm-config-action | |
| with: | |
| path: '.fvmrc' | |
| - name: Setup Flutter | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: ${{ steps.fvm-config-action.outputs.FLUTTER_VERSION }} | |
| channel: ${{ matrix.arch == 'arm64' && 'master' || 'stable' }} | |
| cache: true | |
| - name: Install Linux desktop build deps | |
| run: | | |
| sudo apt-get update --allow-releaseinfo-change | |
| sudo apt-get install -y xvfb libgtk-3-dev | |
| if [ "${{ matrix.arch }}" = "amd64" ]; then | |
| sudo apt-get install -y \ | |
| libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ | |
| libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base \ | |
| gstreamer1.0-plugins-good gstreamer1.0-plugins-bad \ | |
| gstreamer1.0-plugins-ugly gstreamer1.0-libav | |
| else | |
| sudo apt-get install -y \ | |
| clang ninja-build gstreamer1.0-plugins-bad \ | |
| gstreamer1.0-plugins-ugly gstreamer1.0-libav | |
| fi | |
| - name: Download dart_bridge wheel artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: ${{ matrix.wheel_artifact }} | |
| path: ${{ runner.temp }}/dart_bridge_wheels | |
| - name: Pick matching wheel for this arch | |
| id: wheel | |
| run: | | |
| set -euo pipefail | |
| WHL=$(ls "${{ runner.temp }}/dart_bridge_wheels"/dart_bridge-*manylinux*.whl | head -n1) | |
| test -n "$WHL" | |
| echo "path=$WHL" >> "$GITHUB_OUTPUT" | |
| echo "Picked: $WHL" | |
| - name: Package + run integration test | |
| working-directory: src/serious_python_bridge/example | |
| run: | | |
| flutter pub get | |
| dart run serious_python:main package app/src \ | |
| --platform Linux \ | |
| --python-version ${{ matrix.python_version }} \ | |
| --requirements ${{ steps.wheel.outputs.path }} | |
| xvfb-run flutter test integration_test -d linux | |
| test_bridge_example_android: | |
| name: Bridge example Android round-trip (Python ${{ matrix.python_version }}) | |
| runs-on: ubuntu-latest | |
| needs: test_android_build | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| # x86_64 matches the emulator architecture below; only build/install | |
| # for that ABI to keep CI fast. | |
| python_version: ['3.12', '3.13', '3.14'] | |
| env: | |
| SERIOUS_PYTHON_VERSION: ${{ matrix.python_version }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Flutter | |
| uses: kuhnroyal/flutter-fvm-config-action/setup@v3 | |
| with: | |
| path: '.fvmrc' | |
| cache: true | |
| - name: Enable KVM | |
| run: | | |
| echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules | |
| sudo udevadm control --reload-rules | |
| sudo udevadm trigger --name-match=kvm | |
| - name: Gradle cache | |
| uses: gradle/actions/setup-gradle@v3 | |
| - name: AVD cache | |
| uses: actions/cache@v4 | |
| id: avd-cache | |
| with: | |
| path: | | |
| ~/.android/avd/* | |
| ~/.android/adb* | |
| key: avd-bridge | |
| - name: Download Android bridge .so for x86_64 | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: bridge-android-x86_64 | |
| path: ${{ runner.temp }}/dart_bridge_android | |
| - name: Inject dart_bridge.abi3.so into bundled site-packages | |
| run: | | |
| # serious_python_android's gradle takes everything from | |
| # SERIOUS_PYTHON_SITE_PACKAGES/<abi>/ and zips it as | |
| # libpythonsitepackages.so, which ends up in the APK. Drop our | |
| # cross-compiled dart_bridge.abi3.so there so Python finds it at | |
| # runtime. The .so name has to be the canonical 'dart_bridge.abi3.so' | |
| # for CPython's import to match. | |
| set -euxo pipefail | |
| ABI=x86_64 | |
| DEST="$SERIOUS_PYTHON_SITE_PACKAGES/$ABI" | |
| mkdir -p "$DEST" | |
| cp "${{ runner.temp }}/dart_bridge_android/dart_bridge.abi3-android-$ABI.so" \ | |
| "$DEST/dart_bridge.abi3.so" | |
| ls -lh "$DEST" | |
| - name: Setup Android Emulator + Run tests | |
| uses: reactivecircus/android-emulator-runner@v2 | |
| env: | |
| EMULATOR_PORT: 5554 | |
| with: | |
| avd-name: android_emulator | |
| api-level: 33 | |
| target: google_atd | |
| arch: x86_64 | |
| profile: pixel_5 | |
| sdcard-path-or-size: 128M | |
| ram-size: 2048M | |
| disk-size: 4096M | |
| emulator-port: ${{ env.EMULATOR_PORT }} | |
| disable-animations: true | |
| emulator-options: -no-window -noaudio -no-boot-anim -wipe-data -cache-size 1000 -partition-size 8192 | |
| pre-emulator-launch-script: | | |
| sdkmanager --list_installed | |
| script: | | |
| cd src/serious_python_bridge/example && dart run serious_python:main package app/src --platform Android --python-version ${{ matrix.python_version }} | |
| cd src/serious_python_bridge/example && flutter test integration_test --device-id emulator-${{ env.EMULATOR_PORT }} | |
| - name: Diagnostics on failure | |
| if: failure() | |
| shell: bash | |
| run: | | |
| set +e | |
| REPO="$GITHUB_WORKSPACE" | |
| ABI=x86_64 | |
| echo "=== serious_python_android/android/src/main/jniLibs/$ABI/ (post-extract source) ===" | |
| ls -la "$REPO/src/serious_python_android/android/src/main/jniLibs/$ABI/" 2>/dev/null || echo "(not found)" | |
| echo | |
| echo "=== serious_python_bridge/android/src/main/jniLibs/$ABI/ ===" | |
| ls -la "$REPO/src/serious_python_bridge/android/src/main/jniLibs/$ABI/" 2>/dev/null || echo "(not found)" | |
| echo | |
| echo "=== example/build/app/intermediates/merged_native_libs/.../$ABI/ ===" | |
| find "$REPO/src/serious_python_bridge/example/build/app/intermediates/merged_native_libs" -type f 2>/dev/null | |
| echo | |
| echo "=== example/build/app/outputs/apk/debug/*.apk ===" | |
| APK=$(find "$REPO/src/serious_python_bridge/example/build" -name "*-debug.apk" 2>/dev/null | head -n1) | |
| echo "APK: $APK" | |
| if [ -n "$APK" ]; then | |
| echo "Native libs inside APK:" | |
| unzip -l "$APK" | grep -E "lib/$ABI/" || echo "(no lib/$ABI/ entries)" | |
| fi | |
| echo | |
| echo "=== installed app native lib dir (adb) ===" | |
| adb shell run-as com.flet.serious_python_bridge_example ls -la /data/data/com.flet.serious_python_bridge_example/lib/ 2>/dev/null || true | |
| adb shell pm path com.flet.serious_python_bridge_example 2>/dev/null || true | |
| test_bridge_example_windows: | |
| name: Bridge example Windows round-trip (Python ${{ matrix.python_version }}) | |
| runs-on: windows-latest | |
| needs: test_wheel_build | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python_version: ['3.12', '3.13', '3.14'] | |
| env: | |
| SERIOUS_PYTHON_VERSION: ${{ matrix.python_version }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Flutter | |
| uses: kuhnroyal/flutter-fvm-config-action/setup@v3 | |
| with: | |
| path: '.fvmrc' | |
| cache: true | |
| - name: Download dart_bridge wheel artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: bridge-wheels-windows-latest | |
| path: ${{ runner.temp }}\dart_bridge_wheels | |
| - name: Pick matching wheel | |
| id: wheel | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| WHL=$(ls "$RUNNER_TEMP/dart_bridge_wheels"/dart_bridge-*-win_amd64.whl | head -n1) | |
| test -n "$WHL" | |
| echo "path=$WHL" >> "$GITHUB_OUTPUT" | |
| echo "Picked: $WHL" | |
| - name: Package + run integration test | |
| working-directory: "src/serious_python_bridge/example" | |
| run: | | |
| dart run serious_python:main package app/src --platform Windows --python-version ${{ matrix.python_version }} --requirements ${{ steps.wheel.outputs.path }} | |
| flutter test integration_test -d windows | |
| - name: Diagnostics on failure | |
| if: failure() | |
| shell: bash | |
| working-directory: "src/serious_python_bridge/example" | |
| run: | | |
| DBG_DIR=build/windows/x64/runner/Debug | |
| echo "=== runner/Debug dir ===" | |
| ls -la $DBG_DIR/ || true | |
| echo | |
| echo "=== runner/Debug/site-packages ===" | |
| ls -la $DBG_DIR/site-packages/ || true | |
| echo | |
| echo "=== shim log next to .exe ===" | |
| cat $DBG_DIR/dart_bridge_shim.log 2>/dev/null || echo "(log not found)" | |
| echo | |
| echo "=== direct import test (catches the actual ImportError) ===" | |
| # Try to import dart_bridge using the bundled Python so we can see | |
| # the actual error. Python search paths the bundle uses. | |
| PY_BUNDLED=$(find $DBG_DIR -maxdepth 1 -name "python.exe" -o -name "python3*.exe" 2>/dev/null | head -n1) | |
| if [ -z "$PY_BUNDLED" ]; then | |
| echo "(no python.exe in $DBG_DIR — trying system python)" | |
| PY_BUNDLED=python | |
| fi | |
| (cd $DBG_DIR && PYTHONPATH="site-packages;." "$PY_BUNDLED" -c "import sys; print('sys.path:', sys.path); import dart_bridge; print('imported OK:', dart_bridge)") 2>&1 || true | |
| echo | |
| echo "=== look for stray flet_bridge.dll / dart_bridge.pyd ===" | |
| find build -name "flet_bridge.dll" -o -name "dart_bridge*.pyd" || true |