Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a5d1f41
maint: add Windows on ARM64 support
oscarbenjamin Jan 25, 2026
0606e70
Fix env var names in buildwheel.yml
oscarbenjamin Jan 25, 2026
c157dc1
Set --host to arm64 for Windows on arm64
oscarbenjamin Jan 25, 2026
4861f01
Fix --host
oscarbenjamin Jan 25, 2026
d2e8d04
Use msystem = clangarm64 for windows arm64
oscarbenjamin Jan 25, 2026
49291df
Don't run cibuildwheel twice on Windows
oscarbenjamin Jan 25, 2026
a7be3bf
Don't use fat build for Windows on ARM64
oscarbenjamin Jan 25, 2026
fcb49d2
Patch FLINT for mingw on arm64
oscarbenjamin Jan 25, 2026
eca229b
Fix patch command
oscarbenjamin Jan 25, 2026
ad01e83
Fix the ldd patch so that it works on Windows
oscarbenjamin Jan 25, 2026
91f985a
Fix patch
oscarbenjamin Jan 25, 2026
5ba5d33
Patch FLINT's Makefile for Windows on ARM
oscarbenjamin Mar 21, 2026
db240f2
Merge remote-tracking branch 'upstream/main' into pr_windows_arm64
oscarbenjamin Mar 21, 2026
86b6689
Fix the malformed patch
oscarbenjamin Mar 21, 2026
f46aedd
Don't try to link Pythonxxx.dll to extension modules
oscarbenjamin Mar 21, 2026
4278084
Try generating the .dll.a file for clangarm64
oscarbenjamin Mar 21, 2026
f089e09
Add .exe to commands
oscarbenjamin Mar 21, 2026
484e620
Move the Windows before_build command to GA yaml file
oscarbenjamin Mar 22, 2026
4da6ab6
Simplify befre_build script for Windows on ARM
oscarbenjamin Mar 22, 2026
41bab7a
Add the before_build script
oscarbenjamin Mar 22, 2026
9f961de
Fix dll detection
oscarbenjamin Mar 22, 2026
4de4cc3
Remove pkgconfig check
oscarbenjamin Mar 22, 2026
88ffa03
Try this ...
oscarbenjamin Mar 22, 2026
d2b3303
Add MS_WIN64 define
oscarbenjamin Mar 22, 2026
0b9b982
Try to force using msys2 clangarm64 for Windows on ARM
oscarbenjamin Mar 22, 2026
37b2f29
Add --host again
oscarbenjamin Mar 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 50 additions & 5 deletions .github/workflows/buildwheel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, ubuntu-22.04-arm, windows-2022, macos-15-intel, macos-14]
os: [
ubuntu-22.04,
ubuntu-22.04-arm,
windows-2022,
windows-11-arm,
macos-15-intel,
macos-14,
]

steps:
- uses: actions/checkout@v6.0.2
Expand All @@ -22,7 +29,23 @@ jobs:
- uses: msys2/setup-msys2@v2.30.0
with:
msystem: mingw64
if: ${{ startsWith( matrix.os , 'windows' ) }}
if: ${{ matrix.os == 'windows-2022' }}

- uses: msys2/setup-msys2@v2.29.0
with:
msystem: clangarm64
if: ${{ matrix.os == 'windows-11-arm' }}

- run: |
"C:\\msys64\\clangarm64\\bin" >> $env:GITHUB_PATH
"C:\\msys64\\usr\\bin" >> $env:GITHUB_PATH
"MSYSTEM=CLANGARM64" >> $env:GITHUB_ENV
"CC=clang" >> $env:GITHUB_ENV
"CXX=clang++" >> $env:GITHUB_ENV
"AR=llvm-ar" >> $env:GITHUB_ENV
"RANLIB=llvm-ranlib" >> $env:GITHUB_ENV
"STRIP=llvm-strip" >> $env:GITHUB_ENV
if: ${{ matrix.os == 'windows-11-arm' }}

# Install pkgconfig on Windows from choco rather than from msys and
# avoid using the Strawberry one.
Expand All @@ -34,11 +57,32 @@ jobs:
- run: echo "PKG_CONFIG_PATH=${{ github.workspace }}/.local/lib/pkgconfig" >> $env:GITHUB_ENV
if: ${{ startsWith( matrix.os , 'windows' ) }}

- name: Build wheels
# We need to set these environment variables here rather than
# pyproject.toml so we can use different values for different
# architectures. For non-Windows this is just done with $(uname -m)
# don't know what that would do in msys2 on Windows when cibuildwheel
# parses it.

