5 # bash replacement for the original euse by Arun Bhanu
6 # Author: Marius Mauch <genone@gentoo.org>
7 # Jared Hancock (Signigicant rewrite for package.use support)
8 # Licensed under the GPL v2
13 EPREFIX=${EPREFIX:-$(portageq envvar EPREFIX)}
15 USR_SHARE_PORTAGE="${EPREFIX}/usr/share/portage"
17 # define error functions so they can be used immediately
29 echo -e "WARNING: ${*}"
32 # /etc/make.conf can now exist in /etc/portage/make.conf, prefer it over
33 # /etc/make.conf for changes. Since this will only be used for modifying
34 # the USE variable, we need to make sure the one we pick is the one with
35 # the USE variable defined.
36 if [[ -n $(grep '^USE="' "${ETC}/portage/make.conf" 2>/dev/null) ]]; then
37 MAKE_CONF_PATH="${ETC}/portage/make.conf"
38 elif [[ -e "${ETC}/make.conf" ]]; then
39 MAKE_CONF_PATH="${ETC}/make.conf"
41 fatal "make.conf does not exist"
43 MAKE_CONF_BACKUP_PATH="${MAKE_CONF_PATH}.euse_backup"
45 # /etc/make.globals has been moved to /usr/share/portage/config/make.globals
46 if [ -e "${USR_SHARE_PORTAGE}/config/make.globals" ]; then
47 MAKE_GLOBALS_PATH="${USR_SHARE_PORTAGE}/config/make.globals"
49 MAKE_GLOBALS_PATH="${ETC}/make.globals"
52 # /etc/make.profile or /etc/portage/make.profile, if /etc/make.profile exists, it will be used
53 if [ -e "${ETC}/make.profile" ]; then
54 MAKE_PROFILE_PATH="${ETC}/make.profile"
55 elif [ -e "${ETC}/portage/make.profile" ]; then
56 MAKE_PROFILE_PATH="${ETC}/portage/make.profile"
58 fatal "make.profile does not exist"
60 PACKAGE_USE_PATH=${ETC}/portage/package.use
62 [ -z "${MODE:-}" ] && MODE="showhelp" # available operation modes: showhelp, showversion, showdesc, showflags, modify
65 if [ -z "${1}" ]; then
68 while [ -n "${1}" ]; do
70 -h | --help) MODE="showhelp";;
71 -V | -v | --version) MODE="showversion";;
72 -i | --info) MODE="showdesc";;
73 -I | --info-installed) MODE="showinstdesc";;
74 -l | --local) SCOPE="local";;
75 -g | --global) SCOPE="global";;
76 -a | --active) MODE="showflags";;
77 -E | --enable) MODE="modify"; ACTION="add";;
78 -D | --disable) MODE="modify"; ACTION="remove";;
79 -P | --prune | -R | --remove)
80 MODE="modify"; ACTION="prune";;
81 -p | --package) MODE="modify"; shift; PACKAGE=${1}; SCOPE="local";;
83 echo "ERROR: unknown option ${1} specified."
89 ARGUMENTS="${ARGUMENTS:-} ${ACTIVE_FLAGS[9]}"
92 ARGUMENTS="${ARGUMENTS:-} ${1}"
107 # Function: check_sanity {{{
108 # Performs some basic system sanity checks
110 # file permission tests
115 [[ ! -d "${MAKE_PROFILE_PATH}" || ! -r "${MAKE_PROFILE_PATH}" ]] && error "${MAKE_PROFILE_PATH} is not readable"
117 for make_conf in $(get_all_make_conf); do
118 [ ! -r "${make_conf}" ] && fatal "${make_conf} is not readable"
121 descdir="$(get_portdir)/profiles"
123 [ ! -r "${MAKE_GLOBALS_PATH}" ] && fatal "${MAKE_GLOBALS_PATH} is not readable"
124 [ -z "$(get_portdir)" ] && fatal "\$PORTDIR couldn't be determined"
125 [ ! -d "${descdir}" ] && fatal "${descdir} does not exist or is not a directory"
126 [ ! -r "${descdir}/use.desc" ] && fatal "${descdir}/use.desc is not readable"
127 [ ! -r "${descdir}/use.local.desc" ] && fatal "${descdir}/use.local.desc is not readable"
129 for make_defaults in $(get_all_make_defaults); do
130 [ ! -r "$make_defaults" ] && fatal "$_make_defaults is not readable"
132 [ "${MODE}" == "modify" -a ! -w "${MAKE_CONF_PATH}" ] && fatal ""${MAKE_CONF_PATH}" is not writable"
133 [ "${MODE}" == "modify" -a -s "${PACKAGE_USE_PATH}" -a ! -w "${PACKAGE_USE_PATH}" ] && fatal ""${PACKAGE_USE_PATH}" is not writable"
139 ${PROGRAM_NAME} (${VERSION})
141 Syntax: ${PROGRAM_NAME} <option> [suboptions] [useflaglist]
143 Options: -h, --help - show this message
144 -V, --version - show version information
145 -i, --info - show descriptions for the given useflags
146 -I, --info-installed - show descriptions for the given useflags and
147 their current impact on the installed system
148 -g, --global - show only global use flags (suboption)
149 -l, --local - show only local use flags (suboption)
150 -a, --active - show currently active useflags and their origin
151 -E, --enable - enable the given useflags
152 -D, --disable - disable the given useflags
153 -R, --remove - remove all references to the given flags from
154 make.conf and package.use to revert to default
156 -P, --prune - alias for --remove
157 -p, --package - used with -E, -D, and -R to apply to a
158 specific package only
160 Notes: ${PROGRAM_NAME} currently works for global flags defined
161 in make.globals, make.defaults, make.conf, use.force, and use.mask
162 and local flags defined in package.use and individual package ebuilds.
163 It might have issues with cascaded profiles. If multiple options are
164 specified only the last one will be used.
170 ${PROGRAM_NAME} (${VERSION})
171 Written by Marius Mauch
173 Copyright (C) 2004-2009 Gentoo Foundation, Inc.
174 This is free software; see the source for copying conditions.
178 # Function: reduce_incrementals {{{
179 # remove duplicate flags from the given list in both positive and negative forms
180 # (but unlike portage always keep the last value even if it's negative)
181 # Otherwise the status flags could be incorrect if a flag appers multiple times in
182 # one location (like make.conf).
183 # Using python here as bash sucks for list handling.
184 # NOTE: bash isn't actually that bad at handling lists -- sh is. This may be
185 # worth another look to avoid calling python unnecessariy. Or we could
186 # just write the whole thing in python. ;)
187 reduce_incrementals() {
188 echo $@ | python -c "from __future__ import print_function;import sys
190 for x in sys.stdin.read().split():
191 if x[0] == '-' and x[1:] in r:
194 elif x[0] != '-' and '-'+x in r:
197 elif x == '-*': r = ['-*']
198 elif x not in r: r.append(x)
202 # Function: reduce_incrementals_trump {{{
203 # Similar to reduce_incrementals but negative flags trump positive
204 # flags, regardless of which follows which
205 reduce_incrementals_trump() {
206 echo $@ | python -c "from __future__ import print_function;import sys
208 for x in sys.stdin.read().split():
209 if x[0] == '-' and x[1:] in r:
212 elif x == '-*': r = ['-*']
213 elif x not in r and not '-'+x in r: r.append(x)
217 # Function: reduce_package_use {{{
218 # Similar to reduce_incrementals except converts lines from package atoms
219 # in /etc/portage/package.use files to lines of "pkg {[-]flag}*"
222 # * - Lines of package atom followed by flags
223 # (app-editors/vim flag1 flag2 -flag3)
224 reduce_package_use() {
225 echo "${@}" | python -c "from __future__ import print_function;import sys,re
226 h={}; getflags=re.compile(r'(-?[\w*-]+)')
227 for x in sys.stdin.read().split('\n'):
229 parts = x.lstrip().split(' ',1)
230 if len(parts)==1: continue
232 flags = getflags.findall(parts[1])
233 if not pkg in h: h[pkg]=[]
236 if x[0] == '-' and x[1:] in r:
239 elif x[0] != '-' and '-'+x in r:
242 elif x == '-*': r = h[pkg] = ['-*']
245 print('\n'.join(['%s %s' % (pkg,' '.join(flgs)) for pkg,flgs in h.items() if len(flgs)]))"
248 # Function: get_useflags {{{
249 # Creates a bash array ACTIVE_FLAGS that contains the global use flags,
250 # indexed by origin: 0: environment, 1: make.conf, 2: make.defaults,
251 # 3: make.globals, and local use flags, indexed by origin: 4: package.use,
252 # 5: ebuild IUSE, 6: use.mask, 7: use.force,
253 # 9: flags indicated active by emerge --info (get_portageuseflags)
255 if [[ -z ${ACTIVE_FLAGS[4]} && ( $SCOPE == "local" || -z $SCOPE ) ]]; then
256 # Parse through /etc/portage/package.use
257 if [[ -d ${PACKAGE_USE_PATH} ]]; then
258 ACTIVE_FLAGS[4]="$( cat ${PACKAGE_USE_PATH}/* \
259 | sed -re "s/ *#.*$//g" -e "s/^ *$//g" )"
260 elif [[ -e ${PACKAGE_USE_PATH} ]]; then
261 # JWM, 23/12/2009: I edited this following line but I'm not sure if it's 100% correct.
262 ACTIVE_FLAGS[4]="$( sed -re "s/ *#.*$//g" -e "s/^ *$//g" \
263 ${PACKAGE_USE_PATH})"
265 # Simplify ACTIVE_FLAGS[4] to be lines of pkg {[-]flag}*
266 ACTIVE_FLAGS[4]="$(reduce_package_use "${ACTIVE_FLAGS[4]}")"
268 # ACTIVE_FLAGS[5] reserved for USE flags defined in ebuilds and
269 # is generated/maintained in the get_useflaglist_ebuild() function
272 # only calculate once as calling emerge is painfully slow
273 [ -n "${USE_FLAGS_CALCULATED}" ] && return
275 # backup portdir so get_portdir() doesn't give false results later
276 portdir_backup="${PORTDIR}"
278 ACTIVE_FLAGS[0]="$(reduce_incrementals "${USE}")"
280 for x in $(get_all_make_conf); do
282 ACTIVE_FLAGS[1]="$(reduce_incrementals "${ACTIVE_FLAGS[1]}" "${USE}")"
285 for x in $(get_all_make_defaults); do
287 ACTIVE_FLAGS[2]="${ACTIVE_FLAGS[2]} ${USE}"
289 ACTIVE_FLAGS[2]="$(reduce_incrementals "${ACTIVE_FLAGS[2]}")"
291 source "${MAKE_GLOBALS_PATH}"
292 ACTIVE_FLAGS[3]="$(reduce_incrementals "${USE}")"
294 # restore saved env variables
295 USE="${ACTIVE_FLAGS[0]}"
296 PORTDIR="${portdir_backup}"
299 # Traverse through use.mask and use.force (0.5s)
300 # Flip signs of use.mask (it's interpreted oppositely),
301 ACTIVE_FLAGS[6]=$(reduce_incrementals_trump \
302 $(cat $(traverse_profile "use.mask") | sed -re "/^#.*$/{d}") \
303 | sed -re "s/(^| )-[^ ]*//g" -e "s/(^| )([a-z0-9])/ -\2/g")
304 ACTIVE_FLAGS[7]=$(reduce_incrementals \
305 $(cat $(traverse_profile "use.force") \
306 | sed -re "/^#.*$/ {d}"))
308 USE_FLAGS_CALCULATED=1
311 # Function: get_portageuseflags # {{{
312 # Fetch USE flags reported active by Portage
313 get_portageuseflags() {
314 # only calculate once as calling emerge is painfully slow
315 [ -n "${_PORTAGE_USE_FLAGS_CALCULATED}" ] && return
316 # get the currently active USE flags as seen by portage, this has to be after
317 # restoring USE or portage won't see the original environment
318 # Bug 181309, emerge may complain if EMERGE_DEFAULT_OPTS="--ask" is set
319 ACTIVE_FLAGS[9]="$(portageq envvar USE)" #'
320 _PORTAGE_USE_FLAGS_CALCULATED=1
323 # Function: get_useflaglist {{{
324 # Get the list of all known USE flags by reading use.desc and/or
325 # use.local.desc (depending on the value of $SCOPE). Also searches any
326 # registered overlays after searching the main portage tree first.
327 # Use flags visible in both the main tree and overlays are trumped by
328 # the main tree. Overlays are indicated by brackets [xxx] at the
329 # beginning of the description.
332 # (written to stdout) Sorted, unique list of system-wide USE flags and
333 # descriptions. Flags defined in overlays have the overlay in brackets
334 # prepended to the descriptions.
337 # SCOPE - [local|global] constrain search to local (use.local.desc) or
342 for profiledir in ${ALL_PORTDIRS[@]}; do
343 descdir="${profiledir}/profiles"
344 if [[ -z ${SCOPE} || ${SCOPE} == "global" ]]; then
345 [[ ! -s "${descdir}/use.desc" ]] && continue
346 egrep "^[^# ]+ +-" "${descdir}/use.desc"
348 if [[ -z ${SCOPE} || ${SCOPE} == "local" ]]; then
349 [[ ! -s "${descdir}/use.local.desc" ]] && continue
350 egrep "^[^# :]+:[^ ]+ +-" "${descdir}/use.local.desc" \
353 done | cut -d " " -f1 | sort --field=":" --key=1,1 --unique
356 # Function: get_useflaglist_ebuild {{{
357 # Builds USE flag information for specified package atom into
358 # ACTIVE_FLAGS[5]. For the atom, the versions available are found, and
359 # for each, the corresponding SLOT, IUSE are stored along with which
360 # overlay the ebuild lives in. Considering that the pieces of information
361 # may be required in any order or any subsets, it is intended for the
362 # function to cache the information and it be retrieved from
363 # ACTIVE_FLAGS[5]. So the format of ACTIVE_FLAGS[5] is newline-separated
366 # category/packge;version;SLOT;IUSE;overlay
369 # $1 - Package atom to lookup (app-editor/vim)
372 # Nothing significant
375 # PORTDIR - Root of portage tree
376 # ACTIVE_FLAGS - Array of current use flag info
378 get_useflaglist_ebuild() {
379 local known=$(echo "${ACTIVE_FLAGS[5]}" | egrep "^${1}")
380 if [[ -n $known ]]; then
384 local pkg=$(echo ${1} | cut -d/ -f2)
386 for portdir in ${ALL_PORTDIRS[@]}; do
387 if [[ -s "${portdir}/profiles/repo_name" ]]; then
388 overlay="$(cat "${portdir}/profiles/repo_name")"
390 # XXX: May be better to use full path
391 overlay="x-$(basename "${portdir}")"
393 # Open the ebuild file and retrieve defined USE flags
394 [[ ! -d "$portdir/${1}" ]] && continue
395 if [[ ! -d "$portdir/metadata/cache" ]]; then
396 echo "!!! Metadata cache not found. You need to run " >&2
397 echo "!!! 'egencache --repo=$overlay --update'" >&2
398 echo "!!! to generate metadata for your overlays" >&2
401 append=$(ls $portdir/metadata/cache/${1}-* \
402 | egrep "${1}-[0-9.]+" \
403 | sed -e "s:$portdir/metadata/cache/${1}-::g" \
404 | while read -d $'\n' version; do
406 if [[ ! -e "$portdir/metadata/cache/${1}-$version" ]]; then
407 # Repo does not have this particular package
410 iuse=$(head -11 "$portdir/metadata/cache/${1}-$version"|tail -1)
411 slot=$(head -3 "$portdir/metadata/cache/${1}-$version"|tail -1)
412 echo "${1};${version};${slot};${iuse};${overlay}"
415 if [[ -z ${ACTIVE_FLAGS[5]} ]]; then ACTIVE_FLAGS[5]="$append"
416 else ACTIVE_FLAGS[5]="${ACTIVE_FLAGS[5]}"$'\n'"$append"
421 # get all make.conf files that exist on the system
422 get_all_make_conf() {
423 # At least one of the files exists or we would not have made it this far
424 for x in ${ETC}/make.conf ${ETC}/portage/make.conf; do
425 [ -e "${x}" ] && echo "${x}"
428 # Function: traverse_profile {{{
429 # General method of collecting the contents of a profile
430 # component by traversing through the cascading profile
433 # $1 - Filename (make.profile)
434 # [$2] - Current directory (unspecified means to start at the top)
440 curdir="${2:-$(get_real_path ${MAKE_PROFILE_PATH})}"
442 if [[ -f "${curdir}/parent" ]]; then
443 for parent in $(egrep -v '(^#|^ *$)' ${curdir}/parent); do
444 # Bug 231394, handle parent path being absolute
445 if [[ ${parent:0:1} == "/" ]]; then
446 pdir="$(get_real_path ${parent})"
448 pdir="$(get_real_path ${curdir}/${parent})"
450 rvalue="${rvalue} $(traverse_profile ${1} ${pdir})"
453 [[ -f "${curdir}/${1}" ]] && rvalue="${rvalue} ${curdir}/${1}"
458 # Function: get_all_make_defaults {{{
459 # Det all make.defaults by traversing the cascaded profile directories
460 get_all_make_defaults() {
461 if [[ -z ${MAKE_DEFAULTS:-} ]]; then
462 MAKE_DEFAULTS=$(traverse_profile "make.defaults")
466 MAKE_DEFAULTS=$(get_all_make_defaults)
468 # Function: get_flagstatus_helper # {{{
469 # Little helper function to get the status of a given flag in one of the
470 # ACTIVE_FLAGS elements.
473 # (Written to STDOUT) Flag active status (+/-) or default string given
474 # in argument 4 or an empty space
478 # 2 - index of ACTIVE_FLAGS
479 # 3 - echo value for positive (and as lowercase for negative) test result
480 # 4 - (optional) echo value for "missing" test result, defaults to blank
481 get_flagstatus_helper() {
482 if [[ -z ${flags} ]]; then
483 local flags=${ACTIVE_FLAGS[${2}]}
485 local flag=$(echo " $flags " | grep -Eo " [+-]?${1} ")
486 if [[ ${flag:1:1} == "-" ]]; then
487 echo -e -n "${3}" | tr [:upper:]+ [:lower:]-
488 elif [[ -n ${flag} ]]; then
495 # Function: get_flagstatus_helper_pkg # {{{
496 # Entry to get_flagstatus_helper for packages which will fetch use
497 # flags set in package.use for the package and pass them on to
498 # get_flagstatus_helper. Also correcly handles lines in package.use
499 # specified for individual package versions
500 get_flagstatus_helper_pkg() {
501 if [[ -z ${2} ]]; then
504 elif [[ -z ${flags} ]]; then
505 # If no atoms are matchers (start with >,<,=, then they all match
507 if [[ -z "${atoms[@]/[<>=]*/}" ]]; then
509 echo "${atoms[@]}" | python -c "
510 from __future__ import print_function;import portage.dep as dep, sys
511 print(' '.join(dep.match_to_list('$5-$6',sys.stdin.read().split())))"))
513 flags=$(for atom in ${atoms[@]}; do
514 [[ -z $atom ]] && continue
515 echo "${ACTIVE_FLAGS[4]}" | \
516 grep "^ *$atom" | cut -d\ -f2-
519 if [[ -z ${5} || -z ${flags} ]]; then
523 get_flagstatus_helper "$@"
526 # Function: get_flagstatus_helper_ebuild {{{
527 # get_flagstatus_helper replacement for packages to fetch ebuild USE flag
531 # (Written to STDOUT) Flag active status (+/-) or default string given
532 # in argument 4 or an empty space. If USE flag is not defined in the list
533 # of flags (2), an '!' is written
537 # 2 - IUSE line from ebuild file
538 # 3 - echo value for positive (and as lowercase for negative) test result
539 # 4 - (optional) echo value for "missing" test result, defaults to blank space
540 get_flagstatus_helper_ebuild() {
541 local flags=$(echo $2 | cut -d\" -f2)
542 local flag=$(echo " $flags " | grep -Eo " [+-]?$1 ")
543 if [[ ${flag:1:1} == "+" ]]; then
545 elif [[ ${flag:1:1} == "-" ]]; then
546 echo -en "${3}" | tr [:upper:]+ [:lower:]-
547 elif [[ -z $flag ]]; then
554 # Function: get_flagstatus {{{
555 # Prints a status string for the given flag, each column indicating the presence
556 # for portage, in the environment, in make.conf, in make.defaults, in
557 # make.globals, and in use.force and flipped in use.mask.
560 # 1 - use flag for which to retrieve status
563 # 0 (True) if flag is active, 1 (False) if not active
566 # Full positive value would be "[+ECDGFm] ", full negative value would be [-ecdgfM],
567 # full missing value would be "[- ] " (portage only sees present or not present)
571 local E=$(get_flagstatus_helper "${1}" 0 "E")
572 local C=$(get_flagstatus_helper "${1}" 1 "C")
573 local D=$(get_flagstatus_helper "${1}" 2 "D")
574 local G=$(get_flagstatus_helper "${1}" 3 "G")
575 local M=$(get_flagstatus_helper "${1}" 6 "M")
576 local F=$(get_flagstatus_helper "${1}" 7 "F")
577 # Use flags are disabled by default
580 # Use flag precedence is defined (at least) at:
581 # http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?part=2&chap=2
582 for location in "E" "C" "D" "G" "F" "M"; do
583 if [[ ${!location} == $location ]]; then
586 elif [[ ${!location} != " " ]]; then
591 echo -n "[${ACTIVE:--}$E$C$D$G$F$M] "
592 [[ $ACTIVE == "+" ]]; return $?
595 # Function: get_flagstatus_pkg {{{
596 # Outputs local flag status information for a specific package and perhaps
597 # specific package version.
601 # 2 - package atom (www-client/elinks)
602 # 3 - +/- whether flag is enabled by global configuration files
603 # 4 - (Optional) version of package to evaluate (empty means all versions)
606 # Flag status for package.use and ebuild, slot and version, and overlay
607 # the version lives is if not PORTDIR
609 # Full positive would be "[+PB]", full negative would be "[-pb]", and full
610 # missing would be "[? ]", question because the sign will default to the
611 # sign of the global status of the flag
612 get_flagstatus_pkg() {
614 # Pre-cache the use flags declared in ebuilds first.
615 # This is required as calling it inside a $() seems to
616 # prevent caching of results into $ACTIVE_FLAGS array
617 get_useflaglist_ebuild ${2}
619 # Get list of ebuilds available for this package. The
620 # get_useflaglist_ebuild() function stores them in $ACTIVE_FLAGS[5]
621 # with the package name leading the lines. The other information
622 # is in the same line, semi-colon (;) separated. The fields are
623 # package;version;SLOT;IUSE;overlay
625 # Fetch package atoms and flags from package.use only once
626 local atoms=$(echo "${ACTIVE_FLAGS[4]}" | \
627 grep -Eo "^ *[<>]?=?${2}(-[0-9rp._*-]*)?")
628 local IFS=$'\n'; for pkgline in $(echo "${ACTIVE_FLAGS[5]}" | grep "^$2" \
629 | sort -t \; -k2,2 -V); do
630 OIFS=$IFS; IFS=";"; INFO=($pkgline); IFS=$OIFS;
631 local version=${INFO[1]}
632 [[ -n $4 && $4 != $version ]] && continue
633 local slot=${INFO[2]}
634 local iuse=${INFO[3]}
635 if [[ $slot == '0' || $slot == "" ]]; then
638 slot="($(echo ${slot} | cut -d\" -f2)) "
640 local overlay=${INFO[4]}
641 [[ -n $overlay ]] && overlay="[$overlay]"
643 # Fetch enabled status for this version
644 local P=$(get_flagstatus_helper_pkg "${1}" "${atoms}" "P" "" "${2}" "${version}")
645 local B=$(get_flagstatus_helper_ebuild "${1}" "${iuse}" "B" "" "${2}" "${version}")
648 for location in "P" "B"; do
649 if [[ ${!location} == $location ]]; then
651 elif [[ ${!location} == "!" ]]; then
654 elif [[ ${!location} != " " ]]; then
659 if [[ $UNUSED == 1 ]]; then
660 echo " ${slot}${version} ${overlay}"
662 echo " [${ACTIVE:-${3:- }}$P$B] ${slot}${version} ${overlay}"
668 # Function: get_portdir {{{
669 # faster replacement to `portageq portdir`
672 # Location of portage tree root
674 # Use a subshell so we don't have to protect the variables in
677 if [ -z "${PORTDIR:-}" ]; then
678 source "${MAKE_GLOBALS_PATH}"
679 for x in $(get_all_make_defaults); do
682 for x in $(get_all_make_conf); do
689 # This won't change while the script is running, so cache it
690 PORTDIR="$(get_portdir)"
692 # Function: get_all_overlays {{{
693 # Outputs list of portage overlays as defined in the PORTDIR_OVERLAY
694 # variable defined in make.conf
696 # Use a subshell so we don't have to protect the variables in
699 for x in $(get_all_make_conf); do
700 [[ -r "${x}" ]] && source "${x}"
702 echo ${PORTDIR_OVERLAY}
705 ALL_PORTDIRS=( "$PORTDIR" $(get_all_overlays) )
707 # Function: array_contains {{{
708 # PHP-style array_contains function.
715 # 0 (True) if needle in haystack, null (False) otherwise
717 for i in $1; do [[ $i == $2 ]] && return 0; done
721 # Function: showdesc {{{
722 # This function takes a list of use flags and shows the status and
723 # the description for each one, honoring $SCOPE
726 # * - USE flags for which to display descriptions. Undefined means to
727 # display descriptions for all known USE flags
730 # SCOPE - defines whether to output local or global USE flag descriptions
731 # Empty means to display both
734 # (STDOUT) Flag description(s) for given USE flags
745 if [ -z "${SCOPE}" ]; then
746 SCOPE="global" showdesc ${args}
748 SCOPE="local" showdesc ${args}
752 local useflags=( $(echo "$(get_useflaglist)") )
754 [ "${SCOPE}" == "global" ] && echo "global use flags (searching: ${args})"
755 [ "${SCOPE}" == "local" ] && echo "local use flags (searching: ${args})"
756 echo "************************************************************"
758 if [ "${args}" == "*" ]; then
759 args="${useflags[*]}"
765 while [[ -n "${1}" ]]; do
766 if [[ "${SCOPE}" == "global" ]]; then
767 if array_contains "${useflags[*]}" "$1"; then
768 get_flagstatus "${1}"
769 # XXX: Handle overlay
770 grep -h "^${1} *-" ${ALL_PORTDIRS[@]/%//profiles/use.desc} 2> /dev/null
774 # local flags are a bit more complicated as there can be multiple
775 # entries per flag and we can't pipe into printf
776 if [[ "${SCOPE}" == "local" ]]; then
777 if array_contains "${useflags[*]}" "$1"; then
780 # Fetch all the packages data using this flag
781 infos=$( grep -h ":${1} *-" ${ALL_PORTDIRS[@]/%//profiles/use.local.desc} 2> /dev/null \
782 | sed -re "s/^([^:]+):(${1}) *- *(.+)/\1|\2|\3/g")
783 OIFS=$IFS; IFS=$'\n'; infos=($infos); IFS=$OIFS;
784 for line in "${infos[@]}"; do
785 OIFS=$IFS; IFS="|"; line=($line); IFS=$OIFS
789 if get_flagstatus "${flag}"; then
794 printf "%s\n" "${flag}"
795 printf "%s: %s\n" "${pkg}" "${desc}" \
796 | fold --width=$((${COLUMNS:-80}-10)) -s | sed -e "s/^/ /g"
797 get_flagstatus_pkg "${flag}" "${pkg}" "${ACTIVE}"
803 if [[ ${foundone} == 0 ]]; then
804 echo "no matching entries found"
808 # Function: showinstdesc {{{
809 # Works like showdesc() but displays only descriptions of which the appropriate
810 # ebuild is installed and prints the name of those packages.
813 # * - USE flags for which to display descriptions. Undefined means to
814 # display descriptions for all known USE flags
817 # SCOPE - defines whether to output local or global USE flag descriptions
818 # Empty means to display both
821 # (STDOUT) Flag description(s) for given USE flags along with installed
834 "global") echo "global use flags (searching: ${args[@]})";;
835 "local") echo "local use flags (searching: ${args[@]})";;
836 *) SCOPE="global" showinstdesc "${args[@]}"
838 SCOPE="local" showinstdesc "${args[@]}"
842 descdir="$(get_portdir)/profiles"
843 echo "************************************************************"
845 if [ "${args}" = "*" ]; then
846 args="$(get_useflaglist | sort -u)"
851 while [ -n "${1}" ]; do
854 if desc=$(grep "^${1} *-" "${descdir}/use.desc"); then
855 get_flagstatus "${1}"
857 # get list of installed packages matching this USE flag.
859 packages=($(equery -q -C hasuse -i "${1}" | awk '{ print $(NF-1) }' | sort))
860 foundone+=${#packages[@]}
861 printf "\nInstalled packages matching this USE flag: "
862 if [ ${foundone} -gt 0 ]; then
863 echo $'\n'"${packages[*]}"
870 # local flags are a bit more complicated as there can be multiple
871 # entries per flag and we can't pipe into printf
872 IFS=': ' # Use a space instead of a dash because dashes occur in cat/pkg
873 while read pkg flag desc; do
874 # print name only if package is installed
875 # NOTE: If we implement bug #114086 's enhancement we can just use the
876 # exit status of equery instead of a subshell and pipe to wc -l
877 # Bug 274472, -e is the default
878 if [ $(equery -q -C list -i "${pkg}" | wc -l) -gt 0 ]; then
881 get_flagstatus "${flag}" "${pkg}"
883 printf "%s (%s):\n%s\n\n" "${flag}" "${pkg}" "${desc#- }"
885 done < <(grep ":${1} *-" "${descdir}/use.local.desc")
891 if [ ${foundone} -lt 1 ]; then
892 echo "no matching entries found"
897 # Function: showflags {{{
898 # show a list of all currently active flags and where they are activated
906 if [ "${args}" == "*" ]; then
907 args="$(get_useflaglist | sort -u)"
913 while [ -n "${1}" ]; do
914 if echo " ${ACTIVE_FLAGS[9]} " | grep " ${1} " > /dev/null; then
919 if echo " ${ACTIVE_FLAGS[4]} " | egrep -e " -?${1} " > /dev/null; then
920 for pkg in $( echo "${ACTIVE_FLAGS[4]}" | \
921 egrep " -?${1} " | cut -d " " -f 2); do
923 SCOPE="local" get_flagstatus ${1} "${pkg}"
924 printf "(%s)\n" ${pkg}
931 # two small helpers to add or remove a flag from a USE string
933 # Remove leading '-' from flag if found
935 [[ ${flag:0:1} == "-" ]] && flag=${1:1}
937 if [[ -n $(grep " -${flag} " <<< " ${ACTIVE_FLAGS[6]} ") ]]; then
938 error "Use flag \"${flag}\" is masked and should not be added" \
941 # Bug #104396 -- Only add use flags defined in use.desc and use.local.desc
942 elif [[ -z $(grep "^${flag}$" <<< "$(get_useflaglist)") ]]; then
943 error "Use flag \"${flag}\" is not defined in use.desc and should" \
944 "not be added\nto make.conf."
947 NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE} ${1}"
948 echo "Adding flag \"${1}\" to make.conf" >&2
953 NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE// ${1} / }"
954 echo "Removing flag \"${1}\" from make.conf" >&2
957 # Function: clean_package_use {{{
958 # Simple utility to remove empty files from package.use
959 clean_package_use() {
960 if [[ -d ${PACKAGE_USE_PATH} ]]; then
961 for f in $(find ${PACKAGE_USE_PATH} -size 0); do
962 echo "Removing empty file ""${f}"""
968 # Function: scrub_use_flag {{{
969 # Utility to remove a use flag from a file in package.use[/]
976 # PACKAGE - Package atom for which to remove flag
978 local atom_re="^[<>]?=?([a-z][\da-z/-]+[a-z])(-[0-9pr._*-]+)?"
980 # Ignore leading - on flag
982 local pkg=$(echo "${PACKAGE}" | sed -re "s/${atom_re}/\1/")
983 local pkg_re="[<>]?=?${pkg}(-[\dpr._*-]+)?"
986 # Skip (preserve) comments on their own lines
987 if [[ -z $(echo "${line}" | sed -re "s/^ *#.*$//") ]]; then
989 # Detect if requested package is defined on this line
990 elif [[ -n "${PACKAGE}" ]]; then
991 if [[ -n $(echo "${line}" | grep -Ee "${pkg_re}") ]]; then
992 # If this is the only (remaining) use flag defined
993 # for this package, then remove the whole line
994 if [[ -z $(echo "${line}" | grep -Ee "${pkg_re} *-?${flag} *$") ]]; then
995 # Remove flag from this line
996 echo "${line}" | sed -re "s/ *-?\b${flag}\b//"
1002 # If line only has this use flag, let it be removed
1003 # (used if PACKAGE is not defined -- from pruning)
1004 elif [[ -n $(echo "${line}" | \
1005 egrep "^[^#]*${atom_re}.*-?${flag}") ]]; then
1006 echo "Removing use flag from ${line}" >&2
1007 if [[ -z $(echo "${line}" | \
1008 grep -Ee "${atom_re} *-?${flag} *$") ]]; then
1009 # Remove flag from this line
1010 echo "${line}" | sed -re "s/-?\b${flag}\b//"
1016 done > "${filename}.new" < "${filename}"
1017 mv "${filename}.new" "${filename}"
1020 # Function: modify_package {{{
1021 # Adds and removes USE flags from individual packages by modifying
1022 # files in package.use. It supports package.use both as a file and
1023 # and as a folder. Also handles "enabling" as removing a disabled flag from
1024 # a file, and "disabling" a globally enabled flag by adding a negative to
1025 # a file. Also warns about unused and unknown flags, and about flags
1026 # already enabled, disabled, or masked.
1029 # * - USE flag(s) to add or remove
1032 # PACKAGE - Package for which to add (-E) or remove (-D) the USE
1037 local atom_re="^[<>]?=?([a-z][\da-z/-]+[a-z])(-[0-9pr._*-]+)?"
1038 local pkg=$(echo "${PACKAGE}" | sed -re "s/${atom_re}/\1/")
1039 local V=$(echo "${PACKAGE}" | sed -re "s/${atom_re}/\2/")
1040 local pkg_re="[<>]?=?${pkg}(-[\dpr._*-]+)?"
1042 local all_flags=$(SCOPE= get_useflaglist)
1043 # Shift at top rather than bottom to support 'continue'
1045 while [[ -n ${2} ]]; do
1051 # Fetch flag ACTIVE status (+,-,null)
1052 get_flagstatus "${flag}" "${pkg}" > /dev/null
1053 GLOBAL_ACTIVE="$ACTIVE"
1054 # XXX: If V is not given, is this necessary? Should it use the version
1055 # that would be installed by emerge?
1056 get_flagstatus_pkg "${flag}" "${pkg}" "${ACTIVE}" "${V}" > /dev/null
1059 # (1) make sure ${pkg} exists in portdir or an overlay
1061 for portdir in ${ALL_PORTDIRS[@]}; do
1062 if [[ -d "${portdir}/${pkg}" ]]; then
1067 if [[ $exists == 1 ]]; then
1068 fatal "Package \"${pkg}\" does not exist"
1070 # (2) make sure ${flag} is defined in get_useflaglist
1071 elif ! array_contains "$all_flags" ${flag}; then
1072 error "USE flag \"${flag}\" does not exist"
1074 # Don't bail just because of this, just warn
1075 # (3) make sure use flag is valid for the package
1076 elif [[ -z $(echo "${ACTIVE_FLAGS[5]} " | grep -Ee "^${pkg_re}" \
1077 | grep -Ee "[; ][+-]?${flag}") ]]; then
1078 # XXX: Handle version or version wildcard?
1079 warn "USE flag \"${flag}\" is not used by $PACKAGE"
1080 # Don't necessarily bail for this, just warn
1081 elif [[ -n "${V}" && -z "$(egrep "<|>|=" <<< "${PACKAGE:0:1}")" ]]; then
1082 error "Invalid package atom. Did you forget the leading '='?"
1084 elif [[ -z "${V}" && -n "$(egrep "<|>|=" <<< "${PACKAGE:0:1}")" ]]; then
1085 error "Invalid package atom. Did you forget the version?"
1088 # If removing a disabled flag, or adding an enabled one, emit a warning,
1089 # indicating a likely misunderstanding
1090 if [[ "${ACTION}" == "remove" ]]; then
1091 if [[ "${ACTIVE:-${GLOBAL_ACTIVE}}" == "-" ]]; then
1092 warn "USE flag \"$flag\" is already disabled for $PACKAGE"
1095 elif [[ "${ACTION}" == "prune" ]]; then
1096 # Just remove the flag below
1097 [[ "${ACTIVE}" == "-" ]] && flag="-${flag}"
1099 elif [[ "${ACTION}" == "add" ]]; then
1100 if [[ "${ACTIVE:-${GLOBAL_ACTIVE:--}}" == "+" ]]; then
1101 # XXX: Perhaps look at indicating where it is enabled
1102 warn "USE flag \"$flag\" is already enabled for $PACKAGE"
1108 if [[ -d ${PACKAGE_USE_PATH} ]]; then
1109 # Use naming convention of package.use/package
1110 filename="${PACKAGE_USE_PATH}/${pkg#*/}"
1111 if [[ ! -s "${filename}" ]]; then
1112 # Create new file to contain flag
1113 echo "${PACKAGE} ${flag}" > "${filename}"
1114 echo "Adding \"${PACKAGE}[${flag}]\" use flag to new file ""${filename}"""
1118 # Add to package.use file instead
1119 filename="${PACKAGE_USE_PATH}"
1120 # Create as necessary
1123 # Walk through the file and add the flag manually
1124 echo "Adding \"${PACKAGE}[${flag}]\" use flag in \"${filename}\""
1127 if [[ -n $(echo "${line}" | egrep -re "^[^#]*${PACKAGE} ") ]]; then
1128 echo $(reduce_package_use "${line} ${flag}")
1134 done < "${filename}" > "${filename}.new"
1135 mv "${filename}.new" "${filename}"
1136 if [[ ${added} -eq 0 ]]; then
1137 echo "${PACKAGE} ${flag}" >> "${filename}"
1142 if [[ -d ${PACKAGE_USE_PATH} ]]; then
1143 # Scan for file containing named package and use flag
1144 filename=$(egrep -rle "${pkg_re}.*[^-]${flag}( |$)" "${PACKAGE_USE_PATH}")
1145 if [[ -z "${filename}" ]]; then
1146 error ""${flag}" is not defined for package "${PACKAGE}""
1150 # Remove from package.use instead
1151 filename=${PACKAGE_USE_PATH}
1152 # Create as necessary
1155 # Scrub use flag from matched files
1156 for f in ${filename}; do
1157 # Remove current flags in file
1158 echo "Removing \"${PACKAGE}[${flag}]\" use flag in \"${f}\""
1159 scrub_use_flag ${f} ${flag}
1161 # Remove empty files
1168 # Function: modify {{{
1169 # USE flag modification function. Mainly a loop with calls to add_flag and
1170 # remove_flag to create a new USE string which is then inserted into make.conf.
1172 if [[ -n "${PACKAGE}" ]]; then
1173 modify_package "${*}"
1177 local make_conf_modified=0
1179 if [ -z "${*}" ]; then
1180 if [ "${ACTION}" != "prune" ]; then
1181 echo "WARNING: no USE flags listed for modification, do you really"
1182 echo " want to ${ACTION} *all* known USE flags?"
1183 echo " If you don't please press Ctrl-C NOW!!!"
1185 set $(get_useflaglist | sort -u)
1191 NEW_MAKE_CONF_USE=" ${ACTIVE_FLAGS[1]} "
1193 while [[ -n "${1}" ]]; do
1194 if [[ "${ACTION}" == "add" ]]; then
1195 if [[ -n $(grep " ${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1196 warn "Use flag \"${1}\" is already enabled globally"
1198 elif [[ -n $(grep " -${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1199 remove_flag "-${1}" || exit
1200 make_conf_modified=1
1202 add_flag "${1}" || exit
1203 make_conf_modified=1
1206 elif [[ "${ACTION}" == "remove" ]]; then
1207 if [[ -n $(grep " -${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1208 warn "Use flag \"${1}\" is already disabled globally"
1210 elif [[ -n $(grep " ${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1211 remove_flag "${1}" || exit
1212 make_conf_modified=1
1214 add_flag "-${1}" || exit
1215 make_conf_modified=1
1218 elif [[ "${ACTION}" == "prune" ]]; then
1219 if [[ -n $(grep " ${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1220 remove_flag "${1}" || exit
1221 make_conf_modified=1
1222 elif [[ -n $(grep " -${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1223 remove_flag "-${1}" || exit
1224 make_conf_modified=1
1226 warn "Use flag \"${1}\" is not set globally"
1232 # a little loop to add linebreaks so we don't end with one ultra-long line
1233 NEW_MAKE_CONF_USE_2=""
1234 for x in ${NEW_MAKE_CONF_USE}; do
1235 if [ $(((${#NEW_MAKE_CONF_USE_2}%70)+${#x}+2)) -gt 70 ]; then
1236 NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}\\ \\n $x "
1238 NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}${x} "
1242 # Bail if there is no need to modify make.conf
1243 [[ ${make_conf_modified} == 1 ]] || return
1244 # make a backup just in case the user doesn't like the new make.conf
1245 cp -p "${MAKE_CONF_PATH}" "${MAKE_CONF_BACKUP_PATH}"
1247 # as sed doesn't really work with multi-line patterns we have to replace USE
1248 # on our own here. Basically just skip everything between USE=" and the
1249 # closing ", printing our new USE line there instead.
1253 (while [ "$x" -eq "0" ]; do
1254 read -r line || break
1256 # Bug 275362 - Handle the case where make.conf includes:
1260 # Consume USE=" when detected so the quote won't be detected
1261 # as the ending quote
1262 if [ "${line:0:4}" == "USE=" ]; then inuse=1; line=${line:5}; fi
1263 [ "${inuse}" == "0" ] && echo -E "${line}"
1264 if [ "${inuse}" == "1" ] && echo "${line}" | egrep '" *(#.*)?$' > /dev/null; then
1266 echo -ne "${NEW_MAKE_CONF_USE_2%% }"
1272 if [ ${had_use} -eq 0 ]; then
1274 echo -ne "${NEW_MAKE_CONF_USE_2%% }"
1276 fi ) < "${MAKE_CONF_BACKUP_PATH}" | sed -e 's:\\ $:\\:' > "${MAKE_CONF_PATH}"
1278 echo "${MAKE_CONF_PATH} was modified, a backup copy has been placed at ${MAKE_CONF_BACKUP_PATH}"
1281 ##### main program comes now #####
1283 # disable globbing as it fucks up with args=*
1285 parse_arguments "$@"
1289 eval ${MODE} ${ARGUMENTS}
1291 # vim: set tabstop=4 shiftwidth=4: