aboutsummaryrefslogtreecommitdiff
path: root/xcframeworkbuild.sh
blob: 14b987d46be9876f4ef717eff75ca3ae27ff3d9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#!/bin/bash
#
# This script generates 'WebP.xcframework', 'WebPDecoder.xcframework',
# 'WebPDemux.xcframework' and 'WebPMux.xcframework'.
# An iOS, Mac or Mac Catalyst app can decode WebP images by including
# 'WebPDecoder.xcframework' and both encode and decode WebP images by including
# 'WebP.xcframework'.
#
# Run ./xcframeworkbuild.sh to generate the frameworks under the current
# directory (the previous build will be erased if it exists).
#

set -e

# Set these variables based on the desired minimum deployment target.
readonly IOS_MIN_VERSION=6.0
readonly MACOSX_MIN_VERSION=10.15
readonly MACOSX_CATALYST_MIN_VERSION=14.0

# Extract Xcode version.
readonly XCODE=$(xcodebuild -version | grep Xcode | cut -d " " -f2)
if [[ -z "${XCODE}" ]] || [[ "${XCODE%%.*}" -lt 11 ]]; then
  echo "Xcode 11.0 or higher is required!"
  exit 1
fi

# Extract the latest SDK version from the final field of the form: iphoneosX.Y
# / macosxX.Y
readonly SDK=($(
  xcodebuild -showsdks \
    | grep iphoneos | sort | tail -n 1 | awk '{print substr($NF, 9)}'
  xcodebuild -showsdks \
    | grep macosx | sort | tail -n 1 | awk '{print substr($NF, 7)}'
))
readonly IOS=0
readonly MACOS=1
readonly IOS_SIMULATOR=2
readonly MACOS_CATALYST=3
readonly NUM_PLATFORMS=4

readonly OLDPATH=${PATH}

# Names should be of the form '<platform>-[<variant>-]<architecture>'.
PLATFORMS[$IOS]="iPhoneOS-armv7 iPhoneOS-armv7s iPhoneOS-arm64"
PLATFORMS[$IOS_SIMULATOR]="iPhoneSimulator-i386 iPhoneSimulator-x86_64"
PLATFORMS[$MACOS]="MacOSX-x86_64"
PLATFORMS[$MACOS_CATALYST]="MacOSX-Catalyst-x86_64"
if [[ "${XCODE%%.*}" -ge 12 ]]; then
  PLATFORMS[$MACOS]+=" MacOSX-arm64"
  PLATFORMS[$MACOS_CATALYST]+=" MacOSX-Catalyst-arm64"
  PLATFORMS[$IOS_SIMULATOR]+=" iPhoneSimulator-arm64"
elif [[ "${XCODE%%.*}" -eq 11 ]]; then
  cat << EOF
WARNING: Xcode 12.0 or higher is required to build targets for
WARNING: Apple Silicon (arm64). The XCFrameworks generated with Xcode 11 will
WARNING: contain libraries for MacOS & Catalyst supporting x86_64 only.
WARNING: The build will continue in 5 seconds...
EOF
  sleep 5
else
  echo "Xcode 11.0 or higher is required!"
  exit 1
fi
readonly PLATFORMS
readonly SRCDIR=$(dirname $0)
readonly TOPDIR=$(pwd)
readonly BUILDDIR="${TOPDIR}/xcframeworkbuild"
readonly TARGETDIR="${TOPDIR}/WebP.xcframework"
readonly DECTARGETDIR="${TOPDIR}/WebPDecoder.xcframework"
readonly MUXTARGETDIR="${TOPDIR}/WebPMux.xcframework"
readonly DEMUXTARGETDIR="${TOPDIR}/WebPDemux.xcframework"
readonly SHARPYUVTARGETDIR="${TOPDIR}/SharpYuv.xcframework"
readonly DEVELOPER=$(xcode-select --print-path)
readonly DEVROOT="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain"
readonly PLATFORMSROOT="${DEVELOPER}/Platforms"
readonly LIPO=$(xcrun -sdk iphoneos${SDK[$IOS]} -find lipo)

if [[ -z "${SDK[$IOS]}" ]] || [[ ${SDK[$IOS]%%.*} -lt 8 ]]; then
  echo "iOS SDK version 8.0 or higher is required!"
  exit 1
fi

