Search:  
Gentoo Wiki

TIP_Speeding_up_portage_with_tmpfs

This article is part of the Tips & Tricks series.
Terminals / Shells Network X Window System Portage System Filesystems Kernel Other

Contents

Introduction

Compiling from source takes a long time. Tweaking around let me compile xorg-x11 (-minimal!) within about 15 minutes on 1800+ AMD CPU

How is that possible?

When emerging a package, Portage creates many temporary files in ${PORTAGE_TMPDIR} (default: /var/tmp) and transfers a lot of temporary data to/from the filesystem. With a tmpfs mounted on ${PORTAGE_TMPDIR}, the temporary data is kept in RAM, which is much faster than the hard disk. This speeds up the compilation, avoids hard disk fragmentation and avoids hard disk wearing.

Requirements

One should have at least 512 MB RAM to emerge a Desktop Environment like KDE or GNOME. Some especially troublesome packages such as Firefox require even more memory.

Kernel support for tmpfs is also required.

File systems -> Pseudo filesystems -> Virtual memory file system support (former shm fs)

tmpfs size requirements

If you have a swap partition activated, then your system will fallback to the the swap space when RAM memory is filled up. A cap of about 900 MB mounted tmpfs ("MEMSIZE=900") should suffice for most builds.

A 300 MB tmpfs is not enough to compile:

450 MB is not enough to compile:

500 MB is enough for:

Compiling www-client/mozilla-firefox-3.0 with no USE flags enabled and an empty LINGUAS variable took 662 MiB.

850 MB is not enough to compile:

850 MB is enough for:

Compiling sys-devel/gcc-4.3.1-r1 with USE flags fortran gcj gtk mudflap openmp enabled took 1606 MiB

Greater than 1.5 mounted GB is needed for wxGTK. You may also need to adjust the number of inodes.

It is possible to compile KDE in 350 MB if you compile X11 and QT seperately.

openoffice needs a lot of memory, and at least with 3.4G mounted, 2G physical, its 10 min slower than without tmpfs.

With big applications there is also problem with number of inodes. One inode = one file and source code have big number of small files. For big applications you must increase number of inodes.

Set-up

As root, run this:

Code: mounting tmpfs

for 50% of your RAM:

mount -t tmpfs tmpfs /var/tmp/portage

for about 850 MB of your RAM and one million inodes:

mount -t tmpfs tmpfs -o size=850M,nr_inodes=1M /var/tmp/portage


Check if it's mounted with

df -h | grep tmpfs

You can also mount tmpfs at boot by adding this to /etc/fstab :

none                    /var/tmp/portage   tmpfs  size=1000M,nr_inodes=1M         0 0

You can use bigger size, if you have big swap. On my machine I have 5 GB of swap, one 1 GB tmpfs mounted in /var/tmp/portage and 3 GB in /tmp. Tmpfs swapping is very fast.

Testing

Well, emerge something!

# emerge xorg-x11

Use genlop to check whether it's faster or not.

Code: Example (from my 1400 centrino celeron)
tmpfs:???
USE="=???"
emerge genlop -n
genlop -t xorg-x11
* x11-base/xorg-x11

Wed Apr  6 17:33:07 2005 >>> x11-base/xorg-x11-6.8.2-r1
  merge time: 7 minutes and 9 seconds.
Code: Example (from a 2400 Athlon XP)
tmpfs: 1024M
USE="-3dfx -3dnow +bitmap-fonts -cjk -debug -dlloader -dmx -doc -font-server -insecure-drivers \
     +ipv6 -minimal -mmx +nls -nocxx +opengl +pam -sdk -sse -static +truetype-fonts \
     +type1-fonts (-uclibc) -xprint +xv"
emerge genlop -n
genlop -t xorg-x11
* x11-base/xorg-x11

Fri Feb 10 10:45:02 2006 >>> x11-base/xorg-x11-6.8.2-r6
       merge time: 35 minutes and 19 seconds.

Script

A small script that mounts tmpfs for you. Go to any binary-dir on your $PATH, like /usr/local/bin, and create temerge with your $EDITOR.

