11#! /bin/bash
22
3- # Setup that needs to be done before multibuild utils are invoked
4- PROJECTDIR=$( pwd)
5- if [[ " $( uname -s) " == " Darwin" ]]; then
6- # Safety check - macOS builds require that CIBW_ARCHS is set, and that it
7- # only contains a single value (even though cibuildwheel allows multiple
8- # values in CIBW_ARCHS).
3+ # Safety check - Pillow builds require that CIBW_ARCHS is set, and that it only
4+ # contains a single value (even though cibuildwheel allows multiple values in
5+ # CIBW_ARCHS). This check doesn't work on Linux because of how the CIBW_ARCHS
6+ # variable is exposed.
7+ function check_cibw_archs {
98 if [[ -z " $CIBW_ARCHS " ]]; then
10- echo " ERROR: Pillow macOS builds require CIBW_ARCHS be defined."
9+ echo " ERROR: Pillow builds require CIBW_ARCHS be defined."
1110 exit 1
1211 fi
1312 if [[ " $CIBW_ARCHS " == * " " * ]]; then
14- echo " ERROR: Pillow macOS builds only support a single architecture in CIBW_ARCHS."
13+ echo " ERROR: Pillow builds only support a single architecture in CIBW_ARCHS."
1514 exit 1
1615 fi
16+ }
17+
18+ # Setup that needs to be done before multibuild utils are invoked. Process
19+ # potential cross-build platforms before native platforms to ensure that we pick
20+ # up the cross environment.
21+ PROJECTDIR=$( pwd)
22+ if [[ " $CIBW_PLATFORM " == " ios" ]]; then
23+ check_cibw_archs
24+ # On iOS, CIBW_ARCHS is actually a multi-arch - arm64_iphoneos,
25+ # arm64_iphonesimulator or x86_64_iphonesimulator. Split into the CPU
26+ # platform, and the iOS SDK.
27+ PLAT=$( echo $CIBW_ARCHS | sed " s/\(.*\)_\(.*\)/\1/" )
28+ IOS_SDK=$( echo $CIBW_ARCHS | sed " s/\(.*\)_\(.*\)/\2/" )
29+
30+ # Build iOS builds in `build/iphoneos` or `build/iphonesimulator`
31+ # (depending on the build target). Install them into `build/deps/iphoneos`
32+ # or `build/deps/iphonesimulator`
33+ WORKDIR=$( pwd) /build/$IOS_SDK
34+ BUILD_PREFIX=$( pwd) /build/deps/$IOS_SDK
35+ PATCH_DIR=$( pwd) /patches/iOS
36+
37+ # GNU tooling insists on using aarch64 rather than arm64
38+ if [[ $PLAT == " arm64" ]]; then
39+ GNU_ARCH=aarch64
40+ else
41+ GNU_ARCH=x86_64
42+ fi
43+
44+ IOS_SDK_PATH=$( xcrun --sdk $IOS_SDK --show-sdk-path)
45+ CMAKE_SYSTEM_NAME=iOS
46+ IOS_HOST_TRIPLE=$PLAT -apple-ios$IPHONEOS_DEPLOYMENT_TARGET
47+ if [[ " $IOS_SDK " == " iphonesimulator" ]]; then
48+ IOS_HOST_TRIPLE=$IOS_HOST_TRIPLE -simulator
49+ fi
1750
51+ # GNU Autotools doesn't recognize the existence of arm64-apple-ios-simulator
52+ # as a valid host. However, the only difference between arm64-apple-ios and
53+ # arm64-apple-ios-simulator is the choice of sysroot, and that is
54+ # coordinated by CC, CFLAGS etc. From the perspective of configure, the two
55+ # platforms are identical, so we can use arm64-apple-ios consistently.
56+ # This (mostly) avoids us needing to patch config.sub in dependency sources.
57+ HOST_CONFIGURE_FLAGS=" --disable-shared --enable-static --host=$GNU_ARCH -apple-ios --build=$GNU_ARCH -apple-darwin"
58+
59+ # CMake has native support for iOS. However, most of that support is based
60+ # on using the Xcode builder, which isn't very helpful for most of Pillow's
61+ # dependencies. Therefore, we lean on the OSX configurations, plus CC, CFLAGS
62+ # etc. to ensure the right sysroot is selected.
63+ 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"
64+
65+ # Meson needs to be pointed at a cross-platform configuration file
66+ # This will be generated once CC etc. have been evaluated.
67+ HOST_MESON_FLAGS=" --cross-file $WORKDIR /meson-cross.txt -Dprefer_static=true -Ddefault_library=static"
68+
69+ elif [[ " $( uname -s) " == " Darwin" ]]; then
70+ check_cibw_archs
1871 # Build macOS dependencies in `build/darwin`
1972 # Install them into `build/deps/darwin`
73+ PLAT=$CIBW_ARCHS
2074 WORKDIR=$( pwd) /build/darwin
2175 BUILD_PREFIX=$( pwd) /build/deps/darwin
2276else
2377 # Build prefix will default to /usr/local
78+ PLAT=" ${CIBW_ARCHS:- $AUDITWHEEL_ARCH } "
2479 WORKDIR=$( pwd) /build
2580 MB_ML_LIBC=${AUDITWHEEL_POLICY:: 9}
2681 MB_ML_VER=${AUDITWHEEL_POLICY: 9}
2782fi
28- PLAT=" ${CIBW_ARCHS:- $AUDITWHEEL_ARCH } "
2983
3084# Define custom utilities
3185source wheels/multibuild/common_utils.sh
3286source wheels/multibuild/library_builders.sh
33- if [ -z " $IS_MACOS " ]; then
87+ if [[ -z " $IS_MACOS " ] ]; then
3488 source wheels/multibuild/manylinux_utils.sh
3589fi
3690
3791ARCHIVE_SDIR=pillow-depends-main
3892
39- # Package versions for fresh source builds
93+ # Package versions for fresh source builds. Version numbers with "Patched"
94+ # annotations have a source code patch that is required for some platforms. If
95+ # you change those versions, ensure the patch is also updated.
4096FREETYPE_VERSION=2.13.3
4197HARFBUZZ_VERSION=11.2.1
4298LIBPNG_VERSION=1.6.49
@@ -47,32 +103,58 @@ TIFF_VERSION=4.7.0
47103LCMS2_VERSION=2.17
48104ZLIB_VERSION=1.3.1
49105ZLIB_NG_VERSION=2.2.4
50- LIBWEBP_VERSION=1.5.0
106+ LIBWEBP_VERSION=1.5.0 # Patched; next release won't need patching. See patch file.
51107BZIP2_VERSION=1.0.8
52108LIBXCB_VERSION=1.17.0
53- BROTLI_VERSION=1.1.0
109+ BROTLI_VERSION=1.1.0 # Patched; next release won't need patching. See patch file.
54110LIBAVIF_VERSION=1.3.0
55111
56112function build_pkg_config {
57113 if [ -e pkg-config-stamp ]; then return ; fi
58- # This essentially duplicates the Homebrew recipe
59- CFLAGS=" $CFLAGS -Wno-int-conversion" build_simple pkg-config 0.29.2 https://pkg-config.freedesktop.org/releases tar.gz \
114+ # This essentially duplicates the Homebrew recipe.
115+ # On iOS, we need a binary that can be executed on the build machine; but we
116+ # can create a host-specific pc-path to store iOS .pc files. To ensure a
117+ # macOS-compatible build, we temporarily clear environment flags that set
118+ # iOS-specific values.
119+ if [[ -n " $IOS_SDK " ]]; then
120+ ORIGINAL_HOST_CONFIGURE_FLAGS=$HOST_CONFIGURE_FLAGS
121+ ORIGINAL_IPHONEOS_DEPLOYMENT_TARGET=$IPHONEOS_DEPLOYMENT_TARGET
122+ unset HOST_CONFIGURE_FLAGS
123+ unset IPHONEOS_DEPLOYMENT_TARGET
124+ fi
125+
126+ CFLAGS=" $CFLAGS -Wno-int-conversion" CPPFLAGS=" " build_simple pkg-config 0.29.2 https://pkg-config.freedesktop.org/releases tar.gz \
60127 --disable-debug --disable-host-tool --with-internal-glib \
61128 --with-pc-path=$BUILD_PREFIX /share/pkgconfig:$BUILD_PREFIX /lib/pkgconfig \
62129 --with-system-include-path=$( xcrun --show-sdk-path --sdk macosx) /usr/include
130+
131+ if [[ -n " $IOS_SDK " ]]; then
132+ HOST_CONFIGURE_FLAGS=$ORIGINAL_HOST_CONFIGURE_FLAGS
133+ IPHONEOS_DEPLOYMENT_TARGET=$ORIGINAL_IPHONEOS_DEPLOYMENT_TARGET
134+ fi ;
135+
63136 export PKG_CONFIG=$BUILD_PREFIX /bin/pkg-config
64137 touch pkg-config-stamp
65138}
66139
67140function build_zlib_ng {
68141 if [ -e zlib-stamp ]; then return ; fi
142+ # zlib-ng uses a "configure" script, but it's not a GNU autotools script, so
143+ # it doesn't honor the usual flags. Temporarily disable any
144+ # cross-compilation flags.
145+ ORIGINAL_HOST_CONFIGURE_FLAGS=$HOST_CONFIGURE_FLAGS
146+ unset HOST_CONFIGURE_FLAGS
147+
69148 build_github zlib-ng/zlib-ng $ZLIB_NG_VERSION --zlib-compat
70149
71- if [ -n " $IS_MACOS " ]; then
150+ HOST_CONFIGURE_FLAGS=$ORIGINAL_HOST_CONFIGURE_FLAGS
151+
152+ if [[ -n " $IS_MACOS " ]] && [[ -z " $IOS_SDK " ]]; then
72153 # Ensure that on macOS, the library name is an absolute path, not an
73154 # @rpath, so that delocate picks up the right library (and doesn't need
74155 # DYLD_LIBRARY_PATH to be set). The default Makefile doesn't have an
75- # option to control the install_name.
156+ # option to control the install_name. This isn't needed on iOS, as iOS
157+ # only builds the static library.
76158 install_name_tool -id $BUILD_PREFIX /lib/libz.1.dylib $BUILD_PREFIX /lib/libz.1.dylib
77159 fi
78160 touch zlib-stamp
@@ -82,7 +164,7 @@ function build_brotli {
82164 if [ -e brotli-stamp ]; then return ; fi
83165 local out_dir=$( fetch_unpack https://github.com/google/brotli/archive/v$BROTLI_VERSION .tar.gz brotli-$BROTLI_VERSION .tar.gz)
84166 (cd $out_dir \
85- && cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_LIBDIR=$BUILD_PREFIX /lib -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX /lib . \
167+ && cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_LIBDIR=$BUILD_PREFIX /lib -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX /lib $HOST_CMAKE_FLAGS . \
86168 && make install)
87169 touch brotli-stamp
88170}
@@ -93,7 +175,7 @@ function build_harfbuzz {
93175
94176 local out_dir=$( fetch_unpack https://github.com/harfbuzz/harfbuzz/releases/download/$HARFBUZZ_VERSION /harfbuzz-$HARFBUZZ_VERSION .tar.xz harfbuzz-$HARFBUZZ_VERSION .tar.xz)
95177 (cd $out_dir \
96- && meson setup build --prefix=$BUILD_PREFIX --libdir=$BUILD_PREFIX /lib --buildtype=minsize -Dfreetype=enabled -Dglib=disabled -Dtests=disabled)
178+ && meson setup build --prefix=$BUILD_PREFIX --libdir=$BUILD_PREFIX /lib --buildtype=minsize -Dfreetype=enabled -Dglib=disabled -Dtests=disabled $HOST_MESON_FLAGS )
97179 (cd $out_dir /build \
98180 && meson install)
99181 touch harfbuzz-stamp
@@ -164,19 +246,19 @@ function build {
164246 fi
165247
166248 build_simple xcb-proto 1.17.0 https://xorg.freedesktop.org/archive/individual/proto
167- if [ -n " $IS_MACOS " ]; then
249+ if [[ -n " $IS_MACOS " ] ]; then
168250 build_simple xorgproto 2024.1 https://www.x.org/pub/individual/proto
169251 build_simple libXau 1.0.12 https://www.x.org/pub/individual/lib
170252 build_simple libpthread-stubs 0.5 https://xcb.freedesktop.org/dist
171253 else
172- sed s/\$ {pc_sysrootdir\} // $BUILD_PREFIX /share/pkgconfig/xcb-proto.pc > $BUILD_PREFIX /lib/pkgconfig/xcb-proto.pc
254+ sed " s/\$ {pc_sysrootdir\}//" $BUILD_PREFIX /share/pkgconfig/xcb-proto.pc > $BUILD_PREFIX /lib/pkgconfig/xcb-proto.pc
173255 fi
174256 build_simple libxcb $LIBXCB_VERSION https://www.x.org/releases/individual/lib
175257
176258 build_libjpeg_turbo
177- if [ -n " $IS_MACOS " ]; then
259+ if [[ -n " $IS_MACOS " ] ]; then
178260 # Custom tiff build to include jpeg; by default, configure won't include
179- # headers/libs in the custom macOS prefix. Explicitly disable webp,
261+ # headers/libs in the custom macOS/iOS prefix. Explicitly disable webp,
180262 # libdeflate and zstd, because on x86_64 macs, it will pick up the
181263 # Homebrew versions of those libraries from /usr/local.
182264 build_simple tiff $TIFF_VERSION https://download.osgeo.org/libtiff tar.gz \
@@ -186,7 +268,10 @@ function build {
186268 build_tiff
187269 fi
188270
189- build_libavif
271+ if [[ -z " $IOS_SDK " ]]; then
272+ # Short term workaround; don't build libavif on iOS
273+ build_libavif
274+ fi
190275 build_libpng
191276 build_lcms2
192277 build_openjpeg
@@ -201,14 +286,44 @@ function build {
201286
202287 build_brotli
203288
204- if [ -n " $IS_MACOS " ]; then
289+ if [[ -n " $IS_MACOS " ] ]; then
205290 # Custom freetype build
206291 build_simple freetype $FREETYPE_VERSION https://download.savannah.gnu.org/releases/freetype tar.gz --with-harfbuzz=no
207292 else
208293 build_freetype
209294 fi
210295
211- build_harfbuzz
296+ if [[ -z " $IOS_SDK " ]]; then
297+ # On iOS, there's no vendor-provided raqm, and we can't ship it due to
298+ # licensing, so there's no point building harfbuzz.
299+ build_harfbuzz
300+ fi
301+ }
302+
303+ function create_meson_cross_config {
304+ cat << EOF > $WORKDIR /meson-cross.txt
305+ [binaries]
306+ pkg-config = '$BUILD_PREFIX /bin/pkg-config'
307+ cmake = '$( which cmake) '
308+ c = '$CC '
309+ cpp = '$CXX '
310+ strip = '$STRIP '
311+
312+ [built-in options]
313+ c_args = '$CFLAGS -I$BUILD_PREFIX /include'
314+ cpp_args = '$CXXFLAGS -I$BUILD_PREFIX /include'
315+ c_link_args = '$CFLAGS -L$BUILD_PREFIX /lib'
316+ cpp_link_args = '$CFLAGS -L$BUILD_PREFIX /lib'
317+
318+ [host_machine]
319+ system = 'darwin'
320+ subsystem = 'ios'
321+ kernel = 'xnu'
322+ cpu_family = '$( uname -m) '
323+ cpu = '$( uname -m) '
324+ endian = 'little'
325+
326+ EOF
212327}
213328
214329# Perform all dependency builds in the build subfolder.
@@ -227,24 +342,40 @@ if [[ ! -d $WORKDIR/pillow-depends-main ]]; then
227342fi
228343
229344if [[ -n " $IS_MACOS " ]]; then
230- # Homebrew (or similar packaging environments) install can contain some of
231- # the libraries that we're going to build. However, they may be compiled
232- # with a MACOSX_DEPLOYMENT_TARGET that doesn't match what we want to use,
233- # and they may bring in other dependencies that we don't want. The same will
234- # be true of any other locations on the path. To avoid conflicts, strip the
235- # path down to the bare minimum (which, on macOS, won't include any
236- # development dependencies).
237- export PATH=" $BUILD_PREFIX /bin:$( dirname $( which python3) ) :/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin"
238- export CMAKE_PREFIX_PATH=$BUILD_PREFIX
239-
240345 # Ensure the basic structure of the build prefix directory exists.
241346 mkdir -p " $BUILD_PREFIX /bin"
242347 mkdir -p " $BUILD_PREFIX /lib"
243348
244- # Ensure pkg-config is available
349+ # Ensure pkg-config is available. This is done *before* setting CC, CFLAGS
350+ # etc. to ensure that the build is *always* a macOS build, even when building
351+ # for iOS.
245352 build_pkg_config
246- # Ensure cmake is available
353+
354+ # Ensure cmake is available, and that the default prefix used by CMake is
355+ # the build prefix
247356 python3 -m pip install cmake
357+ export CMAKE_PREFIX_PATH=$BUILD_PREFIX
358+
359+ if [[ -n " $IOS_SDK " ]]; then
360+ export AR=" $( xcrun --find --sdk $IOS_SDK ar) "
361+ export CPP=" $( xcrun --find --sdk $IOS_SDK clang) -E"
362+ export CC=$( xcrun --find --sdk $IOS_SDK clang)
363+ export CXX=$( xcrun --find --sdk $IOS_SDK clang++)
364+ export LD=$( xcrun --find --sdk $IOS_SDK ld)
365+ export STRIP=$( xcrun --find --sdk $IOS_SDK strip)
366+
367+ CPPFLAGS=" $CPPFLAGS --sysroot=$IOS_SDK_PATH "
368+ CFLAGS=" -target $IOS_HOST_TRIPLE --sysroot=$IOS_SDK_PATH -mios-version-min=$IPHONEOS_DEPLOYMENT_TARGET "
369+ CXXFLAGS=" -target $IOS_HOST_TRIPLE --sysroot=$IOS_SDK_PATH -mios-version-min=$IPHONEOS_DEPLOYMENT_TARGET "
370+
371+ # Having IPHONEOS_DEPLOYMENT_TARGET in the environment causes problems
372+ # with some cross-building toolchains, because it introduces implicit
373+ # behavior into clang.
374+ unset IPHONEOS_DEPLOYMENT_TARGET
375+
376+ # Now that we know CC etc., we can create a meson cross-configuration file
377+ create_meson_cross_config
378+ fi
248379fi
249380
250381wrap_wheel_builder build
0 commit comments