Update objc-build-scripts

This commit is contained in:
Justin Spahr-Summers
2013-11-11 11:21:34 -08:00
parent 75aff11a61
commit c5a6a223bc
5 changed files with 271 additions and 82 deletions

View File

@@ -1,6 +1,66 @@
These scripts are primarily meant to support the use of # objc-build-scripts
[Janky](https://github.com/github/janky). To use them, read the contents of this
repository into a `script` folder: This project is a collection of scripts created with two goals:
1. To standardize how Objective-C projects are bootstrapped after cloning
1. To easily build Objective-C projects on continuous integration servers
## Scripts
Right now, there are two important scripts: [`bootstrap`](#bootstrap) and
[`cibuild`](#cibuild). Both are Bash scripts, to maximize compatibility and
eliminate pesky system configuration issues (like setting up a working Ruby
environment).
The structure of the scripts on disk is meant to follow that of a typical Ruby
project:
```
script/
bootstrap
cibuild
```
### bootstrap
This script is responsible for bootstrapping (initializing) your project after
it's been checked out. Here, you should install or clone any dependencies that
are required for a working build and development environment.
By default, the script will verify that [xctool][] is installed, then initialize
and update submodules recursively. If any submodules contain `script/bootstrap`,
that will be run as well.
To check that other tools are installed, you can set the `REQUIRED_TOOLS`
environment variable before running `script/bootstrap`, or edit it within the
script directly. Note that no installation is performed automatically, though
this can always be added within your specific project.
### cibuild
This script is responsible for building the project, as you would want it built
for continuous integration. This is preferable to putting the logic on the CI
server itself, since it ensures that any changes are versioned along with the
source.
By default, the script will run [`bootstrap`](#bootstrap), look for any Xcode
workspace or project in the working directory, then build all targets/schemes
(as found by `xcodebuild -list`) using [xctool][].
You can also specify the schemes to build by passing them into the script:
```sh
script/cibuild ReactiveCocoa-Mac ReactiveCocoa-iOS
```
As with the `bootstrap` script, there are several environment variables that can
be used to customize behavior. They can be set on the command line before
invoking the script, or the defaults changed within the script directly.
## Getting Started
To add the scripts to your project, read the contents of this repository into
a `script` folder:
``` ```
$ git remote add objc-build-scripts https://github.com/jspahrsummers/objc-build-scripts.git $ git remote add objc-build-scripts https://github.com/jspahrsummers/objc-build-scripts.git
@@ -8,13 +68,15 @@ $ git fetch objc-build-scripts
$ git read-tree --prefix=script/ -u objc-build-scripts/master $ git read-tree --prefix=script/ -u objc-build-scripts/master
``` ```
Then commit the changes to incorporate the scripts into your own repository's Then commit the changes, to incorporate the scripts into your own repository's
history. You can also freely tweak the scripts for your specific project's history. You can also freely tweak the scripts for your specific project's
needs. needs.
To bring in upstream changes later: To merge in upstream changes later:
``` ```
$ git fetch -p objc-build-scripts $ git fetch -p objc-build-scripts
$ git merge -Xsubtree=script objc-build-scripts/master $ git merge --ff --squash -Xsubtree=script objc-build-scripts/master
``` ```
[xctool]: https://github.com/facebook/xctool

View File

@@ -1,11 +1,73 @@
#!/bin/bash #!/bin/bash
SCRIPT_DIR=$(dirname "$0") export SCRIPT_DIR=$(dirname "$0")
cd "$SCRIPT_DIR/.."
set -o errexit ##
## Configuration Variables
##
echo "*** Updating submodules..." config ()
git submodule sync --quiet {
git submodule update --init # A whitespace-separated list of executables that must be present and locatable.
git submodule foreach --recursive --quiet "git submodule sync --quiet && git submodule update --init" : ${REQUIRED_TOOLS="xctool"}
export REQUIRED_TOOLS
}
##
## Bootstrap Process
##
main ()
{
config
if [ -n "$REQUIRED_TOOLS" ]
then
echo "*** Checking dependencies..."
check_deps
fi
local submodules=$(git submodule status 2>/dev/null)
if [ -n "$submodules" ]
then
echo "*** Updating submodules..."
update_submodules
fi
}
check_deps ()
{
for tool in $REQUIRED_TOOLS
do
which -s "$tool"
if [ "$?" -ne "0" ]
then
echo "*** Error: $tool not found. Please install it and bootstrap again."
exit 1
fi
done
}
bootstrap_submodule ()
{
local bootstrap="script/bootstrap"
if [ -e "$bootstrap" ]
then
echo "*** Bootstrapping $name..."
"$bootstrap" >/dev/null
else
update_submodules
fi
}
update_submodules ()
{
git submodule sync --quiet && git submodule update --init && git submodule foreach --quiet bootstrap_submodule
}
export -f bootstrap_submodule
export -f update_submodules
main

View File

@@ -1,114 +1,142 @@
#!/bin/bash #!/bin/bash
SCRIPT_DIR=$(dirname "$0") export SCRIPT_DIR=$(dirname "$0")
cd "$SCRIPT_DIR/.."
## ##
## Configuration Variables ## Configuration Variables
## ##
# The build configuration to use. SCHEMES="$@"
if [ -z "$XCCONFIGURATION" ]
then
XCCONFIGURATION="Release"
fi
# The workspace to build. config ()
# {
# If not set and no workspace is found, the -workspace flag will not be passed # The workspace to build.
# to xcodebuild. #
if [ -z "$XCWORKSPACE" ] # If not set and no workspace is found, the -workspace flag will not be passed
then # to `xctool`.
XCWORKSPACE=$(ls -d *.xcworkspace 2>/dev/null | head -n 1) #
fi # Only one of `XCWORKSPACE` and `XCODEPROJ` needs to be set. The former will
# take precedence.
: ${XCWORKSPACE=$(find_pattern "*.xcworkspace")}
# A bootstrap script to run before building. # The project to build.
# #
# If this file does not exist, it is not considered an error. # If not set and no project is found, the -project flag will not be passed
BOOTSTRAP="$SCRIPT_DIR/bootstrap" # to `xctool`.
#
# Only one of `XCWORKSPACE` and `XCODEPROJ` needs to be set. The former will
# take precedence.
: ${XCODEPROJ=$(find_pattern "*.xcodeproj")}
# A whitespace-separated list of default targets or schemes to build, if none # A bootstrap script to run before building.
# are specified on the command line. #
# # If this file does not exist, it is not considered an error.
# Individual names can be quoted to avoid word splitting. : ${BOOTSTRAP="$SCRIPT_DIR/bootstrap"}
DEFAULT_TARGETS=
# Extra build settings to pass to xcodebuild. # Extra options to pass to xctool.
XCODEBUILD_SETTINGS="TEST_AFTER_BUILD=YES" : ${XCTOOL_OPTIONS="RUN_CLANG_STATIC_ANALYZER=NO"}
# A whitespace-separated list of default schemes to build.
#
# Individual names can be quoted to avoid word splitting.
: ${SCHEMES:=$(xcodebuild -list 2>/dev/null | awk -f "$SCRIPT_DIR/schemes.awk")}
export XCWORKSPACE
export XCODEPROJ
export BOOTSTRAP
export XCTOOL_OPTIONS
export SCHEMES
}
## ##
## Build Process ## Build Process
## ##
if [ -z "$*" ] main ()
then {
# lol recursive shell script config
if [ -n "$DEFAULT_TARGETS" ]
if [ -f "$BOOTSTRAP" ]
then then
echo "$DEFAULT_TARGETS" | xargs "$SCRIPT_DIR/cibuild" echo "*** Bootstrapping..."
else "$BOOTSTRAP" || exit $?
xcodebuild -list | awk -f "$SCRIPT_DIR/targets.awk" | xargs "$SCRIPT_DIR/cibuild"
fi fi
exit $? echo "*** The following schemes will be built:"
fi echo "$SCHEMES" | xargs -n 1 echo " "
echo
if [ -f "$BOOTSTRAP" ] echo "$SCHEMES" | xargs -n 1 | (
then local status=0
echo "*** Bootstrapping..."
bash "$BOOTSTRAP" || exit $?
fi
echo "*** The following targets will be built:" while read scheme
do
build_scheme "$scheme" || status=1
done
for target in "$@" exit $status
do )
echo "$target" }
done
echo "*** Cleaning all targets..." find_pattern ()
xcodebuild -alltargets clean OBJROOT="$PWD/build" SYMROOT="$PWD/build" $XCODEBUILD_SETTINGS
run_xcodebuild ()
{ {
local scheme=$1 ls -d $1 2>/dev/null | head -n 1
}
run_xctool ()
{
if [ -n "$XCWORKSPACE" ] if [ -n "$XCWORKSPACE" ]
then then
xcodebuild -workspace "$XCWORKSPACE" -scheme "$scheme" -configuration "$XCCONFIGURATION" build OBJROOT="$PWD/build" SYMROOT="$PWD/build" $XCODEBUILD_SETTINGS xctool -workspace "$XCWORKSPACE" $XCTOOL_OPTIONS "$@" 2>&1
elif [ -n "$XCODEPROJ" ]
then
xctool -project "$XCODEPROJ" $XCTOOL_OPTIONS "$@" 2>&1
else else
xcodebuild -scheme "$scheme" -configuration "$XCCONFIGURATION" build OBJROOT="$PWD/build" SYMROOT="$PWD/build" $XCODEBUILD_SETTINGS echo "*** No workspace or project file found."
exit 1
fi fi
}
local status=$? parse_build ()
{
return $status awk -f "$SCRIPT_DIR/xctool.awk" 2>&1 >/dev/null
} }
build_scheme () build_scheme ()
{ {
local scheme=$1 local scheme=$1
run_xcodebuild "$scheme" 2>&1 | awk -f "$SCRIPT_DIR/xcodebuild.awk" echo "*** Cleaning $scheme..."
run_xctool -scheme "$scheme" clean >/dev/null || exit $?
echo "*** Building and testing $scheme..."
echo
local sdkflag=
local action=test
# Determine whether we can run unit tests for this target.
run_xctool -scheme "$scheme" run-tests | parse_build
local awkstatus=$? local awkstatus=$?
local xcstatus=${PIPESTATUS[0]}
if [ "$xcstatus" -eq "65" ] if [ "$awkstatus" -ne "0" ]
then then
# This probably means that there's no scheme by that name. Give up. # Unit tests aren't supported.
echo "*** Error building scheme $scheme -- perhaps it doesn't exist" action=build
elif [ "$awkstatus" -eq "1" ]
then
return $awkstatus
fi fi
return $xcstatus if [ "$awkstatus" -eq "1" ]
then
# Build for iOS.
sdkflag="-sdk iphonesimulator"
fi
run_xctool $sdkflag -scheme "$scheme" $action
} }
echo "*** Building..." export -f build_scheme
export -f run_xctool
export -f parse_build
for scheme in "$@" main
do
build_scheme "$scheme" || exit $?
done

12
script/schemes.awk Normal file
View File

@@ -0,0 +1,12 @@
BEGIN {
FS = "\n";
}
/Targets:/ {
while (getline && $0 != "") {
if ($0 ~ /Test/) continue;
sub(/^ +/, "");
print "'" $0 "'";
}
}

25
script/xctool.awk Normal file
View File

@@ -0,0 +1,25 @@
# Exit statuses:
#
# 0 - No errors found.
# 1 - Wrong SDK. Retry with SDK `iphonesimulator`.
# 2 - Missing target.
BEGIN {
status = 0;
}
{
print;
}
/Testing with the '(.+)' SDK is not yet supported/ {
status = 1;
}
/does not contain a target named/ {
status = 2;
}
END {
exit status;
}