xcrun_cache.sh   [plain text]


#!/bin/sh

#
# This shell-script is argument-compatible with xcrun(1) as invoked
# by xnu's Makefiles. Additionally, it supports caching tools
# in the local build directory. It is tightly coupled to exactly
# the queries that MakeInc.cmd makes, in exactly the order it makes
# them. ./tools/remote_build.sh invokes this indirectly in caching
# mode, so '$(XCRUN) -sdk foo -find bar' copies 'bar' from wherever
# it is on-disk into ./BUILD/BuildData, and returns that path to the
# caller. In '-u' mode on a remote build server, cache tools
# relative to the current build directory are returned without
# actually calling through to xcrun(1), since the remote build
# server may not even have Xcode installed.
#

SDKROOT=""
FINDTOOL=""
SDKQUERY=""
VERBOSE=""
OBJROOT=""
CACHE=0

# echo "Calling $0 $@" 1>&2

while [ $# -gt 0 ]; do
    case "$1" in
	-c)
	    CACHE=1
	    shift
	    OBJROOT="$1"
	    shift
	    ;;
	-u)
	    CACHE=0
	    shift
	    OBJROOT="$1"
	    shift
	    ;;
	-sdk)
	    shift
	    SDKROOT="$1"
	    shift
	    ;;
	-verbose)
	    VERBOSE="$1"
	    shift
	    set -x
	    ;;
	-find)
	    shift
	    FINDTOOL="$1"
	    shift
	    ;;
	-show-sdk-path)
	    SDKQUERY="$1"
	    shift
	    ;;
	-show-sdk-platform-path)
	    SDKQUERY="$1"
	    shift
	    ;;
	-show-sdk-version)
	    SDKQUERY="$1"
	    shift
	    ;;
	*)
	    echo "Unrecognized argument $1" 1>&2
	    exit 1
    esac    
done

function CreateFile() {
    local string="$1"
    local filepath="$2"
    echo "${string}" > "${filepath}.new"
    cmp -s "${filepath}" "${filepath}.new"
    if [ $? -eq 0 ]; then
	rm "${filepath}.new"
    else
	mv "${filepath}.new" "${filepath}"
    fi
}

