Restoring package.use from currently installed packages

So I screwed up while re-installing my Gentoo system. Backup was not set, and I had not been committing my /etc/portage in a while as it kept changing. I was in the process of migrating my single-file package.use to a directory, and I made a mistake: end result, my package.use was gone, and any backups of it were too old to be relevant.

So, time to rebuild the file from scratch!

First off, emerge -pvO [package] will tell Portage to pretend to reinstall a package, but assuming all dependencies are fulfilled. This is great as Portage doesn’t waste any time trying to reconcile the default USE flags with your installed ones. It just shows you which USE flags it will enable (i.e. your currently installed package has it disabled) or disable (i.e. it’s currently enabled) for this package, marked with a *.
So this is the tool we need to figure out the USE flags to enable/disable for each package in our package.use.

Now we need to do this for all installed packages. Portage stores state about installed packages in /var/db/pkg, neatly sorted by category and package with version number.
So all that’s needed is to iterate over these and grab the USE flags to enable/disable from the output of emerge -pvO.

For some packages, Portage will throw errors, e.g. if a package only has Python 2.7 as PYTHON_SINGLE_TARGET but you have selected Python 3.x in your make.conf. This is why you can’t do emerge -pvO $(printf ' =%s' */*): Portage will check for errors before printing the list of packages with USE flags. You need to iterate over all packages one by one, and later on update package.use to fix any errors.

At this point we’ve got a list of packages with USE changes displayed among unchanged USE flags. We then only need to filter these to only show USE changes (and other variables too, e.g. PYTHON_TARGETS). After that it’s just a matter of formatting the output so we can directly create a package.use file from it.

Without further ado, the script.
Pardon the wonky formatting and lack of comments, it used to be a one-liner:

#!/bin/bash

cd /var/db/pkg || exit 2
files=(*/*)
for p in "${@:-${files[@]}}"
do
    vars="$(emerge -pvO1 ="$p" | sed -n -e 's/[^=]* \([^= ]\+=".*"\).*/\1/p')" || exit 1
    [[ -n "$vars" ]] && printf '=%s %s\n' "$p" "$vars"
done | sed -n -e '/\*/ p' | while read -r atom rest
do
    printf %s/%s%s $(qatom "$atom" | cut -d' ' -f 1,2,5)
    grep -o '[^ =]\+="[^"]\+"' <<<"$rest" | sed -e 's/\([^=]\+\)="\([^"]\+\)"/\1 \2/' | while read -r type allflags
    do
        flags=()
        while read -r flag
        do
            [[ "$flag" == -* ]] && flags+=("${flag:1:-1}") || flags+=("-${flag::-1}")
        done < <(grep -o '[^ *]\+\*' << 0 )) && {
            [[ "$type" != USE ]] && printf ' %s:' "$type"
            printf ' %s' "${flags[@]}"
        }
    done
    printf ' # %s\n' "$atom"
done | sort

It prints a package.use file to stdout, and Portage errors (that will need to be fixed) to stderr.
Note that it doesn’t handle slots, so you’ll need to scour the output for duplicate package names with different flags.

Leave a Reply

Your email address will not be published. Required fields are marked *