FIXME: setuping the /etc/fstab file correctly could be easier (someone please fill in the details)

File: temerge
#!/bin/bash
MEMSIZE=850M
mounted=false
 
. /etc/init.d/functions.sh
 
mounttmpfs() {
     mount -t tmpfs tmpfs -o size=$MEMSIZE /var/tmp/portage
     mounted="true"
}

compile() {
     einfo "emerging ${*}"
          emerge ${*}
}

unmount() {
     ebegin "unmounting tmpfs"
          umount -f /var/tmp/portage
     eend $?
}

ebegin "Mounting $MEMSIZE of memory to /var/tmp/portage"
if [ -z "$(mount | grep /var/tmp/portage)" ]
then
     mounttmpfs
else
     eerror "tmpfs already mounted!"
     exit 0
fi
eend $?

compile ${*}
 
if [ -n "$mounted" ]
then
     unmount
fi

chmod u+x temerge and use temerge instead of emerge for emerging. Use emerge for fetching/pretending.

Adding bash completion

If you are accustomed to using bash completion with emerge (highly recommended), you may quickly find yourself getting annoyed that temerge doesn't have it. I managed to accomplish this (in a somewhat hackish manner) by modifying /usr/share/bash-completion/gentoo in order to create a bash completion for temerge.

Note: I put this solution together very quickly. It can almost certainly be improved (in terms of speed and script size) by removing emerge functionality that is not used in temerge (--sync, --unmerge, etc.). Use it at your own risk, it has not been heavily tested. Also, note that if /usr/share/bash-completion/gentoo is updated (I'm not sure how often this happens), this script should be updated as well. If there is an easier way to accomplish temerge completion, feel free to modify this section.
Code: Create our temerge completion script
# If you put temerge in /usr/local/bin (as recommended above), then
# the following directory is a good place for our completion script
mkdir -p /usr/local/share/bash-completion
$EDITOR /usr/local/share/bash-completion/temerge
File: /usr/local/share/bash-completion/temerge
# Gentoo Linux Bash Shell Command Completion
#
# $Id: gentoo 57 2005-05-16 06:50:02Z ka0ttic $
#
# Copyright 1999-2005 Gentoo Foundation
# Distributed under the terms of the GNU General Public License, v2 or later

# also defined in bash_completion proper however, will produce command
# not found warnings when this is only enabled "locally" so we define it
# here as well.
have()
{
    unset -v have
    PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin type $1 >&/dev/null && have="yes"
}

#
# Retrieve PORTDIR/PORTDIR_OVERLAY location from user's make.conf or, if it
# is not defined there, from make.globals.
#
_portdir()
{
    sed -n -e '/^PORTDIR=/ { s/^[^=]\+="\?\([^"]\+\|\S\+\).*/\1/p ; q }' \
        /etc/make.{conf,globals} 2>/dev/null

    if [[ $1 == '-o' ]] ; then
        sed -n -e '/^PORTDIR_OVERLAY=/ { s/^[^=]\+="\?\([^"]\+\|\S\+\).*/\1/p ; q }' \
            /etc/make.conf 2>/dev/null
    fi
}

# like _pkgname but completes on package names only (no category)
_pkgname_only()
{
    local i pd
    local cur="$1"
    shift
    local dir="$@"

    COMPREPLY=($(compgen -W "$(\
        for pd in $dir ; do \
            builtin cd ${pd}; \
            for i in *-*/${cur}*; do \
                [[ -d ${i} ]] && { local x=${i##*/} ; echo ${x%-[0-9]*}; } \
            done ; \
        done)" -- ${cur}))
}

#
# This function completes package names.
#
# usage: pkgname <mode> <current-directory>
#
# Where mode is one of:
#   -A  Search all available packages (except for those in the overlays)
#   -I  Only search the installed packages
#
# TODO: Look at breaking this function out and making it a "universal"
#       category/package name completion function.
#
_pkgname()
{
    local mode cur portdir only
    mode="$1"
    cur="$2"
    portdir=$(_portdir -o)
    # Ignore '=' at the beginning of the current completion
    [[ ${cur:1:1} == "=" ]] && cur=${cur:2}
    [[ ${cur:0:1} == "=" ]] && cur=${cur:1}
    case $mode in
	-I)
	    # Complete either the category or the complete package name
	    if [[ $cur == */* ]]; then
		COMPREPLY=($(builtin cd /var/db/pkg; compgen -W "$(compgen -G "$cur*" )" -- $cur))
	    else
		COMPREPLY=($(builtin cd /var/db/pkg; compgen -W "$(compgen -G "$cur*" -S /)" -- $cur))
	    fi
	    # We may just have finished completing the category.
	    # Make sure there isn't anything more to complete now.
	    if [[ ${#COMPREPLY[@]} == 1 ]]; then
		COMPREPLY=($(builtin cd /var/db/pkg; compgen -W "$(compgen -G "$COMPREPLY*")" -- $cur))
	    fi

            if [[ -z "${COMPREPLY}" ]] ; then
                only=1
                _pkgname_only ${cur} /var/db/pkg
            fi
	    ;;
	-A)
	    # Complete either the category or the complete package name
	    if [[ $cur == */* ]]; then
	        # Once the category has been completed, it's safe to use ${portdir}
	        # to continue completion.
                COMPREPLY=($(\
                    for pd in ${portdir} ; do \
                        builtin cd ${pd}; \
                        compgen -W "$(compgen -G "${cur}*")" -- "${cur}" ; \
                    done))
	        # When we've completed most of the name, also display the version for
	        # possible completion.
	        if [[ ${#COMPREPLY[@]} -le 1 || ${cur:${#cur}-1:1} == "-" ]] && 
                    [[ ${cur} != */ ]]; then
		    # The portage cache is appropriate to complete specific versions from.
		    COMPREPLY=(${COMPREPLY[@]} $(\
                        for pd in ${portdir} ; do \
                            if [[ -d ${pd}/metadata/cache ]] ; then \
                                builtin cd ${pd}/metadata/cache; \
                                compgen -W "$(compgen -G "${cur}*")" -- "${cur}" ; \
                            fi ; \
                        done))
	        fi
	    else
		COMPREPLY=( $(compgen -W "$(\
                    for pd in ${portdir} ; do \
                        if [[ -d ${pd}/metadata/cache ]] ; then
                            builtin cd ${pd}/metadata/cache; \
                            compgen -G "$cur*" -S / ; \
                        fi ; \
                    done)" -- $cur) )
                if [[ ${#COMPREPLY[@]} == 1 ]]; then
		    COMPREPLY=($(compgen -W "$(\
                    for pd in ${portdir} ; do \
                        if [[ -d ${pd}/metadata/cache ]] ; then
                            builtin cd ${pd}/metadata/cache; \
                            compgen -G "$COMPREPLY*" ; \
                        fi ; \
                    done)" -- $cur))
	        fi
	    fi

            if [[ -z "${COMPREPLY}" ]] ; then
                only=1
                _pkgname_only ${cur} ${portdir}
            fi
	    ;;
	*)
	    # Somebody screwed up! :-)
	    ;;
    esac
    # 'equery' wants an '=' in front of specific package versions.
    # Add it if there is only one selected package and it isn't there already.
    if [[ ${#COMPREPLY[@]} == 1 && ${COMP_WORDS[COMP_CWORD]:0:1} != "=" ]]
    then
        [[ -z "${only}" ]] && COMPREPLY=("="$COMPREPLY)
    fi
}

#
# This is an helper function for completion of  "-o <list>" / "--option=<list>"
# kind of command lines options.
# 
# Usage: _list_compgen <current> <sep> <item1>[<sep><item2> ...]
# - <current>: what we have so far on the command line
# - <sep>: the separator character used in lists
# - <itemN>: a valid item
# Returns: the function outputs each possible completion (one per line),
# and returns 0. Typical usage is COMPREPLY=($(_list_compgen ...)).
#
# Note: items must not contain the <sep> character (no backslash escaping has 
# been implemented).
#
_list_compgen()
{
	# Read the three parameters. 
	local current="${1}" ; shift
	local sep="${1}" ; shift
	local items="${*}"

	# This is the maximum number of "<current><sep><other_item>" possible
	# completions that should be listed in case <current> is a valid list.
	# Setting it to a negative value means "no bound" (always list everything).
	# Setting it to 0 means "never list anything" (only suggest <sep>).
	# Setting it to a positive value N means "list up to N possible items, and
	# only suggest <sep> if there are more".
	# It is probably not worth a parameter, thus it will defaults to my 
	# prefered setting (1) if not already defined in the environment.
	local max_others_number=${max_others_number:-1}

	# Save IFS. The <sep> character will be used instead in the following.
	local saved_IFS="${IFS}"
	IFS="${sep}"

	# Split the current items list in two parts:
	# - current_item is the last one (maybe partial or even empty)
	# - prefix_item are items are the previous ones
	local current_item="${current##*${sep}}"
	local prefix_items="${current%${current_item}}"

	# Iterate through valid items to recognize those that are:
	# - partial matches of the <current_item>
	# - already used in the list prefix
	# - not used in the list prefix, and not an exact match of <current_item>
	# Also check whether the <current_item> is exactly a valid one.
	local matching_items
	local other_items
	local exact_match
	local my_item
	for my_item in ${items} ; do
		if [[ "${sep}${prefix_items}${sep}" == *"${sep}${my_item}${sep}"* ]] ; then
			# The item has already been used in the list prefix: ignore it.
			continue
		elif [[ "${my_item}" == "${current_item}" ]] ; then
			# The item _exactly_ matches the <current_item>: that means that we
			# will have to suggest some more items to add behind.
			exact_match=1
		elif [[ "${my_item}" == "${current_item}"* ]] ; then
			# The item matches the <current_item>: it will be a possible
			# completion. It will also be a possible additional item in case of
			# exact match.
			matching_items="${matching_items}${sep}${my_item}"
			other_items="${other_items}${sep}${my_item}"
		else
			# The item neither matches the <current_item> nor has been already
			# used: it will only be a possible additional item in case of exact
			# match.
			other_items="${other_items}${sep}${my_item}"
		fi
	done
	matching_items="${matching_items#${sep}}"
	other_items="${other_items#${sep}}"

	# Takes care of the case where <current_item> is not exactly valid but
	# there is only one matching item: force this completion, and handle it
	# just as an exact match.
	if [[ -z "${exact_match}" ]] \
	&& [[ "${matching_items}" != *"${sep}"* ]] ; then
		exact_match=1
		current="${current%${current_item}}${matching_items}"
		current_item="${matching_items}"
		matching_items=""
		other_items="${sep}${other_items}${sep}"
		other_items="${other_items/${sep}${current_item}${sep}/${sep}}"
		other_items="${other_items#${sep}}"
		other_items="${other_items%${sep}}"
	fi

	# List all possible completions. They are stored in an array.
	# XXX: maybe if should be COMPREPLY directly? (with no output at the end)
	local my_compreply=()
	local i=0
	if [[ -n "${exact_match}" ]] ; then
		# Found an exact match? Then add "<current>".
		my_compreply[${i}]="${current}"
		let i++
	fi
	if [[ -n "${matching_items}" ]] ; then
		# Found some matching items?
		# Then add "<prefix_items><matching_item>".
		for my_item in ${matching_items} ; do
			my_compreply[${i}]="${prefix_items}${my_item}"
			let i++
		done
	fi
	if [[ -n "${exact_match}" ]] \
	&& [[ -n "${other_items}" ]] ; then
		# Found an exact match and some other possible items remain?
		# First, count them:
		local count_others=0
		for my_item in ${other_items} ; do
			let count_others++
		done
		# Then decide how to behave depending on the max_others_number setting:
		if (( max_others_number < 0 )) \
		|| (( count_others <= max_others_number )) ; then
			# List the possible "<current><sep><other_item>" completions.
			for my_item in ${other_items} ; do
				my_compreply[${i}]="${current}${sep}${my_item}"
				let i++
			done
		else # Only suggest adding the <sep> character.
			my_compreply[${i}]="${current}${sep}"
			let i++
		fi
	fi

	# Restore IFS.
	IFS="${saved_IFS}"

	# Output the array of possible completions and returns.
	local j=0
	while (( i > j )) ; do
		echo ${my_compreply[$j]}
		let j++
	done
	return 0
}

#
# temerge completion command
#
have temerge && {
_temerge()
{
    local c cur prev curword numwords opts cond prepend
    local words stopre stophere i x
    local action actionpos actionre sysactions eactions pkgpos
    local version_mode searchdesc_mode help_mode resume_mode
    local portdir=$(_portdir -o)
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    numwords=${#COMP_WORDS[*]}
    curword=${COMP_CWORD}
    actionre='@(-?([A-Za-z]*)[CPcis]*|@(metadata|s@(y@(nc|stem)|earch)|regen|@(unmerg|prune)e|world|@(@(dep)@(clean)|@(in@(fo|ject)))))'
    stopre=' @(?(--)depclean|?(--)info|?(--)metadata|regen|sy@(nc|stem)|world|--@(resume|s@(kipfirst|ync)))'
    opts=''

    if [[ ${prev} == '>' || ${prev} == '<' ]] ; then
        COMPREPLY=($(compgen -f -- ${cur}))
        return 0
    fi

    # find action
    for x in ${COMP_LINE} ; do
        if [[ ${x} == ${actionre} ]] ; then
            action=${x}
            break
        fi
    done

    if [[ -n "${action}" ]]; then
	for ((i = 0; i < ${numwords}; i++ )); do
	    if [[ ${COMP_WORDS[${i}]} == "${action}" ]]; then
		actionpos=${i}
		pkgpos=$((actionpos + 1))
		break
	    fi
	done
        
        if [[ ${action} == -* && ${action} != --* ]] ; then
	    case "${action}" in
		-*C*) action='unmerge' ;;
		-*P*) action='prune' ;;
		-*c*) action='clean' ;;
		-*i*) action='inject' ;;
		-*s*) action='search' ;;
	    esac
	fi
    else
	for ((i = 1; i < ${numwords}; i++ )); do
	    if [[ ! "${COMP_WORDS[$i]}" == -* ]]; then
		pkgpos=${i}
		break
	    fi
	done
	[[ -n "${pkgpos}" ]] || pkgpos=${numwords}
    fi
	    
    # Check for special cases.
    [[ ${COMP_LINE} == *" "-@(V|-version)* ]] && version_mode=1
    [[ ${COMP_LINE} == *" "-@(S|-searchdesc)* ]] && searchdesc_mode=1
    [[ ${COMP_LINE} == *" "-@(h|-help)* ]] && help_mode=1
	    
    # Handle special cases.
    if [[ "${action}" == 'search' ]] || [[ -n "${searchdesc_mode}" ]] || \
        [[ -n "${version_mode}" ]] || [[ "${action}" == 'metadata' ]]
    then
        unset COMPREPLY
	return 0
    elif [[ -n "${help_mode}" ]]; then
	unset COMPREPLY
	[[ ${curword} -eq 2 ]] && COMPREPLY=($(compgen -W 'config sync system' -- ${cur}))
	return 0
    fi
	    
    # Complete on options.
    if [[ "${cur}" == -* ]]; then
        # If a resume option was specified, it needs special handling.
        [[ ${COMP_LINE} == *--@(resume|skipfirst)* ]] && resume_mode=1
	
        if [[ -n "${resume_mode}" ]]; then
	    if [[ "${cur}" == --* ]]; then
		opts="--ask --pretend --resume --skipfirst"
	    elif [[ "${cur}" == -* ]]; then
                [[ ${COMP_LINE} == *--@(ask|pretend)* ]] && ask_premode=1
                [[ -n "${ask_premode}" ]] && opts="-a -p"
	    fi
	elif [[ "${cur}" == --* ]]; then
	    # Complete on long options.
	    opts="--ask --autoclean \
		--buildpkg --buildpkgonly \
		--changelog --clean --columns \
		--debug --deep \
		--emptytree \
		--fetch-all-uri --fetchonly \
		--getbinpkg --getbinpkgonly \
		--newuse --noconfmem --nodeps --noreplace --nospinner
		--oneshot --onlydeps \
		--pretend \
		--quiet \
		--sync \
		--tree \
		--update --upgradeonly --usepkg --usepkgonly \
		--verbose --depclean --info --search"
	    if [[ ${curword} -eq 1 ]] && [[ ${numwords} -eq 2 ]] ; then
		opts="${opts} --help --resume --searchdesc --version"
	    fi
	elif [[ "${cur}" == -* ]]; then
	    # Complete on short options.
	    opts="-B -D -G -K -O -U -a -b -d -e -f -g -k -l -n -o -p -q -t -u -v"
	    if [[ ${curword} -eq 1 ]] && [[ ${numwords} -eq 2 ]] ; then
		opts="${opts} -h -S -V"
	    fi
	    if [[ -z "${action}" ]] && [[ ${curword} -eq $((pkgpos - 1)) ]] ; then
		opts="${opts} -C -P -c -i -s"
	    fi
	fi
		    
        COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
	
        # NOTE: This slows things down!
        # (Adapted from bash_completion by Ian Macdonald <ian@caliban.org>)
	# This removes any options from the list of completions that have
	# already been specified on the command line.
	COMPREPLY=($(echo "${COMP_WORDS[@]}" | \
            (while read -d ' ' i; do
		[[ "${i}" == "" ]] && continue
		# flatten array with spaces on either side,
		# otherwise we cannot grep on word boundaries of
		# first and last word
		COMPREPLY=" ${COMPREPLY[@]} "
		# remove word from list of completions
		COMPREPLY=(${COMPREPLY/ ${i%% *} / })
	    done
	    echo ${COMPREPLY[@]})))

	return 0
    fi
	
    # Stop completion if a special case is encountered.
    if [[ ${COMP_LINE} == *${stopre}* ]] ; then
        unset COMPREPLY
	return 0
    fi

    # Complete on installed packages when unmerging.
    if [[ "${action}" == 'unmerge' ]]; then
	if [[ -n "${cur}" ]] ; then
	    if [[ "${cur}" == */* ]]; then
		words=$(builtin cd /var/db/pkg; compgen -G "${cur}*")
	    else
		words=$(builtin cd /var/db/pkg; compgen -S '/' -G "${cur}*")

                local n=0
                for i in ${words} ; do
                    [[ ${i} == ${cur}* ]] && n=$((n+1))
                done

                if [[ ${n} -eq 1 ]] ; then
                    words="$(builtin cd /var/db/pkg ; compgen -G "*-*/*")"
                fi
	    fi
            COMPREPLY=($(for i in ${words} ; do \
                            [[ ${i} == ${cur}* ]] && echo ${i} ; \
                        done))
	else
	    COMPREPLY=($(builtin cd /var/db/pkg ; compgen -S '/' -G "*-*"))
	fi

        [[ -z "${COMPREPLY}" ]] && _pkgname_only ${cur} /var/db/pkg
        return 0
    fi
	
    # Check for conditional.
    cond="${cur%%[A-Za-z0-9]*}"
    cur="${cur:${#cond}}"
    if [[ ${cond:0:1} == "'" || ${cond:0:1} == '"' ]] ; then
        prepend="-P ${cond:1}"
        c="${cond:1}"
    else
        c="${cond}"
    fi
    
    # Handle cases where a conditional is specified.
    if [[ -n "${cond}" ]]; then
	if [[ -n "${cur}" ]]; then
            if [[ ${cur} == */* ]]; then
                if [[ ${cur} == *-[0-9]* ]] ; then
                    words="$(\
                        for pd in ${portdir} ; do \
                            builtin cd ${pd} ; \
                            local cat="${cur%/*}" ; \
                            local pkg="$(echo ${cur%-[0-9]*})" ; \
                            pkg="${pkg##*/}" ; \
                            for x in ${cat}/${pkg}/*.ebuild ; do \
                                [[ -f ${x} ]] || continue ; \
                                x="${x/${pkg}\/}" ; \
                                echo "${x%*.ebuild}" ; \
                            done ; \
                        done)"
                else
		    words="$(\
                    for pd in ${portdir} ; do \
                        builtin cd ${pd}; \
                        compgen -X "*metadata.xml" -G "${cur}*" -- ${cur} ; \
                    done)"
                fi

                local w
                for x in $words ; do
                    w="${x}\n${w}"
                done

                words=$(echo -ne ${w} | sort | uniq)
                COMPREPLY=( ${words} )
		
                # Complete on the specific versions (if appropriate).
                if [[ ${#COMPREPLY[@]} -le 1 ]] ; then
		    COMPREPLY=($(\
                        for pd in ${portdir} ; do \
                            if [[ -d ${pd}/metadata/cache ]] ; then
                                builtin cd ${pd}/metadata/cache; \
                                compgen ${prepend} -G "${cur}*" -- "${cur}" ; \
                            else \
                                builtin cd ${pd} ; \
                                local cat="${cur%/*}" ; \
                                local pkg="$(echo ${cur%-[0-9]*}*)" ; \
                                pkg="${pkg##*/}" ; \
                                for x in ${cat}/${pkg}/*.ebuild ; do \
                                    [[ -f "${x}" ]] || continue ; \
                                    x="${x/${pkg}\/}" ; \
                                    if [[ ${cond:0:1} == "'" ]] || \
                                        [[ ${cond:0:1} == '"' ]] ; then 
                                        echo "${c}${x%*.ebuild}" ; \
                                    else
                                        echo "${x%*.ebuild}" ; \
                                    fi ; \
                                done ; \
                            fi ; \
                        done))
                else
                    COMPREPLY=($(compgen ${prepend} -W "${words}" -- $cur))
                fi
            else
                words="$(\
                    for pd in ${portdir} ; do \
                        builtin cd ${pd} ; \
                        compgen ${prepend} -S '/' -G "${cur}*" -- "${cur}" ; \
                    done)"

                local w
                for x in words ; do
                    w="${x}\n${w}"
                done

                COMPREPLY=($(echo -e ${w} | uniq))
                [[ ${#COMPREPLY[@]} = 1 ]] && \
                    COMPREPLY=($(\
                        for pd in ${portdir} ; do \
                            builtin cd ${pd} ; \
                            compgen ${prepend} -G "${cur}*/*" -- "${cur}" ; \
                        done))
            fi
        else
	    words="$(\
                for pd in ${portdir} ; do \
                    builtin cd ${pd}; \
                    compgen -G "*-*"; \
                done)"
	    COMPREPLY=($(compgen -W "${words}" -- "${cur}"))
	fi
		
        # If all else fails, try to complete on package names without the
	# category being specified.
	if [[ -z "${COMPREPLY}" ]]; then
	    words="$(\
                for pd in ${portdir} ; do \
                    builtin cd ${pd}; \
                    for i in *-*/${cur}*; do \
                        [[ -d $i ]] && echo ${i##*/}; \
                    done ; \
                done)"

	    COMPREPLY=($(compgen ${prepend} -W "${words}" -- ${cur}))

	    if [[ ${#COMPREPLY[@]} -le 1 ]]; then
		# Now complete on the specific versions.
		words="$(\
                    for pd in ${portdir} ; do \
                        if [[ -d ${pd}/metadata/cache ]] ; then \
                            builtin cd ${pd}/metadata/cache; \
                            for i in */${cur}*; do \
                                [[ -f $i ]] && echo ${i##*/}; \
                            done ; \
                        fi ; \
                    done)"
		COMPREPLY=($(compgen ${prepend} -W "${words}" -- "${cur}"))
	    fi
	fi
	return 0
    fi
	
    # Complete on packages.
    if [[ ${COMP_CWORD} -eq 1 ]] ; then
	if [[ $numwords -le 2 ]]; then
	    sysactions=$'\n'"system"$'\n'"world"$'\n'"sync"$'\n'"metadata"
	else
	    sysactions=$'\n'"system"$'\n'"world"$'\n'"sync"
	fi
    else
	# Only allow these actions if no packages have been specified.
	#
	# TODO: This doesn't block these actions if no categories are
	#       specified. Please fix me.
	#
	#       e.g. temerge -a gentoo-dev-sources
	#
	#            will still allow system and world actions to be specified,
	#            as opposed to
	#
	#            temerge -a sys-kernel/gentoo-dev-sources
	#
	
        if [[ ! " ${COMP_LINE} " == *" "*[/]*" "* ]]; then
	    sysactions=$'\n'"system"$'\n'"world"
	else
	    sysactions=''
	fi
    fi
	
    if [[ -z "${action}" ]] && [[ ${curword} -le ${pkgpos} ]] ; then
        eactions=$'\n'"clean"$'\n'"depclean"$'\n'"inject"$'\n'"prune"$'\n'"regen"$'\n'"search"$'\n'"unmerge"
    fi
    if [[ -n "${cur}" ]] ; then
        if [[ ${cur} == virtual/* ]] ; then
            words=$(\
                for pd in ${portdir} ; do \
                    if [[ -d ${pd}/profiles ]] ; then
                        find ${pd}/profiles -name virtuals -exec \
                            sed -n -e 's|^\(virtual/[[:alnum:]]\+\).*$|\1|p' {} \; | \
                            sort -u
                    fi ; \
                done)
	elif [[ ${cur} == */* ]] ; then
	    words=$(\
                for pd in ${portdir} ; do \
                    builtin cd ${pd}; \
                    compgen -X "*metadata.xml" -G "${cur}*" ; \
                done)"${sysactions}""${eactions}"
	else
            local ww=$(\
                for pd in ${portdir} ; do \
                    builtin cd ${pd} ; \
                    compgen -S '/' -G "${cur}*"; \
                done)"${sysactions}""${eactions}"
            # complete on virtuals
            ww="${ww} $(\
                for pd in ${portdir} ; do \
                    if [[ -d ${pd}/profiles ]] ; then
                        find ${pd}/profiles -name virtuals -exec \
                            sed -n -e 's|^\(virtual/[[:alnum:]]\+\).*$|\1|p' {} \; | \
                            sort -u
                    fi ; \
                done)"

            local w
            for x in ${ww} ; do w="${x}\n${w}" ; done

            words=$(echo -e ${w} | sort -u)

            local n=0
            for i in ${words} ; do
                [[ ${i} == ${cur}* ]] && n=$((n+1))
            done

            if [[ ${n} -eq 1 ]] ; then
                words=$(for pd in ${portdir} ; do \
                            builtin cd ${pd} ; \
                            compgen -G "*-*/*" ; \
                        done)"${sysactions}""${eactions}"
            fi
        fi
        COMPREPLY=($(for i in ${words} ; do \
                        [[ ${i} == ${cur}* ]] && echo ${i} ; \
                    done))
    else
        words="$(\
            for pd in ${portdir} ; do \
                builtin cd ${pd} ; \
                compgen -S '/' -G "*-*" ; \
            done)""${sysactions}""${eactions}"
	COMPREPLY=($(compgen -W "${words}" -- ${cur}))
    fi

    # If all else fails, try to complete on package names without the
    # category being specified.
    if [[ -z "${COMPREPLY}" ]]; then
        words="$(\
            for pd in ${portdir} ; do \
                builtin cd ${pd}; \
                for i in [a-z]*-[a-z0-9]*/${cur}*; do \
                    [[ -d $i ]] && echo ${i##*/}; \
                done ; \
            done)"
	COMPREPLY=($(compgen -W "${words}" -- ${cur}))
    fi

    return 0
}
complete -o filenames -F _temerge temerge
}
Code: Create the link
ln -s /usr/local/share/bash-completion/temerge /etc/bash_completion.d/
# refresh our login session in order to get bash completion for temerge
source /etc/profile
Retrieved from "http://www.gentoo-wiki.info/TIP_Speeding_up_portage_with_tmpfs"

Last modified: Fri, 10 Oct 2008 15:18:00 +0000 Hits: 49,748