#! /bin/bash # Definitions and functions for ELLCC scripts. # The variable SCRIPTS should be set to the name of the directory # containing this script when this file is sourced. # See build-llvm in this directory for an example. # Set ELLCC to the top of the ellcc tree. ELLCC=$(dirname ${SCRIPTS}) LIBECC=${ELLCC}/libecc TOOLINFO=${LIBECC}/toolinfo SCRIPTS=${ELLCC}/scripts PATCHES=${LIBECC}/patches # Find out where we are. if [ "${TOP}" = "" ] ; then TOP=$(pwd) fi if [ ${TOP} = ${ELLCC} ] ; then # In the ELLCC top level directory. Make a build directory. TOP=${TOP}/build fi # Set up common directories. # The source directory tree. SRCTOP=${TOP}/src # The distribution directory tree. DISTTOP=${TOP}/dist # The project source directory. PROJECTDIR=${SRCTOP}/${PROJECT} # Check script specific targets. if [ "$(type -t get_targets )" = "function" ] ; then TARGETS=$(get_targets) else TARGETS=$(cat ${TOOLINFO}/targets) TARGETLIST=$(echo ${TARGETS} | sed -e "s/ /,/g") fi last_revision() { if [ "$1" != "" ] ; then p=$1 else p=${PROJECT} fi if [ "${p}" = "dist" ] ; then p="ellcc" fi if [ -e ${PATCHES}/${p}.revision ] ; then cat ${PATCHES}/${p}.revision else echo "" fi } get_revision() { if [ "$1" = "svn" ] ; then echo $(svn info ${PROJECTDIR} | awk '/Revision/ { print $2 }') elif [ "$1" = "git" ] ; then cd ${PROJECTDIR} echo $(git rev-parse HEAD) elif [ "$1" = "tar" ] ; then # Tarball revisions are updated manually. last_revision else bailout "Unknown get revision method for ${PROJECTDIR}: '$1'" fi } save_revision() { if [ "$2" != "" ] ; then # Use the previous date. date=$2 else date=$(date --rfc-3339=date) fi echo $(get_revision $1) $1 ${date} > ${PATCHES}/${PROJECT}.revision } # The last project revision (revision type date). REVISION=($(last_revision)) # The local installation directory. LOCAL=${TOP}/local # The host processor type. HOSTPROCESSOR=$(uname -m) case "${build}" in i[3-9]86) HOSTPROCESSOR=i386 ;; esac # The host OS. HOSTOS=$(uname) # The target OS. TARGETOS=linux # OG: Debug feature to find out where the errors come from... bailout () { echo $@ echo "Error: leaving module $(pwd)" exit 1 } # The make program. Prefer a gmake compatible make. MAKE=$(which gmake 2> /dev/null) || \ MAKE=$(which make 2> /dev/null) || \ bailout "Can't find a make program." # Build tools with shared libraries? SHARED=no # Print out an option for help. option() { if ${LONG} ; then printf " -%s, --%-30s %s\n" $1 "$2" "$3" else printf " -%s %s\n" $1 "$3" fi } # Options followed by a ':' require an argument, by '::' allow an optional argument. SHORT_OPTIONS=hEecr:U:CPpuRbsj:tinv${SHORT_OPTIONS} # Long options are not available with BSD getopt. LONG_OPTIONS=help,noecc,environment,checkout,revision:,username:,configure,create-patch,patch,revert,update,build,shared,jobs:,tests,install,dry-run,verbose${LONG_OPTIONS} common_usage() { echo "Usage: $0 " echo echo "Options:" usage option h "help" "display this help and exit" option E "noecc" "don't use the latest ecc to bootstrap" option e "environment" "display display the build environment" option c "checkout" "check out needed packages" option r "revision" "check out a specific revision" option U "username " "check out as a specific user" option C "configure" "configure for building packages" option P "create-patch" "create a patch file for checked out packages" option p "patch" "patch checked out packages" option u "update" "update checked out packages" option R "revert" "revert checked out packages to the original version" option b "build" "build packages" option s "shared" "build tools using shared libraries" option j "jobs " "jobs to use while buildins" option t "tests" "run regression tests, if available" option i "install" "install packages" option n "dry-run" "print the commands that would be executed" option v "verbose" "print ommands as they are executed" echo echo "Active projects: ${PROJECTS[@]}" echo "Available projects: ${AVAILABLE_PROJECTS[@]}" } getopt --test > /dev/null if [ $? -ne 4 ]; then # Use the BSD style getopt. LONG=false PARSED=$(getopt $SHORT_OPTIONS $*) else LONG=true PARSED=$(getopt --options $SHORT_OPTIONS --longoptions $LONG_OPTIONS --name "$0" -- "$@") fi if [ $? -ne 0 ]; then common_usage bailout "Error occured while parsing arguments." fi # Use eval with "$PARSED" to properly handle the quoting. eval set -- "$PARSED" # Handle arguments until -- while true; do case "$1" in -h|--help) common_usage exit ;; -E|--noecc) NOECC=y shift ;; -e|--environment) ENVIRONMENT=y shift ;; -c|--checkout) CHECKOUT=y shift ;; -r|--revision) REVISION=$2 if [ "$2" = "latest" ] ; then REVISION="" fi shift 2 ;; -U|--username) USERNAME=$2 shift 2 ;; -C|--configure) CONFIGURE=y shift ;; -P|--create-patch) CREATE_PATCH=y shift ;; -p|--patch) PATCH=y shift ;; -u|--update) UPDATE=y shift ;; -R|--revert) REVERT=y shift ;; -b|--build) BUILD=y shift ;; -s|--shared) SHARED=yes shift ;; -j|--jobs) MAXJOBS=$2 shift 2 ;; -t|--tests) TESTS=y shift ;; -i|--install) INSTALL=y shift ;; -n|--dry-run) DRY_RUN=y VERBOSE=y shift ;; -v|--verbose) VERBOSE=y shift ;; --) shift break ;; *) bailout "Missing option handler: $1" ;; esac done case "${HOSTOS}" in Darwin) # Building under Max OS X. HOSTOS=darwin ;; FreeBSD) HOSTOS=freebsd ;; Linux) HOSTOS=linux ;; esac CROSS=no if [ ! -e "${ELLCC}/bin/ecc" ] ; then # ecc is not present, use host tools to build it. case "${HOSTOS}" in darwin) CROSS=yes NOECC=y ;; freebsd) CROSS=yes NOECC=y ;; esac fi HOSTTUPLE=${HOSTPROCESSOR}-${HOSTOS} # What tool set are we using? lookfor() { CC=$(which $1$2 2> /dev/null) if [ $? -ne 0 ] ; then return 1 fi CXX=$(which $1$3 2> /dev/null) if [ $? -ne 0 ] ; then return 1 fi AR=$(which $1$4 2> /dev/null) if [ $? -ne 0 ] ; then return 1 fi RANLIB=$(which $1$5 2> /dev/null) if [ $? -ne 0 ] ; then return 1 fi LD=$(which $1$6 2> /dev/null) if [ $? -ne 0 ] ; then return 1 fi TOOLS=$7 return 0 } # Look for tools in the prefered order. [ "${NOECC}" != "y" ] && lookfor ${LOCAL}/bin/ ecc ecc++ ecc-ar ecc-ranlib ecc-ld ecc || \ [ "${NOECC}" != "y" ] && lookfor ${ELLCC}/bin/ ecc ecc++ ecc-ar ecc-ranlib ecc-ld ecc || \ lookfor ${LOCAL}/bin/ clang clang++ llvm-ar llvm-ranlib ecc-ld clang || \ lookfor ${ELLCC}/bin/ clang clang++ llvm-ar llvm-ranlib ecc-ld clang || \ lookfor "" ecc ecc++ ecc-ar ecc-ranlib ecc-ld ecc || \ lookfor "" clang clang++ llvm-ar llvm-ranlib ld clang || \ lookfor "" cc c++ ar ranlib ld host || \ lookfor "" gcc g++ ar ranlib ld host || \ bailout "Can't find a tool chain." # Define the curl to use. if [ -e ${LOCAL}/bin/curl ] ; then CURL=${LOCAL}/bin/curl elif [ -e ${ELLCC}/bin/curl ] ; then CURL=${ELLCC}/bin/curl else # Use the system curl. CURL=curl fi lookforhost() { HOST_CC=$(which $1$2 2> /dev/null) if [ $? -ne 0 ] ; then return 1 fi HOST_CXX=$(which $1$3 2> /dev/null) if [ $? -ne 0 ] ; then return 1 fi HOST_AR=$(which $1$4 2> /dev/null) if [ $? -ne 0 ] ; then return 1 fi HOST_RANLIB=$(which $1$5 2> /dev/null) if [ $? -ne 0 ] ; then return 1 fi HOST_LD=$(which $1$6 2> /dev/null) if [ $? -ne 0 ] ; then return 1 fi HOST_TOOLS=$7 return 0 } lookforhost "" ecc ecc++ ecc-ar ecc-ranlib ecc-ld ecc || \ lookforhost "" clang clang++ llvm-ar llvm-ranlib ld clang || \ lookforhost "" cc c++ ar ranlib ld host || \ lookforhost "" gcc g++ ar ranlib ld host || \ bailout "Can't find a host tool chain." TARGETTUPLE= if [ $# -ne 0 ] ; then TARGET=$1 TARGETTUPLE=$1 if [ "$(type -t check_target )" = "function" ] ; then TARGETTUPLE=$(check_target $1) fi shift fi if [ "${CROSS}" != "yes" -a "${TARGETTUPLE}" = "" ] ; then TARGETTUPLE=${HOSTTUPLE} fi if [ "${HOSTTUPLE}" = "${TARGETTUPLE}" ] ; then HOST_CC=${CC} fi if [ -z "${MAXJOBS}" ] ; then MAXJOBS=2 case "${HOSTOS}" in darwin) # Building under Max OS X. MAXJOBS=`sysctl hw.ncpu | cut -d: -f2 | tr -d [:space:]` ;; linux) MAXJOBS=`grep processor /proc/cpuinfo | wc -l` ;; esac fi if [ "${TOOLS}" = "ecc" ] ; then if [ "${CROSS}" != "yes" ] ; then # The project build directory. BUILDDIR=${TOP}/build-${TOOLS}/${PROJECT}/${TARGETTUPLE} CC="${CC} -target ${TARGETTUPLE}" CXX="${CXX} -target ${TARGETTUPLE}" CPP="${CC} -E" else BUILDDIR=${TOP}/build-host/${PROJECT}/${TARGETTUPLE} fi else # The project build directory. BUILDDIR=${TOP}/build-${TOOLS}/${PROJECT} CPP="${CC} -E" fi # Install libraries in place. RICH: Get rid of the local versions? LOCALLIBECC=${LIBECC} LOCALTOOLINFO=${LOCALLIBECC}/toolinfo LOCALLIB=${LOCALLIBECC}/lib/${TARGETTUPLE} if [ "${CROSS}" = "yes" -o "${TARGETTUPLE}" = "${HOSTTUPLE}" ] ; then # Building to run on this machine. Install ecc binaries in place LOCALBIN=${ELLCC}/bin else LOCALBIN=${LOCAL}/bin-${TARGETTUPLE} fi # Check for valid tool info. get_toolinfo() { if [ -e $1 ] ; then echo $1 elif [ -e ${TOOLINFO}/$1 ] ; then echo ${TOOLINFO}/$1 else echo -n "" fi } if [ "${CROSS}" != "yes" -a "${TOOLS}" = "ecc" -a "$(get_toolinfo ${TARGETTUPLE})" = "" ] ; then bailout "'${TARGETTUPLE}' is not a valid target." fi # Return the base arch for a tuple. # This is used for include paths, etc. where endian-ness, # for example, doesn't matter. getincarch() { case "$1" in # Catch these early to simplify matching for 32-bit archs arm64*|aarch64*) ARCH=aarch64 ;; arm*) ARCH=arm ;; thumb*) ARCH=arm ;; cortex*) ARCH=arm ;; x86_32*|i?86*) ARCH=i386 ;; x86_64-x32*|x32*|x86_64*x32) ARCH=x32 ;; x86_64*) ARCH=x86_64 ;; mips64*) ARCH=mips64 ;; mips*) ARCH=mips ;; microblaze*) ARCH=microblaze ;; ppc64*) ARCH=ppc64 ;; ppc*) ARCH=ppc ;; sh[1-9bel-]*|sh|superh*) ARCH=sh ;; *) bailout "Unknown include architecture." ;; esac echo $ARCH } # Return the machine arch for a tuple. getmachinearch() { case "$1" in # Catch these early to simplify matching for 32-bit archs arm64*|aarch64*) ARCH=aarch64 ;; arm*) ARCH=arm ;; thumb*) ARCH=arm ;; cortex*) ARCH=armv6m ;; x86_32*|i?86*) ARCH=i386 ;; x86_64-x32*|x32*|x86_64*x32) ARCH=x32 ;; x86_64*) ARCH=x86_64 ;; mips64*) ARCH=mips64 ;; mips*) ARCH=mips ;; microblaze*) ARCH=microblaze ;; ppc64*) ARCH=ppc64 ;; ppc*) ARCH=ppc ;; sh[1-9bel-]*|sh|superh*) ARCH=sh ;; *) bailout "Unknown machine architecture." ;; esac echo $ARCH } # Return the machine OS for a tuple. getmachineos() { case "$1" in *-linux) echo linux ;; *) echo unknown ;; esac } # Check to see if a library exists. check_library() { if [ -e ${LOCALLIB}/$1 ] ; then return 0 fi # RICH: if [ -e ${ELLCC}/libecc/lib/${TARGETTUPLE}/$1 ] ; then # return 0 #fi echo "Missing library $1. $2" return 1 } run() { if [ "$1" == "-" ] ; then # Ignore the exit status. ignore=true shift else ignore=false fi if [ "${VERBOSE}" == "y" ] ; then echo $@ fi if [ "${DRY_RUN}" != "y" ] ; then if [ "$(type -t $1)" = "function" -o "$(type -t $1)" = "builtin" ] ; then "$@" || ${ignore} || bailout "'$@' failed." else env "$@" || ${ignore} || bailout "'$@' failed." fi fi } svn_fetch() { # Check for a checkout username. if [ "${USERNAME}" != "" ] ; then user="--username ${USERNAME}" fi if [ "${REVISION}" != "" ] ; then revision="-r ${REVISION}" fi run mkdir -p $1 run cd $1 run echo Checking out ${4}. run svn co ${revision} ${user} $2 $3 run save_revision svn ${REVISION[2]} } git_fetch() { # Check for a checkout username. if [ "${USERNAME}" != "" ] ; then user="--username ${USERNAME}" fi run mkdir -p $1 run cd $1 if [ -d $3 ] ; then # Already cloned, bring up to date to mimic svn behavior. run cd $3 run git checkout . run git checkout master #run git pull run git checkout ${REVISION} else run git clone ${USERNAME} $2 $3 run cd $3 run git checkout ${REVISION} fi run save_revision git ${REVISION[2]} } tar_fetch() { run mkdir -p $1 run cd $1 if [ ! -e $(basename $2) ] ; then run ${CURL} $2 -o $(basename $2) -L run tar xvfp $(basename $2) fi run rm -fr $3 run cp -r $3-${REVISION} $3 } checkout() { for p in "${PROJECTS[@]}" ; do info=(${!p}) if [ "${info[3]}" = "svn" ] ; then svn_fetch ${info[0]} ${info[1]} ${info[2]} ${p} elif [ "${info[3]}" = "git" ] ; then git_fetch ${info[0]} ${info[1]} ${info[2]} ${p} elif [ "${info[3]}" = "tar" ] ; then tar_fetch ${info[0]} ${info[1]} ${info[2]} ${p} else bailout "Unknown checkout method for ${info[2]}: '${info[3]}'." fi done } create_patch_one() { if [ ! -d $1 ] ; then bailout "$1 has not been checked out." fi # Find files that have been added. run cd $1 if [ "$3" = "svn" ] ; then files=$(svn status | grep \?) elif [ "$3" = "git" ] ; then files=$(git status -s | grep \?) elif [ "$3" = "tar" ] ; then # Rich find added files. files= else bailout "Unknown patch method for $2: '$3'" fi for file in ${files} ; do if [ -f ${file} ] ; then run mkdir -p ${PATCHES}/$2/$(dirname ${file}) run echo "Patching ${file} to ${PATCHES}/$2/${file}" run cp ${file} ${PATCHES}/$2/${file} fi done # Create project patches. run cd $1 if [ "$3" = "svn" ] ; then run svn diff --patch-compatible > $2.patch elif [ "$3" = "git" ] ; then run git diff > $2.patch elif [ "$3" = "tar" ] ; then run cd .. run - diff -Nur $2-${REVISION} $2 > $2.patch else bailout "Unknown patch method for $2: '$3'." fi echo `pwd` if [ -s $2.patch ] ; then run echo "Sending $2.patch to ${PATCHES}" run mv $2.patch ${PATCHES} else run rm -f $2.patch fi } patch_one() { if [ ! -d $1 ] ; then bailout "$1 has not been checked out." fi # Remove any old patches. if [ "$3" = "svn" ] ; then pval=0 run svn revert -R $1 elif [ "$3" = "git" ] ; then pval=1 run cd $1 run git checkout . elif [ "$3" = "tar" ] ; then pval=1 # Restore original files. rm -fr $1 cp -pr $1-${REVISION} $1 else bailout "Unknown revert method for $2: '$3'." fi if [ -d ${PATCHES}/$2 ] ; then # Patch whole files. run cd ${PATCHES}/$2 run tar cvfp - . | (cd $1; tar xfp -) fi if [ -e ${PATCHES}/$2.patch ] ; then # Apply project patches. run cd $1 if [ "${HOSTOS}" = "linux" -o "${HOSTOS}" = "darwin" ] ; then NOBACKUP="--no-backup-if-mismatch" fi run command patch ${NOBACKUP} -p${pval} < ${PATCHES}/$2.patch fi } update_one() { if [ ! -d $1 ] ; then bailout "$1 has not been checked out." fi if [ "$2" = "svn" ] ; then if [ "$3" = "" ] ; then run svn update $1 else run svn update -r $3 $1 fi run save_revision svn elif [ "$2" = "git" ] ; then run cd $1 run git checkout . run git checkout master # run git pull if [ "$3" != "" ] ; then sun git checkout $3 fi run save_revision git elif [ "$2" = "tar" ] ; then # Nothing to update true else bailout "Unknown update method for $1: '$2'." fi } revert_one() { if [ ! -d $1 ] ; then bailout "$1 has not been checked out." fi if [ "$2" = "svn" ] ; then run svn revert -R $1 elif [ "$2" = "git" ] ; then run cd $1 run git checkout ${REVISION} elif [ "$2" = "tar" ] ; then # Restore original files. rm -fr $1 cp -pr $1-${REVISION} $1 else bailout "Unknown revert method for $1: '$2'." fi } check_checkout() { if [ "${DRY_RUN}" == "y" ] ; then return 0 fi if [ ! -d ${PROJECTDIR} ] ; then bailout "Projects have not been checked out to ${PROJECTDIR}." fi } check_configure() { if [ "${DRY_RUN}" == "y" ] ; then return 0 fi if [ ! -d ${BUILDDIR} ] ; then bailout "Projects in ${BUILDDIR} have not been configured." fi } create_patch() { # Create patches for checked out out projects. run check_checkout for p in "${PROJECTS[@]}" ; do info=(${!p}) create_patch_one ${info[0]}/${info[2]} ${info[2]} ${info[3]} done } patch() { # Patch checked out out projects. run check_checkout for p in "${PROJECTS[@]}" ; do info=(${!p}) patch_one ${info[0]}/${info[2]} ${info[2]} ${info[3]} done } update() { # Update checked out out projects. run check_checkout revision="" for p in "${PROJECTS[@]}" ; do info=(${!p}) update_one ${info[0]}/${info[2]} ${info[3]} ${revision} revision=$(get_revision ${info[3]}) done } revert() { # Revert checked out out projects. run check_checkout for p in "${PROJECTS[@]}" ; do info=(${!p}) revert_one ${info[0]}/${info[2]} ${info[3]} done } build() { run check_checkout run check_configure run ${MAKE} -j ${MAXJOBS} -C ${BUILDDIR} } install() { run check_checkout run check_configure run ${MAKE} -j ${MAXJOBS} -C ${BUILDDIR} install } if [ "${TARGETTUPLE}" != "" ] ; then TARGETARCH=$(getmachinearch ${TARGETTUPLE}) TARGETOS=$(getmachineos ${TARGETTUPLE}) fi environment() { printf "%20s: %s\n" PROJECT ${PROJECT} printf "%20s: %s\n" REVISION ${REVISION} printf "%20s: %s\n" ELLCC ${ELLCC} printf "%20s: %s\n" LIBECC ${LIBECC} printf "%20s: %s\n" SCRIPTS ${SCRIPTS} printf "%20s: %s\n" TOOLINFO ${TOOLINFO} printf "%20s: %s\n" PATCHES ${PATCHES} printf "%20s: %s\n" TOP ${TOP} printf "%20s: %s\n" SRCTOP ${SRCTOP} printf "%20s: %s\n" DISTTOP ${DISTTOP} printf "%20s: %s\n" PROJECTDIR ${PROJECTDIR} printf "%20s: %s\n" LOCAL ${LOCAL} printf "%20s: %s\n" LOCALLIBECC ${LOCALLIBECC} printf "%20s: %s\n" LOCALTOOLINFO ${LOCALTOOLINFO} printf "%20s: %s\n" LOCALLIB ${LOCALLIB} printf "%20s: %s\n" LOCALBIN ${LOCALBIN} printf "%20s: %s\n" HOSTOS ${HOSTOS} printf "%20s: %s\n" HOSTPROCESSOR ${HOSTPROCESSOR} printf "%20s: %s\n" TOOLS ${TOOLS} printf "%20s: %s\n" CROSS ${CROSS} printf "%20s: %s\n" HOST_CC "${HOST_CC}" printf "%20s: %s\n" CC "${CC}" printf "%20s: %s\n" CPP "${CPP}" printf "%20s: %s\n" CXX "${CXX}" printf "%20s: %s\n" AR ${AR} printf "%20s: %s\n" RANLIB ${RANLIB} printf "%20s: %s\n" LD ${LD} printf "%20s: %s\n" MAKE ${MAKE} printf "%20s: %s\n" CURL ${CURL} printf "%20s: %s\n" SHARED ${SHARED} printf "%20s: %s\n" BUILDDIR ${BUILDDIR} printf "%20s: %s\n" MAXJOBS ${MAXJOBS} printf "%20s: %s\n" USERNAME ${USERNAME} printf "%20s: %s\n" TARGETTUPLE ${TARGETTUPLE} printf "%20s: %s\n" TARGETARCH ${TARGETARCH} printf "%20s: %s\n" TARGETOS ${TARGETOS} printf "%20s: %s\n" HOSTTUPLE ${HOSTTUPLE} printf "%20s:" TARGETS count=0 for t in ${TARGETS} ; do if [ $count -ge 5 ] ; then printf "\n%22s%s" "" ${t} count=0 else printf " %s" ${t} fi ((count++)) done if [ $count -ne 0 ] ; then echo fi } if [ "${ENVIRONMENT}" == "y" ] ; then environment exit 0 fi