serval-dna/ios/configure
Andrew Bettison ca8e846264 Add iOS Framework Bundle build
The new ios/configure script performs a separate ../configure for each of
the following iOS targets:

    iPhoneOS        on armv7
    iPhoneOS        on arm64
    iPhoneSimulator on i386
    iPhoneSimulator on x86_64

The script then creates ios/Makefile that builds a static iOS Framework
Bundle suitable for inclusion in an Xcode iOS project.

Add the --xcode-sdk=SDK option to configure.ac, to support cross
compiling using Apple Xcode.  It prefixes all compile/link toolchain
commands with the "xcrun --sdk SDK" command, ie, CC, AS, LD, AR, RANLIB,
etc.

Re-structure headerfiles.mk to separate "public" from "private" headers,
because the Framework module only exposes the public ones.  Moves the
SQLITE3_AMALGAMATION definition from Makefile.in into headerfiles.mk.

Update INSTALL.md and add a technical document for Apple iOS.

This enables development of Serval DNA within the context of an Xcode
iOS project using the standard edit-make-test cycle: after modifying a
Serval DNA source file, "cd ios; make" will recompile the changed file
for all the target architectures and update the Framework Bundle.
Rebuilding the Xcode project will then incorporate the changes, which
can be tested immediately.
2018-03-06 15:29:29 +10:30

404 lines
14 KiB
Bash
Executable File