#######################################
# Moves Headers/*.h to Headers/<framework>/
#
# Places framework headers in a subdirectory to avoid Xcode errors when using
# multiple frameworks:
#   error: Multiple commands produce
#     '.../Build/Products/Debug-iphoneos/include/types.h'
# Arguments:
#   $1 - path to framework
#######################################
update_headers_path() {
  local framework_name="$(basename ${1%.xcframework})"
  local subdir
  for d in $(find "$1" -path "*/Headers"); do
    subdir="$d/$framework_name"
    if [[ -d "$subdir" ]]; then
      # SharpYuv will have a sharpyuv subdirectory. macOS is case insensitive,
      # but for consistency with the other frameworks, rename the directory to
      # match the case of the framework name.
      mv "$(echo ${subdir} | tr 'A-Z' 'a-z')" "$subdir"
    else
      mkdir "$subdir"
      mv "$d/"*.h "$subdir"
    fi
  done
}

echo "Xcode Version: ${XCODE}"
echo "iOS SDK Version: ${SDK[$IOS]}"
echo "MacOS SDK Version: ${SDK[$MACOS]}"

if [[ -e "${BUILDDIR}" || -e "${TARGETDIR}" || -e "${DECTARGETDIR}" \
      || -e "${MUXTARGETDIR}" || -e "${DEMUXTARGETDIR}" \
      || -e "${SHARPYUVTARGETDIR}" ]]; then
  cat << EOF
WARNING: The following directories will be deleted:
WARNING:   ${BUILDDIR}
WARNING:   ${TARGETDIR}
WARNING:   ${DECTARGETDIR}
WARNING:   ${MUXTARGETDIR}
WARNING:   ${DEMUXTARGETDIR}
WARNING:   ${SHARPYUVTARGETDIR}
WARNING: The build will continue in 5 seconds...
EOF
  sleep 5
fi
rm -rf ${BUILDDIR} ${TARGETDIR} ${DECTARGETDIR} \
  ${MUXTARGETDIR} ${DEMUXTARGETDIR} ${SHARPYUVTARGETDIR}

if [[ ! -e ${SRCDIR}/configure ]]; then
  if ! (cd ${SRCDIR} && sh autogen.sh); then
    cat << EOF
Error creating configure script!
This script requires the autoconf/automake and libtool to build. MacPorts or
Homebrew can be used to obtain these:
https://www.macports.org/install.php
https://brew.sh/
EOF
    exit 1
  fi
fi

