1#!/bin/bash 2# 3# This script generates 'WebP.xcframework', 'WebPDecoder.xcframework', 4# 'WebPDemux.xcframework' and 'WebPMux.xcframework'. 5# An iOS, Mac or Mac Catalyst app can decode WebP images by including 6# 'WebPDecoder.xcframework' and both encode and decode WebP images by including 7# 'WebP.xcframework'. 8# 9# Run ./xcframeworkbuild.sh to generate the frameworks under the current 10# directory (the previous build will be erased if it exists). 11# 12 13set -e 14 15# Set these variables based on the desired minimum deployment target. 16readonly IOS_MIN_VERSION=6.0 17readonly MACOSX_MIN_VERSION=10.15 18readonly MACOSX_CATALYST_MIN_VERSION=14.0 19 20# Extract Xcode version. 21readonly XCODE=$(xcodebuild -version | grep Xcode | cut -d " " -f2) 22if [[ -z "${XCODE}" ]] || [[ "${XCODE%%.*}" -lt 11 ]]; then 23 echo "Xcode 11.0 or higher is required!" 24 exit 1 25fi 26 27# Extract the latest SDK version from the final field of the form: iphoneosX.Y 28# / macosxX.Y 29readonly SDK=($( 30 xcodebuild -showsdks \ 31 | grep iphoneos | sort | tail -n 1 | awk '{print substr($NF, 9)}' 32 xcodebuild -showsdks \ 33 | grep macosx | sort | tail -n 1 | awk '{print substr($NF, 7)}' 34)) 35readonly IOS=0 36readonly MACOS=1 37readonly IOS_SIMULATOR=2 38readonly MACOS_CATALYST=3 39readonly NUM_PLATFORMS=4 40 41readonly OLDPATH=${PATH} 42 43# Names should be of the form '<platform>-[<variant>-]<architecture>'. 44PLATFORMS[$IOS]="iPhoneOS-armv7 iPhoneOS-armv7s iPhoneOS-arm64" 45PLATFORMS[$IOS_SIMULATOR]="iPhoneSimulator-i386 iPhoneSimulator-x86_64" 46PLATFORMS[$MACOS]="MacOSX-x86_64" 47PLATFORMS[$MACOS_CATALYST]="MacOSX-Catalyst-x86_64" 48if [[ "${XCODE%%.*}" -ge 12 ]]; then 49 PLATFORMS[$MACOS]+=" MacOSX-arm64" 50 PLATFORMS[$MACOS_CATALYST]+=" MacOSX-Catalyst-arm64" 51 PLATFORMS[$IOS_SIMULATOR]+=" iPhoneSimulator-arm64" 52elif [[ "${XCODE%%.*}" -eq 11 ]]; then 53 cat << EOF 54WARNING: Xcode 12.0 or higher is required to build targets for 55WARNING: Apple Silicon (arm64). The XCFrameworks generated with Xcode 11 will 56WARNING: contain libraries for MacOS & Catalyst supporting x86_64 only. 57WARNING: The build will continue in 5 seconds... 58EOF 59 sleep 5 60else 61 echo "Xcode 11.0 or higher is required!" 62 exit 1 63fi 64readonly PLATFORMS 65readonly SRCDIR=$(dirname $0) 66readonly TOPDIR=$(pwd) 67readonly BUILDDIR="${TOPDIR}/xcframeworkbuild" 68readonly TARGETDIR="${TOPDIR}/WebP.xcframework" 69readonly DECTARGETDIR="${TOPDIR}/WebPDecoder.xcframework" 70readonly MUXTARGETDIR="${TOPDIR}/WebPMux.xcframework" 71readonly DEMUXTARGETDIR="${TOPDIR}/WebPDemux.xcframework" 72readonly DEVELOPER=$(xcode-select --print-path) 73readonly DEVROOT="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain" 74readonly PLATFORMSROOT="${DEVELOPER}/Platforms" 75readonly LIPO=$(xcrun -sdk iphoneos${SDK[$IOS]} -find lipo) 76 77if [[ -z "${SDK[$IOS]}" ]] || [[ ${SDK[$IOS]%%.*} -lt 8 ]]; then 78 echo "iOS SDK version 8.0 or higher is required!" 79 exit 1 80fi 81 82####################################### 83# Moves Headers/*.h to Headers/<framework>/ 84# 85# Places framework headers in a subdirectory to avoid Xcode errors when using 86# multiple frameworks: 87# error: Multiple commands produce 88# '.../Build/Products/Debug-iphoneos/include/types.h' 89# Arguments: 90# $1 - path to framework 91####################################### 92update_headers_path() { 93 local framework_name="$(basename ${1%.xcframework})" 94 local subdir 95 for d in $(find "$1" -path "*/Headers"); do 96 subdir="$d/$framework_name" 97 mkdir "$subdir" 98 mv "$d/"*.h "$subdir" 99 done 100} 101 102echo "Xcode Version: ${XCODE}" 103echo "iOS SDK Version: ${SDK[$IOS]}" 104echo "MacOS SDK Version: ${SDK[$MACOS]}" 105 106if [[ -e "${BUILDDIR}" || -e "${TARGETDIR}" || -e "${DECTARGETDIR}" \ 107 || -e "${MUXTARGETDIR}" || -e "${DEMUXTARGETDIR}" ]]; then 108 cat << EOF 109WARNING: The following directories will be deleted: 110WARNING: ${BUILDDIR} 111WARNING: ${TARGETDIR} 112WARNING: ${DECTARGETDIR} 113WARNING: ${MUXTARGETDIR} 114WARNING: ${DEMUXTARGETDIR} 115WARNING: The build will continue in 5 seconds... 116EOF 117 sleep 5 118fi 119rm -rf ${BUILDDIR} ${TARGETDIR} ${DECTARGETDIR} \ 120 ${MUXTARGETDIR} ${DEMUXTARGETDIR} 121 122if [[ ! -e ${SRCDIR}/configure ]]; then 123 if ! (cd ${SRCDIR} && sh autogen.sh); then 124 cat << EOF 125Error creating configure script! 126This script requires the autoconf/automake and libtool to build. MacPorts or 127Homebrew can be used to obtain these: 128https://www.macports.org/install.php 129https://brew.sh/ 130EOF 131 exit 1 132 fi 133fi 134 135for (( i = 0; i < $NUM_PLATFORMS; ++i )); do 136 LIBLIST=() 137 DECLIBLIST=() 138 MUXLIBLIST=() 139 DEMUXLIBLIST=() 140 141 for PLATFORM in ${PLATFORMS[$i]}; do 142 ROOTDIR="${BUILDDIR}/${PLATFORM}" 143 mkdir -p "${ROOTDIR}" 144 145 ARCH="${PLATFORM##*-}" 146 case "${PLATFORM}" in 147 iPhone*) 148 sdk="${SDK[$IOS]}" 149 ;; 150 MacOS*) 151 sdk="${SDK[$MACOS]}" 152 ;; 153 *) 154 echo "Unrecognized platform: ${PLATFORM}!" 155 exit 1 156 ;; 157 esac 158 159 SDKROOT="${PLATFORMSROOT}/${PLATFORM%%-*}.platform/" 160 SDKROOT+="Developer/SDKs/${PLATFORM%%-*}${sdk}.sdk/" 161 CFLAGS="-pipe -isysroot ${SDKROOT} -O3 -DNDEBUG" 162 case "${PLATFORM}" in 163 iPhone*) 164 CFLAGS+=" -fembed-bitcode" 165 CFLAGS+=" -target ${ARCH}-apple-ios${IOS_MIN_VERSION}" 166 [[ "${PLATFORM}" == *Simulator* ]] && CFLAGS+="-simulator" 167 ;; 168 MacOSX-Catalyst*) 169 CFLAGS+=" -target" 170 CFLAGS+=" ${ARCH}-apple-ios${MACOSX_CATALYST_MIN_VERSION}-macabi" 171 ;; 172 MacOSX*) 173 CFLAGS+=" -mmacosx-version-min=${MACOSX_MIN_VERSION}" 174 ;; 175 esac 176 177 set -x 178 export PATH="${DEVROOT}/usr/bin:${OLDPATH}" 179 ${SRCDIR}/configure --host=${ARCH/arm64/aarch64}-apple-darwin \ 180 --build=$(${SRCDIR}/config.guess) \ 181 --prefix=${ROOTDIR} \ 182 --disable-shared --enable-static \ 183 --enable-libwebpdecoder --enable-swap-16bit-csp \ 184 --enable-libwebpmux \ 185 CC="clang -arch ${ARCH}" \ 186 CFLAGS="${CFLAGS}" 187 set +x 188 189 # Build only the libraries, skip the examples. 190 make V=0 -C sharpyuv 191 make V=0 -C src install 192 193 LIBLIST+=("${ROOTDIR}/lib/libwebp.a") 194 DECLIBLIST+=("${ROOTDIR}/lib/libwebpdecoder.a") 195 MUXLIBLIST+=("${ROOTDIR}/lib/libwebpmux.a") 196 DEMUXLIBLIST+=("${ROOTDIR}/lib/libwebpdemux.a") 197 # xcodebuild requires a directory for the -headers option, these will match 198 # for all builds. 199 make -C src install-data DESTDIR="${ROOTDIR}/lib-headers" 200 make -C src install-commonHEADERS DESTDIR="${ROOTDIR}/dec-headers" 201 make -C src/demux install-data DESTDIR="${ROOTDIR}/demux-headers" 202 make -C src/mux install-data DESTDIR="${ROOTDIR}/mux-headers" 203 LIB_HEADERS="${ROOTDIR}/lib-headers/${ROOTDIR}/include/webp" 204 DEC_HEADERS="${ROOTDIR}/dec-headers/${ROOTDIR}/include/webp" 205 DEMUX_HEADERS="${ROOTDIR}/demux-headers/${ROOTDIR}/include/webp" 206 MUX_HEADERS="${ROOTDIR}/mux-headers/${ROOTDIR}/include/webp" 207 208 make distclean 209 210 export PATH=${OLDPATH} 211 done 212 213 [[ -z "${LIBLIST[@]}" ]] && continue 214 215 # Create a temporary target directory for each <platform>[-<variant>]. 216 target_dir="${BUILDDIR}/${PLATFORMS[$i]}" 217 target_dir="${target_dir%% *}" 218 target_dir="${target_dir%-*}" 219 target_lib="${target_dir}/$(basename ${LIBLIST[0]})" 220 target_declib="${target_dir}/$(basename ${DECLIBLIST[0]})" 221 target_demuxlib="${target_dir}/$(basename ${DEMUXLIBLIST[0]})" 222 target_muxlib="${target_dir}/$(basename ${MUXLIBLIST[0]})" 223 224 mkdir -p "${target_dir}" 225 ${LIPO} -create ${LIBLIST[@]} -output "${target_lib}" 226 ${LIPO} -create ${DECLIBLIST[@]} -output "${target_declib}" 227 ${LIPO} -create ${DEMUXLIBLIST[@]} -output "${target_demuxlib}" 228 ${LIPO} -create ${MUXLIBLIST[@]} -output "${target_muxlib}" 229 FAT_LIBLIST+=(-library "${target_lib}" -headers "${LIB_HEADERS}") 230 FAT_DECLIBLIST+=(-library "${target_declib}" -headers "${DEC_HEADERS}") 231 FAT_DEMUXLIBLIST+=(-library "${target_demuxlib}" -headers "${DEMUX_HEADERS}") 232 FAT_MUXLIBLIST+=(-library "${target_muxlib}" -headers "${MUX_HEADERS}") 233done 234 235# lipo will not put archives with the same architecture (e.g., x86_64 236# iPhoneSimulator & MacOS) in the same fat output file. xcodebuild 237# -create-xcframework requires universal archives to avoid e.g.: 238# Both ios-x86_64-maccatalyst and ios-arm64-maccatalyst represent two 239# equivalent library definitions 240set -x 241xcodebuild -create-xcframework "${FAT_LIBLIST[@]}" \ 242 -output ${TARGETDIR} 243xcodebuild -create-xcframework "${FAT_DECLIBLIST[@]}" \ 244 -output ${DECTARGETDIR} 245xcodebuild -create-xcframework "${FAT_DEMUXLIBLIST[@]}" \ 246 -output ${DEMUXTARGETDIR} 247xcodebuild -create-xcframework "${FAT_MUXLIBLIST[@]}" \ 248 -output ${MUXTARGETDIR} 249update_headers_path "${TARGETDIR}" 250update_headers_path "${DECTARGETDIR}" 251update_headers_path "${DEMUXTARGETDIR}" 252update_headers_path "${MUXTARGETDIR}" 253set +x 254 255echo "SUCCESS" 256