#!/bin/bash
#
# Build configuration utility for Serval DNA on Apple iOS.
#
# Copyright 2016-2017 Flinders University
#
# This script runs Serval DNA's main 'configure' script several times, once for
# each different target iOS target, and saves the Makefile that is generated by
# each run. It then creates a top-level Makefile that:
# (1) invokes each of the saved Makefiles in turn to generate one static library
# for each target, then
# (2) combines all these libraries into a single "multi slice" static library
# that can be imported into an Xcode project.
#
# This allows the iOS development cycle to be similarly rapid as for platforms
# such as Linux or native Apple OS-X:
# (1) a single 'configure' step adapts the Makefile(s) to the specific needs of
# the target architecture(s), then
# (2) the edit-make-test cycle can be repeated as often as desired:
# (a) edit; make changes to source file(s)
# (b) make; compile the changed source files into executable form using the
# dependency rules in the Makefile(s), which avoids unnecessary
# re-compilation, then re-build the app in Xcode, which links in the
# new static library
# (c) test; run the re-built app, either in an iOS simulator or by
# side-loading into an iPhone
#
# Exit on error
set -e
usage() {
echo "Usage: ${0##*/} [-f|--force]"
}
# Run the Serval DNA configure script once for each architecture, and save the
# files produced by each run.
main() {
setup
parse_command_line "$@"
check
exec 5>&1
TARGETS=()
configure iphoneos armv7 /Library/Serval
configure iphoneos arm64 /Library/Serval
configure iphonesimulator i386 /tmp/serval-dna
configure iphonesimulator x86_64 /tmp/serval-dna
create_makefile
}
# Trace and diagnostic functions.
run() {
echo + "$@" >&5
"$@"
}
usage_error() {
echo "${0##*/}: $*" >&2
usage >&2
exit 1
}
fatal() {
echo "${0##*/}: $1" >&2
shift
while [ $# -ne 0 ]; do
echo "$1" >&2
shift
done
exit 1
}
# Parse command-line options.
parse_command_line() {
library=libservaldaemon.a
swiftmodule=ServalDNA.swiftmodule
opt_force_configure=false
opt_force_config_status=false
log_display_line_count=10
while [ $# -ne 0 ]; do
opt="$1"
shift
case "$opt" in
-h | --help)
usage
exit 0
;;
-f|--force-configure)
opt_force_configure=true
;;
--force-config-status)
opt_force_config_status=true
;;
-*)
usage_error "Unknown option: $opt"
;;
*)
usage_error "Spurious argument: $opt"
;;
esac
done
}
# Work out the path of the directory containing this script relative to the
# current working directory (SCRIPT_DIR), the path of the Serval DNA repository
# root directory relative to the current working directory (SERVAL_DNA_DIR), and
# the path of these two directories relative to each other.
setup() {
case "$0" in
*/?*/*) SCRIPT_DIR="${0%/*}"; SERVAL_DNA_DIR="${0%/?*/*}";;
./*) SCRIPT_DIR="."; SERVAL_DNA_DIR="..";;
*/*) SCRIPT_DIR="${0%/*}"; SERVAL_DNA_DIR=".";;
*) SCRIPT_DIR="."; SERVAL_DNA_DIR="..";;
esac
SCRIPT_DIR_RELATIVE_TO_SERVAL_DNA_DIR="$(cd "$SCRIPT_DIR" >/dev/null && echo "${PWD##*/}")"
SERVAL_DNA_DIR_RELATIVE_TO_SCRIPT_DIR=".."
SERVAL_DNA_CONFIGURE="$SERVAL_DNA_DIR/configure"
}
# Ensure that the Serval DNA 'configure' script exists and is executable.
check() {
if [ ! -e "$SERVAL_DNA_CONFIGURE" ]; then
fatal "missing script: $SERVAL_DNA_CONFIGURE" \
"Run 'autoreconf -f -i -I m4' then run me again."
fi
if [ ! -x "$SERVAL_DNA_CONFIGURE" ]; then
fatal "script is not executable: $SERVAL_DNA_CONFIGURE" \
"Run 'autoreconf -f -i -I m4' then run me again."
fi
case "$("$SERVAL_DNA_CONFIGURE" --version)" in
servald\ configure\ *) ;;
*) fatal "malfunctioning script: $SERVAL_DNA_CONFIGURE" \
"Run 'autoreconf -f -i -I m4' then run me again."
esac
}
configure() {
local platform="${1?}"
local arch="${2?}"
local prefix="${3?}"
# Convert from the Xcode architecture name to the autoconf cpu name, so it
# can be passed in the --host option to the configure script.
local cpu
case "$arch" in
arm64) cpu=aarch64;;
*) cpu="$arch";;
esac
# A "target" is an architecture (aka, "slice" or "architecture", eg, armv7,
# arm64, i386) and an Apple SDK (eg, iphoneos, iphonesimulator, macosx).
local target="$arch-$platform"
TARGETS+=("$arch-$platform")
configure_script="$SERVAL_DNA_DIR_RELATIVE_TO_SCRIPT_DIR/configure"
makefile_in="$SERVAL_DNA_DIR_RELATIVE_TO_SCRIPT_DIR/Makefile.in"
# Path of the directory under which the files this script creates will be placed
# relative to the directory containing this script.
local target_build_dir="$SCRIPT_DIR/build/$target"
target_build_dir="${target_build_dir#./}"
script_dir_relative_to_target_build_dir="../.."
if [ ! -d "$target_build_dir" ]; then
run mkdir -p "$target_build_dir"
fi
# Note: the configure script created by autoconf places its output files in
# the current working directory.
if $opt_force_configure || $opt_force_config_status ||
! [ -e "$target_build_dir/.configured" ] ||
[ "$target_build_dir/config.status" -ot "$configure_script" ] ||
[ "$target_build_dir/Makefile" -ot "$configure_script" ] ||
[ "$target_build_dir/Makefile" -ot "$makefile_in" ] ||
[ "$target_build_dir/Makefile" -ot "$target_build_dir/config.status" ]
then
run pushd "$target_build_dir" >/dev/null
rm -f .configured
configure_script="$script_dir_relative_to_target_build_dir/$configure_script"
makefile_in="$script_dir_relative_to_target_build_dir/$makefile_in"
if $opt_force_configure ||
! [ -e ".configured" ] ||
[ "config.status" -ot "$configure_script" ] ||
[ "Makefile" -ot "$configure_script" ]
then
run rm -f config.status Makefile
if ! run "$configure_script" \
--host="$cpu-apple-darwin" \
--enable-xcode-sdk="$platform" \
--prefix "$prefix" \
&>configure.out
then
fatal "configure failed; see $target_build_dir/configure.out for full log" \
"Last $log_display_line_count lines were:" \
"" \
"$(tail -n $log_display_line_count configure.out)"
fi
if [ ! -r config.status ]; then
fatal "configure for $target did not produce config.status"
fi
if [ ! -r Makefile ]; then
fatal "configure for $target did not produce Makefile"
fi
elif $opt_force_config_status ||
[ "Makefile" -ot "$makefile_in" ] ||
[ "Makefile" -ot "config.status" ]
then
run rm -f Makefile
if ! run ./config.status &>config.status.out
then
fatal "./config.status failed; see $target_build_dir/config.status.out:" \
"$(cat config.status.out)"
fi
fi
> .configured
run popd >/dev/null
fi
}
# Create a Serval DNA Makefile that creates an Apple framework bundle:
# - generates a modulemap file for the bundle
# - generates a Xcode config file
# - invokes all the per-target iOS makefiles to create one static library for
# each target slice (arch-sdk)
# - merges all the static libraries into a single, multi-slice library
# - generates an Info.plist XML file describing the bundle
# - creates the correct directory structure and symbolic links for the bundle
foreach_target() {
local target
local target_dir
for target in "${TARGETS[@]}"; do
arch="${target%%-*}"
sdk="${target#-*}"
target_dir="build/$target"
target_var=$(echo -n "$target" | tr -C a-zA-z0-9_ _)
eval "$@"
done
}
create_makefile() {
echo "Creating $SCRIPT_DIR/Makefile"
cat >"$SCRIPT_DIR/Makefile" <<EOF
# Makefile for Serval DNA on Apple iOS
# Copyright 2017 Flinders University
# This file was auto-generated by the ./${0##*/} script
# $(date)
srcdir=$SERVAL_DNA_DIR_RELATIVE_TO_SCRIPT_DIR
dstdir=frameworks
targets=${TARGETS[*]}
fw_name=ServalDNA
fw_version=A
fw_bundle_id=org.servalproject.serval-dna
fw_bundle_version=1.0
fw_dir=\$(dstdir)/\$(fw_name).framework
fw_dstdir=\$(fw_dir)/Versions/\$(fw_version)
fw_library=\$(fw_dstdir)/\$(fw_name)
fw_swiftmodule=\$(fw_dstdir)/Modules/\$(fw_name).swiftmodule
fw_info_plist=\$(fw_dstdir)/Resources/Info.plist
fw_config_headers=\$(targets:%=\$(fw_dstdir)/Headers/%/config.h)
fw_serval_headers=\$(addprefix \$(fw_dstdir)/Headers/, \$(LIB_HDRS) \$(PUBLIC_HDRS))
fw_sqlite3_headers=\$(addprefix \$(fw_dstdir)/Headers/, \$(notdir \$(SQLITE3_HDRS)))
fw_modulemap=\$(fw_dstdir)/Modules/module.modulemap
xcconfig=\$(dstdir)/\$(fw_name).xcconfig
include \$(srcdir)/headerfiles.mk
.PHONY: all \\
clean\
$(foreach_target echo '" \\"' \; \
echo '" install_${target_var}_sodium_headers \\"' \; \
echo '" $target_dir/$library \\"' \; \
echo '" $target_dir/$swiftmodule \\"' \; \
echo -n '" clean-$target"')
all: \$(fw_library) \\
\$(fw_info_plist) \\
\$(fw_config_headers) \\
\$(fw_serval_headers) \\
\$(fw_sqlite3_headers) \\
\$(fw_modulemap) \\
\$(xcconfig)$(foreach_target echo '" \\"' \; \
echo -n '" install_${target_var}_sodium_headers"' \; \
echo -n '" \$(fw_swiftmodule)/$arch.swiftmodule"')
cd \$(fw_dstdir)/.. && ln -snf \$(fw_version) Current
cd \$(fw_dstdir)/../.. && ln -snf Versions/Current/* .
\$(fw_info_plist): ./info-plist.sh
mkdir -p \$(dir \$@)
./info-plist.sh \$(fw_name) \$(fw_bundle_id) \$(fw_bundle_version) >\$@
\$(fw_library):$(foreach_target echo '" \\"' \; \
echo -n '" $target_dir/$library"')
mkdir -p \$(dir \$@)
lipo -create -output \$@ \$^
$(foreach_target echo '"\$(fw_swiftmodule)/$arch.swiftmodule: $target_dir/$swiftmodule"' \; \
echo '" mkdir -p \$(dir \$@)"' \; \
echo '" cp \$< \$@"' \; \
echo)
\$(fw_config_headers): \$(fw_dstdir)/Headers/%: build/%
mkdir -p \$(dir \$@)
cp \$< \$@
\$(fw_serval_headers): \$(fw_dstdir)/Headers/%: \$(srcdir)/%
mkdir -p \$(dir \$@)
cp \$< \$@
\$(fw_sqlite3_headers): \$(fw_dstdir)/Headers/%: \$(srcdir)/\$(SQLITE3_AMALGAMATION)/%
mkdir -p \$(dir \$@)
cp \$< \$@
\$(fw_modulemap):$(foreach_target echo '" \\"' \; \
echo -n '" install_${target_var}_sodium_headers"') \\
\$(srcdir)/headerfiles.mk Makefile
mkdir -p \$(dir \$@)
@{ \\
echo '/*' \\
echo 'module.modulemap for Serval DNA on Apple iOS' ;\\
echo 'Copyright 2017 Flinders University' ;\\
echo 'This file was auto-generated by \$(abspath Makefile)' ;\\
echo '\$(shell date)' ;\\
echo '*/' ;\\
echo 'framework module ServalDNA {' ;\\
echo ' requires tls // thread-local storage' ;\\
echo ' explicit module Daemon {' ;\\
echo ' export Sodium' ;\\
$(foreach_target echo '" echo '\'' header \"$target/config.h\"'\'' ;\\"')
for header in \$(LIB_HDRS) \$(PUBLIC_HDRS) \$(notdir \$(SQLITE3_HDRS)); do \\
echo " header \"\$\$header\"" ;\\
done ; \\
echo ' }' ;\\
echo ' explicit module Sodium {' ;\\
$(foreach_target echo '" echo '\'' header \"$target/sodium.h\"'\'' ;\\"' \; \
echo '" (cd $target_dir/libsodium-dev/include/; find . -type f -print | sed -e '\''s|^\./||'\'' -e '\''s|.*| header \"$target/&\"|'\'' ) ;\\"')
echo ' }' ;\\
echo '}' ;\\
echo 'module ifaddrs [system] [extern_c] {' ;\\
echo ' header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/ifaddrs.h"' ;\\
echo ' export *' ;\\
echo '}' ;\\
echo 'module inttypes [system] [extern_c] {' ;\\
echo ' header "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/clang/include/inttypes.h"' ;\\
echo ' export *' ;\\
echo '}' ;\\
} >\$@
\$(xcconfig): Makefile
mkdir -p \$(dir \$@)
@{ \\
echo '// ServalDNA.xcconfig for Serval DNA on Apple iOS' ;\\
echo '// Copyright 2017 Flinders University' ;\\
echo '// This file was auto-generated by \$(abspath Makefile)' ;\\
echo '// \$(shell date)' ;\\
echo 'SERVAL_DNA_FRAMEWORK_DIR = \$(abspath \$(fw_dir))' ;\\
echo 'FRAMEWORK_SEARCH_PATHS = \$\$(inherited) \$(abspath \$(dstdir))' ;\\
echo 'GCC_PREPROCESSOR_DEFINITIONS = \$\$(inherited) HAVE_CONFIG_H=1' ;\\
$(foreach_target echo '" echo '\''HEADER_SEARCH_PATHS[arch=$arch] = \$\$(inherited) \$\$(SERVAL_DNA_FRAMEWORK_DIR)/Headers/$target'\'' ;\\"')
echo 'SWIFT_INCLUDE_PATHS = \$\$(inherited) \$\$(SERVAL_DNA_FRAMEWORK_DIR)/Modules' ;\\
echo 'OTHER_SWIFT_FLAGS = \$\$(inherited) -Xcc -DHAVE_CONFIG_H=1' ;\\
} >\$@
$(foreach_target echo \; \
echo '"install_${target_var}_sodium_headers: $target_dir/$library"' \; \
echo '" cp -R $target_dir/libsodium-dev/include/ \$(fw_dstdir)/Headers/$target/"')
# If the source directory is already configured, then its config.h header
# file would interfere with these sub-makes, so delete it before invoking
# each sub-make.
$(foreach_target echo \; \
echo '"$target_dir/$library:"' \; \
echo '" \$(RM) \$(srcdir)/config.h"' \; \
echo '" \$(MAKE) -C $target_dir $library"')
$(foreach_target echo \; \
echo '"$target_dir/$swiftmodule:"' \; \
echo '" \$(RM) \$(srcdir)/config.h"' \; \
echo '" \$(MAKE) -C $target_dir $swiftmodule"')
clean: $(foreach_target echo -n '" clean-$target"')
\$(RM) -r \$(dstdir)
$(foreach_target echo \; \
echo '"clean-$target:"' \; \
echo '" \$(MAKE) -C $target_dir clean"')
EOF
}
main "$@"