- name: Build wheels for windows-2022
uses: pypa/cibuildwheel@298ed2fb2c105540f5ed055e8a6ad78d82dd3a7e # v3.3.1
if: ${{ matrix.os == 'windows-2022' }}
env:
CIBW_BEFORE_ALL_WINDOWS: msys2 -c bin/cibw_before_all_windows_amd64.sh

- name: Build wheels for windows-11-arm
uses: pypa/cibuildwheel@298ed2fb2c105540f5ed055e8a6ad78d82dd3a7e # v3.3.1
if: ${{ matrix.os == 'windows-11-arm' }}
env:
# override setting in pyproject.toml to use msys2 instead of msys64 bash
CIBW_BEFORE_ALL_WINDOWS: msys2 -c bin/cibw_before_all_windows.sh
CIBW_BEFORE_ALL_WINDOWS: msys2 -c bin/cibw_before_all_windows_arm64.sh
CIBW_BEFORE_BUILD_WINDOWS: python bin/cibw_before_build_windows_arm64.py && msys2 -c bin/cibw_before_build_windows_arm64.sh && pip install wheel delvewheel
PYTHONPATH: ${{ github.workspace }}\.local\python-site

# After all the Windows-specific steps above this is what actually
# builds the wheels for every other OS:

- name: Build wheels for any other OS
uses: pypa/cibuildwheel@298ed2fb2c105540f5ed055e8a6ad78d82dd3a7e # v3.3.1
if: ${{ !startsWith( matrix.os , 'windows' ) }}

- uses: actions/upload-artifact@v7
with:
Expand Down Expand Up @@ -78,6 +122,7 @@ jobs:
ubuntu-24.04-arm,
windows-2022,
windows-2025,
windows-11-arm,
macos-15-intel,
macos-14,
macos-15,
Expand Down
27 changes: 25 additions & 2 deletions bin/build_dependencies_unix.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ set -o errexit

SKIP_GMP=no
SKIP_MPFR=no
FAT_GMP_ARG="--enable-fat"
PATCH_LDD=no

USE_GMP=gmp
PATCH_GMP_ARM64=no
Expand All @@ -38,6 +40,8 @@ do
echo " --host <HOST> - set the host (target) for GMP build"
echo " --skip-gmp - skip building GMP"
echo " --skip-mpfr - skip building MPFR"
echo " --disable-fat - disable building fat binaries"
echo " --patch-ldd - patch flint shared linking for mingw on arm64"
echo
echo "Legacy options:"
echo " --gmp gmp - build based on GMP (default)"
Expand Down Expand Up @@ -84,6 +88,11 @@ do
SKIP_MPFR=yes
shift
;;
--disable-fat)
# Disable building fat binaries
FAT_GMP_ARG="--disable-assembly"
shift
;;
--patch-gmp-arm64)
# Needed only for GMP 6.2.1 on OSX arm64 (Apple M1) hardware
# As of GMP 6.3.0 this patch is no longer needed
Expand All @@ -95,6 +104,11 @@ do
PATCH_GMP_C23=yes
shift
;;
--patch-ldd)
# Needed only for the FLINT shared build on mingw arm64.
PATCH_LDD=yes
shift
;;
--use-gmp-github-mirror)
USE_GMP_GITHUB_MIRROR=yes
shift
Expand Down Expand Up @@ -191,7 +205,7 @@ if [ "$USE_GMP" = "gmp" ]; then
./configfsf.guess

./configure --prefix=$PREFIX\
--enable-fat\
$FAT_GMP_ARG\
--enable-shared=yes\
--enable-static=no\
--host=$HOST_ARG
Expand Down Expand Up @@ -310,14 +324,23 @@ echo
curl -O -L https://github.com/flintlib/flint/releases/download/v$FLINTVER/flint-$FLINTVER.tar.gz
tar xf flint-$FLINTVER.tar.gz
cd flint-$FLINTVER

if [ $PATCH_LDD = "yes" ]; then
echo
echo --------------------------------------------
echo " patching FLINT"
echo --------------------------------------------
patch -N -Z -p1 < ../../../bin/patch-flint-windows-arm64-link.diff
fi

./bootstrap.sh
./configure --prefix=$PREFIX\
--host=$HOST_ARG\
$FLINTARB_WITHGMP\
--with-mpfr=$PREFIX\
--disable-static\
--disable-debug
make -j6
make -j4
make install
cd ..

Expand Down
File renamed without changes.
23 changes: 23 additions & 0 deletions bin/cibw_before_all_windows_arm64.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash

set -o errexit

