mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-07-10 16:22:22 +03:00
Add support for iOS (#9030)
Co-authored-by: Andrew Murray <radarhere@users.noreply.github.com>
This commit is contained in:
parent
be2b4e7864
commit
da10ed1cf3
207
.github/workflows/wheels-dependencies.sh
vendored
207
.github/workflows/wheels-dependencies.sh
vendored
|
@ -1,42 +1,98 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Setup that needs to be done before multibuild utils are invoked
|
||||
PROJECTDIR=$(pwd)
|
||||
if [[ "$(uname -s)" == "Darwin" ]]; then
|
||||
# Safety check - macOS builds require that CIBW_ARCHS is set, and that it
|
||||
# only contains a single value (even though cibuildwheel allows multiple
|
||||
# values in CIBW_ARCHS).
|
||||
# Safety check - Pillow builds require that CIBW_ARCHS is set, and that it only
|
||||
# contains a single value (even though cibuildwheel allows multiple values in
|
||||
# CIBW_ARCHS). This check doesn't work on Linux because of how the CIBW_ARCHS
|
||||
# variable is exposed.
|
||||
function check_cibw_archs {
|
||||
if [[ -z "$CIBW_ARCHS" ]]; then
|
||||
echo "ERROR: Pillow macOS builds require CIBW_ARCHS be defined."
|
||||
echo "ERROR: Pillow builds require CIBW_ARCHS be defined."
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$CIBW_ARCHS" == *" "* ]]; then
|
||||
echo "ERROR: Pillow macOS builds only support a single architecture in CIBW_ARCHS."
|
||||
echo "ERROR: Pillow builds only support a single architecture in CIBW_ARCHS."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Setup that needs to be done before multibuild utils are invoked. Process
|
||||
# potential cross-build platforms before native platforms to ensure that we pick
|
||||
# up the cross environment.
|
||||
PROJECTDIR=$(pwd)
|
||||
if [[ "$CIBW_PLATFORM" == "ios" ]]; then
|
||||
check_cibw_archs
|
||||
# On iOS, CIBW_ARCHS is actually a multi-arch - arm64_iphoneos,
|
||||
# arm64_iphonesimulator or x86_64_iphonesimulator. Split into the CPU
|
||||
# platform, and the iOS SDK.
|
||||
PLAT=$(echo $CIBW_ARCHS | sed "s/\(.*\)_\(.*\)/\1/")
|
||||
IOS_SDK=$(echo $CIBW_ARCHS | sed "s/\(.*\)_\(.*\)/\2/")
|
||||
|
||||
# Build iOS builds in `build/iphoneos` or `build/iphonesimulator`
|
||||
# (depending on the build target). Install them into `build/deps/iphoneos`
|
||||
# or `build/deps/iphonesimulator`
|
||||
WORKDIR=$(pwd)/build/$IOS_SDK
|
||||
BUILD_PREFIX=$(pwd)/build/deps/$IOS_SDK
|
||||
PATCH_DIR=$(pwd)/patches/iOS
|
||||
|
||||
# GNU tooling insists on using aarch64 rather than arm64
|
||||
if [[ $PLAT == "arm64" ]]; then
|
||||
GNU_ARCH=aarch64
|
||||
else
|
||||
GNU_ARCH=x86_64
|
||||
fi
|
||||
|
||||
IOS_SDK_PATH=$(xcrun --sdk $IOS_SDK --show-sdk-path)
|
||||
CMAKE_SYSTEM_NAME=iOS
|
||||
IOS_HOST_TRIPLE=$PLAT-apple-ios$IPHONEOS_DEPLOYMENT_TARGET
|
||||
if [[ "$IOS_SDK" == "iphonesimulator" ]]; then
|
||||
IOS_HOST_TRIPLE=$IOS_HOST_TRIPLE-simulator
|
||||
fi
|
||||
|
||||
# GNU Autotools doesn't recognize the existence of arm64-apple-ios-simulator
|
||||
# as a valid host. However, the only difference between arm64-apple-ios and
|
||||
# arm64-apple-ios-simulator is the choice of sysroot, and that is
|
||||
# coordinated by CC, CFLAGS etc. From the perspective of configure, the two
|
||||
# platforms are identical, so we can use arm64-apple-ios consistently.
|
||||
# This (mostly) avoids us needing to patch config.sub in dependency sources.
|
||||
HOST_CONFIGURE_FLAGS="--disable-shared --enable-static --host=$GNU_ARCH-apple-ios --build=$GNU_ARCH-apple-darwin"
|
||||
|
||||
# CMake has native support for iOS. However, most of that support is based
|
||||
# on using the Xcode builder, which isn't very helpful for most of Pillow's
|
||||
# dependencies. Therefore, we lean on the OSX configurations, plus CC, CFLAGS
|
||||
# etc. to ensure the right sysroot is selected.
|
||||
HOST_CMAKE_FLAGS="-DCMAKE_SYSTEM_NAME=$CMAKE_SYSTEM_NAME -DCMAKE_SYSTEM_PROCESSOR=$GNU_ARCH -DCMAKE_OSX_DEPLOYMENT_TARGET=$IPHONEOS_DEPLOYMENT_TARGET -DCMAKE_OSX_SYSROOT=$IOS_SDK_PATH -DBUILD_SHARED_LIBS=NO"
|
||||
|
||||
# Meson needs to be pointed at a cross-platform configuration file
|
||||
# This will be generated once CC etc. have been evaluated.
|
||||
HOST_MESON_FLAGS="--cross-file $WORKDIR/meson-cross.txt -Dprefer_static=true -Ddefault_library=static"
|
||||
|
||||
elif [[ "$(uname -s)" == "Darwin" ]]; then
|
||||
check_cibw_archs
|
||||
# Build macOS dependencies in `build/darwin`
|
||||
# Install them into `build/deps/darwin`
|
||||
PLAT=$CIBW_ARCHS
|
||||
WORKDIR=$(pwd)/build/darwin
|
||||
BUILD_PREFIX=$(pwd)/build/deps/darwin
|
||||
else
|
||||
# Build prefix will default to /usr/local
|
||||
PLAT="${CIBW_ARCHS:-$AUDITWHEEL_ARCH}"
|
||||
WORKDIR=$(pwd)/build
|
||||
MB_ML_LIBC=${AUDITWHEEL_POLICY::9}
|
||||
MB_ML_VER=${AUDITWHEEL_POLICY:9}
|
||||
fi
|
||||
PLAT="${CIBW_ARCHS:-$AUDITWHEEL_ARCH}"
|
||||
|
||||
# Define custom utilities
|
||||
source wheels/multibuild/common_utils.sh
|
||||
source wheels/multibuild/library_builders.sh
|
||||
if [ -z "$IS_MACOS" ]; then
|
||||
if [[ -z "$IS_MACOS" ]]; then
|
||||
source wheels/multibuild/manylinux_utils.sh
|
||||
fi
|
||||
|
||||
ARCHIVE_SDIR=pillow-depends-main
|
||||
|
||||
# Package versions for fresh source builds
|
||||
# Package versions for fresh source builds. Version numbers with "Patched"
|
||||
# annotations have a source code patch that is required for some platforms. If
|
||||
# you change those versions, ensure the patch is also updated.
|
||||
FREETYPE_VERSION=2.13.3
|
||||
HARFBUZZ_VERSION=11.2.1
|
||||
LIBPNG_VERSION=1.6.49
|
||||
|
@ -47,32 +103,58 @@ TIFF_VERSION=4.7.0
|
|||
LCMS2_VERSION=2.17
|
||||
ZLIB_VERSION=1.3.1
|
||||
ZLIB_NG_VERSION=2.2.4
|
||||
LIBWEBP_VERSION=1.5.0
|
||||
LIBWEBP_VERSION=1.5.0 # Patched; next release won't need patching. See patch file.
|
||||
BZIP2_VERSION=1.0.8
|
||||
LIBXCB_VERSION=1.17.0
|
||||
BROTLI_VERSION=1.1.0
|
||||
BROTLI_VERSION=1.1.0 # Patched; next release won't need patching. See patch file.
|
||||
LIBAVIF_VERSION=1.3.0
|
||||
|
||||
function build_pkg_config {
|
||||
if [ -e pkg-config-stamp ]; then return; fi
|
||||
# This essentially duplicates the Homebrew recipe
|
||||
CFLAGS="$CFLAGS -Wno-int-conversion" build_simple pkg-config 0.29.2 https://pkg-config.freedesktop.org/releases tar.gz \
|
||||
# This essentially duplicates the Homebrew recipe.
|
||||
# On iOS, we need a binary that can be executed on the build machine; but we
|
||||
# can create a host-specific pc-path to store iOS .pc files. To ensure a
|
||||
# macOS-compatible build, we temporarily clear environment flags that set
|
||||
# iOS-specific values.
|
||||
if [[ -n "$IOS_SDK" ]]; then
|
||||
ORIGINAL_HOST_CONFIGURE_FLAGS=$HOST_CONFIGURE_FLAGS
|
||||
ORIGINAL_IPHONEOS_DEPLOYMENT_TARGET=$IPHONEOS_DEPLOYMENT_TARGET
|
||||
unset HOST_CONFIGURE_FLAGS
|
||||
unset IPHONEOS_DEPLOYMENT_TARGET
|
||||
fi
|
||||
|
||||
CFLAGS="$CFLAGS -Wno-int-conversion" CPPFLAGS="" build_simple pkg-config 0.29.2 https://pkg-config.freedesktop.org/releases tar.gz \
|
||||
--disable-debug --disable-host-tool --with-internal-glib \
|
||||
--with-pc-path=$BUILD_PREFIX/share/pkgconfig:$BUILD_PREFIX/lib/pkgconfig \
|
||||
--with-system-include-path=$(xcrun --show-sdk-path --sdk macosx)/usr/include
|
||||
|
||||
if [[ -n "$IOS_SDK" ]]; then
|
||||
HOST_CONFIGURE_FLAGS=$ORIGINAL_HOST_CONFIGURE_FLAGS
|
||||
IPHONEOS_DEPLOYMENT_TARGET=$ORIGINAL_IPHONEOS_DEPLOYMENT_TARGET
|
||||
fi;
|
||||
|
||||
export PKG_CONFIG=$BUILD_PREFIX/bin/pkg-config
|
||||
touch pkg-config-stamp
|
||||
}
|
||||
|
||||
function build_zlib_ng {
|
||||
if [ -e zlib-stamp ]; then return; fi
|
||||
# zlib-ng uses a "configure" script, but it's not a GNU autotools script, so
|
||||
# it doesn't honor the usual flags. Temporarily disable any
|
||||
# cross-compilation flags.
|
||||
ORIGINAL_HOST_CONFIGURE_FLAGS=$HOST_CONFIGURE_FLAGS
|
||||
unset HOST_CONFIGURE_FLAGS
|
||||
|
||||
build_github zlib-ng/zlib-ng $ZLIB_NG_VERSION --zlib-compat
|
||||
|
||||
if [ -n "$IS_MACOS" ]; then
|
||||
HOST_CONFIGURE_FLAGS=$ORIGINAL_HOST_CONFIGURE_FLAGS
|
||||
|
||||
if [[ -n "$IS_MACOS" ]] && [[ -z "$IOS_SDK" ]]; then
|
||||
# Ensure that on macOS, the library name is an absolute path, not an
|
||||
# @rpath, so that delocate picks up the right library (and doesn't need
|
||||
# DYLD_LIBRARY_PATH to be set). The default Makefile doesn't have an
|
||||
# option to control the install_name.
|
||||
# option to control the install_name. This isn't needed on iOS, as iOS
|
||||
# only builds the static library.
|
||||
install_name_tool -id $BUILD_PREFIX/lib/libz.1.dylib $BUILD_PREFIX/lib/libz.1.dylib
|
||||
fi
|
||||
touch zlib-stamp
|
||||
|
@ -82,7 +164,7 @@ function build_brotli {
|
|||
if [ -e brotli-stamp ]; then return; fi
|
||||
local out_dir=$(fetch_unpack https://github.com/google/brotli/archive/v$BROTLI_VERSION.tar.gz brotli-$BROTLI_VERSION.tar.gz)
|
||||
(cd $out_dir \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_LIBDIR=$BUILD_PREFIX/lib -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib . \
|
||||
&& cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_LIBDIR=$BUILD_PREFIX/lib -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib $HOST_CMAKE_FLAGS . \
|
||||
&& make install)
|
||||
touch brotli-stamp
|
||||
}
|
||||
|
@ -93,7 +175,7 @@ function build_harfbuzz {
|
|||
|
||||
local out_dir=$(fetch_unpack https://github.com/harfbuzz/harfbuzz/releases/download/$HARFBUZZ_VERSION/harfbuzz-$HARFBUZZ_VERSION.tar.xz harfbuzz-$HARFBUZZ_VERSION.tar.xz)
|
||||
(cd $out_dir \
|
||||
&& meson setup build --prefix=$BUILD_PREFIX --libdir=$BUILD_PREFIX/lib --buildtype=minsize -Dfreetype=enabled -Dglib=disabled -Dtests=disabled)
|
||||
&& meson setup build --prefix=$BUILD_PREFIX --libdir=$BUILD_PREFIX/lib --buildtype=minsize -Dfreetype=enabled -Dglib=disabled -Dtests=disabled $HOST_MESON_FLAGS)
|
||||
(cd $out_dir/build \
|
||||
&& meson install)
|
||||
touch harfbuzz-stamp
|
||||
|
@ -164,19 +246,19 @@ function build {
|
|||
fi
|
||||
|
||||
build_simple xcb-proto 1.17.0 https://xorg.freedesktop.org/archive/individual/proto
|
||||
if [ -n "$IS_MACOS" ]; then
|
||||
if [[ -n "$IS_MACOS" ]]; then
|
||||
build_simple xorgproto 2024.1 https://www.x.org/pub/individual/proto
|
||||
build_simple libXau 1.0.12 https://www.x.org/pub/individual/lib
|
||||
build_simple libpthread-stubs 0.5 https://xcb.freedesktop.org/dist
|
||||
else
|
||||
sed s/\${pc_sysrootdir\}// $BUILD_PREFIX/share/pkgconfig/xcb-proto.pc > $BUILD_PREFIX/lib/pkgconfig/xcb-proto.pc
|
||||
sed "s/\${pc_sysrootdir\}//" $BUILD_PREFIX/share/pkgconfig/xcb-proto.pc > $BUILD_PREFIX/lib/pkgconfig/xcb-proto.pc
|
||||
fi
|
||||
build_simple libxcb $LIBXCB_VERSION https://www.x.org/releases/individual/lib
|
||||
|
||||
build_libjpeg_turbo
|
||||
if [ -n "$IS_MACOS" ]; then
|
||||
if [[ -n "$IS_MACOS" ]]; then
|
||||
# Custom tiff build to include jpeg; by default, configure won't include
|
||||
# headers/libs in the custom macOS prefix. Explicitly disable webp,
|
||||
# headers/libs in the custom macOS/iOS prefix. Explicitly disable webp,
|
||||
# libdeflate and zstd, because on x86_64 macs, it will pick up the
|
||||
# Homebrew versions of those libraries from /usr/local.
|
||||
build_simple tiff $TIFF_VERSION https://download.osgeo.org/libtiff tar.gz \
|
||||
|
@ -186,7 +268,10 @@ function build {
|
|||
build_tiff
|
||||
fi
|
||||
|
||||
build_libavif
|
||||
if [[ -z "$IOS_SDK" ]]; then
|
||||
# Short term workaround; don't build libavif on iOS
|
||||
build_libavif
|
||||
fi
|
||||
build_libpng
|
||||
build_lcms2
|
||||
build_openjpeg
|
||||
|
@ -201,14 +286,44 @@ function build {
|
|||
|
||||
build_brotli
|
||||
|
||||
if [ -n "$IS_MACOS" ]; then
|
||||
if [[ -n "$IS_MACOS" ]]; then
|
||||
# Custom freetype build
|
||||
build_simple freetype $FREETYPE_VERSION https://download.savannah.gnu.org/releases/freetype tar.gz --with-harfbuzz=no
|
||||
else
|
||||
build_freetype
|
||||
fi
|
||||
|
||||
build_harfbuzz
|
||||
if [[ -z "$IOS_SDK" ]]; then
|
||||
# On iOS, there's no vendor-provided raqm, and we can't ship it due to
|
||||
# licensing, so there's no point building harfbuzz.
|
||||
build_harfbuzz
|
||||
fi
|
||||
}
|
||||
|
||||
function create_meson_cross_config {
|
||||
cat << EOF > $WORKDIR/meson-cross.txt
|
||||
[binaries]
|
||||
pkg-config = '$BUILD_PREFIX/bin/pkg-config'
|
||||
cmake = '$(which cmake)'
|
||||
c = '$CC'
|
||||
cpp = '$CXX'
|
||||
strip = '$STRIP'
|
||||
|
||||
[built-in options]
|
||||
c_args = '$CFLAGS -I$BUILD_PREFIX/include'
|
||||
cpp_args = '$CXXFLAGS -I$BUILD_PREFIX/include'
|
||||
c_link_args = '$CFLAGS -L$BUILD_PREFIX/lib'
|
||||
cpp_link_args = '$CFLAGS -L$BUILD_PREFIX/lib'
|
||||
|
||||
[host_machine]
|
||||
system = 'darwin'
|
||||
subsystem = 'ios'
|
||||
kernel = 'xnu'
|
||||
cpu_family = '$(uname -m)'
|
||||
cpu = '$(uname -m)'
|
||||
endian = 'little'
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# Perform all dependency builds in the build subfolder.
|
||||
|
@ -227,24 +342,40 @@ if [[ ! -d $WORKDIR/pillow-depends-main ]]; then
|
|||
fi
|
||||
|
||||
if [[ -n "$IS_MACOS" ]]; then
|
||||
# Homebrew (or similar packaging environments) install can contain some of
|
||||
# the libraries that we're going to build. However, they may be compiled
|
||||
# with a MACOSX_DEPLOYMENT_TARGET that doesn't match what we want to use,
|
||||
# and they may bring in other dependencies that we don't want. The same will
|
||||
# be true of any other locations on the path. To avoid conflicts, strip the
|
||||
# path down to the bare minimum (which, on macOS, won't include any
|
||||
# development dependencies).
|
||||
export PATH="$BUILD_PREFIX/bin:$(dirname $(which python3)):/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin"
|
||||
export CMAKE_PREFIX_PATH=$BUILD_PREFIX
|
||||
|
||||
# Ensure the basic structure of the build prefix directory exists.
|
||||
mkdir -p "$BUILD_PREFIX/bin"
|
||||
mkdir -p "$BUILD_PREFIX/lib"
|
||||
|
||||
# Ensure pkg-config is available
|
||||
# Ensure pkg-config is available. This is done *before* setting CC, CFLAGS
|
||||
# etc. to ensure that the build is *always* a macOS build, even when building
|
||||
# for iOS.
|
||||
build_pkg_config
|
||||
# Ensure cmake is available
|
||||
|
||||
# Ensure cmake is available, and that the default prefix used by CMake is
|
||||
# the build prefix
|
||||
python3 -m pip install cmake
|
||||
export CMAKE_PREFIX_PATH=$BUILD_PREFIX
|
||||
|
||||
if [[ -n "$IOS_SDK" ]]; then
|
||||
export AR="$(xcrun --find --sdk $IOS_SDK ar)"
|
||||
export CPP="$(xcrun --find --sdk $IOS_SDK clang) -E"
|
||||
export CC=$(xcrun --find --sdk $IOS_SDK clang)
|
||||
export CXX=$(xcrun --find --sdk $IOS_SDK clang++)
|
||||
export LD=$(xcrun --find --sdk $IOS_SDK ld)
|
||||
export STRIP=$(xcrun --find --sdk $IOS_SDK strip)
|
||||
|
||||
CPPFLAGS="$CPPFLAGS --sysroot=$IOS_SDK_PATH"
|
||||
CFLAGS="-target $IOS_HOST_TRIPLE --sysroot=$IOS_SDK_PATH -mios-version-min=$IPHONEOS_DEPLOYMENT_TARGET"
|
||||
CXXFLAGS="-target $IOS_HOST_TRIPLE --sysroot=$IOS_SDK_PATH -mios-version-min=$IPHONEOS_DEPLOYMENT_TARGET"
|
||||
|
||||
# Having IPHONEOS_DEPLOYMENT_TARGET in the environment causes problems
|
||||
# with some cross-building toolchains, because it introduces implicit
|
||||
# behavior into clang.
|
||||
unset IPHONEOS_DEPLOYMENT_TARGET
|
||||
|
||||
# Now that we know CC etc., we can create a meson cross-configuration file
|
||||
create_meson_cross_config
|
||||
fi
|
||||
fi
|
||||
|
||||
wrap_wheel_builder build
|
||||
|
|
5
.github/workflows/wheels-test.ps1
vendored
5
.github/workflows/wheels-test.ps1
vendored
|
@ -15,15 +15,12 @@ if (Test-Path $venv\Scripts\pypy.exe) {
|
|||
$python = "python.exe"
|
||||
}
|
||||
& reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\python.exe" /v "GlobalFlag" /t REG_SZ /d "0x02000000" /f
|
||||
if ("$venv" -like "*\cibw-run-*-win_amd64\*") {
|
||||
& $venv\Scripts\$python -m pip install numpy
|
||||
}
|
||||
cd $pillow
|
||||
& $venv\Scripts\$python -VV
|
||||
if (!$?) { exit $LASTEXITCODE }
|
||||
& $venv\Scripts\$python selftest.py
|
||||
if (!$?) { exit $LASTEXITCODE }
|
||||
& $venv\Scripts\$python -m pytest -vv -x Tests\check_wheel.py
|
||||
& $venv\Scripts\$python -m pytest -vv -x checks\check_wheel.py
|
||||
if (!$?) { exit $LASTEXITCODE }
|
||||
& $venv\Scripts\$python -m pytest -vv -x Tests
|
||||
if (!$?) { exit $LASTEXITCODE }
|
||||
|
|
4
.github/workflows/wheels-test.sh
vendored
4
.github/workflows/wheels-test.sh
vendored
|
@ -25,8 +25,6 @@ else
|
|||
yum install -y fribidi
|
||||
fi
|
||||
|
||||
python3 -m pip install numpy
|
||||
|
||||
if [ ! -d "test-images-main" ]; then
|
||||
curl -fsSL -o pillow-test-images.zip https://github.com/python-pillow/test-images/archive/main.zip
|
||||
unzip pillow-test-images.zip
|
||||
|
@ -35,5 +33,5 @@ fi
|
|||
|
||||
# Runs tests
|
||||
python3 selftest.py
|
||||
python3 -m pytest -vv -x Tests/check_wheel.py
|
||||
python3 -m pytest -vv -x checks/check_wheel.py
|
||||
python3 -m pytest -vv -x
|
||||
|
|
23
.github/workflows/wheels.yml
vendored
23
.github/workflows/wheels.yml
vendored
|
@ -51,40 +51,60 @@ jobs:
|
|||
matrix:
|
||||
include:
|
||||
- name: "macOS 10.10 x86_64"
|
||||
platform: macos
|
||||
os: macos-13
|
||||
cibw_arch: x86_64
|
||||
build: "cp3{9,10,11}*"
|
||||
macosx_deployment_target: "10.10"
|
||||
- name: "macOS 10.13 x86_64"
|
||||
platform: macos
|
||||
os: macos-13
|
||||
cibw_arch: x86_64
|
||||
build: "cp3{12,13,14}*"
|
||||
macosx_deployment_target: "10.13"
|
||||
- name: "macOS 10.15 x86_64"
|
||||
platform: macos
|
||||
os: macos-13
|
||||
cibw_arch: x86_64
|
||||
build: "pp3*"
|
||||
macosx_deployment_target: "10.15"
|
||||
- name: "macOS arm64"
|
||||
platform: macos
|
||||
os: macos-latest
|
||||
cibw_arch: arm64
|
||||
macosx_deployment_target: "11.0"
|
||||
- name: "manylinux2014 and musllinux x86_64"
|
||||
platform: linux
|
||||
os: ubuntu-latest
|
||||
cibw_arch: x86_64
|
||||
- name: "manylinux_2_28 x86_64"
|
||||
platform: linux
|
||||
os: ubuntu-latest
|
||||
cibw_arch: x86_64
|
||||
build: "*manylinux*"
|
||||
manylinux: "manylinux_2_28"
|
||||
- name: "manylinux2014 and musllinux aarch64"
|
||||
platform: linux
|
||||
os: ubuntu-24.04-arm
|
||||
cibw_arch: aarch64
|
||||
- name: "manylinux_2_28 aarch64"
|
||||
platform: linux
|
||||
os: ubuntu-24.04-arm
|
||||
cibw_arch: aarch64
|
||||
build: "*manylinux*"
|
||||
manylinux: "manylinux_2_28"
|
||||
- name: "iOS arm64 device"
|
||||
platform: ios
|
||||
os: macos-latest
|
||||
cibw_arch: arm64_iphoneos
|
||||
- name: "iOS arm64 simulator"
|
||||
platform: ios
|
||||
os: macos-latest
|
||||
cibw_arch: arm64_iphonesimulator
|
||||
- name: "iOS x86_64 simulator"
|
||||
platform: ios
|
||||
os: macos-13
|
||||
cibw_arch: x86_64_iphonesimulator
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
@ -103,6 +123,7 @@ jobs:
|
|||
run: |
|
||||
python3 -m cibuildwheel --output-dir wheelhouse
|
||||
env:
|
||||
CIBW_PLATFORM: ${{ matrix.platform }}
|
||||
CIBW_ARCHS: ${{ matrix.cibw_arch }}
|
||||
CIBW_BUILD: ${{ matrix.build }}
|
||||
CIBW_ENABLE: cpython-prerelease cpython-freethreading pypy
|
||||
|
@ -114,7 +135,7 @@ jobs:
|
|||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dist-${{ matrix.os }}${{ matrix.macosx_deployment_target && format('-{0}', matrix.macosx_deployment_target) }}-${{ matrix.cibw_arch }}${{ matrix.manylinux && format('-{0}', matrix.manylinux) }}
|
||||
name: dist-${{ matrix.name }}
|
||||
path: ./wheelhouse/*.whl
|
||||
|
||||
windows:
|
||||
|
|
|
@ -21,7 +21,7 @@ repos:
|
|||
rev: v1.5.5
|
||||
hooks:
|
||||
- id: remove-tabs
|
||||
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$)
|
||||
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$|\.patch$)
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v20.1.6
|
||||
|
@ -46,9 +46,9 @@ repos:
|
|||
- id: check-yaml
|
||||
args: [--allow-multiple-documents]
|
||||
- id: end-of-file-fixer
|
||||
exclude: ^Tests/images/
|
||||
exclude: ^Tests/images/|\.patch$
|
||||
- id: trailing-whitespace
|
||||
exclude: ^.github/.*TEMPLATE|^Tests/(fonts|images)/
|
||||
exclude: ^.github/.*TEMPLATE|^Tests/(fonts|images)/|\.patch$
|
||||
|
||||
- repo: https://github.com/python-jsonschema/check-jsonschema
|
||||
rev: 0.33.1
|
||||
|
|
|
@ -13,6 +13,8 @@ include LICENSE
|
|||
include Makefile
|
||||
include tox.ini
|
||||
graft Tests
|
||||
graft checks
|
||||
graft patches
|
||||
graft src
|
||||
graft depends
|
||||
graft winbuild
|
||||
|
|
|
@ -10,8 +10,9 @@ import pytest
|
|||
from PIL import Image, features
|
||||
from Tests.helper import skip_unless_feature
|
||||
|
||||
if sys.platform.startswith("win32"):
|
||||
pytest.skip("Fuzzer is linux only", allow_module_level=True)
|
||||
if sys.platform.startswith("win32") or sys.platform == "ios":
|
||||
pytest.skip("Fuzzer doesn't run on Windows or iOS", allow_module_level=True)
|
||||
|
||||
libjpeg_turbo_version = features.version("libjpeg_turbo")
|
||||
if libjpeg_turbo_version is not None:
|
||||
version = packaging.version.parse(libjpeg_turbo_version)
|
||||
|
|
|
@ -7,6 +7,7 @@ import sys
|
|||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.platform == "ios", reason="Processes not supported on iOS")
|
||||
@pytest.mark.parametrize(
|
||||
"args, report",
|
||||
((["PIL"], False), (["PIL", "--report"], True), (["PIL.report"], True)),
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from importlib.metadata import metadata
|
||||
|
||||
import pytest
|
||||
|
||||
from PIL import __version__
|
||||
|
@ -9,7 +11,7 @@ pyroma = pytest.importorskip("pyroma", reason="Pyroma not installed")
|
|||
|
||||
def test_pyroma() -> None:
|
||||
# Arrange
|
||||
data = pyroma.projectdata.get_data(".")
|
||||
data = pyroma.projectdata.map_metadata_keys(metadata("Pillow"))
|
||||
|
||||
# Act
|
||||
rating = pyroma.ratings.rate(data)
|
||||
|
|
|
@ -4,8 +4,7 @@ import platform
|
|||
import sys
|
||||
|
||||
from PIL import features
|
||||
|
||||
from .helper import is_pypy
|
||||
from Tests.helper import is_pypy
|
||||
|
||||
|
||||
def test_wheel_modules() -> None:
|
||||
|
@ -24,6 +23,11 @@ def test_wheel_modules() -> None:
|
|||
if platform.machine() == "ARM64":
|
||||
expected_modules.remove("avif")
|
||||
|
||||
elif sys.platform == "ios":
|
||||
# tkinter is not available on iOS
|
||||
# libavif is not available on iOS (for now)
|
||||
expected_modules -= {"tkinter", "avif"}
|
||||
|
||||
assert set(features.get_supported_modules()) == expected_modules
|
||||
|
||||
|
||||
|
@ -50,5 +54,9 @@ def test_wheel_features() -> None:
|
|||
expected_features.remove("xcb")
|
||||
elif sys.platform == "darwin" and not is_pypy() and platform.processor() != "arm":
|
||||
expected_features.remove("zlib_ng")
|
||||
elif sys.platform == "ios":
|
||||
# Can't distribute raqm due to licensing, and there's no system version;
|
||||
# fribidi and harfbuzz won't be available if raqm isn't available.
|
||||
expected_features -= {"raqm", "fribidi", "harfbuzz"}
|
||||
|
||||
assert set(features.get_supported_features()) == expected_features
|
|
@ -68,7 +68,13 @@ AVIF support in wheels
|
|||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Support for reading and writing AVIF images is now included in Pillow's wheels, except
|
||||
for Windows ARM64. libaom is available as an encoder and dav1d as a decoder.
|
||||
for Windows ARM64 and iOS. libaom is available as an encoder and dav1d as a decoder.
|
||||
|
||||
iOS
|
||||
^^^
|
||||
|
||||
Pillow now provides wheels that can be used on iOS ARM64 devices, and on the iOS
|
||||
simulator on ARM64 and x86_64. Currently, only Python 3.13 wheels are available.
|
||||
|
||||
Python 3.14 beta
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
|
14
patches/README.md
Normal file
14
patches/README.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
Although we try to use official sources for dependencies, sometimes the official
|
||||
sources don't support a platform (especially mobile platforms), or there's a bug
|
||||
fix/feature that is required to support Pillow's usage.
|
||||
|
||||
This folder contains patches that must be applied to official sources, organized
|
||||
by the platforms that need those patches.
|
||||
|
||||
Each patch is against the root of the unpacked official tarball, and is named by
|
||||
appending `.patch` to the end of the tarball that is to be patched. This
|
||||
includes the full version number; so if the version is bumped, the patch will
|
||||
at a minimum require a filename change.
|
||||
|
||||
Wherever possible, these patches should be contributed upstream, in the hope that
|
||||
future Pillow versions won't need to maintain these patches.
|
46
patches/iOS/brotli-1.1.0.tar.gz.patch
Normal file
46
patches/iOS/brotli-1.1.0.tar.gz.patch
Normal file
|
@ -0,0 +1,46 @@
|
|||
# Brotli 1.1.0 doesn't have explicit support for iOS as a CMAKE_SYSTEM_NAME.
|
||||
# That release was from 2023; there have been subsequent changes that allow
|
||||
# Brotli to build on iOS without any patches, as long as -DBROTLI_BUILD_TOOLS=NO
|
||||
# is specified on the command line.
|
||||
#
|
||||
diff -ru brotli-1.1.0-orig/CMakeLists.txt brotli-1.1.0/CMakeLists.txt
|
||||
--- brotli-1.1.0-orig/CMakeLists.txt 2023-08-29 19:00:29
|
||||
+++ brotli-1.1.0/CMakeLists.txt 2024-11-07 10:46:26
|
||||
@@ -114,6 +114,8 @@
|
||||
add_definitions(-DOS_MACOSX)
|
||||
set(CMAKE_MACOS_RPATH TRUE)
|
||||
set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib")
|
||||
+elseif(${CMAKE_SYSTEM_NAME} MATCHES "iOS")
|
||||
+ add_definitions(-DOS_IOS)
|
||||
endif()
|
||||
|
||||
if(BROTLI_EMSCRIPTEN)
|
||||
@@ -174,10 +176,12 @@
|
||||
|
||||
# Installation
|
||||
if(NOT BROTLI_BUNDLED_MODE)
|
||||
- install(
|
||||
- TARGETS brotli
|
||||
- RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
- )
|
||||
+ if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "iOS")
|
||||
+ install(
|
||||
+ TARGETS brotli
|
||||
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
+ )
|
||||
+ endif()
|
||||
|
||||
install(
|
||||
TARGETS ${BROTLI_LIBRARIES_CORE}
|
||||
diff -ru brotli-1.1.0-orig/c/common/platform.h brotli-1.1.0/c/common/platform.h
|
||||
--- brotli-1.1.0-orig/c/common/platform.h 2023-08-29 19:00:29
|
||||
+++ brotli-1.1.0/c/common/platform.h 2024-11-07 10:47:28
|
||||
@@ -33,7 +33,7 @@
|
||||
#include <endian.h>
|
||||
#elif defined(OS_FREEBSD)
|
||||
#include <machine/endian.h>
|
||||
-#elif defined(OS_MACOSX)
|
||||
+#elif defined(OS_MACOSX) || defined(OS_IOS)
|
||||
#include <machine/endian.h>
|
||||
/* Let's try and follow the Linux convention */
|
||||
#define BROTLI_X_BYTE_ORDER BYTE_ORDER
|
42
patches/iOS/libwebp-1.5.0.tar.gz.patch
Normal file
42
patches/iOS/libwebp-1.5.0.tar.gz.patch
Normal file
|
@ -0,0 +1,42 @@
|
|||
# libwebp example binaries require dependencies that aren't available for iOS builds.
|
||||
# There's also no easy way to invoke the build to *exclude* the example builds.
|
||||
# Since we don't need the examples anyway, remove them from the Makefile.
|
||||
#
|
||||
# As a point of reference, libwebp provides an XCFramework build script that involves
|
||||
# 7 separate invocations of make to avoid building the examples. Patching the Makefile
|
||||
# to remove the examples is a simpler approach, and one that is more compatible with
|
||||
# the existing multibuild infrastructure.
|
||||
#
|
||||
# In the next release, it should be possible to pass --disable-libwebpexamples
|
||||
# instead of applying this patch.
|
||||
#
|
||||
diff -ur libwebp-1.5.0-orig/Makefile.am libwebp-1.5.0/Makefile.am
|
||||
--- libwebp-1.5.0-orig/Makefile.am 2024-12-20 09:17:50
|
||||
+++ libwebp-1.5.0/Makefile.am 2025-01-09 11:24:17
|
||||
@@ -5,5 +5,3 @@
|
||||
if BUILD_EXTRAS
|
||||
SUBDIRS += extras
|
||||
endif
|
||||
-
|
||||
-SUBDIRS += examples
|
||||
diff -ur libwebp-1.5.0-orig/Makefile.in libwebp-1.5.0/Makefile.in
|
||||
--- libwebp-1.5.0-orig/Makefile.in 2024-12-20 09:52:53
|
||||
+++ libwebp-1.5.0/Makefile.in 2025-01-09 11:24:17
|
||||
@@ -156,7 +156,7 @@
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | $(am__uniquify_input)`
|
||||
-DIST_SUBDIRS = sharpyuv src imageio man extras examples
|
||||
+DIST_SUBDIRS = sharpyuv src imageio man extras
|
||||
am__DIST_COMMON = $(srcdir)/Makefile.in \
|
||||
$(top_srcdir)/src/webp/config.h.in AUTHORS COPYING ChangeLog \
|
||||
NEWS README.md ar-lib compile config.guess config.sub \
|
||||
@@ -351,7 +351,7 @@
|
||||
top_srcdir = @top_srcdir@
|
||||
webp_libname_prefix = @webp_libname_prefix@
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
-SUBDIRS = sharpyuv src imageio man $(am__append_1) examples
|
||||
+SUBDIRS = sharpyuv src imageio man $(am__append_1)
|
||||
EXTRA_DIST = COPYING autogen.sh
|
||||
all: all-recursive
|
||||
|
|
@ -103,15 +103,55 @@ before-all = ".github/workflows/wheels-dependencies.sh"
|
|||
build-verbosity = 1
|
||||
|
||||
config-settings = "raqm=enable raqm=vendor fribidi=vendor imagequant=disable"
|
||||
# Disable platform guessing on macOS
|
||||
macos.config-settings = "raqm=enable raqm=vendor fribidi=vendor imagequant=disable platform-guessing=disable"
|
||||
|
||||
test-command = "cd {project} && .github/workflows/wheels-test.sh"
|
||||
test-extras = "tests"
|
||||
test-requires = [
|
||||
"numpy",
|
||||
]
|
||||
xbuild-tools = [ ]
|
||||
|
||||
[tool.cibuildwheel.macos]
|
||||
# Disable platform guessing on macOS to avoid picking up Homebrew etc.
|
||||
config-settings = "raqm=enable raqm=vendor fribidi=vendor imagequant=disable platform-guessing=disable"
|
||||
|
||||
[tool.cibuildwheel.macos.environment]
|
||||
# Isolate macOS build environment from Homebrew etc.
|
||||
PATH = "$(pwd)/build/deps/darwin/bin:$(dirname $(which python3)):/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin"
|
||||
|
||||
[tool.cibuildwheel.ios]
|
||||
# Disable platform guessing on iOS, and disable raqm (since there won't be a
|
||||
# vendor version, and we can't distribute it due to licensing)
|
||||
config-settings = "raqm=disable imagequant=disable platform-guessing=disable"
|
||||
|
||||
# iOS needs to be given a specific pytest invocation and list of test sources.
|
||||
test-sources = [
|
||||
"checks",
|
||||
"Tests",
|
||||
"selftest.py",
|
||||
]
|
||||
test-command = [
|
||||
"python -m selftest",
|
||||
"python -m pytest -vv -x -W always checks/check_wheel.py Tests",
|
||||
]
|
||||
|
||||
# There's no numpy wheel for iOS (yet...)
|
||||
test-requires = [ ]
|
||||
|
||||
[[tool.cibuildwheel.overrides]]
|
||||
# iOS environment is isolated by cibuildwheel, but needs the dependencies
|
||||
select = "*_iphoneos"
|
||||
environment.PATH = "$(pwd)/build/deps/iphoneos/bin:$PATH"
|
||||
|
||||
[[tool.cibuildwheel.overrides]]
|
||||
# iOS simulator environment is isolated by cibuildwheel, but needs the dependencies
|
||||
select = "*_iphonesimulator"
|
||||
environment.PATH = "$(pwd)/build/deps/iphonesimulator/bin:$PATH"
|
||||
|
||||
[[tool.cibuildwheel.overrides]]
|
||||
select = "*-win32"
|
||||
test-requires = [ ]
|
||||
|
||||
[tool.black]
|
||||
exclude = "wheels/multibuild"
|
||||
|
||||
|
@ -168,7 +208,7 @@ lint.isort.required-imports = [
|
|||
max_supported_python = "3.13"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
addopts = "-ra --color=yes"
|
||||
addopts = "-ra --color=auto"
|
||||
testpaths = [
|
||||
"Tests",
|
||||
]
|
||||
|
|
39
setup.py
39
setup.py
|
@ -473,6 +473,19 @@ class pil_build_ext(build_ext):
|
|||
sdk_path = commandlinetools_sdk_path
|
||||
return sdk_path
|
||||
|
||||
def get_ios_sdk_path(self) -> str:
|
||||
try:
|
||||
sdk = sys.implementation._multiarch.split("-")[-1]
|
||||
_dbg("Using %s SDK", sdk)
|
||||
return (
|
||||
subprocess.check_output(["xcrun", "--show-sdk-path", "--sdk", sdk])
|
||||
.strip()
|
||||
.decode("latin1")
|
||||
)
|
||||
except Exception:
|
||||
msg = "Unable to identify location of iOS SDK."
|
||||
raise ValueError(msg)
|
||||
|
||||
def build_extensions(self) -> None:
|
||||
library_dirs: list[str] = []
|
||||
include_dirs: list[str] = []
|
||||
|
@ -622,6 +635,18 @@ class pil_build_ext(build_ext):
|
|||
|
||||
for extension in self.extensions:
|
||||
extension.extra_compile_args = ["-Wno-nullability-completeness"]
|
||||
|
||||
elif sys.platform == "ios":
|
||||
# Add the iOS SDK path.
|
||||
sdk_path = self.get_ios_sdk_path()
|
||||
|
||||
# Add the iOS SDK path.
|
||||
_add_directory(library_dirs, os.path.join(sdk_path, "usr", "lib"))
|
||||
_add_directory(include_dirs, os.path.join(sdk_path, "usr", "include"))
|
||||
|
||||
for extension in self.extensions:
|
||||
extension.extra_compile_args = ["-Wno-nullability-completeness"]
|
||||
|
||||
elif sys.platform.startswith(("linux", "gnu", "freebsd")):
|
||||
for dirname in _find_library_dirs_ldconfig():
|
||||
_add_directory(library_dirs, dirname)
|
||||
|
@ -877,6 +902,9 @@ class pil_build_ext(build_ext):
|
|||
# so we have to guess; by default it is defined in all Windows builds.
|
||||
# See #4237, #5243, #5359 for more information.
|
||||
defs.append(("USE_WIN32_FILEIO", None))
|
||||
elif sys.platform == "ios":
|
||||
# Ensure transitive dependencies are linked.
|
||||
libs.append("lzma")
|
||||
if feature.get("jpeg"):
|
||||
libs.append(feature.get("jpeg"))
|
||||
defs.append(("HAVE_LIBJPEG", None))
|
||||
|
@ -893,6 +921,9 @@ class pil_build_ext(build_ext):
|
|||
defs.append(("HAVE_LIBIMAGEQUANT", None))
|
||||
if feature.get("xcb"):
|
||||
libs.append(feature.get("xcb"))
|
||||
if sys.platform == "ios":
|
||||
# Ensure transitive dependencies are linked.
|
||||
libs.append("Xau")
|
||||
defs.append(("HAVE_XCB", None))
|
||||
if sys.platform == "win32":
|
||||
libs.extend(["kernel32", "user32", "gdi32"])
|
||||
|
@ -924,6 +955,11 @@ class pil_build_ext(build_ext):
|
|||
libs.append(feature.get("fribidi"))
|
||||
else: # building FriBiDi shim from src/thirdparty
|
||||
srcs.append("src/thirdparty/fribidi-shim/fribidi.c")
|
||||
|
||||
if sys.platform == "ios":
|
||||
# Ensure transitive dependencies are linked.
|
||||
libs.extend(["z", "bz2", "brotlicommon", "brotlidec", "png"])
|
||||
|
||||
self._update_extension("PIL._imagingft", libs, defs, srcs)
|
||||
|
||||
else:
|
||||
|
@ -940,6 +976,9 @@ class pil_build_ext(build_ext):
|
|||
webp = feature.get("webp")
|
||||
if isinstance(webp, str):
|
||||
libs = [webp, webp + "mux", webp + "demux"]
|
||||
if sys.platform == "ios":
|
||||
# Ensure transitive dependencies are linked.
|
||||
libs.append("sharpyuv")
|
||||
self._update_extension("PIL._webp", libs)
|
||||
else:
|
||||
self._remove_extension("PIL._webp")
|
||||
|
|
Loading…
Reference in New Issue
Block a user