function print_separator() { echo "--------------------------------------------------------------------------------------------------" } # Gets svn revision for app whose tree starts in current dir. # INPUT: # arg1 (optional) dir to check, default is the current dir # OUTPUT: # $SVN_REVISION function get_svn_revision { local DIR=$1 if [ -n "$DIR" ]; then pushd $DIR > /dev/null fi SVN_REVISION="" local revision=$(svn info . | grep -i '^last changed rev' | cut '-d ' -f4) local ccm_app=$(pwd | sed 's!.*/!!') if [ -z "$revision" ]; then echo "Could not find the most recent svn revision number for $ccm_app" exit 1 fi # Woo hoo, we have revision number now! echo "Found the svn revision number for $ccm_app: $revision" SVN_REVISION=".r$revision" if [ -n "$DIR" ]; then popd > /dev/null fi } # Bails out if uncommitted svn changes found in current dir function check_svn_clean { local DIR=$1 if [ -n "$DIR" ]; then pushd $DIR > /dev/null fi echo "Running 'svn status' in $(pwd)" local svnstatus=$(svn st | grep -v '^\?') if [ -n "$svnstatus" ]; then echo "$svnstatus" echo "Uncommited changes exist. Bailing out." exit 1 fi if [ -n "$DIR" ]; then popd > /dev/null fi } function check_svn_tagged { SVN_REVISION= if [ -n "$SVN_TAGGED" ]; then check_svn_clean $@ && get_svn_revision $@ fi } # INPUT: # arg1: Build root dir # arg2: app dir, whose properties we're about to inquire # arg3: SVN_TAGGED # OUTPUT: # $APP_NAME # $APP_VERSION # $RELEASE function set_application_properties() { local BUILD_HOME=$1 local APP=$2 local SVN_TAGGED=$3 local PROJ_FILE=$BUILD_HOME/$APP/project.xml if [ ! -e $PROJ_FILE ]; then echo "$PROJ_FILE doesn't exist" exit 1; fi local VERSION_FROM=`grep "versionFrom=\".*\"" $PROJ_FILE | sed 's/.*versionFrom="\(.*\)"/\1/'` if [ -z $VERSION_FROM ]; then echo "versionFrom not found" fi local APP_FILE=$BUILD_HOME/$APP/$VERSION_FROM/application.xml if [ ! -e $APP_FILE ]; then echo "$APP_FILE doesn't exist" exit 1 fi local FRAGMENT=$BUILD_HOME/$APP/application.xml.frag awk 'BEGIN { inTag = 0; } // { inTag = 0; }' \ $APP_FILE > $FRAGMENT APP_VERSION=`grep 'version=\"[^"]*\"' $FRAGMENT | sed 's/.*version="\([^\"]*\)".*/\1/'` RELEASE=`grep 'release=\"[^"]*\"' $FRAGMENT | sed 's/.*release="\([^\"]*\)".*/\1/'` if [ -n "$SVN_TAGGED" ]; then get_svn_revision $BUILD_HOME/$APP/$APP RELEASE="${RELEASE}${SVN_REVISION}" fi APP_NAME=`grep 'name=\"[^"]*\"' $FRAGMENT | sed 's/.*name="\([^\"]*\)".*/\1/'` rm -f $FRAGMENT } # Here's how we deal with eq,lt,le,gt,ge relations: # # [eq]: - easiest, we look for the exact match # # [ge]: we compose the list which is made up from # installed version(s) of the required packages and the required # version. # # An example: # # Now suppose that we have following ccm-core available in our local rpm db: # ccm-core-6.1.1 # ccm-core-6.1.3 # # We now construct a list of versions: # # 6.1.1 # 6.1.3 # 6.1.2.-1 # # First two are installed ones, the last is one mentioned in Requires: # We call that one the 'tagged' version. # We append '.-1' b/c we're just about to sort numerically that list, which # will put the tagged version before the 6.1.2, if such exists. Sorting is # performed numerically with field delimiter being period (.), so the sorted # list looks like: # # 6.1.1 # 6.1.2.-1 # 6.1.3 # # We now grep this list for the tagged version (6.1.2.-1), and take the first line *below* # the match. In the case 6.1.2 was available, it will yield a match, since it will be # the first one below the tag 6.1.2.-1. However, in our example, the match is '6.1.3'. # In case there are no lines after the tagged version, dependencies can't be met. # # [le]: similar to [ge], except that the tagged version carries the .001 suffix. # We proceed with composing the list and sorting it as above, but we take the first # line *above* the tagged version. If no line exists above the tagged version, # dependencies can't be met. # # [gt,lt]: similar to [ge,le], except that we do no funky suffixes to # the tagged version. Every time we sort, we use '-u' to supress duplicate lines. # We then grep for the tagged version and take the first one below or above it, # respectively. # # INPUT: # arg1: path to local rpm database # arg2: package name to check # arg3: package version to check; optional # arg4: dependency relation (eq, le, lt, ge, gt); optional, default = eq # checked only if package version (arg3) is provided # OUTPUT: # RPM_CHECK_ERROR: empty on successful check # RPM_MATCH: version number of installed package that resolves dependency # function check_local_rpm() { local rpmdb=$1 local pkg_name=$2 local pkg_version=$3 local pkg_relation=$4 if [ -z "$pkg_relation" ] then pkg_relation=eq fi local dependency="$2 $4 $3" local output="" local sortedoutput="" local match="" if [ -n "$pkg_version" ] then local pkg_tagged_version="$pkg_version" # Mangle the tagged version case $pkg_relation in le) pkg_tagged_version="${pkg_version}.001";; ge) pkg_tagged_version="${pkg_version}.-1";; esac output=$(rpm --dbpath $rpmdb -q --queryformat '%{VERSION}\n' $pkg_name | grep -v 'is not installed') || true; sortedoutput=$(echo -e "$output\n$pkg_tagged_version") sortedoutput=$(echo "$sortedoutput" | sort -u -n -t. -k1,1 -k2,2 -k3,3 -k4,4 -k5,5) # echo -e "output: \n$output" # echo -e "sortedoutput: \n$sortedoutput" case $pkg_relation in eq) match=$(echo "$output" | grep -F -x "$pkg_version") ;; le|lt) match=$(echo "$sortedoutput" | grep -B1 -F -x "$pkg_tagged_version" | head --lines=-1) ;; ge|gt) match=$(echo "$sortedoutput" | grep -A1 -F -x "$pkg_tagged_version" | tail --lines=+2) ;; esac else output=$(rpm --dbpath $rpmdb -q --queryformat '%{VERSION}\n' $pkg_name | grep -v 'is not installed') \ && match=$(echo "$output" | sort -u -n -t. -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 | tail -1) fi RPM_MATCH="" RPM_CHECK_ERROR="" if [ "$(echo "$match" | sed 's/ //g')" = "" ] then if [ "$output" != "" ] then RPM_CHECK_ERROR="'$output'" else RPM_CHECK_ERROR="'$pkg_name' not built at all" fi else RPM_MATCH="$match" fi } # Generate skeleton project.xml. # # We will generate the block by expanding # the for each required CCM app in turn. # The actual expansion work will be done by a perl script. # However we must deal with the relation="eq,gt,ge,lt,le" as well. # This is where life gets interesting. This shell function will # use: # 1. the output of the perl script, which looks like: # ccm-core -> version="6.1.1" relation="ge" # ccm-forum -> version="1.4.2" # 2. local rpm database # to find out which RPM packages (installed in scratch RPM # database, and which have been built during the execution of # this very script) can satisfy the dependencies listed. # # INPUT: # arg1: build root dir # arg2: ccm app name, which must match the directory name in # the build root dir # # OUTPUT: # - project.xml will be written to the $arg1/$arg2 directory # - RPM_CHECK_ERROR will contain the error message, if something # goes wrong, eg. unresolved dependencies function write_project_xml() { echo "Generating project.xml" local BUILD_ROOT="$1" local CCM_APP="$2" local PROJ_FILE="$BUILD_ROOT/$CCM_APP/project.xml" rm -f $PROJ_FILE cat > $PROJ_FILE < EOF $BUILD_ROOT/tools/misc/expand-dependencies $BUILD_ROOT $CCM_APP | while read app_dep do echo -n " Processing dependency: $app_dep" local app_name=$(expr match "$app_dep" '\([^ ]*\) *->') local app_version=$(expr match "$app_dep" '.*version="\([^"]*\)"') local app_relation=$(expr match "$app_dep" '.*relation="\([^"]*\)"') check_local_rpm $RPM_DB $app_name $app_version $app_relation if [ -n "$RPM_CHECK_ERROR" ] then echo " ... $RPM_CHECK_ERROR" # It seems there's no easy way to pass the error message to the caller # b/c of while ... do ... done construct constraints echo error >> $PROJ_FILE else echo " ... found '$RPM_MATCH'" echo "" >> $PROJ_FILE fi done local FAILED=$(grep -c ^error $PROJ_FILE) if [ "$FAILED" -gt 0 ] then echo "$FAILED unresolved dependencies" exit 1 fi cat >> $PROJ_FILE << EOF2 EOF2 echo "Successfully written $PROJ_FILE" } # INPUT: # arg1: build root dir # function set_environment() { local BUILD_HOME=$1 export VIRTUAL_ROOT=$BUILD_HOME/root if [ -f $BUILD_HOME/rpm ] then RPM="$BUILD_HOME/rpm" else RPM="$(which rpm)" fi export RPM export RPM_DB=$VIRTUAL_ROOT/rpmdb export RPM_ARGS="--dbpath $RPM_DB" export HOMETOPDIR="$(echo ~/rpm)" export HOMERPMDIR="$HOMETOPDIR/RPMS/noarch" export HOMESRPMDIR="$HOMETOPDIR/SRPMS" export HOMEBUILDDIR="$HOMETOPDIR/BUILD" export PATH=$VIRTUAL_ROOT/usr/bin:$PATH } # INPUT: # arg1: build root dir # function build_tools() { local BUILD_HOME=$1 set_environment $BUILD_HOME rm -rf $VIRTUAL_ROOT mkdir $VIRTUAL_ROOT mkdir $RPM_DB # (20041108 Seb) # This directory must exist because 'rpm -i' of httpunit and friends will fail. # In a real world, this dir would have been already created by the packages httpunit at ali depend on. mkdir -p $VIRTUAL_ROOT/usr/share/java mkdir -p $VIRTUAL_ROOT/etc/ccm mkdir -p $VIRTUAL_ROOT/etc/profile.d mkdir -p $VIRTUAL_ROOT/usr/share/ccm-tools mkdir -p $VIRTUAL_ROOT/usr/share/doc mkdir -p $VIRTUAL_ROOT/usr/bin mkdir -p $VIRTUAL_ROOT/var/cache mkdir -p $VIRTUAL_ROOT/var/lib/ccm mkdir -p $VIRTUAL_ROOT/var/lib/ccm-devel mkdir -p $VIRTUAL_ROOT/var/log mkdir -p $VIRTUAL_ROOT/var/opt/ccm/data $RPM $RPM_ARGS --initdb cat > fake.spec < /dev/null ( print_separator set -e check_svn_tagged ./rollingbuild.sh TOOLS_NAME=`grep 'Name:' *.spec | sed -e 's/Name://' | sed -e 's/ //g'` TOOLS_VERSION=`grep 'Version:' *.spec | sed -e 's/Version://' | sed -e 's/ //g'` TOOLS_RELEASE=`grep 'Release:' *.spec | sed -e 's/Release://' | sed -e 's/ //g'` $RPM $RPM_ARGS -ivh --noscripts --relocate /usr=$VIRTUAL_ROOT/usr --relocate /etc=$VIRTUAL_ROOT/etc $HOMERPMDIR/$TOOLS_NAME-$TOOLS_VERSION-$TOOLS_RELEASE.noarch.rpm ) || exit $? popd > /dev/null done pushd $BUILD_HOME/tools/tools > /dev/null ( print_separator set -e check_svn_tagged TOOLS_VERSION=`grep 'VERSION=' configure.in | sed -e 's/VERSION=//'` TOOLS_RELEASE="`grep 'RELEASE=' configure.in | sed -e 's/RELEASE=//'`${SVN_REVISION}" ccm_tools_home="$HOMEBUILDDIR/ccm-tools-$TOOLS_VERSION" CCM_TOOLS_HOME="$ccm_tools_home" ./rollingbuild.sh $RPM $RPM_ARGS -ivh --noscripts --relocate /usr=$VIRTUAL_ROOT/usr --relocate /etc=$VIRTUAL_ROOT/etc --relocate /var=$VIRTUAL_ROOT/var $HOMERPMDIR/ccm-tools-$TOOLS_VERSION-$TOOLS_RELEASE.noarch.rpm ) || exit $? popd > /dev/null . $VIRTUAL_ROOT/etc/profile.d/ccm-tools.sh pushd $BUILD_HOME/tools/devel > /dev/null ( print_separator set -e check_svn_tagged CCM_TOOLS_HOME=$VIRTUAL_ROOT/usr/share/ccm-tools ./rollingbuild.sh DEVEL_VERSION=`grep 'VERSION=' configure.in | sed -e 's/VERSION=//'` DEVEL_RELEASE="`grep 'RELEASE=' configure.in | sed -e 's/RELEASE=//'`${SVN_REVISION}" $RPM $RPM_ARGS -ivh --noscripts --relocate /usr=$VIRTUAL_ROOT/usr --relocate /etc=$VIRTUAL_ROOT/etc --relocate /var=$VIRTUAL_ROOT/var $HOMERPMDIR/ccm-devel-$DEVEL_VERSION-$DEVEL_RELEASE.noarch.rpm ) || exit $? popd > /dev/null . $VIRTUAL_ROOT/etc/profile.d/ccm-devel.sh pushd $BUILD_HOME/tools/scripts > /dev/null ( print_separator set -e check_svn_tagged CCM_TOOLS_HOME=$VIRTUAL_ROOT/usr/share/ccm-tools ./rollingbuild.sh SCRIPTS_VERSION=`grep 'VERSION=' configure.in | sed -e 's/VERSION=//'` SCRIPTS_RELEASE="`grep 'RELEASE=' configure.in | sed -e 's/RELEASE=//'`${SVN_REVISION}" $RPM $RPM_ARGS -ivh --noscripts --relocate /usr=$VIRTUAL_ROOT/usr --relocate /etc=$VIRTUAL_ROOT/etc $HOMERPMDIR/ccm-scripts-$SCRIPTS_VERSION-$SCRIPTS_RELEASE.noarch.rpm ) || exit $? popd > /dev/null . $VIRTUAL_ROOT/etc/profile.d/ccm-scripts.sh pushd $BUILD_HOME/tools/bundle > /dev/null ( print_separator set -e check_svn_tagged ./rollingbuild.sh BUNDLE_VERSION=`grep 'VERSION=' configure.in | sed -e 's/VERSION=//'` BUNDLE_RELEASE="`grep 'RELEASE=' configure.in | sed -e 's/RELEASE=//'`${SVN_REVISION}" $RPM $RPM_ARGS -ivh --noscripts --relocate /usr=$VIRTUAL_ROOT/usr $HOMERPMDIR/ccm-tools-bundle-$BUNDLE_VERSION-$BUNDLE_RELEASE.noarch.rpm ) || exit $? popd > /dev/null # This is somewhat lame-ish way, but we really need # ant-contrib package installed. # The build system needs ant-contrib.jar ( task). # It is no longer present in the ccm-devel, so the appropriate # RPM must be installed beforehand. if rpm -q ant-contrib | grep 'not installed' then echo "Please install ant-contrib RPM, the build system depends on it." exit 1 else for file in $(rpm -ql ant-contrib) do if [ -f $file ] then local dir=$VIRTUAL_ROOT${file%/*} mkdir -p $dir cp -f $file $dir fi done fi } # INPUT: # arg1: build root dir # arg2: ccm app dir below the build root # $VIRTUAL_ROOT # $SVN_TAGGED # $RPM # $RPM_ARGS # $HOMERPMDIR function build_app() { local BUILD_ROOT=$1 local CCM_APP=$2 pushd $BUILD_ROOT/$CCM_APP > /dev/null ( echo "BUILDING $CCM_APP" write_project_xml $BUILD_ROOT $CCM_APP rm -rf rollingbuild build MANIFEST MANIFEST.SKIP check_svn_tagged $CCM_APP CCM_SHARED_LIB_DIST_DIR=$VIRTUAL_ROOT/usr/share/java CCM_CONFIG_LIB_DIR=$VIRTUAL_ROOT/usr/share/java CCM_TOOLS_HOME=$VIRTUAL_ROOT/usr/share/ccm-tools CCM_SCRIPTS_HOME=$VIRTUAL_ROOT/usr/share/ccm-scripts CCM_CONFIG_HOME=$VIRTUAL_ROOT/usr/share/ccm-devel $VIRTUAL_ROOT/usr/share/ccm-scripts/bin/build.sh ) || exit $? ( set -e set_application_properties $BUILD_ROOT $CCM_APP $SVN_TAGGED $RPM $RPM_ARGS --oldpackage --replacefiles --replacepkgs --relocate /usr=$VIRTUAL_ROOT/usr --relocate /etc=$VIRTUAL_ROOT/etc -Uvh $HOMERPMDIR/$APP_NAME-$APP_VERSION-$RELEASE.noarch.rpm ) || exit $? popd > /dev/null } # INPUT: # arg1: build root dir # arg2, arg3, ....: list of apps to build # OUTPUT: # $SORTED_APPS # function arrange_build_order() { pushd $1 > /dev/null shift SORTED_APPS="" local apps="$@" local app_build_order=$(grep ccm:requires */*/application.xml | sed 's!^\(ccm-[^/]*\)/.*name="\([^"]*\)".*!\2 \1!' | tsort) local app for app in $app_build_order do if echo " ${apps} " | grep -F " ${app} " > /dev/null then SORTED_APPS="$SORTED_APPS $app" fi done for app in $apps do if ! echo " ${SORTED_APPS} " | grep -F " ${app} " > /dev/null then echo "Could not read $app/*/application.xml." echo "Make sure to include a copy or symbolic link of this application in the build area." exit 1 fi done popd > /dev/null } # INPUT: # arg1: build root dir # arg2, arg3, ....: list of apps to build # OUTPUT: # $RESOLVED_APPS ... a list of all applications to build, with dependencies resolved, # might contain duplicates # function add_required_apps() { local BUILD_HOME=$1 shift RESOLVED_APPS="" local app local deps for app in "$@" do deps=$($BUILD_HOME/tools/misc/expand-dependencies $BUILD_HOME $app) || { echo " Could not build the dependencies for: $app" echo " Consult earlier messages, if any, to determine the culprit." exit 1 } RESOLVED_APPS=$(echo -e "$app\n$RESOLVED_APPS\n$deps" | cut -d " " -f1) done # A lame way to convert all whitespace into single space, I know, ... RESOLVED_APPS=$(echo $RESOLVED_APPS) } # INPUT: # arg1: build root dir # arg2: list of args to build supplied on the command line # arg3: app to check and, maybe, build # $RPM_DB # # The arg3 app will be built unconditionally if it's included in $BUILD_ARGS. # Otherwise, it will only be built if it hasn't already been built. # function build_app_conditionally() { local BUILD_ROOT=$1 local BUILD_ARGS=$2 local CCM_APP=$3 if echo " $BUILD_ARGS " | grep -F " ${CCM_APP} " > /dev/null then build_app $BUILD_ROOT $CCM_APP else check_local_rpm $RPM_DB $CCM_APP if [ -z "$RPM_CHECK_ERROR" ] then echo "Application '$CCM_APP' appears already built ($RPM_MATCH), skipping ..." else build_app $BUILD_ROOT $CCM_APP fi fi print_separator } # INPUT: # arg1: path to local rpm database # arg2: non-empty if -x has been provided on the command line # $RPM_DB # # OUTPUT: # $BUILD_TOOLS non-empty if tools must be built # function check_tools() { BUILD_TOOLS=yes if [ -n "$2" ] then echo "Starting the build process from scratch on user demand (-x flag provided)." elif rpm -q --dbpath $1 ccm-tools-bundle > /dev/null then BUILD_TOOLS= echo "Tools OK." else echo "Starting the build process from scratch, tools appear built incompletely or not at all." fi echo "${BUILD_TOOLS:+..... Building: TOOLS}" }