pacman -S --noconfirm \
mingw-w64-clang-aarch64-toolchain\
mingw-w64-clang-aarch64-tools-git\
m4\
make\
base-devel\
autoconf-wrapper\
automake-wrapper\
libtool\
git\
#

bin/build_dependencies_unix.sh \
--disable-fat\
--use-gmp-github-mirror\
--host aarch64-pc-windows-gnullvm\
--patch-C23\
--patch-ldd\
#
98 changes: 98 additions & 0 deletions bin/cibw_before_build_windows_arm64.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/usr/bin/env python3

from __future__ import annotations

import shlex
import sys
import sysconfig
from pathlib import Path


def _normalize_path(path: Path | str) -> str:
return str(path).replace('\\', '/')


def _default_dll_name() -> str:
vernum = sysconfig.get_config_var('py_version_nodot')
if not vernum:
vernum = f"{sys.version_info.major}{sys.version_info.minor}"
is_freethreaded = bool(sysconfig.get_config_var('Py_GIL_DISABLED'))
if not is_freethreaded:
abiflags = sysconfig.get_config_var('ABIFLAGS') or getattr(sys, 'abiflags', '') or ''
is_freethreaded = 't' in abiflags
suffix = 't' if is_freethreaded else ''
return f'python{vernum}{suffix}.dll'


def _normalize_dll_name(name: str) -> str:
if name.endswith('.dll'):
return name
if name.startswith('lib') and name.endswith('.dll.a'):
return _default_dll_name()
return _default_dll_name()


def _find_dll(dll_name: str) -> Path:
candidates: list[Path] = []
libdir = sysconfig.get_config_var('LIBDIR')
if libdir:
candidates.append(Path(libdir) / dll_name)
exe_dir = Path(sys.executable).resolve().parent
candidates.append(exe_dir / dll_name)
base_prefix = Path(getattr(sys, 'base_prefix', sys.prefix))
candidates.append(base_prefix / dll_name)
candidates.append(base_prefix / 'DLLs' / dll_name)
candidates.append(base_prefix / 'libs' / dll_name)

for candidate in candidates:
if candidate.exists():
return candidate.resolve()

paths = '\n'.join(_normalize_path(path) for path in candidates)
raise SystemExit(f'Could not find {dll_name} in:\n{paths}')


def main() -> None:
repo_root = Path.cwd().resolve()
lib_dir = repo_root / '.local' / 'lib'
pkgconfig_dir = lib_dir / 'pkgconfig'
lib_dir.mkdir(parents=True, exist_ok=True)
pkgconfig_dir.mkdir(parents=True, exist_ok=True)

raw_name = sysconfig.get_config_var('DLLLIBRARY') or sysconfig.get_config_var('LDLIBRARY') or ''
dll_name = _normalize_dll_name(raw_name)
include_dir = sysconfig.get_config_var('INCLUDEPY')
if not include_dir:
raise SystemExit('Could not determine Python include dir')
pkg_version = sysconfig.get_config_var('LDVERSION') or sysconfig.get_python_version()
dll_path = _find_dll(dll_name)

values = {
'REPO_ROOT': _normalize_path(repo_root),
'LIB_DIR': _normalize_path(lib_dir),
'PKGCONFIG_DIR': _normalize_path(pkgconfig_dir),
'DLL_NAME': dll_name,
'DLL_PATH': _normalize_path(dll_path),
'PKG_VERSION': pkg_version,
'INCLUDE_DIR': _normalize_path(include_dir),
}

env_path = repo_root / '.local' / 'cibw_before_build_windows_arm64.env'
env_text = ''.join(f'{key}={shlex.quote(value)}\n' for key, value in values.items())
env_path.write_text(env_text, encoding='utf-8')

python_site = repo_root / '.local' / 'python-site'
python_site.mkdir(parents=True, exist_ok=True)
sitecustomize = python_site / 'sitecustomize.py'
sitecustomize.write_text(
'import sysconfig\n'
f"sysconfig.get_config_vars()['LIBPC'] = {values['PKGCONFIG_DIR']!r}\n",
encoding='utf-8',
)

print(f'Generated {env_path}')
print(f'Generated {sitecustomize}')


if __name__ == '__main__':
main()
77 changes: 77 additions & 0 deletions bin/cibw_before_build_windows_arm64.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/bin/bash

set -o errexit

env_file=.local/cibw_before_build_windows_arm64.env
if [ ! -f "$env_file" ]; then
echo "Could not find $env_file" >&2
exit 1
fi

# shellcheck disable=SC1090
. "$env_file"

