diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c1fd9446..57da24f3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,7 +43,7 @@ jobs: macos_cross: false - target: x86_64-pc-windows-msvc - runner: windows-latest + runner: ubuntu-24.04 binary_ext: ".exe" linux_cross: false macos_cross: false diff --git a/.github/workflows/release-auto.yml b/.github/workflows/release-auto.yml index 15c0cb3d..02956b0c 100644 --- a/.github/workflows/release-auto.yml +++ b/.github/workflows/release-auto.yml @@ -144,7 +144,7 @@ jobs: macos_cross: false - target: x86_64-pc-windows-msvc - runner: windows-latest + runner: ubuntu-24.04 binary_ext: ".exe" linux_cross: false macos_cross: false diff --git a/.github/workflows/template_native_build.yml b/.github/workflows/template_native_build.yml index acf3b6cd..16f0b28d 100644 --- a/.github/workflows/template_native_build.yml +++ b/.github/workflows/template_native_build.yml @@ -84,24 +84,65 @@ jobs: # Linux musl toolchain (libudev is auto-excluded for musl targets by serialport) - name: Install Linux dependencies - if: runner.os == 'Linux' && !inputs.linux_cross + if: runner.os == 'Linux' && !inputs.linux_cross && !contains(inputs.target, 'pc-windows-msvc') run: | sudo apt-get update sudo apt-get install -y musl-tools pkg-config + # Windows MSVC release artifacts are built from Linux with cargo-xwin. + - name: Install cargo-xwin + if: contains(inputs.target, 'pc-windows-msvc') + uses: taiki-e/install-action@v2 + with: + tool: cargo-xwin + + - name: Install xwin system dependencies + if: contains(inputs.target, 'pc-windows-msvc') + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends clang lld zip + # cargo-zigbuild — used for two reasons on Linux: # 1. musl cross-compilation (linux_cross targets) # 2. Building the PyO3 extension with a pinned glibc symbol version # so manylinux_2_17 wheels actually run on glibc 2.17. The native # ubuntu-latest linker would otherwise pull in glibc 2.39 symbols. - name: Install cross-compilation tools - if: runner.os == 'Linux' + if: runner.os == 'Linux' && !contains(inputs.target, 'pc-windows-msvc') run: pip install cargo-zigbuild + - name: Fetch Windows Python lib for PyO3 cross-compile + if: contains(inputs.target, 'pc-windows-msvc') + shell: bash + run: | + set -euo pipefail + PYTHON_VERSION="3.13.0" + if [[ "${{ inputs.target }}" == aarch64-* ]]; then + PKG="pythonarm64" + else + PKG="python" + fi + curl -fsSL -o python.zip "https://www.nuget.org/api/v2/package/${PKG}/${PYTHON_VERSION}" + mkdir -p python-windows + unzip -q python.zip -d python-windows + libs_dir="$(pwd)/python-windows/tools/libs" + if [ ! -f "$libs_dir/python3.lib" ]; then + echo "ERROR: python3.lib not found in extracted NuGet package" >&2 + find python-windows -name "python3*.lib" || true + exit 1 + fi + echo "PYO3_CROSS_LIB_DIR=$libs_dir" >> "$GITHUB_ENV" + echo "PYO3_CROSS_PYTHON_VERSION=3.13" >> "$GITHUB_ENV" + echo "PYO3_CROSS_PYTHON_IMPLEMENTATION=CPython" >> "$GITHUB_ENV" + - name: Build release binaries shell: bash run: | - if [[ "${{ inputs.target }}" == *-unknown-linux-musl ]]; then + if [[ "${{ inputs.target }}" == *-pc-windows-msvc ]]; then + cargo xwin build --release --target ${{ inputs.target }} \ + -p fbuild-cli \ + -p fbuild-daemon + elif [[ "${{ inputs.target }}" == *-unknown-linux-musl ]]; then # Direct `cargo zigbuild` (pip-installed wrapper) bypasses # soldr's bin cache, which has been serving a corrupted # cargo-zigbuild binary (`Syntax error: ")" unexpected` at @@ -131,7 +172,12 @@ jobs: shell: bash run: | PYTHON_TARGET_DIR="target/python-extension" - if [ "${{ runner.os }}" = "Linux" ]; then + if [[ "${{ inputs.target }}" == *-pc-windows-msvc ]]; then + PYO3_NO_PYTHON=1 cargo xwin build --release \ + --target-dir "${PYTHON_TARGET_DIR}" \ + --target ${{ inputs.target }} -p fbuild-python \ + --features extension-module + elif [ "${{ runner.os }}" = "Linux" ]; then TARGET="${{ inputs.target }}" ARCH="${TARGET%%-*}" PYO3_TARGET="${ARCH}-unknown-linux-gnu" @@ -167,7 +213,13 @@ jobs: cp target/${{ inputs.target }}/release/fbuild-daemon${{ inputs.binary_ext }} staging/ # Stage Python extension — location depends on how it was built - if [ "${{ runner.os }}" = "Linux" ]; then + if [[ "${{ inputs.target }}" == *-pc-windows-msvc ]]; then + for ext_src in \ + "${PYTHON_TARGET_DIR}/${{ inputs.target }}/release/_native.dll" \ + "${PYTHON_TARGET_DIR}/release/_native.dll"; do + [ -f "$ext_src" ] && cp "$ext_src" staging/_native.pyd && break + done + elif [ "${{ runner.os }}" = "Linux" ]; then TARGET="${{ inputs.target }}" ARCH="${TARGET%%-*}" ext_src="${PYTHON_TARGET_DIR}/${ARCH}-unknown-linux-gnu.2.17/release/lib_native.so" @@ -176,12 +228,6 @@ jobs: elif [ "${{ runner.os }}" = "macOS" ]; then ext_src="${PYTHON_TARGET_DIR}/${{ inputs.target }}/release/lib_native.dylib" [ -f "$ext_src" ] && cp "$ext_src" staging/_native.abi3.so - elif [ "${{ inputs.binary_ext }}" = ".exe" ]; then - for ext_src in \ - "${PYTHON_TARGET_DIR}/${{ inputs.target }}/release/_native.dll" \ - "${PYTHON_TARGET_DIR}/release/_native.dll"; do - [ -f "$ext_src" ] && cp "$ext_src" staging/_native.pyd && break - done else for ext in so dylib; do src="${PYTHON_TARGET_DIR}/release/lib_native.$ext" @@ -201,6 +247,8 @@ jobs: llvm-strip staging/fbuild || true llvm-strip staging/fbuild-daemon || true llvm-strip staging/_native.abi3.so 2>/dev/null || true + elif [[ "${{ inputs.target }}" == *-pc-windows-msvc ]]; then + true elif command -v strip &> /dev/null; then strip staging/fbuild${{ inputs.binary_ext }} || true strip staging/fbuild-daemon${{ inputs.binary_ext }} || true