# # Copyright (c) 2006,2008 Bryan L Blackburn. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # 3. Neither the name Bryan L Blackburn, nor the names of any contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' # AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # #------------------------------------------------------------------------ # Check necessary bits are available. Either svn export tarball, or svn # working directory, or release tarball have to be present. # $1 - data directory # $2 - MacPorts source tarball # $3 - MacPosts svn working copy # $4 - MacPorts release tarball function checkDependencies() { local dataDir=$1 local mpTarball=$2 local mpSvnWorkdir=$3 local installPackage=$4 if [[ `id -u` != 0 ]]; then echo "This must be run as root, please do so" return 1 fi if [[ ! -f ${dataDir}/${mpTarball} && ! -d ${dataDir}/${mpSvnWorkdir} && ! -f ${dataDir}/${installPackage} ]]; then cat << EOF Need to have a MacPorts source tarball ${mpTarball} or svn working directory ${mpSvnWorkDir} See the ReadMe.txt file. EOF return 2 fi } #------------------------------------------------------------------------ # Print out usage help and exit function printUsageAndExit() { cat << EOF Usage: $0 [help | mount | umount | shell | buildmp [RELEASE_FILE] | rebuildmp [RELEASE_FILE] | buildports [portlist_file]] help: this help mount: just mount the chroot images umount: unmount the chroot images shell: start an interactive shell in the chroot buildmp: build/install MacPorts inside the chroot rebuildmp: force a rebuild of MacPorts inside the chroot buildports: build ports; if a portlist file is given, use it, otherwise build all; portlist_file is a simple text file with one port per line; dependencies will be built as needed default is to do everything (except rebuildmp); buildmp and buildports will unmount the chroot when done EOF exit 0 } #------------------------------------------------------------------------ # Do whatever cleanup is necessary # $1 - base directory # $2 - chroot path function exitFunction() { local baseDir=$1 local chrootPath=$2 if [[ -n $exitMessage ]]; then echo $exitMessage fi if [[ -d ${chrootPath}/var/tmp/portresults/fail ]]; then moveAndReport ${baseDir} "${chrootPath}" fi if [[ -n "$chrootPath" && -d ${chrootPath} ]]; then umountChroot ${chrootPath} fi } #------------------------------------------------------------------------ # Build the base chroot and distfile cache disk images # $1 - base directory # $2 - chroot path # $3 - base name for the disk images function buildImages() { local baseDir=$1 local chrootPath=$2 local imgBaseName=$3 if [[ -z "$chrootPath" ]]; then return 0 fi echo "Building MP chroot images, if necessary" # Paths to create within the chroot pathsToCreate="private/etc private/var/folders private/var/log private/var/spool private/var/run private/var/tmp private/var/db/dyld private/tmp" os_major=$(uname -r | sed 's/\..*//') # Paths to copy to the chroot if [[ $os_major -eq 8 ]]; then # XXX these are just copied from the leopard list and so are probably wrong for tiger pathsToCopy="private/etc/bashrc private/etc/group private/etc/hosts private/etc/pam.d private/etc/passwd private/etc/profile private/etc/shells private/var/root bin sbin etc tmp var usr/bin usr/include usr/lib usr/libexec usr/sbin usr/share usr/X11 usr/X11R6 Developer/Headers Developer/Library Developer/Makefiles Developer/Platforms Developer/Private Developer/SDKs Developer/Tools Developer/usr System/Library/CoreServices System/Library/Filesystems System/Library/Frameworks System/Library/Java System/Library/Perl System/Library/PrivateFrameworks System/Library/Tcl Developer/Applications/Xcode.app" fi if [[ $os_major -eq 9 ]]; then pathsToCopy="private/etc/bashrc private/etc/group private/etc/hosts private/etc/pam.d private/etc/passwd private/etc/profile private/etc/shells private/var/root bin sbin etc tmp var usr/bin usr/include usr/lib usr/libexec usr/sbin usr/share usr/X11 usr/X11R6 Developer/Headers Developer/Library Developer/Makefiles Developer/Platforms Developer/Private Developer/SDKs Developer/Tools Developer/usr System/Library/CoreServices System/Library/Filesystems System/Library/Frameworks System/Library/Java System/Library/Perl System/Library/PrivateFrameworks System/Library/Tcl Developer/Applications/Xcode.app" fi # update this for future releases if [[ $os_major -ge 10 ]]; then pathsToCopy="private/etc/bashrc private/etc/group private/etc/hosts private/etc/pam.d private/etc/passwd private/etc/profile private/etc/shells private/var/root bin sbin etc tmp var usr/bin usr/include usr/lib usr/libexec usr/sbin usr/share usr/X11 usr/X11R6 Developer/Headers Developer/Library Developer/Makefiles Developer/Platforms Developer/Private Developer/SDKs Developer/Tools Developer/usr System/Library/CoreServices System/Library/Filesystems System/Library/Frameworks System/Library/Java System/Library/Perl System/Library/PrivateFrameworks System/Library/Tcl Developer/Applications/Xcode.app" fi # Size of the disk images (hdiutil nomenclature) imageSize="32g" # Filesystem of the disk images (hdiutil nomenclature) imageFileSystem="HFS+J" mkdir -p ${chrootPath} if [[ ! -f ${baseDir}/${imgBaseName}.dmg ]]; then hdiutil create -size ${imageSize} -fs ${imageFileSystem} -volname MPRoot -imagekey sparse-band-size=16384 ${baseDir}/${imgBaseName}.sparseimage ${HDIUTILDEBUG} returnValue=$? if [[ ${returnValue} != 0 ]]; then echo "hdiutil create (MPRoot) failed" return ${returnValue} fi hdiutil attach ${baseDir}/${imgBaseName}.sparseimage -mountpoint ${chrootPath} -owners on -nobrowse -noautofsck ${HDIUTILDEBUG} returnValue=$? if [[ ${returnValue} != 0 ]]; then echo "Failed to attach" return ${returnValue} fi mkdir ${chrootPath}/dev mount_devfs devfs ${chrootPath}/dev mount_fdesc -o union fdesc ${chrootPath}/dev if [[ ! -d ${chrootPath}/usr ]]; then cd / for createpath in ${pathsToCreate}; do mkdir -p ${chrootPath}/${createpath} done for copypath in ${pathsToCopy}; do echo "Copying ${copypath}..." ### pax -r -w -pe $copypath ${chrootPath} done cd - > /dev/null fi umount -f ${chrootPath}/dev umount -f ${chrootPath}/dev hdiutil detach ${chrootPath} ${HDIUTILDEBUG} returnValue=$? if [[ ${returnValue} != 0 ]]; then echo "hdiutil detach failed" return ${returnValue} fi hdiutil convert ${baseDir}/${imgBaseName}.sparseimage -format UDRO -o ${baseDir}/${imgBaseName}.dmg ${HDIUTILDEBUG} returnValue=$? if [[ ${returnValue} != 0 ]]; then echo "hdiutil convert failed" return ${returnValue} fi rm ${baseDir}/${imgBaseName}.sparseimage fi if [[ ! -f ${baseDir}/${imgBaseName}_distcache.sparseimage ]]; then hdiutil create -size ${imageSize} -fs ${imageFileSystem} -volname MPCache -imagekey sparse-band-size=16384 ${baseDir}/${imgBaseName}_distcache.sparseimage ${HDIUTILDEBUG} returnValue=$? if [[ ${returnValue} != 0 ]]; then echo "hdiutil create (MPCache) failed" return ${returnValue} fi fi return 0 } #------------------------------------------------------------------------ # Build MacPorts in the chroot (if not already there) # $1 - base directory # $2 - dataDir # $3 - chroot path # $4 - MacPorts source tarball or svn checkout directory function buildMacPorts() { local baseDir=$1 local dataDir=$2 local chrootPath=$3 local mpExport=$4 needIndex="" if [[ ! -d ${chrootPath}${SRC_PREFIX} ]]; then if [[ -f ${dataDir}/${mpExport} ]]; then mkdir -p ${chrootPath}${SRC_PREFIX} echo ${mpExport} | grep -q "MacPorts-......tar.gz" if [[ $? == 0 ]]; then cp ${mpExport} ${chrootPath}${SRC_PREFIX} cd ${chrootPath}${SRC_PREFIX} tar xzf ${mpExport} || return 1 rm -rf base 2>/dev/null mv $(ls -d MacPorts-?.?.?) base || return 1 else cd ${chrootPath}${SRC_PREFIX} bunzip2 -c ${dataDir}/${mpExport} | tar xf - fi cd - > /dev/null elif [[ -d ${dataDir}/${mpExport} ]]; then mkdir -p ${chrootPath}${SRC_PREFIX} cd ${dataDir}/${mpExport} && \ rsync -r --del --exclude '*~' --exclude '.svn' . ${chrootPath}${SRC_PREFIX} cd - > /dev/null else echo "No ${mpExport} found" return 1 fi chmod -R a+rX ${chrootPath}${SRC_PREFIX} needIndex=1 fi if [[ ! -f ${chrootPath}${PREFIX}/bin/port ]]; then chrootExec installmacports needIndex=1 fi if [[ -n "$needIndex" ]]; then chrootExec recreateportindex fi return 0 } #------------------------------------------------------------------------ # Build MP ports, either those listed in a file given or by generating # a list of all ports and using that # $1 - base directory # $2 - chroot path function buildPorts() { local baseDir=$1 local chrootPath=$2 if [[ $baseDir == "." ]]; then baseDir=`pwd` fi echo "Building ports" cp -p ${baseDir}/chroot-scripts/genportlist.tcl ${chrootPath}/var/tmp/ cp -p ${baseDir}/chroot-scripts/archivepath.tcl ${chrootPath}/var/tmp/ cp -p ${baseDir}/chroot-scripts/dep_ports.tcl ${chrootPath}/var/tmp/ rm -f ${chrootPath}/var/tmp/progress.log ${baseDir}/progress.log touch ${baseDir}/progress.log if ! ln ${baseDir}/progress.log ${chrootPath}/var/tmp/progress.log 2>/dev/null ; then ln -s ${baseDir}/progress.log ${chrootPath}/var/tmp/progress.log fi mkdir -p ${baseDir}/failcache rsync -a ${baseDir}/failcache ${chrootPath}/var/tmp chrootExec buildports rsync -a --delete ${chrootPath}/var/tmp/failcache ${baseDir} return 0 } #------------------------------------------------------------------------ # Start a shell inside of the chroot jail. function chrootShell() { echo "Starting chroot shell" cp -p ${baseDir}/chroot-scripts/chrootshell ${chrootPath}/var/tmp/ chrootExec chrootshell echo "Chroot shell exited" return 0 } #------------------------------------------------------------------------ # Move logs and report results # $1 - base directory # $2 - chroot path function moveAndReport() { local baseDir=$1 local chrootPath=$2 now=`date '+%Y%m%d-%H%M%S'` mkdir ${baseDir}/logs-${now} mv ${chrootPath}/var/tmp/portresults/fail ${baseDir}/logs-${now} mv ${chrootPath}/var/tmp/portresults/success ${baseDir}/logs-${now} chmod -R a+rX ${baseDir}/logs-${now} failcount=`ls -1 ${baseDir}/logs-${now}/fail | wc -l` successcount=`ls -1 ${baseDir}/logs-${now}/success | wc -l` echo "Ports built successfully: $successcount" echo "Ports failed: $failcount" echo "All logs are located in ${baseDir}/logs-${now}" return 0 } #------------------------------------------------------------------------ # Mount the full chroot stuff # $1 - base directory # $2 - chroot path # $3 - base name for the disk images function mountChroot() { local baseDir=$1 local chrootPath=$2 local imgBaseName=$3 if [[ -z "$chrootPath" ]]; then return 0 fi echo "Mounting chroot images" mkdir -p ${chrootPath} if [[ ! -d ${chrootPath}/usr ]]; then if [[ ! -f ${baseDir}/${imgBaseName}.dmg ]]; then echo "MPRoot image ${baseDir}/${imgBaseName}.dmg not found" return 1 fi hdiutil attach ${baseDir}/${imgBaseName}.dmg -mountpoint ${chrootPath} -shadow -noverify -owners on -nobrowse -noautofsck ${HDIUTILDEBUG} returnValue=$? if [[ ${returnValue} != 0 ]]; then echo "Failed to attach root image" return ${returnValue} fi mount_devfs devfs ${chrootPath}/dev mount_fdesc -o union fdesc ${chrootPath}/dev mkdir -p ${chrootPath}${PREFIX}/var/macports/distfiles hdiutil attach ${baseDir}/${imgBaseName}_distcache.sparseimage -mountpoint ${chrootPath}${PREFIX}/var/macports/distfiles -noverify -owners on -nobrowse -noautofsck ${HDIUTILDEBUG} returnValue=$? if [[ ${returnValue} != 0 ]]; then echo "Failed to attach distfiles image" return ${returnValue} fi fi return 0 } #------------------------------------------------------------------------ # Unmount everything for the MP chroot # $1 - chroot path function umountChroot() { local chrootPath=$1 if [[ -z "$chrootPath" ]]; then return 0 fi if [[ -d ${chrootPath}/dev ]]; then # First for the fdesc umount -f ${chrootPath}/dev # And again for the devfs umount -f ${chrootPath}/dev fi if [[ -d ${chrootPath}${PREFIX}/var/macports/distfiles ]]; then # Now the cache image for the dist files hdiutil detach ${chrootPath}${PREFIX}/var/macports/distfiles ${HDIUTILDEBUG} fi if [[ -d ${chrootPath} ]]; then # Finally, the main image itself hdiutil detach ${chrootPath} ${HDIUTILDEBUG} rmdir ${chrootPath} fi return 0 } #------------------------------------------------------------------------ # Execute a script inside the chroot environment. Includes copying it # into a temp file and removing it afterwards # $1 - script to execute function chrootExec () { cp -p ${baseDir}/chroot-scripts/$1 ${chrootPath}/var/tmp if [[ -n "$chrootPath" ]]; then # Set DYLD_NO_FIX_PREBINDING as otherwise, on 10.5, dyld will spew # errors to syslog/console log like: # com.apple.launchd[1] (com.apple.dyld): Throttling respawn: Will start in 10 seconds env -i PATH=/bin:/usr/bin:/sbin:/usr/sbin HOME=/var/root DYLD_NO_FIX_PREBINDING=1 PREFIX=${PREFIX} SRC_PREFIX=${SRC_PREFIX} /usr/sbin/chroot ${chrootPath} /bin/sh /var/tmp/$1 else env -i PATH=/bin:/usr/bin:/sbin:/usr/sbin HOME=/var/root PREFIX=${PREFIX} SRC_PREFIX=${SRC_PREFIX} /bin/sh /var/tmp/$1 fi rm ${chrootPath}/var/tmp/$1 } # Local Variables: # mode: shell-script # indent-tabs-mode: nil # sh-basic-offset: 3 # End: