• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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