I've added a lot of new features to the newest version of update-world. It seems pretty stable (to me). Let me know if there are any problems.
Changelog v1.8:
-Cleaned up and modularized code. It should be a lot more readable now.
-Script now sources /etc/make.conf. Several portage variables are used by this script now.
-Added ability to 'emerge -p <package> | update-world. (realize that this is really only meant for updating existing packages...if you're installing a new one, you probably can't just skip a package failure, as it's likely a required dependency)
-Added interface with portage 2.1's elog capability. You can type 'update-world --log' to view build messages after updating (mesages stored in ~/update-world/<date>/notice/. Check out http://www.gentoo.org/doc/en/handbook/h ... doc_chap4. if you haven't set up elog yet.
-Added interface with revdep-rebuild. You can run it alone (update-world --revdep) to prepare a list of packages to install from revdep rebuild, or you can use it in conjunction with --install (update-world --revdep --install) to automatically run revdep-rebuild when the update is complete.
About update-world
If your experience is anything like mine, the process of updating a Gentoo system is no small endeavour. Typing 'emerge -uD world' is an almost guaranteed day or two periodically checking on the process to make sure it hasn't died because of a compile failure. I recently updated my system (680 packages !!!), which took literally 3 days because I would find that after I started the process and went to bed/work/etc., it would die within a few hours of starting it. 10 packages wouldn't compile properly, causing me to have to restart the emerge process 10 times. This is obviously not very efficient.
This nightmarish update process led me to come up with something that makes things easier. "update-world" is a bash script that controls the update process and forces portage to skip past failed builds and compile until the update is finished. Failed builds are added to a list for review once the update is finished.
The script does depend on the 'equery' command, part of app-portage/gentoolkit.
Here's how to do it:
First, make the script executable and put it somewhere in your $PATH.
Then, run the script.
Code: Select all
update-world --prepare
or
update-world --empty
or
emerge <package> | update-worldCode: Select all
update-world --installNow you can automatically run revdep-rebuild whenever you update with update-world. You can use it alone or in conjunction with --install.
Code: Select all
update-world --revdep
or
update-world --revdep --installLook at http://www.gentoo.org/doc/en/handbook/h ... doc_chap4. for more information on setting this up. These notices are stored in ~/.update-world/notice.
Code: Select all
update-world --logIf you ever need to stop the process, just hit 'Control-C' to abort the process. The update can then be restarted at any time, just where it left off, by retyping update-world --install.
I've only tested this on my machine, but it worked flawlessly, taking only 16 hours to compile all 680 packages. And the ten packages that wouldn't compile were waiting for me to fix at my leisure.
So, you can download the latest stable version of the script here: http://dixieflatline.homelinux.org/file ... date-world
Mirror 1: http://dixieflatline.homelinux.org/files/gentoo/
Mirror 2 (thanks ryker!): http://www.mindlesstechie.net/gentoo/update-world/
Featured in Gentoo Weekly Newsletter 12/04/06
http://www.gentoo.org/news/en/gwn/20061 ... letter.xml
Here's the code:
Code: Select all
#!/bin/bash
# update-world 1.8
# count_zero 2007
# 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.8: Cleaned and modularized code. Added ability to utilize revdep-rebuild if wished. Can now pipe other
# emerges into update-world. Sources /etc/make.conf. Added interfacing with elog. Some bugfixes.
# v1.7 broken.
# v1.6.1: Bugfixes, added utf-8 support
# 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
#
#
#### Help ####
# Help information
displayhelp ()
{
echo "update-world 1.8
count_zero 2007
licensed under the GPLv2
http://www.fsf.org/licensing/licenses/gpl.txt
Description:
This is a bash script to automate the 'emerge -uD world' process.
The default program directory is ~/.update-world.
Error logs are filed in ~/.update-world/<date>/log.
Ebuild notices (elogs) are filed in ~/.update-world/<date>/notice.
Usage:
update-world [ --help | -h ]
Display this help file and exit.
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 listed
in this file with your favorite editor before continuing.
update-world [ --empty | -e ]
Same as --prepare except it performs 'emerge -ev 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 program directory.
update-world [ --revdep | -r ]
Run revdep-rebuild to prepare the emergelist. Can be used alone
or in conjunction with --install.
emerge -p <package> | update-world
Let portage produce an emergelist determined by a package. This can be
useful for large packages with lots of dependencies (kde, gnome, xorg, etc.).
Once complete, run update-world --install.
update-world [ --log | -l ]
View the ebuild notices (elogs) of packages installed by update-world,
sorted by date.
"
}
#### end help ####
# Set the program directory and important variables
source /etc/make.conf
export EMERGE_DEFAULT_OPTS=""
homedir=~/.update-world
workdir="$homedir/`date +%F`"
logdir="$workdir/log"
noticedir="$workdir/notice"
mkdir -p $logdir
mkdir -p $noticedir
touch $homedir/failedlist
emergelist=$homedir/emergelist
emergetemp=$workdir/emergetemp
failedlist=$homedir/failedlist
parameter="-puvD --newuse"
#### error-check ####
# make sure there are no errors in the emerge process.
errorcheck ()
{
cat $emergelist | xargs emerge -p > $emergetemp
if [[ -n `cat $emergetemp | grep '\[blocks'` ]]
then echo "WARNING: You have a blocking package. Fix this before continuing."; exit 1
exit 1
elif grep -q "masked by:" $emergetemp
then echo "WARNING: You may need to unmask some packages before continuing."; exit 1
elif grep -q "emerge: there are no ebuilds to satisfy" $emergetemp
then echo "WARNING: One of the specified packages doesn't exist"; exit 1
elif [[ -n `cat $emergetemp | sed -n 's/^\[ebuild[[:upper:][:lower:] ]*F[[:upper:][:lower:] ]*\].*/&/p'` ]]
then fetchrestricted=`cat $emergetemp | sed -n 's/^\[ebuild[[:upper:][:lower:] ]*F[[:upper:][:lower:] ]*\].*/&/p' | sed -n 's/^\[ebuild[[:upper:][:lower:] ]*\]\ \([[:lower:][:upper:]0-9._+\/-]*\).*/\1/p'`
echo -e "\nWARNING: The following packages have Fetch Restriction turned on:\n$fetchrestricted"
for each in $fetchrestricted
do emerge =$each
done
exit 1
else :
fi
}
#### end error-check ####
#### empty ####
# parameter for '--empty' to emerge -e world
empty ()
{
parameter='-pev'
}
#### end empty
#### prepare ####
# Prepare the list of packages to emerge into emerglist
prepare ()
{
if [[ -e "$emergelist" ]]
then echo -en "The file 'emergelist' already exists and will be overwritten.\nContinue 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
errorcheck
emerge $parameter world
echo -e "\nThese are the packages that will be installed. If you want to alter this list,\njust edit the file 'emergelist' in $homedir. When finished,\nrun 'update-world --install'"
cat $emergelist | sed -n 's/^\[ebuild[[:upper:][:lower:] ]*\]\ \([[:lower:][:upper:]0-9._+\/-]*\).*/\1/p' | sed 's/.*/=&/' | sort -d > $emergetemp
cp $emergetemp $emergelist
rm -f $failedlist
}
#### end prepare ####
#### installworld ####
# Install the packages listed in emergelist
installworld ()
{
# Make sure there's an emergelist
if [[ -s "$emergelist" ]]
then :
else echo "WARNING: no 'emergelist' exists. Did you run 'update-world --prepare' first?"; exit 1
fi
errorcheck
condition=false
# set begin time
before_time="${before_time:=`date +%s`}"
# Until loop to make portage continue until finished
until [[ $condition = true ]]
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 `cat "$emergelist" | sed 's/^=//'`
do
if [[ -n `echo "$installed" | grep "$each"` ]]
then
# Checking emerge logs for any ebuild notices
einfo=""
notice="$noticedir/${each/\//:}-notice.log"
elog="${PORT_LOGDIR}/elog/`ls -lt $PORT_LOGDIR/elog | grep ${each/\//:} | sed '2,$ d' | sed 's/.*\ //'`"
if [[ -n "$PORTAGE_ELOG_SYSTEM" ]] && [[ -n "${einfo:=`cat $elog | sed '/^$/d' | sed '/^[ ]*$/d' | sed 's/.*/ * &/'`}" ]]
then
echo ------------------------ > $notice
echo -e "$each \n" >> $notice
echo "$einfo" >> $notice
echo ------------------------ >> $notice
else :
fi
# Removing successful emerges
each=`echo ${each//\//\\/} | sed 's/\-[0-9].*//'`
cat $emergelist | sed "/${each//\//\\/}/d" > $emergetemp
mv $emergetemp $emergelist
fi
done
# See if all packages have been emerged. If so, exit function "installworld"
if [[ -z `cat "$emergelist"` ]]
then condition=true
break
else :
fi
## If there are remaining packages, find why the emerge stopped
errorlog=`ls -t $PORT_LOGDIR | sed '2,$d'`
# if user-initiated "control-c" just exit.
if [[ -n `cat $PORT_LOGDIR/$errorlog | grep 'signal 2'` ]]
then echo -e "\n*** User hit 'Control-C' ... exiting.\n"
exit 1
else :
fi
# if package failed, give user chance to stop update-world
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.*//'`
#### uh-oh, stop if something's really wrong to prevent infinite emerge (tried to emerge same package again)
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
# if package added to emergelist manually doesn't have a version, make sure it's still removed if it fails
if [[ -z `cat "$emergelist" | grep "$failedpkg"` ]]
then failedpkg2=`equery which =$failedpkg | sed 's/\/[^/]*$//' | sed -n 's/.*\/\([^/]*\/[^/]*\)/\1/p'`
cat $emergelist | sed "/^${failedpkg2//\//\\/}/d" > $emergetemp
else cat $emergelist | sed "/${failedpkg//\//\\/}/d" > $emergetemp
fi
mv $emergetemp $emergelist
# link to the log file of the failed package
ln -s $PORT_LOGDIR/$errorlog $logdir/${errorlog}_error-log
echo
echo
echo "*** $failedpkg compile failed, skipping."
# Is there a package in 'emergelist' that REQUIRES the failed package as a dep? If so, remove it too.
failedpkgdeps=`equery which =$failedpkg | sed 's/\/[^/]*$//' | sed -n 's/.*\/\([^/]*\/[^/]*\)/\1/p'`
deps=`equery depends $failedpkgdeps | sed '/^\[/d' | sed 's/.*/=&/'`
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
cat $emergelist | sed "/${each//\//\\/}/d" > $emergetemp
mv $emergetemp $emergelist
echo "*** $each depends on $failedpkg, skipping."
else :
fi
else :
fi
done
if [[ -z `cat "$emergelist"` ]]
then condition=true
break
else :
fi
echo "*** Continuing with emerge world"
done
}
#### end installworld ####
#### revdep-rebuild ####
# this script can call revdep-rebuild and get it's emergelist from there.
# useful to run in conjunction with update-world --install, it will automatically
# run revdep-rebuild after the emerge -uD world is complete
revdep ()
{
rm /root/.revdep-rebuild*.?_*
revdep-rebuild --package-names --pretend | tee $emergelist
cat $emergelist | sed -n 's/^\[ebuild[[:upper:][:lower:] ]*\]\ \([[:lower:][:upper:]0-9._+\/-]*\).*/\1/p' | sed 's/.*/=&/' > $emergetemp
mv $emergetemp $emergelist
}
#### end revdep-rebuild ####
#### piped ####
# emergelist is piped from another emerge (i.e., emerge kde | update-world)
piped ()
{
echo "$piped" > $emergelist
errorcheck
cat $emergelist | sed -n 's/^\[ebuild[[:upper:][:lower:] ]*\]\ \([[:lower:][:upper:]0-9._+\/-]*\).*/\1/p' | sed 's/.*/=&/' > $emergetemp
mv $emergetemp $emergelist
cat $emergelist | xargs emerge -p
echo -e "\nThese are the packages that will be installed. If you want to alter this list,\njust edit the file 'emergelist' in $homedir. When finished, run\n'update-world --install'"
}
#### end piped ####
#### finished ####
# Display closing messages
finished ()
{
echo -e "\n\nCongratulations! 'update-world' complete."
elapsed_time
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 -e "Look in $logdir/ for the portage log files of the failed builds.\nCheck bugs.gentoo.org or the Gentoo Forums for help."
fi
if [[ -n "$PORTAGE_ELOG_SYSTEM" ]]
then echo -e "\n * To review portage elog notices, type \"update-world --log\"\n"
else echo " * To enable portage notice viewing with elog, check out http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?part=3&chap=1#doc_chap4."
fi
}
#### end finished ####
#### elapsed_time ####
# just for fun let's see how long the process takes...
elapsed_time ()
{
after_time=`date +%s`
time=$(( after_time - before_time ))
days=$(( time / 86400 ))
time=$(( time - ( days * 86400 ) ))
hours=$(( time / 3600 ))
time=$(( time - ( hours * 3600 ) ))
minutes=$(( time / 60 ))
time=$(( time - ( minutes * 60 ) ))
seconds=$time
if [[ "$days" -eq 0 ]]
then days=""
elif [[ "$days" -eq 1 ]]
then days="$days day, "
elif [[ "$days" -gt 1 ]]
then days="$days days, "
fi
if [[ "$hours" -eq 0 ]]
then hours=""
elif [[ "$hours" -eq 1 ]]
then hours="$hours hour, "
elif [[ "$hours" -gt 1 ]]
then hours="$hours hours, "
fi
if [[ "$minutes" -eq 0 ]]
then minutes=""
elif [[ "$minutes" -eq 1 ]]
then minutes="$minutes minute, and "
elif [[ "$minutes" -gt 1 ]]
then minutes="$minutes minutes, and "
fi
seconds="$seconds seconds"
echo -e "Total compiling time: $days$hours$minutes$seconds.\n"
}
#### end elapsed_time ####
#### What should the script do when invoked? ####
# I have nine functions: displayhelp, empty, prepare, errorcheck, installworld, revdep, piped, elapsed_time, and finished
arguments="$@"
# update-world --help
if [[ -n `echo "$arguments" | sed -n 's/-h/&/p'` || -n `echo "$arguments" | sed -n 's/--help/&/p'` ]]
then
displayhelp
exit 0
else :
fi
# update-world --empty
if [[ -n `echo -e \\t "$arguments" | sed -n 's/-e/&/p'` || -n `echo "$arguments" | sed -n 's/--empty/&/p'` ]]
then echo Preparing list of packages...
empty
prepare
exit 0
else :
fi
# update-world --prepare
if [[ -n `echo "$arguments" | sed -n 's/-p/&/p'` || -n `echo "$arguments" | sed -n 's/--prepare/&/p'` ]]
then echo Preparing list of packages...
prepare
exit 0
else :
fi
# update-world --install
if [[ -n `echo "$arguments" | sed -n 's/-[[:lower:]]*i/&/p'` || -n `echo "$arguments" | sed -n 's/--install/&/p'` ]]
then
installworld
check=true
else :
fi
# update-world --revdep
if [[ -n `echo "$arguments" | sed -n 's/-[[:lower:]]*r/&/p'` || -n `echo "$arguments" | sed -n 's/--revdep/&/p'` ]]
then
revdep
if [[ -s "$emergelist" ]]
then installworld
else :
fi
check=true
else :
fi
# emerge -p <package> | update-world
# piped command MUST include --pretend !!!
if [[ "$arguments" = "" ]]
then echo "No arguments supplied. If you're piping from the emerge command (emerge -p <package> | update-world), this is fine. Otherwise, cancel and type 'update-world --help' for usage information."
until [[ `echo "$piped" | wc -l` -eq 10 ]] && [[ -z `echo "$piped" | grep 'Calculating dependencies'` ]] || [[ -z `echo "${piped:-1}" | tail -n 4` ]]
do read line
piped="${piped}
$line"
done
else :
fi
if [[ -n `echo "$piped" | sed -n 's/Calculating\ dependencies/&/p'` ]]
then
piped
exit 0
else :
fi
# update-world --log
if [[ -n `echo "$arguments" | sed -n 's/-l/&/p'` || -n `echo "$arguments" | sed -n 's/--log/&/p'` ]]
then echo "Choose a date: "
select directory in `ls -Al | grep ^d | sed 's/.*\ //'`
do
if [[ -n "$directory" ]]
then date=$directory
break
else echo "invalid selection"
fi
done
rm -f $homedir/$date/notice/log
for each in `ls $homedir/$date/notice/`
do cat $homedir/$date/notice/$each >> $homedir/$date/notice/log
done
less $homedir/$date/notice/log
check=true
exit 0
else :
fi
# Check if arguments were valid
if [[ ${check:-false} == false ]]
then echo -e "Error: Invalid Command.\nRun 'update-world --help' for usage."
exit 1
else :
fi
# done! Display closing text
finished
exit 0
[Update] 9/13/06 (version 1.1)
Fixed script to prevent errors when adding a package without specifying a version number to 'emergelist'.
[Update] 10/27/06 (version 1.2)
Script now automatically detects the portage log directory instead of just assuming /var/log/portage.
[Update] 11/22/06 (version 1.4) I guess my edit didn't take yesterday for v1.3...
Added --newuse and --oneshot to emerge variables.
[Update] 11/22/06 (version 1.5)
Now handles 'fetch restriction' properly, prompting user to download sources before continuing.
[Update] 12/06/06 (version 1.6)
Script preserves colored emerge output. Fixed version numbering in help output.
[Update] 12/09/06 (version 1.7)
Cleaned up the code a little, fixed some bugs. Check out the new feature for managing USE flags!
Edit: seems to be broken
[Update] 12/10/06 (version 1.6.1)
bugfixes and utf-8 support.
[Update] 2/16/07 (version 1.
Cleaned and modularized code. Added ability to utilize revdep-rebuild if wished. Can now pipe other
emerges into update-world. Sources /etc/make.conf. Added interfacing with elog. Some bugfixes.




