1#!/bin/sh
2##
3##  Copyright (c) 2014 The WebM project authors. All Rights Reserved.
4##
5##  Use of this source code is governed by a BSD-style license
6##  that can be found in the LICENSE file in the root of the source
7##  tree. An additional intellectual property rights grant can be found
8##  in the file PATENTS.  All contributing project authors may
9##  be found in the AUTHORS file in the root of the source tree.
10##
11##
12## This script generates 'VPX.framework'. An iOS app can encode and decode VPx
13## video by including 'VPX.framework'.
14##
15## Run iosbuild.sh to create 'VPX.framework' in the current directory.
16##
17set -e
18devnull='> /dev/null 2>&1'
19
20BUILD_ROOT="_iosbuild"
21CONFIGURE_ARGS="--disable-docs
22                --disable-examples
23                --disable-libyuv
24                --disable-unit-tests"
25DIST_DIR="_dist"
26FRAMEWORK_DIR="VPX.framework"
27FRAMEWORK_LIB="VPX.framework/VPX"
28HEADER_DIR="${FRAMEWORK_DIR}/Headers/vpx"
29SCRIPT_DIR=$(dirname "$0")
30LIBVPX_SOURCE_DIR=$(cd ${SCRIPT_DIR}/../..; pwd)
31LIPO=$(xcrun -sdk iphoneos${SDK} -find lipo)
32ORIG_PWD="$(pwd)"
33ARM_TARGETS="arm64-darwin-gcc
34             armv7-darwin-gcc
35             armv7s-darwin-gcc"
36SIM_TARGETS="x86-iphonesimulator-gcc
37             x86_64-iphonesimulator-gcc"
38OSX_TARGETS="x86-darwin18-gcc
39             x86_64-darwin18-gcc"
40TARGETS="${ARM_TARGETS} ${SIM_TARGETS}"
41
42# Configures for the target specified by $1, and invokes make with the dist
43# target using $DIST_DIR as the distribution output directory.
44build_target() {
45  local target="$1"
46  local old_pwd="$(pwd)"
47  local target_specific_flags=""
48
49  vlog "***Building target: ${target}***"
50
51  case "${target}" in
52    x86-*)
53      target_specific_flags="--enable-pic"
54      vlog "Enabled PIC for ${target}"
55      ;;
56  esac
57
58  mkdir "${target}"
59  cd "${target}"
60  eval "${LIBVPX_SOURCE_DIR}/configure" --target="${target}" \
61    ${CONFIGURE_ARGS} ${EXTRA_CONFIGURE_ARGS} ${target_specific_flags} \
62    ${devnull}
63  export DIST_DIR
64  eval make dist ${devnull}
65  cd "${old_pwd}"
66
67  vlog "***Done building target: ${target}***"
68}
69
70# Returns the preprocessor symbol for the target specified by $1.
71target_to_preproc_symbol() {
72  target="$1"
73  case "${target}" in
74    arm64-*)
75      echo "__aarch64__"
76      ;;
77    armv7-*)
78      echo "__ARM_ARCH_7A__"
79      ;;
80    armv7s-*)
81      echo "__ARM_ARCH_7S__"
82      ;;
83    x86-*)
84      echo "__i386__"
85      ;;
86    x86_64-*)
87      echo "__x86_64__"
88      ;;
89    *)
90      echo "#error ${target} unknown/unsupported"
91      return 1
92      ;;
93  esac
94}
95
96# Create a vpx_config.h shim that, based on preprocessor settings for the
97# current target CPU, includes the real vpx_config.h for the current target.
98# $1 is the list of targets.
99create_vpx_framework_config_shim() {
100  local targets="$1"
101  local config_file="${HEADER_DIR}/vpx_config.h"
102  local preproc_symbol=""
103  local target=""
104  local include_guard="VPX_FRAMEWORK_HEADERS_VPX_VPX_CONFIG_H_"
105
106  local file_header="/*
107 *  Copyright (c) $(date +%Y) The WebM project authors. All Rights Reserved.
108 *
109 *  Use of this source code is governed by a BSD-style license
110 *  that can be found in the LICENSE file in the root of the source
111 *  tree. An additional intellectual property rights grant can be found
112 *  in the file PATENTS.  All contributing project authors may
113 *  be found in the AUTHORS file in the root of the source tree.
114 */
115
116/* GENERATED FILE: DO NOT EDIT! */
117
118#ifndef ${include_guard}
119#define ${include_guard}
120
121#if defined"
122
123  printf "%s" "${file_header}" > "${config_file}"
124  for target in ${targets}; do
125    preproc_symbol=$(target_to_preproc_symbol "${target}")
126    printf " ${preproc_symbol}\n" >> "${config_file}"
127    printf "#define VPX_FRAMEWORK_TARGET \"${target}\"\n" >> "${config_file}"
128    printf "#include \"VPX/vpx/${target}/vpx_config.h\"\n" >> "${config_file}"
129    printf "#elif defined" >> "${config_file}"
130    mkdir "${HEADER_DIR}/${target}"
131    cp -p "${BUILD_ROOT}/${target}/vpx_config.h" "${HEADER_DIR}/${target}"
132  done
133
134  # Consume the last line of output from the loop: We don't want it.
135  sed -i.bak -e '$d' "${config_file}"
136  rm "${config_file}.bak"
137
138  printf "#endif\n\n" >> "${config_file}"
139  printf "#endif  // ${include_guard}" >> "${config_file}"
140}
141
142# Verifies that $FRAMEWORK_LIB fat library contains requested builds.
143verify_framework_targets() {
144  local requested_cpus=""
145  local cpu=""
146
147  # Extract CPU from full target name.
148  for target; do
149    cpu="${target%%-*}"
150    if [ "${cpu}" = "x86" ]; then
151      # lipo -info outputs i386 for libvpx x86 targets.
152      cpu="i386"
153    fi
154    requested_cpus="${requested_cpus}${cpu} "
155  done
156
157  # Get target CPUs present in framework library.
158  local targets_built=$(${LIPO} -info ${FRAMEWORK_LIB})
159
160  # $LIPO -info outputs a string like the following:
161  #   Architectures in the fat file: $FRAMEWORK_LIB <architectures>
162  # Capture only the architecture strings.
163  targets_built=${targets_built##*: }
164
165  # Sort CPU strings to make the next step a simple string compare.
166  local actual=$(echo ${targets_built} | tr " " "\n" | sort | tr "\n" " ")
167  local requested=$(echo ${requested_cpus} | tr " " "\n" | sort | tr "\n" " ")
168
169  vlog "Requested ${FRAMEWORK_LIB} CPUs: ${requested}"
170  vlog "Actual ${FRAMEWORK_LIB} CPUs: ${actual}"
171
172  if [ "${requested}" != "${actual}" ]; then
173    elog "Actual ${FRAMEWORK_LIB} targets do not match requested target list."
174    elog "  Requested target CPUs: ${requested}"
175    elog "  Actual target CPUs: ${actual}"
176    return 1
177  fi
178}
179
180# Configures and builds each target specified by $1, and then builds
181# VPX.framework.
182build_framework() {
183  local lib_list=""
184  local targets="$1"
185  local target=""
186  local target_dist_dir=""
187
188  # Clean up from previous build(s).
189  rm -rf "${BUILD_ROOT}" "${FRAMEWORK_DIR}"
190
191  # Create output dirs.
192  mkdir -p "${BUILD_ROOT}"
193  mkdir -p "${HEADER_DIR}"
194
195  cd "${BUILD_ROOT}"
196
197  for target in ${targets}; do
198    build_target "${target}"
199    target_dist_dir="${BUILD_ROOT}/${target}/${DIST_DIR}"
200    if [ "${ENABLE_SHARED}" = "yes" ]; then
201      local suffix="dylib"
202    else
203      local suffix="a"
204    fi
205    lib_list="${lib_list} ${target_dist_dir}/lib/libvpx.${suffix}"
206  done
207
208  cd "${ORIG_PWD}"
209
210  # The basic libvpx API includes are all the same; just grab the most recent
211  # set.
212  cp -p "${target_dist_dir}"/include/vpx/* "${HEADER_DIR}"
213
214  # Build the fat library.
215  ${LIPO} -create ${lib_list} -output ${FRAMEWORK_DIR}/VPX
216
217  # Create the vpx_config.h shim that allows usage of vpx_config.h from
218  # within VPX.framework.
219  create_vpx_framework_config_shim "${targets}"
220
221  # Copy in vpx_version.h.
222  cp -p "${BUILD_ROOT}/${target}/vpx_version.h" "${HEADER_DIR}"
223
224  if [ "${ENABLE_SHARED}" = "yes" ]; then
225    # Adjust the dylib's name so dynamic linking in apps works as expected.
226    install_name_tool -id '@rpath/VPX.framework/VPX' ${FRAMEWORK_DIR}/VPX
227
228    # Copy in Info.plist.
229    cat "${SCRIPT_DIR}/ios-Info.plist" \
230      | sed "s/\${FULLVERSION}/${FULLVERSION}/g" \
231      | sed "s/\${VERSION}/${VERSION}/g" \
232      | sed "s/\${IOS_VERSION_MIN}/${IOS_VERSION_MIN}/g" \
233      > "${FRAMEWORK_DIR}/Info.plist"
234  fi
235
236  # Confirm VPX.framework/VPX contains the targets requested.
237  verify_framework_targets ${targets}
238
239  vlog "Created fat library ${FRAMEWORK_LIB} containing:"
240  for lib in ${lib_list}; do
241    vlog "  $(echo ${lib} | awk -F / '{print $2, $NF}')"
242  done
243}
244
245# Trap function. Cleans up the subtree used to build all targets contained in
246# $TARGETS.
247cleanup() {
248  local res=$?
249  cd "${ORIG_PWD}"
250
251  if [ $res -ne 0 ]; then
252    elog "build exited with error ($res)"
253  fi
254
255  if [ "${PRESERVE_BUILD_OUTPUT}" != "yes" ]; then
256    rm -rf "${BUILD_ROOT}"
257  fi
258}
259
260print_list() {
261  local indent="$1"
262  shift
263  local list="$@"
264  for entry in ${list}; do
265    echo "${indent}${entry}"
266  done
267}
268
269iosbuild_usage() {
270cat << EOF
271  Usage: ${0##*/} [arguments]
272    --help: Display this message and exit.
273    --enable-shared: Build a dynamic framework for use on iOS 8 or later.
274    --extra-configure-args <args>: Extra args to pass when configuring libvpx.
275    --macosx: Uses darwin18 targets instead of iphonesimulator targets for x86
276              and x86_64. Allows linking to framework when builds target MacOSX
277              instead of iOS.
278    --preserve-build-output: Do not delete the build directory.
279    --show-build-output: Show output from each library build.
280    --targets <targets>: Override default target list. Defaults:
281$(print_list "        " ${TARGETS})
282    --test-link: Confirms all targets can be linked. Functionally identical to
283                 passing --enable-examples via --extra-configure-args.
284    --verbose: Output information about the environment and each stage of the
285               build.
286EOF
287}
288
289elog() {
290  echo "${0##*/} failed because: $@" 1>&2
291}
292
293vlog() {
294  if [ "${VERBOSE}" = "yes" ]; then
295    echo "$@"
296  fi
297}
298
299trap cleanup EXIT
300
301# Parse the command line.
302while [ -n "$1" ]; do
303  case "$1" in
304    --extra-configure-args)
305      EXTRA_CONFIGURE_ARGS="$2"
306      shift
307      ;;
308    --help)
309      iosbuild_usage
310      exit
311      ;;
312    --enable-shared)
313      ENABLE_SHARED=yes
314      ;;
315    --preserve-build-output)
316      PRESERVE_BUILD_OUTPUT=yes
317      ;;
318    --show-build-output)
319      devnull=
320      ;;
321    --test-link)
322      EXTRA_CONFIGURE_ARGS="${EXTRA_CONFIGURE_ARGS} --enable-examples"
323      ;;
324    --targets)
325      TARGETS="$2"
326      shift
327      ;;
328    --macosx)
329      TARGETS="${ARM_TARGETS} ${OSX_TARGETS}"
330      ;;
331    --verbose)
332      VERBOSE=yes
333      ;;
334    *)
335      iosbuild_usage
336      exit 1
337      ;;
338  esac
339  shift
340done
341
342if [ "${ENABLE_SHARED}" = "yes" ]; then
343  CONFIGURE_ARGS="--enable-shared ${CONFIGURE_ARGS}"
344fi
345
346FULLVERSION=$("${SCRIPT_DIR}"/version.sh --bare "${LIBVPX_SOURCE_DIR}")
347VERSION=$(echo "${FULLVERSION}" | sed -E 's/^v([0-9]+\.[0-9]+\.[0-9]+).*$/\1/')
348
349if [ "$ENABLE_SHARED" = "yes" ]; then
350  IOS_VERSION_OPTIONS="--enable-shared"
351  IOS_VERSION_MIN="8.0"
352else
353  IOS_VERSION_OPTIONS=""
354  IOS_VERSION_MIN="7.0"
355fi
356
357if [ "${VERBOSE}" = "yes" ]; then
358cat << EOF
359  BUILD_ROOT=${BUILD_ROOT}
360  DIST_DIR=${DIST_DIR}
361  CONFIGURE_ARGS=${CONFIGURE_ARGS}
362  EXTRA_CONFIGURE_ARGS=${EXTRA_CONFIGURE_ARGS}
363  FRAMEWORK_DIR=${FRAMEWORK_DIR}
364  FRAMEWORK_LIB=${FRAMEWORK_LIB}
365  HEADER_DIR=${HEADER_DIR}
366  LIBVPX_SOURCE_DIR=${LIBVPX_SOURCE_DIR}
367  LIPO=${LIPO}
368  MAKEFLAGS=${MAKEFLAGS}
369  ORIG_PWD=${ORIG_PWD}
370  PRESERVE_BUILD_OUTPUT=${PRESERVE_BUILD_OUTPUT}
371  TARGETS="$(print_list "" ${TARGETS})"
372  ENABLE_SHARED=${ENABLE_SHARED}
373  OSX_TARGETS="${OSX_TARGETS}"
374  SIM_TARGETS="${SIM_TARGETS}"
375  SCRIPT_DIR="${SCRIPT_DIR}"
376  FULLVERSION="${FULLVERSION}"
377  VERSION="${VERSION}"
378  IOS_VERSION_MIN="${IOS_VERSION_MIN}"
379EOF
380fi
381
382build_framework "${TARGETS}"
383echo "Successfully built '${FRAMEWORK_DIR}' for:"
384print_list "" ${TARGETS}
385