lib_dir_msys="$LIB_DIR"
pkgconfig_dir_msys="$PKGCONFIG_DIR"
dll_path_msys="$DLL_PATH"
if command -v cygpath >/dev/null 2>&1; then
lib_dir_msys=$(cygpath -u "$LIB_DIR")
pkgconfig_dir_msys=$(cygpath -u "$PKGCONFIG_DIR")
dll_path_msys=$(cygpath -u "$DLL_PATH")
fi
mkdir -p "$pkgconfig_dir_msys"

if ! command -v gendef >/dev/null 2>&1; then
echo "Could not find gendef on PATH" >&2
exit 1
fi

dlltool=
if command -v cc >/dev/null 2>&1; then
dlltool=$(cc -print-prog-name=dlltool)
fi
if [ -n "${dlltool:-}" ] && [ -x "$dlltool" ]; then
:
elif [ -n "${dlltool:-}" ] && command -v "$dlltool" >/dev/null 2>&1; then
dlltool=$(command -v "$dlltool")
elif command -v dlltool >/dev/null 2>&1; then
dlltool=$(command -v dlltool)
elif command -v llvm-dlltool >/dev/null 2>&1; then
dlltool=$(command -v llvm-dlltool)
else
echo "Could not find dlltool or llvm-dlltool on PATH" >&2
exit 1
fi

dll_stem=${DLL_NAME%.dll}
def_path_msys="$lib_dir_msys/$dll_stem.def"
import_lib_msys="$lib_dir_msys/lib$dll_stem.dll.a"
pc_path_msys="$pkgconfig_dir_msys/python-$PKG_VERSION.pc"

rm -f "$def_path_msys" "$import_lib_msys"
(
cd "$lib_dir_msys"
gendef "$dll_path_msys"
)
echo "Generating $import_lib_msys with $dlltool"
"$dlltool" -d "$def_path_msys" -D "$DLL_NAME" -l "$import_lib_msys"
if [ ! -f "$import_lib_msys" ]; then
echo "Failed to create $import_lib_msys" >&2
exit 1
fi

cat > "$pc_path_msys" <<EOF
prefix=$REPO_ROOT
exec_prefix=\${prefix}
libdir=$LIB_DIR
includedir=$INCLUDE_DIR

Name: Python
Description: CPython import library for MinGW wheel builds
Version: $PKG_VERSION
Libs: $LIB_DIR/lib$dll_stem.dll.a
Cflags: -DMS_WIN64 -I\${includedir}
EOF

echo "Generated $import_lib_msys"
echo "Generated $pc_path_msys"
27 changes: 27 additions & 0 deletions bin/patch-flint-windows-arm64-link.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
diff --git a/Makefile.in b/Makefile.in
index 0f25aa2b5..f6f46b7d6 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -416,18 +416,12 @@ endif
ifneq ($(SHARED), 0)
shared: $(FLINT_DIR)/$(FLINT_LIB_FULL)

-# The following is to avoid reaching the maximum length of command line
-# arguments, mainly present on MinGW.
-define xxx_merged_lobj_rule
-$(BUILD_DIR)/$(1)_merged.lo: $($(1)_LOBJS) | $(BUILD_DIR)
- @$(LD) -r $($(1)_LOBJS) -o $(BUILD_DIR)/$(1)_merged.lo
-endef
-$(foreach dir, $(DIRS), $(eval $(call xxx_merged_lobj_rule,$(dir))))
-MERGED_LOBJS:=$(foreach dir, $(DIRS),$(BUILD_DIR)/$(dir)_merged.lo)
+SHARED_LINK_RSP := $(BUILD_DIR)/libflint-shared.rsp

-$(FLINT_DIR)/$(FLINT_LIB_FULL): $(MERGED_LOBJS)
+$(FLINT_DIR)/$(FLINT_LIB_FULL): $(LOBJS) | $(BUILD_DIR)
@echo "Building $(FLINT_LIB_FULL)"
- @$(CC) $(CFLAGS) -shared $(EXTRA_SHARED_FLAGS) $(MERGED_LOBJS) -o $(FLINT_LIB_FULL) $(LDFLAGS) $(LIBS)
+ @: $(file >$(SHARED_LINK_RSP))$(foreach obj,$(LOBJS),$(file >>$(SHARED_LINK_RSP),$(obj)))
+ @$(CC) $(CFLAGS) -shared $(EXTRA_SHARED_FLAGS) @$(SHARED_LINK_RSP) -o $(FLINT_LIB_FULL) $(LDFLAGS) $(LIBS)
@$(RM_F) $(FLINT_LIB)
@$(RM_F) $(FLINT_LIB_MAJOR)
@$(LN_S) $(FLINT_LIB_FULL) $(FLINT_LIB)
Loading
Loading