#!/bin/bash # # --- ROCK-COPYRIGHT-NOTE-BEGIN --- # # This copyright note is auto-generated by ./scripts/Create-CopyPatch. # Please add additional copyright information _after_ the line containing # the ROCK-COPYRIGHT-NOTE-END tag. Otherwise it might get removed by # the ./scripts/Create-CopyPatch script. Do not edit this copyright text! # # ROCK Linux: rock-src/scripts/Build-Target # ROCK Linux is Copyright (C) 1998 - 2006 Clifford Wolf # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. A copy of the GNU General Public # License can be found at Documentation/COPYING. # # Many people helped and are helping developing ROCK Linux. Please # have a look at https://www.rocklinux.org/ and the Documentation/TEAM # file for details. # # --- ROCK-COPYRIGHT-NOTE-END --- # # Run this command from the ROCK directory as ./scripts/Build-Target # after running the ./scripts/Config and ./scripts/Download commands. # # It compiles/builds all the packages and stores them as tar balls suitable # for distribution. # # This script is the ROCK work-horse. It builds in a chroot environment # (stage 3..9) and goes through a number of build stages: # config=default build_only_this_job= stages=0123456789 daemon_mode=0 options="$*" tasks=0 while [ "$1" ] ; do case "$1" in -cfg) config=$2 ; shift ; shift ;; -stages) stages=$2 ; shift ; shift ;; -tasks) tasks=$2 ; shift ; shift ;; -job) build_only_this_job=$2 ; shift ; shift ;; -daemon) daemon_mode=1 ; shift ;; -nodaemon) daemon_mode=0 ; shift ;; *) echo "Usage: $0 [ -daemon ] [ -tasks ] [ -cfg ]" \ "[ -stages ] [ -job - ]" echo echo " Compile/build all packages for a given configuration and store them as tar" echo " balls suitable for distribution." echo " This script is the ROCK work-horse. It builds in a chroot environment" echo " (stage 2..9) and goes through a number of build stages." echo " Run this command from the ROCK directory as ./scripts/Build-Target after" echo " running the ./scripts/Config and ./scripts/Download commands." echo echo " -cfg the build configuration to use; defaults to 'default'" echo " -tasks run Build-Job daemons (for cluster builds)" echo " -daemon run as daemon in the background" echo " -stages only build the stages listed in the parameter" echo " -job - build only one job: the given package" echo " in the given stage" exit 1 ;; esac done if [ $tasks -gt 1 ] ; then . config/$config/config if [ "$ROCKCFG_PARALLEL" = 0 ] ; then echo_error "You have not configured this to be a cluster build!" echo_error "Please remove -tasks $tasks or configure this for cluster build" exit 1 fi fi if [ "$daemon_mode" = 1 ] ; then . config/$config/config echo "Running $0 in the background (messages go to logfile only).." echo "Logfile: build/$ROCKCFG_ID/ROCK/logs/build_target.log" nohup $0 $options -nodaemon > /dev/null 2> /dev/null < /dev/null & exit 0 fi . scripts/parse-config . scripts/functions # so we can disable stuff not available in Build-Target export ROCK_BUILD_TARGET=1 export build_root="$base/build/$ROCKCFG_ID" # Set permissions and ownership of $build_root; $base/build might have different ones. mkdir -p "${build_root}" ; chmod 700 "${build_root}" ; chown 0:0 "${build_root}" export build_rock="$base/build/$ROCKCFG_ID/ROCK" export build_logs="$build_rock/logs" ; mkdir -p "${build_logs}" export build_pkgs="$build_rock/pkgs" ; mkdir -p "${build_pkgs}" if [ "$ROCKCFG_PARANOIA_CHECK" = 1 ] ; then ./scripts/Check-System -paranoia || exit 1 else ./scripts/Check-System || exit 1 fi # Package Build loop - executed by build-target # pkgloop() { if [ "$ROCKCFG_NOBROKENDEPS" = 1 ]; then nobrokendeps="-nobrokendeps" else nobrokendeps="" fi if [ "$ROCKCFG_RETRY_BROKEN" -eq 1 -a \ -z "$build_only_this_job" -a \ "`ls ${build_root}/var/adm/logs/*.err 2> /dev/null`" ] ; then echo_header "Removing old error logs ..." for x in ${build_root}/var/adm/logs/*.err ; do echo_status "Removing ${x#$build_root/} ..." rm -f $x done fi if [ -z "$build_only_this_job" -a \ "`ls ${build_root}/var/adm/logs/*.out 2> /dev/null`" ] ; then echo_header "Removing old output logs ..." for x in ${build_root}/var/adm/logs/*.out ; do echo_status "Removing ${x#$build_root/} ..." rm -f $x done fi if [ "$ROCKCFG_PARALLEL" = 1 -a -z "$build_only_this_job" ] then qdir="$base/build/$ROCKCFG_ID/ROCK/queue" mkdir -p $qdir; rm -f $qdir/* newqueue=1 ; printstatus=1 ; printstatus_counter=30 finished=0 ; touch $qdir/queue.txt if [ -z "$ROCKCFG_PARALLEL_ADDJOB" ] ; then touch $qdir/use_build_job_daemon if [ $tasks = 0 ]; then echo " Job Queue for parallel (cluster) build created. You have not configured a command for adding jobs. So you need to use the ROCK Linux built-in job queue. To do so, you need to login to your build nodes and execute the command: ./scripts/Build-Job -cfg $config -daemon " else echo " Job Queue for parallel (cluster) build created. Now creating worker tasks.. " for ((n=0; n $qdir/queue.new mv $qdir/queue.new $qdir/queue.txt newqueue=0 while read next ; do set $next ; qid="$1-$6" if [ ! -f $qdir/$qid.job ] ; then date "+%H:%M %Y-%m-%d: creating new job '$qid'" | expand -t20 echo "Job $qid waiting in the job queue (priority $2)" > $qdir/$qid.todo printstatus=1 # we need to remove some variables that should be uniqe per node dump_env | grep -v HOSTNAME= > $qdir/$qid.new mv $qdir/$qid.new $qdir/$qid.job if [ "$ROCKCFG_PARALLEL_ADDJOB" ] ; then x="${ROCKCFG_PARALLEL_ADDJOB//\{\}/.\/scripts\/Build-Job -cfg $config $qid}" ; sh -c "$x" fi fi done < $qdir/queue.txt fi if [ -s $qdir/queue.txt ] || \ [ "`ls $qdir/*.job 2>/dev/null`" ] then finished=0 else finished=$(( $finished + 1 )) newqueue=1 fi [ $finished -le 3 ] do sleep 1 done echo "All packages built." sleep 2; rm -rf $qdir else if [ "$build_only_this_job" ] ; then rm -f "${build_root}"/var/adm/logs/${build_only_this_job}.log rm -f "${build_root}"/var/adm/logs/${build_only_this_job}.err next="$( awk 'BEGIN { FS=" "; } $0 ~ /[ =]'${build_only_this_job#*-}' / && \ $2 ~ /'${build_only_this_job%%-*}'/ \ { $1="'${build_only_this_job%%-*}' 0"; print; exit; }' < config/$config/packages )" [ "$next" ] && pkgloop_package $next exit 0 else while next="`./scripts/Create-PkgQueue \ -cfg "$config" -stages $stages -single $nobrokendeps`" [ "$next" ] do pkgloop_package $next done fi fi local pkglst=`mktemp` errors=0; rm -f src/invalid-files.lst echo_header "Searching for old lingering files ..." grep "^X" config/$config/packages | cut -d' ' -f5 | sed 's,.*=,,' | if [ $ROCKCFG_PKGFILE_VER = 1 ] ; then while read p; do v=$( grep -h '^Package Name and Version:' build/$ROCKCFG_ID/var/adm/packages/$p:* \ build/$ROCKCFG_ID/var/adm/packages/$p 2> /dev/null | cut -f6,7 -d' ' | tr ' ' - | head -n1 ) echo "$p-$v" done else cat fi > $pkglst for file in $( ls build/$ROCKCFG_ID/ROCK/pkgs/ ) ; do x="$file" [ "$x" = packages.db ] && continue [ "$x" = packages.db.md5 ] && continue [ "$x" = packages_stripped.db ] && continue [ "$x" = packages_stripped.db.md5 ] && continue [ $ROCKCFG_CREATE_GEM = 0 ] || x=${x%.gem} [ $ROCKCFG_CREATE_TARBZ2 = 0 ] || x=${x%.tar.bz2} y=$( echo $x | sed 's,:[^-]*,,' ) if ! grep -qx "$y" $pkglst; then file="build/$ROCKCFG_ID/ROCK/pkgs/$file" echo_error "$file [$x $y] should not be present" \ "(now in src/invalid-files.lst)!" mkdir -p src; echo "$file" >> src/invalid-files.lst errors=1 fi done for dir in build/$ROCKCFG_ID/var/adm/{cache,cksums,dependencies,descs,flists,md5sums,packages} ; do for file in $( ls $dir ) ; do if [ $ROCKCFG_PKGFILE_VER = 1 ] ; then x="${file%:*}-.*"; else x="${file%:*}"; fi if ! grep -xq "$x" $pkglst ; then echo_error "$dir/$file should not be present (now in src/invalid-files.lst)!" mkdir -p src; echo "$dir/$file" >> src/invalid-files.lst errors=1 fi done done for file in $( ls build/$ROCKCFG_ID/var/adm/logs/ ) ; do x="`echo $file | sed -e 's/^.-//' -e 's/\.log//' -e 's/\.err//' -e s'/\.out//'`" if [ $ROCKCFG_PKGFILE_VER = 1 ] ; then x="$x-.*"; else x="$x"; fi if ! grep -xq "$x" $pkglst ; then file="build/$ROCKCFG_ID/var/adm/logs/$file" echo_error "$file should not be present (now in src/invalid-files.lst)!" mkdir -p src; echo "$file" >> src/invalid-files.lst errors=1 fi done [ $errors = 0 ] && echo_status "No errors found." rm $pkglst } # Process one line of output generated by Create-PkgQueue # pkgloop_package() { for x in stagelevel pkg_depnr pkg_stages pkg_pri pkg_tree \ pkg_name pkg_ver pkg_extraver pkg_extra do eval "$x=\$1" ; shift ; done # Maybe this is a forked package pkg_basename="${pkg_name%=*}" pkg_name="${pkg_name#*=}" [ "$build_only_this_job" -a \ "$stagelevel-$pkg_name" != "$build_only_this_job" ] && return [ $(expr "$pkg_stages" : ".*$stagelevel.*") -eq 0 ] && return pkg_laststage=$(echo "$pkg_stages" | sed "s,-,,g; s,.*\(.\),\1,") cmd_root="-root auto" [ $stagelevel -ge 3 ] && cmd_root="$cmd_root -chroot" cmd_buildpkg="./scripts/Build-Pkg -$stagelevel -cfg $config -nopostinst" cmd_buildpkg="$cmd_buildpkg $cmd_root $pkg_basename=$pkg_name" # Execute action handler pkgloop_action || [ "$ROCKCFG_ABORT_ON_ERROR" != 1 ] || exit 1 if [ -f ${build_root}/var/adm/logs/$stagelevel-$pkg_name.err -a \ "$ROCKCFG_SENDMAIL" = 1 ] then { cat << EOT Subject: [ROCK Build-Target] $stagelevel-$pkg_name in $config failed Building package $pkg_name failed in stage $stagelevel: ---- EOT echo; tail -n 200 ${build_root}/var/adm/logs/$stagelevel-$pkg_name.err; echo } | $ROCKCFG_SENDMAIL_BIN $ROCKCFG_SENDMAIL_TO fi if [ -f ${build_root}/var/adm/logs/$stagelevel-$pkg_name.err -a \ "$ROCKCFG_HTTP" = 1 ] then { query=${ROCKCFG_HTTP_SITE} query=${query//PACKAGE/$pkg_name} query=${query//STAGE/$stagelevel} query=${query//CONFIG/$config} query=${query//STATUS/failed} curl "${query}" > /dev/null 2>&1 } fi if [ ! -f ${build_root}/var/adm/logs/$stagelevel-$pkg_name.log -a \ ! -f ${build_root}/var/adm/logs/$stagelevel-$pkg_name.err ] then echo_header "Package build ended abnormally!" echo_error "Usually a package build creates eighter a *.log" echo_error "or a *.err file. Neither the 1st nor the 2nd is" echo_error "there. So I'm going to create a *.err file now" echo_error "and abort the build process." touch ${build_root}/var/adm/logs/$stagelevel-$pkg_name.err exit 1 fi if [ "${ROCKCFG_HTTP}" = "1" -a -f ${build_root}/var/adm/logs/$stagelevel-$pkg_name.log ] then { query=${ROCKCFG_HTTP_SITE} query=${query//PACKAGE/$pkg_name} query=${query//STAGE/$stagelevel} query=${query//CONFIG/$config} query=${query//STATUS/finished} curl "${query}" > /dev/null 2>&1 } fi if [ $pkg_laststage -eq $stagelevel ] && \ [ "$ROCKCFG_CREATE_TARBZ2" = 1 -o "$ROCKCFG_CREATE_GEM" = 1 ] then if [ -f ${build_root}/var/adm/logs/$stagelevel-$pkg_name.err ] then echo_error "Creation of binary package isn't possible," echo_error "because the package was not successfully" echo_error "built in (at least) the current stage." else for spkg in $( cd ${build_root}/var/adm/packages/; ls ${pkg_name} ${pkg_name}:* 2>/dev/null ) do echo_header "Creating binary package file for ${spkg}." mkdir -p "${build_pkgs}" if [ "$ROCKCFG_PKGFILE_VER" = 1 ] then v="-$( grep '^Package Name and Version:' \ ${build_root}/var/adm/packages/$spkg | cut -f6,7 -d' ' | tr ' ' - )" else v="" fi echo_status "Building build/.../pkgs/` `${spkg}${v}.tar.bz2" ( cd "$build_root/" cut -f2- -d' ' var/adm/flists/$spkg | \ tar -cf- --no-recursion --files-from=- | bzip2 ) > "${build_pkgs}/${spkg}${v}.tar.bz2.tmp" if [ "$ROCKCFG_CREATE_GEM" = 1 ] ; then echo_status "Building build/.../pkgs/` `${spkg}${v}.gem" mine -C "$build_root/var/adm" \ "${build_pkgs}/${spkg}${v}.tar.bz2.tmp" \ $spkg "$build_pkgs/${spkg}${v}.gem" fi if [ "$ROCKCFG_CREATE_TARBZ2" = 1 ] ; then mv "$build_pkgs/${spkg}${v}.tar.bz2.tmp" \ "$build_pkgs/${spkg}${v}.tar.bz2" else echo_status "Removing temporary tar.bz2." rm -f "$build_pkgs/${spkg}${v}.tar.bz2.tmp" fi done fi fi } # Action executed by pkgloop(). This function may be redefined # before calling pkgloop(). # pkgloop_action() { $cmd_buildpkg } # Try to umount any directories mounted by Build-Pkg -chroot # if we are the last process using them. # build_target_exit() { exec 201> /dev/null exec 1>&0 2>&0 echo_header "Running Build-Target cleanup procedure" echo_status "Killing lingering processes and removing parallel build queue." rm -rf $build_rock/queue fuser -k ${build_logs}/*.log &> /dev/null sleep 2 echo_status "Umounting lingering mounts in build/$ROCKCFG_ID." umount -d -f $build_rock/{loop,config,download} 2> /dev/null umount -d -f -l $build_rock/{loop,config,download} 2> /dev/null umount -d -f $build_root/proc 2> /dev/null umount -d -f -l $build_root/proc 2> /dev/null umount -d -f $build_root/dev 2> /dev/null umount -d -f -l $build_root/dev 2> /dev/null echo_status "READY." echo } # must trap outside the group command trap 'build_target_exit' EXIT { ln -sf build_target_$$.log ${build_logs}/build_target.log ./scripts/Build-Tools -1 -cfg $config . ./target/$ROCKCFG_TARGET/build.sh echo_header "Finished building this target." echo_status "Going to run cleanup procedure now.." } 2>&1 201>> "${build_logs}/build_target_$$.log" | \ tee -a "${build_logs}/build_target_$$.log" if [ "${ROCKCFG_HTTP}" = "1" ] ; then { query=${ROCKCFG_HTTP_SITE} query=${query//PACKAGE/} query=${query//STAGE/} query=${query//CONFIG/$config} query=${query//STATUS/finished} curl "${query}" > /dev/null 2>&1 } fi if [ "$ROCKCFG_SENDMAIL" = 1 ]; then $ROCKCFG_SENDMAIL_BIN $ROCKCFG_SENDMAIL_TO << EOT Subject: [ROCK Build-Target] $config finished. Finished building $config. EOT fi