for (( i = 0; i < $NUM_PLATFORMS; ++i )); do
  LIBLIST=()
  DECLIBLIST=()
  MUXLIBLIST=()
  DEMUXLIBLIST=()
  SHARPYUVLIBLIST=()

  for PLATFORM in ${PLATFORMS[$i]}; do
    ROOTDIR="${BUILDDIR}/${PLATFORM}"
    mkdir -p "${ROOTDIR}"

    ARCH="${PLATFORM##*-}"
    case "${PLATFORM}" in
      iPhone*)
        sdk="${SDK[$IOS]}"
        ;;
      MacOS*)
        sdk="${SDK[$MACOS]}"
        ;;
      *)
        echo "Unrecognized platform: ${PLATFORM}!"
        exit 1
        ;;
    esac

    SDKROOT="${PLATFORMSROOT}/${PLATFORM%%-*}.platform/"
    SDKROOT+="Developer/SDKs/${PLATFORM%%-*}${sdk}.sdk/"
    CFLAGS="-pipe -isysroot ${SDKROOT} -O3 -DNDEBUG"
    case "${PLATFORM}" in
      iPhone*)
        CFLAGS+=" -fembed-bitcode"
        CFLAGS+=" -target ${ARCH}-apple-ios${IOS_MIN_VERSION}"
        [[ "${PLATFORM}" == *Simulator* ]] && CFLAGS+="-simulator"
        ;;
      MacOSX-Catalyst*)
        CFLAGS+=" -target"
        CFLAGS+=" ${ARCH}-apple-ios${MACOSX_CATALYST_MIN_VERSION}-macabi"
        ;;
      MacOSX*)
        CFLAGS+=" -mmacosx-version-min=${MACOSX_MIN_VERSION}"
        ;;
    esac

    set -x
    export PATH="${DEVROOT}/usr/bin:${OLDPATH}"
    ${SRCDIR}/configure --host=${ARCH/arm64/aarch64}-apple-darwin \
      --build=$(${SRCDIR}/config.guess) \
      --prefix=${ROOTDIR} \
      --disable-shared --enable-static \
      --enable-libwebpdecoder --enable-swap-16bit-csp \
      --enable-libwebpmux \
      CC="clang -arch ${ARCH}" \
      CFLAGS="${CFLAGS}"
    set +x

    # Build only the libraries, skip the examples.
    make V=0 -C sharpyuv install
    make V=0 -C src install

    LIBLIST+=("${ROOTDIR}/lib/libwebp.a")
    DECLIBLIST+=("${ROOTDIR}/lib/libwebpdecoder.a")
    MUXLIBLIST+=("${ROOTDIR}/lib/libwebpmux.a")
    DEMUXLIBLIST+=("${ROOTDIR}/lib/libwebpdemux.a")
    SHARPYUVLIBLIST+=("${ROOTDIR}/lib/libsharpyuv.a")
    # xcodebuild requires a directory for the -headers option, these will match
    # for all builds.
    make -C src install-data DESTDIR="${ROOTDIR}/lib-headers"
    make -C src install-commonHEADERS DESTDIR="${ROOTDIR}/dec-headers"
    make -C src/demux install-data DESTDIR="${ROOTDIR}/demux-headers"
    make -C src/mux install-data DESTDIR="${ROOTDIR}/mux-headers"
    make -C sharpyuv install-data DESTDIR="${ROOTDIR}/sharpyuv-headers"
    LIB_HEADERS="${ROOTDIR}/lib-headers/${ROOTDIR}/include/webp"
    DEC_HEADERS="${ROOTDIR}/dec-headers/${ROOTDIR}/include/webp"
    DEMUX_HEADERS="${ROOTDIR}/demux-headers/${ROOTDIR}/include/webp"
    MUX_HEADERS="${ROOTDIR}/mux-headers/${ROOTDIR}/include/webp"
    SHARPYUV_HEADERS="${ROOTDIR}/sharpyuv-headers/${ROOTDIR}/include/webp"

    make distclean

    export PATH=${OLDPATH}
  done

  [[ -z "${LIBLIST[@]}" ]] && continue

  # Create a temporary target directory for each <platform>[-<variant>].
  target_dir="${BUILDDIR}/${PLATFORMS[$i]}"
  target_dir="${target_dir%% *}"
  target_dir="${target_dir%-*}"
  target_lib="${target_dir}/$(basename ${LIBLIST[0]})"
  target_declib="${target_dir}/$(basename ${DECLIBLIST[0]})"
  target_demuxlib="${target_dir}/$(basename ${DEMUXLIBLIST[0]})"
  target_muxlib="${target_dir}/$(basename ${MUXLIBLIST[0]})"
  target_sharpyuvlib="${target_dir}/$(basename ${SHARPYUVLIBLIST[0]})"

  mkdir -p "${target_dir}"
  ${LIPO} -create ${LIBLIST[@]} -output "${target_lib}"
  ${LIPO} -create ${DECLIBLIST[@]} -output "${target_declib}"
  ${LIPO} -create ${DEMUXLIBLIST[@]} -output "${target_demuxlib}"
  ${LIPO} -create ${MUXLIBLIST[@]} -output "${target_muxlib}"
  ${LIPO} -create ${SHARPYUVLIBLIST[@]} -output "${target_sharpyuvlib}"
  FAT_LIBLIST+=(-library "${target_lib}" -headers "${LIB_HEADERS}")
  FAT_DECLIBLIST+=(-library "${target_declib}" -headers "${DEC_HEADERS}")
  FAT_DEMUXLIBLIST+=(-library "${target_demuxlib}" -headers "${DEMUX_HEADERS}")
  FAT_MUXLIBLIST+=(-library "${target_muxlib}" -headers "${MUX_HEADERS}")
  FAT_SHARPYUVLIBLIST+=(-library "${target_sharpyuvlib}")
  FAT_SHARPYUVLIBLIST+=(-headers "${SHARPYUV_HEADERS}")
done

# lipo will not put archives with the same architecture (e.g., x86_64
# iPhoneSimulator & MacOS) in the same fat output file. xcodebuild
# -create-xcframework requires universal archives to avoid e.g.:
#   Both ios-x86_64-maccatalyst and ios-arm64-maccatalyst represent two
#   equivalent library definitions
set -x
xcodebuild -create-xcframework "${FAT_LIBLIST[@]}" \
  -output ${TARGETDIR}
xcodebuild -create-xcframework "${FAT_DECLIBLIST[@]}" \
  -output ${DECTARGETDIR}
xcodebuild -create-xcframework "${FAT_DEMUXLIBLIST[@]}" \
  -output ${DEMUXTARGETDIR}
xcodebuild -create-xcframework "${FAT_MUXLIBLIST[@]}" \
  -output ${MUXTARGETDIR}
xcodebuild -create-xcframework "${FAT_SHARPYUVLIBLIST[@]}" \
  -output ${SHARPYUVTARGETDIR}
update_headers_path "${TARGETDIR}"
update_headers_path "${DECTARGETDIR}"
update_headers_path "${DEMUXTARGETDIR}"
update_headers_path "${MUXTARGETDIR}"
update_headers_path "${SHARPYUVTARGETDIR}"
set +x

echo  "SUCCESS"