if [ $CACHE -eq 1 ]; then

    if [ -n "$SDKQUERY" ]; then
	# MakeInc.cmd makes SDK queries up-front first. Generally the
	# SDKROOT that is an input to these are one of:
	# "macosx" => Host SDK
	# "iphonehostXXX" => iPhone Host SDK
	# other shortcut or full path => Target SDK
	#
	# Once an initial lookup is made, subsequent SDKROOTs for
	# that same SDK may use a full path or cached path
	SDKTYPE=""
	case "$SDKROOT" in
	    macosx)
		SDKTYPE="host"
		;;
	    iphonehost*)
		SDKTYPE="iphonehost"
		;;
	    *)
		if [ -f "$SDKROOT/.sdktype" ]; then
		    SDKTYPE=`cat "$SDKROOT/.sdktype"`
		else
		    SDKTYPE="target"
		fi
		;;
	esac

	# A cached SDK path can be passed to xcrun, so
	# we need the original on-disk path
	if [ -f "$SDKROOT/.realsdkpath" ]; then
	    REALSDKROOT=`cat "$SDKROOT/.realsdkpath"`
	else
	    REALSDKROOT="$SDKROOT"
	fi

	SDKPROPERTY=`/usr/bin/xcrun $VERBOSE -sdk "$REALSDKROOT" "$SDKQUERY"`
	if [ $? -ne 0 ]; then
	    exit $?
	fi
	
	case $SDKQUERY in
	    -show-sdk-path)
		# Cache the SDK locally, and transform the resulting SDKPROPERTY
		if [ -z "$SDKPROPERTY" ]; then
		    SDKPROPERTY="/"
		    SDKNAME="Slash.sdk"
		else
		    SDKNAME=$(basename "${SDKPROPERTY}")
		fi
		mkdir -p "${OBJROOT}/BuildTools/${SDKNAME}"
		mkdir -p "${OBJROOT}/BuildTools/${SDKNAME}/usr/include"
		rsync -aq --exclude=c++ --exclude=php --exclude=soc "${SDKPROPERTY}/usr/include/" "${OBJROOT}/BuildTools/${SDKNAME}/usr/include/"
		if [ "$SDKTYPE" = "iphonehost" ]; then
		    mkdir -p "${OBJROOT}/BuildTools/${SDKNAME}/usr/local/lib/system"
		    rsync -aq "${SDKPROPERTY}/usr/local/lib/system/" "${OBJROOT}/BuildTools/${SDKNAME}/usr/local/lib/system/"
		else
		    mkdir -p "${OBJROOT}/BuildTools/${SDKNAME}/usr/lib"
		    rsync -aq "${SDKPROPERTY}/usr/lib/libSystem"* "${OBJROOT}/BuildTools/${SDKNAME}/usr/lib/"
		    rsync -aq "${SDKPROPERTY}/usr/lib/libc++"* "${OBJROOT}/BuildTools/${SDKNAME}/usr/lib/"
		    rsync -aq "${SDKPROPERTY}/usr/lib/libstdc++"* "${OBJROOT}/BuildTools/${SDKNAME}/usr/lib/"
		    mkdir -p "${OBJROOT}/BuildTools/${SDKNAME}/usr/lib/system"
		    rsync -aq --exclude=\*_debug.dylib --exclude=\*_profile.dylib "${SDKPROPERTY}/usr/lib/system/" "${OBJROOT}/BuildTools/${SDKNAME}/usr/lib/system/"
		fi
		if [ -f "${SDKPROPERTY}/usr/local/libexec/availability.pl" ]; then
		    mkdir -p "${OBJROOT}/BuildTools/${SDKNAME}/usr/local/libexec"
		    rsync -aq "${SDKPROPERTY}/usr/local/libexec/availability.pl" "${OBJROOT}/BuildTools/${SDKNAME}/usr/local/libexec/"
		fi
		CreateFile "${SDKPROPERTY}" "${OBJROOT}/BuildTools/${SDKNAME}/.realsdkpath"
		CreateFile "${SDKTYPE}" "${OBJROOT}/BuildTools/${SDKNAME}/.sdktype"
		CreateFile "BuildTools/${SDKNAME}" "${OBJROOT}/BuildTools/.${SDKTYPE}sdk"
		echo "${OBJROOT}/BuildTools/${SDKNAME}"
		exit 0
		;;
	    -show-sdk-platform-path)
		PLATFORMNAME=$(basename "${SDKPROPERTY}")
		mkdir -p "${OBJROOT}/BuildTools/${PLATFORMNAME}"
		if [ -f "${SDKPROPERTY}/usr/local/standalone/firmware/device_map.db" ]; then
		    mkdir -p "${OBJROOT}/BuildTools/${PLATFORMNAME}/usr/local/standalone/firmware"
		    rsync -aq "${SDKPROPERTY}/usr/local/standalone/firmware/device_map.db" \
			"${OBJROOT}/BuildTools/${PLATFORMNAME}/usr/local/standalone/firmware/"
		fi
		CreateFile "BuildTools/${PLATFORMNAME}" "${OBJROOT}/BuildTools/.targetplatform"
		echo "${OBJROOT}/BuildTools/${PLATFORMNAME}"
		exit 0
		;;
	    -show-sdk-version)
		CreateFile "${SDKPROPERTY}" "${OBJROOT}/BuildTools/.targetsdkversion"
		echo "${SDKPROPERTY}"
		exit 0
		;;
	esac

    elif [ -n "$FINDTOOL" ]; then

	# We assume SDK Queries have been performed first and subsequent
	# SDKROOTs used to find tools are all using cached SDKs in
	# the build directory, in which case metadata is present

	if [ ! -f "$SDKROOT/.realsdkpath" ]; then
	    exit 1
	fi
	REALSDKROOT=`cat "$SDKROOT/.realsdkpath"`

	if [ ! -f "$SDKROOT/.sdktype" ]; then
	    exit 1
	fi
	SDKTYPE=`cat "$SDKROOT/.sdktype"`

	TOOLPATH=`/usr/bin/xcrun $VERBOSE -sdk "$REALSDKROOT" -find "$FINDTOOL"`
	if [ $? -ne 0 ]; then
	    exit $?
	fi

	# Keep the parent directory when caching tools, along with Host vs. Target
	TOOLNAME=$(basename "${TOOLPATH}")
	TOOLDIR=$(basename $(dirname "${TOOLPATH}"))
	if [ "$SDKTYPE" = "host" ]; then
	    NEWTOOLPATH="${OBJROOT}/BuildTools/Host/${TOOLDIR}/${TOOLNAME}"
	    mkdir -p "${OBJROOT}/BuildTools/Host"
	    CreateFile "BuildTools/Host/${TOOLDIR}/${TOOLNAME}" "${OBJROOT}/BuildTools/Host/.${TOOLNAME}"
	else
	    NEWTOOLPATH="${OBJROOT}/BuildTools/Target/${TOOLDIR}/${TOOLNAME}"
	    mkdir -p "${OBJROOT}/BuildTools/Target"
	    CreateFile "BuildTools/Target/${TOOLDIR}/${TOOLNAME}" "${OBJROOT}/BuildTools/Target/.${TOOLNAME}"
	fi
	mkdir -p $(dirname "${NEWTOOLPATH}")
	rsync -aq "${TOOLPATH}" "${NEWTOOLPATH}"
	case "${TOOLNAME}" in
	    clang)
		mkdir -p $(dirname $(dirname "${NEWTOOLPATH}"))/lib/clang
		rsync -aq $(dirname "${TOOLPATH}")/ld $(dirname "${NEWTOOLPATH}")/ld
		rsync -aq $(dirname $(dirname "${TOOLPATH}"))/lib/clang/ $(dirname $(dirname "${NEWTOOLPATH}"))/lib/clang/
		rsync -aq $(dirname $(dirname "${TOOLPATH}"))/lib/libLTO.dylib $(dirname $(dirname "${NEWTOOLPATH}"))/lib/libLTO.dylib
		;;
	    bison)
		mkdir -p $(dirname $(dirname "${NEWTOOLPATH}"))/share/bison
		rsync -aq $(dirname $(dirname "${TOOLPATH}"))/share/bison/ $(dirname $(dirname "${NEWTOOLPATH}"))/share/bison/
		;;
	esac

	echo "${NEWTOOLPATH}"
	exit 0
    else
	echo "Unrecognized option" 1>&2
	exit 1
    fi
