#!/usr/bin/env bash # # Your build script should simply source this script, optionally override any build hooks and then invoke `build`. # The build product should be available under `build-~/out`, under the library path. # # Hook lifecycle: # - build # - initialize # - needs # - clean # - prepare # - target # - prepare # - configure # - build # - finalize # - merge # - clean # # You can override any of these hooks to provide a custom implementation or call their underscore variant to delegate to the default implementation. # For example: # target_prepare() { make -s distclean; } # target_configure() { _target_configure "$@" --enable-minimal; } set -e # needs ... # # Utility for ensuring all tools needed by the script are installed prior to starting. needs() { _needs "$@"; } _needs() { local failed=0 for tool; do hash "$tool" || { echo >&2 "Missing: $tool. Please install this tool."; (( failed++ )); } done return $failed } # initialize # # The build script invokes this once prior to all other actions if the user wants a clean slate. initialize() { _initialize "$@"; } _initialize() { initialize_needs "$@" initialize_clean "$@" } # initialize_needs # # Check if all tools needed for the default implementations are available. # # By default, this will check for `automake` and `autoreconf`. initialize_needs() { _initialize_needs "$@"; } _initialize_needs() { needs automake autoreconf } # initialize_clean # # Perform any necessary clean-up of the library code prior to building. # # By default, this will run `make distclean`. initialize_clean() { _initialize_clean "$@"; } _initialize_clean() { [[ ! -e Makefile ]] || make -s distclean } # prepare [ ...] # # Configure the library for building the s on this machine. # The build script invokes this once prior to building each of its targets. # The has been newly created. # # By default, this will run `autoreconf`. prepare() { _prepare "$@"; } _prepare() { local prefix=$1; shift 1 autoreconf --verbose --install --symlink 2> >(sed 's/^\([^:]*\):[0-9]\{1,\}: /\1: /') } # target # # Build the library for the given and into the given . # The build script invokes this function when it's ready to build the library's code. # Generic platform-specific environment setup has been done. target() { _target "$@"; } _target() { target_prepare "$@" target_configure "$@" target_build "$@" } # target_prepare # # Prepare the library configuration for building the target. # # By default, this will run `make clean` if a Makefile is found. target_prepare() { _target_prepare "$@"; } _target_prepare() { local prefix=$1 arch=$2 platform=$3; shift 3 [[ ! -e Makefile ]] || make -s clean } # target_configure [ ...] # # Configure the library for building the target. # # By default, this will run `./configure --host= --prefix=/ --disable-shared `. target_configure() { _target_configure "$@"; } _target_configure() { local prefix=$1 arch=$2 platform=$3 cpu=$arch; shift 3 [[ $cpu = *arm* ]] && cpu=arm ./configure ${cpu:+--host="$cpu"} --prefix="$prefix/$arch" --disable-shared "$@" } # target_build # # Build the library code for the target. # # By default, this will run `make check install`. target_build() { _target_build "$@"; } _target_build() { local prefix=$1 arch=$2 platform=$3; shift 3 #make -j3 check make -j3 install } # finalize [ ... ] # # Prepare the final build product. # The build script invokes this once after a successful build of all targets. finalize() { _finalize "$@"; } _finalize() { finalize_merge "$@" finalize_clean "$@" } # finalize_merge [ ... ] # # Merge all targets into a product the application can use, available at `/out`. # # By default, this will copy the headers to `/out/include` and merge the libraries into `/out/lib`. finalize_merge() { _finalize_merge "$@"; } _finalize_merge() { local prefix=$1; shift 1 mv -f -- "$prefix/$1/include" "$prefix/out/" mkdir -p "$prefix/out/lib" for lib in "$prefix/$1/lib/"*; do if lipo -info "$lib" >/dev/null 2>&1; then local lib=("${lib##*/}") libs=("${@/#/$prefix/}") libs=("${libs[@]/%//lib/$lib}") lipo -create "${libs[@]}" -output "$prefix/out/lib/$lib" fi done } # finalize_clean [ ... ] # # Clean up the library after a successful build (eg. housekeeping of temporary files). # # By default, this will run `make distclean`. finalize_clean() { _finalize_clean "$@"; } _finalize_clean() { [[ ! -e Makefile ]] || make -s distclean } # build [] # # Build the library (found at ../) for platform (or "host" if unspecified). build() { _build "$@"; } _build() { local name=$1 platform=${2:-host} local path="../$name" [[ $path = /* ]] || path="${BASH_SOURCE%/*}/$path" cd "$path" if [[ $platform = host ]]; then case "$(uname -s)" in 'Darwin') platform='macos' archs=( "$(uname -m)" ) ;; esac fi if (( ! ${#archs[@]} )); then case "$platform" in 'macos') archs=( 'x86_64' ) ;; 'ios') archs=( 'i386' 'x86_64' 'armv7' 'armv7s' 'arm64' ) ;; esac fi local prefix="$PWD/build-$platform~" initialize "$prefix" # "clean" argument wipes the prefix and exits. If lib exists in prefix, skip build. if [[ ${BASH_ARGV[@]:(-1)} = clean ]]; then rm -rf "$prefix" exit elif files=( "$prefix"/out/lib/* ) && [[ -e $files ]]; then echo >&2 "Output product already exists: ${files[*]}. Skipping build." exit fi # Prepare the output location and build configuration. mkdir -p "$prefix/out" prepare "$prefix" "${archs[@]}" # Repeat the build for each individual architecture. for arch in "${archs[@]}"; do ( # Set up a base environment for the platform. case "$platform" in 'macos') SDKROOT="$(xcrun --show-sdk-path --sdk macosx)" export PATH="$(xcrun --show-sdk-platform-path --sdk macosx)/usr/bin:$PATH" export CFLAGS="-arch $arch -isysroot $SDKROOT -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET:-10.8} -O2 -flto -g $CFLAGS" export LDFLAGS="-arch $arch -isysroot $SDKROOT -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET:-10.8} -flto $LDFLAGS" export CPPFLAGS="$CFLAGS $CPPFLAGS" ;; 'ios') if [[ $arch = *arm* ]]; then SDKROOT="$(xcrun --show-sdk-path --sdk iphoneos)" export PATH="$(xcrun --show-sdk-platform-path --sdk iphoneos)/usr/bin:$PATH" export CFLAGS="-mthumb -arch $arch -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -O2 -flto -g $CFLAGS" export LDFLAGS="-mthumb -arch $arch -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -flto $LDFLAGS" export CPPFLAGS="$CFLAGS $CPPFLAGS" else SDKROOT="$(xcrun --show-sdk-path --sdk iphonesimulator)" export PATH="$(xcrun --show-sdk-platform-path --sdk iphonesimulator)/usr/bin:$PATH" export CFLAGS="-arch $arch -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -O2 -flto -g $CFLAGS" export LDFLAGS="-arch $arch -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -flto $LDFLAGS" export CPPFLAGS="$CFLAGS $CPPFLAGS" fi ;; esac target "$prefix" "$arch" "$platform" ); done finalize "$prefix" "${archs[@]}" }