1#!/bin/bash 2# 3# Script that rsyncs a source/build tree to a remote server, performs a build, 4# and copies the result back 5# 6 7# This script is invoked instead of the initial recursive make(1) in ./Makefile. 8# First it must cache all binaries that might be used during the build by 9# calling "make print_exports" (any target would work) with an overriden xcrun(1) 10# which caches tools an SDKs into ./BUILD/obj/BuildTools. When the combined 11# source+build tree is rsync-ed to the remote server, we run a script to 12# re-initiate the build using an overriden xcrun(1) which hands back 13# cached tools in ./BUILD/obj/BuildTools instead of whatever Xcode tools are on 14# the remote system (or if no Xcode tools are installed remotely). Finally, 15# the build results are copied back locally. 16# 17 18function die() { 19 echo "$1" 1>&2 20 exit 1 21} 22 23 24TARGET= 25declare -a ARGS 26declare -a REMOTEARGS 27index=0 28for arg in "$@"; do 29 case $arg in 30 _REMOTEBUILD_TARGET=*) 31 TARGET=`echo $arg | awk -F= '{print $2}'` 32 continue 33 ;; 34 _REMOTEBUILD_MAKE=*) 35 MAKE=`echo $arg | awk -F= '{print $2}'` 36 continue 37 ;; 38 REMOTEBUILD=*) 39 # Don't restart another remote build remotely 40 ;; 41 SRCROOT=*) 42 continue 43 ;; 44 OBJROOT=*) 45 continue 46 ;; 47 SYMROOT=*) 48 continue 49 ;; 50 DSTROOT=*) 51 continue 52 ;; 53 CCHROOT=*) 54 continue 55 ;; 56 RC_XBS=*) 57 # Remote build isn't chrooted or special in any way 58 arg="VERBOSE=YES" 59 continue 60 ;; 61 VERBOSE=YES) 62 set -x 63 ;; 64 esac 65 ARGS[$index]="$arg" 66 REMOTEARGS[$index]="\"$arg\"" 67 index=$(($index+1)) 68done 69 70 71ARGS[$index]="REMOTEBUILD=" 72REMOTEARGS[$index]="\"REMOTEBUILD=\"" 73 74# For some targets like installsrc, we can't to a remote build 75SKIPREMOTE=0 76case $TARGET in 77 clean) 78 SKIPREMOTE=1 79 ;; 80 installsrc) 81 SKIPREMOTE=1 82 ;; 83 installopensource) 84 SKIPREMOTE=1 85 ;; 86 cscope) 87 SKIPREMOTE=1 88 ;; 89 tags) 90 SKIPREMOTE=1 91 ;; 92 help) 93 SKIPREMOTE=1 94 ;; 95esac 96 97if [ $SKIPREMOTE -eq 1 ]; then 98 exec "$MAKE" "$TARGET" "${ARGS[@]}" 99fi 100 101SRC="$(pwd -P)" 102SRCNAME="$(basename $SRC)" 103 104# Pick up build locations passed in the environment 105OBJROOT="${OBJROOT}" 106SYMROOT="${SYMROOT}" 107DSTROOT="${DSTROOT}" 108 109if [ -z "${OBJROOT}" ]; then 110 die "OBJROOT not set in environment" 111fi 112mkdir -p "${OBJROOT}" || die "Could not create ${OBJROOT}" 113 114if [ -z "${SYMROOT}" ]; then 115 die "SYMROOT not set in environment" 116fi 117mkdir -p "${SYMROOT}" || die "Could not create ${SYMROOT}" 118 119if [ -z "${DSTROOT}" ]; then 120 die "DSTROOT not set in environment" 121fi 122mkdir -p "${DSTROOT}" || die "Could not create ${DSTROOT}" 123 124if [ "$REMOTEBUILD" = "$SPECIALREMOTEBUILD" ]; then 125 : 126else 127 DOINSTALLSRC=0 128 REMOTE_SRCREL="./" 129 BUILDTOOLSDIR="$OBJROOT" 130 REMOTE_BUILDTOOLSREL="./BUILD/obj" 131 BUILDSCRIPTDIR="$OBJROOT" 132 REMOTE_BUILDSCRIPTREL="./BUILD/obj" 133 BUILDSCRIPTNAME="build.sh" 134 if [ ! -d "${OBJROOT}/SETUP" ]; then 135 RSYNC_ARGS="--delete-excluded" 136 else 137 RSYNC_ARGS="" 138 fi 139 if [ ! -e "${SYMROOT}/" ]; then 140 RSYNC_DELETE_SYMROOT=1 141 else 142 RSYNC_DELETE_SYMROOT=0 143 fi 144 if [ ! -e "${DSTROOT}/" ]; then 145 RSYNC_DELETE_DSTROOT=1 146 else 147 RSYNC_DELETE_DSTROOT=0 148 fi 149 TARBUILDDIRS=0 150fi 151 152echo "Caching build tools..." 1>&2 153mkdir -p "${BUILDTOOLSDIR}" || die "Could not create BUILDTOOLSDIR" 154$MAKE print_exports "${ARGS[@]}" XCRUN="${SRC}/tools/xcrun_cache.sh -c \"${BUILDTOOLSDIR}\"" >/dev/null || die "Could not cache build tools" 155 156# Cache the make(1) binary itself 157MAKE_SDKROOT=`"${SRC}/tools/xcrun_cache.sh" -u "${BUILDTOOLSDIR}" -sdk / -show-sdk-path` 158"${SRC}/tools/xcrun_cache.sh" -c "${BUILDTOOLSDIR}" -sdk "${MAKE_SDKROOT}" -find make >/dev/null || die "Could not cache make" 159 160# Create a canned build script that can restart the build on the remote server. 161mkdir -p "${BUILDSCRIPTDIR}" || die "Could not create BUILDSCRIPTDIR" 162cat > "${BUILDSCRIPTDIR}/${BUILDSCRIPTNAME}" <<EOF 163#!/bin/sh 164mkdir -p /private/tmp 165mkdir -p /private/var/tmp 166mkdir -p "\${TMPDIR}" 167cd "${REMOTE_SRCREL}" 168mkdir -p ./BUILD/obj 169mkdir -p ./BUILD/sym 170mkdir -p ./BUILD/dst 171MAKE=\`\$PWD/tools/xcrun_cache.sh -u "\$PWD/${REMOTE_BUILDTOOLSREL}" -sdk / -find make\` 172if [ -z "\${MAKE}" ]; then exit 1; fi 173\${MAKE} ${TARGET} ${REMOTEARGS[@]} XCRUN="\$PWD/tools/xcrun_cache.sh -u \"\$PWD/${REMOTE_BUILDTOOLSREL}\"" 174ret=\$? 175if [ \$ret -eq 0 ]; then 176if [ ${TARBUILDDIRS} -eq 1 ]; then 177tar jcf ./BUILD/obj.tar.bz2 --exclude=\*.o --exclude=\*.cpo --exclude=\*.d --exclude=\*.cpd --exclude=\*.non_lto --exclude=\*.ctf --exclude=conf -C ./BUILD/obj . || exit 1 178tar jcf ./BUILD/sym.tar.bz2 -C ./BUILD/sym . || exit 1 179tar jcf ./BUILD/dst.tar.bz2 -C ./BUILD/dst . || exit 1 180fi 181fi 182exit \$ret 183EOF 184chmod a+x "${BUILDSCRIPTDIR}/${BUILDSCRIPTNAME}" 185#echo "Build script is:" 186#cat "${BUILDSCRIPTDIR}/${BUILDSCRIPTNAME}" 187 188mkdir -p "${BUILDTOOLSDIR}/empty" 189 190if [ "$REMOTEBUILD" = "$SPECIALREMOTEBUILD" ]; then 191 : 192else 193 194 REMOTEBUILD="$REMOTEBUILD" 195 REMOTEBUILDPATH="$REMOTEBUILDPATH" 196 197 if [ -z "$REMOTEBUILDPATH" ]; then 198 WHOAMI=`whoami` 199 case "${REMOTEBUILD}" in 200 *@*) 201 WHOAMI=`echo "${REMOTEBUILD}" | awk -F@ '{print $1}'` 202 ;; 203 esac 204 REMOTEBUILDPATH="/tmp/$WHOAMI" 205 fi 206 207# Construct a unique remote path 208 eval `stat -s "${SRC}"` 209 210 REMOTEBUILDPATH="${REMOTEBUILDPATH}/$st_ino/${SRCNAME}/" 211 echo "Remote path is ${REMOTEBUILD}:${REMOTEBUILDPATH}" 1>&2 212 213 ssh $REMOTEBUILD "mkdir -p \"${REMOTEBUILDPATH}/BUILD/\"{obj,sym,dst}" || die "Could not make remote build directory" 214 215 # Copy source only 216 rsync -azv --delete --exclude=\*~ --exclude=.svn --exclude=.git --exclude=/BUILD . $REMOTEBUILD:"${REMOTEBUILDPATH}" || die "Could not rsync source tree" 217 218 # Copy partial OBJROOT (just build tools and build script), and optionally delete everything else 219 rsync -azv --delete $RSYNC_ARGS --include=/build.sh --include=/BuildTools --include=/BuildTools/\*\* --exclude=\* "${OBJROOT}/" $REMOTEBUILD:"${REMOTEBUILDPATH}/BUILD/obj/" || die "Could not rsync build tree" 220 221 # Delete remote SYMROOT if it has been deleted locally 222 if [ "$RSYNC_DELETE_SYMROOT" -eq 1 ]; then 223 rsync -azv --delete "${BUILDTOOLSDIR}/empty/" $REMOTEBUILD:"${REMOTEBUILDPATH}/BUILD/sym/" || die "Could not rsync delete SYMROOT" 224 fi 225 226 # Delete remote DSTROOT if it has been deleted locally 227 if [ "$RSYNC_DELETE_DSTROOT" -eq 1 ]; then 228 rsync -azv --delete "${BUILDTOOLSDIR}/empty/" $REMOTEBUILD:"${REMOTEBUILDPATH}/BUILD/dst/" || die "Could not rsync delete DSTROOT" 229 fi 230 231 # Start the build 232 echo ssh $REMOTEBUILD "/bin/bash -c 'cd \"${REMOTEBUILDPATH}\" && ${REMOTE_BUILDSCRIPTREL}/${BUILDSCRIPTNAME}'" 1>&2 233 ssh $REMOTEBUILD "/bin/bash -c 'cd \"${REMOTEBUILDPATH}\" && ${REMOTE_BUILDSCRIPTREL}/${BUILDSCRIPTNAME}'" || die "Could not complete remote build" 234 235 # Copy back build results except for object files (which might be several GB) 236 echo "Copying results back..." 237 rsync -azv --no-o --no-g --exclude=\*.o --exclude=\*.cpo --exclude=\*.d --exclude=\*.cpd --exclude=\*.non_lto --exclude=\*.ctf --exclude=conf $REMOTEBUILD:"${REMOTEBUILDPATH}/BUILD/obj/" "${OBJROOT}/" || die "Could not rsync build results" 238 rsync -azv --no-o --no-g $REMOTEBUILD:"${REMOTEBUILDPATH}/BUILD/sym/" "${SYMROOT}/" || die "Could not rsync build results" 239 rsync -azv --no-o --no-g $REMOTEBUILD:"${REMOTEBUILDPATH}/BUILD/dst/" "${DSTROOT}/" || die "Could not rsync build results" 240 241fi 242 243exit 0 244