fi

# When using cached SDK information, first try to do
# an initial classification, and then read properties from
# cached locations
SDKTYPE=""
case "$SDKROOT" in
    macosx)
	SDKTYPE="host"
	;;
    iphonehost*)
	SDKTYPE="iphonehost"
	;;
    *)
	if [ -f "$SDKROOT/.sdktype" ]; then
	    SDKTYPE=`cat "$SDKROOT/.sdktype"`
	else
	    SDKTYPE="target"
	fi
	;;
esac

if [ -n "$FINDTOOL" ]; then
    TOOLNAME=$(basename "${FINDTOOL}")
    if [ "${SDKTYPE}" = "host" ]; then
	RELPATH=`cat ${OBJROOT}/BuildTools/Host/.${TOOLNAME}`
    else
	RELPATH=`cat ${OBJROOT}/BuildTools/Target/.${TOOLNAME}`
    fi
    echo "${OBJROOT}/${RELPATH}"
else	
    case $SDKQUERY in
	-show-sdk-path)
	    RELPATH=`cat ${OBJROOT}/BuildTools/.${SDKTYPE}sdk`
	    echo "${OBJROOT}/${RELPATH}"
	    ;;
	-show-sdk-platform-path)
	    RELPATH=`cat ${OBJROOT}/BuildTools/.targetplatform`
	    echo "${OBJROOT}/${RELPATH}"
	    ;;
	-show-sdk-version)
	    echo `cat ${OBJROOT}/BuildTools/.targetsdkversion`
	    ;;
    esac
fi