#!/bin/bash # update-world 1.7b # count_zero 2006 # Licensed under the GPLv2 # # update-world is a bash script that forces an emerge -uD world to continue, even # in the face of (inevitable) compile failures. Failed builds are added to a list, # and the script jumps to the next package in the queue. # # just run update-world --prepare (or --empty) followed by update-world --install. # # Release Notes: # v1.7b: Cleaned up some of the code, fixed some bugs. Also added feature to manage use flags. # v1.6: Script preserves colored output. Fixed version numbering in help output. # v1.5: now handles 'fetch restriction' properly # v1.4: uses '--newuse' for emerges to respect new use flags # v1.3: uses '--oneshot' for emerges so they don't get added to the world file. # v1.2: Script now detects log directly instead of accepting only /var/log/portage # v1.1: minor bugfix to allow package without version to be added to 'emergelist' # v1.0: Release # # # Set the program directory and important variables homedir=~/.update-world workdir="$homedir/`date +%F`" logdir="$workdir/log" mkdir -p $logdir touch $homedir/failedlist emergelist=$homedir/emergelist emergetemp=$workdir/emergetemp emergetemp2=$workdir/emergetemp2 failedlist=$homedir/failedlist portlogdir=`cat /etc/make.conf | grep ^PORT_LOGDIR | sed 's/PORT_LOGDIR=//' | sed 's/\"//g' | sed 's/\/$//'` masked=$homedir/masked packagedotuse=/etc/portage/package.use # parameter for '--empty' to emerge -e world if [[ $1 == '--empty' || $1 == '-e' ]] then parameter='-pev' else parameter='-puvD --newuse' fi #### update-world --prepare #### # Prepare the list of packages to emerge into emerglist if [[ $1 == "--prepare" || $1 == "-p" || $1 == '--empty' || $1 == '-e' ]] then if [[ -e $emergelist ]] then echo -n "The file 'emergelist' already exists and will be overwritten. Continue anyway? (y/n) " read continue if [[ $continue == 'y' || $continue == 'yes' || $continue == 'Y' || $continue == 'Yes' || $continue == 'YES' || $continue == '' ]] then : else echo exiting. exit 0 fi else : fi emerge $parameter world > $emergelist emerge $parameter world # Make sure there are no errors in the emerge process echo if grep -q "blocks B " $emergelist then echo "WARNING: You have a blocking package. Fix this before continuing." rm -f $emergelist exit 1 elif grep -q "masked" $emergelist then echo "WARNING: You may need to unmask some packages before continuing." elif grep -q "emerge: there are no ebuilds to satisfy" $emergelist then echo "WARNING: One of the specified packages doesn't exist" rm -f $emergelist exit 1 elif grep -q 'Fetch Restriction' $emergelist then fetchrestricted=`cat $emergelist | grep ^[[]ebuild[A-Za-z\ ]*F[A-Za-z\ ]*[]] | grep -o []][a-zA-Z0-9\/.\ -]*[[] | sed 's/\]\ //' | sed 's/\ \[//'` echo "WARNING: The following packages have Fetch Restriction turned on: $fetchrestricted Please download the sources from the location specified in the ebuild to /usr/portage/distfiles/ and rerun this script." exit 1 else : fi echo " These are the packages that will be installed. If you want to alter this list, just edit the file 'emergelist' in $homedir. When finished, run 'update-world --install'" cat $emergelist | sed -n 's/^\[ebuild/&/p' | sed 's/\[ebuild.*[ A-Z]\]\ //' | sed 's/.*/\=&/' | sed -n 's/.*\(^.*USE=\"[a-zA-Z0-9_\ *%()-]*\"\).*/\1/p' | sed 's/\[.*\]//' | sed 's/(.*)//' | sed 's/\ \"/\"/' | sort -d > $emergetemp # I haven't figured out how to manage separate USE flags for slotted builds #dups=`cat $emergetemp | sed 's/-[0-9]*\.[a-zA-Z0-9_.-]*[ ]//' | sed 's/-[0-9]*[ ]//' | sed 's/\ .*//' | uniq -d | sed 's|\/|\\\/|'` #cp $emergetemp $emergetemp2 #for each in `echo $dups` #do # cat $emergetemp2 | sed "/$each/ d" > $emergetemp2 # cat $emergetemp | sed -n "s/.*\($each-[a-zA-Z0-9_.-]*\).*/\1\ 'Sorry, cannot handle USE flags for slotted builds yet'/p" >> $emergetemp2 # cat $emergetemp2 | sort -d > $emergetemp #done #### Format various parts: #packageswithversions sed 's/[ ].*//' < $emergelist #packageswithoutversions for each in `sed 's/[ ].*//' < $emergelist do equery w $each | sed -n 's/.*\/\([[:upper:][:lower:]0-9-]*\/[[:upper:][:lower:]0-9+_-]*\)\/[[:upper:][:lower:]0-9_.+-]*$/\1/p' >> $packageswithoutversions echo -n '.' done #duplicateswithversionsanduses for each in `cat $packagesonly | uniq -d` do grep $each `sed -n 's/\(.*\)\ USE=\"\([[:upper:][:lower:]0-9%*_ -]*\)\".*/\1\2/p' < $emergelist` >> $duplicateswithversionsanduses done #### Delete duplicates from the emergelist for each in `cat $duplicateswithversionsanduses | sed 's/[ ].*//' | sed 's|\/|\\\/|'` do sed "/$each/d" -i packageswithversions done #### cp $emergetemp $emergelist rm -f $failedlist exit 0 #### update-world --install #### # Install the packages listed in emergelist elif [[ $1 == "--install" || $1 == "-i" ]] then # Manage USE flag changes echo -n Checking use flags for each in `cat $emergelist | sed 's/[ ]/#/g'` do usepackage=`echo $each | sed 's/#.*$//' | sed 's/-[0-9]*\..*$//' | sed 's/-[0-9]*$//' | sed 's/=//'` #usepackage2=`echo $each | sed 's/#.*$//' | sed 's/#/\ /g'` flags=`echo $each | sed 's/#/\ /g' | sed -n 's/.*\(USE=\"[a-zA-Z0-9_\ *%()-]*\"\).*/\1/p' | sed 's/[ ]$//' | sed 's/USE=//' | sed 's/[*%]//g' | sed 's/[ ]*\"//g'` oldflags=`cat $emergetemp | sed 's/-[0-9]*\.[a-zA-Z0-9_.-]*[ ]//' | sed 's/-[0-9]*[ ]//' | grep "$usepackage " | sed 's/#/\ /g' | sed -n 's/.*\(USE=\"[a-zA-Z0-9_\ *%()-]*\"\).*/\1/p' | sed 's/[ ]$//' | sed 's/USE=//' | sed 's/[*%]//g' | sed 's/[ ]*\"//g'` if [[ $flags == $oldflags ]] then echo -n . else usepackagesed=`echo $usepackage | sed 's|\/|\\\/|'` cat $packagedotuse | sed "/$usepackagesed\ / d" > $packagedotuse echo $usepackage $flags >> $packagedotuse echo -n '#' fi done echo 'done!' # Verify that the emergelist doesn't produce errors #cat $emergelist | sed 's/\ .*//' > $emergelist cat $emergelist | xargs emerge -p > $emergetemp if grep -q "blocks B " $emergetemp then echo "WARNING: You have a blocking package. Fix this before continuing." rm -f $emergetemp exit 1 elif grep -q "masked" $emergelist then echo "WARNING: You need to unmask some packages before continuing." rm -f $emergetemp exit 1 elif grep -q "emerge: there are no ebuilds to satisfy" $emergelist then echo "WARNING: One of the specified packages doesn't exist" rm -f $emergetemp exit 1 elif [[ -z `cat $emergelist` ]] then echo "WARNING: no 'emergelist' exists. Did you run 'update-world --prepare' first?" rm -f $emergetemp exit 1 else : fi # Until loop to make portage continue until finished increment=0 until [[ $increment == 1 ]] do cat $emergelist | xargs emerge -1 # Detect which packages have been successfully emerged and remove from the queue installed=`tac /var/log/emerge.log | sed '/Started\ emerge/,$d' | grep "completed emerge" | sed 's/^.*)\ //' | sed 's/\ to.*//'` for each in $installed do eachsed=`echo $each | sed 's|\/|\\\/|'` if [[ -z `cat $emergelist | grep $each` ]] then eachsed=`echo $eachsed | sed 's/\-[0-9].*//'` else : fi cat $emergelist | sed "/$eachsed/d" > $emergetemp mv $emergetemp $emergelist done # See if all packages have been emerged if [[ -z `cat $emergelist` ]] then increment=1 else : fi if [[ $increment == 0 ]] then errorlog=`ls -t $portlogdir | sed '2,$d'` if [[ -n `cat $portlogdir/$errorlog | grep 'signal 2'` ]] then echo " *** User hit 'Control-C' ... exiting. " exit 1 else : fi sleep 5 echo echo "*** Hit Control-C to exit, or just wait to continue with emerge." echo sleep 10 echo "*** Continuing with emerge." # Detect failed emerge and add to failedlist, remove failed package from emergelist failedpkg=`tac /var/log/emerge.log | sed '1d' | sed '/terminating/,$d' | sed '/completed\ emerge/,$d' | grep ">>>" | sed 's/.*)\ //' | sed 's/\ to.*//'` if [[ -n `tail -n 1 $failedlist | grep $failedpkg` ]] then echo "The failed package $failedpkg could not be merged and must be successfully installed before continuing." exit 1 else : fi echo $failedpkg >> $failedlist failedpkgsed=`echo $failedpkg | sed 's|\/|\\\/|'` cat $emergelist | sed "/$failedpkgsed/d" > $emergetemp mv $emergetemp $emergelist ln -s $portlogdir/$errorlog $logdir/${errorlog}_error-log echo echo echo "*** $failedpkg compile failed, skipping." # Is there a package in 'emergelist' that requires the failed pkg as a dep? If so, remove it too. failedpkgdeps=`echo $failedpkg | sed 's/-[0-9].*//'` deps=`equery depends $failedpkgdeps | sed '/^\[/d' | sed 's/-[0-9].*//'` for each in `echo $deps` do if [[ -n `cat $emergelist | grep "$each"` ]] then if [[ -n `emerge -p $each | grep "$failedpkg"` ]] then each2=`cat $emergelist | grep $each | sed 's/\=//'` echo "$each2 (depends on $failedpkg)" >> $failedlist eachsed=`echo $each | sed 's|\/|\\\/|'` cat $emergelist | sed "/$eachsed/d" > $emergetemp mv $emergetemp $emergelist echo "*** $each depends on $failedpkg, skipping." else : fi else : fi done echo "*** Continuing with emerge world" else : fi done # if improper argument is given to 'update-world' display the help text else echo "update-world 1.7 count_zero 2006 licensed under the GPLv2 Description: This is a bash script to automate the 'emerge -uD world' process. Its purpose is to make updating the system as hands-free as possible. It does this by automatically recovering from a failed emerge and moving on to the next one, presenting the user with a list of failed packages once the update has finished. No more 'babysitting' the update process! Just start it, and deal with any failed packages at the end. Log files from the failed emerges are saved in the log directory. The default program directory is ~/.update-world ***NEW*** You can now manage your USE flags by modifying the flags listed next to the package names in the emergelist after running 'update-world --prepare' or 'update-world --empty' (just add or remove the '-'). Only those values you change will be added to your package.use file. Usage: update-world [--prepare | -p ] Prepares a list of packages produced by 'emerge -puvD world' and outputs them to the file 'emergelist' in the current directory. Add, remove, or change the version of any of the packages or USE flags listed in this file with your favorite editor before continuing. update-world [--empty | -e ] Same as --prepare except it performs 'emerge -e world' to recompile all packages on a system. update-world [--install | -i ] Begins the emerge process based upon the 'emergelist' file created from running 'update-world --prepare'. Once finished, failed builds will be saved in the file 'failedlist' in the current directory. " exit 1 fi echo "Congratulations! 'emerge world' complete." echo # All done! display the failed packages, if any if [[ -z `cat $failedlist` ]] then echo "All packages emerged successfully" else echo "These packages couldn't be merged due to compile errors:" echo cat $failedlist echo echo "Look in $logdir/ for the portage log files of the failed builds. Check bugs.gentoo.org or the Gentoo Forums for help." fi exit 0