diff options
Diffstat (limited to 'bin')
92 files changed, 4758 insertions, 2911 deletions
diff --git a/bin/archive-conf b/bin/archive-conf index 797866817..f73ca425e 100755 --- a/bin/archive-conf +++ b/bin/archive-conf @@ -1,5 +1,5 @@ -#!/usr/bin/python -# Copyright 1999-2006 Gentoo Foundation +#!/usr/bin/python -b +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # @@ -12,43 +12,21 @@ from __future__ import print_function import sys -try: - import portage -except ImportError: - from os import path as osp - sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) - import portage +from os import path as osp +pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym") +sys.path.insert(0, pym_path) +import portage +portage._internal_caller = True + +import portage.dispatch_conf from portage import os -from portage import dispatch_conf +from portage.checksum import perform_md5 FIND_EXTANT_CONTENTS = "find %s -name CONTENTS" MANDATORY_OPTS = [ 'archive-dir' ] -try: - import fchksum - def perform_checksum(filename): return fchksum.fmd5t(filename) -except ImportError: - import md5 - def md5_to_hex(md5sum): - hexform = "" - for ix in range(len(md5sum)): - hexform = hexform + "%02x" % ord(md5sum[ix]) - return hexform.lower() - - def perform_checksum(filename): - f = open(filename, 'rb') - blocksize=32768 - data = f.read(blocksize) - size = 0 - sum = md5.new() - while data: - sum.update(data) - size = size + len(data) - data = f.read(blocksize) - return (md5_to_hex(sum.digest()),size) - def archive_conf(): args = [] content_files = [] @@ -63,19 +41,19 @@ def archive_conf(): md5_match_hash[conf] = '' # Find all the CONTENT files in VDB_PATH. - content_files += os.popen(FIND_EXTANT_CONTENTS % - (os.path.join(portage.settings['EROOT'], portage.VDB_PATH))).readlines() + with os.popen(FIND_EXTANT_CONTENTS % (os.path.join(portage.settings['EROOT'], portage.VDB_PATH))) as f: + content_files += f.readlines() # Search for the saved md5 checksum of all the specified config files # and see if the current file is unmodified or not. try: todo_cnt = len(args) - for file in content_files: - file = file.rstrip() + for filename in content_files: + filename = filename.rstrip() try: - contents = open(file, "r") + contents = open(filename, "r") except IOError as e: - print('archive-conf: Unable to open %s: %s' % (file, e), file=sys.stderr) + print('archive-conf: Unable to open %s: %s' % (filename, e), file=sys.stderr) sys.exit(1) lines = contents.readlines() for line in lines: @@ -84,7 +62,7 @@ def archive_conf(): for conf in args: if items[1] == conf: stored = items[2].lower() - real = perform_checksum(conf)[0].lower() + real = perform_md5(conf).lower() if stored == real: md5_match_hash[conf] = conf todo_cnt -= 1 diff --git a/bin/banned-helper b/bin/banned-helper deleted file mode 100755 index 17ea9915d..000000000 --- a/bin/banned-helper +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -# Copyright 2009 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 - -die "'${0##*/}' has been banned for EAPI '$EAPI'" -exit 1 diff --git a/bin/bashrc-functions.sh b/bin/bashrc-functions.sh index 4da558581..503b17224 100644 --- a/bin/bashrc-functions.sh +++ b/bin/bashrc-functions.sh @@ -1,9 +1,9 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 portageq() { - PYTHONPATH=${PORTAGE_PYM_PATH}${PYTHONPATH:+:}${PYTHONPATH} \ + PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}}\ "${PORTAGE_PYTHON:-/usr/bin/python}" "${PORTAGE_BIN_PATH}/portageq" "$@" } @@ -23,71 +23,16 @@ register_success_hook() { done } -strip_duplicate_slashes() { +__strip_duplicate_slashes() { if [[ -n $1 ]] ; then local removed=$1 while [[ ${removed} == *//* ]] ; do removed=${removed//\/\///} done - echo ${removed} + echo "${removed}" fi } -# this is a function for removing any directory matching a passed in pattern from -# PATH -remove_path_entry() { - save_IFS - IFS=":" - stripped_path="${PATH}" - while [ -n "$1" ]; do - cur_path="" - for p in ${stripped_path}; do - if [ "${p/${1}}" == "${p}" ]; then - cur_path="${cur_path}:${p}" - fi - done - stripped_path="${cur_path#:*}" - shift - done - restore_IFS - PATH="${stripped_path}" -} - -# Set given variables unless these variable have been already set (e.g. during emerge -# invocation) to values different than values set in make.conf. -set_unless_changed() { - if [[ $# -lt 1 ]]; then - die "${FUNCNAME}() requires at least 1 argument: VARIABLE=VALUE" - fi - - local argument value variable - for argument in "$@"; do - if [[ ${argument} != *=* ]]; then - die "${FUNCNAME}(): Argument '${argument}' has incorrect syntax" - fi - variable="${argument%%=*}" - value="${argument#*=}" - if eval "[[ \${${variable}} == \$(env -u ${variable} portageq envvar ${variable}) ]]"; then - eval "${variable}=\"\${value}\"" - fi - done -} - -# Unset given variables unless these variable have been set (e.g. during emerge -# invocation) to values different than values set in make.conf. -unset_unless_changed() { - if [[ $# -lt 1 ]]; then - die "${FUNCNAME}() requires at least 1 argument: VARIABLE" - fi - - local variable - for variable in "$@"; do - if eval "[[ \${${variable}} == \$(env -u ${variable} portageq envvar ${variable}) ]]"; then - unset ${variable} - fi - done -} - KV_major() { [[ -z $1 ]] && return 1 diff --git a/bin/binhost-snapshot b/bin/binhost-snapshot index 9d2697d03..c2204f03d 100755 --- a/bin/binhost-snapshot +++ b/bin/binhost-snapshot @@ -1,9 +1,8 @@ -#!/usr/bin/python -# Copyright 2010-2011 Gentoo Foundation +#!/usr/bin/python -b +# Copyright 2010-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 import io -import optparse import os import sys import textwrap @@ -13,13 +12,12 @@ try: except ImportError: from urlparse import urlparse -try: - import portage -except ImportError: - from os import path as osp - sys.path.insert(0, osp.join(osp.dirname(osp.dirname( - osp.realpath(__file__))), "pym")) - import portage +from os import path as osp +pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym") +sys.path.insert(0, pym_path) +import portage +portage._internal_caller = True +from portage.util._argparse import ArgumentParser def parse_args(argv): prog_name = os.path.basename(argv[0]) @@ -33,7 +31,7 @@ def parse_args(argv): usage += "\n\n" for line in textwrap.wrap(prog_desc, 70): - usage += line + "\n" + usage += line + "\n" usage += "\n" usage += "Required Arguments:\n\n" @@ -47,11 +45,12 @@ def parse_args(argv): "write Packages index with\n" + \ " snapshot_uri" - parser = optparse.OptionParser(usage=usage) - parser.add_option('--hardlinks', help='create hardlinks (y or n, default is y)', - choices=('y', 'n')) - parser.set_defaults(hardlinks='y') - options, args = parser.parse_args(argv[1:]) + parser = ArgumentParser(usage=usage) + parser.add_argument('--hardlinks', + help='create hardlinks (y or n, default is y)', + choices=('y', 'n'), + default='y') + options, args = parser.parse_known_args(argv[1:]) if len(args) != 4: parser.error("Required 4 arguments, got %d" % (len(args),)) diff --git a/bin/check-implicit-pointer-usage.py b/bin/check-implicit-pointer-usage.py index 8822c4504..242436c1d 100755 --- a/bin/check-implicit-pointer-usage.py +++ b/bin/check-implicit-pointer-usage.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python -b # Ripped from HP and updated from Debian # Update by Gentoo to support unicode output diff --git a/bin/chpathtool.py b/bin/chpathtool.py index d0d49cb6d..64606623a 100755 --- a/bin/chpathtool.py +++ b/bin/chpathtool.py @@ -1,15 +1,26 @@ -#!/usr/bin/python -# Copyright 2011 Gentoo Foundation +#!/usr/bin/python -b +# Copyright 2011-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 +"""Helper tool for converting installed files to custom prefixes. + +In other words, eprefixy $D for Gentoo/Prefix.""" + import io -import optparse import os import stat import sys -CONTENT_ENCODING = "utf_8" -FS_ENCODING = "utf_8" +from portage.util._argparse import ArgumentParser + +# Argument parsing compatibility for Python 2.6 using optparse. +if sys.hexversion < 0x2070000: + from optparse import OptionParser + +from optparse import OptionError + +CONTENT_ENCODING = 'utf_8' +FS_ENCODING = 'utf_8' try: import magic @@ -41,7 +52,9 @@ class IsTextFile(object): def _is_text_magic(self, filename): mime_type = self._m.file(filename) - return mime_type.startswith("text/") + if isinstance(mime_type, bytes): + mime_type = mime_type.decode('ascii', 'replace') + return mime_type.startswith('text/') def _is_text_encoding(self, filename): try: @@ -64,7 +77,7 @@ def chpath_inplace(filename, is_text_file, old, new): try: orig_mode = stat.S_IMODE(os.lstat(filename).st_mode) except OSError as e: - sys.stderr.write("%s: %s\n" % (e, filename)) + sys.stderr.write('%s: %s\n' % (e, filename)) return temp_mode = 0o200 | orig_mode os.chmod(filename, temp_mode) @@ -121,8 +134,12 @@ def chpath_inplace(filename, is_text_file, old, new): f.close() if modified: - orig_mtime = orig_stat[stat.ST_MTIME] - os.utime(filename, (orig_mtime, orig_mtime)) + if sys.hexversion >= 0x3030000: + orig_mtime = orig_stat.st_mtime_ns + os.utime(filename, ns=(orig_mtime, orig_mtime)) + else: + orig_mtime = orig_stat[stat.ST_MTIME] + os.utime(filename, (orig_mtime, orig_mtime)) return modified def chpath_inplace_symlink(filename, st, old, new): @@ -135,14 +152,37 @@ def chpath_inplace_symlink(filename, st, old, new): def main(argv): - usage = "%s [options] <location> <old> <new>" % (os.path.basename(argv[0],)) - parser = optparse.OptionParser(usage=usage) - options, args = parser.parse_args(argv[1:]) - - if len(args) != 3: - parser.error("3 args required, got %s" % (len(args),)) - - location, old, new = args + parser = ArgumentParser(description=__doc__) + try: + parser.add_argument('location', default=None, + help='root directory (e.g. $D)') + parser.add_argument('old', default=None, + help='original build prefix (e.g. /)') + parser.add_argument('new', default=None, + help='new install prefix (e.g. $EPREFIX)') + opts = parser.parse_args(argv) + + location, old, new = opts.location, opts.old, opts.new + except OptionError: + # Argument parsing compatibility for Python 2.6 using optparse. + if sys.hexversion < 0x2070000: + parser = OptionParser(description=__doc__, + usage="usage: %prog [-h] location old new\n\n" + \ + " location: root directory (e.g. $D)\n" + \ + " old: original build prefix (e.g. /)\n" + \ + " new: new install prefix (e.g. $EPREFIX)") + + (opts, args) = parser.parse_args() + + if len(args) != 3: + parser.print_usage() + print("%s: error: expected 3 arguments, got %i" + % (__file__, len(args))) + return + + location, old, new = args[0:3] + else: + raise is_text_file = IsTextFile() @@ -178,5 +218,5 @@ def main(argv): return os.EX_OK -if __name__ == "__main__": - sys.exit(main(sys.argv)) +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/bin/clean_locks b/bin/clean_locks index 8c4299c92..3e969f2c6 100755 --- a/bin/clean_locks +++ b/bin/clean_locks @@ -1,21 +1,17 @@ -#!/usr/bin/python -O -# Copyright 1999-2006 Gentoo Foundation +#!/usr/bin/python -bO +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import print_function import sys, errno -try: - import portage -except ImportError: - from os import path as osp - sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) - import portage - -from portage import os +from os import path as osp +pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym") +sys.path.insert(0, pym_path) +import portage +portage._internal_caller = True if not sys.argv[1:] or "--help" in sys.argv or "-h" in sys.argv: - import portage print() print("You must specify directories with hardlink-locks to clean.") print("You may optionally specify --force, which will remove all") @@ -26,11 +22,11 @@ if not sys.argv[1:] or "--help" in sys.argv or "-h" in sys.argv: print("%s --force %s/.locks" % (sys.argv[0], portage.settings["DISTDIR"])) print() sys.exit(1) - + force = False if "--force" in sys.argv[1:]: force=True - + for x in sys.argv[1:]: if x == "--force": continue @@ -38,7 +34,7 @@ for x in sys.argv[1:]: for y in portage.locks.hardlock_cleanup(x, remove_all_locks=force): print(y) print() - + except OSError as e: if e.errno in (errno.ENOENT, errno.ENOTDIR): print("!!! %s is not a directory or does not exist" % x) diff --git a/bin/dispatch-conf b/bin/dispatch-conf index 139a001e8..4b0c0ac8f 100755 --- a/bin/dispatch-conf +++ b/bin/dispatch-conf @@ -1,5 +1,5 @@ -#!/usr/bin/python -O -# Copyright 1999-2011 Gentoo Foundation +#!/usr/bin/python -bO +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # @@ -16,19 +16,15 @@ from __future__ import print_function from stat import ST_GID, ST_MODE, ST_UID from random import random import atexit, re, shutil, stat, sys - -try: - import portage -except ImportError: - from os import path as osp - sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) - import portage - +from os import path as osp +pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym") +sys.path.insert(0, pym_path) +import portage +portage._internal_caller = True from portage import os -from portage import dispatch_conf from portage import _unicode_decode from portage.dispatch_conf import diffstatusoutput -from portage.process import find_binary +from portage.process import find_binary, spawn FIND_EXTANT_CONFIGS = "find '%s' %s -name '._cfg????_%s' ! -name '.*~' ! -iname '.*.bak' -print" DIFF_CONTENTS = "diff -Nu '%s' '%s'" @@ -83,7 +79,7 @@ class dispatch: confs = [] count = 0 - config_root = portage.const.EPREFIX or os.sep + config_root = portage.settings["EPREFIX"] or os.sep self.options = portage.dispatch_conf.read_config(MANDATORY_OPTS) if "log-file" in self.options: @@ -411,7 +407,8 @@ class dispatch: def do_help (self): - print(); print + print() + print() print(' u -- update current config with new config and continue') print(' z -- zap (delete) new config and continue') @@ -431,7 +428,7 @@ class dispatch: def getch (): # from ASPN - Danny Yoo # - import sys, tty, termios + import tty, termios fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) @@ -456,17 +453,18 @@ def clear_screen(): pass os.system("clear 2>/dev/null") -from portage.process import find_binary, spawn shell = os.environ.get("SHELL") if not shell or not os.access(shell, os.EX_OK): shell = find_binary("sh") def spawn_shell(cmd): if shell: + sys.__stdout__.flush() + sys.__stderr__.flush() spawn([shell, "-c", cmd], env=os.environ, - fd_pipes = { 0 : sys.stdin.fileno(), - 1 : sys.stdout.fileno(), - 2 : sys.stderr.fileno()}) + fd_pipes = { 0 : portage._get_stdin().fileno(), + 1 : sys.__stdout__.fileno(), + 2 : sys.__stderr__.fileno()}) else: os.system(cmd) diff --git a/bin/dohtml.py b/bin/dohtml.py index 3e80ef5f6..5359f5e89 100755 --- a/bin/dohtml.py +++ b/bin/dohtml.py @@ -1,5 +1,5 @@ -#!/usr/bin/python -# Copyright 1999-2012 Gentoo Foundation +#!/usr/bin/python -b +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # @@ -31,13 +31,25 @@ from __future__ import print_function import os +import shutil import sys +from portage.util import normalize_path + +# Change back to original cwd _after_ all imports (bug #469338). +os.chdir(os.environ["__PORTAGE_HELPER_CWD"]) + def dodir(path): - os.spawnlp(os.P_WAIT, "install", "install", "-d", path) + try: + os.makedirs(path, 0o755) + except OSError: + if not os.path.isdir(path): + raise + os.chmod(path, 0o755) def dofile(src,dst): - os.spawnlp(os.P_WAIT, "install", "install", "-m0644", src, dst) + shutil.copy(src, dst) + os.chmod(dst, 0o644) def eqawarn(lines): cmd = "source '%s/isolated-functions.sh' ; " % \ @@ -55,14 +67,18 @@ unwarned_skipped_files = os.environ.get("PORTAGE_DOHTML_UNWARNED_SKIPPED_FILES", def install(basename, dirname, options, prefix=""): fullpath = basename if prefix: - fullpath = prefix + "/" + fullpath + fullpath = os.path.join(prefix, fullpath) if dirname: - fullpath = dirname + "/" + fullpath + fullpath = os.path.join(dirname, fullpath) if options.DOCDESTTREE: - destdir = options.ED + "usr/share/doc/" + options.PF + "/" + options.DOCDESTTREE + "/" + options.doc_prefix + "/" + prefix + desttree = options.DOCDESTTREE else: - destdir = options.ED + "usr/share/doc/" + options.PF + "/html/" + options.doc_prefix + "/" + prefix + desttree = "html" + + destdir = os.path.join(options.ED, "usr", "share", "doc", + options.PF.lstrip(os.sep), desttree.lstrip(os.sep), + options.doc_prefix.lstrip(os.sep), prefix).rstrip(os.sep) if not os.path.exists(fullpath): sys.stderr.write("!!! dohtml: %s does not exist\n" % fullpath) @@ -71,14 +87,15 @@ def install(basename, dirname, options, prefix=""): ext = os.path.splitext(basename)[1][1:] if ext in options.allowed_exts or basename in options.allowed_files: dodir(destdir) - dofile(fullpath, destdir + "/" + basename) + dofile(fullpath, os.path.join(destdir, basename)) elif warn_on_skipped_files and ext not in unwarned_skipped_extensions and basename not in unwarned_skipped_files: skipped_files.append(fullpath) elif options.recurse and os.path.isdir(fullpath) and \ basename not in options.disallowed_dirs: for i in os.listdir(fullpath): pfx = basename - if prefix: pfx = prefix + "/" + pfx + if prefix: + pfx = os.path.join(prefix, pfx) install(i, dirname, options, pfx) elif not options.recurse and os.path.isdir(fullpath): global skipped_directories @@ -97,16 +114,22 @@ class OptionsClass: if "PF" in os.environ: self.PF = os.environ["PF"] + if self.PF: + self.PF = normalize_path(self.PF) if "force-prefix" not in os.environ.get("FEATURES", "").split() and \ os.environ.get("EAPI", "0") in ("0", "1", "2"): self.ED = os.environ.get("D", "") else: self.ED = os.environ.get("ED", "") + if self.ED: + self.ED = normalize_path(self.ED) if "_E_DOCDESTTREE_" in os.environ: self.DOCDESTTREE = os.environ["_E_DOCDESTTREE_"] + if self.DOCDESTTREE: + self.DOCDESTTREE = normalize_path(self.DOCDESTTREE) self.allowed_exts = ['css', 'gif', 'htm', 'html', 'jpeg', 'jpg', 'js', 'png'] - if os.environ.get("EAPI", "0") in ("4-python",): + if os.environ.get("EAPI", "0") in ("4-python", "5-progress"): self.allowed_exts += ['ico', 'svg', 'xhtml', 'xml'] self.allowed_files = [] self.disallowed_dirs = ['CVS'] @@ -153,6 +176,8 @@ def parse_args(): sys.exit(0) elif arg == "-p": options.doc_prefix = sys.argv[x] + if options.doc_prefix: + options.doc_prefix = normalize_path(options.doc_prefix) else: values = sys.argv[x].split(",") if arg == "-A": @@ -179,8 +204,17 @@ def main(): print("Allowed files :", options.allowed_files) success = False + endswith_slash = (os.sep, os.sep + ".") for x in args: + trailing_slash = x.endswith(endswith_slash) + x = normalize_path(x) + if trailing_slash: + # Modify behavior of basename and dirname + # as noted in bug #425214, causing foo/ to + # behave similarly to the way that foo/* + # behaves. + x += os.sep basename = os.path.basename(x) dirname = os.path.dirname(x) success |= install(basename, dirname, options) diff --git a/bin/eapi.sh b/bin/eapi.sh new file mode 100644 index 000000000..623b89fee --- /dev/null +++ b/bin/eapi.sh @@ -0,0 +1,145 @@ +#!/bin/bash +# Copyright 2012 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +# PHASES + +___eapi_has_pkg_pretend() { + [[ ! ${1-${EAPI}} =~ ^(0|1|2|3)$ ]] +} + +___eapi_has_src_prepare() { + [[ ! ${1-${EAPI}} =~ ^(0|1)$ ]] +} + +___eapi_has_src_configure() { + [[ ! ${1-${EAPI}} =~ ^(0|1)$ ]] +} + +___eapi_default_src_test_disables_parallel_jobs() { + [[ ${1-${EAPI}} =~ ^(0|1|2|3|4|4-python|4-slot-abi)$ ]] +} + +___eapi_has_S_WORKDIR_fallback() { + [[ ${1-${EAPI}} =~ ^(0|1|2|3)$ ]] +} + +# VARIABLES + +___eapi_has_prefix_variables() { + [[ ! ${1-${EAPI}} =~ ^(0|1|2)$ || " ${FEATURES} " == *" force-prefix "* ]] +} + +___eapi_has_HDEPEND() { + [[ ${1-${EAPI}} =~ ^(5-hdepend)$ ]] +} + +___eapi_has_RDEPEND_DEPEND_fallback() { + [[ ${1-${EAPI}} =~ ^(0|1|2|3)$ ]] +} + +# HELPERS PRESENCE + +___eapi_has_dohard() { + [[ ${1-${EAPI}} =~ ^(0|1|2|3)$ ]] +} + +___eapi_has_dosed() { + [[ ${1-${EAPI}} =~ ^(0|1|2|3)$ ]] +} + +___eapi_has_docompress() { + [[ ! ${1-${EAPI}} =~ ^(0|1|2|3)$ ]] +} + +___eapi_has_nonfatal() { + [[ ! ${1-${EAPI}} =~ ^(0|1|2|3)$ ]] +} + +___eapi_has_doheader() { + [[ ! ${1-${EAPI}} =~ ^(0|1|2|3|4|4-python|4-slot-abi)$ ]] +} + +___eapi_has_usex() { + [[ ! ${1-${EAPI}} =~ ^(0|1|2|3|4|4-python|4-slot-abi)$ ]] +} + +___eapi_has_master_repositories() { + [[ ${1-${EAPI}} =~ ^(5-progress)$ ]] +} + +___eapi_has_repository_path() { + [[ ${1-${EAPI}} =~ ^(5-progress)$ ]] +} + +___eapi_has_available_eclasses() { + [[ ${1-${EAPI}} =~ ^(5-progress)$ ]] +} + +___eapi_has_eclass_path() { + [[ ${1-${EAPI}} =~ ^(5-progress)$ ]] +} + +___eapi_has_license_path() { + [[ ${1-${EAPI}} =~ ^(5-progress)$ ]] +} + +___eapi_has_package_manager_build_user() { + [[ ${1-${EAPI}} =~ ^(5-progress)$ ]] +} + +___eapi_has_package_manager_build_group() { + [[ ${1-${EAPI}} =~ ^(5-progress)$ ]] +} + +# HELPERS BEHAVIOR + +___eapi_best_version_and_has_version_support_--host-root() { + [[ ! ${1-${EAPI}} =~ ^(0|1|2|3|4|4-python|4-slot-abi)$ ]] +} + +___eapi_unpack_supports_xz() { + [[ ! ${1-${EAPI}} =~ ^(0|1|2)$ ]] +} + +___eapi_econf_passes_--disable-dependency-tracking() { + [[ ! ${1-${EAPI}} =~ ^(0|1|2|3)$ ]] +} + +___eapi_econf_passes_--disable-silent-rules() { + [[ ! ${1-${EAPI}} =~ ^(0|1|2|3|4|4-python|4-slot-abi)$ ]] +} + +___eapi_use_enable_and_use_with_support_empty_third_argument() { + [[ ! ${1-${EAPI}} =~ ^(0|1|2|3)$ ]] +} + +___eapi_dodoc_supports_-r() { + [[ ! ${1-${EAPI}} =~ ^(0|1|2|3)$ ]] +} + +___eapi_doins_and_newins_preserve_symlinks() { + [[ ! ${1-${EAPI}} =~ ^(0|1|2|3)$ ]] +} + +___eapi_newins_supports_reading_from_standard_input() { + [[ ! ${1-${EAPI}} =~ ^(0|1|2|3|4|4-python|4-slot-abi)$ ]] +} + +___eapi_helpers_can_die() { + [[ ! ${1-${EAPI}} =~ ^(0|1|2|3)$ ]] +} + +___eapi_disallows_helpers_in_global_scope() { + [[ ${1-${EAPI}} =~ ^(4-python|5-progress)$ ]] +} + +___eapi_unpack_is_case_sensitive() { + [[ ${1-${EAPI}} =~ ^(0|1|2|3|4|4-python|4-slot-abi|5|5-hdepend)$ ]] +} + +# OTHERS + +___eapi_enables_globstar() { + [[ ${1-${EAPI}} =~ ^(4-python|5-progress)$ ]] +} diff --git a/bin/ebuild b/bin/ebuild index 65e5bef63..8f4b103c6 100755 --- a/bin/ebuild +++ b/bin/ebuild @@ -1,5 +1,5 @@ -#!/usr/bin/python -O -# Copyright 1999-2012 Gentoo Foundation +#!/usr/bin/python -bO +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import print_function @@ -10,7 +10,7 @@ import sys # This block ensures that ^C interrupts are handled quietly. try: - def exithandler(signum,frame): + def exithandler(signum, _frame): signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGTERM, signal.SIG_IGN) sys.exit(128 + signum) @@ -24,7 +24,7 @@ try: except KeyboardInterrupt: sys.exit(128 + signal.SIGINT) -def debug_signal(signum, frame): +def debug_signal(_signum, _frame): import pdb pdb.set_trace() @@ -35,51 +35,50 @@ else: signal.signal(debug_signum, debug_signal) -import imp import io -import optparse import os +from os import path as osp +pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym") +sys.path.insert(0, pym_path) +import portage +portage._internal_caller = True +from portage import os +from portage import _encodings +from portage import _shell_quote +from portage import _unicode_decode +from portage import _unicode_encode +from portage.const import VDB_PATH +from portage.util._argparse import ArgumentParser +from _emerge.Package import Package +from _emerge.RootConfig import RootConfig description = "See the ebuild(1) man page for more info" usage = "Usage: ebuild <ebuild file> <command> [command] ..." -parser = optparse.OptionParser(description=description, usage=usage) +parser = ArgumentParser(description=description, usage=usage) force_help = "When used together with the digest or manifest " + \ "command, this option forces regeneration of digests for all " + \ "distfiles associated with the current ebuild. Any distfiles " + \ "that do not already exist in ${DISTDIR} will be automatically fetched." -parser.add_option("--force", help=force_help, action="store_true", dest="force") -parser.add_option("--color", help="enable or disable color output", - type="choice", choices=("y", "n")) -parser.add_option("--debug", help="show debug output", - action="store_true", dest="debug") -parser.add_option("--version", help="show version and exit", - action="store_true", dest="version") -parser.add_option("--ignore-default-opts", +parser.add_argument("--force", help=force_help, action="store_true") +parser.add_argument("--color", help="enable or disable color output", + choices=("y", "n")) +parser.add_argument("--debug", help="show debug output", + action="store_true") +parser.add_argument("--version", help="show version and exit", + action="store_true") +parser.add_argument("--ignore-default-opts", action="store_true", help="do not use the EBUILD_DEFAULT_OPTS environment variable") -parser.add_option("--skip-manifest", help="skip all manifest checks", - action="store_true", dest="skip_manifest") - -opts, pargs = parser.parse_args(args=sys.argv[1:]) +parser.add_argument("--skip-manifest", help="skip all manifest checks", + action="store_true") -try: - import portage -except ImportError: - from os import path as osp - sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) - import portage +opts, pargs = parser.parse_known_args(args=sys.argv[1:]) -portage.dep._internal_warnings = True -from portage import os -from portage import _encodings -from portage import _shell_quote -from portage import _unicode_decode -from portage import _unicode_encode -from portage.const import VDB_PATH -from _emerge.Package import Package -from _emerge.RootConfig import RootConfig +def err(txt): + portage.writemsg('ebuild: %s\n' % (txt,), noiselevel=-1) + sys.exit(1) if opts.version: print("Portage", portage.VERSION) @@ -89,8 +88,9 @@ if len(pargs) < 2: parser.error("missing required args") if not opts.ignore_default_opts: - default_opts = portage.settings.get("EBUILD_DEFAULT_OPTS", "").split() - opts, pargs = parser.parse_args(default_opts + sys.argv[1:]) + default_opts = portage.util.shlex_split( + portage.settings.get("EBUILD_DEFAULT_OPTS", "")) + opts, pargs = parser.parse_known_args(default_opts + sys.argv[1:]) debug = opts.debug force = opts.force @@ -119,9 +119,7 @@ if ebuild.endswith(".ebuild"): pf = os.path.basename(ebuild)[:-7] if pf is None: - portage.writemsg("'%s' does not end with '.ebuild'.\n" % \ - (ebuild,), noiselevel=-1) - sys.exit(1) + err("%s: does not end with '.ebuild'" % (ebuild,)) if not os.path.isabs(ebuild): mycwd = os.getcwd() @@ -160,15 +158,14 @@ if ebuild_portdir != vdb_path and \ encoding=_encodings['content'], errors='strict') print("Appending %s to PORTDIR_OVERLAY..." % ebuild_portdir) - imp.reload(portage) + portage._reset_legacy_globals() myrepo = None if ebuild_portdir != vdb_path: myrepo = portage.portdb.getRepositoryName(ebuild_portdir) if not os.path.exists(ebuild): - print("'%s' does not exist." % ebuild) - sys.exit(1) + err('%s: does not exist' % (ebuild,)) ebuild_split = ebuild.split("/") cpv = "%s/%s" % (ebuild_split[-3], pf) @@ -179,8 +176,7 @@ with io.open(_unicode_encode(ebuild, encoding=_encodings['fs'], errors='strict') if eapi is None: eapi = "0" if not portage.catpkgsplit(cpv, eapi=eapi): - print("!!! %s does not follow correct package syntax." % (cpv)) - sys.exit(1) + err('%s: %s: does not follow correct package syntax' % (ebuild, cpv)) if ebuild.startswith(vdb_path): mytree = "vartree" @@ -189,8 +185,7 @@ if ebuild.startswith(vdb_path): portage_ebuild = portage.db[portage.root][mytree].dbapi.findname(cpv, myrepo=myrepo) if os.path.realpath(portage_ebuild) != ebuild: - print("!!! Portage seems to think that %s is at %s" % (cpv, portage_ebuild)) - sys.exit(1) + err('Portage seems to think that %s is at %s' % (cpv, portage_ebuild)) else: mytree = "porttree" @@ -199,12 +194,10 @@ else: portage_ebuild = portage.portdb.findname(cpv, myrepo=myrepo) if not portage_ebuild or portage_ebuild != ebuild: - print("!!! %s does not seem to have a valid PORTDIR structure." % ebuild) - sys.exit(1) + err('%s: does not seem to have a valid PORTDIR structure' % (ebuild,)) if len(pargs) > 1 and "config" in pargs: - print("config must be called on it's own, not combined with any other phase") - sys.exit(1) + err('"config" must not be called with any other phase') def discard_digests(myebuild, mysettings, mydbapi): """Discard all distfiles digests for the given ebuild. This is useful when @@ -313,14 +306,16 @@ def stale_env_warning(): if ebuild_changed: open(os.path.join(tmpsettings['PORTAGE_BUILDDIR'], - '.ebuild_changed'), 'w') + '.ebuild_changed'), 'w').close() from portage.exception import PermissionDenied, \ PortagePackageException, UnsupportedAPIException -if 'digest' in tmpsettings.features and \ - not set(["digest", "manifest"]).intersection(pargs): - pargs = ['digest'] + pargs +if 'digest' in tmpsettings.features: + if pargs and pargs[0] not in ("digest", "manifest"): + pargs = ['digest'] + pargs + # We only need to build digests on the first pass. + tmpsettings.features.discard('digest') checked_for_stale_env = False @@ -334,7 +329,7 @@ for arg in pargs: if arg in ("digest", "manifest") and force: discard_digests(ebuild, tmpsettings, portage.portdb) - a = portage.doebuild(ebuild, arg, portage.root, tmpsettings, + a = portage.doebuild(ebuild, arg, settings=tmpsettings, debug=debug, tree=mytree, vartree=portage.db[portage.root]['vartree']) except KeyboardInterrupt: diff --git a/bin/ebuild-helpers/4/dodoc b/bin/ebuild-helpers/4/dodoc deleted file mode 120000 index 35080ada3..000000000 --- a/bin/ebuild-helpers/4/dodoc +++ /dev/null @@ -1 +0,0 @@ -../doins
\ No newline at end of file diff --git a/bin/ebuild-helpers/4/dohard b/bin/ebuild-helpers/4/dohard deleted file mode 120000 index 1a6b57a39..000000000 --- a/bin/ebuild-helpers/4/dohard +++ /dev/null @@ -1 +0,0 @@ -../../banned-helper
\ No newline at end of file diff --git a/bin/ebuild-helpers/4/dosed b/bin/ebuild-helpers/4/dosed deleted file mode 120000 index 1a6b57a39..000000000 --- a/bin/ebuild-helpers/4/dosed +++ /dev/null @@ -1 +0,0 @@ -../../banned-helper
\ No newline at end of file diff --git a/bin/ebuild-helpers/4/prepalldocs b/bin/ebuild-helpers/4/prepalldocs deleted file mode 120000 index 1a6b57a39..000000000 --- a/bin/ebuild-helpers/4/prepalldocs +++ /dev/null @@ -1 +0,0 @@ -../../banned-helper
\ No newline at end of file diff --git a/bin/ebuild-helpers/sed b/bin/ebuild-helpers/bsd/sed index b21e8569c..01b88471d 100755 --- a/bin/ebuild-helpers/sed +++ b/bin/ebuild-helpers/bsd/sed @@ -1,27 +1,27 @@ #!/bin/bash -# Copyright 2007 Gentoo Foundation +# Copyright 2007-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 scriptpath=${BASH_SOURCE[0]} scriptname=${scriptpath##*/} -if [[ sed == ${scriptname} ]] && [[ -n ${ESED} ]]; then +if [[ sed == ${scriptname} && -n ${ESED} ]]; then exec ${ESED} "$@" elif type -P g${scriptname} > /dev/null ; then exec g${scriptname} "$@" else old_IFS="${IFS}" IFS=":" - + for path in $PATH; do - [[ ${path}/${scriptname} == ${scriptpath} ]] && continue if [[ -x ${path}/${scriptname} ]]; then - exec ${path}/${scriptname} "$@" + [[ ${path}/${scriptname} -ef ${scriptpath} ]] && continue + exec "${path}/${scriptname}" "$@" exit 0 fi done - + IFS="${old_IFS}" fi - + exit 1 diff --git a/bin/ebuild-helpers/dobin b/bin/ebuild-helpers/dobin index f90d8933c..0ba1eb0c4 100755 --- a/bin/ebuild-helpers/dobin +++ b/bin/ebuild-helpers/dobin @@ -1,19 +1,20 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh if [[ $# -lt 1 ]] ; then - helpers_die "${0##*/}: at least one argument needed" + __helpers_die "${0##*/}: at least one argument needed" exit 1 fi -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi if [[ ! -d ${ED}${DESTTREE}/bin ]] ; then - install -d "${ED}${DESTTREE}/bin" || { helpers_die "${0##*/}: failed to install ${ED}${DESTTREE}/bin"; exit 2; } + install -d "${ED}${DESTTREE}/bin" || { __helpers_die "${0##*/}: failed to install ${ED}${DESTTREE}/bin"; exit 2; } fi ret=0 @@ -28,5 +29,5 @@ for x in "$@" ; do ((ret|=$?)) done -[[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" +[[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit ${ret} diff --git a/bin/ebuild-helpers/doconfd b/bin/ebuild-helpers/doconfd index e14600022..a3c09a50e 100755 --- a/bin/ebuild-helpers/doconfd +++ b/bin/ebuild-helpers/doconfd @@ -4,7 +4,7 @@ if [[ $# -lt 1 ]] ; then source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh - helpers_die "${0##*/}: at least one argument needed" + __helpers_die "${0##*/}: at least one argument needed" exit 1 fi diff --git a/bin/ebuild-helpers/dodir b/bin/ebuild-helpers/dodir index 90a3efed4..e03ba9a58 100755 --- a/bin/ebuild-helpers/dodir +++ b/bin/ebuild-helpers/dodir @@ -1,13 +1,14 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi install -d ${DIROPTIONS} "${@/#/${ED}/}" ret=$? -[[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" +[[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit $ret diff --git a/bin/ebuild-helpers/dodoc b/bin/ebuild-helpers/dodoc index 1f333a615..99122c443 100755 --- a/bin/ebuild-helpers/dodoc +++ b/bin/ebuild-helpers/dodoc @@ -1,16 +1,24 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh +if ___eapi_dodoc_supports_-r; then + exec \ + env \ + __PORTAGE_HELPER="dodoc" \ + doins "$@" +fi + if [ $# -lt 1 ] ; then - helpers_die "${0##*/}: at least one argument needed" - exit 1 + __helpers_die "${0##*/}: at least one argument needed" + exit 1 fi -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi dir="${ED}usr/share/doc/${PF}/${_E_DOCDESTTREE_}" if [ ! -d "${dir}" ] ; then @@ -30,5 +38,5 @@ for x in "$@" ; do fi done -[[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" +[[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit ${ret} diff --git a/bin/ebuild-helpers/doenvd b/bin/ebuild-helpers/doenvd index 28ab5d234..9287933a9 100755 --- a/bin/ebuild-helpers/doenvd +++ b/bin/ebuild-helpers/doenvd @@ -4,7 +4,7 @@ if [[ $# -lt 1 ]] ; then source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh - helpers_die "${0##*/}: at least one argument needed" + __helpers_die "${0##*/}: at least one argument needed" exit 1 fi diff --git a/bin/ebuild-helpers/doexe b/bin/ebuild-helpers/doexe index fb228f905..c34fcae74 100755 --- a/bin/ebuild-helpers/doexe +++ b/bin/ebuild-helpers/doexe @@ -1,23 +1,23 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh if [[ $# -lt 1 ]] ; then - helpers_die "${0##*/}: at least one argument needed" + __helpers_die "${0##*/}: at least one argument needed" exit 1 fi -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi if [[ ! -d ${ED}${_E_EXEDESTTREE_} ]] ; then install -d "${ED}${_E_EXEDESTTREE_}" fi -TMP=$T/.doexe_tmp -mkdir "$TMP" +TMP=$(mktemp -d "${T}/.doexe_tmp_XXXXXX") ret=0 @@ -26,7 +26,7 @@ for x in "$@" ; do cp "$x" "$TMP" mysrc=$TMP/${x##*/} elif [ -d "${x}" ] ; then - vecho "doexe: warning, skipping directory ${x}" + __vecho "doexe: warning, skipping directory ${x}" continue else mysrc="${x}" @@ -42,5 +42,5 @@ done rm -rf "$TMP" -[[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" +[[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit $ret diff --git a/bin/ebuild-helpers/dohard b/bin/ebuild-helpers/dohard index b52fd7c00..e0a44faf1 100755 --- a/bin/ebuild-helpers/dohard +++ b/bin/ebuild-helpers/dohard @@ -1,14 +1,22 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 +source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh + +if ! ___eapi_has_dohard; then + die "'${0##*/}' has been banned for EAPI '$EAPI'" + exit 1 +fi + if [[ $# -ne 2 ]] ; then echo "$0: two arguments needed" 1>&2 exit 1 fi -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi destdir=${2%/*} [[ ! -d ${ED}${destdir} ]] && dodir "${destdir}" diff --git a/bin/ebuild-helpers/doheader b/bin/ebuild-helpers/doheader new file mode 100755 index 000000000..37953658b --- /dev/null +++ b/bin/ebuild-helpers/doheader @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright 1999-2012 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh + +if ! ___eapi_has_doheader; then + die "${0##*/} is not supported in EAPI ${EAPI}" +fi + +if [[ $# -lt 1 ]] || [[ $1 == -r && $# -lt 2 ]] ; then + __helpers_die "${0##*/}: at least one argument needed" + exit 1 +fi + +exec \ +env \ +INSDESTTREE="/usr/include/" \ +doins "$@" diff --git a/bin/ebuild-helpers/dohtml b/bin/ebuild-helpers/dohtml index 630629a4b..75d3d00ab 100755 --- a/bin/ebuild-helpers/dohtml +++ b/bin/ebuild-helpers/dohtml @@ -1,14 +1,19 @@ #!/bin/bash -# Copyright 2009-2010 Gentoo Foundation +# Copyright 2009-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh PORTAGE_BIN_PATH=${PORTAGE_BIN_PATH:-/usr/lib/portage/bin} PORTAGE_PYM_PATH=${PORTAGE_PYM_PATH:-/usr/lib/portage/pym} -PYTHONPATH=$PORTAGE_PYM_PATH${PYTHONPATH:+:}$PYTHONPATH \ +# Use safe cwd, avoiding unsafe import for bug #469338. +export __PORTAGE_HELPER_CWD=${PWD} +cd "${PORTAGE_PYM_PATH}" +PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \ "${PORTAGE_PYTHON:-/usr/bin/python}" "$PORTAGE_BIN_PATH/dohtml.py" "$@" ret=$? -[[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" +# Restore cwd for display by __helpers_die +cd "${__PORTAGE_HELPER_CWD}" +[[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit $ret diff --git a/bin/ebuild-helpers/doinfo b/bin/ebuild-helpers/doinfo index 8fd7d45f8..2edbdc592 100755 --- a/bin/ebuild-helpers/doinfo +++ b/bin/ebuild-helpers/doinfo @@ -1,19 +1,20 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh if [[ -z $1 ]] ; then - helpers_die "${0##*/}: at least one argument needed" - exit 1 + __helpers_die "${0##*/}: at least one argument needed" + exit 1 fi -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi if [[ ! -d ${ED}usr/share/info ]] ; then - install -d "${ED}usr/share/info" || { helpers_die "${0##*/}: failed to install ${ED}usr/share/info"; exit 1; } + install -d "${ED}usr/share/info" || { __helpers_die "${0##*/}: failed to install ${ED}usr/share/info"; exit 1; } fi install -m0644 "$@" "${ED}usr/share/info" @@ -22,6 +23,6 @@ if [ $rval -ne 0 ] ; then for x in "$@" ; do [ -e "$x" ] || echo "!!! ${0##*/}: $x does not exist" 1>&2 done - helpers_die "${0##*/} failed" + __helpers_die "${0##*/} failed" fi exit $rval diff --git a/bin/ebuild-helpers/doinitd b/bin/ebuild-helpers/doinitd index b711e190a..476b858f6 100755 --- a/bin/ebuild-helpers/doinitd +++ b/bin/ebuild-helpers/doinitd @@ -4,7 +4,7 @@ if [[ $# -lt 1 ]] ; then source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh - helpers_die "${0##*/}: at least one argument needed" + __helpers_die "${0##*/}: at least one argument needed" exit 1 fi diff --git a/bin/ebuild-helpers/doins b/bin/ebuild-helpers/doins index 443bfdb21..c60e05789 100755 --- a/bin/ebuild-helpers/doins +++ b/bin/ebuild-helpers/doins @@ -1,14 +1,17 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh -if [[ ${0##*/} == dodoc ]] ; then +helper=${__PORTAGE_HELPER:-${0##*/}} + +if [[ ${helper} == dodoc ]] ; then if [ $# -eq 0 ] ; then # default_src_install may call dodoc with no arguments # when DOC is defined but empty, so simply return # sucessfully in this case. + eqawarn "QA Notice: dodoc called with no arguments" exit 0 fi export INSOPTIONS=-m0644 @@ -16,7 +19,7 @@ if [[ ${0##*/} == dodoc ]] ; then fi if [ $# -lt 1 ] ; then - helpers_die "${0##*/}: at least one argument needed" + __helpers_die "${helper}: at least one argument needed" exit 1 fi @@ -27,28 +30,26 @@ else DOINSRECUR=n fi -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) export ED="${D}" ;; esac +if ! ___eapi_has_prefix_variables; then + export ED="${D}" +fi if [[ ${INSDESTTREE#${ED}} != "${INSDESTTREE}" ]]; then - vecho "-------------------------------------------------------" 1>&2 - vecho "You should not use \${D} or \${ED} with helpers." 1>&2 - vecho " --> ${INSDESTTREE}" 1>&2 - vecho "-------------------------------------------------------" 1>&2 - helpers_die "${0##*/} used with \${D} or \${ED}" + __vecho "-------------------------------------------------------" 1>&2 + __vecho "You should not use \${D} or \${ED} with helpers." 1>&2 + __vecho " --> ${INSDESTTREE}" 1>&2 + __vecho "-------------------------------------------------------" 1>&2 + __helpers_die "${helper} used with \${D} or \${ED}" exit 1 fi -case "$EAPI" in - 0|1|2|3|3_pre2) - PRESERVE_SYMLINKS=n - ;; - *) - PRESERVE_SYMLINKS=y - ;; -esac +if ___eapi_doins_and_newins_preserve_symlinks; then + PRESERVE_SYMLINKS=y +else + PRESERVE_SYMLINKS=n +fi -export TMP=$T/.doins_tmp +export TMP=$(mktemp -d "${T}/.doins_tmp_XXXXXX") # Use separate directories to avoid potential name collisions. mkdir -p "$TMP"/{1,2} @@ -79,7 +80,7 @@ _doins() { install ${INSOPTIONS} "${mysrc}" "${ED}${INSDESTTREE}/${mydir}" rval=$? [[ -n ${cleanup} ]] && rm -f "${cleanup}" - [ $rval -ne 0 ] && echo "!!! ${0##*/}: $mysrc does not exist" 1>&2 + [ $rval -ne 0 ] && echo "!!! ${helper}: $mysrc does not exist" 1>&2 return $rval } @@ -99,8 +100,8 @@ for x in "$@" ; do if [[ $PRESERVE_SYMLINKS = n && -d $x ]] || \ [[ $PRESERVE_SYMLINKS = y && -d $x && ! -L $x ]] ; then if [ "${DOINSRECUR}" == "n" ] ; then - if [[ ${0##*/} == dodoc ]] ; then - echo "!!! ${0##*/}: $x is a directory" 1>&2 + if [[ ${helper} == dodoc ]] ; then + echo "!!! ${helper}: $x is a directory" 1>&2 ((failed|=1)) fi continue @@ -155,4 +156,4 @@ for x in "$@" ; do fi done rm -rf "$TMP" -[[ $failed -ne 0 || $success -eq 0 ]] && { helpers_die "${0##*/} failed"; exit 1; } || exit 0 +[[ $failed -ne 0 || $success -eq 0 ]] && { __helpers_die "${helper} failed"; exit 1; } || exit 0 diff --git a/bin/ebuild-helpers/dolib b/bin/ebuild-helpers/dolib index 9af541890..fd92d7f03 100755 --- a/bin/ebuild-helpers/dolib +++ b/bin/ebuild-helpers/dolib @@ -1,11 +1,12 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi # Setup ABI cruft LIBDIR_VAR="LIBDIR_${ABI}" @@ -19,11 +20,11 @@ libdir="${ED}${DESTTREE}/${CONF_LIBDIR}" if [[ $# -lt 1 ]] ; then - helpers_die "${0##*/}: at least one argument needed" + __helpers_die "${0##*/}: at least one argument needed" exit 1 fi if [[ ! -d ${libdir} ]] ; then - install -d "${libdir}" || { helpers_die "${0##*/}: failed to install ${libdir}"; exit 1; } + install -d "${libdir}" || { __helpers_die "${0##*/}: failed to install ${libdir}"; exit 1; } fi ret=0 @@ -42,5 +43,5 @@ for x in "$@" ; do ((ret|=$?)) done -[[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" +[[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit ${ret} diff --git a/bin/ebuild-helpers/doman b/bin/ebuild-helpers/doman index b4047ce40..d6808597a 100755 --- a/bin/ebuild-helpers/doman +++ b/bin/ebuild-helpers/doman @@ -1,16 +1,17 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh if [[ $# -lt 1 ]] ; then - helpers_die "${0##*/}: at least one argument needed" + __helpers_die "${0##*/}: at least one argument needed" exit 1 fi -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi i18n="" @@ -58,10 +59,10 @@ for x in "$@" ; do ((ret|=1)) fi else - vecho "doman: '${x}' is probably not a man page; skipping" 1>&2 + __vecho "doman: '${x}' is probably not a man page; skipping" 1>&2 ((ret|=1)) fi done -[[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" +[[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit ${ret} diff --git a/bin/ebuild-helpers/domo b/bin/ebuild-helpers/domo index d994343a9..9a8dda38a 100755 --- a/bin/ebuild-helpers/domo +++ b/bin/ebuild-helpers/domo @@ -1,17 +1,18 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh mynum=${#} if [ ${mynum} -lt 1 ] ; then - helpers_die "${0}: at least one argument needed" + __helpers_die "${0}: at least one argument needed" exit 1 fi -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi if [ ! -d "${ED}${DESTTREE}/share/locale" ] ; then install -d "${ED}${DESTTREE}/share/locale/" @@ -34,5 +35,5 @@ for x in "$@" ; do ((ret|=$?)) done -[[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" +[[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit $ret diff --git a/bin/ebuild-helpers/dosbin b/bin/ebuild-helpers/dosbin index d101c8a6d..361ca83ca 100755 --- a/bin/ebuild-helpers/dosbin +++ b/bin/ebuild-helpers/dosbin @@ -1,19 +1,20 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh if [[ $# -lt 1 ]] ; then - helpers_die "${0##*/}: at least one argument needed" + __helpers_die "${0##*/}: at least one argument needed" exit 1 fi -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi if [[ ! -d ${ED}${DESTTREE}/sbin ]] ; then - install -d "${ED}${DESTTREE}/sbin" || { helpers_die "${0##*/}: failed to install ${ED}${DESTTREE}/sbin"; exit 2; } + install -d "${ED}${DESTTREE}/sbin" || { __helpers_die "${0##*/}: failed to install ${ED}${DESTTREE}/sbin"; exit 2; } fi ret=0 @@ -28,5 +29,5 @@ for x in "$@" ; do ((ret|=$?)) done -[[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" +[[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit ${ret} diff --git a/bin/ebuild-helpers/dosed b/bin/ebuild-helpers/dosed index f202df7a7..7db062963 100755 --- a/bin/ebuild-helpers/dosed +++ b/bin/ebuild-helpers/dosed @@ -1,14 +1,22 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 +source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh + +if ! ___eapi_has_dosed; then + die "'${0##*/}' has been banned for EAPI '$EAPI'" + exit 1 +fi + if [[ $# -lt 1 ]] ; then echo "!!! ${0##*/}: at least one argument needed" >&2 exit 1 fi -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi ret=0 file_found=0 diff --git a/bin/ebuild-helpers/dosym b/bin/ebuild-helpers/dosym index 2489e22a2..649b100de 100755 --- a/bin/ebuild-helpers/dosym +++ b/bin/ebuild-helpers/dosym @@ -5,12 +5,13 @@ source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh if [[ $# -ne 2 ]] ; then - helpers_die "${0##*/}: two arguments needed" + __helpers_die "${0##*/}: two arguments needed" exit 1 fi -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi if [[ ${2} == */ ]] || \ [[ -d ${ED}${2} && ! -L ${ED}${2} ]] ; then @@ -26,5 +27,5 @@ target="${1}" ln -snf "${target}" "${ED}${2}" ret=$? -[[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" +[[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit $ret diff --git a/bin/ebuild-helpers/ecompress b/bin/ebuild-helpers/ecompress index b61421b00..71287b4b0 100755 --- a/bin/ebuild-helpers/ecompress +++ b/bin/ebuild-helpers/ecompress @@ -5,7 +5,7 @@ source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh if [[ -z $1 ]] ; then - helpers_die "${0##*/}: at least one argument needed" + __helpers_die "${0##*/}: at least one argument needed" exit 1 fi @@ -68,7 +68,7 @@ decompress_args() { case $1 in --suffix) - [[ -n $2 ]] && vecho "${0##*/}: --suffix takes no additional arguments" 1>&2 + [[ -n $2 ]] && __vecho "${0##*/}: --suffix takes no additional arguments" 1>&2 if [[ ! -e ${T}/.ecompress.suffix ]] ; then set -e @@ -93,7 +93,7 @@ case $1 in cat "${T}/.ecompress.suffix" ;; --bin) - [[ -n $2 ]] && vecho "${0##*/}: --bin takes no additional arguments" 1>&2 + [[ -n $2 ]] && __vecho "${0##*/}: --bin takes no additional arguments" 1>&2 echo "${PORTAGE_COMPRESS} ${PORTAGE_COMPRESS_FLAGS}" ;; @@ -104,18 +104,18 @@ case $1 in >> "$x" ((ret|=$?)) done - [[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" + [[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit $ret ;; --dequeue) - [[ -n $2 ]] && vecho "${0##*/}: --dequeue takes no additional arguments" 1>&2 + [[ -n $2 ]] && __vecho "${0##*/}: --dequeue takes no additional arguments" 1>&2 find "${D}" -name '*.ecompress.file' -print0 \ | sed -e 's:\.ecompress\.file::g' \ | ${XARGS} -0 ecompress find "${D}" -name '*.ecompress.file' -print0 | ${XARGS} -0 rm -f ;; --*) - helpers_die "${0##*/}: unknown arguments '$*'" + __helpers_die "${0##*/}: unknown arguments '$*'" exit 1 ;; *) @@ -155,7 +155,7 @@ case $1 in # Finally, let's actually do some real work "${PORTAGE_COMPRESS}" ${PORTAGE_COMPRESS_FLAGS} "$@" ret=$? - [[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" + [[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit $ret ;; esac diff --git a/bin/ebuild-helpers/ecompressdir b/bin/ebuild-helpers/ecompressdir index 6801a07d4..eca588869 100755 --- a/bin/ebuild-helpers/ecompressdir +++ b/bin/ebuild-helpers/ecompressdir @@ -1,18 +1,21 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/helper-functions.sh if [[ -z $1 ]] ; then - helpers_die "${0##*/}: at least one argument needed" + __helpers_die "${0##*/}: at least one argument needed" exit 1 fi -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} EPREFIX= ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} EPREFIX= +fi -case $1 in +SIZE_LIMIT='' +while [[ $# -gt 0 ]] ; do + case $1 in --ignore) shift for skip in "$@" ; do @@ -27,45 +30,66 @@ case $1 in set -- "${@/#/${ED}}" ret=0 for x in "$@" ; do - >> "$x" + # Stash the limit in the .dir file so we can reload it later. + printf "${SIZE_LIMIT}" > "${x}" ((ret|=$?)) done - [[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" + [[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit $ret ;; --dequeue) - [[ -n $2 ]] && vecho "${0##*/}: --dequeue takes no additional arguments" 1>&2 + [[ -n $2 ]] && __vecho "${0##*/}: --dequeue takes no additional arguments" 1>&2 find "${ED}" -name '*.ecompress.dir' -print0 \ | sed -e 's:\.ecompress\.dir::g' -e "s:${ED}:/:g" \ | ${XARGS} -0 ecompressdir find "${ED}" -name '*.ecompress.skip' -print0 | ${XARGS} -0 rm -f exit 0 ;; + --limit) + SIZE_LIMIT=$2 + shift + ;; --*) - helpers_die "${0##*/}: unknown arguments '$*'" + __helpers_die "${0##*/}: unknown arguments '$*'" exit 1 ;; -esac + *) + break + ;; + esac + shift +done # figure out the new suffix suffix=$(ecompress --suffix) -# funk_up_dir(action, suffix, binary) +# funk_up_dir(action, suffix, binary, [size_limit]) # - action: compress or decompress # - suffix: the compression suffix to work with # - binary: the program to execute that'll compress/decompress +# - size_limit: if compressing, skip files smaller than this # The directory we act on is implied in the ${dir} variable funk_up_dir() { - local act=$1 suffix=$2 binary=$3 + local act=$1 suffix=$2 binary=$3 size_limit=$4 local negate="" [[ ${act} == "compress" ]] && negate="!" local ret=0 # first we act on all the files - find "${dir}" -type f ${negate} -iname '*'${suffix} -print0 | ${XARGS} -0 ${binary} + local args=( + -type f + ${negate} -iname "*${suffix}" + ) + [[ -n ${size_limit} ]] && args+=( -size "+${size_limit}c" ) + find "${dir}" "${args[@]}" -print0 | ${XARGS} -0 ${binary} ((ret|=$?)) + # Repeat until nothing changes, in order to handle multiple + # levels of indirection (see bug #470916). + local -i indirection=0 + while true ; do + local something_changed= while read -r -d $'\0' brokenlink ; do [[ -e ${brokenlink} ]] && continue olddest=$(readlink "${brokenlink}") @@ -91,12 +115,22 @@ funk_up_dir() { else [[ -f "${dir}/${brokenlink%/*}/${newdest}" ]] || continue fi + something_changed=${brokenlink} rm -f "${brokenlink}" [[ ${act} == "compress" ]] \ && ln -snf "${newdest}" "${brokenlink}${suffix}" \ || ln -snf "${newdest}" "${brokenlink%${suffix}}" ((ret|=$?)) done < <(find "${dir}" -type l -print0) + [[ -n ${something_changed} ]] || break + (( indirection++ )) + if (( indirection >= 100 )) ; then + # Protect against possibility of a bug triggering an endless loop. + eerror "ecompressdir: too many levels of indirection for" \ + "'${actual_dir#${ED}}/${something_changed#./}'" + break + fi + done return ${ret} } @@ -133,13 +167,13 @@ decompressors=( ".lzma" "unxz -f" ) -multijob_init +__multijob_init for dir in "$@" ; do dir=${dir#/} dir="${ED}${dir}" if [[ ! -d ${dir} ]] ; then - vecho "${0##*/}: /${dir#${ED}} does not exist!" + __vecho "${0##*/}: /${dir#${ED}} does not exist!" continue fi cd "${dir}" @@ -151,24 +185,25 @@ for dir in "$@" ; do # since we've been requested to compress the whole dir, # delete any individual queued requests + size_limit=${SIZE_LIMIT:-$(<"${actual_dir}.ecompress.dir")} rm -f "${actual_dir}.ecompress.dir" find "${dir}" -type f -name '*.ecompress.file' -print0 | ${XARGS} -0 rm -f # not uncommon for packages to compress doc files themselves - for (( d = 0; d < ${#decompressors[@]}; d += 2 )) ; do + for (( i = 0; i < ${#decompressors[@]}; i += 2 )) ; do # It's faster to parallelize at this stage than to try to # parallelize the compressors. This is because the find|xargs # ends up launching less compressors overall, so the overhead # of forking children ends up dominating. ( - multijob_child_init + __multijob_child_init funk_up_dir "decompress" "${decompressors[i]}" "${decompressors[i+1]}" ) & - multijob_post_fork + __multijob_post_fork : $(( ret |= $? )) done - multijob_finish + __multijob_finish : $(( ret |= $? )) # forcibly break all hard links as some compressors whine about it @@ -177,8 +212,8 @@ for dir in "$@" ; do # now lets do our work if [[ -n ${suffix} ]] ; then - vecho "${0##*/}: $(ecompress --bin) /${actual_dir#${ED}}" - funk_up_dir "compress" "${suffix}" "ecompress" + __vecho "${0##*/}: $(ecompress --bin) /${actual_dir#${ED}}" + funk_up_dir "compress" "${suffix}" "ecompress" "${size_limit}" : $(( ret |= $? )) fi @@ -186,5 +221,5 @@ for dir in "$@" ; do restore_skip_dirs done -[[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" +[[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit ${ret} diff --git a/bin/ebuild-helpers/emake b/bin/ebuild-helpers/emake index d842781a7..69d836f1a 100755 --- a/bin/ebuild-helpers/emake +++ b/bin/ebuild-helpers/emake @@ -24,5 +24,5 @@ fi ${MAKE:-make} ${MAKEOPTS} ${EXTRA_EMAKE} "$@" ret=$? -[[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" +[[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit $ret diff --git a/bin/ebuild-helpers/fowners b/bin/ebuild-helpers/fowners index a213c9eb1..cee4108ce 100755 --- a/bin/ebuild-helpers/fowners +++ b/bin/ebuild-helpers/fowners @@ -4,8 +4,9 @@ source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) EPREFIX= ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + EPREFIX= ED=${D} +fi # we can't prefix all arguments because # chown takes random options @@ -13,10 +14,5 @@ slash="/" chown "${@/#${slash}/${ED}${slash}}" ret=$? -if [[ ${ret} != 0 && -n ${EPREFIX} && ${EUID} != 0 ]] ; then - ewarn "fowners failure ignored in Prefix with non-privileged user" - exit 0 -fi - -[[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" +[[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit $ret diff --git a/bin/ebuild-helpers/fperms b/bin/ebuild-helpers/fperms index a2f77ea00..d854ebbdf 100755 --- a/bin/ebuild-helpers/fperms +++ b/bin/ebuild-helpers/fperms @@ -1,16 +1,17 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi # we can't prefix all arguments because # chmod takes random options slash="/" chmod "${@/#${slash}/${ED}${slash}}" ret=$? -[[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" +[[ $ret -ne 0 ]] && __helpers_die "${0##*/} failed" exit $ret diff --git a/bin/ebuild-helpers/keepdir b/bin/ebuild-helpers/keepdir new file mode 100755 index 000000000..bec2feb77 --- /dev/null +++ b/bin/ebuild-helpers/keepdir @@ -0,0 +1,20 @@ +#!/bin/bash +# Copyright 1999-2013 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh + +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi + +dodir "$@" +ret=$? + +for x in "$@"; do + >> "${ED}${x}/.keep_${CATEGORY}_${PN}-${SLOT%/*}" || \ + { echo "!!! ${0##*/}: cannot write .keep in ${ED}${x}" 1>&2; ret=1; } +done + +[[ ${ret} -ne 0 ]] && __helpers_die "${0##*/} failed" +exit ${ret} diff --git a/bin/ebuild-helpers/newbin b/bin/ebuild-helpers/newbin index bf9874472..59a0db27b 100755..120000 --- a/bin/ebuild-helpers/newbin +++ b/bin/ebuild-helpers/newbin @@ -1,22 +1 @@ -#!/bin/bash -# Copyright 1999-2011 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 - -source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh - -if [[ -z ${T} ]] || [[ -z ${2} ]] ; then - helpers_die "${0##*/}: Need two arguments, old file and new file" - exit 1 -fi - -if [ ! -e "$1" ] ; then - helpers_die "!!! ${0##*/}: $1 does not exist" - exit 1 -fi - -(($#>2)) && \ - eqawarn "QA Notice: ${0##*/} called with more than 2 arguments: ${@:3}" - -rm -rf "${T}/${2}" && \ -cp -f "${1}" "${T}/${2}" && \ -exec dobin "${T}/${2}" +newins
\ No newline at end of file diff --git a/bin/ebuild-helpers/newconfd b/bin/ebuild-helpers/newconfd index fa3710d8f..59a0db27b 100755..120000 --- a/bin/ebuild-helpers/newconfd +++ b/bin/ebuild-helpers/newconfd @@ -1,22 +1 @@ -#!/bin/bash -# Copyright 1999-2011 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 - -source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh - -if [[ -z ${T} ]] || [[ -z ${2} ]] ; then - helpers_die "${0##*/}: Need two arguments, old file and new file" - exit 1 -fi - -if [ ! -e "$1" ] ; then - helpers_die "!!! ${0##*/}: $1 does not exist" - exit 1 -fi - -(($#>2)) && \ - eqawarn "QA Notice: ${0##*/} called with more than 2 arguments: ${@:3}" - -rm -rf "${T}/${2}" && \ -cp -f "${1}" "${T}/${2}" && \ -exec doconfd "${T}/${2}" +newins
\ No newline at end of file diff --git a/bin/ebuild-helpers/newdoc b/bin/ebuild-helpers/newdoc index df6fb1d58..59a0db27b 100755..120000 --- a/bin/ebuild-helpers/newdoc +++ b/bin/ebuild-helpers/newdoc @@ -1,22 +1 @@ -#!/bin/bash -# Copyright 1999-2011 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 - -source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh - -if [[ -z ${T} ]] || [[ -z ${2} ]] ; then - helpers_die "${0##*/}: Need two arguments, old file and new file" - exit 1 -fi - -if [ ! -e "$1" ] ; then - helpers_die "!!! ${0##*/}: $1 does not exist" - exit 1 -fi - -(($#>2)) && \ - eqawarn "QA Notice: ${0##*/} called with more than 2 arguments: ${@:3}" - -rm -rf "${T}/${2}" && \ -cp -f "${1}" "${T}/${2}" && \ -exec dodoc "${T}/${2}" +newins
\ No newline at end of file diff --git a/bin/ebuild-helpers/newenvd b/bin/ebuild-helpers/newenvd index c54af0520..59a0db27b 100755..120000 --- a/bin/ebuild-helpers/newenvd +++ b/bin/ebuild-helpers/newenvd @@ -1,22 +1 @@ -#!/bin/bash -# Copyright 1999-2011 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 - -source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh - -if [[ -z ${T} ]] || [[ -z ${2} ]] ; then - helpers_die "${0##*/}: Need two arguments, old file and new file" - exit 1 -fi - -if [ ! -e "$1" ] ; then - helpers_die "!!! ${0##*/}: $1 does not exist" - exit 1 -fi - -(($#>2)) && \ - eqawarn "QA Notice: ${0##*/} called with more than 2 arguments: ${@:3}" - -rm -rf "${T}/${2}" && \ -cp -f "${1}" "${T}/${2}" && \ -exec doenvd "${T}/${2}" +newins
\ No newline at end of file diff --git a/bin/ebuild-helpers/newexe b/bin/ebuild-helpers/newexe index 9bcf64b31..59a0db27b 100755..120000 --- a/bin/ebuild-helpers/newexe +++ b/bin/ebuild-helpers/newexe @@ -1,22 +1 @@ -#!/bin/bash -# Copyright 1999-2011 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 - -source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh - -if [[ -z ${T} ]] || [[ -z ${2} ]] ; then - helpers_die "${0##*/}: Need two arguments, old file and new file" - exit 1 -fi - -if [ ! -e "$1" ] ; then - helpers_die "!!! ${0##*/}: $1 does not exist" - exit 1 -fi - -(($#>2)) && \ - eqawarn "QA Notice: ${0##*/} called with more than 2 arguments: ${@:3}" - -rm -rf "${T}/${2}" && \ -cp -f "${1}" "${T}/${2}" && \ -exec doexe "${T}/${2}" +newins
\ No newline at end of file diff --git a/bin/ebuild-helpers/newheader b/bin/ebuild-helpers/newheader new file mode 120000 index 000000000..59a0db27b --- /dev/null +++ b/bin/ebuild-helpers/newheader @@ -0,0 +1 @@ +newins
\ No newline at end of file diff --git a/bin/ebuild-helpers/newinitd b/bin/ebuild-helpers/newinitd index 03bbe68a7..59a0db27b 100755..120000 --- a/bin/ebuild-helpers/newinitd +++ b/bin/ebuild-helpers/newinitd @@ -1,22 +1 @@ -#!/bin/bash -# Copyright 1999-2011 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 - -source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh - -if [[ -z ${T} ]] || [[ -z ${2} ]] ; then - helpers_die "${0##*/}: Need two arguments, old file and new file" - exit 1 -fi - -if [ ! -e "$1" ] ; then - helpers_die "!!! ${0##*/}: $1 does not exist" - exit 1 -fi - -(($#>2)) && \ - eqawarn "QA Notice: ${0##*/} called with more than 2 arguments: ${@:3}" - -rm -rf "${T}/${2}" && \ -cp -f "${1}" "${T}/${2}" && \ -exec doinitd "${T}/${2}" +newins
\ No newline at end of file diff --git a/bin/ebuild-helpers/newins b/bin/ebuild-helpers/newins index adf2d809e..03359851a 100755 --- a/bin/ebuild-helpers/newins +++ b/bin/ebuild-helpers/newins @@ -1,38 +1,57 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh +helper=${0##*/} + if [[ -z ${T} ]] || [[ -z ${2} ]] ; then - helpers_die "${0##*/}: Need two arguments, old file and new file" + __helpers_die "${helper}: Need two arguments, old file and new file" exit 1 fi -if [ ! -e "$1" ] ; then - helpers_die "!!! ${0##*/}: $1 does not exist" - exit 1 +(($#>2)) && \ + eqawarn "QA Notice: ${helper} called with more than 2 arguments: ${@:3}" + +stdin= +if ___eapi_newins_supports_reading_from_standard_input && [[ $1 == "-" ]]; then + stdin=yes fi -(($#>2)) && \ - eqawarn "QA Notice: ${0##*/} called with more than 2 arguments: ${@:3}" - -rm -rf "${T}/${2}" || exit $? -case "$EAPI" in - 0|1|2|3|3_pre2) - cp "$1" "$T/$2" || exit $? - ;; - *) - cp -P "$1" "$T/$2" - ret=$? - if [[ $ret -ne 0 ]] ; then - helpers_die "${0##*/} failed" - exit $ret +TMP=$(mktemp -d "${T}/.newins_tmp_XXXXXX") +trap 'rm -rf "${TMP}"' EXIT + +if [[ ${stdin} ]] ; then + if [[ -t 0 ]] ; then + __helpers_die "!!! ${helper}: Input is from a terminal" + exit 1 + fi + cat > "${TMP}/$2" + ret=$? +else + if [[ ! -e $1 ]] ; then + __helpers_die "!!! ${helper}: $1 does not exist" + exit 1 + fi + + cp_args="-f" + if [[ ${helper} == newins ]] ; then + if ___eapi_doins_and_newins_preserve_symlinks; then + cp_args+=" -P" fi - ;; -esac -doins "${T}/${2}" + fi + + cp ${cp_args} "$1" "${TMP}/$2" + ret=$? +fi + +if [[ ${ret} -ne 0 ]] ; then + __helpers_die "${0##*/} failed" + exit ${ret} +fi + +do${helper#new} "${TMP}/$2" ret=$? -rm -rf "${T}/${2}" -[[ $ret -ne 0 ]] && helpers_die "${0##*/} failed" +[[ $ret -ne 0 ]] && __helpers_die "${helper} failed" exit $ret diff --git a/bin/ebuild-helpers/newlib.a b/bin/ebuild-helpers/newlib.a index 7ff819547..59a0db27b 100755..120000 --- a/bin/ebuild-helpers/newlib.a +++ b/bin/ebuild-helpers/newlib.a @@ -1,22 +1 @@ -#!/bin/bash -# Copyright 1999-2011 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 - -source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh - -if [[ -z ${T} ]] || [[ -z ${2} ]] ; then - helpers_die "${0##*/}: Need two arguments, old file and new file" - exit 1 -fi - -if [ ! -e "$1" ] ; then - helpers_die "!!! ${0##*/}: $1 does not exist" - exit 1 -fi - -(($#>2)) && \ - eqawarn "QA Notice: ${0##*/} called with more than 2 arguments: ${@:3}" - -rm -rf "${T}/${2}" && \ -cp -f "${1}" "${T}/${2}" && \ -exec dolib.a "${T}/${2}" +newins
\ No newline at end of file diff --git a/bin/ebuild-helpers/newlib.so b/bin/ebuild-helpers/newlib.so index fd4c097d7..59a0db27b 100755..120000 --- a/bin/ebuild-helpers/newlib.so +++ b/bin/ebuild-helpers/newlib.so @@ -1,22 +1 @@ -#!/bin/bash -# Copyright 1999-2011 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 - -source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh - -if [[ -z ${T} ]] || [[ -z ${2} ]] ; then - helpers_die "${0##*/}: Need two arguments, old file and new file" - exit 1 -fi - -if [ ! -e "$1" ] ; then - helpers_die "!!! ${0##*/}: $1 does not exist" - exit 1 -fi - -(($#>2)) && \ - eqawarn "QA Notice: ${0##*/} called with more than 2 arguments: ${@:3}" - -rm -rf "${T}/${2}" && \ -cp -f "${1}" "${T}/${2}" && \ -exec dolib.so "${T}/${2}" +newins
\ No newline at end of file diff --git a/bin/ebuild-helpers/newman b/bin/ebuild-helpers/newman index 889e0f985..59a0db27b 100755..120000 --- a/bin/ebuild-helpers/newman +++ b/bin/ebuild-helpers/newman @@ -1,22 +1 @@ -#!/bin/bash -# Copyright 1999-2011 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 - -source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh - -if [[ -z ${T} ]] || [[ -z ${2} ]] ; then - helpers_die "${0##*/}: Need two arguments, old file and new file" - exit 1 -fi - -if [ ! -e "$1" ] ; then - helpers_die "!!! ${0##*/}: $1 does not exist" - exit 1 -fi - -(($#>2)) && \ - eqawarn "QA Notice: ${0##*/} called with more than 2 arguments: ${@:3}" - -rm -rf "${T}/${2}" && \ -cp -f "${1}" "${T}/${2}" && \ -exec doman "${T}/${2}" +newins
\ No newline at end of file diff --git a/bin/ebuild-helpers/newsbin b/bin/ebuild-helpers/newsbin index 9df0af275..59a0db27b 100755..120000 --- a/bin/ebuild-helpers/newsbin +++ b/bin/ebuild-helpers/newsbin @@ -1,22 +1 @@ -#!/bin/bash -# Copyright 1999-2011 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 - -source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh - -if [[ -z ${T} ]] || [[ -z ${2} ]] ; then - helpers_die "${0##*/}: Need two arguments, old file and new file" - exit 1 -fi - -if [ ! -e "$1" ] ; then - helpers_die "!!! ${0##*/}: $1 does not exist" - exit 1 -fi - -(($#>2)) && \ - eqawarn "QA Notice: ${0##*/} called with more than 2 arguments: ${@:3}" - -rm -rf "${T}/${2}" && \ -cp -f "${1}" "${T}/${2}" && \ -exec dosbin "${T}/${2}" +newins
\ No newline at end of file diff --git a/bin/ebuild-helpers/portageq b/bin/ebuild-helpers/portageq index ec30b66cb..b67b03f33 100755 --- a/bin/ebuild-helpers/portageq +++ b/bin/ebuild-helpers/portageq @@ -1,8 +1,10 @@ #!/bin/bash -# Copyright 2009-2010 Gentoo Foundation +# Copyright 2009-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 PORTAGE_BIN_PATH=${PORTAGE_BIN_PATH:-/usr/lib/portage/bin} PORTAGE_PYM_PATH=${PORTAGE_PYM_PATH:-/usr/lib/portage/pym} -PYTHONPATH=$PORTAGE_PYM_PATH${PYTHONPATH:+:}$PYTHONPATH \ +# Use safe cwd, avoiding unsafe import for bug #469338. +cd "${PORTAGE_PYM_PATH}" +PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \ exec "${PORTAGE_PYTHON:-/usr/bin/python}" "$PORTAGE_BIN_PATH/portageq" "$@" diff --git a/bin/ebuild-helpers/prepall b/bin/ebuild-helpers/prepall index 49e646cd2..fb5c2db55 100755 --- a/bin/ebuild-helpers/prepall +++ b/bin/ebuild-helpers/prepall @@ -1,11 +1,12 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi if has chflags $FEATURES ; then # Save all the file flags for restoration at the end of prepall. diff --git a/bin/ebuild-helpers/prepalldocs b/bin/ebuild-helpers/prepalldocs index 560a02bcb..3094661f5 100755 --- a/bin/ebuild-helpers/prepalldocs +++ b/bin/ebuild-helpers/prepalldocs @@ -1,15 +1,21 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh +if ___eapi_has_docompress; then + die "'${0##*/}' has been banned for EAPI '$EAPI'" + exit 1 +fi + if [[ -n $1 ]] ; then - vecho "${0##*/}: invalid usage; takes no arguments" 1>&2 + __vecho "${0##*/}: invalid usage; takes no arguments" 1>&2 fi -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi [[ -d ${ED}usr/share/doc ]] || exit 0 diff --git a/bin/ebuild-helpers/prepallinfo b/bin/ebuild-helpers/prepallinfo index db9bbfacb..1a2027580 100755 --- a/bin/ebuild-helpers/prepallinfo +++ b/bin/ebuild-helpers/prepallinfo @@ -1,11 +1,12 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi [[ -d ${ED}usr/share/info ]] || exit 0 diff --git a/bin/ebuild-helpers/prepallman b/bin/ebuild-helpers/prepallman index dee1c7236..5331eaf01 100755 --- a/bin/ebuild-helpers/prepallman +++ b/bin/ebuild-helpers/prepallman @@ -1,22 +1,22 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh # replaced by controllable compression in EAPI 4 -has "${EAPI}" 0 1 2 3 || exit 0 +___eapi_has_docompress && exit 0 -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi ret=0 -find "${ED}" -type d -name man > "${T}"/prepallman.filelist -while read -r mandir ; do +while IFS= read -r -d '' mandir ; do mandir=${mandir#${ED}} prepman "${mandir%/man}" ((ret|=$?)) -done < "${T}"/prepallman.filelist +done < <(find "${ED}" -type d -name man -print0) exit ${ret} diff --git a/bin/ebuild-helpers/prepallstrip b/bin/ebuild-helpers/prepallstrip index 28320d975..1aa6686cd 100755 --- a/bin/ebuild-helpers/prepallstrip +++ b/bin/ebuild-helpers/prepallstrip @@ -1,8 +1,11 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh + +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi exec prepstrip "${ED}" diff --git a/bin/ebuild-helpers/prepinfo b/bin/ebuild-helpers/prepinfo index ffe2ecec3..5afc18a71 100755 --- a/bin/ebuild-helpers/prepinfo +++ b/bin/ebuild-helpers/prepinfo @@ -1,11 +1,12 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi if [[ -z $1 ]] ; then infodir="/usr/share/info" @@ -19,7 +20,7 @@ fi if [[ ! -d ${ED}${infodir} ]] ; then if [[ -n $1 ]] ; then - vecho "${0##*/}: '${infodir}' does not exist!" + __vecho "${0##*/}: '${infodir}' does not exist!" exit 1 else exit 0 @@ -33,5 +34,5 @@ find "${ED}${infodir}" -type d -print0 | while read -r -d $'\0' x ; do rm -f "${x}"/dir{,.info}{,.gz,.bz2} done -has "${EAPI}" 0 1 2 3 || exit 0 +___eapi_has_docompress && exit 0 exec ecompressdir --queue "${infodir}" diff --git a/bin/ebuild-helpers/preplib b/bin/ebuild-helpers/preplib deleted file mode 100755 index 6e91cf33d..000000000 --- a/bin/ebuild-helpers/preplib +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -# Copyright 1999-2011 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 - -source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh - -eqawarn "QA Notice: Deprecated call to 'preplib'" - -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac - -LIBDIR_VAR="LIBDIR_${ABI}" -if [ -n "${ABI}" -a -n "${!LIBDIR_VAR}" ]; then - CONF_LIBDIR="${!LIBDIR_VAR}" -fi -unset LIBDIR_VAR - -if [ -z "${CONF_LIBDIR}" ]; then - # we need this to default to lib so that things dont break - CONF_LIBDIR="lib" -fi - -if [ -z "$1" ] ; then - z="${ED}usr/${CONF_LIBDIR}" -else - z="${ED}$1/${CONF_LIBDIR}" -fi - -if [ -d "${z}" ] ; then - ldconfig -n -N "${z}" -fi diff --git a/bin/ebuild-helpers/prepman b/bin/ebuild-helpers/prepman index f96b64147..fb5dcb4a5 100755 --- a/bin/ebuild-helpers/prepman +++ b/bin/ebuild-helpers/prepman @@ -1,13 +1,17 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 +# Do not compress man pages which are smaller than this (in bytes). #169260 +SIZE_LIMIT='128' + source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + ED=${D} +fi -if [[ -z $1 ]] ; then +if [[ -z $1 ]] ; then mandir="${ED}usr/share/man" else mandir="${ED}$1/man" @@ -19,7 +23,7 @@ if [[ ! -d ${mandir} ]] ; then fi # replaced by controllable compression in EAPI 4 -has "${EAPI}" 0 1 2 3 || exit 0 +___eapi_has_docompress && exit 0 shopt -s nullglob @@ -30,6 +34,6 @@ for subdir in "${mandir}"/man* "${mandir}"/*/man* ; do [[ -d ${subdir} ]] && really_is_mandir=1 && break done -[[ ${really_is_mandir} == 1 ]] && exec ecompressdir --queue "${mandir#${ED}}" +[[ ${really_is_mandir} == 1 ]] && exec ecompressdir --limit ${SIZE_LIMIT} --queue "${mandir#${ED}}" exit 0 diff --git a/bin/ebuild-helpers/prepstrip b/bin/ebuild-helpers/prepstrip index fe5c1bc32..2ef8a1ace 100755 --- a/bin/ebuild-helpers/prepstrip +++ b/bin/ebuild-helpers/prepstrip @@ -1,7 +1,8 @@ #!/bin/bash -# Copyright 1999-2012 Gentoo Foundation +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 +PORTAGE_PYM_PATH=${PORTAGE_PYM_PATH:-/usr/lib/portage/pym} source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/helper-functions.sh # avoid multiple calls to `has`. this creates things like: @@ -15,11 +16,12 @@ exp_tf() { eval ${var}_${flag}=$(tf has ${flag} ${!var}) done } -exp_tf FEATURES compressdebug installsources nostrip splitdebug -exp_tf RESTRICT binchecks installsources strip +exp_tf FEATURES compressdebug installsources nostrip splitdebug xattr +exp_tf RESTRICT binchecks installsources splitdebug strip -[[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "${EAPI}" in 0|1|2) EPREFIX= ED=${D} ;; esac +if ! ___eapi_has_prefix_variables; then + EPREFIX= ED=${D} +fi banner=false SKIP_STRIP=false @@ -29,6 +31,30 @@ if ${RESTRICT_strip} || ${FEATURES_nostrip} ; then ${FEATURES_installsources} || exit 0 fi +PRESERVE_XATTR=false +if [[ ${KERNEL} == linux ]] && ${FEATURES_xattr} ; then + PRESERVE_XATTR=true + if type -P getfattr >/dev/null && type -P setfattr >/dev/null ; then + dump_xattrs() { + getfattr -d --absolute-names "$1" + } + restore_xattrs() { + setfattr --restore=- + } + else + dump_xattrs() { + PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \ + "${PORTAGE_PYTHON:-/usr/bin/python}" \ + "${PORTAGE_BIN_PATH}/xattr-helper.py" --dump < <(echo -n "$1") + } + restore_xattrs() { + PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \ + "${PORTAGE_PYTHON:-/usr/bin/python}" \ + "${PORTAGE_BIN_PATH}/xattr-helper.py" --restore + } + fi +fi + # look up the tools we might be using for t in STRIP:strip OBJCOPY:objcopy READELF:readelf ; do v=${t%:*} # STRIP @@ -51,7 +77,7 @@ case $(${STRIP} --version 2>/dev/null) in # We'll leave out -R .note for now until we can check out the relevance # of the section when it has the ALLOC flag set on it ... SAFE_STRIP_FLAGS="--strip-unneeded" - DEF_STRIP_FLAGS="-R .comment -R .GCC.command.line" + DEF_STRIP_FLAGS="-R .comment -R .GCC.command.line -R .note.gnu.gold-version" SPLIT_STRIP_FLAGS= ;; esac @@ -62,23 +88,13 @@ prepstrip_sources_dir=${EPREFIX}/usr/src/debug/${CATEGORY}/${PF} type -P debugedit >/dev/null && debugedit_found=true || debugedit_found=false debugedit_warned=false -multijob_init +__multijob_init # Setup $T filesystem layout that we care about. tmpdir="${T}/prepstrip" rm -rf "${tmpdir}" mkdir -p "${tmpdir}"/{inodes,splitdebug,sources} -# Usage: inode_var_name: <file> -inode_file_link() { - echo -n "${tmpdir}/inodes/" - if [[ ${USERLAND} == "BSD" ]] ; then - stat -f '%i' "$1" - else - stat -c '%i' "$1" - fi -} - # Usage: save_elf_sources <elf> save_elf_sources() { ${FEATURES_installsources} || return 0 @@ -93,7 +109,6 @@ save_elf_sources() { fi local x=$1 - [[ -f $(inode_file_link "${x}") ]] && return 0 # since we're editing the ELF here, we should recompute the build-id # (the -i flag below). save that output so we don't need to recompute @@ -101,20 +116,22 @@ save_elf_sources() { buildid=$(debugedit -i \ -b "${WORKDIR}" \ -d "${prepstrip_sources_dir}" \ - -l "${tmpdir}/sources/${x##*/}.${BASHPID}" \ + -l "${tmpdir}/sources/${x##*/}.${BASHPID:-$(__bashpid)}" \ "${x}") } # Usage: save_elf_debug <elf> [splitdebug file] save_elf_debug() { ${FEATURES_splitdebug} || return 0 + ${RESTRICT_splitdebug} && return 0 # NOTE: Debug files must be installed in # ${EPREFIX}/usr/lib/debug/${EPREFIX} (note that ${EPREFIX} occurs # twice in this path) in order for gdb's debug-file-directory # lookup to work correctly. local x=$1 - local splitdebug=$2 + local inode_debug=$2 + local splitdebug=$3 local y=${ED}usr/lib/debug/${x:${#D}}.debug # dont save debug info twice @@ -122,9 +139,8 @@ save_elf_debug() { mkdir -p "${y%/*}" - local inode=$(inode_file_link "${x}") - if [[ -f ${inode} ]] ; then - ln "${inode}" "${y}" + if [ -f "${inode_debug}" ] ; then + ln "${inode_debug}" "${y}" || die "ln failed unexpectedly" else if [[ -n ${splitdebug} ]] ; then mv "${splitdebug}" "${y}" @@ -134,64 +150,89 @@ save_elf_debug() { ${OBJCOPY} ${objcopy_flags} "${x}" "${y}" ${OBJCOPY} --add-gnu-debuglink="${y}" "${x}" fi - local args="a-x,o-w" - [[ -g ${x} || -u ${x} ]] && args+=",go-r" - chmod ${args} "${y}" - ln "${y}" "${inode}" + # Only do the following if the debug file was + # successfully created (see bug #446774). + if [ $? -eq 0 ] ; then + local args="a-x,o-w" + [[ -g ${x} || -u ${x} ]] && args+=",go-r" + chmod ${args} "${y}" + ln "${y}" "${inode_debug}" || die "ln failed unexpectedly" + fi fi # if we don't already have build-id from debugedit, look it up if [[ -z ${buildid} ]] ; then # convert the readelf output to something useful - buildid=$(${READELF} -x .note.gnu.build-id "${x}" 2>/dev/null \ - | awk '$NF ~ /GNU/ { getline; printf $2$3$4$5; getline; print $2 }') + buildid=$(${READELF} -n "${x}" 2>/dev/null | awk '/Build ID:/{ print $NF; exit }') fi if [[ -n ${buildid} ]] ; then local buildid_dir="${ED}usr/lib/debug/.build-id/${buildid:0:2}" local buildid_file="${buildid_dir}/${buildid:2}" mkdir -p "${buildid_dir}" - ln -s "../../${x:${#D}}.debug" "${buildid_file}.debug" - ln -s "/${x:${#D}}" "${buildid_file}" + [ -L "${buildid_file}".debug ] || ln -s "../../${x:${#D}}.debug" "${buildid_file}.debug" + [ -L "${buildid_file}" ] || ln -s "/${x:${#D}}" "${buildid_file}" fi } # Usage: process_elf <elf> process_elf() { - local x=$1 strip_flags=${*:2} - - vecho " ${x:${#ED}}" - save_elf_sources "${x}" + local x=$1 inode_link=$2 strip_flags=${*:3} + local already_stripped lockfile xt_data + + __vecho " ${x:${#ED}}" + + # If two processes try to debugedit or strip the same hardlink at the + # same time, it may corrupt files or cause loss of splitdebug info. + # So, use a lockfile to prevent interference (easily observed with + # dev-vcs/git which creates ~111 hardlinks to one file in + # /usr/libexec/git-core). + lockfile=${inode_link}_lockfile + if ! ln "${inode_link}" "${lockfile}" 2>/dev/null ; then + while [[ -f ${lockfile} ]] ; do + sleep 1 + done + unset lockfile + fi - if ${strip_this} ; then + [ -f "${inode_link}_stripped" ] && already_stripped=true || already_stripped=false - # If two processes try to strip the same hardlink at the same - # time, it will cause one of them to lose the splitdebug info. - # So, use a lockfile to prevent interference (easily observed - # with dev-vcs/git which creates ~109 hardlinks to one file in - # /usr/libexec/git-core). - local lockfile=$(inode_file_link "${x}")_lockfile - if ! ln "${x}" "${lockfile}" ; then - while [[ -f ${lockfile} ]] ; do - sleep 1 - done - unset lockfile + if ! ${already_stripped} ; then + if ${PRESERVE_XATTR} ; then + xt_data=$(dump_xattrs "${x}") fi + save_elf_sources "${x}" + fi + + if ${strip_this} ; then # see if we can split & strip at the same time if [[ -n ${SPLIT_STRIP_FLAGS} ]] ; then local shortname="${x##*/}.debug" - local splitdebug="${tmpdir}/splitdebug/${shortname}.${BASHPID}" + local splitdebug="${tmpdir}/splitdebug/${shortname}.${BASHPID:-$(__bashpid)}" + ${already_stripped} || \ ${STRIP} ${strip_flags} \ -f "${splitdebug}" \ -F "${shortname}" \ "${x}" - save_elf_debug "${x}" "${splitdebug}" + save_elf_debug "${x}" "${inode_link}_debug" "${splitdebug}" else - save_elf_debug "${x}" + save_elf_debug "${x}" "${inode_link}_debug" + ${already_stripped} || \ ${STRIP} ${strip_flags} "${x}" fi - [[ -n ${lockfile} ]] && rm -f "${lockfile}" fi + + if ${already_stripped} ; then + rm -f "${x}" || die "rm failed unexpectedly" + ln "${inode_link}_stripped" "${x}" || die "ln failed unexpectedly" + else + ln "${x}" "${inode_link}_stripped" || die "ln failed unexpectedly" + if [[ ${xt_data} ]] ; then + restore_xattrs <<< "${xt_data}" + fi + fi + + [[ -n ${lockfile} ]] && rm -f "${lockfile}" } # The existance of the section .symtab tells us that a binary is stripped. @@ -204,7 +245,7 @@ if ! ${RESTRICT_binchecks} && ! ${RESTRICT_strip} ; then log=${tmpdir}/scanelf-already-stripped.log scanelf -yqRBF '#k%F' -k '!.symtab' "$@" | sed -e "s#^${ED}##" > "${log}" ( - multijob_child_init + __multijob_child_init qa_var="QA_PRESTRIPPED_${ARCH/-/_}" [[ -n ${!qa_var} ]] && QA_PRESTRIPPED="${!qa_var}" if [[ -n ${QA_PRESTRIPPED} && -s ${log} && \ @@ -219,28 +260,49 @@ if ! ${RESTRICT_binchecks} && ! ${RESTRICT_strip} ; then fi sed -e "/^\$/d" -e "s#^#/#" -i "${log}" if [[ -s ${log} ]] ; then - vecho -e "\n" + __vecho -e "\n" eqawarn "QA Notice: Pre-stripped files found:" eqawarn "$(<"${log}")" else rm -f "${log}" fi ) & - multijob_post_fork + __multijob_post_fork +fi + +# Since strip creates a new inode, we need to know the initial set of +# inodes in advance, so that we can avoid interference due to trying +# to strip the same (hardlinked) file multiple times in parallel. +# See bug #421099. +if [[ ${USERLAND} == BSD ]] ; then + get_inode_number() { stat -f '%i' "$1"; } +else + get_inode_number() { stat -c '%i' "$1"; } fi +cd "${tmpdir}/inodes" || die "cd failed unexpectedly" +while read -r x ; do + inode_link=$(get_inode_number "${x}") || die "stat failed unexpectedly" + echo "${x}" >> "${inode_link}" || die "echo failed unexpectedly" +done < <( + # Use sort -u to eliminate duplicates for bug #445336. + ( + scanelf -yqRBF '#k%F' -k '.symtab' "$@" + find "$@" -type f ! -type l -name '*.a' + ) | LC_ALL=C sort -u +) # Now we look for unstripped binaries. -for x in \ - $(scanelf -yqRBF '#k%F' -k '.symtab' "$@") \ - $(find "$@" -type f -name '*.a') +for inode_link in $(shopt -s nullglob; echo *) ; do +while read -r x do + if ! ${banner} ; then - vecho "strip: ${STRIP} ${PORTAGE_STRIP_FLAGS}" + __vecho "strip: ${STRIP} ${PORTAGE_STRIP_FLAGS}" banner=true fi ( - multijob_child_init + __multijob_child_init f=$(file "${x}") || exit 0 [[ -z ${f} ]] && exit 0 @@ -275,27 +337,34 @@ do buildid= if [[ ${f} == *"current ar archive"* ]] ; then - vecho " ${x:${#ED}}" + __vecho " ${x:${#ED}}" if ${strip_this} ; then - # hmm, can we split debug/sources for .a ? - ${STRIP} -g "${x}" + # If we have split debug enabled, then do not strip this. + # There is no concept of splitdebug for objects not yet + # linked in (only for finally linked ELFs), so we have to + # retain the debug info in the archive itself. + if ! ${FEATURES_splitdebug} || ${RESTRICT_splitdebug} ; then + ${STRIP} -g "${x}" + fi fi elif [[ ${f} == *"SB executable"* || ${f} == *"SB shared object"* ]] ; then - process_elf "${x}" ${PORTAGE_STRIP_FLAGS} + process_elf "${x}" "${inode_link}" ${PORTAGE_STRIP_FLAGS} elif [[ ${f} == *"SB relocatable"* ]] ; then - process_elf "${x}" ${SAFE_STRIP_FLAGS} + process_elf "${x}" "${inode_link}" ${SAFE_STRIP_FLAGS} fi if ${was_not_writable} ; then chmod u-w "${x}" fi ) & - multijob_post_fork + __multijob_post_fork + +done < "${inode_link}" done # With a bit more work, we could run the rsync processes below in # parallel, but not sure that'd be an overall improvement. -multijob_finish +__multijob_finish cd "${tmpdir}"/sources/ && cat * > "${tmpdir}/debug.sources" 2>/dev/null if [[ -s ${tmpdir}/debug.sources ]] && \ @@ -303,11 +372,11 @@ if [[ -s ${tmpdir}/debug.sources ]] && \ ! ${RESTRICT_installsources} && \ ${debugedit_found} then - vecho "installsources: rsyncing source files" + __vecho "installsources: rsyncing source files" [[ -d ${D}${prepstrip_sources_dir} ]] || mkdir -p "${D}${prepstrip_sources_dir}" grep -zv '/<[^/>]*>$' "${tmpdir}"/debug.sources | \ (cd "${WORKDIR}"; LANG=C sort -z -u | \ - rsync -tL0 --files-from=- "${WORKDIR}/" "${D}${prepstrip_sources_dir}/" ) + rsync -tL0 --chmod=ugo-st,a+r,go-w,Da+x,Fa-x --files-from=- "${WORKDIR}/" "${D}${prepstrip_sources_dir}/" ) # Preserve directory structure. # Needed after running save_elf_sources. diff --git a/bin/ebuild-helpers/unprivileged/chgrp b/bin/ebuild-helpers/unprivileged/chgrp new file mode 120000 index 000000000..6fb0fcd80 --- /dev/null +++ b/bin/ebuild-helpers/unprivileged/chgrp @@ -0,0 +1 @@ +chown
\ No newline at end of file diff --git a/bin/ebuild-helpers/unprivileged/chown b/bin/ebuild-helpers/unprivileged/chown new file mode 100755 index 000000000..08fa650c5 --- /dev/null +++ b/bin/ebuild-helpers/unprivileged/chown @@ -0,0 +1,41 @@ +#!/bin/bash +# Copyright 2012-2013 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +scriptpath=${BASH_SOURCE[0]} +scriptname=${scriptpath##*/} + +IFS=':' + +for path in ${PATH}; do + [[ -x ${path}/${scriptname} ]] || continue + [[ ${path}/${scriptname} -ef ${scriptpath} ]] && continue + IFS=$' \t\n' + output=$("${path}/${scriptname}" "$@" 2>&1) + if [[ $? -ne 0 ]] ; then + + # Avoid an extreme performance problem when the + # output is very long (bug #470992). + if [[ $(wc -l <<< "${output}") -gt 100 ]]; then + output=$(head -n100 <<< "${output}") + output="${output}\n ... (further messages truncated)" + fi + + source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh + + if ! ___eapi_has_prefix_variables; then + EPREFIX= + fi + msg="${scriptname} failure ignored with unprivileged user:\n ${scriptname} $*\n ${output}" + # Reverse expansion of ${D} and ${EPREFIX}, for readability. + msg=${msg//${D}/'${D}'} + if [[ -n ${EPREFIX} ]] ; then + msg=${msg//${EPREFIX}/'${EPREFIX}'} + msg=${msg//${EPREFIX#/}/'${EPREFIX}'} + fi + ewarn "${msg}" + fi + exit 0 +done + +exit 1 diff --git a/bin/ebuild-helpers/xattr/install b/bin/ebuild-helpers/xattr/install new file mode 100755 index 000000000..f51f621bc --- /dev/null +++ b/bin/ebuild-helpers/xattr/install @@ -0,0 +1,12 @@ +#!/bin/bash +# Copyright 2013 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +PORTAGE_BIN_PATH=${PORTAGE_BIN_PATH:-/usr/lib/portage/bin} +PORTAGE_PYM_PATH=${PORTAGE_PYM_PATH:-/usr/lib/portage/pym} +# Use safe cwd, avoiding unsafe import for bug #469338. +export __PORTAGE_HELPER_CWD=${PWD} +cd "${PORTAGE_PYM_PATH}" +export __PORTAGE_HELPER_PATH=${BASH_SOURCE[0]} +PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \ + exec "${PORTAGE_PYTHON:-/usr/bin/python}" "${PORTAGE_BIN_PATH}/install.py" "$@" diff --git a/bin/ebuild-ipc b/bin/ebuild-ipc index 43e4a02ae..820005fbb 100755 --- a/bin/ebuild-ipc +++ b/bin/ebuild-ipc @@ -1,8 +1,10 @@ #!/bin/bash -# Copyright 2010 Gentoo Foundation +# Copyright 2010-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 PORTAGE_BIN_PATH=${PORTAGE_BIN_PATH:-/usr/lib/portage/bin} PORTAGE_PYM_PATH=${PORTAGE_PYM_PATH:-/usr/lib/portage/pym} -PYTHONPATH=$PORTAGE_PYM_PATH${PYTHONPATH:+:}$PYTHONPATH \ +# Use safe cwd, avoiding unsafe import for bug #469338. +cd "${PORTAGE_PYM_PATH}" +PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \ exec "${PORTAGE_PYTHON:-/usr/bin/python}" "$PORTAGE_BIN_PATH/ebuild-ipc.py" "$@" diff --git a/bin/ebuild-ipc.py b/bin/ebuild-ipc.py index 3caf2d185..00337ee22 100755 --- a/bin/ebuild-ipc.py +++ b/bin/ebuild-ipc.py @@ -1,20 +1,17 @@ -#!/usr/bin/python -# Copyright 2010-2012 Gentoo Foundation +#!/usr/bin/python -b +# Copyright 2010-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # # This is a helper which ebuild processes can use # to communicate with portage's main python process. -import errno import logging import os import pickle import platform -import select import signal import sys import time -import traceback def debug_signal(signum, frame): import pdb @@ -38,14 +35,28 @@ if os.environ.get("SANDBOX_ON") == "1": ":".join(filter(None, sandbox_write)) import portage +portage._internal_caller = True portage._disable_legacy_globals() +from portage.util._async.ForkProcess import ForkProcess +from portage.util._eventloop.global_event_loop import global_event_loop +from _emerge.PipeReader import PipeReader + +class FifoWriter(ForkProcess): + + __slots__ = ('buf', 'fifo',) + + def _run(self): + # Atomically write the whole buffer into the fifo. + with open(self.fifo, 'wb', 0) as f: + f.write(self.buf) + return os.EX_OK + class EbuildIpc(object): # Timeout for each individual communication attempt (we retry # as long as the daemon process appears to be alive). - _COMMUNICATE_RETRY_TIMEOUT_SECONDS = 15 - _BUFSIZE = 4096 + _COMMUNICATE_RETRY_TIMEOUT_MS = 15000 def __init__(self): self.fifo_dir = os.environ['PORTAGE_BUILDDIR'] @@ -89,7 +100,7 @@ class EbuildIpc(object): 'ebuild-ipc: daemon process not detected\n'), level=logging.ERROR, noiselevel=-1) - def _wait(self, pid, pr, msg): + def _run_writer(self, fifo_writer, msg): """ Wait on pid and return an appropriate exit code. This may return unsuccessfully due to timeout if the daemon @@ -98,88 +109,48 @@ class EbuildIpc(object): start_time = time.time() - while True: - try: - events = select.select([pr], [], [], - self._COMMUNICATE_RETRY_TIMEOUT_SECONDS) - except select.error as e: - portage.util.writemsg_level( - "ebuild-ipc: %s: %s\n" % \ - (portage.localization._('during select'), e), - level=logging.ERROR, noiselevel=-1) - continue + fifo_writer.start() + eof = fifo_writer.poll() is not None - if events[0]: - break + while not eof: + fifo_writer._wait_loop(timeout=self._COMMUNICATE_RETRY_TIMEOUT_MS) - if self._daemon_is_alive(): + eof = fifo_writer.poll() is not None + if eof: + break + elif self._daemon_is_alive(): self._timeout_retry_msg(start_time, msg) else: + fifo_writer.cancel() self._no_daemon_msg() - try: - os.kill(pid, signal.SIGKILL) - os.waitpid(pid, 0) - except OSError as e: - portage.util.writemsg_level( - "ebuild-ipc: %s\n" % (e,), - level=logging.ERROR, noiselevel=-1) + fifo_writer.wait() return 2 - try: - wait_retval = os.waitpid(pid, 0) - except OSError as e: - portage.util.writemsg_level( - "ebuild-ipc: %s: %s\n" % (msg, e), - level=logging.ERROR, noiselevel=-1) - return 2 + return fifo_writer.wait() - if not os.WIFEXITED(wait_retval[1]): - portage.util.writemsg_level( - "ebuild-ipc: %s: %s\n" % (msg, - portage.localization._('subprocess failure: %s') % \ - wait_retval[1]), - level=logging.ERROR, noiselevel=-1) - return 2 + def _receive_reply(self, input_fd): - return os.WEXITSTATUS(wait_retval[1]) + start_time = time.time() - def _receive_reply(self, input_fd): + pipe_reader = PipeReader(input_files={"input_fd":input_fd}, + scheduler=global_event_loop()) + pipe_reader.start() - # Timeouts are handled by the parent process, so just - # block until input is available. For maximum portability, - # use a single atomic read. - buf = None - while True: - try: - events = select.select([input_fd], [], []) - except select.error as e: - portage.util.writemsg_level( - "ebuild-ipc: %s: %s\n" % \ - (portage.localization._('during select for read'), e), - level=logging.ERROR, noiselevel=-1) - continue - - if events[0]: - # For maximum portability, use os.read() here since - # array.fromfile() and file.read() are both known to - # erroneously return an empty string from this - # non-blocking fifo stream on FreeBSD (bug #337465). - try: - buf = os.read(input_fd, self._BUFSIZE) - except OSError as e: - if e.errno != errno.EAGAIN: - portage.util.writemsg_level( - "ebuild-ipc: %s: %s\n" % \ - (portage.localization._('read error'), e), - level=logging.ERROR, noiselevel=-1) - break - # Assume that another event will be generated - # if there's any relevant data. - continue - - # Only one (atomic) read should be necessary. - if buf: - break + eof = pipe_reader.poll() is not None + + while not eof: + pipe_reader._wait_loop(timeout=self._COMMUNICATE_RETRY_TIMEOUT_MS) + eof = pipe_reader.poll() is not None + if not eof: + if self._daemon_is_alive(): + self._timeout_retry_msg(start_time, + portage.localization._('during read')) + else: + pipe_reader.cancel() + self._no_daemon_msg() + return 2 + + buf = pipe_reader.getvalue() retval = 2 @@ -232,32 +203,9 @@ class EbuildIpc(object): # un-interrupted, while the parent handles all timeout # considerations. This helps to avoid possible race conditions # from interference between timeouts and blocking IO operations. - pr, pw = os.pipe() - pid = os.fork() - - if pid == 0: - retval = 2 - try: - os.close(pr) - - # File streams are in unbuffered mode since we do atomic - # read and write of whole pickles. - output_file = open(self.ipc_in_fifo, 'wb', 0) - output_file.write(pickle.dumps(args)) - output_file.close() - retval = os.EX_OK - except SystemExit: - raise - except: - traceback.print_exc() - finally: - os._exit(retval) - - os.close(pw) - msg = portage.localization._('during write') - retval = self._wait(pid, pr, msg) - os.close(pr) + retval = self._run_writer(FifoWriter(buf=pickle.dumps(args), + fifo=self.ipc_in_fifo, scheduler=global_event_loop()), msg) if retval != os.EX_OK: portage.util.writemsg_level( @@ -270,26 +218,7 @@ class EbuildIpc(object): self._no_daemon_msg() return 2 - pr, pw = os.pipe() - pid = os.fork() - - if pid == 0: - retval = 2 - try: - os.close(pr) - retval = self._receive_reply(input_fd) - except SystemExit: - raise - except: - traceback.print_exc() - finally: - os._exit(retval) - - os.close(pw) - retval = self._wait(pid, pr, portage.localization._('during read')) - os.close(pr) - os.close(input_fd) - return retval + return self._receive_reply(input_fd) def ebuild_ipc_main(args): ebuild_ipc = EbuildIpc() diff --git a/bin/ebuild.sh b/bin/ebuild.sh index 9829f68b3..be044e08d 100755 --- a/bin/ebuild.sh +++ b/bin/ebuild.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 1999-2012 Gentoo Foundation +# Copyright 1999-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 PORTAGE_BIN_PATH="${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}" @@ -21,21 +21,23 @@ else # in global scope, even though they are completely useless during # the "depend" phase. for x in diropts docompress exeopts get_KV insopts \ - keepdir KV_major KV_micro KV_minor KV_to_int \ + KV_major KV_micro KV_minor KV_to_int \ libopts register_die_hook register_success_hook \ - remove_path_entry set_unless_changed strip_duplicate_slashes \ - unset_unless_changed use_with use_enable ; do + __strip_duplicate_slashes \ + use_with use_enable ; do eval "${x}() { - if has \"\${EAPI:-0}\" 4-python; then + if ___eapi_disallows_helpers_in_global_scope; then die \"\${FUNCNAME}() calls are not allowed in global scope\" fi }" done - # These dummy functions return false in older EAPIs, in order to ensure that + # These dummy functions return false in non-strict EAPIs, in order to ensure that # `use multislot` is false for the "depend" phase. - for x in use useq usev ; do + funcs="use useq usev" + ___eapi_has_usex && funcs+=" usex" + for x in ${funcs} ; do eval "${x}() { - if has \"\${EAPI:-0}\" 4-python; then + if ___eapi_disallows_helpers_in_global_scope; then die \"\${FUNCNAME}() calls are not allowed in global scope\" else return 1 @@ -44,10 +46,16 @@ else done # These functions die because calls to them during the "depend" phase # are considered to be severe QA violations. - for x in best_version has_version portageq ; do + funcs="best_version has_version portageq" + ___eapi_has_master_repositories && funcs+=" master_repositories" + ___eapi_has_repository_path && funcs+=" repository_path" + ___eapi_has_available_eclasses && funcs+=" available_eclasses" + ___eapi_has_eclass_path && funcs+=" eclass_path" + ___eapi_has_license_path && funcs+=" license_path" + for x in ${funcs} ; do eval "${x}() { die \"\${FUNCNAME}() calls are not allowed in global scope\"; }" done - unset x + unset funcs x fi # Don't use sandbox's BASH_ENV for new shells because it does @@ -66,7 +74,7 @@ export PORTAGE_BZIP2_COMMAND=${PORTAGE_BZIP2_COMMAND:-bzip2} # with shell opts (shopts). Ebuilds/eclasses changing shopts should reset them # when they are done. -qa_source() { +__qa_source() { local shopts=$(shopt) OLDIFS="$IFS" local retval source "$@" @@ -79,7 +87,7 @@ qa_source() { return $retval } -qa_call() { +__qa_call() { local shopts=$(shopt) OLDIFS="$IFS" local retval "$@" @@ -102,20 +110,19 @@ unset GZIP BZIP BZIP2 CDPATH GREP_OPTIONS GREP_COLOR GLOBIGNORE [[ $PORTAGE_QUIET != "" ]] && export PORTAGE_QUIET # sandbox support functions; defined prior to profile.bashrc srcing, since the profile might need to add a default exception (/usr/lib64/conftest fex) -_sb_append_var() { +__sb_append_var() { local _v=$1 ; shift local var="SANDBOX_${_v}" - [[ -z $1 || -n $2 ]] && die "Usage: add$(echo ${_v} | \ - LC_ALL=C tr [:upper:] [:lower:]) <colon-delimited list of paths>" + [[ -z $1 || -n $2 ]] && die "Usage: add$(LC_ALL=C tr "[:upper:]" "[:lower:]" <<< "${_v}") <colon-delimited list of paths>" export ${var}="${!var:+${!var}:}$1" } # bash-4 version: # local var="SANDBOX_${1^^}" -# addread() { _sb_append_var ${0#add} "$@" ; } -addread() { _sb_append_var READ "$@" ; } -addwrite() { _sb_append_var WRITE "$@" ; } -adddeny() { _sb_append_var DENY "$@" ; } -addpredict() { _sb_append_var PREDICT "$@" ; } +# addread() { __sb_append_var ${0#add} "$@" ; } +addread() { __sb_append_var READ "$@" ; } +addwrite() { __sb_append_var WRITE "$@" ; } +adddeny() { __sb_append_var DENY "$@" ; } +addpredict() { __sb_append_var PREDICT "$@" ; } addwrite "${PORTAGE_TMPDIR}" addread "/:${PORTAGE_TMPDIR}" @@ -136,19 +143,11 @@ fi # the sandbox is disabled by default except when overridden in the relevant stages export SANDBOX_ON=0 -esyslog() { - # Custom version of esyslog() to take care of the "Red Star" bug. - # MUST follow functions.sh to override the "" parameter problem. - return 0 -} - # Ensure that $PWD is sane whenever possible, to protect against # exploitation of insecure search path for python -c in ebuilds. -# See bug #239560. -if ! has "$EBUILD_PHASE" clean cleanrm depend help ; then - cd "$PORTAGE_BUILDDIR" || \ - die "PORTAGE_BUILDDIR does not exist: '$PORTAGE_BUILDDIR'" -fi +# See bug #239560 and bug #469338. +cd "${PORTAGE_PYM_PATH}" || \ + die "PORTAGE_PYM_PATH does not exist: '${PORTAGE_PYM_PATH}'" #if no perms are specified, dirs/files will have decent defaults #(not secretive, but not stupid) @@ -178,8 +177,8 @@ debug-print() { # default target printf '%s\n' "${@}" >> "${T}/eclass-debug.log" # let the portage user own/write to this file - chgrp portage "${T}/eclass-debug.log" &>/dev/null - chmod g+w "${T}/eclass-debug.log" &>/dev/null + chgrp "${PORTAGE_GRPNAME:-portage}" "${T}/eclass-debug.log" + chmod g+w "${T}/eclass-debug.log" fi } @@ -208,8 +207,9 @@ inherit() { | fmt -w 75 | while read -r ; do eqawarn "$REPLY" ; done fi + local repo_location local location - local olocation + local potential_location local x # These variables must be restored before returning. @@ -221,9 +221,10 @@ inherit() { local B_DEPEND local B_RDEPEND local B_PDEPEND + local B_HDEPEND while [ "$1" ]; do - location="${ECLASSDIR}/${1}.eclass" - olocation="" + location="" + potential_location="" export ECLASS="$1" __export_funcs_var=__export_functions_$ECLASS_DEPTH @@ -244,43 +245,36 @@ inherit() { fi fi - # any future resolution code goes here - if [ -n "$PORTDIR_OVERLAY" ]; then - local overlay - for overlay in ${PORTDIR_OVERLAY}; do - olocation="${overlay}/eclass/${1}.eclass" - if [ -e "$olocation" ]; then - location="${olocation}" - debug-print " eclass exists: ${location}" - fi - done - fi + for repo_location in "${PORTAGE_ECLASS_LOCATIONS[@]}"; do + potential_location="${repo_location}/eclass/${1}.eclass" + if [[ -f ${potential_location} ]]; then + location="${potential_location}" + debug-print " eclass exists: ${location}" + break + fi + done debug-print "inherit: $1 -> $location" - [ ! -e "$location" ] && die "${1}.eclass could not be found by inherit()" - - if [ "${location}" == "${olocation}" ] && \ - ! has "${location}" ${EBUILD_OVERLAY_ECLASSES} ; then - EBUILD_OVERLAY_ECLASSES="${EBUILD_OVERLAY_ECLASSES} ${location}" - fi + [[ -z ${location} ]] && die "${1}.eclass could not be found by inherit()" - #We need to back up the value of DEPEND and RDEPEND to B_DEPEND and B_RDEPEND + #We need to back up the values of *DEPEND to B_*DEPEND #(if set).. and then restore them after the inherit call. #turn off glob expansion set -f # Retain the old data and restore it later. - unset B_IUSE B_REQUIRED_USE B_DEPEND B_RDEPEND B_PDEPEND + unset B_IUSE B_REQUIRED_USE B_DEPEND B_RDEPEND B_PDEPEND B_HDEPEND [ "${IUSE+set}" = set ] && B_IUSE="${IUSE}" [ "${REQUIRED_USE+set}" = set ] && B_REQUIRED_USE="${REQUIRED_USE}" [ "${DEPEND+set}" = set ] && B_DEPEND="${DEPEND}" [ "${RDEPEND+set}" = set ] && B_RDEPEND="${RDEPEND}" [ "${PDEPEND+set}" = set ] && B_PDEPEND="${PDEPEND}" - unset IUSE REQUIRED_USE DEPEND RDEPEND PDEPEND + [ "${HDEPEND+set}" = set ] && B_HDEPEND="${HDEPEND}" + unset IUSE REQUIRED_USE DEPEND RDEPEND PDEPEND HDEPEND #turn on glob expansion set +f - qa_source "$location" || die "died sourcing $location in inherit()" + __qa_source "$location" || die "died sourcing $location in inherit()" #turn off glob expansion set -f @@ -292,6 +286,7 @@ inherit() { [ "${DEPEND+set}" = set ] && E_DEPEND+="${E_DEPEND:+ }${DEPEND}" [ "${RDEPEND+set}" = set ] && E_RDEPEND+="${E_RDEPEND:+ }${RDEPEND}" [ "${PDEPEND+set}" = set ] && E_PDEPEND+="${E_PDEPEND:+ }${PDEPEND}" + [ "${HDEPEND+set}" = set ] && E_HDEPEND+="${E_HDEPEND:+ }${HDEPEND}" [ "${B_IUSE+set}" = set ] && IUSE="${B_IUSE}" [ "${B_IUSE+set}" = set ] || unset IUSE @@ -308,6 +303,9 @@ inherit() { [ "${B_PDEPEND+set}" = set ] && PDEPEND="${B_PDEPEND}" [ "${B_PDEPEND+set}" = set ] || unset PDEPEND + [ "${B_HDEPEND+set}" = set ] && HDEPEND="${B_HDEPEND}" + [ "${B_HDEPEND+set}" = set ] || unset HDEPEND + #turn on glob expansion set +f @@ -348,7 +346,7 @@ EXPORT_FUNCTIONS() { PORTAGE_BASHRCS_SOURCED=0 -# @FUNCTION: source_all_bashrcs +# @FUNCTION: __source_all_bashrcs # @DESCRIPTION: # Source a relevant bashrc files and perform other miscellaneous # environment initialization when appropriate. @@ -359,7 +357,7 @@ PORTAGE_BASHRCS_SOURCED=0 # * A "default" function which is an alias for the default phase # function for the current phase. # -source_all_bashrcs() { +__source_all_bashrcs() { [[ $PORTAGE_BASHRCS_SOURCED = 1 ]] && return 0 PORTAGE_BASHRCS_SOURCED=1 local x @@ -373,7 +371,7 @@ source_all_bashrcs() { local path_array=($PROFILE_PATHS) restore_IFS for x in "${path_array[@]}" ; do - [ -f "$x/profile.bashrc" ] && qa_source "$x/profile.bashrc" + [ -f "$x/profile.bashrc" ] && __qa_source "$x/profile.bashrc" done fi @@ -390,7 +388,7 @@ source_all_bashrcs() { if [[ $EBUILD_PHASE != depend ]] ; then # The user's bashrc is the ONLY non-portage bit of code that can # change shopts without a QA violation. - for x in "${PM_EBUILD_HOOK_DIR}"/${CATEGORY}/{${PN},${PN}:${SLOT},${P},${PF}}; do + for x in "${PM_EBUILD_HOOK_DIR}"/${CATEGORY}/{${PN},${PN}:${SLOT%/*},${P},${PF}}; do if [ -r "${x}" ]; then # If $- contains x, then tracing has already been enabled # elsewhere for some reason. We preserve it's state so as @@ -470,7 +468,7 @@ if [[ -n ${QA_INTERCEPTORS} ]] ; then fi # Subshell/helper die support (must export for the die helper). -export EBUILD_MASTER_PID=$BASHPID +export EBUILD_MASTER_PID=${BASHPID:-$(__bashpid)} trap 'exit 1' SIGTERM if ! has "$EBUILD_PHASE" clean cleanrm depend && \ @@ -479,7 +477,7 @@ if ! has "$EBUILD_PHASE" clean cleanrm depend && \ # may have come from another version of ebuild.sh or something. # In any case, preprocess it to prevent any potential interference. # NOTE: export ${FOO}=... requires quoting, unlike normal exports - preprocess_ebuild_env || \ + __preprocess_ebuild_env || \ die "error processing environment" # Colon separated SANDBOX_* variables need to be cumulative. for x in SANDBOX_DENY SANDBOX_READ SANDBOX_PREDICT SANDBOX_WRITE ; do @@ -512,17 +510,22 @@ if ! has "$EBUILD_PHASE" clean cleanrm depend && \ [[ -n $EAPI ]] || EAPI=0 fi -if has "${EAPI:-0}" 4-python; then +if ___eapi_enables_globstar; then shopt -s globstar fi +# Convert quoted paths to array. +eval "PORTAGE_ECLASS_LOCATIONS=(${PORTAGE_ECLASS_LOCATIONS})" + +# Source the ebuild every time for FEATURES=noauto, so that ebuild +# modifications take effect immediately. if ! has "$EBUILD_PHASE" clean cleanrm ; then if [[ $EBUILD_PHASE = depend || ! -f $T/environment || \ - -f $PORTAGE_BUILDDIR/.ebuild_changed ]] || \ - has noauto $FEATURES ; then + -f $PORTAGE_BUILDDIR/.ebuild_changed || \ + " ${FEATURES} " == *" noauto "* ]] ; then # The bashrcs get an opportunity here to set aliases that will be expanded # during sourcing of ebuilds and eclasses. - source_all_bashrcs + __source_all_bashrcs # When EBUILD_PHASE != depend, INHERITED comes pre-initialized # from cache. In order to make INHERITED content independent of @@ -534,8 +537,9 @@ if ! has "$EBUILD_PHASE" clean cleanrm ; then # In order to ensure correct interaction between ebuilds and # eclasses, they need to be unset before this process of # interaction begins. - unset EAPI DEPEND RDEPEND PDEPEND INHERITED IUSE REQUIRED_USE \ - ECLASS E_IUSE E_REQUIRED_USE E_DEPEND E_RDEPEND E_PDEPEND + unset EAPI DEPEND RDEPEND PDEPEND HDEPEND INHERITED IUSE REQUIRED_USE \ + ECLASS E_IUSE E_REQUIRED_USE E_DEPEND E_RDEPEND E_PDEPEND \ + E_HDEPEND if [[ $PORTAGE_DEBUG != 1 || ${-/x/} != $- ]] ; then source "$EBUILD" || die "error sourcing ebuild" @@ -556,7 +560,7 @@ if ! has "$EBUILD_PHASE" clean cleanrm ; then # export EAPI for helpers (especially since we unset it above) export EAPI - if has "$EAPI" 0 1 2 3 3_pre2 ; then + if ___eapi_has_RDEPEND_DEPEND_fallback; then export RDEPEND=${RDEPEND-${DEPEND}} debug-print "RDEPEND: not set... Setting to: ${DEPEND}" fi @@ -566,19 +570,20 @@ if ! has "$EBUILD_PHASE" clean cleanrm ; then DEPEND+="${DEPEND:+ }${E_DEPEND}" RDEPEND+="${RDEPEND:+ }${E_RDEPEND}" PDEPEND+="${PDEPEND:+ }${E_PDEPEND}" + HDEPEND+="${HDEPEND:+ }${E_HDEPEND}" REQUIRED_USE+="${REQUIRED_USE:+ }${E_REQUIRED_USE}" - unset ECLASS E_IUSE E_REQUIRED_USE E_DEPEND E_RDEPEND E_PDEPEND \ + unset ECLASS E_IUSE E_REQUIRED_USE E_DEPEND E_RDEPEND E_PDEPEND E_HDEPEND \ __INHERITED_QA_CACHE # alphabetically ordered by $EBUILD_PHASE value - case "$EAPI" in + case ${EAPI} in 0|1) _valid_phases="src_compile pkg_config pkg_info src_install pkg_nofetch pkg_postinst pkg_postrm pkg_preinst pkg_prerm pkg_setup src_test src_unpack" ;; - 2|3|3_pre2) + 2|3) _valid_phases="src_compile pkg_config src_configure pkg_info src_install pkg_nofetch pkg_postinst pkg_postrm pkg_preinst src_prepare pkg_prerm pkg_setup src_test src_unpack" @@ -670,9 +675,13 @@ if [[ $EBUILD_PHASE = depend ]] ; then auxdbkeys="DEPEND RDEPEND SLOT SRC_URI RESTRICT HOMEPAGE LICENSE DESCRIPTION KEYWORDS INHERITED IUSE REQUIRED_USE PDEPEND PROVIDE EAPI - PROPERTIES DEFINED_PHASES UNUSED_05 UNUSED_04 + PROPERTIES DEFINED_PHASES HDEPEND UNUSED_04 UNUSED_03 UNUSED_02 UNUSED_01" + if ! ___eapi_has_HDEPEND; then + unset HDEPEND + fi + # The extra $(echo) commands remove newlines. if [ -n "${dbkey}" ] ; then > "${dbkey}" @@ -681,31 +690,28 @@ if [[ $EBUILD_PHASE = depend ]] ; then done else for f in ${auxdbkeys} ; do - echo $(echo ${!f}) 1>&9 || exit $? + eval "echo \$(echo \${!f}) 1>&${PORTAGE_PIPE_FD}" || exit $? done - exec 9>&- + eval "exec ${PORTAGE_PIPE_FD}>&-" fi set +f else - # Note: readonly variables interfere with preprocess_ebuild_env(), so + # Note: readonly variables interfere with __preprocess_ebuild_env(), so # declare them only after it has already run. declare -r $PORTAGE_READONLY_METADATA $PORTAGE_READONLY_VARS - case "$EAPI" in - 0|1|2) - [[ " ${FEATURES} " == *" force-prefix "* ]] && \ - declare -r ED EPREFIX EROOT - ;; - *) - declare -r ED EPREFIX EROOT - ;; - esac + if ___eapi_has_prefix_variables; then + declare -r ED EPREFIX EROOT + fi if [[ -n $EBUILD_SH_ARGS ]] ; then ( # Don't allow subprocesses to inherit the pipe which # emerge uses to monitor ebuild.sh. - exec 9>&- - ebuild_main ${EBUILD_SH_ARGS} + if [[ -n ${PORTAGE_PIPE_FD} ]] ; then + eval "exec ${PORTAGE_PIPE_FD}>&-" + unset PORTAGE_PIPE_FD + fi + __ebuild_main ${EBUILD_SH_ARGS} exit 0 ) exit $? diff --git a/bin/egencache b/bin/egencache index a75a34172..c14be936b 100755 --- a/bin/egencache +++ b/bin/egencache @@ -1,15 +1,17 @@ -#!/usr/bin/python -# Copyright 2009-2012 Gentoo Foundation +#!/usr/bin/python -b +# Copyright 2009-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -from __future__ import print_function +# unicode_literals for compat with TextIOWrapper in Python 2 +from __future__ import print_function, unicode_literals +import platform import signal import sys # This block ensures that ^C interrupts are handled quietly. try: - def exithandler(signum,frame): + def exithandler(signum, _frame): signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGTERM, signal.SIG_IGN) sys.exit(128 + signum) @@ -20,26 +22,39 @@ try: except KeyboardInterrupt: sys.exit(128 + signal.SIGINT) +def debug_signal(_signum, _frame): + import pdb + pdb.set_trace() + +if platform.python_implementation() == 'Jython': + debug_signum = signal.SIGUSR2 # bug #424259 +else: + debug_signum = signal.SIGUSR1 + +signal.signal(debug_signum, debug_signal) + import io import logging -import optparse import subprocess import time import textwrap import re -try: - import portage -except ImportError: - from os import path as osp - sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) - import portage - +from os import path as osp +pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym") +sys.path.insert(0, pym_path) +import portage +portage._internal_caller = True from portage import os, _encodings, _unicode_encode, _unicode_decode from _emerge.MetadataRegen import MetadataRegen from portage.cache.cache_errors import CacheError, StatCollision +from portage.const import TIMESTAMP_FORMAT from portage.manifest import guessManifestFileType +from portage.package.ebuild._parallel_manifest.ManifestScheduler import ManifestScheduler from portage.util import cmp_sort_key, writemsg_level +from portage.util._argparse import ArgumentParser +from portage.util._async.run_main_scheduler import run_main_scheduler +from portage.util._eventloop.global_event_loop import global_event_loop from portage import cpv_getkey from portage.dep import Atom, isjustname from portage.versions import pkgsplit, vercmp @@ -59,72 +74,98 @@ else: from repoman.utilities import FindVCS if sys.hexversion >= 0x3000000: + # pylint: disable=W0622 long = int def parse_args(args): usage = "egencache [options] <action> ... [atom] ..." - parser = optparse.OptionParser(usage=usage) + parser = ArgumentParser(usage=usage) - actions = optparse.OptionGroup(parser, 'Actions') - actions.add_option("--update", + actions = parser.add_argument_group('Actions') + actions.add_argument("--update", action="store_true", - help="update metadata/cache/ (generate as necessary)") - actions.add_option("--update-use-local-desc", + help="update metadata/md5-cache/ (generate as necessary)") + actions.add_argument("--update-use-local-desc", action="store_true", help="update the use.local.desc file from metadata.xml") - actions.add_option("--update-changelogs", + actions.add_argument("--update-changelogs", action="store_true", help="update the ChangeLog files from SCM logs") - parser.add_option_group(actions) + actions.add_argument("--update-manifests", + action="store_true", + help="update manifests") - common = optparse.OptionGroup(parser, 'Common options') - common.add_option("--repo", + common = parser.add_argument_group('Common options') + common.add_argument("--repo", action="store", - help="name of repo to operate on (default repo is located at $PORTDIR)") - common.add_option("--config-root", + help="name of repo to operate on") + common.add_argument("--config-root", help="location of portage config files", dest="portage_configroot") - common.add_option("--portdir", - help="override the portage tree location", + common.add_argument("--gpg-dir", + help="override the PORTAGE_GPG_DIR variable", + dest="gpg_dir") + common.add_argument("--gpg-key", + help="override the PORTAGE_GPG_KEY variable", + dest="gpg_key") + common.add_argument("--portdir", + help="override the PORTDIR variable (deprecated in favor of --repositories-configuration)", dest="portdir") - common.add_option("--portdir-overlay", - help="override the PORTDIR_OVERLAY variable (requires that --repo is also specified)", + common.add_argument("--portdir-overlay", + help="override the PORTDIR_OVERLAY variable (deprecated in favor of --repositories-configuration)", dest="portdir_overlay") - common.add_option("--tolerant", + common.add_argument("--repositories-configuration", + help="override configuration of repositories (in format of repos.conf)", + dest="repositories_configuration") + common.add_argument("--sign-manifests", + choices=('y', 'n'), + metavar="<y|n>", + help="manually override layout.conf sign-manifests setting") + common.add_argument("--strict-manifests", + choices=('y', 'n'), + metavar="<y|n>", + help="manually override \"strict\" FEATURES setting") + common.add_argument("--thin-manifests", + choices=('y', 'n'), + metavar="<y|n>", + help="manually override layout.conf thin-manifests setting") + common.add_argument("--tolerant", action="store_true", help="exit successfully if only minor errors occurred") - common.add_option("--ignore-default-opts", + common.add_argument("--ignore-default-opts", action="store_true", help="do not use the EGENCACHE_DEFAULT_OPTS environment variable") - parser.add_option_group(common) + common.add_argument("--write-timestamp", + action="store_true", + help="write metadata/timestamp.chk as required for rsync repositories") - update = optparse.OptionGroup(parser, '--update options') - update.add_option("--cache-dir", + update = parser.add_argument_group('--update options') + update.add_argument("--cache-dir", help="location of the metadata cache", dest="cache_dir") - update.add_option("--jobs", + update.add_argument("-j", "--jobs", + type=int, action="store", help="max ebuild processes to spawn") - update.add_option("--load-average", + update.add_argument("--load-average", + type=float, action="store", help="max load allowed when spawning multiple jobs", dest="load_average") - update.add_option("--rsync", + update.add_argument("--rsync", action="store_true", help="enable rsync stat collision workaround " + \ "for bug 139134 (use with --update)") - parser.add_option_group(update) - uld = optparse.OptionGroup(parser, '--update-use-local-desc options') - uld.add_option("--preserve-comments", + uld = parser.add_argument_group('--update-use-local-desc options') + uld.add_argument("--preserve-comments", action="store_true", help="preserve the comments from the existing use.local.desc file") - uld.add_option("--use-local-desc-output", + uld.add_argument("--use-local-desc-output", help="output file for use.local.desc data (or '-' for stdout)", dest="uld_output") - parser.add_option_group(uld) - options, args = parser.parse_args(args) + options, args = parser.parse_known_args(args) if options.jobs: jobs = None @@ -171,9 +212,12 @@ def parse_args(args): parser.error("Write access denied: --cache-dir='%s'" % \ (options.cache_dir,)) - if options.portdir_overlay is not None and \ - options.repo is None: - parser.error("--portdir-overlay option requires --repo option") + if options.portdir is not None: + writemsg_level("egencache: warning: --portdir option is deprecated in favor of --repositories-configuration option\n", + level=logging.WARNING, noiselevel=-1) + if options.portdir_overlay is not None: + writemsg_level("egencache: warning: --portdir-overlay option is deprecated in favor of --repositories-configuration option\n", + level=logging.WARNING, noiselevel=-1) for atom in args: try: @@ -215,9 +259,11 @@ class GenCache(object): else: self._cp_set = None self._cp_missing = set() + write_auxdb = "metadata-transfer" in portdb.settings.features self._regen = MetadataRegen(portdb, cp_iter=cp_iter, consumer=self._metadata_callback, - max_jobs=max_jobs, max_load=max_load) + max_jobs=max_jobs, max_load=max_load, + write_auxdb=write_auxdb, main=True) self.returncode = os.EX_OK conf = portdb.repositories.get_repo_for_location(tree) self._trg_caches = tuple(conf.iter_pregenerated_caches( @@ -255,98 +301,74 @@ class GenCache(object): def _write_cache(self, trg_cache, cpv, repo_path, metadata, ebuild_hash): - if not hasattr(trg_cache, 'raise_stat_collision'): - # This cache does not avoid redundant writes automatically, - # so check for an identical existing entry before writing. - # This prevents unnecessary disk writes and can also prevent - # unnecessary rsync transfers. - try: - dest = trg_cache[cpv] - except (KeyError, CacheError): - pass - else: - if trg_cache.validate_entry(dest, - ebuild_hash, self._eclass_db): - identical = True - for k in self._auxdbkeys: - if dest.get(k, '') != metadata.get(k, ''): - identical = False - break - if identical: - return + if not hasattr(trg_cache, 'raise_stat_collision'): + # This cache does not avoid redundant writes automatically, + # so check for an identical existing entry before writing. + # This prevents unnecessary disk writes and can also prevent + # unnecessary rsync transfers. + try: + dest = trg_cache[cpv] + except (KeyError, CacheError): + pass + else: + if trg_cache.validate_entry(dest, + ebuild_hash, self._eclass_db): + identical = True + for k in self._auxdbkeys: + if dest.get(k, '') != metadata.get(k, ''): + identical = False + break + if identical: + return + try: + chf = trg_cache.validation_chf + metadata['_%s_' % chf] = getattr(ebuild_hash, chf) try: - chf = trg_cache.validation_chf - metadata['_%s_' % chf] = getattr(ebuild_hash, chf) + trg_cache[cpv] = metadata + except StatCollision as sc: + # If the content of a cache entry changes and neither the + # file mtime nor size changes, it will prevent rsync from + # detecting changes. Cache backends may raise this + # exception from _setitem() if they detect this type of stat + # collision. These exceptions are handled by bumping the + # mtime on the ebuild (and the corresponding cache entry). + # See bug #139134. It is convenient to include checks for + # redundant writes along with the internal StatCollision + # detection code, so for caches with the + # raise_stat_collision attribute, we do not need to + # explicitly check for redundant writes like we do for the + # other cache types above. + max_mtime = sc.mtime + for _ec, ec_hash in metadata['_eclasses_'].items(): + if max_mtime < ec_hash.mtime: + max_mtime = ec_hash.mtime + if max_mtime == sc.mtime: + max_mtime += 1 + max_mtime = long(max_mtime) try: + os.utime(ebuild_hash.location, (max_mtime, max_mtime)) + except OSError as e: + self.returncode |= 1 + writemsg_level( + "%s writing target: %s\n" % (cpv, e), + level=logging.ERROR, noiselevel=-1) + else: + ebuild_hash.mtime = max_mtime + metadata['_mtime_'] = max_mtime trg_cache[cpv] = metadata - except StatCollision as sc: - # If the content of a cache entry changes and neither the - # file mtime nor size changes, it will prevent rsync from - # detecting changes. Cache backends may raise this - # exception from _setitem() if they detect this type of stat - # collision. These exceptions are handled by bumping the - # mtime on the ebuild (and the corresponding cache entry). - # See bug #139134. It is convenient to include checks for - # redundant writes along with the internal StatCollision - # detection code, so for caches with the - # raise_stat_collision attribute, we do not need to - # explicitly check for redundant writes like we do for the - # other cache types above. - max_mtime = sc.mtime - for ec, ec_hash in metadata['_eclasses_'].items(): - if max_mtime < ec_hash.mtime: - max_mtime = ec_hash.mtime - if max_mtime == sc.mtime: - max_mtime += 1 - max_mtime = long(max_mtime) - try: - os.utime(ebuild_hash.location, (max_mtime, max_mtime)) - except OSError as e: - self.returncode |= 1 - writemsg_level( - "%s writing target: %s\n" % (cpv, e), - level=logging.ERROR, noiselevel=-1) - else: - ebuild_hash.mtime = max_mtime - metadata['_mtime_'] = max_mtime - trg_cache[cpv] = metadata - self._portdb.auxdb[repo_path][cpv] = metadata + self._portdb.auxdb[repo_path][cpv] = metadata - except CacheError as ce: - self.returncode |= 1 - writemsg_level( - "%s writing target: %s\n" % (cpv, ce), - level=logging.ERROR, noiselevel=-1) + except CacheError as ce: + self.returncode |= 1 + writemsg_level( + "%s writing target: %s\n" % (cpv, ce), + level=logging.ERROR, noiselevel=-1) def run(self): - - received_signal = [] - - def sighandler(signum, frame): - signal.signal(signal.SIGINT, signal.SIG_IGN) - signal.signal(signal.SIGTERM, signal.SIG_IGN) - self._regen.terminate() - received_signal.append(128 + signum) - - earlier_sigint_handler = signal.signal(signal.SIGINT, sighandler) - earlier_sigterm_handler = signal.signal(signal.SIGTERM, sighandler) - - try: - self._regen.run() - finally: - # Restore previous handlers - if earlier_sigint_handler is not None: - signal.signal(signal.SIGINT, earlier_sigint_handler) - else: - signal.signal(signal.SIGINT, signal.SIG_DFL) - if earlier_sigterm_handler is not None: - signal.signal(signal.SIGTERM, earlier_sigterm_handler) - else: - signal.signal(signal.SIGTERM, signal.SIG_DFL) - - if received_signal: - sys.exit(received_signal[0]) + signum = run_main_scheduler(self._regen) + if signum is not None: + sys.exit(128 + signum) self.returncode |= self._regen.returncode @@ -371,8 +393,8 @@ class GenCache(object): self.returncode |= 1 writemsg_level( "Error listing cache entries for " + \ - "'%s/metadata/cache': %s, continuing...\n" % \ - (self._portdb.porttree_root, ce), + "'%s': %s, continuing...\n" % \ + (trg_cache.location, ce), level=logging.ERROR, noiselevel=-1) else: @@ -393,8 +415,8 @@ class GenCache(object): self.returncode |= 1 writemsg_level( "Error listing cache entries for " + \ - "'%s/metadata/cache': %s, continuing...\n" % \ - (self._portdb.porttree_root, ce), + "'%s': %s, continuing...\n" % \ + (trg_cache.location, ce), level=logging.ERROR, noiselevel=-1) if cp_missing: @@ -436,7 +458,7 @@ class GenUseLocalDesc(object): self._portdb = portdb self._output = output self._preserve_comments = preserve_comments - + def run(self): repo_path = self._portdb.porttrees[0] ops = {'<':0, '<=':1, '=':2, '>=':3, '>':4} @@ -509,14 +531,14 @@ class GenUseLocalDesc(object): encoding=_encodings['fs'], errors='strict'), mode='a', encoding=_encodings['repo.content'], errors='backslashreplace') - output.write(_unicode_decode('\n')) + output.write('\n') else: - output.write(textwrap.dedent(_unicode_decode('''\ + output.write(textwrap.dedent('''\ # This file is deprecated as per GLEP 56 in favor of metadata.xml. Please add # your descriptions to your package's metadata.xml ONLY. # * generated automatically using egencache * - '''))) + ''')) # The cmp function no longer exists in python3, so we'll # implement our own here under a slightly different name @@ -544,7 +566,8 @@ class GenUseLocalDesc(object): for cp in self._portdb.cp_all(): metadata_path = os.path.join(repo_path, cp, 'metadata.xml') try: - metadata = ElementTree.parse(metadata_path, + metadata = ElementTree.parse(_unicode_encode(metadata_path, + encoding=_encodings['fs'], errors='strict'), parser=ElementTree.XMLParser( target=_MetadataTreeBuilder())) except IOError: @@ -600,8 +623,7 @@ class GenUseLocalDesc(object): resatoms = sorted(reskeys, key=cmp_sort_key(atomcmp)) resdesc = resdict[reskeys[resatoms[-1]]] - output.write(_unicode_decode( - '%s:%s - %s\n' % (cp, flag, resdesc))) + output.write('%s:%s - %s\n' % (cp, flag, resdesc)) output.close() @@ -623,7 +645,8 @@ class _special_filename(_filename_base): self.file_name = file_name self.file_type = guessManifestFileType(file_name) - def file_type_lt(self, a, b): + @staticmethod + def file_type_lt(a, b): """ Defines an ordering between file types. """ @@ -698,12 +721,12 @@ class GenChangeLogs(object): self.returncode |= 2 return - output.write(textwrap.dedent(_unicode_decode('''\ + output.write(textwrap.dedent('''\ # ChangeLog for %s # Copyright 1999-%s Gentoo Foundation; Distributed under the GPL v2 # $Header: $ - ''' % (cp, time.strftime('%Y'))))) + ''' % (cp, time.strftime('%Y')))) # now grab all the commits commits = self.grab(['git', 'rev-list', 'HEAD', '--', '.']).split() @@ -767,11 +790,10 @@ class GenChangeLogs(object): # Reverse the sort order for headers. for c in reversed(changed): if c.startswith('+') and c.endswith('.ebuild'): - output.write(_unicode_decode( - '*%s (%s)\n' % (c[1:-7], date))) + output.write('*%s (%s)\n' % (c[1:-7], date)) wroteheader = True if wroteheader: - output.write(_unicode_decode('\n')) + output.write('\n') # strip '<cp>: ', '[<cp>] ', and similar body[0] = re.sub(r'^\W*' + re.escape(cp) + r'\W+', '', body[0]) @@ -791,13 +813,12 @@ class GenChangeLogs(object): # don't break filenames on hyphens self._wrapper.break_on_hyphens = False - output.write(_unicode_decode( - self._wrapper.fill( - '%s; %s %s:' % (date, author, ', '.join(changed))))) + output.write(self._wrapper.fill( + '%s; %s %s:' % (date, author, ', '.join(changed)))) # but feel free to break commit messages there self._wrapper.break_on_hyphens = True - output.write(_unicode_decode( - '\n%s\n\n' % '\n'.join(self._wrapper.fill(x) for x in body))) + output.write( + '\n%s\n\n' % '\n'.join(self._wrapper.fill(x) for x in body)) output.close() @@ -830,17 +851,22 @@ class GenChangeLogs(object): self.generate_changelog(cp) def egencache_main(args): - parser, options, atoms = parse_args(args) - - config_root = options.config_root # The calling environment is ignored, so the program is # completely controlled by commandline arguments. env = {} - if options.repo is None: - env['PORTDIR_OVERLAY'] = '' - elif options.portdir_overlay: + if not sys.stdout.isatty(): + portage.output.nocolor() + env['NOCOLOR'] = 'true' + + parser, options, atoms = parse_args(args) + + config_root = options.config_root + + if options.repositories_configuration is not None: + env['PORTAGE_REPOSITORIES'] = options.repositories_configuration + elif options.portdir_overlay is not None: env['PORTDIR_OVERLAY'] = options.portdir_overlay if options.cache_dir is not None: @@ -854,7 +880,8 @@ def egencache_main(args): default_opts = None if not options.ignore_default_opts: - default_opts = settings.get('EGENCACHE_DEFAULT_OPTS', '').split() + default_opts = portage.util.shlex_split( + settings.get('EGENCACHE_DEFAULT_OPTS', '')) if default_opts: parser, options, args = parse_args(default_opts + args) @@ -865,18 +892,50 @@ def egencache_main(args): settings = portage.config(config_root=config_root, local_config=False, env=env) - if not options.update and not options.update_use_local_desc \ - and not options.update_changelogs: + if not (options.update or options.update_use_local_desc or + options.update_changelogs or options.update_manifests): parser.error('No action specified') return 1 + if options.repo is None: + if len(settings.repositories.prepos) == 2: + for repo in settings.repositories: + if repo.name != "DEFAULT": + options.repo = repo.name + break + + if options.repo is None: + parser.error("--repo option is required") + + repo_path = settings.repositories.treemap.get(options.repo) + if repo_path is None: + parser.error("Unable to locate repository named '%s'" % (options.repo,)) + return 1 + + repo_config = settings.repositories.get_repo_for_location(repo_path) + + if options.strict_manifests is not None: + if options.strict_manifests == "y": + settings.features.add("strict") + else: + settings.features.discard("strict") + if options.update and 'metadata-transfer' not in settings.features: - settings.features.add('metadata-transfer') + # Forcibly enable metadata-transfer if portdbapi has a pregenerated + # cache that does not support eclass validation. + cache = repo_config.get_pregenerated_cache( + portage.dbapi.dbapi._known_keys, readonly=True) + if cache is not None and not cache.complete_eclass_entries: + settings.features.add('metadata-transfer') + cache = None settings.lock() portdb = portage.portdbapi(mysettings=settings) + # Limit ebuilds to the specified repo. + portdb.porttrees = [repo_path] + if options.update: if options.cache_dir is not None: # already validated earlier @@ -892,17 +951,71 @@ def egencache_main(args): level=logging.ERROR, noiselevel=-1) return 1 - if options.repo is not None: - repo_path = portdb.getRepositoryPath(options.repo) - if repo_path is None: - parser.error("Unable to locate repository named '%s'" % \ - (options.repo,)) - return 1 + if options.sign_manifests is not None: + repo_config.sign_manifest = options.sign_manifests == 'y' - # Limit ebuilds to the specified repo. - portdb.porttrees = [repo_path] - else: - portdb.porttrees = [portdb.porttree_root] + if options.thin_manifests is not None: + repo_config.thin_manifest = options.thin_manifests == 'y' + + gpg_cmd = None + gpg_vars = None + force_sign_key = None + + if options.update_manifests: + if repo_config.sign_manifest: + + sign_problem = False + gpg_dir = None + gpg_cmd = settings.get("PORTAGE_GPG_SIGNING_COMMAND") + if gpg_cmd is None: + writemsg_level("egencache: error: " + "PORTAGE_GPG_SIGNING_COMMAND is unset! " + "Is make.globals missing?\n", + level=logging.ERROR, noiselevel=-1) + sign_problem = True + elif "${PORTAGE_GPG_KEY}" in gpg_cmd and \ + options.gpg_key is None and \ + "PORTAGE_GPG_KEY" not in settings: + writemsg_level("egencache: error: " + "PORTAGE_GPG_KEY is unset!\n", + level=logging.ERROR, noiselevel=-1) + sign_problem = True + elif "${PORTAGE_GPG_DIR}" in gpg_cmd: + if options.gpg_dir is not None: + gpg_dir = options.gpg_dir + elif "PORTAGE_GPG_DIR" not in settings: + gpg_dir = os.path.expanduser("~/.gnupg") + else: + gpg_dir = os.path.expanduser(settings["PORTAGE_GPG_DIR"]) + if not os.access(gpg_dir, os.X_OK): + writemsg_level(("egencache: error: " + "Unable to access directory: " + "PORTAGE_GPG_DIR='%s'\n") % gpg_dir, + level=logging.ERROR, noiselevel=-1) + sign_problem = True + + if sign_problem: + writemsg_level("egencache: You may disable manifest " + "signatures with --sign-manifests=n or by setting " + "\"sign-manifests = false\" in metadata/layout.conf\n", + level=logging.ERROR, noiselevel=-1) + return 1 + + gpg_vars = {} + if gpg_dir is not None: + gpg_vars["PORTAGE_GPG_DIR"] = gpg_dir + gpg_var_names = [] + if options.gpg_key is None: + gpg_var_names.append("PORTAGE_GPG_KEY") + else: + gpg_vars["PORTAGE_GPG_KEY"] = options.gpg_key + + for k in gpg_var_names: + v = settings.get(k) + if v is not None: + gpg_vars[k] = v + + force_sign_key = gpg_vars.get("PORTAGE_GPG_KEY") ret = [os.EX_OK] @@ -921,6 +1034,29 @@ def egencache_main(args): else: ret.append(gen_cache.returncode) + if options.update_manifests: + + cp_iter = None + if atoms: + cp_iter = iter(atoms) + + event_loop = global_event_loop() + scheduler = ManifestScheduler(portdb, cp_iter=cp_iter, + gpg_cmd=gpg_cmd, gpg_vars=gpg_vars, + force_sign_key=force_sign_key, + max_jobs=options.jobs, + max_load=options.load_average, + event_loop=event_loop) + + signum = run_main_scheduler(scheduler) + if signum is not None: + sys.exit(128 + signum) + + if options.tolerant: + ret.append(os.EX_OK) + else: + ret.append(scheduler.returncode) + if options.update_use_local_desc: gen_desc = GenUseLocalDesc(portdb, output=options.uld_output, @@ -933,6 +1069,16 @@ def egencache_main(args): gen_clogs.run() ret.append(gen_clogs.returncode) + if options.write_timestamp: + timestamp_path = os.path.join(repo_path, 'metadata', 'timestamp.chk') + try: + with open(timestamp_path, 'w') as f: + f.write(time.strftime('%s\n' % TIMESTAMP_FORMAT, time.gmtime())) + except IOError: + ret.append(os.EX_IOERR) + else: + ret.append(os.EX_OK) + return max(ret) if __name__ == "__main__": diff --git a/bin/emaint b/bin/emaint index bee46c40d..aeeb18328 100755 --- a/bin/emaint +++ b/bin/emaint @@ -1,9 +1,8 @@ -#!/usr/bin/python -O -# Copyright 2005-2012 Gentoo Foundation +#!/usr/bin/python -bO +# Copyright 2005-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -"""'The emaint program provides an interface to system health - checks and maintenance. +"""System health checks and maintenance utilities. """ from __future__ import print_function @@ -14,10 +13,10 @@ import errno try: import signal - def exithandler(signum,frame): + def exithandler(signum, _frame): signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGTERM, signal.SIG_IGN) - sys.exit(1) + sys.exit(128 + signum) signal.signal(signal.SIGINT, exithandler) signal.signal(signal.SIGTERM, exithandler) @@ -26,13 +25,11 @@ try: except KeyboardInterrupt: sys.exit(1) -try: - import portage -except ImportError: - from os import path as osp - sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) - import portage - +from os import path as osp +pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym") +sys.path.insert(0, pym_path) +import portage +portage._internal_caller = True from portage.emaint.main import emaint_main try: diff --git a/bin/emerge b/bin/emerge index a9a56432c..bb93d83a6 100755 --- a/bin/emerge +++ b/bin/emerge @@ -1,5 +1,5 @@ -#!/usr/bin/python -# Copyright 2006-2012 Gentoo Foundation +#!/usr/bin/python -b +# Copyright 2006-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import print_function @@ -7,67 +7,73 @@ from __future__ import print_function import platform import signal import sys -# This block ensures that ^C interrupts are handled quietly. + +# This block ensures that ^C interrupts are handled quietly. We handle +# KeyboardInterrupt instead of installing a SIGINT handler, since +# exiting from signal handlers intermittently causes python to ignore +# the SystemExit exception with a message like this: +# Exception SystemExit: 130 in <function remove at 0x7fd2146c1320> ignored try: - def exithandler(signum,frame): - signal.signal(signal.SIGINT, signal.SIG_IGN) + def exithandler(signum, _frame): signal.signal(signal.SIGTERM, signal.SIG_IGN) sys.exit(128 + signum) - signal.signal(signal.SIGINT, exithandler) signal.signal(signal.SIGTERM, exithandler) # Prevent "[Errno 32] Broken pipe" exceptions when # writing to a pipe. signal.signal(signal.SIGPIPE, signal.SIG_DFL) -except KeyboardInterrupt: - sys.exit(128 + signal.SIGINT) + def debug_signal(_signum, _frame): + import pdb + pdb.set_trace() -def debug_signal(signum, frame): - import pdb - pdb.set_trace() + if platform.python_implementation() == 'Jython': + debug_signum = signal.SIGUSR2 # bug #424259 + else: + debug_signum = signal.SIGUSR1 -if platform.python_implementation() == 'Jython': - debug_signum = signal.SIGUSR2 # bug #424259 -else: - debug_signum = signal.SIGUSR1 + signal.signal(debug_signum, debug_signal) -signal.signal(debug_signum, debug_signal) - -try: - from _emerge.main import emerge_main -except ImportError: from os import path as osp - import sys - sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) + pym_path = osp.join(osp.dirname(osp.dirname( + osp.realpath(__file__))), "pym") + sys.path.insert(0, pym_path) + import portage + portage._internal_caller = True + portage._disable_legacy_globals() from _emerge.main import emerge_main -if __name__ == "__main__": - import sys - from portage.exception import ParseError, PermissionDenied - try: - retval = emerge_main() - except PermissionDenied as e: - sys.stderr.write("Permission denied: '%s'\n" % str(e)) - sys.exit(e.errno) - except ParseError as e: - sys.stderr.write("%s\n" % str(e)) - sys.exit(1) - except SystemExit: - raise - except Exception: - # If an unexpected exception occurs then we don't want the mod_echo - # output to obscure the traceback, so dump the mod_echo output before - # showing the traceback. - import traceback - tb_str = traceback.format_exc() + if __name__ == "__main__": + from portage.exception import ParseError, PermissionDenied try: - from portage.elog import mod_echo - except ImportError: - pass - else: - mod_echo.finalize() - sys.stderr.write(tb_str) - sys.exit(1) - sys.exit(retval) + retval = emerge_main() + except PermissionDenied as e: + sys.stderr.write("Permission denied: '%s'\n" % str(e)) + sys.exit(e.errno) + except ParseError as e: + sys.stderr.write("%s\n" % str(e)) + sys.exit(1) + except (KeyboardInterrupt, SystemExit): + raise + except Exception: + # If an unexpected exception occurs then we don't want the + # mod_echo output to obscure the traceback, so dump the + # mod_echo output before showing the traceback. + import traceback + tb_str = traceback.format_exc() + try: + from portage.elog import mod_echo + except ImportError: + pass + else: + mod_echo.finalize() + sys.stderr.write(tb_str) + sys.exit(1) + sys.exit(retval) + +except KeyboardInterrupt: + sys.stderr.write("\n\nExiting on signal %(signal)s\n" % + {"signal": signal.SIGINT}) + sys.stderr.flush() + sys.exit(128 + signal.SIGINT) diff --git a/bin/emerge-webrsync b/bin/emerge-webrsync index bfd9aa2fc..2f0689c15 100755 --- a/bin/emerge-webrsync +++ b/bin/emerge-webrsync @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # Author: Karl Trygve Kalleberg <karltk@gentoo.org> # Rewritten from the old, Perl-based emerge-webrsync script @@ -22,9 +22,9 @@ vvecho() { [[ ${do_verbose} -eq 1 ]] && echo "$@" ; } # Only echo if not in verbose mode nvecho() { [[ ${do_verbose} -eq 0 ]] && echo "$@" ; } # warning echos -wecho() { echo "${argv0}: warning: $*" 1>&2 ; } +wecho() { echo "${argv0##*/}: warning: $*" 1>&2 ; } # error echos -eecho() { echo "${argv0}: error: $*" 1>&2 ; } +eecho() { echo "${argv0##*/}: error: $*" 1>&2 ; } argv0=$0 @@ -39,23 +39,33 @@ else eecho "could not find 'portageq'; aborting" exit 1 fi -eval $("${portageq}" envvar -v FEATURES FETCHCOMMAND GENTOO_MIRRORS \ - PORTAGE_BIN_PATH PORTAGE_GPG_DIR \ - PORTAGE_NICENESS PORTAGE_RSYNC_EXTRA_OPTS PORTAGE_TMPDIR PORTDIR \ - SYNC http_proxy ftp_proxy) -DISTDIR="${PORTAGE_TMPDIR}/emerge-webrsync" +eval "$("${portageq}" envvar -v DISTDIR EPREFIX FEATURES \ + FETCHCOMMAND GENTOO_MIRRORS \ + PORTAGE_BIN_PATH PORTAGE_CONFIGROOT PORTAGE_GPG_DIR \ + PORTAGE_NICENESS PORTAGE_REPOSITORIES PORTAGE_RSYNC_EXTRA_OPTS \ + PORTAGE_RSYNC_OPTS PORTAGE_TMPDIR \ + USERLAND http_proxy ftp_proxy)" export http_proxy ftp_proxy +source "${PORTAGE_BIN_PATH}"/isolated-functions.sh || exit 1 + +repo_name=gentoo +repo_location=$(__repo_attr "${repo_name}" location) +if [[ -z ${repo_location} ]]; then + eecho "Repository '${repo_name}' not found" + exit 1 +fi +repo_sync_type=$(__repo_attr "${repo_name}" sync-type) + # If PORTAGE_NICENESS is overriden via the env then it will # still pass through the portageq call and override properly. if [ -n "${PORTAGE_NICENESS}" ]; then renice $PORTAGE_NICENESS $$ > /dev/null fi -source "${PORTAGE_BIN_PATH}"/isolated-functions.sh || exit 1 - do_verbose=0 do_debug=0 +keep=false if has webrsync-gpg ${FEATURES} ; then WEBSYNC_VERIFY_SIGNATURE=1 @@ -99,7 +109,9 @@ get_date_part() { get_utc_second_from_string() { local s="$1" if [[ ${USERLAND} == BSD ]] ; then - date -juf "%Y%m%d" "$s" +"%s" + # Specify zeros for the least significant digits, or else those + # digits are inherited from the current system clock time. + date -juf "%Y%m%d%H%M.%S" "${s}0000.00" +"%s" else date -d "${s:0:4}-${s:4:2}-${s:6:2}" -u +"%s" fi @@ -108,8 +120,8 @@ get_utc_second_from_string() { get_portage_timestamp() { local portage_current_timestamp=0 - if [ -f "${PORTDIR}/metadata/timestamp.x" ]; then - portage_current_timestamp=$(cut -f 1 -d " " "${PORTDIR}/metadata/timestamp.x" ) + if [ -f "${repo_location}/metadata/timestamp.x" ]; then + portage_current_timestamp=$(cut -f 1 -d " " "${repo_location}/metadata/timestamp.x" ) fi echo "${portage_current_timestamp}" @@ -125,13 +137,18 @@ fetch_file() { elif [ "${FETCHCOMMAND/curl/}" != "${FETCHCOMMAND}" ]; then opts="--continue-at - $(nvecho -s -f)" else - rm -f "${FILE}" + rm -f "${DISTDIR}/${FILE}" fi - vecho "Fetching file ${FILE} ..." + __vecho "Fetching file ${FILE} ..." # already set DISTDIR= - eval "${FETCHCOMMAND}" ${opts} - [ -s "${FILE}" ] + eval "${FETCHCOMMAND} ${opts}" + if [[ $? -eq 0 && -s ${DISTDIR}/${FILE} ]] ; then + return 0 + else + rm -f "${DISTDIR}/${FILE}" + return 1 + fi } check_file_digest() { @@ -139,10 +156,12 @@ check_file_digest() { local file="$2" local r=1 - vecho "Checking digest ..." + __vecho "Checking digest ..." if type -P md5sum > /dev/null; then - md5sum -c $digest && r=0 + local md5sum_output=$(md5sum "${file}") + local digest_content=$(< "${digest}") + [ "${md5sum_output%%[[:space:]]*}" = "${digest_content%%[[:space:]]*}" ] && r=0 elif type -P md5 > /dev/null; then [ "$(md5 -q "${file}")" == "$(cut -d ' ' -f 1 "${digest}")" ] && r=0 else @@ -159,7 +178,7 @@ check_file_signature() { if [ ${WEBSYNC_VERIFY_SIGNATURE} != 0 ]; then - vecho "Checking signature ..." + __vecho "Checking signature ..." if type -P gpg > /dev/null; then gpg --homedir "${PORTAGE_GPG_DIR}" --verify "$signature" "$file" && r=0 @@ -183,13 +202,25 @@ get_snapshot_timestamp() { sync_local() { local file="$1" - vecho "Syncing local tree ..." + __vecho "Syncing local tree ..." + + local ownership="portage:portage" + if has usersync ${FEATURES} ; then + case "${USERLAND}" in + BSD) + ownership=$(stat -f '%Su:%Sg' "${repo_location}") + ;; + *) + ownership=$(stat -c '%U:%G' "${repo_location}") + ;; + esac + fi if type -P tarsync > /dev/null ; then - local chown_opts="-o portage -g portage" - chown portage:portage portage > /dev/null 2>&1 || chown_opts="" + local chown_opts="-o ${ownership%:*} -g ${ownership#*:}" + chown ${ownership} "${repo_location}" > /dev/null 2>&1 || chown_opts="" if ! tarsync $(vvecho -v) -s 1 ${chown_opts} \ - -e /distfiles -e /packages -e /local "${file}" "${PORTDIR}"; then + -e /distfiles -e /packages -e /local "${file}" "${repo_location}"; then eecho "tarsync failed; tarball is corrupt? (${file})" return 1 fi @@ -201,27 +232,29 @@ sync_local() { fi # Free disk space - rm -f "${file}" + ${keep} || rm -f "${file}" - chown portage:portage portage > /dev/null 2>&1 && \ - chown -R portage:portage portage + local rsync_opts="${PORTAGE_RSYNC_OPTS} ${PORTAGE_RSYNC_EXTRA_OPTS}" + if chown ${ownership} portage > /dev/null 2>&1; then + chown -R ${ownership} portage + rsync_opts+=" --owner --group" + fi cd portage - rsync -av --progress --stats --delete --delete-after \ - --exclude='/distfiles' --exclude='/packages' \ - --exclude='/local' ${PORTAGE_RSYNC_EXTRA_OPTS} . "${PORTDIR%%/}" + rsync ${rsync_opts} . "${repo_location%%/}" cd .. - vecho "Cleaning up ..." + __vecho "Cleaning up ..." rm -fr portage fi if has metadata-transfer ${FEATURES} ; then - vecho "Updating cache ..." - emerge --metadata + __vecho "Updating cache ..." + "${PORTAGE_BIN_PATH}/emerge" --metadata fi - [ -x /etc/portage/bin/post_sync ] && /etc/portage/bin/post_sync + local post_sync=${PORTAGE_CONFIGROOT}etc/portage/bin/post_sync + [ -x "${post_sync}" ] && "${post_sync}" # --quiet suppresses output if there are no relevant news items - has news ${FEATURES} && emerge --check-news --quiet + has news ${FEATURES} && "${PORTAGE_BIN_PATH}/emerge" --check-news --quiet return 0 } @@ -251,14 +284,15 @@ do_snapshot() { for mirror in ${GENTOO_MIRRORS} ; do - vecho "Trying to retrieve ${date} snapshot from ${mirror} ..." + mirror=${mirror%/} + __vecho "Trying to retrieve ${date} snapshot from ${mirror} ..." for compression in ${compressions} ; do local file="portage-${date}.tar.${compression}" local digest="${file}.md5sum" local signature="${file}.gpgsig" - if [ -s "${file}" -a -s "${digest}" -a -s "${signature}" ] ; then + if [ -s "${DISTDIR}/${file}" -a -s "${DISTDIR}/${digest}" -a -s "${DISTDIR}/${signature}" ] ; then check_file_digest "${DISTDIR}/${digest}" "${DISTDIR}/${file}" && \ check_file_signature "${DISTDIR}/${signature}" "${DISTDIR}/${file}" && \ have_files=1 @@ -280,8 +314,8 @@ do_snapshot() { # if [ ${have_files} -eq 1 ]; then - vecho "Getting snapshot timestamp ..." - local snapshot_timestamp=$(get_snapshot_timestamp "${file}") + __vecho "Getting snapshot timestamp ..." + local snapshot_timestamp=$(get_snapshot_timestamp "${DISTDIR}/${file}") if [ ${ignore_timestamp} == 0 ]; then if [ ${snapshot_timestamp} -lt $(get_portage_timestamp) ]; then @@ -310,7 +344,7 @@ do_snapshot() { # # Remove files and use a different mirror # - rm -f "${file}" "${digest}" "${signature}" + rm -f "${DISTDIR}/${file}" "${DISTDIR}/${digest}" "${DISTDIR}/${signature}" fi done @@ -318,12 +352,12 @@ do_snapshot() { done if [ ${have_files} -eq 1 ]; then - sync_local "${file}" && r=0 + sync_local "${DISTDIR}/${file}" && r=0 else - vecho "${date} snapshot was not found" + __vecho "${date} snapshot was not found" fi - - rm -f "${file}" "${digest}" "${signature}" + + ${keep} || rm -f "${DISTDIR}/${file}" "${DISTDIR}/${digest}" "${DISTDIR}/${signature}" return "${r}" } @@ -331,9 +365,9 @@ do_latest_snapshot() { local attempts=0 local r=1 - vecho "Fetching most recent snapshot ..." + __vecho "Fetching most recent snapshot ..." - # The snapshot for a given day is generated at 01:45 UTC on the following + # The snapshot for a given day is generated at 00:45 UTC on the following # day, so the current day's snapshot (going by UTC time) hasn't been # generated yet. Therefore, always start by looking for the previous day's # snapshot (for attempts=1, subtract 1 day from the current UTC time). @@ -349,10 +383,10 @@ do_latest_snapshot() { local start_time=$(get_utc_date_in_seconds) local start_hour=$(get_date_part ${start_time} "%H") - # Daily snapshots are created at 1:45 AM and are not - # available until after 2 AM. Don't waste time trying + # Daily snapshots are created at 00:45 and are not + # available until after 01:00. Don't waste time trying # to fetch a snapshot before it's been created. - if [ ${start_hour} -lt 2 ] ; then + if [ ${start_hour} -lt 1 ] ; then (( start_time -= 86400 )) fi local snapshot_date=$(get_date_part ${start_time} "%Y%m%d") @@ -361,8 +395,8 @@ do_latest_snapshot() { while (( ${attempts} < 40 )) ; do (( attempts++ )) (( snapshot_date_seconds -= 86400 )) - # snapshots are created at 1:45 AM - (( approx_snapshot_time = snapshot_date_seconds + 86400 + 6300 )) + # snapshots are created at 00:45 + (( approx_snapshot_time = snapshot_date_seconds + 86400 + 2700 )) (( timestamp_difference = existing_timestamp - approx_snapshot_time )) [ ${timestamp_difference} -lt 0 ] && (( timestamp_difference = -1 * timestamp_difference )) snapshot_date=$(get_date_part ${snapshot_date_seconds} "%Y%m%d") @@ -388,7 +422,7 @@ do_latest_snapshot() { "snapshot. In order to force sync," \ "use the --revert option or remove" \ "the timestamp file located at" \ - "'${PORTDIR}/metadata/timestamp.x'." | fmt -w 70 | \ + "'${repo_location}/metadata/timestamp.x'." | fmt -w 70 | \ while read -r line ; do ewarn "${line}" done @@ -408,9 +442,10 @@ do_latest_snapshot() { usage() { cat <<-EOF Usage: $0 [options] - + Options: --revert=yyyymmdd Revert to snapshot + -k, --keep Keep snapshots in DISTDIR (don't delete) -q, --quiet Only output errors -v, --verbose Enable verbose output -x, --debug Enable debug output @@ -427,14 +462,12 @@ usage() { main() { local arg local revert_date - - [ ! -d "${DISTDIR}" ] && mkdir -p "${DISTDIR}" - cd "${DISTDIR}" for arg in "$@" ; do local v=${arg#*=} case ${arg} in -h|--help) usage ;; + -k|--keep) keep=true ;; -q|--quiet) PORTAGE_QUIET=1 ;; -v|--verbose) do_verbose=1 ;; -x|--debug) do_debug=1 ;; @@ -443,16 +476,39 @@ main() { esac done + [[ -d ${repo_location} ]] || mkdir -p "${repo_location}" + if [[ ! -w ${repo_location} ]] ; then + eecho "Repository '${repo_name}' is not writable: ${repo_location}" + exit 1 + fi + + [[ -d ${PORTAGE_TMPDIR}/portage ]] || mkdir -p "${PORTAGE_TMPDIR}/portage" + TMPDIR=$(mktemp -d "${PORTAGE_TMPDIR}/portage/webrsync-XXXXXX") + if [[ ! -w ${TMPDIR} ]] ; then + eecho "TMPDIR is not writable: ${TMPDIR}" + exit 1 + fi + trap 'cd / ; rm -rf "${TMPDIR}"' EXIT + cd "${TMPDIR}" || exit 1 + + ${keep} || DISTDIR=${TMPDIR} + [ ! -d "${DISTDIR}" ] && mkdir -p "${DISTDIR}" + + if ${keep} && [[ ! -w ${DISTDIR} ]] ; then + eecho "DISTDIR is not writable: ${DISTDIR}" + exit 1 + fi + # This is a sanity check to help prevent people like funtoo users # from accidentally wiping out their git tree. - if [[ -n $SYNC && ${SYNC#rsync:} = $SYNC ]] ; then - echo "The current SYNC variable setting does not refer to an rsync URI:" >&2 + if [[ -n ${repo_sync_type} && ${repo_sync_type} != rsync ]] ; then + echo "The current sync-type attribute of repository 'gentoo' is not set to 'rsync':" >&2 echo >&2 - echo " SYNC=$SYNC" >&2 + echo " sync-type=${repo_sync_type}" >&2 echo >&2 echo "If you intend to use emerge-webrsync then please" >&2 - echo "adjust SYNC to refer to an rsync URI." >&2 - echo "emerge-webrsync exiting due to abnormal SYNC setting." >&2 + echo "adjust sync-type and sync-uri attributes to refer to rsync." >&2 + echo "emerge-webrsync exiting due to abnormal sync-type setting." >&2 exit 1 fi diff --git a/bin/emirrordist b/bin/emirrordist new file mode 100755 index 000000000..0368eee2a --- /dev/null +++ b/bin/emirrordist @@ -0,0 +1,13 @@ +#!/usr/bin/python -b +# Copyright 2013-2014 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +import sys + +import portage +portage._internal_caller = True +portage._disable_legacy_globals() +from portage._emirrordist.main import emirrordist_main + +if __name__ == "__main__": + sys.exit(emirrordist_main(sys.argv[1:])) diff --git a/bin/env-update b/bin/env-update index 8a69f2bb2..7651ef9c1 100755 --- a/bin/env-update +++ b/bin/env-update @@ -1,5 +1,5 @@ -#!/usr/bin/python -O -# Copyright 1999-2006 Gentoo Foundation +#!/usr/bin/python -bO +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import print_function @@ -25,12 +25,12 @@ if len(sys.argv) > 1: print("!!! Invalid command line options!\n") usage(1) -try: - import portage -except ImportError: - from os import path as osp - sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) - import portage +from os import path as osp +pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym") +sys.path.insert(0, pym_path) +import portage +portage._internal_caller = True + try: portage.env_update(makelinks) except IOError as e: diff --git a/bin/etc-update b/bin/etc-update index d763c1f73..1a99231b7 100755 --- a/bin/etc-update +++ b/bin/etc-update @@ -62,7 +62,7 @@ do_mv_ln() { } scan() { - echo "Scanning Configuration files..." + ${QUIET} || echo "Scanning Configuration files..." rm -rf "${TMP}"/files > /dev/null 2>&1 mkdir "${TMP}"/files || die "Failed mkdir command!" count=0 @@ -107,13 +107,13 @@ scan() { for mpath in ${CONFIG_PROTECT_MASK}; do mpath="${EROOT%/}${mpath}" if [[ "${rpath}" == "${mpath}"* ]] ; then - echo "Updating masked file: ${live_file}" + ${QUIET} || echo "Updating masked file: ${live_file}" mv "${cfg_file}" "${live_file}" continue 2 fi done if [[ ! -f ${file} ]] ; then - echo "Skipping non-file ${file} ..." + ${QUIET} || echo "Skipping non-file ${file} ..." continue fi @@ -140,7 +140,7 @@ scan() { fi if [[ ${MATCHES} == 1 ]] ; then - echo "Automerging trivial changes in: ${live_file}" + ${QUIET} || echo "Automerging trivial changes in: ${live_file}" do_mv_ln "${cfg_file}" "${live_file}" continue else @@ -548,9 +548,9 @@ die() { local msg=$1 exitcode=${2:-1} if [ ${exitcode} -eq 0 ] ; then - printf 'Exiting: %b\n' "${msg}" + ${QUIET} || printf 'Exiting: %b\n' "${msg}" scan > /dev/null - [ ${count} -gt 0 ] && echo "NOTE: ${count} updates remaining" + ! ${QUIET} && [ ${count} -gt 0 ] && echo "NOTE: ${count} updates remaining" else error "${msg}" fi @@ -575,6 +575,7 @@ usage() { -d, --debug Enable shell debugging -h, --help Show help and run away -p, --preen Automerge trivial changes only and quit + -q, --quiet Show only essential output -v, --verbose Show settings and such along the way -V, --version Show version and trundle away @@ -600,6 +601,7 @@ declare title="Gentoo's etc-update tool!" PREEN=false SET_X=false +QUIET=false VERBOSE=false NONINTERACTIVE_MV=false while [[ -n $1 ]] ; do @@ -607,6 +609,7 @@ while [[ -n $1 ]] ; do -d|--debug) SET_X=true;; -h|--help) usage;; -p|--preen) PREEN=true;; + -q|--quiet) QUIET=true;; -v|--verbose) VERBOSE=true;; -V|--version) emerge --version; exit 0;; --automode) parse_automode_flag $2 && shift || usage 1 "Invalid mode '$2'";; @@ -617,7 +620,7 @@ while [[ -n $1 ]] ; do done ${SET_X} && set -x -type portageq >/dev/null || die "missing portageq" +type -P portageq >/dev/null || die "missing portageq" portage_vars=( CONFIG_PROTECT{,_MASK} PORTAGE_CONFIGROOT @@ -627,7 +630,7 @@ portage_vars=( USERLAND NOCOLOR ) -eval $(portageq envvar -v ${portage_vars[@]}) +eval $(${PORTAGE_PYTHON:+"${PORTAGE_PYTHON}"} "$(type -P portageq)" envvar -v ${portage_vars[@]}) export PORTAGE_TMPDIR SCAN_PATHS=${*:-${CONFIG_PROTECT}} diff --git a/bin/filter-bash-environment.py b/bin/filter-bash-environment.py index b9aec96d0..a4cdc5429 100755 --- a/bin/filter-bash-environment.py +++ b/bin/filter-bash-environment.py @@ -1,10 +1,9 @@ -#!/usr/bin/python -# Copyright 1999-2011 Gentoo Foundation +#!/usr/bin/python -b +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 import codecs import io -import optparse import os import re import sys @@ -126,10 +125,19 @@ if __name__ == "__main__": "intact. The PATTERN is a space separated list of variable names" + \ " and it supports python regular expression syntax." usage = "usage: %s PATTERN" % os.path.basename(sys.argv[0]) - parser = optparse.OptionParser(description=description, usage=usage) - options, args = parser.parse_args(sys.argv[1:]) + args = sys.argv[1:] + + if '-h' in args or '--help' in args: + sys.stdout.write(usage + "\n") + sys.stdout.flush() + sys.exit(os.EX_OK) + if len(args) != 1: - parser.error("Missing required PATTERN argument.") + sys.stderr.write(usage + "\n") + sys.stderr.write("Exactly one PATTERN argument required.\n") + sys.stderr.flush() + sys.exit(2) + file_in = sys.stdin file_out = sys.stdout if sys.hexversion >= 0x3000000: diff --git a/bin/fixpackages b/bin/fixpackages index dc43ed2b3..cec0030f2 100755 --- a/bin/fixpackages +++ b/bin/fixpackages @@ -1,5 +1,5 @@ -#!/usr/bin/python -# Copyright 1999-2011 Gentoo Foundation +#!/usr/bin/python -b +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import print_function @@ -7,21 +7,27 @@ from __future__ import print_function import os import sys -try: - import portage -except ImportError: - from os import path as osp - sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) - import portage - +from os import path as osp +pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym") +sys.path.insert(0, pym_path) +import portage +portage._internal_caller = True from portage import os from portage.output import EOutput +from portage.util._argparse import ArgumentParser from textwrap import wrap from portage._global_updates import _global_updates mysettings = portage.settings mytrees = portage.db mtimedb = portage.mtimedb +description = """The fixpackages program performs package move updates on + configuration files, installed packages, and binary packages.""" +description = " ".join(description.split()) + +parser = ArgumentParser(description=description) +parser.parse_args() + if mysettings['ROOT'] != "/": out = EOutput() msg = "The fixpackages program is not intended for use with " + \ diff --git a/bin/glsa-check b/bin/glsa-check index a840c3206..972679a80 100755 --- a/bin/glsa-check +++ b/bin/glsa-check @@ -1,81 +1,79 @@ -#!/usr/bin/python -# Copyright 2008-2011 Gentoo Foundation +#!/usr/bin/python -b +# Copyright 2008-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import print_function import sys +import codecs -try: - import portage -except ImportError: - from os import path as osp - sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) - import portage - +from os import path as osp +pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym") +sys.path.insert(0, pym_path) +import portage +portage._internal_caller = True from portage import os -from portage.output import * - -from optparse import OptionGroup, OptionParser +from portage.output import green, red, nocolor, white +from portage.util._argparse import ArgumentParser __program__ = "glsa-check" __author__ = "Marius Mauch <genone@gentoo.org>" __version__ = "1.0" -def cb_version(*args, **kwargs): - """Callback for --version""" - sys.stderr.write("\n"+ __program__ + ", version " + __version__ + "\n") - sys.stderr.write("Author: " + __author__ + "\n") - sys.stderr.write("This program is licensed under the GPL, version 2\n\n") - sys.exit(0) - # option parsing -parser = OptionParser(usage="%prog <option> [glsa-list]", - version="%prog "+ __version__) -parser.epilog = "glsa-list can contain an arbitrary number of GLSA ids," \ +epilog = "glsa-list can contain an arbitrary number of GLSA ids," \ " filenames containing GLSAs or the special identifiers" \ " 'all', 'new' and 'affected'" +parser = ArgumentParser(usage=__program__ + " <option> [glsa-list]", + epilog=epilog) -modes = OptionGroup(parser, "Modes") -modes.add_option("-l", "--list", action="store_const", +modes = parser.add_argument_group("Modes") +modes.add_argument("-l", "--list", action="store_const", const="list", dest="mode", help="List all unapplied GLSA") -modes.add_option("-d", "--dump", action="store_const", +modes.add_argument("-d", "--dump", action="store_const", const="dump", dest="mode", help="Show all information about the given GLSA") -modes.add_option("", "--print", action="store_const", +modes.add_argument("--print", action="store_const", const="dump", dest="mode", help="Alias for --dump") -modes.add_option("-t", "--test", action="store_const", +modes.add_argument("-t", "--test", action="store_const", const="test", dest="mode", help="Test if this system is affected by the given GLSA") -modes.add_option("-p", "--pretend", action="store_const", +modes.add_argument("-p", "--pretend", action="store_const", const="pretend", dest="mode", help="Show the necessary commands to apply this GLSA") -modes.add_option("-f", "--fix", action="store_const", +modes.add_argument("-f", "--fix", action="store_const", const="fix", dest="mode", help="Try to auto-apply this GLSA (experimental)") -modes.add_option("-i", "--inject", action="store_const", dest="mode", - help="Inject the given GLSA into the checkfile") -modes.add_option("-m", "--mail", action="store_const", +modes.add_argument("-i", "--inject", action="store_const", + const="inject", dest="mode", + help="inject the given GLSA into the glsa_injected file") +modes.add_argument("-m", "--mail", action="store_const", const="mail", dest="mode", help="Send a mail with the given GLSAs to the administrator") -parser.add_option_group(modes) -parser.remove_option("--version") -parser.add_option("-V", "--version", action="callback", - callback=cb_version, help="Some information about this tool") -parser.add_option("-v", "--verbose", action="store_true", dest="verbose", +parser.add_argument("-V", "--version", action="store_true", + help="Some information about this tool") +parser.add_argument("-v", "--verbose", action="store_true", dest="verbose", help="Print more information") -parser.add_option("-n", "--nocolor", action="callback", - callback=lambda *args, **kwargs: nocolor(), +parser.add_argument("-n", "--nocolor", action="store_true", help="Disable colors") -parser.add_option("-e", "--emergelike", action="store_false", dest="least_change", +parser.add_argument("-e", "--emergelike", action="store_false", dest="least_change", help="Do not use a least-change algorithm") -parser.add_option("-c", "--cve", action="store_true", dest="list_cve", +parser.add_argument("-c", "--cve", action="store_true", dest="list_cve", help="Show CAN ids in listing mode") -options, params = parser.parse_args() +options, params = parser.parse_known_args() + +if options.nocolor: + nocolor() + +if options.version: + sys.stderr.write("\n"+ __program__ + ", version " + __version__ + "\n") + sys.stderr.write("Author: " + __author__ + "\n") + sys.stderr.write("This program is licensed under the GPL, version 2\n\n") + sys.exit(0) mode = options.mode least_change = options.least_change @@ -101,7 +99,8 @@ elif mode == "list" and not params: params.append("new") # delay this for speed increase -from portage.glsa import * +from portage.glsa import (Glsa, GlsaTypeException, GlsaFormatException, + get_applied_glsas, get_glsa_list) eroot = portage.settings['EROOT'] vardb = portage.db[eroot]["vartree"].dbapi @@ -117,7 +116,7 @@ glsalist = [] if "new" in params: glsalist = todolist params.remove("new") - + if "all" in params: glsalist = completelist params.remove("all") @@ -142,8 +141,17 @@ for p in params[:]: glsalist.extend([g for g in params if g not in glsalist]) -def summarylist(myglsalist, fd1=sys.stdout, fd2=sys.stderr): - fd2.write(white("[A]")+" means this GLSA was already applied,\n") +def summarylist(myglsalist, fd1=sys.stdout, fd2=sys.stderr, encoding="utf-8"): + # Get to the raw streams in py3k before wrapping them with an encoded writer + # to avoid writing bytes to a text stream (stdout/stderr are text streams + # by default in py3k) + if hasattr(fd1, "buffer"): + fd1 = fd1.buffer + if hasattr(fd2, "buffer"): + fd2 = fd2.buffer + fd1 = codecs.getwriter(encoding)(fd1) + fd2 = codecs.getwriter(encoding)(fd2) + fd2.write(white("[A]")+" means this GLSA was marked as applied (injected),\n") fd2.write(green("[U]")+" means the system is not affected and\n") fd2.write(red("[N]")+" indicates that the system might be affected.\n\n") @@ -155,7 +163,7 @@ def summarylist(myglsalist, fd1=sys.stdout, fd2=sys.stderr): if verbose: fd2.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e))) continue - if myglsa.isApplied(): + if myglsa.isInjected(): status = "[A]" color = white elif myglsa.isVulnerable(): @@ -186,7 +194,7 @@ def summarylist(myglsalist, fd1=sys.stdout, fd2=sys.stderr): fd1.write(")") if list_cve: fd1.write(" "+(",".join([r[:13] for r in myglsa.references if r[:4] in ["CAN-", "CVE-"]]))) - fd1.write("\n") + fd1.write("\n") return 0 if mode == "list": @@ -204,39 +212,46 @@ if mode in ["dump", "fix", "inject", "pretend"]: if mode == "dump": myglsa.dump() elif mode == "fix": - sys.stdout.write("fixing "+myid+"\n") - mergelist = myglsa.getMergeList(least_change=least_change) - for pkg in mergelist: - sys.stdout.write(">>> merging "+pkg+"\n") - # using emerge for the actual merging as it contains the dependency - # code and we want to be consistent in behaviour. Also this functionality - # will be integrated in emerge later, so it shouldn't hurt much. - emergecmd = "emerge --oneshot " + portage.settings["EMERGE_OPTS"] + " =" + pkg - if verbose: - sys.stderr.write(emergecmd+"\n") - exitcode = os.system(emergecmd) - # system() returns the exitcode in the high byte of a 16bit integer - if exitcode >= 1<<8: - exitcode >>= 8 - if exitcode: - sys.exit(exitcode) - myglsa.inject() + sys.stdout.write("Fixing GLSA "+myid+"\n") + if not myglsa.isVulnerable(): + sys.stdout.write(">>> no vulnerable packages installed\n") + else: + mergelist = myglsa.getMergeList(least_change=least_change) + if mergelist == []: + sys.stdout.write(">>> cannot fix GLSA, no unaffected packages available\n") + sys.exit(2) + for pkg in mergelist: + sys.stdout.write(">>> merging "+pkg+"\n") + # using emerge for the actual merging as it contains the dependency + # code and we want to be consistent in behaviour. Also this functionality + # will be integrated in emerge later, so it shouldn't hurt much. + emergecmd = "emerge --oneshot " + " =" + pkg + if verbose: + sys.stderr.write(emergecmd+"\n") + exitcode = os.system(emergecmd) + # system() returns the exitcode in the high byte of a 16bit integer + if exitcode >= 1<<8: + exitcode >>= 8 + if exitcode: + sys.exit(exitcode) + if len(mergelist): + sys.stdout.write("\n") elif mode == "pretend": sys.stdout.write("Checking GLSA "+myid+"\n") - mergelist = myglsa.getMergeList(least_change=least_change) - if mergelist: - sys.stdout.write("The following updates will be performed for this GLSA:\n") - for pkg in mergelist: - oldver = None - for x in vardb.match(portage.cpv_getkey(pkg)): - if vardb.aux_get(x, ["SLOT"]) == portdb.aux_get(pkg, ["SLOT"]): - oldver = x - if oldver == None: - raise ValueError("could not find old version for package %s" % pkg) - oldver = oldver[len(portage.cpv_getkey(oldver))+1:] - sys.stdout.write(" " + pkg + " (" + oldver + ")\n") + if not myglsa.isVulnerable(): + sys.stdout.write(">>> no vulnerable packages installed\n") else: - sys.stdout.write("Nothing to do for this GLSA\n") + mergedict = {} + for (vuln, update) in myglsa.getAffectionTable(least_change=least_change): + mergedict.setdefault(update, []).append(vuln) + + sys.stdout.write(">>> The following updates will be performed for this GLSA:\n") + for pkg in mergedict: + if pkg != "": + sys.stdout.write(" " + pkg + " (vulnerable: " + ", ".join(mergedict[pkg]) + ")\n") + if "" in mergedict: + sys.stdout.write("\n>>> For the following packages, no upgrade path exists:\n") + sys.stdout.write(" " + ", ".join(mergedict[""])) elif mode == "inject": sys.stdout.write("injecting " + myid + "\n") myglsa.inject() @@ -268,9 +283,9 @@ if mode == "test": # mail mode as requested by solar if mode == "mail": import portage.mail, socket - from io import StringIO + from io import BytesIO from email.mime.text import MIMEText - + # color doesn't make any sense for mail nocolor() @@ -278,7 +293,7 @@ if mode == "mail": myrecipient = portage.settings["PORTAGE_ELOG_MAILURI"].split()[0] else: myrecipient = "root@localhost" - + if "PORTAGE_ELOG_MAILFROM" in portage.settings: myfrom = portage.settings["PORTAGE_ELOG_MAILFROM"] else: @@ -287,11 +302,13 @@ if mode == "mail": mysubject = "[glsa-check] Summary for %s" % socket.getfqdn() # need a file object for summarylist() - myfd = StringIO() - myfd.write("GLSA Summary report for host %s\n" % socket.getfqdn()) - myfd.write("(Command was: %s)\n\n" % " ".join(sys.argv)) + myfd = BytesIO() + line = "GLSA Summary report for host %s\n" % socket.getfqdn() + myfd.write(line.encode("utf-8")) + line = "(Command was: %s)\n\n" % " ".join(sys.argv) + myfd.write(line.encode("utf-8")) summarylist(glsalist, fd1=myfd, fd2=myfd) - summary = str(myfd.getvalue()) + summary = myfd.getvalue().decode("utf-8") myfd.close() myattachments = [] @@ -302,16 +319,17 @@ if mode == "mail": if verbose: sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e))) continue - myfd = StringIO() + myfd = BytesIO() myglsa.dump(outstream=myfd) - myattachments.append(MIMEText(str(myfd.getvalue()), _charset="utf8")) + attachment = myfd.getvalue().decode("utf-8") + myattachments.append(MIMEText(attachment, _charset="utf8")) myfd.close() - + mymessage = portage.mail.create_message(myfrom, myrecipient, mysubject, summary, myattachments) portage.mail.send_mail(portage.settings, mymessage) - + sys.exit(0) - + # something wrong here, all valid paths are covered with sys.exit() sys.stderr.write("nothing more to do\n") sys.exit(2) diff --git a/bin/helper-functions.sh b/bin/helper-functions.sh index c7400fa4b..b9bc74a2e 100644 --- a/bin/helper-functions.sh +++ b/bin/helper-functions.sh @@ -10,42 +10,45 @@ source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh # # API functions for doing parallel processing # -numjobs() { +makeopts_jobs() { # Copied from eutils.eclass:makeopts_jobs() local jobs=$(echo " ${MAKEOPTS} " | \ sed -r -n 's:.*[[:space:]](-j|--jobs[=[:space:]])[[:space:]]*([0-9]+).*:\2:p') echo ${jobs:-1} } -multijob_init() { +__multijob_init() { # Setup a pipe for children to write their pids to when they finish. - mj_control_pipe=$(mktemp -t multijob.XXXXXX) - rm "${mj_control_pipe}" - mkfifo "${mj_control_pipe}" - redirect_alloc_fd mj_control_fd "${mj_control_pipe}" - rm -f "${mj_control_pipe}" + # We have to allocate two fd's because POSIX has undefined behavior + # when you open a FIFO for simultaneous read/write. #487056 + local pipe=$(mktemp -t multijob.XXXXXX) + rm -f "${pipe}" + mkfifo -m 600 "${pipe}" + __redirect_alloc_fd mj_write_fd "${pipe}" + __redirect_alloc_fd mj_read_fd "${pipe}" + rm -f "${pipe}" # See how many children we can fork based on the user's settings. - mj_max_jobs=$(numjobs) + mj_max_jobs=$(makeopts_jobs "$@") mj_num_jobs=0 } -multijob_child_init() { - trap 'echo ${BASHPID} $? >&'${mj_control_fd} EXIT +__multijob_child_init() { + trap 'echo ${BASHPID:-$(__bashpid)} $? >&'${mj_write_fd} EXIT trap 'exit 1' INT TERM } -multijob_finish_one() { +__multijob_finish_one() { local pid ret - read -r -u ${mj_control_fd} pid ret + read -r -u ${mj_read_fd} pid ret : $(( --mj_num_jobs )) return ${ret} } -multijob_finish() { +__multijob_finish() { local ret=0 while [[ ${mj_num_jobs} -gt 0 ]] ; do - multijob_finish_one + __multijob_finish_one : $(( ret |= $? )) done # Let bash clean up its internal child tracking state. @@ -53,38 +56,42 @@ multijob_finish() { return ${ret} } -multijob_post_fork() { +__multijob_post_fork() { : $(( ++mj_num_jobs )) if [[ ${mj_num_jobs} -ge ${mj_max_jobs} ]] ; then - multijob_finish_one + __multijob_finish_one fi return $? } -# @FUNCTION: redirect_alloc_fd +# @FUNCTION: __redirect_alloc_fd # @USAGE: <var> <file> [redirection] # @DESCRIPTION: # Find a free fd and redirect the specified file via it. Store the new # fd in the specified variable. Useful for the cases where we don't care # about the exact fd #. -redirect_alloc_fd() { +__redirect_alloc_fd() { local var=$1 file=$2 redir=${3:-"<>"} if [[ $(( (BASH_VERSINFO[0] << 8) + BASH_VERSINFO[1] )) -ge $(( (4 << 8) + 1 )) ]] ; then - # Newer bash provides this functionality. - eval "exec {${var}}${redir}'${file}'" + # Newer bash provides this functionality. + eval "exec {${var}}${redir}'${file}'" else - # Need to provide the functionality ourselves. - local fd=10 - while :; do - # Make sure the fd isn't open. It could be a char device, - # or a symlink (possibly broken) to something else. - if [[ ! -e /dev/fd/${fd} ]] && [[ ! -L /dev/fd/${fd} ]] ; then - eval "exec ${fd}${redir}'${file}'" && break - fi - [[ ${fd} -gt 1024 ]] && die "redirect_alloc_fd failed" - : $(( ++fd )) - done - : $(( ${var} = fd )) + # Need to provide the functionality ourselves. + local fd=10 + local fddir=/dev/fd + # Prefer /proc/self/fd if available (/dev/fd + # doesn't work on solaris, see bug #474536). + [[ -d /proc/self/fd ]] && fddir=/proc/self/fd + while :; do + # Make sure the fd isn't open. It could be a char device, + # or a symlink (possibly broken) to something else. + if [[ ! -e ${fddir}/${fd} ]] && [[ ! -L ${fddir}/${fd} ]] ; then + eval "exec ${fd}${redir}'${file}'" && break + fi + [[ ${fd} -gt 1024 ]] && die 'could not locate a free temp fd !?' + : $(( ++fd )) + done + : $(( ${var} = fd )) fi } diff --git a/bin/install.py b/bin/install.py new file mode 100755 index 000000000..3c5e0de65 --- /dev/null +++ b/bin/install.py @@ -0,0 +1,253 @@ +#!/usr/bin/python -b +# Copyright 2013-2014 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +import os +import stat +import sys +import subprocess +import traceback + +import portage +from portage.util._argparse import ArgumentParser +from portage.util.movefile import _copyxattr +from portage.exception import OperationNotSupported + +# Change back to original cwd _after_ all imports (bug #469338). +os.chdir(os.environ["__PORTAGE_HELPER_CWD"]) + +def parse_args(args): + """ + Parse the command line arguments using optparse for python 2.6 compatibility + Args: + args: a list of the white space delimited command line + Returns: + tuple of the Namespace of parsed options, and a list of order parameters + """ + parser = ArgumentParser(add_help=False) + + parser.add_argument( + "-b", + action="store_true", + dest="shortopt_b" + ) + parser.add_argument( + "--backup", + action="store", + dest="backup" + ) + parser.add_argument( + "-c", + action="store_true", + dest="shortopt_c" + ) + parser.add_argument( + "--compare", + "-C", + action="store_true", + dest="compare" + ) + parser.add_argument( + "--directory", + "-d", + action="store_true", + dest="directory" + ) + parser.add_argument( + "-D", + action="store_true", + dest="shortopt_D" + ) + parser.add_argument( + "--owner", + "-o", + action="store", + dest="owner" + ) + parser.add_argument( + "--group", + "-g", + action="store", + dest="group" + ) + parser.add_argument( + "--mode", + "-m", + action="store", + dest="mode" + ) + parser.add_argument( + "--preserve-timestamps", + "-p", + action="store_true", + dest="preserve_timestamps" + ) + parser.add_argument( + "--strip", + "-s", + action="store_true", + dest="strip" + ) + parser.add_argument( + "--strip-program", + action="store", + dest="strip_program" + ) + parser.add_argument( + "--suffix", + "-S", + action="store", + dest="suffix" + ) + parser.add_argument( + "--target-directory", + "-t", + action="store", + dest="target_directory" + ) + parser.add_argument( + "--no-target-directory", + "-T", + action="store_true", + dest="no_target_directory" + ) + parser.add_argument( + "--context", + "-Z", + action="store", + dest="context" + ) + parser.add_argument( + "--verbose", + "-v", + action="store_true", + dest="verbose" + ) + parser.add_argument( + "--help", + action="store_true", + dest="help" + ) + parser.add_argument( + "--version", + action="store_true", + dest="version" + ) + + # Use parse_known_args for maximum compatibility with + # getopt handling of non-option file arguments. Note + # that parser.add_argument("files", nargs='+') would + # be subtly incompatible because it requires that all + # of the file arguments be grouped sequentially. Also + # note that we have to explicitly call add_argument + # for known options in order for argparse to correctly + # separate option arguments from file arguments in all + # cases (it also allows for optparse compatibility). + parsed_args = parser.parse_known_args() + + opts = parsed_args[0] + files = parsed_args[1] + files = [f for f in files if f != "--"] # filter out "--" + + return (opts, files) + + +def copy_xattrs(opts, files): + """ + Copy the extended attributes using portage.util.movefile._copyxattr + Args: + opts: Namespace of the parsed command line otions + files: list of ordered command line parameters which should be files/directories + Returns: + system exit code + """ + if opts.directory or not files: + return os.EX_OK + + if opts.target_directory is None: + source, target = files[:-1], files[-1] + target_is_directory = os.path.isdir(target) + else: + source, target = files, opts.target_directory + target_is_directory = True + + exclude = os.environ.get("PORTAGE_XATTR_EXCLUDE", "security.* system.nfs4_acl") + + try: + if target_is_directory: + for s in source: + abs_path = os.path.join(target, os.path.basename(s)) + _copyxattr(s, abs_path, exclude=exclude) + else: + _copyxattr(source[0], target, exclude=exclude) + return os.EX_OK + + except OperationNotSupported: + traceback.print_exc() + return os.EX_OSERR + + +def Which(filename, path=None, exclude=None): + """ + Find the absolute path of 'filename' in a given search 'path' + Args: + filename: basename of the file + path: colon delimited search path + exclude: path of file to exclude + """ + if path is None: + path = os.environ.get('PATH', '') + + if exclude is not None: + st = os.stat(exclude) + exclude = (st.st_ino, st.st_dev) + + for p in path.split(':'): + p = os.path.join(p, filename) + if os.access(p, os.X_OK): + try: + st = os.stat(p) + except OSError: + # file disappeared? + pass + else: + if stat.S_ISREG(st.st_mode) and \ + (exclude is None or exclude != (st.st_ino, st.st_dev)): + return p + + return None + + +def main(args): + opts, files = parse_args(args) + install_binary = Which('install', exclude=os.environ["__PORTAGE_HELPER_PATH"]) + if install_binary is None: + sys.stderr.write("install: command not found\n") + return 127 + + cmdline = [install_binary] + cmdline += args + + if sys.hexversion >= 0x3000000: + # We can't trust that the filesystem encoding (locale dependent) + # correctly matches the arguments, so use surrogateescape to + # pass through the original argv bytes for Python 3. + fs_encoding = sys.getfilesystemencoding() + cmdline = [x.encode(fs_encoding, 'surrogateescape') for x in cmdline] + files = [x.encode(fs_encoding, 'surrogateescape') for x in files] + if opts.target_directory is not None: + opts.target_directory = \ + opts.target_directory.encode(fs_encoding, 'surrogateescape') + + returncode = subprocess.call(cmdline) + if returncode == os.EX_OK: + returncode = copy_xattrs(opts, files) + if returncode != os.EX_OK: + portage.util.writemsg("!!! install: copy_xattrs failed with the " + "following arguments: %s\n" % + " ".join(portage._shell_quote(x) for x in args), noiselevel=-1) + return returncode + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/bin/isolated-functions.sh b/bin/isolated-functions.sh index dbf988b28..a22af574a 100644 --- a/bin/isolated-functions.sh +++ b/bin/isolated-functions.sh @@ -1,7 +1,9 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 +source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}/eapi.sh" + # We need this next line for "die" and "assert". It expands # It _must_ preceed all the calls to die and assert. shopt -s expand_aliases @@ -15,7 +17,7 @@ assert() { done } -assert_sigpipe_ok() { +__assert_sigpipe_ok() { # When extracting a tar file like this: # # bzip2 -dc foo.tar.bz2 | tar xof - @@ -43,21 +45,21 @@ assert_sigpipe_ok() { shopt -s extdebug -# dump_trace([number of funcs on stack to skip], +# __dump_trace([number of funcs on stack to skip], # [whitespacing for filenames], # [whitespacing for line numbers]) -dump_trace() { +__dump_trace() { local funcname="" sourcefile="" lineno="" s="yes" n p declare -i strip=${1:-1} local filespacing=$2 linespacing=$3 - # The qa_call() function and anything before it are portage internals + # The __qa_call() function and anything before it are portage internals # that the user will not be interested in. Therefore, the stack trace - # should only show calls that come after qa_call(). + # should only show calls that come after __qa_call(). (( n = ${#FUNCNAME[@]} - 1 )) (( p = ${#BASH_ARGV[@]} )) while (( n > 0 )) ; do - [ "${FUNCNAME[${n}]}" == "qa_call" ] && break + [ "${FUNCNAME[${n}]}" == "__qa_call" ] && break (( p -= ${BASH_ARGC[${n}]} )) (( n-- )) done @@ -86,7 +88,7 @@ dump_trace() { } nonfatal() { - if has "${EAPI:-0}" 0 1 2 3 3_pre2 ; then + if ! ___eapi_has_nonfatal; then die "$FUNCNAME() not supported in this EAPI" fi if [[ $# -lt 1 ]]; then @@ -96,18 +98,24 @@ nonfatal() { PORTAGE_NONFATAL=1 "$@" } -helpers_die() { - case "${EAPI:-0}" in - 0|1|2|3) - echo -e "$@" >&2 - ;; - *) - die "$@" - ;; - esac +__bashpid() { + # The BASHPID variable is new to bash-4.0, so add a hack for older + # versions. This must be used like so: + # ${BASHPID:-$(__bashpid)} + sh -c 'echo ${PPID}' +} + +__helpers_die() { + if ___eapi_helpers_can_die; then + die "$@" + else + echo -e "$@" >&2 + fi } die() { + local IFS=$' \t\n' + if [[ $PORTAGE_NONFATAL -eq 1 ]]; then echo -e " $WARN*$NORMAL ${FUNCNAME[1]}: WARNING: $@" >&2 return 1 @@ -124,7 +132,7 @@ die() { # setup spacing to make output easier to read (( n = ${#FUNCNAME[@]} - 1 )) while (( n > 0 )) ; do - [ "${FUNCNAME[${n}]}" == "qa_call" ] && break + [ "${FUNCNAME[${n}]}" == "__qa_call" ] && break (( n-- )) done (( n == 0 )) && (( n = ${#FUNCNAME[@]} - 1 )) @@ -140,14 +148,14 @@ die() { # get a stack trace, so at least report the phase that failed. local phase_str= [[ -n $EBUILD_PHASE ]] && phase_str=" ($EBUILD_PHASE phase)" - eerror "ERROR: $CATEGORY/$PF failed${phase_str}:" + eerror "ERROR: ${CATEGORY}/${PF}::${PORTAGE_REPO_NAME} failed${phase_str}:" eerror " ${*:-(no error message)}" eerror - # dump_trace is useless when the main script is a helper binary + # __dump_trace is useless when the main script is a helper binary local main_index (( main_index = ${#BASH_SOURCE[@]} - 1 )) if has ${BASH_SOURCE[$main_index]##*/} ebuild.sh misc-functions.sh ; then - dump_trace 2 ${filespacing} ${linespacing} + __dump_trace 2 ${filespacing} ${linespacing} eerror " $(printf "%${filespacing}s" "${BASH_SOURCE[1]##*/}"), line $(printf "%${linespacing}s" "${BASH_LINENO[0]}"): Called die" eerror "The specific snippet of code:" # This scans the file that called die and prints out the logic that @@ -173,39 +181,12 @@ die() { | while read -r n ; do eerror " ${n#RETAIN-LEADING-SPACE}" ; done eerror fi - eerror "If you need support, post the output of \`emerge --info '=$CATEGORY/$PF'\`," - eerror "the complete build log and the output of \`emerge -pqv '=$CATEGORY/$PF'\`." - if [[ -n ${EBUILD_OVERLAY_ECLASSES} ]] ; then - eerror "This ebuild used the following eclasses from overlays:" - local x - for x in ${EBUILD_OVERLAY_ECLASSES} ; do - eerror " ${x}" - done - fi - if [ "${EMERGE_FROM}" != "binary" ] && \ - ! has ${EBUILD_PHASE} prerm postrm && \ - [ "${EBUILD#${PORTDIR}/}" == "${EBUILD}" ] ; then - local overlay=${EBUILD%/*} - overlay=${overlay%/*} - overlay=${overlay%/*} - if [[ -n $PORTAGE_REPO_NAME ]] ; then - eerror "This ebuild is from an overlay named" \ - "'$PORTAGE_REPO_NAME': '${overlay}/'" - else - eerror "This ebuild is from an overlay: '${overlay}/'" - fi - elif [[ -n $PORTAGE_REPO_NAME && -f "$PORTDIR"/profiles/repo_name ]] ; then - local portdir_repo_name=$(<"$PORTDIR"/profiles/repo_name) - if [[ -n $portdir_repo_name && \ - $portdir_repo_name != $PORTAGE_REPO_NAME ]] ; then - eerror "This ebuild is from a repository" \ - "named '$PORTAGE_REPO_NAME'" - fi - fi + eerror "If you need support, post the output of \`emerge --info '=${CATEGORY}/${PF}::${PORTAGE_REPO_NAME}'\`," + eerror "the complete build log and the output of \`emerge -pqv '=${CATEGORY}/${PF}::${PORTAGE_REPO_NAME}'\`." # Only call die hooks here if we are executed via ebuild.sh or # misc-functions.sh, since those are the only cases where the environment - # contains the hook functions. When necessary (like for helpers_die), die + # contains the hook functions. When necessary (like for __helpers_die), die # hooks are automatically called later by a misc-functions.sh invocation. if has ${BASH_SOURCE[$main_index]##*/} ebuild.sh misc-functions.sh && \ [[ ${EBUILD_PHASE} != depend ]] ; then @@ -218,7 +199,8 @@ die() { if [[ -n ${PORTAGE_LOG_FILE} ]] ; then eerror "The complete build log is located at '${PORTAGE_LOG_FILE}'." - if [[ ${PORTAGE_LOG_FILE} != ${T}/* ]] ; then + if [[ ${PORTAGE_LOG_FILE} != ${T}/* ]] && \ + ! has fail-clean ${FEATURES} ; then # Display path to symlink in ${T}, as requested in bug #412865. local log_ext=log [[ ${PORTAGE_LOG_FILE} != *.log ]] && log_ext+=.${PORTAGE_LOG_FILE##*.} @@ -241,26 +223,20 @@ die() { [[ -n $PORTAGE_IPC_DAEMON ]] && "$PORTAGE_BIN_PATH"/ebuild-ipc exit 1 # subshell die support - [[ $BASHPID = $EBUILD_MASTER_PID ]] || kill -s SIGTERM $EBUILD_MASTER_PID + [[ ${BASHPID:-$(__bashpid)} == ${EBUILD_MASTER_PID} ]] || kill -s SIGTERM ${EBUILD_MASTER_PID} exit 1 } -# We need to implement diefunc() since environment.bz2 files contain -# calls to it (due to alias expansion). -diefunc() { - die "${@}" -} - -quiet_mode() { +__quiet_mode() { [[ ${PORTAGE_QUIET} -eq 1 ]] } -vecho() { - quiet_mode || echo "$@" +__vecho() { + __quiet_mode || echo "$@" } # Internal logging function, don't use this in ebuilds -elog_base() { +__elog_base() { local messagetype [ -z "${1}" -o -z "${T}" -o ! -d "${T}/logging" ] && return 1 case "${1}" in @@ -269,7 +245,7 @@ elog_base() { shift ;; *) - vecho -e " ${BAD}*${NORMAL} Invalid use of internal function elog_base(), next message will not be logged" + __vecho -e " ${BAD}*${NORMAL} Invalid use of internal function __elog_base(), next message will not be logged" return 1 ;; esac @@ -281,17 +257,17 @@ elog_base() { } eqawarn() { - elog_base QA "$*" + __elog_base QA "$*" [[ ${RC_ENDCOL} != "yes" && ${LAST_E_CMD} == "ebegin" ]] && echo echo -e "$@" | while read -r ; do - vecho " $WARN*$NORMAL $REPLY" >&2 + __vecho " $WARN*$NORMAL $REPLY" >&2 done LAST_E_CMD="eqawarn" return 0 } elog() { - elog_base LOG "$*" + __elog_base LOG "$*" [[ ${RC_ENDCOL} != "yes" && ${LAST_E_CMD} == "ebegin" ]] && echo echo -e "$@" | while read -r ; do echo " $GOOD*$NORMAL $REPLY" @@ -300,26 +276,8 @@ elog() { return 0 } -esyslog() { - local pri= - local tag= - - if [ -x /usr/bin/logger ] - then - pri="$1" - tag="$2" - - shift 2 - [ -z "$*" ] && return 0 - - /usr/bin/logger -p "${pri}" -t "${tag}" -- "$*" - fi - - return 0 -} - einfo() { - elog_base INFO "$*" + __elog_base INFO "$*" [[ ${RC_ENDCOL} != "yes" && ${LAST_E_CMD} == "ebegin" ]] && echo echo -e "$@" | while read -r ; do echo " $GOOD*$NORMAL $REPLY" @@ -329,7 +287,7 @@ einfo() { } einfon() { - elog_base INFO "$*" + __elog_base INFO "$*" [[ ${RC_ENDCOL} != "yes" && ${LAST_E_CMD} == "ebegin" ]] && echo echo -ne " ${GOOD}*${NORMAL} $*" LAST_E_CMD="einfon" @@ -337,7 +295,7 @@ einfon() { } ewarn() { - elog_base WARN "$*" + __elog_base WARN "$*" [[ ${RC_ENDCOL} != "yes" && ${LAST_E_CMD} == "ebegin" ]] && echo echo -e "$@" | while read -r ; do echo " $WARN*$NORMAL $RC_INDENTATION$REPLY" >&2 @@ -347,7 +305,7 @@ ewarn() { } eerror() { - elog_base ERROR "$*" + __elog_base ERROR "$*" [[ ${RC_ENDCOL} != "yes" && ${LAST_E_CMD} == "ebegin" ]] && echo echo -e "$@" | while read -r ; do echo " $BAD*$NORMAL $RC_INDENTATION$REPLY" >&2 @@ -372,7 +330,7 @@ ebegin() { return 0 } -_eend() { +__eend() { local retval=${1:-0} efunc=${2:-eerror} msg shift 2 @@ -399,13 +357,13 @@ eend() { local retval=${1:-0} shift - _eend ${retval} eerror "$*" + __eend ${retval} eerror "$*" LAST_E_CMD="eend" return ${retval} } -unset_colors() { +__unset_colors() { COLS=80 ENDCOL= @@ -417,7 +375,7 @@ unset_colors() { BRACKET= } -set_colors() { +__set_colors() { COLS=${COLUMNS:-0} # bash's internal COLUMNS variable # Avoid wasteful stty calls during the "depend" phases. # If stdout is a pipe, the parent process can export COLUMNS @@ -450,10 +408,10 @@ RC_DOT_PATTERN='' case "${NOCOLOR:-false}" in yes|true) - unset_colors + __unset_colors ;; no|false) - set_colors + __set_colors ;; esac @@ -504,4 +462,24 @@ has() { return 1 } +__repo_attr() { + local appropriate_section=0 exit_status=1 line saved_extglob_shopt=$(shopt -p extglob) + shopt -s extglob + while read line; do + [[ ${appropriate_section} == 0 && ${line} == "[$1]" ]] && appropriate_section=1 && continue + [[ ${appropriate_section} == 1 && ${line} == "["*"]" ]] && appropriate_section=0 && continue + # If a conditional expression like [[ ${line} == $2*( )=* ]] is used + # then bash-3.2 produces an error like the following when the file is + # sourced: syntax error in conditional expression: unexpected token `(' + # Therefore, use a regular expression for compatibility. + if [[ ${appropriate_section} == 1 && ${line} =~ ^${2}[[:space:]]*= ]]; then + echo "${line##$2*( )=*( )}" + exit_status=0 + break + fi + done <<< "${PORTAGE_REPOSITORIES}" + eval "${saved_extglob_shopt}" + return ${exit_status} +} + true diff --git a/bin/lock-helper.py b/bin/lock-helper.py index dfb887669..aa2dd60fa 100755 --- a/bin/lock-helper.py +++ b/bin/lock-helper.py @@ -1,11 +1,12 @@ -#!/usr/bin/python -# Copyright 2010-2011 Gentoo Foundation +#!/usr/bin/python -b +# Copyright 2010-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 import os import sys sys.path.insert(0, os.environ['PORTAGE_PYM_PATH']) import portage +portage._internal_caller = True portage._disable_legacy_globals() def main(args): diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh index 9eec8bb69..5ccf7c224 100755 --- a/bin/misc-functions.sh +++ b/bin/misc-functions.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # # Miscellaneous shell functions that make use of the ebuild env but don't need @@ -17,8 +17,9 @@ shift $# source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}/ebuild.sh" install_symlink_html_docs() { - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) local ED=${D} ;; esac + if ! ___eapi_has_prefix_variables; then + local ED=${D} + fi cd "${ED}" || die "cd failed" #symlink the html documentation (if DOC_SYMLINKS_DIR is set in make.conf) if [ -n "${DOC_SYMLINKS_DIR}" ] ; then @@ -30,10 +31,10 @@ install_symlink_html_docs() { done if [ -n "${mydocdir}" ] ; then local mysympath - if [ -z "${SLOT}" -o "${SLOT}" = "0" ] ; then + if [ -z "${SLOT}" -o "${SLOT%/*}" = "0" ] ; then mysympath="${DOC_SYMLINKS_DIR}/${CATEGORY}/${PN}" else - mysympath="${DOC_SYMLINKS_DIR}/${CATEGORY}/${PN}-${SLOT}" + mysympath="${DOC_SYMLINKS_DIR}/${CATEGORY}/${PN}-${SLOT%/*}" fi einfo "Symlinking ${mysympath} to the HTML documentation" dodir "${DOC_SYMLINKS_DIR}/${CATEGORY}" @@ -43,7 +44,20 @@ install_symlink_html_docs() { } # replacement for "readlink -f" or "realpath" +READLINK_F_WORKS="" canonicalize() { + if [[ -z ${READLINK_F_WORKS} ]] ; then + if [[ $(readlink -f -- /../ 2>/dev/null) == "/" ]] ; then + READLINK_F_WORKS=true + else + READLINK_F_WORKS=false + fi + fi + if ${READLINK_F_WORKS} ; then + readlink -f -- "$@" + return + fi + local f=$1 b n=10 wd=$(pwd) while (( n-- > 0 )); do while [[ ${f: -1} = / && ${#f} -gt 1 ]]; do @@ -66,8 +80,9 @@ canonicalize() { prepcompress() { local -a include exclude incl_d incl_f local f g i real_f real_d - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) local ED=${D} ;; esac + if ! ___eapi_has_prefix_variables; then + local ED=${D} + fi # Canonicalize path names and check for their existence. real_d=$(canonicalize "${ED}") @@ -141,7 +156,7 @@ prepcompress() { # Queue up for compression. # ecompress{,dir} doesn't like to be called with empty argument lists. - [[ ${#incl_d[@]} -gt 0 ]] && ecompressdir --queue "${incl_d[@]}" + [[ ${#incl_d[@]} -gt 0 ]] && ecompressdir --limit ${PORTAGE_DOCOMPRESS_SIZE_LIMIT:-0} --queue "${incl_d[@]}" [[ ${#incl_f[@]} -gt 0 ]] && ecompress --queue "${incl_f[@]/#/${ED}}" [[ ${#exclude[@]} -gt 0 ]] && ecompressdir --ignore "${exclude[@]}" return 0 @@ -149,13 +164,12 @@ prepcompress() { install_qa_check() { local f i qa_var x - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) local EPREFIX= ED=${D} ;; esac + if ! ___eapi_has_prefix_variables; then + local EPREFIX= ED=${D} + fi cd "${ED}" || die "cd failed" - # Merge QA_FLAGS_IGNORED and QA_DT_HASH into a single array, since - # QA_DT_HASH is deprecated. qa_var="QA_FLAGS_IGNORED_${ARCH/-/_}" eval "[[ -n \${!qa_var} ]] && QA_FLAGS_IGNORED=(\"\${${qa_var}[@]}\")" if [[ ${#QA_FLAGS_IGNORED[@]} -eq 1 ]] ; then @@ -166,29 +180,6 @@ install_qa_check() { set -${shopts} fi - qa_var="QA_DT_HASH_${ARCH/-/_}" - eval "[[ -n \${!qa_var} ]] && QA_DT_HASH=(\"\${${qa_var}[@]}\")" - if [[ ${#QA_DT_HASH[@]} -eq 1 ]] ; then - local shopts=$- - set -o noglob - QA_DT_HASH=(${QA_DT_HASH}) - set +o noglob - set -${shopts} - fi - - if [[ -n ${QA_DT_HASH} ]] ; then - QA_FLAGS_IGNORED=("${QA_FLAGS_IGNORED[@]}" "${QA_DT_HASH[@]}") - unset QA_DT_HASH - fi - - # Merge QA_STRICT_FLAGS_IGNORED and QA_STRICT_DT_HASH, since - # QA_STRICT_DT_HASH is deprecated - if [ "${QA_STRICT_FLAGS_IGNORED-unset}" = unset ] && \ - [ "${QA_STRICT_DT_HASH-unset}" != unset ] ; then - QA_STRICT_FLAGS_IGNORED=1 - unset QA_STRICT_DT_HASH - fi - # Check for files built without respecting *FLAGS. Note that # -frecord-gcc-switches must be in all *FLAGS variables, in # order to avoid false positive results here. @@ -200,8 +191,7 @@ install_qa_check() { [[ "${FFLAGS}" == *-frecord-gcc-switches* ]] && \ [[ "${FCFLAGS}" == *-frecord-gcc-switches* ]] ; then rm -f "${T}"/scanelf-ignored-CFLAGS.log - for x in $(scanelf -qyRF '%k %p' -k \!.GCC.command.line "${ED}" | \ - sed -e "s:\!.GCC.command.line ::") ; do + for x in $(scanelf -qyRF '#k%p' -k '!.GCC.command.line' "${ED}") ; do # Separate out file types that are known to support # .GCC.command.line sections, using the `file` command # similar to how prepstrip uses it. @@ -226,11 +216,11 @@ install_qa_check() { -i "${T}"/scanelf-ignored-CFLAGS.log f=$(<"${T}"/scanelf-ignored-CFLAGS.log) if [[ -n ${f} ]] ; then - vecho -ne '\n' + __vecho -ne '\n' eqawarn "${BAD}QA Notice: Files built without respecting CFLAGS have been detected${NORMAL}" eqawarn " Please include the following list of files in your report:" eqawarn "${f}" - vecho -ne '\n' + __vecho -ne '\n' sleep 1 else rm -f "${T}"/scanelf-ignored-CFLAGS.log @@ -240,7 +230,7 @@ install_qa_check() { export STRIP_MASK prepall - has "${EAPI}" 0 1 2 3 || prepcompress + ___eapi_has_docompress && prepcompress ecompressdir --dequeue ecompress --dequeue @@ -251,32 +241,50 @@ install_qa_check() { for x in etc/app-defaults usr/man usr/info usr/X11R6 usr/doc usr/locale ; do [[ -d ${ED}/$x ]] && f+=" $x\n" done - if [[ -n $f ]] ; then eqawarn "QA Notice: This ebuild installs into the following deprecated directories:" eqawarn eqawarn "$f" fi - if [[ -d ${ED}/etc/udev/rules.d ]] ; then - f= - for x in $(ls "${ED}/etc/udev/rules.d") ; do - f+=" etc/udev/rules.d/$x\n" - done - if [[ -n $f ]] ; then - eqawarn "QA Notice: udev rules should be installed in /lib/udev/rules.d:" - eqawarn - eqawarn "$f" + # It's ok create these directories, but not to install into them. #493154 + # TODO: We should add var/lib to this list. + f= + for x in var/cache var/lock var/run run ; do + if [[ ! -L ${ED}/${x} && -d ${ED}/${x} ]] ; then + if [[ -z $(find "${ED}/${x}" -prune -empty) ]] ; then + f+=$(cd "${ED}"; find "${x}" -printf ' %p\n') + fi fi + done + if [[ -n ${f} ]] ; then + eqawarn "QA Notice: This ebuild installs into paths that should be created at runtime." + eqawarn " To fix, simply do not install into these directories. Instead, your package" + eqawarn " should create dirs on the fly at runtime as needed via init scripts/etc..." + eqawarn + eqawarn "${f}" + fi + + set +f + f= + for x in "${ED}etc/udev/rules.d/"* "${ED}lib"*"/udev/rules.d/"* ; do + [[ -e ${x} ]] || continue + [[ ${x} == ${ED}lib/udev/rules.d/* ]] && continue + f+=" ${x#${ED}}\n" + done + if [[ -n $f ]] ; then + eqawarn "QA Notice: udev rules should be installed in /lib/udev/rules.d:" + eqawarn + eqawarn "$f" fi # Now we look for all world writable files. local unsafe_files=$(find "${ED}" -type f -perm -2 | sed -e "s:^${ED}:- :") if [[ -n ${unsafe_files} ]] ; then - vecho "QA Security Notice: world writable file(s):" - vecho "${unsafe_files}" - vecho "- This may or may not be a security problem, most of the time it is one." - vecho "- Please double check that $PF really needs a world writeable bit and file bugs accordingly." + __vecho "QA Security Notice: world writable file(s):" + __vecho "${unsafe_files}" + __vecho "- This may or may not be a security problem, most of the time it is one." + __vecho "- Please double check that $PF really needs a world writeable bit and file bugs accordingly." sleep 1 fi @@ -307,7 +315,7 @@ install_qa_check() { for l in $(echo "${rpath_files}" | grep -E ":${dir}|::|: "); do f+=" ${l%%:*}\n" if ! has stricter ${FEATURES}; then - vecho "Auto fixing rpaths for ${l%%:*}" + __vecho "Auto fixing rpaths for ${l%%:*}" TMPDIR="${dir}" scanelf -BXr "${l%%:*}" -o /dev/null fi done @@ -321,12 +329,12 @@ install_qa_check() { # Print QA notice. if [[ -n ${f}${x} ]] ; then - vecho -ne '\n' + __vecho -ne '\n' eqawarn "QA Notice: The following files contain insecure RUNPATHs" eqawarn " Please file a bug about this at http://bugs.gentoo.org/" eqawarn " with the maintaining herd of the package." eqawarn "${f}${f:+${x:+\n}}${x}" - vecho -ne '\n' + __vecho -ne '\n' if [[ -n ${x} ]] || has stricter ${FEATURES} ; then insecure_rpath=1 fi @@ -344,7 +352,7 @@ install_qa_check() { f=$(scanelf -qyRF '%t %p' "${ED}" | grep -v 'usr/lib/debug/') if [[ -n ${f} ]] ; then scanelf -qyRAF '%T %p' "${PORTAGE_BUILDDIR}"/ &> "${T}"/scanelf-textrel.log - vecho -ne '\n' + __vecho -ne '\n' eqawarn "QA Notice: The following files contain runtime text relocations" eqawarn " Text relocations force the dynamic linker to perform extra" eqawarn " work at startup, waste system resources, and may pose a security" @@ -353,7 +361,7 @@ install_qa_check() { eqawarn " For more information, see http://hardened.gentoo.org/pic-fix-guide.xml" eqawarn " Please include the following list of files in your report:" eqawarn "${f}" - vecho -ne '\n' + __vecho -ne '\n' die_msg="${die_msg} textrels," sleep 1 fi @@ -364,7 +372,7 @@ install_qa_check() { *-linux-gnu*) # Check for files with executable stacks, but only on arches which # are supported at the moment. Keep this list in sync with - # http://hardened.gentoo.org/gnu-stack.xml (Arch Status) + # http://www.gentoo.org/proj/en/hardened/gnu-stack.xml (Arch Status) case ${CTARGET:-${CHOST}} in arm*|i?86*|ia64*|m68k*|s390*|sh*|x86_64*) # Allow devs to mark things as ignorable ... e.g. things @@ -389,7 +397,7 @@ install_qa_check() { if [[ -n ${f} ]] ; then # One more pass to help devs track down the source scanelf -qyRAF '%e %p' "${PORTAGE_BUILDDIR}"/ &> "${T}"/scanelf-execstack.log - vecho -ne '\n' + __vecho -ne '\n' eqawarn "QA Notice: The following files contain writable and executable sections" eqawarn " Files with such sections will not work properly (or at all!) on some" eqawarn " architectures/operating systems. A bug should be filed at" @@ -399,15 +407,15 @@ install_qa_check() { eqawarn " Note: Bugs should be filed for the respective maintainers" eqawarn " of the package in question and not hardened@g.o." eqawarn "${f}" - vecho -ne '\n' + __vecho -ne '\n' die_msg="${die_msg} execstacks" sleep 1 fi # Check for files built without respecting LDFLAGS if [[ "${LDFLAGS}" == *,--hash-style=gnu* ]] && \ - ! has binchecks ${RESTRICT} ; then - f=$(scanelf -qyRF '%k %p' -k .hash "${ED}" | sed -e "s:\.hash ::") + ! has binchecks ${RESTRICT} ; then + f=$(scanelf -qyRF '#k%p' -k .hash "${ED}") if [[ -n ${f} ]] ; then echo "${f}" > "${T}"/scanelf-ignored-LDFLAGS.log if [ "${QA_STRICT_FLAGS_IGNORED-unset}" = unset ] ; then @@ -421,11 +429,11 @@ install_qa_check() { -i "${T}"/scanelf-ignored-LDFLAGS.log f=$(<"${T}"/scanelf-ignored-LDFLAGS.log) if [[ -n ${f} ]] ; then - vecho -ne '\n' + __vecho -ne '\n' eqawarn "${BAD}QA Notice: Files built without respecting LDFLAGS have been detected${NORMAL}" eqawarn " Please include the following list of files in your report:" eqawarn "${f}" - vecho -ne '\n' + __vecho -ne '\n' sleep 1 else rm -f "${T}"/scanelf-ignored-LDFLAGS.log @@ -442,7 +450,7 @@ install_qa_check() { # Check for shared libraries lacking SONAMEs qa_var="QA_SONAME_${ARCH/-/_}" eval "[[ -n \${!qa_var} ]] && QA_SONAME=(\"\${${qa_var}[@]}\")" - f=$(scanelf -ByF '%S %p' "${ED}"{,usr/}lib*/lib*.so* | gawk '$2 == "" { print }' | sed -e "s:^[[:space:]]${ED}:/:") + f=$(scanelf -ByF '%S %p' "${ED}"{,usr/}lib*/lib*.so* | awk '$2 == "" { print }' | sed -e "s:^[[:space:]]${ED}:/:") if [[ -n ${f} ]] ; then echo "${f}" > "${T}"/scanelf-missing-SONAME.log if [[ "${QA_STRICT_SONAME-unset}" == unset ]] ; then @@ -463,10 +471,10 @@ install_qa_check() { sed -e "/^\$/d" -i "${T}"/scanelf-missing-SONAME.log f=$(<"${T}"/scanelf-missing-SONAME.log) if [[ -n ${f} ]] ; then - vecho -ne '\n' + __vecho -ne '\n' eqawarn "QA Notice: The following shared libraries lack a SONAME" eqawarn "${f}" - vecho -ne '\n' + __vecho -ne '\n' sleep 1 else rm -f "${T}"/scanelf-missing-SONAME.log @@ -476,7 +484,7 @@ install_qa_check() { # Check for shared libraries lacking NEEDED entries qa_var="QA_DT_NEEDED_${ARCH/-/_}" eval "[[ -n \${!qa_var} ]] && QA_DT_NEEDED=(\"\${${qa_var}[@]}\")" - f=$(scanelf -ByF '%n %p' "${ED}"{,usr/}lib*/lib*.so* | gawk '$2 == "" { print }' | sed -e "s:^[[:space:]]${ED}:/:") + f=$(scanelf -ByF '%n %p' "${ED}"{,usr/}lib*/lib*.so* | awk '$2 == "" { print }' | sed -e "s:^[[:space:]]${ED}:/:") if [[ -n ${f} ]] ; then echo "${f}" > "${T}"/scanelf-missing-NEEDED.log if [[ "${QA_STRICT_DT_NEEDED-unset}" == unset ]] ; then @@ -497,10 +505,10 @@ install_qa_check() { sed -e "/^\$/d" -i "${T}"/scanelf-missing-NEEDED.log f=$(<"${T}"/scanelf-missing-NEEDED.log) if [[ -n ${f} ]] ; then - vecho -ne '\n' + __vecho -ne '\n' eqawarn "QA Notice: The following shared libraries lack NEEDED entries" eqawarn "${f}" - vecho -ne '\n' + __vecho -ne '\n' sleep 1 else rm -f "${T}"/scanelf-missing-NEEDED.log @@ -545,14 +553,13 @@ install_qa_check() { die "Unsafe files found in \${D}. Portage will not install them." fi - if [[ -d ${D}/${D} ]] ; then - declare -i INSTALLTOD=0 - for i in $(find "${D}/${D}/"); do - eqawarn "QA Notice: /${i##${D}/${D}} installed in \${D}/\${D}" + if [[ -d ${D%/}${D} ]] ; then + local -i INSTALLTOD=0 + while read -r -d $'\0' i ; do + eqawarn "QA Notice: /${i##${D%/}${D}} installed in \${D}/\${D}" ((INSTALLTOD++)) - done - die "Aborting due to QA concerns: ${INSTALLTOD} files installed in ${D}/${D}" - unset INSTALLTOD + done < <(find "${D%/}${D}" -print0) + die "Aborting due to QA concerns: ${INSTALLTOD} files installed in ${D%/}${D}" fi # Sanity check syntax errors in init.d scripts @@ -563,10 +570,31 @@ install_qa_check() { [[ -L ${i} ]] && continue # if empty conf.d/init.d dir exists (baselayout), then i will be "/etc/conf.d/*" and not exist [[ ! -e ${i} ]] && continue + if [[ ${d} == /etc/init.d && ${i} != *.sh ]] ; then + # skip non-shell-script for bug #451386 + [[ $(head -n1 "${i}") =~ ^#!.*[[:space:]/](runscript|sh)$ ]] || continue + fi bash -n "${i}" || die "The init.d file has syntax errors: ${i}" done done + local checkbashisms=$(type -P checkbashisms) + if [[ -n ${checkbashisms} ]] ; then + for d in /etc/init.d ; do + [[ -d ${ED}${d} ]] || continue + for i in "${ED}${d}"/* ; do + [[ -e ${i} ]] || continue + [[ -L ${i} ]] && continue + f=$("${checkbashisms}" -f "${i}" 2>&1) + [[ $? != 0 && -n ${f} ]] || continue + eqawarn "QA Notice: shell script appears to use non-POSIX feature(s):" + while read -r ; + do eqawarn " ${REPLY}" + done <<< "${f//${ED}}" + done + done + fi + # Look for leaking LDFLAGS into pkg-config files f=$(egrep -sH '^Libs.*-Wl,(-O[012]|--hash-style)' "${ED}"/usr/*/pkgconfig/*.pc) if [[ -n ${f} ]] ; then @@ -577,17 +605,16 @@ install_qa_check() { # this should help to ensure that all (most?) shared libraries are executable # and that all libtool scripts / static libraries are not executable local j - for i in "${ED}"opt/*/lib{,32,64} \ - "${ED}"lib{,32,64} \ - "${ED}"usr/lib{,32,64} \ - "${ED}"usr/X11R6/lib{,32,64} ; do + for i in "${ED}"opt/*/lib* \ + "${ED}"lib* \ + "${ED}"usr/lib* ; do [[ ! -d ${i} ]] && continue for j in "${i}"/*.so.* "${i}"/*.so ; do [[ ! -e ${j} ]] && continue [[ -L ${j} ]] && continue [[ -x ${j} ]] && continue - vecho "making executable: ${j#${ED}}" + __vecho "making executable: ${j#${ED}}" chmod +x "${j}" done @@ -595,7 +622,7 @@ install_qa_check() { [[ ! -e ${j} ]] && continue [[ -L ${j} ]] && continue [[ ! -x ${j} ]] && continue - vecho "removing executable bit: ${j#${ED}}" + __vecho "removing executable bit: ${j#${ED}}" chmod -x "${j}" done @@ -604,7 +631,7 @@ install_qa_check() { [[ ! -L ${j} ]] && continue linkdest=$(readlink "${j}") if [[ ${linkdest} == /* ]] ; then - vecho -ne '\n' + __vecho -ne '\n' eqawarn "QA Notice: Found an absolute symlink in a library directory:" eqawarn " ${j#${D}} -> ${linkdest}" eqawarn " It should be a relative symlink if in the same directory" @@ -613,8 +640,8 @@ install_qa_check() { done done - # When installing static libraries into /usr/lib and shared libraries into - # /lib, we have to make sure we have a linker script in /usr/lib along side + # When installing static libraries into /usr/lib and shared libraries into + # /lib, we have to make sure we have a linker script in /usr/lib along side # the static library, or gcc will utilize the static lib when linking :(. # http://bugs.gentoo.org/4411 abort="no" @@ -624,7 +651,7 @@ install_qa_check() { if [[ ! -e ${s} ]] ; then s=${s%usr/*}${s##*/usr/} if [[ -e ${s} ]] ; then - vecho -ne '\n' + __vecho -ne '\n' eqawarn "QA Notice: Missing gen_usr_ldscript for ${s##*/}" abort="yes" fi @@ -635,11 +662,11 @@ install_qa_check() { # Make sure people don't store libtool files or static libs in /lib f=$(ls "${ED}"lib*/*.{a,la} 2>/dev/null) if [[ -n ${f} ]] ; then - vecho -ne '\n' + __vecho -ne '\n' eqawarn "QA Notice: Excessive files found in the / partition" eqawarn "${f}" - vecho -ne '\n' - die "static archives (*.a) and libtool library files (*.la) do not belong in /" + __vecho -ne '\n' + die "static archives (*.a) and libtool library files (*.la) belong in /usr/lib*, not /lib*" fi # Verify that the libtool files don't contain bogus $D entries. @@ -647,7 +674,7 @@ install_qa_check() { for a in "${ED}"usr/lib*/*.la ; do s=${a##*/} if grep -qs "${ED}" "${a}" ; then - vecho -ne '\n' + __vecho -ne '\n' eqawarn "QA Notice: ${s} appears to contain PORTAGE_TMPDIR paths" abort="yes" fi @@ -688,6 +715,8 @@ install_qa_check() { ": warning: reference to local variable .* returned" ": warning: returning reference to temporary" ": warning: function returns address of local variable" + ": warning: .*\\[-Wsizeof-pointer-memaccess\\]" + ": warning: .*\\[-Waggressive-loop-optimizations\\]" # this may be valid code :/ #": warning: multi-character character constant" # need to check these two ... @@ -726,18 +755,19 @@ install_qa_check() { eerror " with the maintaining herd of the package." eerror else - vecho -ne '\n' + __vecho -ne '\n' eqawarn "QA Notice: Package triggers severe warnings which indicate that it" eqawarn " may exhibit random runtime failures." eqawarn "${f}" - vecho -ne '\n' + __vecho -ne '\n' fi fi done local cat_cmd=cat [[ $PORTAGE_LOG_FILE = *.gz ]] && cat_cmd=zcat [[ $reset_debug = 1 ]] && set -x - f=$($cat_cmd "${PORTAGE_LOG_FILE}" | \ + # Use safe cwd, avoiding unsafe import for bug #469338. + f=$(cd "${PORTAGE_PYM_PATH}" ; $cat_cmd "${PORTAGE_LOG_FILE}" | \ "${PORTAGE_PYTHON:-/usr/bin/python}" "$PORTAGE_BIN_PATH"/check-implicit-pointer-usage.py || die "check-implicit-pointer-usage.py failed") if [[ -n ${f} ]] ; then @@ -763,11 +793,11 @@ install_qa_check() { eerror " with the maintaining herd of the package." eerror else - vecho -ne '\n' + __vecho -ne '\n' eqawarn "QA Notice: Package triggers severe warnings which indicate that it" eqawarn " will almost certainly crash on 64bit architectures." eqawarn "${f}" - vecho -ne '\n' + __vecho -ne '\n' fi fi @@ -793,32 +823,42 @@ install_qa_check() { [[ -x /usr/bin/file && -x /usr/bin/find ]] && \ [[ -n ${MULTILIB_STRICT_DIRS} && -n ${MULTILIB_STRICT_DENY} ]] then - local abort=no dir file firstrun=yes + rm -f "${T}/multilib-strict.log" + local abort=no dir file MULTILIB_STRICT_EXEMPT=$(echo ${MULTILIB_STRICT_EXEMPT} | sed -e 's:\([(|)]\):\\\1:g') for dir in ${MULTILIB_STRICT_DIRS} ; do [[ -d ${ED}/${dir} ]] || continue for file in $(find ${ED}/${dir} -type f | grep -v "^${ED}/${dir}/${MULTILIB_STRICT_EXEMPT}"); do if file ${file} | egrep -q "${MULTILIB_STRICT_DENY}" ; then - if [[ ${firstrun} == yes ]] ; then - echo "Files matching a file type that is not allowed:" - firstrun=no - fi - abort=yes - echo " ${file#${ED}//}" + echo "${file#${ED}//}" >> "${T}/multilib-strict.log" fi done done - [[ ${abort} == yes ]] && die "multilib-strict check failed!" - fi - # ensure packages don't install systemd units automagically - if ! has systemd ${INHERITED} && \ - [[ -d "${ED}"/lib/systemd/system ]] - then - eqawarn "QA Notice: package installs systemd unit files (/lib/systemd/system)" - eqawarn " but does not inherit systemd.eclass." - has stricter ${FEATURES} \ - && die "install aborted due to missing inherit of systemd.eclass" + if [[ -s ${T}/multilib-strict.log ]] ; then + if [[ ${#QA_MULTILIB_PATHS[@]} -eq 1 ]] ; then + local shopts=$- + set -o noglob + QA_MULTILIB_PATHS=(${QA_MULTILIB_PATHS}) + set +o noglob + set -${shopts} + fi + if [ "${QA_STRICT_MULTILIB_PATHS-unset}" = unset ] ; then + for x in "${QA_MULTILIB_PATHS[@]}" ; do + sed -e "s#^${x#/}\$##" -i "${T}/multilib-strict.log" + done + sed -e "/^\$/d" -i "${T}/multilib-strict.log" + fi + if [[ -s ${T}/multilib-strict.log ]] ; then + abort=yes + echo "Files matching a file type that is not allowed:" + while read -r ; do + echo " ${REPLY}" + done < "${T}/multilib-strict.log" + fi + fi + + [[ ${abort} == yes ]] && die "multilib-strict check failed!" fi } @@ -851,16 +891,6 @@ install_qa_check_prefix() { # all further checks rely on ${ED} existing [[ -d ${ED} ]] || return - # this does not really belong here, but it's closely tied to - # the code below; many runscripts generate positives here, and we - # know they don't work (bug #196294) so as long as that one - # remains an issue, simply remove them as they won't work - # anyway, avoid etc/init.d/functions.sh from being thrown away - if [[ ( -d "${ED}"/etc/conf.d || -d "${ED}"/etc/init.d ) && ! -f "${ED}"/etc/init.d/functions.sh ]] ; then - ewarn "removed /etc/init.d and /etc/conf.d directories until bug #196294 has been resolved" - rm -Rf "${ED}"/etc/{conf,init}.d - fi - # check shebangs, bug #282539 rm -f "${T}"/non-prefix-shebangs-errs local WHITELIST=" /usr/bin/env " @@ -952,7 +982,7 @@ install_mask() { local no_inst for no_inst in ${install_mask}; do set +o noglob - quiet_mode || einfo "Removing ${no_inst}" + __quiet_mode || einfo "Removing ${no_inst}" # normal stuff rm -Rf "${root}"/${no_inst} >&/dev/null @@ -971,8 +1001,9 @@ preinst_mask() { return 1 fi - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) local ED=${D} ;; esac + if ! ___eapi_has_prefix_variables; then + local ED=${D} + fi # Make sure $PWD is not ${D} so that we don't leave gmon.out files # in there in case any tools were built with -pg in CFLAGS. @@ -1000,8 +1031,9 @@ preinst_sfperms() { return 1 fi - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) local ED=${D} ;; esac + if ! ___eapi_has_prefix_variables; then + local ED=${D} + fi # Smart FileSystem Permissions if has sfperms $FEATURES; then @@ -1039,8 +1071,9 @@ preinst_suid_scan() { return 1 fi - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) local ED=${D} ;; esac + if ! ___eapi_has_prefix_variables; then + local ED=${D} + fi # total suid control. if has suidctl $FEATURES; then @@ -1050,19 +1083,19 @@ preinst_suid_scan() { # to files outside of the sandbox, but this # can easly be bypassed using the addwrite() function addwrite "${sfconf}" - vecho ">>> Performing suid scan in ${ED}" + __vecho ">>> Performing suid scan in ${ED}" for i in $(find "${ED}" -type f \( -perm -4000 -o -perm -2000 \) ); do if [ -s "${sfconf}" ]; then install_path=/${i#${ED}} if grep -q "^${install_path}\$" "${sfconf}" ; then - vecho "- ${install_path} is an approved suid file" + __vecho "- ${install_path} is an approved suid file" else - vecho ">>> Removing sbit on non registered ${install_path}" + __vecho ">>> Removing sbit on non registered ${install_path}" for x in 5 4 3 2 1 0; do sleep 0.25 ; done ls_ret=$(ls -ldh "${i}") chmod ugo-s "${i}" grep "^#${install_path}$" "${sfconf}" > /dev/null || { - vecho ">>> Appending commented out entry to ${sfconf} for ${PF}" + __vecho ">>> Appending commented out entry to ${sfconf} for ${PF}" echo "## ${ls_ret%${ED}*}${install_path}" >> "${sfconf}" echo "#${install_path}" >> "${sfconf}" # no delwrite() eh? @@ -1070,7 +1103,7 @@ preinst_suid_scan() { } fi else - vecho "suidctl feature set but you are lacking a ${sfconf}" + __vecho "suidctl feature set but you are lacking a ${sfconf}" fi done fi @@ -1082,34 +1115,35 @@ preinst_selinux_labels() { return 1 fi if has selinux ${FEATURES}; then - # SELinux file labeling (needs to always be last in dyn_preinst) + # SELinux file labeling (needs to execute after preinst) # only attempt to label if setfiles is executable # and 'context' is available on selinuxfs. if [ -f /selinux/context -o -f /sys/fs/selinux/context ] && \ [ -x /usr/sbin/setfiles -a -x /usr/sbin/selinuxconfig ]; then - vecho ">>> Setting SELinux security labels" + __vecho ">>> Setting SELinux security labels" ( eval "$(/usr/sbin/selinuxconfig)" || \ die "Failed to determine SELinux policy paths."; - + addwrite /selinux/context addwrite /sys/fs/selinux/context - + /usr/sbin/setfiles "${file_contexts_path}" -r "${D}" "${D}" ) || die "Failed to set SELinux security labels." else # nonfatal, since merging can happen outside a SE kernel # like during a recovery situation - vecho "!!! Unable to set SELinux security labels" + __vecho "!!! Unable to set SELinux security labels" fi fi } -dyn_package() { +__dyn_package() { local PROOT - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) local EPREFIX= ED=${D} ;; esac + if ! ___eapi_has_prefix_variables; then + local EPREFIX= ED=${D} + fi # Make sure $PWD is not ${D} so that we don't leave gmon.out files # in there in case any tools were built with -pg in CFLAGS. @@ -1132,6 +1166,7 @@ dyn_package() { local tar_options="" [[ $PORTAGE_VERBOSE = 1 ]] && tar_options+=" -v" + has xattr ${FEATURES} && [[ $(tar --help 2> /dev/null) == *--xattrs* ]] && tar_options+=" --xattrs" # Sandbox is disabled in case the user wants to use a symlink # for $PKGDIR and/or $PKGDIR/All. export SANDBOX_ON="0" @@ -1141,7 +1176,7 @@ dyn_package() { tar $tar_options -cf - $PORTAGE_BINPKG_TAR_OPTS -C "${PROOT}" . | \ $PORTAGE_BZIP2_COMMAND -c > "$PORTAGE_BINPKG_TMPFILE" assert "failed to pack binary package: '$PORTAGE_BINPKG_TMPFILE'" - PYTHONPATH=${PORTAGE_PYM_PATH}${PYTHONPATH:+:}${PYTHONPATH} \ + PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \ "${PORTAGE_PYTHON:-/usr/bin/python}" "$PORTAGE_BIN_PATH"/xpak-helper.py recompose \ "$PORTAGE_BINPKG_TMPFILE" "$PORTAGE_BUILDDIR/build-info" if [ $? -ne 0 ]; then @@ -1158,7 +1193,7 @@ dyn_package() { fi [ -n "${md5_hash}" ] && \ echo ${md5_hash} > "${PORTAGE_BUILDDIR}"/build-info/BINPKGMD5 - vecho ">>> Done." + __vecho ">>> Done." # cleanup our temp tree [[ -n ${PKG_INSTALL_MASK} ]] && rm -rf "${PROOT}" @@ -1167,8 +1202,8 @@ dyn_package() { die "Failed to create $PORTAGE_BUILDDIR/.packaged" } -dyn_spec() { - local sources_dir=/usr/src/rpm/SOURCES +__dyn_spec() { + local sources_dir=${T}/rpmbuild/SOURCES mkdir -p "${sources_dir}" declare -a tar_args=("${EBUILD}") [[ -d ${FILESDIR} ]] && tar_args=("${EBUILD}" "${FILESDIR}") @@ -1181,10 +1216,9 @@ Summary: ${DESCRIPTION} Name: ${PN} Version: ${PV} Release: ${PR} -Copyright: GPL +License: GPL Group: portage/${CATEGORY} Source: ${PF}.tar.gz -Buildroot: ${D} %description ${DESCRIPTION} @@ -1205,18 +1239,18 @@ __END1__ } -dyn_rpm() { - - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) local EPREFIX= ;; esac +__dyn_rpm() { + if ! ___eapi_has_prefix_variables; then + local EPREFIX= + fi cd "${T}" || die "cd failed" - local machine_name=$(uname -m) - local dest_dir=${EPREFIX}/usr/src/rpm/RPMS/${machine_name} - addwrite ${EPREFIX}/usr/src/rpm + local machine_name=${CHOST%%-*} + local dest_dir=${T}/rpmbuild/RPMS/${machine_name} addwrite "${RPMDIR}" - dyn_spec - rpmbuild -bb --clean --rmsource "${PF}.spec" || die "Failed to integrate rpm spec file" + __dyn_spec + HOME=${T} \ + rpmbuild -bb --clean --nodeps --rmsource "${PF}.spec" --buildroot "${D}" --target "${CHOST}" || die "Failed to integrate rpm spec file" install -D "${dest_dir}/${PN}-${PV}-${PR}.${machine_name}.rpm" \ "${RPMDIR}/${CATEGORY}/${PN}-${PV}-${PR}.rpm" || \ die "Failed to move rpm" @@ -1254,7 +1288,7 @@ install_hooks() { } if [ -n "${MISC_FUNCTIONS_ARGS}" ]; then - source_all_bashrcs + __source_all_bashrcs [ "$PORTAGE_DEBUG" == "1" ] && set -x for x in ${MISC_FUNCTIONS_ARGS}; do ${x} diff --git a/bin/phase-functions.sh b/bin/phase-functions.sh index ce251ceb9..f39a024a2 100644 --- a/bin/phase-functions.sh +++ b/bin/phase-functions.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 1999-2012 Gentoo Foundation +# Copyright 1999-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # Hardcoded bash lists are needed for backward compatibility with @@ -8,28 +8,31 @@ # when portage is upgrading itself. PORTAGE_READONLY_METADATA="DEFINED_PHASES DEPEND DESCRIPTION - EAPI HOMEPAGE INHERITED IUSE REQUIRED_USE KEYWORDS LICENSE + EAPI HDEPEND HOMEPAGE INHERITED IUSE REQUIRED_USE KEYWORDS LICENSE PDEPEND PROVIDE RDEPEND REPOSITORY RESTRICT SLOT SRC_URI" -PORTAGE_READONLY_VARS="D EBUILD EBUILD_PHASE \ +PORTAGE_READONLY_VARS="D EBUILD EBUILD_PHASE EBUILD_PHASE_FUNC \ EBUILD_SH_ARGS ECLASSDIR EMERGE_FROM FILESDIR MERGE_TYPE \ PM_EBUILD_HOOK_DIR \ PORTAGE_ACTUAL_DISTDIR PORTAGE_ARCHLIST PORTAGE_BASHRC \ PORTAGE_BINPKG_FILE PORTAGE_BINPKG_TAR_OPTS PORTAGE_BINPKG_TMPFILE \ - PORTAGE_BIN_PATH PORTAGE_BUILDDIR PORTAGE_BUNZIP2_COMMAND \ + PORTAGE_BIN_PATH PORTAGE_BUILDDIR PORTAGE_BUILD_GROUP \ + PORTAGE_BUILD_USER PORTAGE_BUNZIP2_COMMAND \ PORTAGE_BZIP2_COMMAND PORTAGE_COLORMAP PORTAGE_CONFIGROOT \ PORTAGE_DEBUG PORTAGE_DEPCACHEDIR PORTAGE_EBUILD_EXIT_FILE \ + PORTAGE_ECLASS_LOCATIONS \ PORTAGE_GID PORTAGE_GRPNAME PORTAGE_INST_GID PORTAGE_INST_UID \ - PORTAGE_IPC_DAEMON PORTAGE_IUSE PORTAGE_LOG_FILE \ + PORTAGE_INTERNAL_CALLER PORTAGE_IPC_DAEMON PORTAGE_IUSE PORTAGE_LOG_FILE \ PORTAGE_MUTABLE_FILTERED_VARS PORTAGE_OVERRIDE_EPREFIX \ - PORTAGE_PYM_PATH PORTAGE_PYTHON \ + PORTAGE_PYM_PATH PORTAGE_PYTHON PORTAGE_PYTHONPATH \ PORTAGE_READONLY_METADATA PORTAGE_READONLY_VARS \ - PORTAGE_REPO_NAME PORTAGE_RESTRICT \ + PORTAGE_REPO_NAME PORTAGE_REPOSITORIES PORTAGE_RESTRICT \ PORTAGE_SAVED_READONLY_VARS PORTAGE_SIGPIPE_STATUS \ PORTAGE_TMPDIR PORTAGE_UPDATE_ENV PORTAGE_USERNAME \ - PORTAGE_VERBOSE PORTAGE_WORKDIR_MODE PORTDIR PORTDIR_OVERLAY \ + PORTAGE_VERBOSE PORTAGE_WORKDIR_MODE PORTAGE_XATTR_EXCLUDE \ + PORTDIR \ PROFILE_PATHS REPLACING_VERSIONS REPLACED_BY_VERSION T WORKDIR \ - __PORTAGE_TEST_HARDLINK_LOCKS" + __PORTAGE_HELPER __PORTAGE_TEST_HARDLINK_LOCKS" PORTAGE_SAVED_READONLY_VARS="A CATEGORY P PF PN PR PV PVR" @@ -39,7 +42,7 @@ PORTAGE_SAVED_READONLY_VARS="A CATEGORY P PF PN PR PV PVR" # it is saved or loaded (any mutations do not persist). PORTAGE_MUTABLE_FILTERED_VARS="AA HOSTNAME" -# @FUNCTION: filter_readonly_variables +# @FUNCTION: __filter_readonly_variables # @DESCRIPTION: [--filter-sandbox] [--allow-extra-vars] # Read an environment from stdin and echo to stdout while filtering variables # with names that are known to cause interference: @@ -81,14 +84,14 @@ PORTAGE_MUTABLE_FILTERED_VARS="AA HOSTNAME" # readonly variable cause the shell to exit while executing the "source" # builtin command. To avoid this problem, this function filters those # variables out and discards them. See bug #190128. -filter_readonly_variables() { +__filter_readonly_variables() { local x filtered_vars local readonly_bash_vars="BASHOPTS BASHPID DIRSTACK EUID FUNCNAME GROUPS PIPESTATUS PPID SHELLOPTS UID" local bash_misc_vars="BASH BASH_.* COLUMNS COMP_WORDBREAKS HISTCMD HISTFILE HOSTNAME HOSTTYPE IFS LINENO MACHTYPE OLDPWD OPTERR OPTIND OSTYPE POSIXLY_CORRECT PS4 PWD RANDOM - SECONDS SHELL SHLVL _" + SECONDS SHLVL _" local filtered_sandbox_vars="SANDBOX_ACTIVE SANDBOX_BASHRC SANDBOX_DEBUG_LOG SANDBOX_DISABLED SANDBOX_LIB SANDBOX_LOG SANDBOX_ON" @@ -100,15 +103,9 @@ filter_readonly_variables() { # Don't filter/interfere with prefix variables unless they are # supported by the current EAPI. - case "${EAPI:-0}" in - 0|1|2) - [[ " ${FEATURES} " == *" force-prefix "* ]] && \ - filtered_vars+=" ED EPREFIX EROOT" - ;; - *) - filtered_vars+=" ED EPREFIX EROOT" - ;; - esac + if ___eapi_has_prefix_variables; then + filtered_vars+=" ED EPREFIX EROOT" + fi if has --filter-sandbox $* ; then filtered_vars="${filtered_vars} SANDBOX_.*" @@ -140,14 +137,14 @@ filter_readonly_variables() { "${PORTAGE_PYTHON:-/usr/bin/python}" "${PORTAGE_BIN_PATH}"/filter-bash-environment.py "${filtered_vars}" || die "filter-bash-environment.py failed" } -# @FUNCTION: preprocess_ebuild_env +# @FUNCTION: __preprocess_ebuild_env # @DESCRIPTION: # Filter any readonly variables from ${T}/environment, source it, and then -# save it via save_ebuild_env(). This process should be sufficient to prevent +# save it via __save_ebuild_env(). This process should be sufficient to prevent # any stale variables or functions from an arbitrary environment from # interfering with the current environment. This is useful when an existing # environment needs to be loaded from a binary or installed package. -preprocess_ebuild_env() { +__preprocess_ebuild_env() { local _portage_filter_opts="--filter-features --filter-locale --filter-path --filter-sandbox" # If environment.raw is present, this is a signal from the python side, @@ -156,7 +153,7 @@ preprocess_ebuild_env() { # Otherwise, we don't need to filter the environment. [ -f "${T}/environment.raw" ] || return 0 - filter_readonly_variables $_portage_filter_opts < "${T}"/environment \ + __filter_readonly_variables $_portage_filter_opts < "${T}"/environment \ >> "$T/environment.filtered" || return $? unset _portage_filter_opts mv "${T}"/environment.filtered "${T}"/environment || return $? @@ -174,20 +171,20 @@ preprocess_ebuild_env() { # until we've merged them with our current values. export SANDBOX_ON=0 - # It's remotely possible that save_ebuild_env() has been overridden + # It's remotely possible that __save_ebuild_env() has been overridden # by the above source command. To protect ourselves, we override it # here with our own version. ${PORTAGE_BIN_PATH} is safe to use here # because it's already filtered above. source "${PORTAGE_BIN_PATH}/save-ebuild-env.sh" || exit $? - # Rely on save_ebuild_env() to filter out any remaining variables + # Rely on __save_ebuild_env() to filter out any remaining variables # and functions that could interfere with the current environment. - save_ebuild_env || exit $? + __save_ebuild_env || exit $? >> "$T/environment.success" || exit $? ) > "${T}/environment.filtered" local retval if [ -e "${T}/environment.success" ] ; then - filter_readonly_variables --filter-features < \ + __filter_readonly_variables --filter-features < \ "${T}/environment.filtered" > "${T}/environment" retval=$? else @@ -197,62 +194,62 @@ preprocess_ebuild_env() { return ${retval} } -ebuild_phase() { - declare -F "$1" >/dev/null && qa_call $1 +__ebuild_phase() { + declare -F "$1" >/dev/null && __qa_call $1 } -ebuild_phase_with_hooks() { +__ebuild_phase_with_hooks() { local x phase_name=${1} for x in {pre_,,post_}${phase_name} ; do - ebuild_phase ${x} + __ebuild_phase ${x} done } -dyn_pretend() { +__dyn_pretend() { if [[ -e $PORTAGE_BUILDDIR/.pretended ]] ; then - vecho ">>> It appears that '$PF' is already pretended; skipping." - vecho ">>> Remove '$PORTAGE_BUILDDIR/.pretended' to force pretend." + __vecho ">>> It appears that '$PF' is already pretended; skipping." + __vecho ">>> Remove '$PORTAGE_BUILDDIR/.pretended' to force pretend." return 0 fi - ebuild_phase pre_pkg_pretend - ebuild_phase pkg_pretend + __ebuild_phase pre_pkg_pretend + __ebuild_phase pkg_pretend >> "$PORTAGE_BUILDDIR/.pretended" || \ die "Failed to create $PORTAGE_BUILDDIR/.pretended" - ebuild_phase post_pkg_pretend + __ebuild_phase post_pkg_pretend } -dyn_setup() { +__dyn_setup() { if [[ -e $PORTAGE_BUILDDIR/.setuped ]] ; then - vecho ">>> It appears that '$PF' is already setup; skipping." - vecho ">>> Remove '$PORTAGE_BUILDDIR/.setuped' to force setup." + __vecho ">>> It appears that '$PF' is already setup; skipping." + __vecho ">>> Remove '$PORTAGE_BUILDDIR/.setuped' to force setup." return 0 fi - ebuild_phase pre_pkg_setup - ebuild_phase pkg_setup + __ebuild_phase pre_pkg_setup + __ebuild_phase pkg_setup >> "$PORTAGE_BUILDDIR/.setuped" || \ die "Failed to create $PORTAGE_BUILDDIR/.setuped" - ebuild_phase post_pkg_setup + __ebuild_phase post_pkg_setup } -dyn_unpack() { +__dyn_unpack() { if [[ -f ${PORTAGE_BUILDDIR}/.unpacked ]] ; then - vecho ">>> WORKDIR is up-to-date, keeping..." + __vecho ">>> WORKDIR is up-to-date, keeping..." return 0 fi if [ ! -d "${WORKDIR}" ]; then install -m${PORTAGE_WORKDIR_MODE:-0700} -d "${WORKDIR}" || die "Failed to create dir '${WORKDIR}'" fi cd "${WORKDIR}" || die "Directory change failed: \`cd '${WORKDIR}'\`" - ebuild_phase pre_src_unpack - vecho ">>> Unpacking source..." - ebuild_phase src_unpack + __ebuild_phase pre_src_unpack + __vecho ">>> Unpacking source..." + __ebuild_phase src_unpack >> "$PORTAGE_BUILDDIR/.unpacked" || \ die "Failed to create $PORTAGE_BUILDDIR/.unpacked" - vecho ">>> Source unpacked in ${WORKDIR}" - ebuild_phase post_src_unpack + __vecho ">>> Source unpacked in ${WORKDIR}" + __ebuild_phase post_src_unpack } -dyn_clean() { +__dyn_clean() { if [ -z "${PORTAGE_BUILDDIR}" ]; then echo "Aborting clean phase because PORTAGE_BUILDDIR is unset!" return 1 @@ -299,7 +296,7 @@ dyn_clean() { true } -abort_handler() { +__abort_handler() { local msg if [ "$2" != "fail" ]; then msg="${EBUILD}: ${1} aborted; exiting." @@ -314,37 +311,37 @@ abort_handler() { trap - SIGINT SIGQUIT } -abort_prepare() { - abort_handler src_prepare $1 +__abort_prepare() { + __abort_handler src_prepare $1 rm -f "$PORTAGE_BUILDDIR/.prepared" exit 1 } -abort_configure() { - abort_handler src_configure $1 +__abort_configure() { + __abort_handler src_configure $1 rm -f "$PORTAGE_BUILDDIR/.configured" exit 1 } -abort_compile() { - abort_handler "src_compile" $1 +__abort_compile() { + __abort_handler "src_compile" $1 rm -f "${PORTAGE_BUILDDIR}/.compiled" exit 1 } -abort_test() { - abort_handler "dyn_test" $1 +__abort_test() { + __abort_handler "__dyn_test" $1 rm -f "${PORTAGE_BUILDDIR}/.tested" exit 1 } -abort_install() { - abort_handler "src_install" $1 +__abort_install() { + __abort_handler "src_install" $1 rm -rf "${PORTAGE_BUILDDIR}/image" exit 1 } -has_phase_defined_up_to() { +__has_phase_defined_up_to() { local phase for phase in unpack prepare configure compile install; do has ${phase} ${DEFINED_PHASES} && return 0 @@ -354,89 +351,89 @@ has_phase_defined_up_to() { return 1 } -dyn_prepare() { +__dyn_prepare() { if [[ -e $PORTAGE_BUILDDIR/.prepared ]] ; then - vecho ">>> It appears that '$PF' is already prepared; skipping." - vecho ">>> Remove '$PORTAGE_BUILDDIR/.prepared' to force prepare." + __vecho ">>> It appears that '$PF' is already prepared; skipping." + __vecho ">>> Remove '$PORTAGE_BUILDDIR/.prepared' to force prepare." return 0 fi if [[ -d $S ]] ; then cd "${S}" - elif has $EAPI 0 1 2 3 3_pre2 ; then + elif ___eapi_has_S_WORKDIR_fallback; then cd "${WORKDIR}" - elif [[ -z ${A} ]] && ! has_phase_defined_up_to prepare; then + elif [[ -z ${A} ]] && ! __has_phase_defined_up_to prepare; then cd "${WORKDIR}" else die "The source directory '${S}' doesn't exist" fi - trap abort_prepare SIGINT SIGQUIT + trap __abort_prepare SIGINT SIGQUIT - ebuild_phase pre_src_prepare - vecho ">>> Preparing source in $PWD ..." - ebuild_phase src_prepare + __ebuild_phase pre_src_prepare + __vecho ">>> Preparing source in $PWD ..." + __ebuild_phase src_prepare >> "$PORTAGE_BUILDDIR/.prepared" || \ die "Failed to create $PORTAGE_BUILDDIR/.prepared" - vecho ">>> Source prepared." - ebuild_phase post_src_prepare + __vecho ">>> Source prepared." + __ebuild_phase post_src_prepare trap - SIGINT SIGQUIT } -dyn_configure() { +__dyn_configure() { if [[ -e $PORTAGE_BUILDDIR/.configured ]] ; then - vecho ">>> It appears that '$PF' is already configured; skipping." - vecho ">>> Remove '$PORTAGE_BUILDDIR/.configured' to force configuration." + __vecho ">>> It appears that '$PF' is already configured; skipping." + __vecho ">>> Remove '$PORTAGE_BUILDDIR/.configured' to force configuration." return 0 fi if [[ -d $S ]] ; then cd "${S}" - elif has $EAPI 0 1 2 3 3_pre2 ; then + elif ___eapi_has_S_WORKDIR_fallback; then cd "${WORKDIR}" - elif [[ -z ${A} ]] && ! has_phase_defined_up_to configure; then + elif [[ -z ${A} ]] && ! __has_phase_defined_up_to configure; then cd "${WORKDIR}" else die "The source directory '${S}' doesn't exist" fi - trap abort_configure SIGINT SIGQUIT + trap __abort_configure SIGINT SIGQUIT - ebuild_phase pre_src_configure + __ebuild_phase pre_src_configure - vecho ">>> Configuring source in $PWD ..." - ebuild_phase src_configure + __vecho ">>> Configuring source in $PWD ..." + __ebuild_phase src_configure >> "$PORTAGE_BUILDDIR/.configured" || \ die "Failed to create $PORTAGE_BUILDDIR/.configured" - vecho ">>> Source configured." + __vecho ">>> Source configured." - ebuild_phase post_src_configure + __ebuild_phase post_src_configure trap - SIGINT SIGQUIT } -dyn_compile() { +__dyn_compile() { if [[ -e $PORTAGE_BUILDDIR/.compiled ]] ; then - vecho ">>> It appears that '${PF}' is already compiled; skipping." - vecho ">>> Remove '$PORTAGE_BUILDDIR/.compiled' to force compilation." + __vecho ">>> It appears that '${PF}' is already compiled; skipping." + __vecho ">>> Remove '$PORTAGE_BUILDDIR/.compiled' to force compilation." return 0 fi if [[ -d $S ]] ; then cd "${S}" - elif has $EAPI 0 1 2 3 3_pre2 ; then + elif ___eapi_has_S_WORKDIR_fallback; then cd "${WORKDIR}" - elif [[ -z ${A} ]] && ! has_phase_defined_up_to compile; then + elif [[ -z ${A} ]] && ! __has_phase_defined_up_to compile; then cd "${WORKDIR}" else die "The source directory '${S}' doesn't exist" fi - trap abort_compile SIGINT SIGQUIT + trap __abort_compile SIGINT SIGQUIT if has distcc $FEATURES && has distcc-pump $FEATURES ; then if [[ -z $INCLUDE_SERVER_PORT ]] || [[ ! -w $INCLUDE_SERVER_PORT ]] ; then @@ -445,90 +442,96 @@ dyn_compile() { fi fi - ebuild_phase pre_src_compile + __ebuild_phase pre_src_compile - vecho ">>> Compiling source in $PWD ..." - ebuild_phase src_compile + __vecho ">>> Compiling source in $PWD ..." + __ebuild_phase src_compile >> "$PORTAGE_BUILDDIR/.compiled" || \ die "Failed to create $PORTAGE_BUILDDIR/.compiled" - vecho ">>> Source compiled." + __vecho ">>> Source compiled." - ebuild_phase post_src_compile + __ebuild_phase post_src_compile trap - SIGINT SIGQUIT } -dyn_test() { +__dyn_test() { if [[ -e $PORTAGE_BUILDDIR/.tested ]] ; then - vecho ">>> It appears that ${PN} has already been tested; skipping." - vecho ">>> Remove '${PORTAGE_BUILDDIR}/.tested' to force test." + __vecho ">>> It appears that ${PN} has already been tested; skipping." + __vecho ">>> Remove '${PORTAGE_BUILDDIR}/.tested' to force test." return fi - if [ "${EBUILD_FORCE_TEST}" == "1" ] ; then - # If USE came from ${T}/environment then it might not have USE=test - # like it's supposed to here. - ! has test ${USE} && export USE="${USE} test" - fi - - trap "abort_test" SIGINT SIGQUIT + trap "__abort_test" SIGINT SIGQUIT if [ -d "${S}" ]; then cd "${S}" else cd "${WORKDIR}" fi - if ! has test $FEATURES && [ "${EBUILD_FORCE_TEST}" != "1" ]; then - vecho ">>> Test phase [not enabled]: ${CATEGORY}/${PF}" - elif has test $RESTRICT; then + if has test ${RESTRICT} ; then einfo "Skipping make test/check due to ebuild restriction." - vecho ">>> Test phase [explicitly disabled]: ${CATEGORY}/${PF}" + __vecho ">>> Test phase [disabled because of RESTRICT=test]: ${CATEGORY}/${PF}" + + # If ${EBUILD_FORCE_TEST} == 1 and FEATURES came from ${T}/environment + # then it might not have FEATURES=test like it's supposed to here. + elif [[ ${EBUILD_FORCE_TEST} != 1 ]] && ! has test ${FEATURES} ; then + __vecho ">>> Test phase [not enabled]: ${CATEGORY}/${PF}" else + # If ${EBUILD_FORCE_TEST} == 1 and USE came from ${T}/environment + # then it might not have USE=test like it's supposed to here. + if [[ ${EBUILD_FORCE_TEST} == 1 && test =~ ${PORTAGE_IUSE} ]] && \ + ! has test ${USE} ; then + export USE="${USE} test" + fi + local save_sp=${SANDBOX_PREDICT} addpredict / - ebuild_phase pre_src_test - ebuild_phase src_test + __ebuild_phase pre_src_test + __ebuild_phase src_test >> "$PORTAGE_BUILDDIR/.tested" || \ die "Failed to create $PORTAGE_BUILDDIR/.tested" - ebuild_phase post_src_test + __ebuild_phase post_src_test SANDBOX_PREDICT=${save_sp} fi trap - SIGINT SIGQUIT } -dyn_install() { +__dyn_install() { [ -z "$PORTAGE_BUILDDIR" ] && die "${FUNCNAME}: PORTAGE_BUILDDIR is unset" if has noauto $FEATURES ; then rm -f "${PORTAGE_BUILDDIR}/.installed" elif [[ -e $PORTAGE_BUILDDIR/.installed ]] ; then - vecho ">>> It appears that '${PF}' is already installed; skipping." - vecho ">>> Remove '${PORTAGE_BUILDDIR}/.installed' to force install." + __vecho ">>> It appears that '${PF}' is already installed; skipping." + __vecho ">>> Remove '${PORTAGE_BUILDDIR}/.installed' to force install." return 0 fi - trap "abort_install" SIGINT SIGQUIT - ebuild_phase pre_src_install + trap "__abort_install" SIGINT SIGQUIT + __ebuild_phase pre_src_install - _x=${ED} - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) _x=${D} ;; esac + if ___eapi_has_prefix_variables; then + _x=${ED} + else + _x=${D} + fi rm -rf "${D}" mkdir -p "${_x}" unset _x if [[ -d $S ]] ; then cd "${S}" - elif has $EAPI 0 1 2 3 3_pre2 ; then + elif ___eapi_has_S_WORKDIR_fallback; then cd "${WORKDIR}" - elif [[ -z ${A} ]] && ! has_phase_defined_up_to install; then + elif [[ -z ${A} ]] && ! __has_phase_defined_up_to install; then cd "${WORKDIR}" else die "The source directory '${S}' doesn't exist" fi - vecho - vecho ">>> Install ${PF} into ${D} category ${CATEGORY}" + __vecho + __vecho ">>> Install ${PF} into ${D} category ${CATEGORY}" #our custom version of libtool uses $S and $D to fix #invalid paths in .la files export S D @@ -541,12 +544,12 @@ dyn_install() { export _E_EXEDESTTREE_="" export _E_DOCDESTTREE_="" - ebuild_phase src_install + __ebuild_phase src_install >> "$PORTAGE_BUILDDIR/.installed" || \ die "Failed to create $PORTAGE_BUILDDIR/.installed" - vecho ">>> Completed installing ${PF} into ${D}" - vecho - ebuild_phase post_src_install + __vecho ">>> Completed installing ${PF} into ${D}" + __vecho + __ebuild_phase post_src_install cd "${PORTAGE_BUILDDIR}"/build-info set -f @@ -560,10 +563,15 @@ dyn_install() { if [[ $CATEGORY != virtual ]] ; then for f in ASFLAGS CBUILD CC CFLAGS CHOST CTARGET CXX \ CXXFLAGS EXTRA_ECONF EXTRA_EINSTALL EXTRA_MAKE \ - LDFLAGS LIBCFLAGS LIBCXXFLAGS ; do + LDFLAGS LIBCFLAGS LIBCXXFLAGS QA_CONFIGURE_OPTIONS \ + QA_DESKTOP_FILE ; do x=$(echo -n ${!f}) [[ -n $x ]] && echo "$x" > $f done + # whitespace preserved + for f in QA_AM_MAINTAINER_MODE ; do + [[ -n ${!f} ]] && echo "${!f}" > $f + done fi echo "${USE}" > USE echo "${EAPI:-0}" > EAPI @@ -571,24 +579,22 @@ dyn_install() { # Save EPREFIX, since it makes it easy to use chpathtool to # adjust the content of a binary package so that it will # work in a different EPREFIX from the one is was built for. - case "${EAPI:-0}" in - 0|1|2) - [[ " ${FEATURES} " == *" force-prefix "* ]] && \ - [ -n "${EPREFIX}" ] && echo "${EPREFIX}" > EPREFIX - ;; - *) - [ -n "${EPREFIX}" ] && echo "${EPREFIX}" > EPREFIX - ;; - esac + if ___eapi_has_prefix_variables && [[ -n ${EPREFIX} ]]; then + echo "${EPREFIX}" > EPREFIX + fi set +f # local variables can leak into the saved environment. unset f - save_ebuild_env --exclude-init-phases | filter_readonly_variables \ - --filter-path --filter-sandbox --allow-extra-vars > environment - assert "save_ebuild_env failed" + # Use safe cwd, avoiding unsafe import for bug #469338. + cd "${PORTAGE_PYM_PATH}" + __save_ebuild_env --exclude-init-phases | __filter_readonly_variables \ + --filter-path --filter-sandbox --allow-extra-vars > \ + "${PORTAGE_BUILDDIR}"/build-info/environment + assert "__save_ebuild_env failed" + cd "${PORTAGE_BUILDDIR}"/build-info || die ${PORTAGE_BZIP2_COMMAND} -f9 environment @@ -601,15 +607,7 @@ dyn_install() { trap - SIGINT SIGQUIT } -dyn_preinst() { - if [ -z "${D}" ]; then - eerror "${FUNCNAME}: D is unset" - return 1 - fi - ebuild_phase_with_hooks pkg_preinst -} - -dyn_help() { +__dyn_help() { echo echo "Portage" echo "Copyright 1999-2010 Gentoo Foundation" @@ -625,6 +623,7 @@ dyn_help() { echo " pretend : execute package specific pretend actions" echo " setup : execute package specific setup actions" echo " fetch : download source archive(s) and patches" + echo " nofetch : display special fetch instructions" echo " digest : create a manifest file for the package" echo " manifest : create a manifest file for the package" echo " unpack : unpack sources (auto-dependencies if needed)" @@ -672,19 +671,18 @@ dyn_help() { echo } -# @FUNCTION: _ebuild_arg_to_phase +# @FUNCTION: __ebuild_arg_to_phase # @DESCRIPTION: # Translate a known ebuild(1) argument into the precise # name of it's corresponding ebuild phase. -_ebuild_arg_to_phase() { - [ $# -ne 2 ] && die "expected exactly 2 args, got $#: $*" - local eapi=$1 - local arg=$2 +__ebuild_arg_to_phase() { + [ $# -ne 1 ] && die "expected exactly 1 arg, got $#: $*" + local arg=$1 local phase_func="" case "$arg" in pretend) - ! has $eapi 0 1 2 3 3_pre2 && \ + ___eapi_has_pkg_pretend && \ phase_func=pkg_pretend ;; setup) @@ -697,11 +695,11 @@ _ebuild_arg_to_phase() { phase_func=src_unpack ;; prepare) - ! has $eapi 0 1 && \ + ___eapi_has_src_prepare && \ phase_func=src_prepare ;; configure) - ! has $eapi 0 1 && \ + ___eapi_has_src_configure && \ phase_func=src_configure ;; compile) @@ -732,7 +730,7 @@ _ebuild_arg_to_phase() { return 0 } -_ebuild_phase_funcs() { +__ebuild_phase_funcs() { [ $# -ne 2 ] && die "expected exactly 2 args, got $#: $*" local eapi=$1 local phase_func=$2 @@ -742,20 +740,20 @@ _ebuild_phase_funcs() { for x in pkg_nofetch src_unpack src_test ; do declare -F $x >/dev/null || \ - eval "$x() { _eapi0_$x \"\$@\" ; }" + eval "$x() { __eapi0_$x \"\$@\" ; }" done - case $eapi in + case "$eapi" in 0|1) if ! declare -F src_compile >/dev/null ; then - case $eapi in + case "$eapi" in 0) - src_compile() { _eapi0_src_compile "$@" ; } + src_compile() { __eapi0_src_compile "$@" ; } ;; *) - src_compile() { _eapi1_src_compile "$@" ; } + src_compile() { __eapi1_src_compile "$@" ; } ;; esac fi @@ -775,35 +773,35 @@ _ebuild_phase_funcs() { *) declare -F src_configure >/dev/null || \ - src_configure() { _eapi2_src_configure "$@" ; } + src_configure() { __eapi2_src_configure "$@" ; } declare -F src_compile >/dev/null || \ - src_compile() { _eapi2_src_compile "$@" ; } + src_compile() { __eapi2_src_compile "$@" ; } - has $eapi 2 3 3_pre2 || declare -F src_install >/dev/null || \ - src_install() { _eapi4_src_install "$@" ; } + has $eapi 2 3 || declare -F src_install >/dev/null || \ + src_install() { __eapi4_src_install "$@" ; } if has $phase_func $default_phases ; then - _eapi2_pkg_nofetch () { _eapi0_pkg_nofetch "$@" ; } - _eapi2_src_unpack () { _eapi0_src_unpack "$@" ; } - _eapi2_src_prepare () { true ; } - _eapi2_src_test () { _eapi0_src_test "$@" ; } - _eapi2_src_install () { die "$FUNCNAME is not supported" ; } + __eapi2_pkg_nofetch () { __eapi0_pkg_nofetch "$@" ; } + __eapi2_src_unpack () { __eapi0_src_unpack "$@" ; } + __eapi2_src_prepare () { true ; } + __eapi2_src_test () { __eapi0_src_test "$@" ; } + __eapi2_src_install () { die "$FUNCNAME is not supported" ; } for x in $default_phases ; do - eval "default_$x() { _eapi2_$x \"\$@\" ; }" + eval "default_$x() { __eapi2_$x \"\$@\" ; }" done - eval "default() { _eapi2_$phase_func \"\$@\" ; }" + eval "default() { __eapi2_$phase_func \"\$@\" ; }" - case $eapi in + case "$eapi" in 2|3) ;; *) - eval "default_src_install() { _eapi4_src_install \"\$@\" ; }" + eval "default_src_install() { __eapi4_src_install \"\$@\" ; }" [[ $phase_func = src_install ]] && \ - eval "default() { _eapi4_$phase_func \"\$@\" ; }" + eval "default() { __eapi4_$phase_func \"\$@\" ; }" ;; esac @@ -825,14 +823,14 @@ _ebuild_phase_funcs() { esac } -ebuild_main() { +__ebuild_main() { # Subshell/helper die support (must export for the die helper). # Since this function is typically executed in a subshell, # setup EBUILD_MASTER_PID to refer to the current $BASHPID, # which seems to give the best results when further # nested subshells call die. - export EBUILD_MASTER_PID=$BASHPID + export EBUILD_MASTER_PID=${BASHPID:-$(__bashpid)} trap 'exit 1' SIGTERM #a reasonable default for $S @@ -861,37 +859,39 @@ ebuild_main() { # respect FEATURES="-ccache". has ccache $FEATURES || export CCACHE_DISABLE=1 - local phase_func=$(_ebuild_arg_to_phase "$EAPI" "$EBUILD_PHASE") - [[ -n $phase_func ]] && _ebuild_phase_funcs "$EAPI" "$phase_func" + local phase_func=$(__ebuild_arg_to_phase "$EBUILD_PHASE") + [[ -n $phase_func ]] && __ebuild_phase_funcs "$EAPI" "$phase_func" unset phase_func - source_all_bashrcs + __source_all_bashrcs case ${1} in nofetch) - ebuild_phase_with_hooks pkg_nofetch + __ebuild_phase_with_hooks pkg_nofetch ;; - prerm|postrm|postinst|config|info) + prerm|postrm|preinst|postinst|config|info) if has "${1}" config info && \ ! declare -F "pkg_${1}" >/dev/null ; then ewarn "pkg_${1}() is not defined: '${EBUILD##*/}'" fi export SANDBOX_ON="0" if [ "${PORTAGE_DEBUG}" != "1" ] || [ "${-/x/}" != "$-" ]; then - ebuild_phase_with_hooks pkg_${1} + __ebuild_phase_with_hooks pkg_${1} else set -x - ebuild_phase_with_hooks pkg_${1} + __ebuild_phase_with_hooks pkg_${1} set +x fi - if [[ $EBUILD_PHASE == postinst ]] && [[ -n $PORTAGE_UPDATE_ENV ]]; then + if [[ -n $PORTAGE_UPDATE_ENV ]] ; then # Update environment.bz2 in case installation phases # need to pass some variables to uninstallation phases. - save_ebuild_env --exclude-init-phases | \ - filter_readonly_variables --filter-path \ + # Use safe cwd, avoiding unsafe import for bug #469338. + cd "${PORTAGE_PYM_PATH}" + __save_ebuild_env --exclude-init-phases | \ + __filter_readonly_variables --filter-path \ --filter-sandbox --allow-extra-vars \ | ${PORTAGE_BZIP2_COMMAND} -c -f9 > "$PORTAGE_UPDATE_ENV" - assert "save_ebuild_env failed" + assert "__save_ebuild_env failed" fi ;; unpack|prepare|configure|compile|test|clean|install) @@ -917,7 +917,7 @@ ebuild_main() { x=LIBDIR_$ABI [ -z "$PKG_CONFIG_PATH" -a -n "$ABI" -a -n "${!x}" ] && \ - export PKG_CONFIG_PATH=/usr/${!x}/pkgconfig + export PKG_CONFIG_PATH=${EPREFIX}/usr/${!x}/pkgconfig if has noauto $FEATURES && \ [[ ! -f $PORTAGE_BUILDDIR/.unpacked ]] ; then @@ -952,24 +952,24 @@ ebuild_main() { esac if [ "${PORTAGE_DEBUG}" != "1" ] || [ "${-/x/}" != "$-" ]; then - dyn_${1} + __dyn_${1} else set -x - dyn_${1} + __dyn_${1} set +x fi export SANDBOX_ON="0" ;; - help|pretend|setup|preinst) + help|pretend|setup) #pkg_setup needs to be out of the sandbox for tmp file creation; #for example, awking and piping a file in /tmp requires a temp file to be created #in /etc. If pkg_setup is in the sandbox, both our lilo and apache ebuilds break. export SANDBOX_ON="0" if [ "${PORTAGE_DEBUG}" != "1" ] || [ "${-/x/}" != "$-" ]; then - dyn_${1} + __dyn_${1} else set -x - dyn_${1} + __dyn_${1} set +x fi ;; @@ -979,7 +979,7 @@ ebuild_main() { export SANDBOX_ON="1" echo "Unrecognized arg '${1}'" echo - dyn_help + __dyn_help exit 1 ;; esac @@ -987,11 +987,13 @@ ebuild_main() { # Save the env only for relevant phases. if ! has "${1}" clean help info nofetch ; then umask 002 - save_ebuild_env | filter_readonly_variables \ + # Use safe cwd, avoiding unsafe import for bug #469338. + cd "${PORTAGE_PYM_PATH}" + __save_ebuild_env | __filter_readonly_variables \ --filter-features > "$T/environment" - assert "save_ebuild_env failed" - chown portage:portage "$T/environment" &>/dev/null - chmod g+w "$T/environment" &>/dev/null + assert "__save_ebuild_env failed" + chgrp "${PORTAGE_GRPNAME:-portage}" "$T/environment" + chmod g+w "$T/environment" fi [[ -n $PORTAGE_EBUILD_EXIT_FILE ]] && > "$PORTAGE_EBUILD_EXIT_FILE" if [[ -n $PORTAGE_IPC_DAEMON ]] ; then diff --git a/bin/phase-helpers.sh b/bin/phase-helpers.sh index 946520b20..412decbe0 100644 --- a/bin/phase-helpers.sh +++ b/bin/phase-helpers.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 export DESTTREE=/usr @@ -11,6 +11,8 @@ export EXEOPTIONS="-m0755" export LIBOPTIONS="-m0644" export DIROPTIONS="-m0755" export MOPREFIX=${PN} +# Do not compress files which are smaller than this (in bytes). #169260 +export PORTAGE_DOCOMPRESS_SIZE_LIMIT="128" declare -a PORTAGE_DOCOMPRESS=( /usr/share/{doc,info,man} ) declare -a PORTAGE_DOCOMPRESS_SKIP=( /usr/share/doc/${PF}/html ) @@ -19,13 +21,14 @@ into() { export DESTTREE="" else export DESTTREE=$1 - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) local ED=${D} ;; esac + if ! ___eapi_has_prefix_variables; then + local ED=${D} + fi if [ ! -d "${ED}${DESTTREE}" ]; then install -d "${ED}${DESTTREE}" local ret=$? if [[ $ret -ne 0 ]] ; then - helpers_die "${FUNCNAME[0]} failed" + __helpers_die "${FUNCNAME[0]} failed" return $ret fi fi @@ -37,13 +40,14 @@ insinto() { export INSDESTTREE="" else export INSDESTTREE=$1 - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) local ED=${D} ;; esac + if ! ___eapi_has_prefix_variables; then + local ED=${D} + fi if [ ! -d "${ED}${INSDESTTREE}" ]; then install -d "${ED}${INSDESTTREE}" local ret=$? if [[ $ret -ne 0 ]] ; then - helpers_die "${FUNCNAME[0]} failed" + __helpers_die "${FUNCNAME[0]} failed" return $ret fi fi @@ -55,13 +59,14 @@ exeinto() { export _E_EXEDESTTREE_="" else export _E_EXEDESTTREE_="$1" - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) local ED=${D} ;; esac + if ! ___eapi_has_prefix_variables; then + local ED=${D} + fi if [ ! -d "${ED}${_E_EXEDESTTREE_}" ]; then install -d "${ED}${_E_EXEDESTTREE_}" local ret=$? if [[ $ret -ne 0 ]] ; then - helpers_die "${FUNCNAME[0]} failed" + __helpers_die "${FUNCNAME[0]} failed" return $ret fi fi @@ -73,13 +78,14 @@ docinto() { export _E_DOCDESTTREE_="" else export _E_DOCDESTTREE_="$1" - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) local ED=${D} ;; esac + if ! ___eapi_has_prefix_variables; then + local ED=${D} + fi if [ ! -d "${ED}usr/share/doc/${PF}/${_E_DOCDESTTREE_}" ]; then install -d "${ED}usr/share/doc/${PF}/${_E_DOCDESTTREE_}" local ret=$? if [[ $ret -ne 0 ]] ; then - helpers_die "${FUNCNAME[0]} failed" + __helpers_die "${FUNCNAME[0]} failed" return $ret fi fi @@ -112,13 +118,13 @@ libopts() { } docompress() { - has "${EAPI}" 0 1 2 3 && die "'docompress' not supported in this EAPI" + ___eapi_has_docompress || die "'docompress' not supported in this EAPI" local f g if [[ $1 = "-x" ]]; then shift for f; do - f=$(strip_duplicate_slashes "${f}"); f=${f%/} + f=$(__strip_duplicate_slashes "${f}"); f=${f%/} [[ ${f:0:1} = / ]] || f="/${f}" for g in "${PORTAGE_DOCOMPRESS_SKIP[@]}"; do [[ ${f} = "${g}" ]] && continue 2 @@ -127,7 +133,7 @@ docompress() { done else for f; do - f=$(strip_duplicate_slashes "${f}"); f=${f%/} + f=$(__strip_duplicate_slashes "${f}"); f=${f%/} [[ ${f:0:1} = / ]] || f="/${f}" for g in "${PORTAGE_DOCOMPRESS[@]}"; do [[ ${f} = "${g}" ]] && continue 2 @@ -137,29 +143,6 @@ docompress() { fi } -# adds ".keep" files so that dirs aren't auto-cleaned -keepdir() { - dodir "$@" - local x - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) local ED=${D} ;; esac - if [ "$1" == "-R" ] || [ "$1" == "-r" ]; then - shift - find "$@" -type d -printf "${ED}%p/.keep_${CATEGORY}_${PN}-${SLOT}\n" \ - | tr "\n" "\0" | \ - while read -r -d $'\0' ; do - >> "$REPLY" || \ - die "Failed to recursively create .keep files" - done - else - for x in "$@"; do - >> "${ED}${x}/.keep_${CATEGORY}_${PN}-${SLOT}" || \ - die "Failed to create .keep in ${ED}${x}" - done - fi -} - - useq() { has $EBUILD_PHASE prerm postrm || eqawarn \ "QA Notice: The 'useq' function is deprecated (replaced by 'use')" @@ -174,6 +157,17 @@ usev() { return 1 } +if ___eapi_has_usex; then + usex() { + if use "$1"; then + echo "${2-yes}$4" + else + echo "${3-no}$5" + fi + return 0 + } +fi + use() { local u=$1 local found=0 @@ -194,18 +188,31 @@ use() { #fi true - # Make sure we have this USE flag in IUSE - elif [[ -n $PORTAGE_IUSE && -n $EBUILD_PHASE ]] ; then - [[ $u =~ $PORTAGE_IUSE ]] || \ + # Make sure we have this USE flag in IUSE, but exempt binary + # packages for API consumers like Entropy which do not require + # a full profile with IUSE_IMPLICIT and stuff (see bug #456830). + elif [[ -n $PORTAGE_IUSE && -n $EBUILD_PHASE && + -n $PORTAGE_INTERNAL_CALLER ]] ; then + if [[ ! $u =~ $PORTAGE_IUSE ]] ; then + if [[ ! ${EAPI} =~ ^(0|1|2|3|4|4-python|4-slot-abi)$ ]] ; then + # This is only strict starting with EAPI 5, since implicit IUSE + # is not well defined for earlier EAPIs (see bug #449708). + die "USE Flag '${u}' not in IUSE for ${CATEGORY}/${PF}" + fi eqawarn "QA Notice: USE Flag '${u}' not" \ "in IUSE for ${CATEGORY}/${PF}" + fi fi + local IFS=$' \t\n' prev_shopts=$- ret + set -f if has ${u} ${USE} ; then - return ${found} + ret=${found} else - return $((!found)) + ret=$((!found)) fi + [[ ${prev_shopts} == *f* ]] || set +f + return ${ret} } use_with() { @@ -215,7 +222,7 @@ use_with() { return 1 fi - if ! has "${EAPI:-0}" 0 1 2 3 ; then + if ___eapi_use_enable_and_use_with_support_empty_third_argument; then local UW_SUFFIX=${3+=$3} else local UW_SUFFIX=${3:+=$3} @@ -237,7 +244,7 @@ use_enable() { return 1 fi - if ! has "${EAPI:-0}" 0 1 2 3 ; then + if ___eapi_use_enable_and_use_with_support_empty_third_argument; then local UE_SUFFIX=${3+=$3} else local UE_SUFFIX=${3:+=$3} @@ -255,15 +262,19 @@ use_enable() { unpack() { local srcdir local x - local y + local y y_insensitive + local suffix suffix_insensitive local myfail local eapi=${EAPI:-0} [ -z "$*" ] && die "Nothing passed to the 'unpack' command" for x in "$@"; do - vecho ">>> Unpacking ${x} to ${PWD}" + __vecho ">>> Unpacking ${x} to ${PWD}" + suffix=${x##*.} + suffix_insensitive=$(LC_ALL=C tr "[:upper:]" "[:lower:]" <<< "${suffix}") y=${x%.*} y=${y##*.} + y_insensitive=$(LC_ALL=C tr "[:upper:]" "[:lower:]" <<< "${y}") if [[ ${x} == "./"* ]] ; then srcdir="" @@ -276,10 +287,16 @@ unpack() { fi [[ ! -s ${srcdir}${x} ]] && die "${x} does not exist" - _unpack_tar() { - if [ "${y}" == "tar" ]; then + __unpack_tar() { + if [[ ${y_insensitive} == tar ]] ; then + if ___eapi_unpack_is_case_sensitive && \ + [[ tar != ${y} ]] ; then + eqawarn "QA Notice: unpack called with" \ + "secondary suffix '${y}' which is unofficially" \ + "supported with EAPI '${EAPI}'. Instead use 'tar'." + fi $1 -c -- "$srcdir$x" | tar xof - - assert_sigpipe_ok "$myfail" + __assert_sigpipe_ok "$myfail" else local cwd_dest=${x##*/} cwd_dest=${cwd_dest%.*} @@ -288,30 +305,67 @@ unpack() { } myfail="failure unpacking ${x}" - case "${x##*.}" in + case "${suffix_insensitive}" in tar) + if ___eapi_unpack_is_case_sensitive && \ + [[ tar != ${suffix} ]] ; then + eqawarn "QA Notice: unpack called with" \ + "suffix '${suffix}' which is unofficially supported" \ + "with EAPI '${EAPI}'. Instead use 'tar'." + fi tar xof "$srcdir$x" || die "$myfail" ;; tgz) + if ___eapi_unpack_is_case_sensitive && \ + [[ tgz != ${suffix} ]] ; then + eqawarn "QA Notice: unpack called with" \ + "suffix '${suffix}' which is unofficially supported" \ + "with EAPI '${EAPI}'. Instead use 'tgz'." + fi tar xozf "$srcdir$x" || die "$myfail" ;; tbz|tbz2) + if ___eapi_unpack_is_case_sensitive && \ + [[ " tbz tbz2 " != *" ${suffix} "* ]] ; then + eqawarn "QA Notice: unpack called with" \ + "suffix '${suffix}' which is unofficially supported" \ + "with EAPI '${EAPI}'. Instead use 'tbz' or 'tbz2'." + fi ${PORTAGE_BUNZIP2_COMMAND:-${PORTAGE_BZIP2_COMMAND} -d} -c -- "$srcdir$x" | tar xof - - assert_sigpipe_ok "$myfail" + __assert_sigpipe_ok "$myfail" ;; - ZIP|zip|jar) + zip|jar) + if ___eapi_unpack_is_case_sensitive && \ + [[ " ZIP zip jar " != *" ${suffix} "* ]] ; then + eqawarn "QA Notice: unpack called with" \ + "suffix '${suffix}' which is unofficially supported" \ + "with EAPI '${EAPI}'." \ + "Instead use 'ZIP', 'zip', or 'jar'." + fi # unzip will interactively prompt under some error conditions, # as reported in bug #336285 ( set +x ; while true ; do echo n || break ; done ) | \ unzip -qo "${srcdir}${x}" || die "$myfail" ;; - gz|Z|z) - _unpack_tar "gzip -d" + gz|z) + if ___eapi_unpack_is_case_sensitive && \ + [[ " gz z Z " != *" ${suffix} "* ]] ; then + eqawarn "QA Notice: unpack called with" \ + "suffix '${suffix}' which is unofficially supported" \ + "with EAPI '${EAPI}'. Instead use 'gz', 'z', or 'Z'." + fi + __unpack_tar "gzip -d" ;; bz2|bz) - _unpack_tar "${PORTAGE_BUNZIP2_COMMAND:-${PORTAGE_BZIP2_COMMAND} -d}" + if ___eapi_unpack_is_case_sensitive && \ + [[ " bz bz2 " != *" ${suffix} "* ]] ; then + eqawarn "QA Notice: unpack called with" \ + "suffix '${suffix}' which is unofficially supported" \ + "with EAPI '${EAPI}'. Instead use 'bz' or 'bz2'." + fi + __unpack_tar "${PORTAGE_BUNZIP2_COMMAND:-${PORTAGE_BZIP2_COMMAND} -d}" ;; - 7Z|7z) + 7z) local my_output my_output="$(7z x -y "${srcdir}${x}")" if [ $? -ne 0 ]; then @@ -319,16 +373,41 @@ unpack() { die "$myfail" fi ;; - RAR|rar) + rar) + if ___eapi_unpack_is_case_sensitive && \ + [[ " rar RAR " != *" ${suffix} "* ]] ; then + eqawarn "QA Notice: unpack called with" \ + "suffix '${suffix}' which is unofficially supported" \ + "with EAPI '${EAPI}'. Instead use 'rar' or 'RAR'." + fi unrar x -idq -o+ "${srcdir}${x}" || die "$myfail" ;; - LHa|LHA|lha|lzh) + lha|lzh) + if ___eapi_unpack_is_case_sensitive && \ + [[ " LHA LHa lha lzh " != *" ${suffix} "* ]] ; then + eqawarn "QA Notice: unpack called with" \ + "suffix '${suffix}' which is unofficially supported" \ + "with EAPI '${EAPI}'." \ + "Instead use 'LHA', 'LHa', 'lha', or 'lzh'." + fi lha xfq "${srcdir}${x}" || die "$myfail" ;; a) + if ___eapi_unpack_is_case_sensitive && \ + [[ " a " != *" ${suffix} "* ]] ; then + eqawarn "QA Notice: unpack called with" \ + "suffix '${suffix}' which is unofficially supported" \ + "with EAPI '${EAPI}'. Instead use 'a'." + fi ar x "${srcdir}${x}" || die "$myfail" ;; deb) + if ___eapi_unpack_is_case_sensitive && \ + [[ " deb " != *" ${suffix} "* ]] ; then + eqawarn "QA Notice: unpack called with" \ + "suffix '${suffix}' which is unofficially supported" \ + "with EAPI '${EAPI}'. Instead use 'deb'." + fi # Unpacking .deb archives can not always be done with # `ar`. For instance on AIX this doesn't work out. If # we have `deb2targz` installed, prefer it over `ar` for @@ -356,17 +435,29 @@ unpack() { fi ;; lzma) - _unpack_tar "lzma -d" + if ___eapi_unpack_is_case_sensitive && \ + [[ " lzma " != *" ${suffix} "* ]] ; then + eqawarn "QA Notice: unpack called with" \ + "suffix '${suffix}' which is unofficially supported" \ + "with EAPI '${EAPI}'. Instead use 'lzma'." + fi + __unpack_tar "lzma -d" ;; xz) - if has $eapi 0 1 2 ; then - vecho "unpack ${x}: file format not recognized. Ignoring." + if ___eapi_unpack_is_case_sensitive && \ + [[ " xz " != *" ${suffix} "* ]] ; then + eqawarn "QA Notice: unpack called with" \ + "suffix '${suffix}' which is unofficially supported" \ + "with EAPI '${EAPI}'. Instead use 'xz'." + fi + if ___eapi_unpack_supports_xz; then + __unpack_tar "xz -d" else - _unpack_tar "xz -d" + __vecho "unpack ${x}: file format not recognized. Ignoring." fi ;; *) - vecho "unpack ${x}: file format not recognized. Ignoring." + __vecho "unpack ${x}: file format not recognized. Ignoring." ;; esac done @@ -378,22 +469,24 @@ unpack() { econf() { local x + local pid=${BASHPID:-$(__bashpid)} - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) local EPREFIX= ;; esac + if ! ___eapi_has_prefix_variables; then + local EPREFIX= + fi - _hasg() { + __hasg() { local x s=$1 shift for x ; do [[ ${x} == ${s} ]] && echo "${x}" && return 0 ; done return 1 } - _hasgq() { _hasg "$@" >/dev/null ; } + __hasgq() { __hasg "$@" >/dev/null ; } - local phase_func=$(_ebuild_arg_to_phase "$EAPI" "$EBUILD_PHASE") + local phase_func=$(__ebuild_arg_to_phase "$EBUILD_PHASE") if [[ -n $phase_func ]] ; then - if has "$EAPI" 0 1 ; then + if ! ___eapi_has_src_configure; then [[ $phase_func != src_compile ]] && \ eqawarn "QA Notice: econf called in" \ "$phase_func instead of src_compile" @@ -408,23 +501,44 @@ econf() { if [ -x "${ECONF_SOURCE}/configure" ]; then if [[ -n $CONFIG_SHELL && \ "$(head -n1 "$ECONF_SOURCE/configure")" =~ ^'#!'[[:space:]]*/bin/sh([[:space:]]|$) ]] ; then - sed -e "1s:^#![[:space:]]*/bin/sh:#!$CONFIG_SHELL:" -i "$ECONF_SOURCE/configure" || \ - die "Substition of shebang in '$ECONF_SOURCE/configure' failed" + # preserve timestamp, see bug #440304 + touch -r "${ECONF_SOURCE}/configure" "${ECONF_SOURCE}/configure._portage_tmp_.${pid}" || die + sed -i \ + -e "1s:^#![[:space:]]*/bin/sh:#!$CONFIG_SHELL:" \ + "${ECONF_SOURCE}/configure" \ + || die "Substition of shebang in '${ECONF_SOURCE}/configure' failed" + touch -r "${ECONF_SOURCE}/configure._portage_tmp_.${pid}" "${ECONF_SOURCE}/configure" || die + rm -f "${ECONF_SOURCE}/configure._portage_tmp_.${pid}" fi if [ -e "${EPREFIX}"/usr/share/gnuconfig/ ]; then find "${WORKDIR}" -type f '(' \ -name config.guess -o -name config.sub ')' -print0 | \ while read -r -d $'\0' x ; do - vecho " * econf: updating ${x/${WORKDIR}\/} with ${EPREFIX}/usr/share/gnuconfig/${x##*/}" - cp -f "${EPREFIX}"/usr/share/gnuconfig/"${x##*/}" "${x}" + __vecho " * econf: updating ${x/${WORKDIR}\/} with ${EPREFIX}/usr/share/gnuconfig/${x##*/}" + # Make sure we do this atomically incase we're run in parallel. #487478 + cp -f "${EPREFIX}"/usr/share/gnuconfig/"${x##*/}" "${x}.${pid}" + mv -f "${x}.${pid}" "${x}" done fi - # EAPI=4 adds --disable-dependency-tracking to econf - if ! has "$EAPI" 0 1 2 3 3_pre2 && \ - "${ECONF_SOURCE}/configure" --help 2>/dev/null | \ - grep -q disable-dependency-tracking ; then - set -- --disable-dependency-tracking "$@" + if ___eapi_econf_passes_--disable-dependency-tracking || ___eapi_econf_passes_--disable-silent-rules; then + local conf_help=$("${ECONF_SOURCE}/configure" --help 2>/dev/null) + + if ___eapi_econf_passes_--disable-dependency-tracking; then + case "${conf_help}" in + *--disable-dependency-tracking*) + set -- --disable-dependency-tracking "$@" + ;; + esac + fi + + if ___eapi_econf_passes_--disable-silent-rules; then + case "${conf_help}" in + *--disable-silent-rules*) + set -- --disable-silent-rules "$@" + ;; + esac + fi fi # if the profile defines a location to install libs to aside from default, pass it on. @@ -433,16 +547,19 @@ econf() { if [[ -n ${ABI} && -n ${!LIBDIR_VAR} ]] ; then CONF_LIBDIR=${!LIBDIR_VAR} fi - if [[ -n ${CONF_LIBDIR} ]] && ! _hasgq --libdir=\* "$@" ; then - export CONF_PREFIX=$(_hasg --exec-prefix=\* "$@") - [[ -z ${CONF_PREFIX} ]] && CONF_PREFIX=$(_hasg --prefix=\* "$@") + if [[ -n ${CONF_LIBDIR} ]] && ! __hasgq --libdir=\* "$@" ; then + export CONF_PREFIX=$(__hasg --exec-prefix=\* "$@") + [[ -z ${CONF_PREFIX} ]] && CONF_PREFIX=$(__hasg --prefix=\* "$@") : ${CONF_PREFIX:=${EPREFIX}/usr} CONF_PREFIX=${CONF_PREFIX#*=} [[ ${CONF_PREFIX} != /* ]] && CONF_PREFIX="/${CONF_PREFIX}" [[ ${CONF_LIBDIR} != /* ]] && CONF_LIBDIR="/${CONF_LIBDIR}" - set -- --libdir="$(strip_duplicate_slashes ${CONF_PREFIX}${CONF_LIBDIR})" "$@" + set -- --libdir="$(__strip_duplicate_slashes "${CONF_PREFIX}${CONF_LIBDIR}")" "$@" fi + # Handle arguments containing quoted whitespace (see bug #457136). + eval "local -a EXTRA_ECONF=(${EXTRA_ECONF})" + set -- \ --prefix="${EPREFIX}"/usr \ ${CBUILD:+--build=${CBUILD}} \ @@ -454,8 +571,8 @@ econf() { --sysconfdir="${EPREFIX}"/etc \ --localstatedir="${EPREFIX}"/var/lib \ "$@" \ - ${EXTRA_ECONF} - vecho "${ECONF_SOURCE}/configure" "$@" + "${EXTRA_ECONF[@]}" + __vecho "${ECONF_SOURCE}/configure" "$@" if ! "${ECONF_SOURCE}/configure" "$@" ; then @@ -476,8 +593,9 @@ econf() { einstall() { # CONF_PREFIX is only set if they didn't pass in libdir above. local LOCAL_EXTRA_EINSTALL="${EXTRA_EINSTALL}" - [[ " ${FEATURES} " == *" force-prefix "* ]] || \ - case "$EAPI" in 0|1|2) local ED=${D} ;; esac + if ! ___eapi_has_prefix_variables; then + local ED=${D} + fi LIBDIR_VAR="LIBDIR_${ABI}" if [ -n "${ABI}" -a -n "${!LIBDIR_VAR}" ]; then CONF_LIBDIR="${!LIBDIR_VAR}" @@ -485,7 +603,7 @@ einstall() { unset LIBDIR_VAR if [ -n "${CONF_LIBDIR}" ] && [ "${CONF_PREFIX:+set}" = set ]; then EI_DESTLIBDIR="${D}/${CONF_PREFIX}/${CONF_LIBDIR}" - EI_DESTLIBDIR="$(strip_duplicate_slashes ${EI_DESTLIBDIR})" + EI_DESTLIBDIR="$(__strip_duplicate_slashes "${EI_DESTLIBDIR}")" LOCAL_EXTRA_EINSTALL="libdir=${EI_DESTLIBDIR} ${LOCAL_EXTRA_EINSTALL}" unset EI_DESTLIBDIR fi @@ -516,7 +634,7 @@ einstall() { fi } -_eapi0_pkg_nofetch() { +__eapi0_pkg_nofetch() { [ -z "${SRC_URI}" ] && return elog "The following are listed in SRC_URI for ${PN}:" @@ -526,55 +644,59 @@ _eapi0_pkg_nofetch() { done } -_eapi0_src_unpack() { +__eapi0_src_unpack() { [[ -n ${A} ]] && unpack ${A} } -_eapi0_src_compile() { +__eapi0_src_compile() { if [ -x ./configure ] ; then econf fi - _eapi2_src_compile + __eapi2_src_compile } -_eapi0_src_test() { +__eapi0_src_test() { # Since we don't want emake's automatic die # support (EAPI 4 and later), and we also don't # want the warning messages that it produces if # we call it in 'nonfatal' mode, we use emake_cmd # to emulate the desired parts of emake behavior. local emake_cmd="${MAKE:-make} ${MAKEOPTS} ${EXTRA_EMAKE}" - if $emake_cmd -j1 check -n &> /dev/null; then - vecho ">>> Test phase [check]: ${CATEGORY}/${PF}" - $emake_cmd -j1 check || \ + local internal_opts= + if ___eapi_default_src_test_disables_parallel_jobs; then + internal_opts+=" -j1" + fi + if $emake_cmd ${internal_opts} check -n &> /dev/null; then + __vecho ">>> Test phase [check]: ${CATEGORY}/${PF}" + $emake_cmd ${internal_opts} check || \ die "Make check failed. See above for details." - elif $emake_cmd -j1 test -n &> /dev/null; then - vecho ">>> Test phase [test]: ${CATEGORY}/${PF}" - $emake_cmd -j1 test || \ + elif $emake_cmd ${internal_opts} test -n &> /dev/null; then + __vecho ">>> Test phase [test]: ${CATEGORY}/${PF}" + $emake_cmd ${internal_opts} test || \ die "Make test failed. See above for details." else - vecho ">>> Test phase [none]: ${CATEGORY}/${PF}" + __vecho ">>> Test phase [none]: ${CATEGORY}/${PF}" fi } -_eapi1_src_compile() { - _eapi2_src_configure - _eapi2_src_compile +__eapi1_src_compile() { + __eapi2_src_configure + __eapi2_src_compile } -_eapi2_src_configure() { +__eapi2_src_configure() { if [[ -x ${ECONF_SOURCE:-.}/configure ]] ; then econf fi } -_eapi2_src_compile() { +__eapi2_src_compile() { if [ -f Makefile ] || [ -f GNUmakefile ] || [ -f makefile ]; then emake || die "emake failed" fi } -_eapi4_src_install() { +__eapi4_src_install() { if [[ -f Makefile || -f GNUmakefile || -f makefile ]] ; then emake DESTDIR="${D}" install fi @@ -593,71 +715,285 @@ _eapi4_src_install() { } # @FUNCTION: has_version -# @USAGE: <DEPEND ATOM> +# @USAGE: [--host-root] <DEPEND ATOM> # @DESCRIPTION: # Return true if given package is installed. Otherwise return false. # Callers may override the ROOT variable in order to match packages from an # alternative ROOT. has_version() { - local eroot - case "$EAPI" in - 0|1|2) - [[ " ${FEATURES} " == *" force-prefix "* ]] && \ - eroot=${ROOT%/}${EPREFIX}/ || eroot=${ROOT} - ;; - *) - eroot=${ROOT%/}${EPREFIX}/ - ;; - esac + local atom eroot host_root=false root=${ROOT} + if [[ $1 == --host-root ]] ; then + host_root=true + shift + fi + atom=$1 + shift + [ $# -gt 0 ] && die "${FUNCNAME[0]}: unused argument(s): $*" + + if ${host_root} ; then + if ! ___eapi_best_version_and_has_version_support_--host-root; then + die "${FUNCNAME[0]}: option --host-root is not supported with EAPI ${EAPI}" + fi + root=/ + fi + + if ___eapi_has_prefix_variables; then + # [[ ${root} == / ]] would be ambiguous here, + # since both prefixes can share root=/ while + # having different EPREFIX offsets. + if ${host_root} ; then + eroot=${root%/}${PORTAGE_OVERRIDE_EPREFIX}/ + else + eroot=${root%/}${EPREFIX}/ + fi + else + eroot=${root} + fi if [[ -n $PORTAGE_IPC_DAEMON ]] ; then - "$PORTAGE_BIN_PATH"/ebuild-ipc has_version "${eroot}" "$1" + "$PORTAGE_BIN_PATH"/ebuild-ipc has_version "${eroot}" "${atom}" else - PYTHONPATH=${PORTAGE_PYM_PATH}${PYTHONPATH:+:}${PYTHONPATH} \ - "${PORTAGE_PYTHON:-/usr/bin/python}" "${PORTAGE_BIN_PATH}/portageq" has_version "${eroot}" "$1" + "${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" has_version "${eroot}" "${atom}" fi local retval=$? case "${retval}" in 0|1) return ${retval} ;; + 2) + die "${FUNCNAME[0]}: invalid atom: ${atom}" + ;; *) - die "unexpected portageq exit code: ${retval}" + if [[ -n ${PORTAGE_IPC_DAEMON} ]]; then + die "${FUNCNAME[0]}: unexpected ebuild-ipc exit code: ${retval}" + else + die "${FUNCNAME[0]}: unexpected portageq exit code: ${retval}" + fi ;; esac } # @FUNCTION: best_version -# @USAGE: <DEPEND ATOM> +# @USAGE: [--host-root] <DEPEND ATOM> # @DESCRIPTION: # Returns the best/most-current match. # Callers may override the ROOT variable in order to match packages from an # alternative ROOT. best_version() { - local eroot - case "$EAPI" in - 0|1|2) - [[ " ${FEATURES} " == *" force-prefix "* ]] && \ - eroot=${ROOT%/}${EPREFIX}/ || eroot=${ROOT} - ;; - *) - eroot=${ROOT%/}${EPREFIX}/ - ;; - esac + local atom eroot host_root=false root=${ROOT} + if [[ $1 == --host-root ]] ; then + host_root=true + shift + fi + atom=$1 + shift + [ $# -gt 0 ] && die "${FUNCNAME[0]}: unused argument(s): $*" + + if ${host_root} ; then + if ! ___eapi_best_version_and_has_version_support_--host-root; then + die "${FUNCNAME[0]}: option --host-root is not supported with EAPI ${EAPI}" + fi + root=/ + fi + + if ___eapi_has_prefix_variables; then + # [[ ${root} == / ]] would be ambiguous here, + # since both prefixes can share root=/ while + # having different EPREFIX offsets. + if ${host_root} ; then + eroot=${root%/}${PORTAGE_OVERRIDE_EPREFIX}/ + else + eroot=${root%/}${EPREFIX}/ + fi + else + eroot=${root} + fi if [[ -n $PORTAGE_IPC_DAEMON ]] ; then - "$PORTAGE_BIN_PATH"/ebuild-ipc best_version "${eroot}" "$1" + "$PORTAGE_BIN_PATH"/ebuild-ipc best_version "${eroot}" "${atom}" else - PYTHONPATH=${PORTAGE_PYM_PATH}${PYTHONPATH:+:}${PYTHONPATH} \ - "${PORTAGE_PYTHON:-/usr/bin/python}" "${PORTAGE_BIN_PATH}/portageq" best_version "${eroot}" "$1" + "${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" best_version "${eroot}" "${atom}" fi local retval=$? case "${retval}" in 0|1) return ${retval} ;; + 2) + die "${FUNCNAME[0]}: invalid atom: ${atom}" + ;; *) - die "unexpected portageq exit code: ${retval}" + if [[ -n ${PORTAGE_IPC_DAEMON} ]]; then + die "${FUNCNAME[0]}: unexpected ebuild-ipc exit code: ${retval}" + else + die "${FUNCNAME[0]}: unexpected portageq exit code: ${retval}" + fi ;; esac } + +if ___eapi_has_master_repositories; then + master_repositories() { + local output repository=$1 retval + shift + [[ $# -gt 0 ]] && die "${FUNCNAME[0]}: unused argument(s): $*" + + if [[ -n ${PORTAGE_IPC_DAEMON} ]]; then + "${PORTAGE_BIN_PATH}/ebuild-ipc" master_repositories "${EROOT}" "${repository}" + else + output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" master_repositories "${EROOT}" "${repository}") + fi + retval=$? + [[ -n ${output} ]] && echo "${output}" + case "${retval}" in + 0|1) + return ${retval} + ;; + 2) + die "${FUNCNAME[0]}: invalid repository: ${repository}" + ;; + *) + if [[ -n ${PORTAGE_IPC_DAEMON} ]]; then + die "${FUNCNAME[0]}: unexpected ebuild-ipc exit code: ${retval}" + else + die "${FUNCNAME[0]}: unexpected portageq exit code: ${retval}" + fi + ;; + esac + } +fi + +if ___eapi_has_repository_path; then + repository_path() { + local output repository=$1 retval + shift + [[ $# -gt 0 ]] && die "${FUNCNAME[0]}: unused argument(s): $*" + + if [[ -n ${PORTAGE_IPC_DAEMON} ]]; then + "${PORTAGE_BIN_PATH}/ebuild-ipc" repository_path "${EROOT}" "${repository}" + else + output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" get_repo_path "${EROOT}" "${repository}") + fi + retval=$? + [[ -n ${output} ]] && echo "${output}" + case "${retval}" in + 0|1) + return ${retval} + ;; + 2) + die "${FUNCNAME[0]}: invalid repository: ${repository}" + ;; + *) + if [[ -n ${PORTAGE_IPC_DAEMON} ]]; then + die "${FUNCNAME[0]}: unexpected ebuild-ipc exit code: ${retval}" + else + die "${FUNCNAME[0]}: unexpected portageq exit code: ${retval}" + fi + ;; + esac + } +fi + +if ___eapi_has_available_eclasses; then + available_eclasses() { + local output repository=${PORTAGE_REPO_NAME} retval + [[ $# -gt 0 ]] && die "${FUNCNAME[0]}: unused argument(s): $*" + + if [[ -n ${PORTAGE_IPC_DAEMON} ]]; then + "${PORTAGE_BIN_PATH}/ebuild-ipc" available_eclasses "${EROOT}" "${repository}" + else + output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" available_eclasses "${EROOT}" "${repository}") + fi + retval=$? + [[ -n ${output} ]] && echo "${output}" + case "${retval}" in + 0|1) + return ${retval} + ;; + 2) + die "${FUNCNAME[0]}: invalid repository: ${repository}" + ;; + *) + if [[ -n ${PORTAGE_IPC_DAEMON} ]]; then + die "${FUNCNAME[0]}: unexpected ebuild-ipc exit code: ${retval}" + else + die "${FUNCNAME[0]}: unexpected portageq exit code: ${retval}" + fi + ;; + esac + } +fi + +if ___eapi_has_eclass_path; then + eclass_path() { + local eclass=$1 output repository=${PORTAGE_REPO_NAME} retval + shift + [[ $# -gt 0 ]] && die "${FUNCNAME[0]}: unused argument(s): $*" + + if [[ -n ${PORTAGE_IPC_DAEMON} ]]; then + "${PORTAGE_BIN_PATH}/ebuild-ipc" eclass_path "${EROOT}" "${repository}" "${eclass}" + else + output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" eclass_path "${EROOT}" "${repository}" "${eclass}") + fi + retval=$? + [[ -n ${output} ]] && echo "${output}" + case "${retval}" in + 0|1) + return ${retval} + ;; + 2) + die "${FUNCNAME[0]}: invalid repository: ${repository}" + ;; + *) + if [[ -n ${PORTAGE_IPC_DAEMON} ]]; then + die "${FUNCNAME[0]}: unexpected ebuild-ipc exit code: ${retval}" + else + die "${FUNCNAME[0]}: unexpected portageq exit code: ${retval}" + fi + ;; + esac + } +fi + +if ___eapi_has_license_path; then + license_path() { + local license=$1 output repository=${PORTAGE_REPO_NAME} retval + shift + [[ $# -gt 0 ]] && die "${FUNCNAME[0]}: unused argument(s): $*" + + if [[ -n ${PORTAGE_IPC_DAEMON} ]]; then + "${PORTAGE_BIN_PATH}/ebuild-ipc" license_path "${EROOT}" "${repository}" "${license}" + else + output=$("${PORTAGE_BIN_PATH}/ebuild-helpers/portageq" license_path "${EROOT}" "${repository}" "${license}") + fi + retval=$? + [[ -n ${output} ]] && echo "${output}" + case "${retval}" in + 0|1) + return ${retval} + ;; + 2) + die "${FUNCNAME[0]}: invalid repository: ${repository}" + ;; + *) + if [[ -n ${PORTAGE_IPC_DAEMON} ]]; then + die "${FUNCNAME[0]}: unexpected ebuild-ipc exit code: ${retval}" + else + die "${FUNCNAME[0]}: unexpected portageq exit code: ${retval}" + fi + ;; + esac + } +fi + +if ___eapi_has_package_manager_build_user; then + package_manager_build_user() { + echo "${PORTAGE_BUILD_USER}" + } +fi + +if ___eapi_has_package_manager_build_group; then + package_manager_build_group() { + echo "${PORTAGE_BUILD_GROUP}" + } +fi diff --git a/bin/portageq b/bin/portageq index d9abb0bad..79818f679 100755 --- a/bin/portageq +++ b/bin/portageq @@ -1,15 +1,15 @@ -#!/usr/bin/python -O -# Copyright 1999-2012 Gentoo Foundation +#!/usr/bin/python -bO +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -from __future__ import print_function +from __future__ import print_function, unicode_literals import signal import sys # This block ensures that ^C interrupts are handled quietly. try: - def exithandler(signum, frame): + def exithandler(signum, _frame): signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGTERM, signal.SIG_IGN) sys.exit(128 + signum) @@ -34,23 +34,22 @@ if os.environ.get("SANDBOX_ON") == "1": ":".join(filter(None, sandbox_write)) del sandbox_write -try: - import portage -except ImportError: - sys.path.insert(0, pym_path) - import portage -del pym_path - +sys.path.insert(0, pym_path) +import portage +portage._internal_caller = True from portage import os from portage.eapi import eapi_has_repo_deps from portage.util import writemsg, writemsg_stdout -from portage.output import colormap +from portage.util._argparse import ArgumentParser portage.proxy.lazyimport.lazyimport(globals(), + 're', 'subprocess', '_emerge.Package:Package', '_emerge.RootConfig:RootConfig', + '_emerge.is_valid_package_atom:insert_category_into_atom', 'portage.dbapi._expand_new_virt:expand_new_virt', 'portage._sets.base:InternalPackageSet', + 'portage.xml.metadata:MetaDataXML' ) def eval_atom_use(atom): @@ -59,6 +58,10 @@ def eval_atom_use(atom): atom = atom.evaluate_conditionals(use) return atom +def uses_eroot(function): + function.uses_eroot = True + return function + #----------------------------------------------------------------------------- # # To add functionality to this tool, add a function below. @@ -80,13 +83,14 @@ def eval_atom_use(atom): # and will automaticly add a command by the same name as the function! # +@uses_eroot def has_version(argv): """<eroot> <category/package> Return code 0 if it's available, 1 otherwise. """ if (len(argv) < 2): print("ERROR: insufficient parameters!") - return 2 + return 3 warnings = [] @@ -105,9 +109,7 @@ def has_version(argv): try: atom = portage.dep.Atom(argv[1], allow_repo=allow_repo, eapi=eapi) except portage.exception.InvalidAtom as e: - warnings.append( - portage._unicode_decode("QA Notice: %s: %s") % \ - ('has_version', e)) + warnings.append("QA Notice: %s: %s" % ('has_version', e)) atom = eval_atom_use(atom) if warnings: @@ -125,16 +127,16 @@ def has_version(argv): portage.writemsg("ERROR: Invalid atom: '%s'\n" % argv[1], noiselevel=-1) return 2 -has_version.uses_root = True +@uses_eroot def best_version(argv): """<eroot> <category/package> Returns category/package-version (without .ebuild). """ if (len(argv) < 2): print("ERROR: insufficient parameters!") - return 2 + return 3 warnings = [] @@ -153,9 +155,7 @@ def best_version(argv): try: atom = portage.dep.Atom(argv[1], allow_repo=allow_repo, eapi=eapi) except portage.exception.InvalidAtom as e: - warnings.append( - portage._unicode_decode("QA Notice: %s: %s") % \ - ('best_version', e)) + warnings.append("QA Notice: %s: %s" % ('best_version', e)) atom = eval_atom_use(atom) if warnings: @@ -166,9 +166,9 @@ def best_version(argv): print(portage.best(mylist)) except KeyError: return 1 -best_version.uses_root = True +@uses_eroot def mass_best_version(argv): """<eroot> [<category/package>]+ Returns category/package-version (without .ebuild). @@ -178,23 +178,25 @@ def mass_best_version(argv): return 2 try: for pack in argv[1:]: - mylist=portage.db[argv[0]]["vartree"].dbapi.match(pack) - print(pack+":"+portage.best(mylist)) + mylist = portage.db[argv[0]]['vartree'].dbapi.match(pack) + print('%s:%s' % (pack, portage.best(mylist))) except KeyError: return 1 -mass_best_version.uses_root = True + +@uses_eroot def metadata(argv): if (len(argv) < 4): - print("ERROR: insufficient parameters!", file=sys.stderr) + print('ERROR: insufficient parameters!', file=sys.stderr) return 2 eroot, pkgtype, pkgspec = argv[0:3] metakeys = argv[3:] type_map = { - "ebuild":"porttree", - "binary":"bintree", - "installed":"vartree"} + 'ebuild': 'porttree', + 'binary': 'bintree', + 'installed': 'vartree' + } if pkgtype not in type_map: print("Unrecognized package type: '%s'" % pkgtype, file=sys.stderr) return 1 @@ -202,9 +204,9 @@ def metadata(argv): repo = portage.dep.dep_getrepo(pkgspec) pkgspec = portage.dep.remove_slot(pkgspec) try: - values = trees[eroot][type_map[pkgtype]].dbapi.aux_get( - pkgspec, metakeys, myrepo=repo) - writemsg_stdout(''.join('%s\n' % x for x in values), noiselevel=-1) + values = trees[eroot][type_map[pkgtype]].dbapi.aux_get( + pkgspec, metakeys, myrepo=repo) + writemsg_stdout(''.join('%s\n' % x for x in values), noiselevel=-1) except KeyError: print("Package not found: '%s'" % pkgspec, file=sys.stderr) return 1 @@ -216,8 +218,8 @@ Available keys: %s """ % ','.join(sorted(x for x in portage.auxdbkeys \ if not x.startswith('UNUSED_'))) -metadata.uses_root = True +@uses_eroot def contents(argv): """<eroot> <category/package> List the files that are installed for a given package, with @@ -238,8 +240,9 @@ def contents(argv): treetype="vartree", vartree=vartree) writemsg_stdout(''.join('%s\n' % x for x in sorted(db.getcontents())), noiselevel=-1) -contents.uses_root = True + +@uses_eroot def owners(argv): """<eroot> [<filename>]+ Given a list of files, print the packages that own the files and which @@ -253,7 +256,6 @@ def owners(argv): sys.stderr.flush() return 2 - from portage import catsplit, dblink eroot = argv[0] vardb = portage.db[eroot]["vartree"].dbapi root = portage.settings['ROOT'] @@ -319,8 +321,8 @@ def owners(argv): return 0 return 1 -owners.uses_root = True +@uses_eroot def is_protected(argv): """<eroot> <filename> Given a single filename, return code 0 if it's protected, 1 otherwise. @@ -366,8 +368,8 @@ def is_protected(argv): return 0 return 1 -is_protected.uses_root = True +@uses_eroot def filter_protected(argv): """<eroot> Read filenames from stdin and write them to stdout if they are protected. @@ -395,7 +397,6 @@ def filter_protected(argv): settings.get("CONFIG_PROTECT_MASK", "")) protect_obj = ConfigProtect(root, protect, protect_mask) - protected = 0 errors = 0 for line in sys.stdin: @@ -417,7 +418,6 @@ def filter_protected(argv): continue if protect_obj.isprotected(f): - protected += 1 out.write("%s\n" % filename) out.flush() @@ -426,8 +426,8 @@ def filter_protected(argv): return 0 -filter_protected.uses_root = True +@uses_eroot def best_visible(argv): """<eroot> [pkgtype] <atom> Returns category/package-version (without .ebuild). @@ -465,8 +465,7 @@ def best_visible(argv): noiselevel=-1) return 2 - root_config = RootConfig(portage.settings, - portage.db[eroot], None) + root_config = RootConfig(portage.settings, portage.db[eroot], None) if hasattr(db, "xmatch"): cpv_list = db.xmatch("match-all-cpv-only", atom) @@ -508,11 +507,11 @@ def best_visible(argv): writemsg_stdout("\n", noiselevel=-1) return 1 -best_visible.uses_root = True +@uses_eroot def mass_best_visible(argv): - """<root> [<type>] [<category/package>]+ + """<eroot> [<type>] [<category/package>]+ Returns category/package-version (without .ebuild). The pkgtype argument defaults to "ebuild" if unspecified, otherwise it must be one of ebuild, binary, or installed. @@ -535,9 +534,9 @@ def mass_best_visible(argv): best_visible([root, pkgtype, pack]) except KeyError: return 1 -mass_best_visible.uses_root = True +@uses_eroot def all_best_visible(argv): """<eroot> Returns all best_visible packages (without .ebuild). @@ -552,9 +551,9 @@ def all_best_visible(argv): mybest=portage.best(portage.db[argv[0]]["porttree"].dbapi.match(pkg)) if mybest: print(mybest) -all_best_visible.uses_root = True +@uses_eroot def match(argv): """<eroot> <atom> Returns a \\n separated list of category/package-version. @@ -601,8 +600,9 @@ def match(argv): results = vardb.match(atom) for cpv in results: print(cpv) -match.uses_root = True + +@uses_eroot def expand_virtual(argv): """<eroot> <atom> Returns a \\n separated list of atoms expanded from a @@ -637,9 +637,8 @@ def expand_virtual(argv): return os.EX_OK -expand_virtual.uses_root = True -def vdb_path(argv): +def vdb_path(_argv): """ Returns the path used for the var(installed) package database for the set environment/configuration options. @@ -649,56 +648,79 @@ def vdb_path(argv): out.flush() return os.EX_OK -def gentoo_mirrors(argv): +def gentoo_mirrors(_argv): """ Returns the mirrors set to use in the portage configuration. """ print(portage.settings["GENTOO_MIRRORS"]) -def portdir(argv): +@uses_eroot +def repositories_configuration(argv): + """<eroot> + Returns the configuration of repositories. + """ + if len(argv) < 1: + print("ERROR: insufficient parameters!", file=sys.stderr) + return 3 + sys.stdout.write(portage.db[argv[0]]["vartree"].settings.repositories.config_string()) + sys.stdout.flush() + +@uses_eroot +def repos_config(argv): + """ + <eroot> + This is an alias for the repositories_configuration command. + """ + return repositories_configuration(argv) + +def portdir(_argv): """ Returns the PORTDIR path. + Deprecated in favor of repositories_configuration command. """ + print("WARNING: 'portageq portdir' is deprecated. Use 'portageq repositories_configuration' instead.", file=sys.stderr) print(portage.settings["PORTDIR"]) -def config_protect(argv): +def config_protect(_argv): """ Returns the CONFIG_PROTECT paths. """ print(portage.settings["CONFIG_PROTECT"]) -def config_protect_mask(argv): +def config_protect_mask(_argv): """ Returns the CONFIG_PROTECT_MASK paths. """ print(portage.settings["CONFIG_PROTECT_MASK"]) -def portdir_overlay(argv): +def portdir_overlay(_argv): """ Returns the PORTDIR_OVERLAY path. + Deprecated in favor of repositories_configuration command. """ + print("WARNING: 'portageq portdir_overlay' is deprecated. Use 'portageq repositories_configuration' instead.", file=sys.stderr) print(portage.settings["PORTDIR_OVERLAY"]) -def pkgdir(argv): +def pkgdir(_argv): """ Returns the PKGDIR path. """ print(portage.settings["PKGDIR"]) -def distdir(argv): +def distdir(_argv): """ Returns the DISTDIR path. """ print(portage.settings["DISTDIR"]) -def colormap(argv): +def colormap(_argv): """ Display the color.map as environment variables. """ @@ -719,11 +741,15 @@ def envvar(argv): return 2 for arg in argv: + if arg in ("PORTDIR", "PORTDIR_OVERLAY", "SYNC"): + print("WARNING: 'portageq envvar %s' is deprecated. Use 'portageq repositories_configuration' instead." % arg, file=sys.stderr) if verbose: - print(arg +"='"+ portage.settings[arg] +"'") + print(arg + "=" + portage._shell_quote(portage.settings[arg])) else: print(portage.settings[arg]) + +@uses_eroot def get_repos(argv): """<eroot> Returns all repos with names (repo_name file) argv[0] = $EROOT @@ -731,25 +757,137 @@ def get_repos(argv): if len(argv) < 1: print("ERROR: insufficient parameters!") return 2 - print(" ".join(portage.db[argv[0]]["porttree"].dbapi.getRepositories())) + print(" ".join(reversed(portage.db[argv[0]]["vartree"].settings.repositories.prepos_order))) + + +@uses_eroot +def master_repositories(argv): + """<eroot> <repo_id>+ + Returns space-separated list of master repositories for specified repository. + """ + if len(argv) < 2: + print("ERROR: insufficient parameters!", file=sys.stderr) + return 3 + for arg in argv[1:]: + if portage.dep._repo_name_re.match(arg) is None: + print("ERROR: invalid repository: %s" % arg, file=sys.stderr) + return 2 + try: + repo = portage.db[argv[0]]["vartree"].settings.repositories[arg] + except KeyError: + print("") + return 1 + else: + print(" ".join(x.name for x in repo.masters)) -get_repos.uses_root = True +@uses_eroot +def master_repos(argv): + """<eroot> <repo_id>+ + This is an alias for the master_repositories command. + """ + return master_repositories(argv) +@uses_eroot def get_repo_path(argv): """<eroot> <repo_id>+ Returns the path to the repo named argv[1], argv[0] = $EROOT """ if len(argv) < 2: - print("ERROR: insufficient parameters!") - return 2 + print("ERROR: insufficient parameters!", file=sys.stderr) + return 3 for arg in argv[1:]: - path = portage.db[argv[0]]["porttree"].dbapi.getRepositoryPath(arg) + if portage.dep._repo_name_re.match(arg) is None: + print("ERROR: invalid repository: %s" % arg, file=sys.stderr) + return 2 + path = portage.db[argv[0]]["vartree"].settings.repositories.treemap.get(arg) if path is None: - path = "" + print("") + return 1 print(path) -get_repo_path.uses_root = True +@uses_eroot +def available_eclasses(argv): + """<eroot> <repo_id>+ + Returns space-separated list of available eclasses for specified repository. + """ + if len(argv) < 2: + print("ERROR: insufficient parameters!", file=sys.stderr) + return 3 + for arg in argv[1:]: + if portage.dep._repo_name_re.match(arg) is None: + print("ERROR: invalid repository: %s" % arg, file=sys.stderr) + return 2 + try: + repo = portage.db[argv[0]]["vartree"].settings.repositories[arg] + except KeyError: + print("") + return 1 + else: + print(" ".join(sorted(repo.eclass_db.eclasses))) + + +@uses_eroot +def eclass_path(argv): + """<eroot> <repo_id> <eclass>+ + Returns the path to specified eclass for specified repository. + """ + if len(argv) < 3: + print("ERROR: insufficient parameters!", file=sys.stderr) + return 3 + if portage.dep._repo_name_re.match(argv[1]) is None: + print("ERROR: invalid repository: %s" % argv[1], file=sys.stderr) + return 2 + try: + repo = portage.db[argv[0]]["vartree"].settings.repositories[argv[1]] + except KeyError: + print("") + return 1 + else: + retval = 0 + for arg in argv[2:]: + try: + eclass = repo.eclass_db.eclasses[arg] + except KeyError: + print("") + retval = 1 + else: + print(eclass.location) + return retval + + +@uses_eroot +def license_path(argv): + """<eroot> <repo_id> <license>+ + Returns the path to specified license for specified repository. + """ + if len(argv) < 3: + print("ERROR: insufficient parameters!", file=sys.stderr) + return 3 + if portage.dep._repo_name_re.match(argv[1]) is None: + print("ERROR: invalid repository: %s" % argv[1], file=sys.stderr) + return 2 + try: + repo = portage.db[argv[0]]["vartree"].settings.repositories[argv[1]] + except KeyError: + print("") + return 1 + else: + retval = 0 + for arg in argv[2:]: + eclass_path = "" + paths = reversed([os.path.join(x.location, 'licenses', arg) for x in list(repo.masters) + [repo]]) + for path in paths: + if os.path.exists(path): + eclass_path = path + break + if eclass_path == "": + retval = 1 + print(eclass_path) + return retval + + +@uses_eroot def list_preserved_libs(argv): """<eroot> Print a list of libraries preserved during a package update in the form @@ -771,21 +909,296 @@ def list_preserved_libs(argv): msg.append('\n') writemsg_stdout(''.join(msg), noiselevel=-1) return rValue -list_preserved_libs.uses_root = True + + +class MaintainerEmailMatcher(object): + def __init__(self, maintainer_emails): + self._re = re.compile("^(%s)$" % "|".join(maintainer_emails)) + + def __call__(self, metadata_xml): + match = False + matcher = self._re.match + for x in metadata_xml.maintainers(): + if x.email is not None and matcher(x.email) is not None: + match = True + break + return match + +class HerdMatcher(object): + def __init__(self, herds): + self._herds = frozenset(herds) + + def __call__(self, metadata_xml): + herds = self._herds + return any(x in herds for x in metadata_xml.herds()) + + +def pquery(parser, opts, args): + """[options] [atom]+ + Emulates a subset of Pkgcore's pquery tool. + """ + + portdb = portage.db[portage.root]['porttree'].dbapi + root_config = RootConfig(portdb.settings, + portage.db[portage.root], None) + + def _pkg(cpv, repo_name): + try: + metadata = dict(zip( + Package.metadata_keys, + portdb.aux_get(cpv, + Package.metadata_keys, + myrepo=repo_name))) + except KeyError: + raise portage.exception.PackageNotFound(cpv) + return Package(built=False, cpv=cpv, + installed=False, metadata=metadata, + root_config=root_config, + type_name="ebuild") + + need_metadata = False + atoms = [] + for arg in args: + if "/" not in arg.split(":")[0]: + atom = insert_category_into_atom(arg, '*') + if atom is None: + writemsg("ERROR: Invalid atom: '%s'\n" % arg, + noiselevel=-1) + return 2 + else: + atom = arg + + try: + atom = portage.dep.Atom(atom, allow_wildcard=True, allow_repo=True) + except portage.exception.InvalidAtom: + writemsg("ERROR: Invalid atom: '%s'\n" % arg, + noiselevel=-1) + return 2 + + if atom.slot is not None: + need_metadata = True + + atoms.append(atom) + + if "*/*" in atoms: + del atoms[:] + need_metadata = False + + if not opts.no_filters: + need_metadata = True + + xml_matchers = [] + if opts.maintainer_email: + maintainer_emails = [] + for x in opts.maintainer_email: + maintainer_emails.extend(x.split(",")) + xml_matchers.append(MaintainerEmailMatcher(maintainer_emails)) + if opts.herd is not None: + herds = [] + for x in opts.herd: + herds.extend(x.split(",")) + xml_matchers.append(HerdMatcher(herds)) + + repos = [] + if opts.all_repos: + repos.extend(portdb.repositories.get_repo_for_location(location) + for location in portdb.porttrees) + elif opts.repo is not None: + repos.append(portdb.repositories[opts.repo]) + else: + repos.append(portdb.repositories.mainRepo()) + + if not atoms: + names = None + categories = list(portdb.categories) + else: + category_wildcard = False + name_wildcard = False + categories = [] + names = [] + for atom in atoms: + category, name = portage.catsplit(atom.cp) + categories.append(category) + names.append(name) + if "*" in category: + category_wildcard = True + if "*" in name: + name_wildcard = True + + if category_wildcard: + categories = list(portdb.categories) + else: + categories = list(set(categories)) + + if name_wildcard: + names = None + else: + names = sorted(set(names)) + + no_version = opts.no_version + categories.sort() + + for category in categories: + if names is None: + cp_list = portdb.cp_all(categories=(category,)) + else: + cp_list = [category + "/" + name for name in names] + for cp in cp_list: + matches = [] + for repo in repos: + match = True + if xml_matchers: + metadata_xml_path = os.path.join( + repo.location, cp, 'metadata.xml') + try: + metadata_xml = MetaDataXML(metadata_xml_path, None) + except (EnvironmentError, SyntaxError): + match = False + else: + for matcher in xml_matchers: + if not matcher(metadata_xml): + match = False + break + if not match: + continue + cpv_list = portdb.cp_list(cp, mytree=[repo.location]) + if atoms: + for cpv in cpv_list: + pkg = None + for atom in atoms: + if atom.repo is not None and \ + atom.repo != repo.name: + continue + if not portage.match_from_list(atom, [cpv]): + continue + if need_metadata: + if pkg is None: + try: + pkg = _pkg(cpv, repo.name) + except portage.exception.PackageNotFound: + continue + + if not (opts.no_filters or pkg.visible): + continue + if not portage.match_from_list(atom, [pkg]): + continue + matches.append(cpv) + break + if no_version and matches: + break + elif opts.no_filters: + matches.extend(cpv_list) + else: + for cpv in cpv_list: + try: + pkg = _pkg(cpv, repo.name) + except portage.exception.PackageNotFound: + continue + else: + if pkg.visible: + matches.append(cpv) + if no_version: + break + + if no_version and matches: + break + + if not matches: + continue + + if no_version: + writemsg_stdout("%s\n" % (cp,), noiselevel=-1) + else: + matches = list(set(matches)) + portdb._cpv_sort_ascending(matches) + for cpv in matches: + writemsg_stdout("%s\n" % (cpv,), noiselevel=-1) + + return os.EX_OK + #----------------------------------------------------------------------------- # # DO NOT CHANGE CODE BEYOND THIS POINT - IT'S NOT NEEDED! # -if not portage.const._ENABLE_PRESERVE_LIBS: - del list_preserved_libs - -non_commands = frozenset(['elog', 'eval_atom_use', - 'exithandler', 'expand_new_virt', 'main', - 'usage', 'writemsg', 'writemsg_stdout']) +non_commands = frozenset(['elog', 'eval_atom_use', 'exithandler', 'main', 'usage', 'uses_eroot']) commands = sorted(k for k, v in globals().items() \ - if k not in non_commands and isinstance(v, types.FunctionType)) + if k not in non_commands and isinstance(v, types.FunctionType) and v.__module__ == "__main__") + + +def add_pquery_arguments(parser): + pquery_option_groups = ( + ( + 'Repository matching options', + ( + { + "longopt": "--no-filters", + "action": "store_true", + "help": "no visibility filters (ACCEPT_KEYWORDS, package masking, etc)" + }, + { + "longopt": "--repo", + "help": "repo to use (default is PORTDIR if omitted)" + }, + { + "longopt": "--all-repos", + "help": "search all repos" + } + ) + ), + ( + 'Package matching options', + ( + { + "longopt": "--herd", + "action": "append", + "help": "exact match on a herd" + }, + { + "longopt": "--maintainer-email", + "action": "append", + "help": "comma-separated list of maintainer email regexes to search for" + } + ) + ), + ( + 'Output formatting', + ( + { + "shortopt": "-n", + "longopt": "--no-version", + "action": "store_true", + "help": "collapse multiple matching versions together" + }, + ) + ), + ) + + for group_title, opt_data in pquery_option_groups: + arg_group = parser.add_argument_group(group_title) + for opt_info in opt_data: + pargs = [] + try: + pargs.append(opt_info["shortopt"]) + except KeyError: + pass + try: + pargs.append(opt_info["longopt"]) + except KeyError: + pass + + kwargs = {} + try: + kwargs["action"] = opt_info["action"] + except KeyError: + pass + try: + kwargs["help"] = opt_info["help"] + except KeyError: + pass + arg_group.add_argument(*pargs, **portage._native_kwargs(kwargs)) + def usage(argv): print(">>> Portage information query tool") @@ -798,7 +1211,7 @@ def usage(argv): # Show our commands -- we do this by scanning the functions in this # file, and formatting each functions documentation. # - help_mode = '--help' in sys.argv + help_mode = '--help' in argv for name in commands: # Drop non-functions obj = globals()[name] @@ -812,12 +1225,21 @@ def usage(argv): lines = doc.lstrip("\n").split("\n") print(" " + name + " " + lines[0].strip()) - if (len(sys.argv) > 1): + if len(argv) > 1: if (not help_mode): lines = lines[:-1] for line in lines[1:]: print(" " + line.strip()) - if (len(sys.argv) == 1): + + print() + print('Pkgcore pquery compatible options:') + print() + parser = ArgumentParser(add_help=False, + usage='portageq pquery [options] [atom ...]') + add_pquery_arguments(parser) + parser.print_help() + + if len(argv) == 1: print("\nRun portageq with --help for info") atom_validate_strict = "EBUILD_PHASE" in os.environ @@ -836,52 +1258,84 @@ else: def elog(elog_funcname, lines): pass -def main(): +def main(argv): + + argv = portage._decode_argv(argv) nocolor = os.environ.get('NOCOLOR') if nocolor in ('yes', 'true'): portage.output.nocolor() - if len(sys.argv) < 2: - usage(sys.argv) - sys.exit(os.EX_USAGE) + parser = ArgumentParser(add_help=False) - for x in sys.argv: - if x in ("-h", "--help"): - usage(sys.argv) - sys.exit(os.EX_OK) - elif x == "--version": - print("Portage", portage.VERSION) - sys.exit(os.EX_OK) - - cmd = sys.argv[1] - function = globals().get(cmd) - if function is None or cmd not in commands: - usage(sys.argv) + # used by envvar + parser.add_argument("-v", dest="verbose", action="store_true") + + actions = parser.add_argument_group('Actions') + actions.add_argument("-h", "--help", action="store_true") + actions.add_argument("--version", action="store_true") + + add_pquery_arguments(parser) + + opts, args = parser.parse_known_args(argv[1:]) + + if opts.help: + usage(argv) + return os.EX_OK + elif opts.version: + print("Portage", portage.VERSION) + return os.EX_OK + + cmd = None + if args and args[0] in commands: + cmd = args[0] + + if cmd == 'pquery': + cmd = None + args = args[1:] + + if cmd is None: + return pquery(parser, opts, args) + + if opts.verbose: + # used by envvar + args.append("-v") + + argv = argv[:1] + args + + if len(argv) < 2: + usage(argv) sys.exit(os.EX_USAGE) + function = globals()[cmd] - uses_root = getattr(function, "uses_root", False) and len(sys.argv) > 2 - if uses_root: - if not os.path.isdir(sys.argv[2]): - sys.stderr.write("Not a directory: '%s'\n" % sys.argv[2]) + uses_eroot = getattr(function, "uses_eroot", False) and len(argv) > 2 + if uses_eroot: + if not os.path.isdir(argv[2]): + sys.stderr.write("Not a directory: '%s'\n" % argv[2]) sys.stderr.write("Run portageq with --help for info\n") sys.stderr.flush() sys.exit(os.EX_USAGE) - eprefix = portage.const.EPREFIX - eroot = portage.util.normalize_path(sys.argv[2]) + eprefix = portage.settings["EPREFIX"] + eroot = portage.util.normalize_path(argv[2]) + if eprefix: - root = eroot[:1-len(eprefix)] + if not eroot.endswith(eprefix): + sys.stderr.write("ERROR: This version of portageq" + " only supports <eroot>s ending in" + " '%s'. The provided <eroot>, '%s'," + " doesn't.\n" % (eprefix, eroot)) + sys.stderr.flush() + sys.exit(os.EX_USAGE) + root = eroot[:1 - len(eprefix)] else: root = eroot + os.environ["ROOT"] = root - args = sys.argv[2:] - if args and isinstance(args[0], bytes): - for i in range(len(args)): - args[i] = portage._unicode_decode(args[i]) + args = argv[2:] try: - if uses_root: + if uses_eroot: args[0] = portage.settings['EROOT'] retval = function(args) if retval: @@ -902,6 +1356,7 @@ def main(): portage.writemsg("\nPlease use a more specific atom.\n", noiselevel=-1) sys.exit(1) -main() +if __name__ == '__main__': + sys.exit(main(sys.argv)) #----------------------------------------------------------------------------- diff --git a/bin/quickpkg b/bin/quickpkg index 76259c5c1..90277ade3 100755 --- a/bin/quickpkg +++ b/bin/quickpkg @@ -1,23 +1,20 @@ -#!/usr/bin/python -# Copyright 1999-2012 Gentoo Foundation +#!/usr/bin/python -b +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import print_function import errno import math -import optparse import signal import sys import tarfile -try: - import portage -except ImportError: - from os import path as osp - sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) - import portage - +from os import path as osp +pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym") +sys.path.insert(0, pym_path) +import portage +portage._internal_caller = True from portage import os from portage import xpak from portage.dbapi.dep_expand import dep_expand @@ -28,6 +25,7 @@ from portage.util import ConfigProtect, ensure_dirs, shlex_split from portage.dbapi.vartree import dblink, tar_contents from portage.checksum import perform_md5 from portage._sets import load_default_config, SETPREFIX +from portage.util._argparse import ArgumentParser def quickpkg_atom(options, infos, arg, eout): settings = portage.settings @@ -291,29 +289,28 @@ def quickpkg_main(options, args, eout): if __name__ == "__main__": usage = "quickpkg [options] <list of package atoms or package sets>" - parser = optparse.OptionParser(usage=usage) - parser.add_option("--umask", + parser = ArgumentParser(usage=usage) + parser.add_argument("--umask", default="0077", help="umask used during package creation (default is 0077)") - parser.add_option("--ignore-default-opts", + parser.add_argument("--ignore-default-opts", action="store_true", help="do not use the QUICKPKG_DEFAULT_OPTS environment variable") - parser.add_option("--include-config", - type="choice", + parser.add_argument("--include-config", choices=["y","n"], default="n", metavar="<y|n>", help="include all files protected by CONFIG_PROTECT (as a security precaution, default is 'n')") - parser.add_option("--include-unmodified-config", - type="choice", + parser.add_argument("--include-unmodified-config", choices=["y","n"], default="n", metavar="<y|n>", help="include files protected by CONFIG_PROTECT that have not been modified since installation (as a security precaution, default is 'n')") - options, args = parser.parse_args(sys.argv[1:]) + options, args = parser.parse_known_args(sys.argv[1:]) if not options.ignore_default_opts: - default_opts = portage.settings.get("QUICKPKG_DEFAULT_OPTS","").split() - options, args = parser.parse_args(default_opts + sys.argv[1:]) + default_opts = shlex_split( + portage.settings.get("QUICKPKG_DEFAULT_OPTS", "")) + options, args = parser.parse_known_args(default_opts + sys.argv[1:]) if not args: parser.error("no packages atoms given") try: diff --git a/bin/regenworld b/bin/regenworld index 3199fdf90..32e8e5c33 100755 --- a/bin/regenworld +++ b/bin/regenworld @@ -1,17 +1,15 @@ -#!/usr/bin/python -# Copyright 1999-2011 Gentoo Foundation +#!/usr/bin/python -b +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import print_function import sys -try: - import portage -except ImportError: - from os import path as osp - sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) - import portage - +from os import path as osp +pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym") +sys.path.insert(0, pym_path) +import portage +portage._internal_caller = True from portage import os from portage._sets.files import StaticFileSet, WorldSelectedSet diff --git a/bin/repoman b/bin/repoman index 795c7ce77..888892b30 100755 --- a/bin/repoman +++ b/bin/repoman @@ -1,20 +1,19 @@ -#!/usr/bin/python -O -# Copyright 1999-2012 Gentoo Foundation +#!/usr/bin/python -bO +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # Next to do: dep syntax checking in mask files # Then, check to make sure deps are satisfiable (to avoid "can't find match for" problems) # that last one is tricky because multiple profiles need to be checked. -from __future__ import print_function +from __future__ import print_function, unicode_literals -import calendar +import codecs import copy import errno import formatter import io import logging -import optparse import re import signal import stat @@ -24,23 +23,20 @@ import tempfile import textwrap import time import platform - -try: - from urllib.request import urlopen as urllib_request_urlopen -except ImportError: - from urllib import urlopen as urllib_request_urlopen - from itertools import chain from stat import S_ISDIR try: - import portage + from urllib.parse import urlparse except ImportError: - from os import path as osp - sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) - import portage + from urlparse import urlparse + +from os import path as osp +pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym") +sys.path.insert(0, pym_path) +import portage +portage._internal_caller = True portage._disable_legacy_globals() -portage.dep._internal_warnings = True try: import xml.etree.ElementTree @@ -58,9 +54,9 @@ except (ImportError, SystemError, RuntimeError, Exception): sys.exit(1) from portage import os -from portage import subprocess_getstatusoutput from portage import _encodings from portage import _unicode_encode +import repoman.checks from repoman.checks import run_checks from repoman import utilities from repoman.herdbase import make_herd_base @@ -69,18 +65,18 @@ from _emerge.RootConfig import RootConfig from _emerge.userquery import userquery import portage.checksum import portage.const +import portage.repository.config from portage import cvstree, normalize_path from portage import util -from portage.exception import (FileNotFound, MissingParameter, +from portage.exception import (FileNotFound, InvalidAtom, MissingParameter, ParseError, PermissionDenied) -from portage.manifest import _prohibited_filename_chars_re as \ - disallowed_filename_chars_re +from portage.dep import Atom from portage.process import find_binary, spawn from portage.output import bold, create_color_func, \ green, nocolor, red from portage.output import ConsoleStyleFile, StyleWriter from portage.util import writemsg_level -from portage.util._desktop_entry import validate_desktop_entry +from portage.util._argparse import ArgumentParser from portage.package.ebuild.digestgen import digestgen from portage.eapi import eapi_has_iuse_defaults, eapi_has_required_use @@ -93,6 +89,7 @@ util.initialize_logger() max_desc_len = 100 allowed_filename_chars="a-zA-Z0-9._-+:" pv_toolong_re = re.compile(r'[0-9]{19,}') +GPG_KEY_ID_REGEX = r'(0x)?([0-9a-fA-F]{8}|[0-9a-fA-F]{16}|[0-9a-fA-F]{24}|[0-9a-fA-F]{32}|[0-9a-fA-F]{40})!?' bad = create_color_func("BAD") # A sane umask is needed for files that portage creates. @@ -116,41 +113,14 @@ def err(txt): warn(txt) sys.exit(1) -def exithandler(signum=None, frame=None): +def exithandler(signum=None, _frame=None): logging.fatal("Interrupted; exiting...") if signum is None: sys.exit(1) else: sys.exit(128 + signum) -signal.signal(signal.SIGINT,exithandler) - -class RepomanHelpFormatter(optparse.IndentedHelpFormatter): - """Repoman needs it's own HelpFormatter for now, because the default ones - murder the help text.""" - - def __init__(self, indent_increment=1, max_help_position=24, width=150, short_first=1): - optparse.HelpFormatter.__init__(self, indent_increment, max_help_position, width, short_first) - - def format_description(self, description): - return description - -class RepomanOptionParser(optparse.OptionParser): - """Add the on_tail function, ruby has it, optionParser should too - """ - - def __init__(self, *args, **kwargs): - optparse.OptionParser.__init__(self, *args, **kwargs) - self.tail = "" - - def on_tail(self, description): - self.tail += description - - def format_help(self, formatter=None): - result = optparse.OptionParser.format_help(self, formatter) - result += self.tail - return result - +signal.signal(signal.SIGINT, exithandler) def ParseArgs(argv, qahelp): """This function uses a customized optionParser to parse command line arguments for repoman @@ -161,8 +131,7 @@ def ParseArgs(argv, qahelp): (opts, args), just like a call to parser.parse_args() """ - if argv and isinstance(argv[0], bytes): - argv = [portage._unicode_decode(x) for x in argv] + argv = portage._decode_argv(argv) modes = { 'commit' : 'Run a scan then commit changes', @@ -172,102 +141,113 @@ def ParseArgs(argv, qahelp): 'help' : 'Show this screen', 'manifest' : 'Generate a Manifest (fetches files if necessary)', 'manifest-check' : 'Check Manifests for missing or incorrect digests', - 'scan' : 'Scan directory tree for QA issues' + 'scan' : 'Scan directory tree for QA issues' + } + + output_choices = { + 'default' : 'The normal output format', + 'column' : 'Columnar output suitable for use with grep' } mode_keys = list(modes) mode_keys.sort() - parser = RepomanOptionParser(formatter=RepomanHelpFormatter(), usage="%prog [options] [mode]") - parser.description = green(" ".join((os.path.basename(argv[0]), "1.2"))) - parser.description += "\nCopyright 1999-2007 Gentoo Foundation" - parser.description += "\nDistributed under the terms of the GNU General Public License v2" - parser.description += "\nmodes: " + " | ".join(map(green,mode_keys)) + output_keys = sorted(output_choices) - parser.add_option('-a', '--ask', dest='ask', action='store_true', default=False, + parser = ArgumentParser(usage="repoman [options] [mode]", + description="Modes: %s" % " | ".join(mode_keys), + epilog="For more help consult the man page.") + + parser.add_argument('-a', '--ask', dest='ask', action='store_true', default=False, help='Request a confirmation before commiting') - parser.add_option('-m', '--commitmsg', dest='commitmsg', + parser.add_argument('-m', '--commitmsg', dest='commitmsg', help='specify a commit message on the command line') - parser.add_option('-M', '--commitmsgfile', dest='commitmsgfile', + parser.add_argument('-M', '--commitmsgfile', dest='commitmsgfile', help='specify a path to a file that contains a commit message') - parser.add_option('--digest', - type='choice', choices=('y', 'n'), metavar='<y|n>', + parser.add_argument('--digest', + choices=('y', 'n'), metavar='<y|n>', help='Automatically update Manifest digests for modified files') - parser.add_option('-p', '--pretend', dest='pretend', default=False, + parser.add_argument('-p', '--pretend', dest='pretend', default=False, action='store_true', help='don\'t commit or fix anything; just show what would be done') - - parser.add_option('-q', '--quiet', dest="quiet", action="count", default=0, + + parser.add_argument('-q', '--quiet', dest="quiet", action="count", default=0, help='do not print unnecessary messages') - parser.add_option( - '--echangelog', type='choice', choices=('y', 'n', 'force'), metavar="<y|n|force>", + parser.add_argument( + '--echangelog', choices=('y', 'n', 'force'), metavar="<y|n|force>", help='for commit mode, call echangelog if ChangeLog is unmodified (or ' 'regardless of modification if \'force\' is specified)') - parser.add_option('-f', '--force', dest='force', default=False, action='store_true', + parser.add_argument('--experimental-inherit', choices=('y', 'n'), + metavar="<y|n>", default='n', + help='Enable experimental inherit.missing checks which may misbehave' + ' when the internal eclass database becomes outdated') + + parser.add_argument('-f', '--force', dest='force', default=False, action='store_true', help='Commit with QA violations') - parser.add_option('--vcs', dest='vcs', + parser.add_argument('--vcs', dest='vcs', help='Force using specific VCS instead of autodetection') - parser.add_option('-v', '--verbose', dest="verbosity", action='count', + parser.add_argument('-v', '--verbose', dest="verbosity", action='count', help='be very verbose in output', default=0) - parser.add_option('-V', '--version', dest='version', action='store_true', + parser.add_argument('-V', '--version', dest='version', action='store_true', help='show version info') - parser.add_option('-x', '--xmlparse', dest='xml_parse', action='store_true', + parser.add_argument('-x', '--xmlparse', dest='xml_parse', action='store_true', default=False, help='forces the metadata.xml parse check to be carried out') - parser.add_option( - '--if-modified', type='choice', choices=('y', 'n'), default='n', + parser.add_argument( + '--if-modified', choices=('y', 'n'), default='n', metavar="<y|n>", help='only check packages that have uncommitted modifications') - parser.add_option('-i', '--ignore-arches', dest='ignore_arches', action='store_true', + parser.add_argument('-i', '--ignore-arches', dest='ignore_arches', action='store_true', default=False, help='ignore arch-specific failures (where arch != host)') - parser.add_option("--ignore-default-opts", + parser.add_argument("--ignore-default-opts", action="store_true", help="do not use the REPOMAN_DEFAULT_OPTS environment variable") - parser.add_option('-I', '--ignore-masked', dest='ignore_masked', action='store_true', + parser.add_argument('-I', '--ignore-masked', dest='ignore_masked', action='store_true', default=False, help='ignore masked packages (not allowed with commit mode)') - parser.add_option('-d', '--include-dev', dest='include_dev', action='store_true', + parser.add_argument('--include-arches', dest='include_arches', + metavar='ARCHES', action='append', + help='A space separated list of arches used to ' + 'filter the selection of profiles for dependency checks') + + parser.add_argument('-d', '--include-dev', dest='include_dev', action='store_true', default=False, help='include dev profiles in dependency checks') - parser.add_option('--unmatched-removal', dest='unmatched_removal', action='store_true', + parser.add_argument('-e', '--include-exp-profiles', choices=('y', 'n'), + default=False, help='include exp profiles in dependency checks', + metavar='<y|n>') + + parser.add_argument('--unmatched-removal', dest='unmatched_removal', action='store_true', default=False, help='enable strict checking of package.mask and package.unmask files for unmatched removal atoms') - parser.add_option('--without-mask', dest='without_mask', action='store_true', + parser.add_argument('--without-mask', dest='without_mask', action='store_true', default=False, help='behave as if no package.mask entries exist (not allowed with commit mode)') - parser.add_option('--mode', type='choice', dest='mode', choices=list(modes), - help='specify which mode repoman will run in (default=full)') - - parser.on_tail("\n " + green("Modes".ljust(20) + " Description\n")) + parser.add_argument('--output-style', dest='output_style', choices=output_keys, + help='select output type', default='default') - for k in mode_keys: - parser.on_tail(" %s %s\n" % (k.ljust(20), modes[k])) - - parser.on_tail("\n " + green("QA keyword".ljust(20) + " Description\n")) - - sorted_qa = list(qahelp) - sorted_qa.sort() - for k in sorted_qa: - parser.on_tail(" %s %s\n" % (k.ljust(20), qahelp[k])) + parser.add_argument('--mode', dest='mode', choices=mode_keys, + help='specify which mode repoman will run in (default=full)') - opts, args = parser.parse_args(argv[1:]) + opts, args = parser.parse_known_args(argv[1:]) if not opts.ignore_default_opts: - default_opts = repoman_settings.get("REPOMAN_DEFAULT_OPTS", "").split() + default_opts = portage.util.shlex_split( + repoman_settings.get("REPOMAN_DEFAULT_OPTS", "")) if default_opts: - opts, args = parser.parse_args(default_opts + sys.argv[1:]) + opts, args = parser.parse_known_args(default_opts + sys.argv[1:]) if opts.mode == 'help': parser.print_help(short=False) @@ -282,16 +262,10 @@ def ParseArgs(argv, qahelp): if not opts.mode: opts.mode = 'full' - + if opts.mode == 'ci': opts.mode = 'commit' # backwards compat shortcut - if opts.mode == 'commit' and not (opts.force or opts.pretend): - if opts.ignore_masked: - parser.error('Commit mode and --ignore-masked are not compatible') - if opts.without_mask: - parser.error('Commit mode and --without-mask are not compatible') - # Use the verbosity and quiet options to fiddle with the loglevel appropriately for val in range(opts.verbosity): logger = logging.getLogger() @@ -301,101 +275,99 @@ def ParseArgs(argv, qahelp): logger = logging.getLogger() logger.setLevel(logger.getEffectiveLevel() + 10) + if opts.mode == 'commit' and not (opts.force or opts.pretend): + if opts.ignore_masked: + opts.ignore_masked = False + logging.warn('Commit mode automatically disables --ignore-masked') + if opts.without_mask: + opts.without_mask = False + logging.warn('Commit mode automatically disables --without-mask') + return (opts, args) -qahelp={ - "CVS/Entries.IO_error":"Attempting to commit, and an IO error was encountered access the Entries file", - "desktop.invalid":"desktop-file-validate reports errors in a *.desktop file", - "ebuild.invalidname":"Ebuild files with a non-parseable or syntactically incorrect name (or using 2.1 versioning extensions)", - "ebuild.namenomatch":"Ebuild files that do not have the same name as their parent directory", - "changelog.ebuildadded":"An ebuild was added but the ChangeLog was not modified", - "changelog.missing":"Missing ChangeLog files", - "ebuild.notadded":"Ebuilds that exist but have not been added to cvs", - "ebuild.patches":"PATCHES variable should be a bash array to ensure white space safety", - "changelog.notadded":"ChangeLogs that exist but have not been added to cvs", - "dependency.unknown" : "Ebuild has a dependency that refers to an unknown package (which may be valid if it is a blocker for a renamed/removed package, or is an alternative choice provided by an overlay)", - "file.executable":"Ebuilds, digests, metadata.xml, Manifest, and ChangeLog do not need the executable bit", - "file.size":"Files in the files directory must be under 20 KiB", - "file.size.fatal":"Files in the files directory must be under 60 KiB", - "file.name":"File/dir name must be composed of only the following chars: %s " % allowed_filename_chars, - "file.UTF8":"File is not UTF8 compliant", - "inherit.deprecated":"Ebuild inherits a deprecated eclass", - "inherit.missing":"Ebuild uses functions from an eclass but does not inherit it", - "inherit.unused":"Ebuild inherits an eclass but does not use it", - "java.eclassesnotused":"With virtual/jdk in DEPEND you must inherit a java eclass", - "wxwidgets.eclassnotused":"Ebuild DEPENDs on x11-libs/wxGTK without inheriting wxwidgets.eclass", - "KEYWORDS.dropped":"Ebuilds that appear to have dropped KEYWORDS for some arch", - "KEYWORDS.missing":"Ebuilds that have a missing or empty KEYWORDS variable", - "KEYWORDS.stable":"Ebuilds that have been added directly with stable KEYWORDS", - "KEYWORDS.stupid":"Ebuilds that use KEYWORDS=-* instead of package.mask", - "LICENSE.missing":"Ebuilds that have a missing or empty LICENSE variable", - "LICENSE.virtual":"Virtuals that have a non-empty LICENSE variable", - "DESCRIPTION.missing":"Ebuilds that have a missing or empty DESCRIPTION variable", - "DESCRIPTION.toolong":"DESCRIPTION is over %d characters" % max_desc_len, - "EAPI.definition":"EAPI definition does not conform to PMS section 7.3.1 (first non-comment, non-blank line)", - "EAPI.deprecated":"Ebuilds that use features that are deprecated in the current EAPI", - "EAPI.incompatible":"Ebuilds that use features that are only available with a different EAPI", - "EAPI.unsupported":"Ebuilds that have an unsupported EAPI version (you must upgrade portage)", - "SLOT.invalid":"Ebuilds that have a missing or invalid SLOT variable value", - "HOMEPAGE.missing":"Ebuilds that have a missing or empty HOMEPAGE variable", - "HOMEPAGE.virtual":"Virtuals that have a non-empty HOMEPAGE variable", - "DEPEND.bad":"User-visible ebuilds with bad DEPEND settings (matched against *visible* ebuilds)", - "RDEPEND.bad":"User-visible ebuilds with bad RDEPEND settings (matched against *visible* ebuilds)", - "PDEPEND.bad":"User-visible ebuilds with bad PDEPEND settings (matched against *visible* ebuilds)", - "DEPEND.badmasked":"Masked ebuilds with bad DEPEND settings (matched against *all* ebuilds)", - "RDEPEND.badmasked":"Masked ebuilds with RDEPEND settings (matched against *all* ebuilds)", - "PDEPEND.badmasked":"Masked ebuilds with PDEPEND settings (matched against *all* ebuilds)", - "DEPEND.badindev":"User-visible ebuilds with bad DEPEND settings (matched against *visible* ebuilds) in developing arch", - "RDEPEND.badindev":"User-visible ebuilds with bad RDEPEND settings (matched against *visible* ebuilds) in developing arch", - "PDEPEND.badindev":"User-visible ebuilds with bad PDEPEND settings (matched against *visible* ebuilds) in developing arch", - "DEPEND.badmaskedindev":"Masked ebuilds with bad DEPEND settings (matched against *all* ebuilds) in developing arch", - "RDEPEND.badmaskedindev":"Masked ebuilds with RDEPEND settings (matched against *all* ebuilds) in developing arch", - "PDEPEND.badmaskedindev":"Masked ebuilds with PDEPEND settings (matched against *all* ebuilds) in developing arch", - "PDEPEND.suspect":"PDEPEND contains a package that usually only belongs in DEPEND.", - "DEPEND.syntax":"Syntax error in DEPEND (usually an extra/missing space/parenthesis)", - "RDEPEND.syntax":"Syntax error in RDEPEND (usually an extra/missing space/parenthesis)", - "PDEPEND.syntax":"Syntax error in PDEPEND (usually an extra/missing space/parenthesis)", - "DEPEND.badtilde":"DEPEND uses the ~ dep operator with a non-zero revision part, which is useless (the revision is ignored)", - "RDEPEND.badtilde":"RDEPEND uses the ~ dep operator with a non-zero revision part, which is useless (the revision is ignored)", - "PDEPEND.badtilde":"PDEPEND uses the ~ dep operator with a non-zero revision part, which is useless (the revision is ignored)", - "LICENSE.syntax":"Syntax error in LICENSE (usually an extra/missing space/parenthesis)", - "PROVIDE.syntax":"Syntax error in PROVIDE (usually an extra/missing space/parenthesis)", - "PROPERTIES.syntax":"Syntax error in PROPERTIES (usually an extra/missing space/parenthesis)", - "RESTRICT.syntax":"Syntax error in RESTRICT (usually an extra/missing space/parenthesis)", - "REQUIRED_USE.syntax":"Syntax error in REQUIRED_USE (usually an extra/missing space/parenthesis)", - "SRC_URI.syntax":"Syntax error in SRC_URI (usually an extra/missing space/parenthesis)", - "SRC_URI.mirror":"A uri listed in profiles/thirdpartymirrors is found in SRC_URI", - "ebuild.syntax":"Error generating cache entry for ebuild; typically caused by ebuild syntax error or digest verification failure", - "ebuild.output":"A simple sourcing of the ebuild produces output; this breaks ebuild policy.", - "ebuild.nesteddie":"Placing 'die' inside ( ) prints an error, but doesn't stop the ebuild.", - "variable.invalidchar":"A variable contains an invalid character that is not part of the ASCII character set", - "variable.readonly":"Assigning a readonly variable", - "variable.usedwithhelpers":"Ebuild uses D, ROOT, ED, EROOT or EPREFIX with helpers", - "LIVEVCS.stable":"This ebuild is a live checkout from a VCS but has stable keywords.", - "LIVEVCS.unmasked":"This ebuild is a live checkout from a VCS but has keywords and is not masked in the global package.mask.", - "IUSE.invalid":"This ebuild has a variable in IUSE that is not in the use.desc or its metadata.xml file", - "IUSE.missing":"This ebuild has a USE conditional which references a flag that is not listed in IUSE", - "IUSE.undefined":"This ebuild does not define IUSE (style guideline says to define IUSE even when empty)", - "LICENSE.invalid":"This ebuild is listing a license that doesnt exist in portages license/ dir.", - "KEYWORDS.invalid":"This ebuild contains KEYWORDS that are not listed in profiles/arch.list or for which no valid profile was found", - "RDEPEND.implicit":"RDEPEND is unset in the ebuild which triggers implicit RDEPEND=$DEPEND assignment (prior to EAPI 4)", - "RDEPEND.suspect":"RDEPEND contains a package that usually only belongs in DEPEND.", - "RESTRICT.invalid":"This ebuild contains invalid RESTRICT values.", - "digest.assumed":"Existing digest must be assumed correct (Package level only)", - "digest.missing":"Some files listed in SRC_URI aren't referenced in the Manifest", - "digest.unused":"Some files listed in the Manifest aren't referenced in SRC_URI", - "ebuild.majorsyn":"This ebuild has a major syntax error that may cause the ebuild to fail partially or fully", - "ebuild.minorsyn":"This ebuild has a minor syntax error that contravenes gentoo coding style", - "ebuild.badheader":"This ebuild has a malformed header", - "manifest.bad":"Manifest has missing or incorrect digests", - "metadata.missing":"Missing metadata.xml files", - "metadata.bad":"Bad metadata.xml files", - "metadata.warning":"Warnings in metadata.xml files", - "portage.internal":"The ebuild uses an internal Portage function", - "virtual.oldstyle":"The ebuild PROVIDEs an old-style virtual (see GLEP 37)", - "virtual.suspect":"Ebuild contains a package that usually should be pulled via virtual/, not directly.", - "usage.obsolete":"The ebuild makes use of an obsolete construct", - "upstream.workaround":"The ebuild works around an upstream bug, an upstream bug should be filed and tracked in bugs.gentoo.org" +qahelp = { + "CVS/Entries.IO_error": "Attempting to commit, and an IO error was encountered access the Entries file", + "ebuild.invalidname": "Ebuild files with a non-parseable or syntactically incorrect name (or using 2.1 versioning extensions)", + "ebuild.namenomatch": "Ebuild files that do not have the same name as their parent directory", + "changelog.ebuildadded": "An ebuild was added but the ChangeLog was not modified", + "changelog.missing": "Missing ChangeLog files", + "ebuild.notadded": "Ebuilds that exist but have not been added to cvs", + "ebuild.patches": "PATCHES variable should be a bash array to ensure white space safety", + "changelog.notadded": "ChangeLogs that exist but have not been added to cvs", + "dependency.bad": "User-visible ebuilds with unsatisfied dependencies (matched against *visible* ebuilds)", + "dependency.badmasked": "Masked ebuilds with unsatisfied dependencies (matched against *all* ebuilds)", + "dependency.badindev": "User-visible ebuilds with unsatisfied dependencies (matched against *visible* ebuilds) in developing arch", + "dependency.badmaskedindev": "Masked ebuilds with unsatisfied dependencies (matched against *all* ebuilds) in developing arch", + "dependency.badtilde": "Uses the ~ dep operator with a non-zero revision part, which is useless (the revision is ignored)", + "dependency.syntax": "Syntax error in dependency string (usually an extra/missing space/parenthesis)", + "dependency.unknown": "Ebuild has a dependency that refers to an unknown package (which may be valid if it is a blocker for a renamed/removed package, or is an alternative choice provided by an overlay)", + "file.executable": "Ebuilds, digests, metadata.xml, Manifest, and ChangeLog do not need the executable bit", + "file.size": "Files in the files directory must be under 20 KiB", + "file.size.fatal": "Files in the files directory must be under 60 KiB", + "file.name": "File/dir name must be composed of only the following chars: %s " % allowed_filename_chars, + "file.UTF8": "File is not UTF8 compliant", + "inherit.deprecated": "Ebuild inherits a deprecated eclass", + "inherit.missing": "Ebuild uses functions from an eclass but does not inherit it", + "inherit.unused": "Ebuild inherits an eclass but does not use it", + "java.eclassesnotused": "With virtual/jdk in DEPEND you must inherit a java eclass", + "wxwidgets.eclassnotused": "Ebuild DEPENDs on x11-libs/wxGTK without inheriting wxwidgets.eclass", + "KEYWORDS.dropped": "Ebuilds that appear to have dropped KEYWORDS for some arch", + "KEYWORDS.missing": "Ebuilds that have a missing or empty KEYWORDS variable", + "KEYWORDS.stable": "Ebuilds that have been added directly with stable KEYWORDS", + "KEYWORDS.stupid": "Ebuilds that use KEYWORDS=-* instead of package.mask", + "LICENSE.missing": "Ebuilds that have a missing or empty LICENSE variable", + "LICENSE.virtual": "Virtuals that have a non-empty LICENSE variable", + "DESCRIPTION.missing": "Ebuilds that have a missing or empty DESCRIPTION variable", + "DESCRIPTION.toolong": "DESCRIPTION is over %d characters" % max_desc_len, + "EAPI.definition": "EAPI definition does not conform to PMS section 7.3.1 (first non-comment, non-blank line)", + "EAPI.deprecated": "Ebuilds that use features that are deprecated in the current EAPI", + "EAPI.incompatible": "Ebuilds that use features that are only available with a different EAPI", + "EAPI.unsupported": "Ebuilds that have an unsupported EAPI version (you must upgrade portage)", + "SLOT.invalid": "Ebuilds that have a missing or invalid SLOT variable value", + "HOMEPAGE.missing": "Ebuilds that have a missing or empty HOMEPAGE variable", + "HOMEPAGE.virtual": "Virtuals that have a non-empty HOMEPAGE variable", + "PDEPEND.suspect": "PDEPEND contains a package that usually only belongs in DEPEND.", + "LICENSE.syntax": "Syntax error in LICENSE (usually an extra/missing space/parenthesis)", + "PROVIDE.syntax": "Syntax error in PROVIDE (usually an extra/missing space/parenthesis)", + "PROPERTIES.syntax": "Syntax error in PROPERTIES (usually an extra/missing space/parenthesis)", + "RESTRICT.syntax": "Syntax error in RESTRICT (usually an extra/missing space/parenthesis)", + "REQUIRED_USE.syntax": "Syntax error in REQUIRED_USE (usually an extra/missing space/parenthesis)", + "SRC_URI.syntax": "Syntax error in SRC_URI (usually an extra/missing space/parenthesis)", + "SRC_URI.mirror": "A uri listed in profiles/thirdpartymirrors is found in SRC_URI", + "ebuild.syntax": "Error generating cache entry for ebuild; typically caused by ebuild syntax error or digest verification failure", + "ebuild.output": "A simple sourcing of the ebuild produces output; this breaks ebuild policy.", + "ebuild.nesteddie": "Placing 'die' inside ( ) prints an error, but doesn't stop the ebuild.", + "variable.invalidchar": "A variable contains an invalid character that is not part of the ASCII character set", + "variable.readonly": "Assigning a readonly variable", + "variable.usedwithhelpers": "Ebuild uses D, ROOT, ED, EROOT or EPREFIX with helpers", + "LIVEVCS.stable": "This ebuild is a live checkout from a VCS but has stable keywords.", + "LIVEVCS.unmasked": "This ebuild is a live checkout from a VCS but has keywords and is not masked in the global package.mask.", + "IUSE.invalid": "This ebuild has a variable in IUSE that is not in the use.desc or its metadata.xml file", + "IUSE.missing": "This ebuild has a USE conditional which references a flag that is not listed in IUSE", + "IUSE.rubydeprecated": "The ebuild has set a ruby interpreter in USE_RUBY, that is not available as a ruby target anymore", + "LICENSE.invalid": "This ebuild is listing a license that doesnt exist in portages license/ dir.", + "LICENSE.deprecated": "This ebuild is listing a deprecated license.", + "KEYWORDS.invalid": "This ebuild contains KEYWORDS that are not listed in profiles/arch.list or for which no valid profile was found", + "RDEPEND.implicit": "RDEPEND is unset in the ebuild which triggers implicit RDEPEND=$DEPEND assignment (prior to EAPI 4)", + "RDEPEND.suspect": "RDEPEND contains a package that usually only belongs in DEPEND.", + "RESTRICT.invalid": "This ebuild contains invalid RESTRICT values.", + "digest.assumed": "Existing digest must be assumed correct (Package level only)", + "digest.missing": "Some files listed in SRC_URI aren't referenced in the Manifest", + "digest.unused": "Some files listed in the Manifest aren't referenced in SRC_URI", + "ebuild.majorsyn": "This ebuild has a major syntax error that may cause the ebuild to fail partially or fully", + "ebuild.minorsyn": "This ebuild has a minor syntax error that contravenes gentoo coding style", + "ebuild.badheader": "This ebuild has a malformed header", + "manifest.bad": "Manifest has missing or incorrect digests", + "metadata.missing": "Missing metadata.xml files", + "metadata.bad": "Bad metadata.xml files", + "metadata.warning": "Warnings in metadata.xml files", + "portage.internal": "The ebuild uses an internal Portage function or variable", + "repo.eapi.banned": "The ebuild uses an EAPI which is banned by the repository's metadata/layout.conf settings", + "repo.eapi.deprecated": "The ebuild uses an EAPI which is deprecated by the repository's metadata/layout.conf settings", + "virtual.oldstyle": "The ebuild PROVIDEs an old-style virtual (see GLEP 37)", + "virtual.suspect": "Ebuild contains a package that usually should be pulled via virtual/, not directly.", + "usage.obsolete": "The ebuild makes use of an obsolete construct", + "upstream.workaround": "The ebuild works around an upstream bug, an upstream bug should be filed and tracked in bugs.gentoo.org" } qacats = list(qahelp) @@ -409,19 +381,18 @@ qawarnings = set(( "digest.unused", "ebuild.notadded", "ebuild.nesteddie", -"desktop.invalid", -"DEPEND.badmasked","RDEPEND.badmasked","PDEPEND.badmasked", -"DEPEND.badindev","RDEPEND.badindev","PDEPEND.badindev", -"DEPEND.badmaskedindev","RDEPEND.badmaskedindev","PDEPEND.badmaskedindev", -"DEPEND.badtilde", "RDEPEND.badtilde", "PDEPEND.badtilde", +"dependency.badmasked", +"dependency.badindev", +"dependency.badmaskedindev", +"dependency.badtilde", "DESCRIPTION.toolong", "EAPI.deprecated", "HOMEPAGE.virtual", +"LICENSE.deprecated", "LICENSE.virtual", "KEYWORDS.dropped", "KEYWORDS.stupid", "KEYWORDS.missing", -"IUSE.undefined", "PDEPEND.suspect", "RDEPEND.implicit", "RDEPEND.suspect", @@ -437,23 +408,21 @@ qawarnings = set(( "wxwidgets.eclassnotused", "metadata.warning", "portage.internal", +"repo.eapi.deprecated", "usage.obsolete", "upstream.workaround", "LIVEVCS.stable", "LIVEVCS.unmasked", +"IUSE.rubydeprecated", )) -if portage.const._ENABLE_INHERIT_CHECK: - # This is experimental, so it's non-fatal. - qawarnings.add("inherit.missing") - non_ascii_re = re.compile(r'[^\x00-\x7f]') missingvars = ["KEYWORDS", "LICENSE", "DESCRIPTION", "HOMEPAGE"] allvars = set(x for x in portage.auxdbkeys if not x.startswith("UNUSED_")) allvars.update(Package.metadata_keys) allvars = sorted(allvars) -commitmessage=None +commitmessage = None for x in missingvars: x += ".missing" if x not in qacats: @@ -462,19 +431,10 @@ for x in missingvars: qawarnings.add(x) valid_restrict = frozenset(["binchecks", "bindist", - "fetch", "installsources", "mirror", - "primaryuri", "strip", "test", "userpriv"]) - -live_eclasses = frozenset([ - "bzr", - "cvs", - "darcs", - "git", - "git-2", - "mercurial", - "subversion", - "tla", -]) + "fetch", "installsources", "mirror", "preserve-libs", + "primaryuri", "splitdebug", "strip", "test", "userpriv"]) + +live_eclasses = portage.const.LIVE_ECLASSES suspect_rdepend = frozenset([ "app-arch/cabextract", @@ -520,14 +480,25 @@ suspect_virtual = { "dev-util/pkgconf":"virtual/pkgconfig", "dev-util/pkgconfig":"virtual/pkgconfig", "dev-util/pkgconfig-openbsd":"virtual/pkgconfig", + "dev-libs/libusb":"virtual/libusb", + "dev-libs/libusbx":"virtual/libusb", + "dev-libs/libusb-compat":"virtual/libusb", } +ruby_deprecated = frozenset([ + "ruby_targets_ree18", +]) + +metadata_xml_encoding = 'UTF-8' +metadata_xml_declaration = '<?xml version="1.0" encoding="%s"?>' % \ + (metadata_xml_encoding,) +metadata_doctype_name = 'pkgmetadata' metadata_dtd_uri = 'http://www.gentoo.org/dtd/metadata.dtd' # force refetch if the local copy creation time is older than this metadata_dtd_ctime_interval = 60 * 60 * 24 * 7 # 7 days # file.executable -no_exec = frozenset(["Manifest","ChangeLog","metadata.xml"]) +no_exec = frozenset(["Manifest", "ChangeLog", "metadata.xml"]) options, arguments = ParseArgs(sys.argv, qahelp) @@ -535,6 +506,11 @@ if options.version: print("Portage", portage.VERSION) sys.exit(0) +if options.experimental_inherit == 'y': + # This is experimental, so it's non-fatal. + qawarnings.add("inherit.missing") + repoman.checks._init(experimental_inherit=True) + # Set this to False when an extraordinary issue (generally # something other than a QA issue) makes it impossible to # commit (like if Manifest generation fails). @@ -584,14 +560,29 @@ if options.mode == 'commit' and not options.pretend and not vcs: logging.info("Not in a version controlled repository; enabling pretend mode.") options.pretend = True -# Ensure that PORTDIR_OVERLAY contains the repository corresponding to $PWD. -repoman_settings['PORTDIR_OVERLAY'] = "%s %s" % \ - (repoman_settings.get('PORTDIR_OVERLAY', ''), - portage._shell_quote(portdir_overlay)) -# We have to call the config constructor again so -# that config.repositories is initialized correctly. -repoman_settings = portage.config(config_root=config_root, local_config=False, - env=dict(os.environ, PORTDIR_OVERLAY=repoman_settings['PORTDIR_OVERLAY'])) +# Ensure that current repository is in the list of enabled repositories. +repodir = os.path.realpath(portdir_overlay) +try: + repoman_settings.repositories.get_repo_for_location(repodir) +except KeyError: + repo_name = portage.repository.config.RepoConfig._read_valid_repo_name(portdir_overlay)[0] + layout_conf_data = portage.repository.config.parse_layout_conf(portdir_overlay)[0] + if layout_conf_data['repo-name']: + repo_name = layout_conf_data['repo-name'] + tmp_conf_file = io.StringIO(textwrap.dedent(""" + [%s] + location = %s + """) % (repo_name, portdir_overlay)) + # Ensure that the repository corresponding to $PWD overrides a + # repository of the same name referenced by the existing PORTDIR + # or PORTDIR_OVERLAY settings. + repoman_settings['PORTDIR_OVERLAY'] = "%s %s" % \ + (repoman_settings.get('PORTDIR_OVERLAY', ''), + portage._shell_quote(portdir_overlay)) + repositories = portage.repository.config.load_repository_config(repoman_settings, extra_files=[tmp_conf_file]) + # We have to call the config constructor again so that attributes + # dependent on config.repositories are initialized correctly. + repoman_settings = portage.config(config_root=config_root, local_config=False, repositories=repositories) root = repoman_settings['EROOT'] trees = { @@ -601,10 +592,15 @@ portdb = trees[root]['porttree'].dbapi # Constrain dependency resolution to the master(s) # that are specified in layout.conf. -repodir = os.path.realpath(portdir_overlay) repo_config = repoman_settings.repositories.get_repo_for_location(repodir) portdb.porttrees = list(repo_config.eclass_db.porttrees) portdir = portdb.porttrees[0] +commit_env = os.environ.copy() +# list() is for iteration on a copy. +for repo in list(repoman_settings.repositories): + # all paths are canonical + if repo.location not in repo_config.eclass_db.porttrees: + del repoman_settings.repositories[repo.name] if repo_config.allow_provide_virtual: qawarnings.add("virtual.oldstyle") @@ -615,6 +611,15 @@ if repo_config.sign_commit: # the commit arguments. If key_id is unspecified, then it must be # configured by `git config user.signingkey key_id`. vcs_local_opts.append("--gpg-sign") + if repoman_settings.get("PORTAGE_GPG_DIR"): + # Pass GNUPGHOME to git for bug #462362. + commit_env["GNUPGHOME"] = repoman_settings["PORTAGE_GPG_DIR"] + + # Pass GPG_TTY to git for bug #477728. + try: + commit_env["GPG_TTY"] = os.ttyname(sys.stdin.fileno()) + except OSError: + pass # In order to disable manifest signatures, repos may set # "sign-manifests = false" in metadata/layout.conf. This @@ -623,6 +628,25 @@ if repo_config.sign_commit: sign_manifests = "sign" in repoman_settings.features and \ repo_config.sign_manifest +if repo_config.sign_manifest and repo_config.name == "gentoo" and \ + options.mode in ("commit",) and not sign_manifests: + msg = ("The '%s' repository has manifest signatures enabled, " + "but FEATURES=sign is currently disabled. In order to avoid this " + "warning, enable FEATURES=sign in make.conf. Alternatively, " + "repositories can disable manifest signatures by setting " + "'sign-manifests = false' in metadata/layout.conf.") % \ + (repo_config.name,) + for line in textwrap.wrap(msg, 60): + logging.warn(line) + +if sign_manifests and options.mode in ("commit",) and \ + repoman_settings.get("PORTAGE_GPG_KEY") and \ + re.match(r'^%s$' % GPG_KEY_ID_REGEX, + repoman_settings["PORTAGE_GPG_KEY"]) is None: + logging.error("PORTAGE_GPG_KEY value is invalid: %s" % + repoman_settings["PORTAGE_GPG_KEY"]) + sys.exit(1) + manifest_hashes = repo_config.manifest_hashes if manifest_hashes is None: manifest_hashes = portage.const.MANIFEST2_HASH_DEFAULTS @@ -652,19 +676,6 @@ if options.mode in ("commit", "fix", "manifest"): logging.error(line) sys.exit(1) -if "commit" == options.mode and \ - repo_config.name == "gentoo" and \ - "RMD160" in manifest_hashes and \ - "RMD160" not in portage.checksum.hashorigin_map: - msg = "Please install " \ - "pycrypto or enable python's ssl USE flag in order " \ - "to enable RMD160 hash support. See bug #198398 for " \ - "more information." - prefix = bad(" * ") - for line in textwrap.wrap(msg, 70): - print(prefix + line) - sys.exit(1) - if options.echangelog is None and repo_config.update_changelog: options.echangelog = 'y' @@ -689,18 +700,9 @@ logging.debug("vcs: %s" % (vcs,)) logging.debug("repo config: %s" % (repo_config,)) logging.debug("options: %s" % (options,)) -# Generate an appropriate PORTDIR_OVERLAY value for passing into the -# profile-specific config constructor calls. -env = os.environ.copy() -env['PORTDIR'] = portdir -env['PORTDIR_OVERLAY'] = ' '.join(portdb.porttrees[1:]) - -logging.info('Setting paths:') -logging.info('PORTDIR = "' + portdir + '"') -logging.info('PORTDIR_OVERLAY = "%s"' % env['PORTDIR_OVERLAY']) - # It's confusing if these warnings are displayed without the user # being told which profile they come from, so disable them. +env = os.environ.copy() env['FEATURES'] = env.get('FEATURES', '') + ' -unknown-features-warn' categories = [] @@ -724,7 +726,7 @@ repolevel = len(reposplit) # check if it's in $PORTDIR/$CATEGORY/$PN , otherwise bail if commiting. # Reason for this is if they're trying to commit in just $FILESDIR/*, the Manifest needs updating. # this check ensures that repoman knows where it is, and the manifest recommit is at least possible. -if options.mode == 'commit' and repolevel not in [1,2,3]: +if options.mode == 'commit' and repolevel not in [1, 2, 3]: print(red("***")+" Commit attempts *must* be from within a vcs co, category, or package directory.") print(red("***")+" Attempting to commit from a packages files directory will be blocked for instance.") print(red("***")+" This is intended behaviour, to ensure the manifest is recommitted for a package.") @@ -737,10 +739,76 @@ if repolevel == 1: startdir = repodir else: startdir = normalize_path(mydir) - startdir = os.path.join(repodir, *startdir.split(os.sep)[-2-repolevel+3:]) + startdir = os.path.join(repodir, *startdir.split(os.sep)[-2 - repolevel + 3:]) def caterror(mycat): - err(mycat+" is not an official category. Skipping QA checks in this directory.\nPlease ensure that you add "+catdir+" to "+repodir+"/profiles/categories\nif it is a new category.") + err(mycat + " is not an official category. Skipping QA checks in this directory.\nPlease ensure that you add " + catdir + " to " + repodir + "/profiles/categories\nif it is a new category.") + +def repoman_getstatusoutput(cmd): + """ + Implements an interface similar to getstatusoutput(), but with + customized unicode handling (see bug #310789) and without the shell. + """ + args = portage.util.shlex_split(cmd) + + if sys.hexversion < 0x3020000 and sys.hexversion >= 0x3000000 and \ + not os.path.isabs(args[0]): + # Python 3.1 _execvp throws TypeError for non-absolute executable + # path passed as bytes (see http://bugs.python.org/issue8513). + fullname = find_binary(args[0]) + if fullname is None: + raise portage.exception.CommandNotFound(args[0]) + args[0] = fullname + + encoding = _encodings['fs'] + args = [_unicode_encode(x, + encoding=encoding, errors='strict') for x in args] + proc = subprocess.Popen(args, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + output = portage._unicode_decode(proc.communicate()[0], + encoding=encoding, errors='strict') + if output and output[-1] == "\n": + # getstatusoutput strips one newline + output = output[:-1] + return (proc.wait(), output) + +class repoman_popen(portage.proxy.objectproxy.ObjectProxy): + """ + Implements an interface similar to os.popen(), but with customized + unicode handling (see bug #310789) and without the shell. + """ + + __slots__ = ('_proc', '_stdout') + + def __init__(self, cmd): + args = portage.util.shlex_split(cmd) + + if sys.hexversion < 0x3020000 and sys.hexversion >= 0x3000000 and \ + not os.path.isabs(args[0]): + # Python 3.1 _execvp throws TypeError for non-absolute executable + # path passed as bytes (see http://bugs.python.org/issue8513). + fullname = find_binary(args[0]) + if fullname is None: + raise portage.exception.CommandNotFound(args[0]) + args[0] = fullname + + encoding = _encodings['fs'] + args = [_unicode_encode(x, + encoding=encoding, errors='strict') for x in args] + proc = subprocess.Popen(args, stdout=subprocess.PIPE) + object.__setattr__(self, '_proc', proc) + object.__setattr__(self, '_stdout', + codecs.getreader(encoding)(proc.stdout, 'strict')) + + def _get_target(self): + return object.__getattribute__(self, '_stdout') + + __enter__ = _get_target + + def __exit__(self, exc_type, exc_value, traceback): + proc = object.__getattribute__(self, '_proc') + proc.wait() + proc.stdout.close() class ProfileDesc(object): __slots__ = ('abs_path', 'arch', 'status', 'sub_path', 'tree_path',) @@ -818,18 +886,18 @@ for path in portdb.porttrees: continue if len(arch) != 3: err("wrong format: \"" + bad(x.strip()) + "\" in " + \ - desc_path + " line %d" % (i+1, )) + desc_path + " line %d" % (i + 1, )) elif arch[0] not in kwlist: err("invalid arch: \"" + bad(arch[0]) + "\" in " + \ - desc_path + " line %d" % (i+1, )) + desc_path + " line %d" % (i + 1, )) elif arch[2] not in valid_profile_types: err("invalid profile type: \"" + bad(arch[2]) + "\" in " + \ - desc_path + " line %d" % (i+1, )) + desc_path + " line %d" % (i + 1, )) profile_desc = ProfileDesc(arch[0], arch[2], arch[1], path) if not os.path.isdir(profile_desc.abs_path): logging.error( "Invalid %s profile (%s) for arch %s in %s line %d", - arch[2], arch[1], arch[0], desc_path, i+1) + arch[2], arch[1], arch[0], desc_path, i + 1) continue if os.path.exists( os.path.join(profile_desc.abs_path, 'deprecated')): @@ -876,11 +944,16 @@ for x in repoman_settings.archlist(): if x[0] == "~": continue if x not in profiles: - print(red("\""+x+"\" doesn't have a valid profile listed in profiles.desc.")) + print(red("\"" + x + "\" doesn't have a valid profile listed in profiles.desc.")) print(red("You need to either \"cvs update\" your profiles dir or follow this")) - print(red("up with the "+x+" team.")) + print(red("up with the " + x + " team.")) print() +liclist_deprecated = set() +if "DEPRECATED" in repoman_settings._license_manager._license_groups: + liclist_deprecated.update( + repoman_settings._license_manager.expandLicenseTokens(["@DEPRECATED"])) + if not liclist: logging.fatal("Couldn't find licenses?") sys.exit(1) @@ -893,34 +966,34 @@ if not uselist: logging.fatal("Couldn't find use.desc?") sys.exit(1) -scanlist=[] -if repolevel==2: - #we are inside a category directory - catdir=reposplit[-1] +scanlist = [] +if repolevel == 2: + # we are inside a category directory + catdir = reposplit[-1] if catdir not in categories: caterror(catdir) - mydirlist=os.listdir(startdir) + mydirlist = os.listdir(startdir) for x in mydirlist: if x == "CVS" or x.startswith("."): continue - if os.path.isdir(startdir+"/"+x): - scanlist.append(catdir+"/"+x) + if os.path.isdir(startdir + "/" + x): + scanlist.append(catdir + "/" + x) repo_subdir = catdir + os.sep -elif repolevel==1: +elif repolevel == 1: for x in categories: - if not os.path.isdir(startdir+"/"+x): + if not os.path.isdir(startdir + "/" + x): continue - for y in os.listdir(startdir+"/"+x): + for y in os.listdir(startdir + "/" + x): if y == "CVS" or y.startswith("."): continue - if os.path.isdir(startdir+"/"+x+"/"+y): - scanlist.append(x+"/"+y) + if os.path.isdir(startdir + "/" + x + "/" + y): + scanlist.append(x + "/" + y) repo_subdir = "" -elif repolevel==3: +elif repolevel == 3: catdir = reposplit[-2] if catdir not in categories: caterror(catdir) - scanlist.append(catdir+"/"+reposplit[-1]) + scanlist.append(catdir + "/" + reposplit[-1]) repo_subdir = scanlist[-1] + os.sep else: msg = 'Repoman is unable to determine PORTDIR or PORTDIR_OVERLAY' + \ @@ -952,7 +1025,7 @@ def vcs_files_to_cps(vcs_file_iter): if category in categories: for filename in vcs_file_iter: f_split = filename.split(os.sep) - # ['.', pn,...] + # ['.', pn, ...] if len(f_split) > 2: modified_cps.append(category + "/" + f_split[1]) @@ -960,7 +1033,7 @@ def vcs_files_to_cps(vcs_file_iter): # repolevel == 1 for filename in vcs_file_iter: f_split = filename.split(os.sep) - # ['.', category, pn,...] + # ['.', category, pn, ...] if len(f_split) > 3 and f_split[1] in categories: modified_cps.append("/".join(f_split[1:3])) @@ -968,12 +1041,12 @@ def vcs_files_to_cps(vcs_file_iter): def git_supports_gpg_sign(): status, cmd_output = \ - subprocess_getstatusoutput("git --version") + repoman_getstatusoutput("git --version") cmd_output = cmd_output.split() if cmd_output: version = re.match(r'^(\d+)\.(\d+)\.(\d+)', cmd_output[-1]) if version is not None: - version = [int(x) for x in version.groups()[1:]] + version = [int(x) for x in version.groups()] if version[0] > 1 or \ (version[0] == 1 and version[1] > 7) or \ (version[0] == 1 and version[1] == 7 and version[2] >= 9): @@ -1002,47 +1075,16 @@ def dev_keywords(profiles): dev_keywords = dev_keywords(profiles) -stats={} -fails={} - -# provided by the desktop-file-utils package -desktop_file_validate = find_binary("desktop-file-validate") -desktop_pattern = re.compile(r'.*\.desktop$') +stats = {} +fails = {} for x in qacats: - stats[x]=0 - fails[x]=[] + stats[x] = 0 + fails[x] = [] xmllint_capable = False metadata_dtd = os.path.join(repoman_settings["DISTDIR"], 'metadata.dtd') -def parsedate(s): - """Parse a RFC 822 date and time string. - This is required for python3 compatibility, since the - rfc822.parsedate() function is not available.""" - - s_split = [] - for x in s.upper().split(): - for y in x.split(','): - if y: - s_split.append(y) - - if len(s_split) != 6: - return None - - # %a, %d %b %Y %H:%M:%S %Z - a, d, b, Y, H_M_S, Z = s_split - - # Convert month to integer, since strptime %w is locale-dependent. - month_map = {'JAN':1, 'FEB':2, 'MAR':3, 'APR':4, 'MAY':5, 'JUN':6, - 'JUL':7, 'AUG':8, 'SEP':9, 'OCT':10, 'NOV':11, 'DEC':12} - m = month_map.get(b) - if m is None: - return None - m = str(m).rjust(2, '0') - - return time.strptime(':'.join((Y, m, d, H_M_S)), '%Y:%m:%d:%H:%M:%S') - def fetch_metadata_dtd(): """ Fetch metadata.dtd if it doesn't exist or the ctime is older than @@ -1071,45 +1113,40 @@ def fetch_metadata_dtd(): print(green("***") + " the local copy of metadata.dtd " + \ "needs to be refetched, doing that now") print() + parsed_url = urlparse(metadata_dtd_uri) + setting = 'FETCHCOMMAND_' + parsed_url.scheme.upper() + fcmd = repoman_settings.get(setting) + if not fcmd: + fcmd = repoman_settings.get('FETCHCOMMAND') + if not fcmd: + logging.error("FETCHCOMMAND is unset") + return False + + destdir = repoman_settings["DISTDIR"] + fd, metadata_dtd_tmp = tempfile.mkstemp( + prefix='metadata.dtd.', dir=destdir) + os.close(fd) + try: - url_f = urllib_request_urlopen(metadata_dtd_uri) - msg_info = url_f.info() - last_modified = msg_info.get('last-modified') - if last_modified is not None: - last_modified = parsedate(last_modified) - if last_modified is not None: - last_modified = calendar.timegm(last_modified) - - metadata_dtd_tmp = "%s.%s" % (metadata_dtd, os.getpid()) - try: - local_f = open(metadata_dtd_tmp, mode='wb') - local_f.write(url_f.read()) - local_f.close() - if last_modified is not None: - try: - os.utime(metadata_dtd_tmp, - (int(last_modified), int(last_modified))) - except OSError: - # This fails on some odd non-unix-like filesystems. - # We don't really need the mtime to be preserved - # anyway here (currently we use ctime to trigger - # fetch), so just ignore it. - pass - os.rename(metadata_dtd_tmp, metadata_dtd) - finally: - try: - os.unlink(metadata_dtd_tmp) - except OSError: - pass + if not portage.getbinpkg.file_get(metadata_dtd_uri, + destdir, fcmd=fcmd, + filename=os.path.basename(metadata_dtd_tmp)): + logging.error("failed to fetch metadata.dtd from '%s'" % + metadata_dtd_uri) + return False - url_f.close() + try: + portage.util.apply_secpass_permissions(metadata_dtd_tmp, + gid=portage.data.portage_gid, mode=0o664, mask=0o2) + except portage.exception.PortageException: + pass - except EnvironmentError as e: - print() - print(red("!!!")+" attempting to fetch '%s', caught" % metadata_dtd_uri) - print(red("!!!")+" exception '%s' though." % (e,)) - print(red("!!!")+" fetching new metadata.dtd failed, aborting") - return False + os.rename(metadata_dtd_tmp, metadata_dtd) + finally: + try: + os.unlink(metadata_dtd_tmp) + except OSError: + pass return True @@ -1117,14 +1154,14 @@ if options.mode == "manifest": pass elif not find_binary('xmllint'): print(red("!!! xmllint not found. Can't check metadata.xml.\n")) - if options.xml_parse or repolevel==3: + if options.xml_parse or repolevel == 3: print(red("!!!")+" sorry, xmllint is needed. failing\n") sys.exit(1) else: if not fetch_metadata_dtd(): sys.exit(1) - #this can be problematic if xmllint changes their output - xmllint_capable=True + # this can be problematic if xmllint changes their output + xmllint_capable = True if options.mode == 'commit' and vcs: utilities.detect_vcs_conflicts(options, vcs) @@ -1151,45 +1188,46 @@ if vcs == "cvs": myremoved = cvstree.findremoved(mycvstree, recursive=1, basedir="./") elif vcs == "svn": - with os.popen("svn status") as f: + with repoman_popen("svn status") as f: svnstatus = f.readlines() - mychanged = [ "./" + elem.split()[-1:][0] for elem in svnstatus if elem and elem[:1] in "MR" ] - mynew = [ "./" + elem.split()[-1:][0] for elem in svnstatus if elem.startswith("A") ] + mychanged = ["./" + elem.split()[-1:][0] for elem in svnstatus if elem and elem[:1] in "MR"] + mynew = ["./" + elem.split()[-1:][0] for elem in svnstatus if elem.startswith("A")] if options.if_modified == "y": - myremoved = [ "./" + elem.split()[-1:][0] for elem in svnstatus if elem.startswith("D")] + myremoved = ["./" + elem.split()[-1:][0] for elem in svnstatus if elem.startswith("D")] elif vcs == "git": - with os.popen("git diff-index --name-only " + with repoman_popen("git diff-index --name-only " "--relative --diff-filter=M HEAD") as f: mychanged = f.readlines() mychanged = ["./" + elem[:-1] for elem in mychanged] - with os.popen("git diff-index --name-only " + with repoman_popen("git diff-index --name-only " "--relative --diff-filter=A HEAD") as f: mynew = f.readlines() mynew = ["./" + elem[:-1] for elem in mynew] if options.if_modified == "y": - with os.popen("git diff-index --name-only " + with repoman_popen("git diff-index --name-only " "--relative --diff-filter=D HEAD") as f: myremoved = f.readlines() myremoved = ["./" + elem[:-1] for elem in myremoved] elif vcs == "bzr": - with os.popen("bzr status -S .") as f: + with repoman_popen("bzr status -S .") as f: bzrstatus = f.readlines() - mychanged = [ "./" + elem.split()[-1:][0].split('/')[-1:][0] for elem in bzrstatus if elem and elem[1:2] == "M" ] - mynew = [ "./" + elem.split()[-1:][0].split('/')[-1:][0] for elem in bzrstatus if elem and ( elem[1:2] == "NK" or elem[0:1] == "R" ) ] + mychanged = ["./" + elem.split()[-1:][0].split('/')[-1:][0] for elem in bzrstatus if elem and elem[1:2] == "M"] + mynew = ["./" + elem.split()[-1:][0].split('/')[-1:][0] for elem in bzrstatus if elem and (elem[1:2] == "NK" or elem[0:1] == "R")] if options.if_modified == "y": - myremoved = [ "./" + elem.split()[-3:-2][0].split('/')[-1:][0] for elem in bzrstatus if elem and ( elem[1:2] == "K" or elem[0:1] == "R" ) ] + myremoved = ["./" + elem.split()[-3:-2][0].split('/')[-1:][0] for elem in bzrstatus if elem and (elem[1:2] == "K" or elem[0:1] == "R")] elif vcs == "hg": - with os.popen("hg status --no-status --modified .") as f: + with repoman_popen("hg status --no-status --modified .") as f: mychanged = f.readlines() mychanged = ["./" + elem.rstrip() for elem in mychanged] - mynew = os.popen("hg status --no-status --added .").readlines() + with repoman_popen("hg status --no-status --added .") as f: + mynew = f.readlines() mynew = ["./" + elem.rstrip() for elem in mynew] if options.if_modified == "y": - with os.popen("hg status --no-status --removed .") as f: + with repoman_popen("hg status --no-status --removed .") as f: myremoved = f.readlines() myremoved = ["./" + elem.rstrip() for elem in myremoved] @@ -1211,10 +1249,15 @@ dofail = 0 # NOTE: match-all caches are not shared due to potential # differences between profiles in _get_implicit_iuse. -arch_caches={} +arch_caches = {} arch_xmatch_caches = {} shared_xmatch_caches = {"cp-list":{}} +include_arches = None +if options.include_arches: + include_arches = set() + include_arches.update(*[x.split() for x in options.include_arches]) + # Disable the "ebuild.notadded" check when not in commit mode and # running `svn status` in every package dir will be too expensive. @@ -1222,12 +1265,37 @@ check_ebuild_notadded = not \ (vcs == "svn" and repolevel < 3 and options.mode != "commit") # Build a regex from thirdpartymirrors for the SRC_URI.mirror check. -thirdpartymirrors = [] -for v in repoman_settings.thirdpartymirrors().values(): +thirdpartymirrors = {} +for k, v in repoman_settings.thirdpartymirrors().items(): for v in v: if not v.endswith("/"): v += "/" - thirdpartymirrors.append(v) + thirdpartymirrors[v] = k + +class _XMLParser(xml.etree.ElementTree.XMLParser): + + def __init__(self, data, **kwargs): + xml.etree.ElementTree.XMLParser.__init__(self, **kwargs) + self._portage_data = data + if hasattr(self, 'parser'): + self._base_XmlDeclHandler = self.parser.XmlDeclHandler + self.parser.XmlDeclHandler = self._portage_XmlDeclHandler + self._base_StartDoctypeDeclHandler = \ + self.parser.StartDoctypeDeclHandler + self.parser.StartDoctypeDeclHandler = \ + self._portage_StartDoctypeDeclHandler + + def _portage_XmlDeclHandler(self, version, encoding, standalone): + if self._base_XmlDeclHandler is not None: + self._base_XmlDeclHandler(version, encoding, standalone) + self._portage_data["XML_DECLARATION"] = (version, encoding, standalone) + + def _portage_StartDoctypeDeclHandler(self, doctypeName, systemId, publicId, + has_internal_subset): + if self._base_StartDoctypeDeclHandler is not None: + self._base_StartDoctypeDeclHandler(doctypeName, systemId, publicId, + has_internal_subset) + self._portage_data["DOCTYPE"] = (doctypeName, systemId, publicId) class _MetadataTreeBuilder(xml.etree.ElementTree.TreeBuilder): """ @@ -1252,13 +1320,13 @@ if options.if_modified == "y": chain(mychanged, mynew, myremoved))) for x in effective_scanlist: - #ebuilds and digests added to cvs respectively. + # ebuilds and digests added to cvs respectively. logging.info("checking package %s" % x) # save memory by discarding xmatch caches from previous package(s) arch_xmatch_caches.clear() - eadded=[] - catdir,pkgdir=x.split("/") - checkdir=repodir+"/"+x + eadded = [] + catdir, pkgdir = x.split("/") + checkdir = repodir + "/" + x checkdir_relative = "" if repolevel < 3: checkdir_relative = os.path.join(pkgdir, checkdir_relative) @@ -1340,15 +1408,15 @@ for x in effective_scanlist: if options.mode == 'manifest-check': continue - checkdirlist=os.listdir(checkdir) - ebuildlist=[] + checkdirlist = os.listdir(checkdir) + ebuildlist = [] pkgs = {} allvalid = True for y in checkdirlist: if (y in no_exec or y.endswith(".ebuild")) and \ - stat.S_IMODE(os.stat(os.path.join(checkdir, y)).st_mode) & 0o111: - stats["file.executable"] += 1 - fails["file.executable"].append(os.path.join(checkdir, y)) + stat.S_IMODE(os.stat(os.path.join(checkdir, y)).st_mode) & 0o111: + stats["file.executable"] += 1 + fails["file.executable"].append(os.path.join(checkdir, y)) if y.endswith(".ebuild"): pf = y[:-7] ebuildlist.append(pf) @@ -1389,19 +1457,19 @@ for x in effective_scanlist: ebuildlist = [pkg.pf for pkg in ebuildlist] for y in checkdirlist: - m = disallowed_filename_chars_re.search(y.strip(os.sep)) - if m is not None: + index = repo_config.find_invalid_path_char(y) + if index != -1: y_relative = os.path.join(checkdir_relative, y) if vcs is not None and not vcs_new_changed(y_relative): # If the file isn't in the VCS new or changed set, then # assume that it's an irrelevant temporary file (Manifest # entries are not generated for file names containing # prohibited characters). See bug #406877. - m = None - if m is not None: + index = -1 + if index != -1: stats["file.name"] += 1 fails["file.name"].append("%s/%s: char '%s'" % \ - (checkdir, y, m.group(0))) + (checkdir, y, y[index])) if not (y in ("ChangeLog", "metadata.xml") or y.endswith(".ebuild")): continue @@ -1412,7 +1480,7 @@ for x in effective_scanlist: encoding=_encodings['fs'], errors='strict'), mode='r', encoding=_encodings['repo.content']) for l in f: - line +=1 + line += 1 except UnicodeDecodeError as ue: stats["file.UTF8"] += 1 s = ue.object[:ue.start] @@ -1427,10 +1495,10 @@ for x in effective_scanlist: if vcs in ("git", "hg") and check_ebuild_notadded: if vcs == "git": - myf = os.popen("git ls-files --others %s" % \ + myf = repoman_popen("git ls-files --others %s" % \ (portage._shell_quote(checkdir_relative),)) if vcs == "hg": - myf = os.popen("hg status --no-status --unknown %s" % \ + myf = repoman_popen("hg status --no-status --unknown %s" % \ (portage._shell_quote(checkdir_relative),)) for l in myf: if l[:-1][-7:] == ".ebuild": @@ -1442,21 +1510,23 @@ for x in effective_scanlist: if vcs in ("cvs", "svn", "bzr") and check_ebuild_notadded: try: if vcs == "cvs": - myf=open(checkdir+"/CVS/Entries","r") + myf = open(checkdir + "/CVS/Entries", "r") if vcs == "svn": - myf = os.popen("svn status --depth=files --verbose " + checkdir) + myf = repoman_popen("svn status --depth=files --verbose " + + portage._shell_quote(checkdir)) if vcs == "bzr": - myf = os.popen("bzr ls -v --kind=file " + checkdir) + myf = repoman_popen("bzr ls -v --kind=file " + + portage._shell_quote(checkdir)) myl = myf.readlines() myf.close() for l in myl: if vcs == "cvs": - if l[0]!="/": + if l[0] != "/": continue - splitl=l[1:].split("/") + splitl = l[1:].split("/") if not len(splitl): continue - if splitl[0][-7:]==".ebuild": + if splitl[0][-7:] == ".ebuild": eadded.append(splitl[0][:-7]) if vcs == "svn": if l[:1] == "?": @@ -1474,8 +1544,9 @@ for x in effective_scanlist: if l[-7:] == ".ebuild": eadded.append(os.path.basename(l[:-7])) if vcs == "svn": - myf = os.popen("svn status " + checkdir) - myl=myf.readlines() + myf = repoman_popen("svn status " + + portage._shell_quote(checkdir)) + myl = myf.readlines() myf.close() for l in myl: if l[0] == "A": @@ -1485,7 +1556,7 @@ for x in effective_scanlist: except IOError: if vcs == "cvs": stats["CVS/Entries.IO_error"] += 1 - fails["CVS/Entries.IO_error"].append(checkdir+"/CVS/Entries") + fails["CVS/Entries.IO_error"].append(checkdir + "/CVS/Entries") else: raise continue @@ -1493,7 +1564,7 @@ for x in effective_scanlist: mf = repoman_settings.repositories.get_repo_for_location( os.path.dirname(os.path.dirname(checkdir))) mf = mf.load_manifest(checkdir, repoman_settings["DISTDIR"]) - mydigests=mf.getTypeDigests("DIST") + mydigests = mf.getTypeDigests("DIST") fetchlist_dict = portage.FetchlistDict(checkdir, repoman_settings, portdb) myfiles_all = [] @@ -1509,7 +1580,7 @@ for x in effective_scanlist: # This will be reported as an "ebuild.syntax" error. pass else: - stats["SRC_URI.syntax"] = stats["SRC_URI.syntax"] + 1 + stats["SRC_URI.syntax"] += 1 fails["SRC_URI.syntax"].append( "%s.ebuild SRC_URI: %s" % (mykey, e)) del fetchlist_dict @@ -1523,15 +1594,15 @@ for x in effective_scanlist: for entry in mydigests: if entry not in myfiles_all: stats["digest.unused"] += 1 - fails["digest.unused"].append(checkdir+"::"+entry) + fails["digest.unused"].append(checkdir + "::" + entry) for entry in myfiles_all: if entry not in mydigests: stats["digest.missing"] += 1 - fails["digest.missing"].append(checkdir+"::"+entry) + fails["digest.missing"].append(checkdir + "::" + entry) del myfiles_all - if os.path.exists(checkdir+"/files"): - filesdirlist=os.listdir(checkdir+"/files") + if os.path.exists(checkdir + "/files"): + filesdirlist = os.listdir(checkdir + "/files") # recurse through files directory # use filesdirlist as a stack, appending directories as needed so people can't hide > 20k files in a subdirectory. @@ -1551,77 +1622,110 @@ for x in effective_scanlist: # !!! VCS "portability" alert! Need some function isVcsDir() or alike !!! if y == "CVS" or y == ".svn": continue - for z in os.listdir(checkdir+"/files/"+y): + for z in os.listdir(checkdir + "/files/" + y): if z == "CVS" or z == ".svn": continue - filesdirlist.append(y+"/"+z) + filesdirlist.append(y + "/" + z) # Current policy is no files over 20 KiB, these are the checks. File size between # 20 KiB and 60 KiB causes a warning, while file size over 60 KiB causes an error. elif mystat.st_size > 61440: stats["file.size.fatal"] += 1 - fails["file.size.fatal"].append("("+ str(mystat.st_size//1024) + " KiB) "+x+"/files/"+y) + fails["file.size.fatal"].append("(" + str(mystat.st_size//1024) + " KiB) " + x + "/files/" + y) elif mystat.st_size > 20480: stats["file.size"] += 1 - fails["file.size"].append("("+ str(mystat.st_size//1024) + " KiB) "+x+"/files/"+y) + fails["file.size"].append("(" + str(mystat.st_size//1024) + " KiB) " + x + "/files/" + y) - m = disallowed_filename_chars_re.search( - os.path.basename(y.rstrip(os.sep))) - if m is not None: + index = repo_config.find_invalid_path_char(y) + if index != -1: y_relative = os.path.join(checkdir_relative, "files", y) if vcs is not None and not vcs_new_changed(y_relative): # If the file isn't in the VCS new or changed set, then # assume that it's an irrelevant temporary file (Manifest # entries are not generated for file names containing # prohibited characters). See bug #406877. - m = None - if m is not None: + index = -1 + if index != -1: stats["file.name"] += 1 fails["file.name"].append("%s/files/%s: char '%s'" % \ - (checkdir, y, m.group(0))) - - if desktop_file_validate and desktop_pattern.match(y): - cmd_output = validate_desktop_entry(full_path) - if cmd_output: - # Note: in the future we may want to grab the - # warnings in addition to the errors. We're - # just doing errors now since we don't want - # to generate too much noise at first. - error_re = re.compile(r'.*\s*error:\s*(.*)') - for line in cmd_output: - error_match = error_re.match(line) - if error_match is None: - continue - stats["desktop.invalid"] += 1 - fails["desktop.invalid"].append( - relative_path + ': %s' % error_match.group(1)) - + (checkdir, y, y[index])) del mydigests if check_changelog and "ChangeLog" not in checkdirlist: - stats["changelog.missing"]+=1 - fails["changelog.missing"].append(x+"/ChangeLog") - + stats["changelog.missing"] += 1 + fails["changelog.missing"].append(x + "/ChangeLog") + musedict = {} - #metadata.xml file check + # metadata.xml file check if "metadata.xml" not in checkdirlist: - stats["metadata.missing"]+=1 - fails["metadata.missing"].append(x+"/metadata.xml") - #metadata.xml parse check + stats["metadata.missing"] += 1 + fails["metadata.missing"].append(x + "/metadata.xml") + # metadata.xml parse check else: metadata_bad = False + xml_info = {} + xml_parser = _XMLParser(xml_info, target=_MetadataTreeBuilder()) # read metadata.xml into memory try: _metadata_xml = xml.etree.ElementTree.parse( - os.path.join(checkdir, "metadata.xml"), - parser=xml.etree.ElementTree.XMLParser( - target=_MetadataTreeBuilder())) + _unicode_encode(os.path.join(checkdir, "metadata.xml"), + encoding=_encodings['fs'], errors='strict'), + parser=xml_parser) except (ExpatError, SyntaxError, EnvironmentError) as e: metadata_bad = True stats["metadata.bad"] += 1 fails["metadata.bad"].append("%s/metadata.xml: %s" % (x, e)) del e else: + if not hasattr(xml_parser, 'parser') or \ + sys.hexversion < 0x2070000 or \ + (sys.hexversion > 0x3000000 and sys.hexversion < 0x3020000): + # doctype is not parsed with python 2.6 or 3.1 + pass + else: + if "XML_DECLARATION" not in xml_info: + stats["metadata.bad"] += 1 + fails["metadata.bad"].append("%s/metadata.xml: " + "xml declaration is missing on first line, " + "should be '%s'" % (x, metadata_xml_declaration)) + else: + xml_version, xml_encoding, xml_standalone = \ + xml_info["XML_DECLARATION"] + if xml_encoding is None or \ + xml_encoding.upper() != metadata_xml_encoding: + stats["metadata.bad"] += 1 + if xml_encoding is None: + encoding_problem = "but it is undefined" + else: + encoding_problem = "not '%s'" % xml_encoding + fails["metadata.bad"].append("%s/metadata.xml: " + "xml declaration encoding should be '%s', %s" % + (x, metadata_xml_encoding, encoding_problem)) + + if "DOCTYPE" not in xml_info: + metadata_bad = True + stats["metadata.bad"] += 1 + fails["metadata.bad"].append("%s/metadata.xml: %s" % (x, + "DOCTYPE is missing")) + else: + doctype_name, doctype_system, doctype_pubid = \ + xml_info["DOCTYPE"] + if doctype_system != metadata_dtd_uri: + stats["metadata.bad"] += 1 + if doctype_system is None: + system_problem = "but it is undefined" + else: + system_problem = "not '%s'" % doctype_system + fails["metadata.bad"].append("%s/metadata.xml: " + "DOCTYPE: SYSTEM should refer to '%s', %s" % + (x, metadata_dtd_uri, system_problem)) + + if doctype_name != metadata_doctype_name: + stats["metadata.bad"] += 1 + fails["metadata.bad"].append("%s/metadata.xml: " + "DOCTYPE: name should be '%s', not '%s'" % + (x, metadata_doctype_name, doctype_name)) + # load USE flags from metadata.xml try: musedict = utilities.parse_metadata_use(_metadata_xml) @@ -1629,6 +1733,22 @@ for x in effective_scanlist: metadata_bad = True stats["metadata.bad"] += 1 fails["metadata.bad"].append("%s/metadata.xml: %s" % (x, e)) + else: + for atom in chain(*musedict.values()): + if atom is None: + continue + try: + atom = Atom(atom) + except InvalidAtom as e: + stats["metadata.bad"] += 1 + fails["metadata.bad"].append( + "%s/metadata.xml: Invalid atom: %s" % (x, e)) + else: + if atom.cp != x: + stats["metadata.bad"] += 1 + fails["metadata.bad"].append( + ("%s/metadata.xml: Atom contains " + "unexpected cat/pn: %s") % (x, atom)) # Run other metadata.xml checkers try: @@ -1639,19 +1759,20 @@ for x in effective_scanlist: fails["metadata.bad"].append("%s/metadata.xml: %s" % (x, e)) del e - #Only carry out if in package directory or check forced + # Only carry out if in package directory or check forced if xmllint_capable and not metadata_bad: # xmlint can produce garbage output even on success, so only dump # the ouput when it fails. - st, out = subprocess_getstatusoutput( - "xmllint --nonet --noout --dtdvalid '%s' '%s'" % \ - (metadata_dtd, os.path.join(checkdir, "metadata.xml"))) + st, out = repoman_getstatusoutput( + "xmllint --nonet --noout --dtdvalid %s %s" % \ + (portage._shell_quote(metadata_dtd), + portage._shell_quote(os.path.join(checkdir, "metadata.xml")))) if st != os.EX_OK: print(red("!!!") + " metadata.xml is invalid:") for z in out.splitlines(): - print(red("!!! ")+z) - stats["metadata.bad"]+=1 - fails["metadata.bad"].append(x+"/metadata.xml") + print(red("!!! ") + z) + stats["metadata.bad"] += 1 + fails["metadata.bad"].append(x + "/metadata.xml") del metadata_bad muselist = frozenset(musedict) @@ -1677,20 +1798,20 @@ for x in effective_scanlist: fails['changelog.ebuildadded'].append(relative_path) if vcs in ("cvs", "svn", "bzr") and check_ebuild_notadded and y not in eadded: - #ebuild not added to vcs - stats["ebuild.notadded"]=stats["ebuild.notadded"]+1 - fails["ebuild.notadded"].append(x+"/"+y+".ebuild") - myesplit=portage.pkgsplit(y) + # ebuild not added to vcs + stats["ebuild.notadded"] += 1 + fails["ebuild.notadded"].append(x + "/" + y + ".ebuild") + myesplit = portage.pkgsplit(y) if myesplit is None or myesplit[0] != x.split("/")[-1] \ or pv_toolong_re.search(myesplit[1]) \ or pv_toolong_re.search(myesplit[2]): - stats["ebuild.invalidname"]=stats["ebuild.invalidname"]+1 - fails["ebuild.invalidname"].append(x+"/"+y+".ebuild") + stats["ebuild.invalidname"] += 1 + fails["ebuild.invalidname"].append(x + "/" + y + ".ebuild") continue - elif myesplit[0]!=pkgdir: - print(pkgdir,myesplit[0]) - stats["ebuild.namenomatch"]=stats["ebuild.namenomatch"]+1 - fails["ebuild.namenomatch"].append(x+"/"+y+".ebuild") + elif myesplit[0] != pkgdir: + print(pkgdir, myesplit[0]) + stats["ebuild.namenomatch"] += 1 + fails["ebuild.namenomatch"].append(x + "/" + y + ".ebuild") continue pkg = pkgs[y] @@ -1699,15 +1820,25 @@ for x in effective_scanlist: allvalid = False for k, msgs in pkg.invalid.items(): for msg in msgs: - stats[k] = stats[k] + 1 - fails[k].append("%s %s" % (relative_path, msg)) + stats[k] += 1 + fails[k].append("%s: %s" % (relative_path, msg)) continue - myaux = pkg.metadata + myaux = pkg._metadata eapi = myaux["EAPI"] inherited = pkg.inherited live_ebuild = live_eclasses.intersection(inherited) + if repo_config.eapi_is_banned(eapi): + stats["repo.eapi.banned"] += 1 + fails["repo.eapi.banned"].append( + "%s: %s" % (relative_path, eapi)) + + elif repo_config.eapi_is_deprecated(eapi): + stats["repo.eapi.deprecated"] += 1 + fails["repo.eapi.deprecated"].append( + "%s: %s" % (relative_path, eapi)) + for k, v in myaux.items(): if not isinstance(v, basestring): continue @@ -1724,20 +1855,21 @@ for x in effective_scanlist: for uri in portage.dep.use_reduce( \ myaux["SRC_URI"], matchall=True, is_src_uri=True, eapi=eapi, flat=True): contains_mirror = False - for mirror in thirdpartymirrors: + for mirror, mirror_alias in thirdpartymirrors.items(): if uri.startswith(mirror): contains_mirror = True break if not contains_mirror: continue + new_uri = "mirror://%s/%s" % (mirror_alias, uri[len(mirror):]) stats["SRC_URI.mirror"] += 1 fails["SRC_URI.mirror"].append( - "%s: '%s' found in thirdpartymirrors" % \ - (relative_path, mirror)) + "%s: '%s' found in thirdpartymirrors, use '%s'" % \ + (relative_path, mirror, new_uri)) if myaux.get("PROVIDE"): - stats["virtual.oldstyle"]+=1 + stats["virtual.oldstyle"] += 1 fails["virtual.oldstyle"].append(relative_path) for pos, missing_var in enumerate(missingvars): @@ -1747,15 +1879,15 @@ for x in effective_scanlist: continue if live_ebuild and missing_var == "KEYWORDS": continue - myqakey=missingvars[pos]+".missing" - stats[myqakey]=stats[myqakey]+1 - fails[myqakey].append(x+"/"+y+".ebuild") + myqakey = missingvars[pos] + ".missing" + stats[myqakey] += 1 + fails[myqakey].append(x + "/" + y + ".ebuild") if catdir == "virtual": for var in ("HOMEPAGE", "LICENSE"): if myaux.get(var): myqakey = var + ".virtual" - stats[myqakey] = stats[myqakey] + 1 + stats[myqakey] += 1 fails[myqakey].append(relative_path) # 14 is the length of DESCRIPTION="" @@ -1772,7 +1904,7 @@ for x in effective_scanlist: not keyword.startswith("-"): stable_keywords.append(keyword) if stable_keywords: - if ebuild_path in new_ebuilds: + if ebuild_path in new_ebuilds and catdir != "virtual": stable_keywords.sort() stats["KEYWORDS.stable"] += 1 fails["KEYWORDS.stable"].append( @@ -1782,10 +1914,10 @@ for x in effective_scanlist: ebuild_archs = set(kw.lstrip("~") for kw in keywords \ if not kw.startswith("-")) - previous_keywords = slot_keywords.get(myaux["SLOT"]) + previous_keywords = slot_keywords.get(pkg.slot) if previous_keywords is None: - slot_keywords[myaux["SLOT"]] = set() - elif ebuild_archs and not live_ebuild: + slot_keywords[pkg.slot] = set() + elif ebuild_archs and "*" not in ebuild_archs and not live_ebuild: dropped_keywords = previous_keywords.difference(ebuild_archs) if dropped_keywords: stats["KEYWORDS.dropped"] += 1 @@ -1793,7 +1925,7 @@ for x in effective_scanlist: relative_path + ": %s" % \ " ".join(sorted(dropped_keywords))) - slot_keywords[myaux["SLOT"]].update(ebuild_archs) + slot_keywords[pkg.slot].update(ebuild_archs) # KEYWORDS="-*" is a stupid replacement for package.mask and screws general KEYWORDS semantics if "-*" in keywords: @@ -1805,7 +1937,7 @@ for x in effective_scanlist: haskeyword = True if not haskeyword: stats["KEYWORDS.stupid"] += 1 - fails["KEYWORDS.stupid"].append(x+"/"+y+".ebuild") + fails["KEYWORDS.stupid"].append(x + "/" + y + ".ebuild") """ Ebuilds that inherit a "Live" eclass (darcs,subversion,git,cvs,etc..) should @@ -1833,37 +1965,53 @@ for x in effective_scanlist: arches = [[repoman_settings["ARCH"], repoman_settings["ARCH"], repoman_settings["ACCEPT_KEYWORDS"].split()]] else: - arches=[] - for keyword in myaux["KEYWORDS"].split(): - if (keyword[0]=="-"): + arches = set() + for keyword in keywords: + if keyword[0] == "-": continue - elif (keyword[0]=="~"): - arches.append([keyword, keyword[1:], [keyword[1:], keyword]]) + elif keyword[0] == "~": + arch = keyword[1:] + if arch == "*": + for expanded_arch in profiles: + if expanded_arch == "**": + continue + arches.add((keyword, expanded_arch, + (expanded_arch, "~" + expanded_arch))) + else: + arches.add((keyword, arch, (arch, keyword))) else: - arches.append([keyword, keyword, [keyword]]) + if keyword == "*": + for expanded_arch in profiles: + if expanded_arch == "**": + continue + arches.add((keyword, expanded_arch, + (expanded_arch,))) + else: + arches.add((keyword, keyword, (keyword,))) if not arches: # Use an empty profile for checking dependencies of # packages that have empty KEYWORDS. - arches.append(['**', '**', ['**']]) + arches.add(('**', '**', ('**',))) unknown_pkgs = set() baddepsyntax = False badlicsyntax = False badprovsyntax = False - catpkg = catdir+"/"+y + catpkg = catdir + "/" + y inherited_java_eclass = "java-pkg-2" in inherited or \ "java-pkg-opt-2" in inherited inherited_wxwidgets_eclass = "wxwidgets" in inherited operator_tokens = set(["||", "(", ")"]) type_list, badsyntax = [], [] - for mytype in ("DEPEND", "RDEPEND", "PDEPEND", - "LICENSE", "PROPERTIES", "PROVIDE"): + for mytype in Package._dep_keys + ("LICENSE", "PROPERTIES", "PROVIDE"): mydepstr = myaux[mytype] + buildtime = mytype in Package._buildtime_keys + runtime = mytype in Package._runtime_keys token_class = None - if mytype in ("DEPEND", "RDEPEND", "PDEPEND"): - token_class=portage.dep.Atom + if mytype.endswith("DEPEND"): + token_class = portage.dep.Atom try: atoms = portage.dep.use_reduce(mydepstr, matchall=1, flat=True, \ @@ -1872,8 +2020,8 @@ for x in effective_scanlist: atoms = None badsyntax.append(str(e)) - if atoms and mytype in ("DEPEND", "RDEPEND", "PDEPEND"): - if mytype in ("RDEPEND", "PDEPEND") and \ + if atoms and mytype.endswith("DEPEND"): + if runtime and \ "test?" in mydepstr.split(): stats[mytype + '.suspect'] += 1 fails[mytype + '.suspect'].append(relative_path + \ @@ -1902,21 +2050,21 @@ for x in effective_scanlist: ": %s: consider using '%s' instead of '%s'" % (mytype, suspect_virtual[atom.cp], atom)) - if mytype == "DEPEND" and \ + if buildtime and \ not is_blocker and \ not inherited_java_eclass and \ atom.cp == "virtual/jdk": stats['java.eclassesnotused'] += 1 fails['java.eclassesnotused'].append(relative_path) - elif mytype == "DEPEND" and \ + elif buildtime and \ not is_blocker and \ not inherited_wxwidgets_eclass and \ atom.cp == "x11-libs/wxGTK": stats['wxwidgets.eclassnotused'] += 1 fails['wxwidgets.eclassnotused'].append( - relative_path + ": DEPENDs on x11-libs/wxGTK" - " without inheriting wxwidgets.eclass") - elif mytype in ("PDEPEND", "RDEPEND"): + (relative_path + ": %ss on x11-libs/wxGTK" + " without inheriting wxwidgets.eclass") % mytype) + elif runtime: if not is_blocker and \ atom.cp in suspect_rdepend: stats[mytype + '.suspect'] += 1 @@ -1925,21 +2073,26 @@ for x in effective_scanlist: if atom.operator == "~" and \ portage.versions.catpkgsplit(atom.cpv)[3] != "r0": - stats[mytype + '.badtilde'] += 1 - fails[mytype + '.badtilde'].append( + qacat = 'dependency.badtilde' + stats[qacat] += 1 + fails[qacat].append( (relative_path + ": %s uses the ~ operator" " with a non-zero revision:" + \ " '%s'") % (mytype, atom)) type_list.extend([mytype] * (len(badsyntax) - len(type_list))) - for m,b in zip(type_list, badsyntax): - stats[m+".syntax"] += 1 - fails[m+".syntax"].append(catpkg+".ebuild "+m+": "+b) + for m, b in zip(type_list, badsyntax): + if m.endswith("DEPEND"): + qacat = "dependency.syntax" + else: + qacat = m + ".syntax" + stats[qacat] += 1 + fails[qacat].append("%s: %s: %s" % (relative_path, m, b)) badlicsyntax = len([z for z in type_list if z == "LICENSE"]) badprovsyntax = len([z for z in type_list if z == "PROVIDE"]) - baddepsyntax = len(type_list) != badlicsyntax + badprovsyntax + baddepsyntax = len(type_list) != badlicsyntax + badprovsyntax badlicsyntax = badlicsyntax > 0 badprovsyntax = badprovsyntax > 0 @@ -1955,7 +2108,7 @@ for x in effective_scanlist: myuse.append(flag_name) # uselist checks - metadata - for mypos in range(len(myuse)-1,-1,-1): + for mypos in range(len(myuse)-1, -1, -1): if myuse[mypos] and (myuse[mypos] in muselist): del myuse[mypos] @@ -1968,8 +2121,17 @@ for x in effective_scanlist: " '%s'") % (eapi, myflag)) for mypos in range(len(myuse)): - stats["IUSE.invalid"]=stats["IUSE.invalid"]+1 - fails["IUSE.invalid"].append(x+"/"+y+".ebuild: %s" % myuse[mypos]) + stats["IUSE.invalid"] += 1 + fails["IUSE.invalid"].append(x + "/" + y + ".ebuild: %s" % myuse[mypos]) + + # Check for outdated RUBY targets + if "ruby-ng" in inherited or "ruby-fakegem" in inherited or "ruby" in inherited: + ruby_intersection = pkg.iuse.all.intersection(ruby_deprecated) + if ruby_intersection: + for myruby in ruby_intersection: + stats["IUSE.rubydeprecated"] += 1 + fails["IUSE.rubydeprecated"].append( + (relative_path + ": Deprecated ruby target: %s") % myruby) # license checks if not badlicsyntax: @@ -1982,10 +2144,13 @@ for x in effective_scanlist: # Need to check for "||" manually as no portage # function will remove it without removing values. if lic not in liclist and lic != "||": - stats["LICENSE.invalid"]=stats["LICENSE.invalid"]+1 - fails["LICENSE.invalid"].append(x+"/"+y+".ebuild: %s" % lic) + stats["LICENSE.invalid"] += 1 + fails["LICENSE.invalid"].append(x + "/" + y + ".ebuild: %s" % lic) + elif lic in liclist_deprecated: + stats["LICENSE.deprecated"] += 1 + fails["LICENSE.deprecated"].append("%s: %s" % (relative_path, lic)) - #keyword checks + # keyword checks myuse = myaux["KEYWORDS"].split() for mykey in myuse: if mykey not in ("-*", "*", "~*"): @@ -1996,17 +2161,17 @@ for x in effective_scanlist: myskey = myskey[1:] if myskey not in kwlist: stats["KEYWORDS.invalid"] += 1 - fails["KEYWORDS.invalid"].append(x+"/"+y+".ebuild: %s" % mykey) + fails["KEYWORDS.invalid"].append(x + "/" + y + ".ebuild: %s" % mykey) elif myskey not in profiles: stats["KEYWORDS.invalid"] += 1 - fails["KEYWORDS.invalid"].append(x+"/"+y+".ebuild: %s (profile invalid)" % mykey) + fails["KEYWORDS.invalid"].append(x + "/" + y + ".ebuild: %s (profile invalid)" % mykey) - #restrict checks + # restrict checks myrestrict = None try: myrestrict = portage.dep.use_reduce(myaux["RESTRICT"], matchall=1, flat=True) except portage.exception.InvalidDependString as e: - stats["RESTRICT.syntax"] = stats["RESTRICT.syntax"] + 1 + stats["RESTRICT.syntax"] += 1 fails["RESTRICT.syntax"].append( "%s: RESTRICT: %s" % (relative_path, e)) del e @@ -2016,8 +2181,8 @@ for x in effective_scanlist: if mybadrestrict: stats["RESTRICT.invalid"] += len(mybadrestrict) for mybad in mybadrestrict: - fails["RESTRICT.invalid"].append(x+"/"+y+".ebuild: %s" % mybad) - #REQUIRED_USE check + fails["RESTRICT.invalid"].append(x + "/" + y + ".ebuild: %s" % mybad) + # REQUIRED_USE check required_use = myaux["REQUIRED_USE"] if required_use: if not eapi_has_required_use(eapi): @@ -2027,9 +2192,9 @@ for x in effective_scanlist: " not supported with EAPI='%s'" % (eapi,)) try: portage.dep.check_required_use(required_use, (), - pkg.iuse.is_valid_flag) + pkg.iuse.is_valid_flag, eapi=eapi) except portage.exception.InvalidDependString as e: - stats["REQUIRED_USE.syntax"] = stats["REQUIRED_USE.syntax"] + 1 + stats["REQUIRED_USE.syntax"] += 1 fails["REQUIRED_USE.syntax"].append( "%s: REQUIRED_USE: %s" % (relative_path, e)) del e @@ -2062,127 +2227,154 @@ for x in effective_scanlist: # user is intent on forcing the commit anyway. continue - for keyword,arch,groups in arches: - + relevant_profiles = [] + for keyword, arch, groups in arches: if arch not in profiles: # A missing profile will create an error further down # during the KEYWORDS verification. continue - - for prof in profiles[arch]: - if prof.status not in ("stable", "dev") or \ - prof.status == "dev" and not options.include_dev: + if include_arches is not None: + if arch not in include_arches: continue - dep_settings = arch_caches.get(prof.sub_path) - if dep_settings is None: - dep_settings = portage.config( - config_profile_path=prof.abs_path, - config_incrementals=repoman_incrementals, - config_root=config_root, - local_config=False, - _unmatched_removal=options.unmatched_removal, - env=env) - dep_settings.categories = repoman_settings.categories - if options.without_mask: - dep_settings._mask_manager_obj = \ - copy.deepcopy(dep_settings._mask_manager) - dep_settings._mask_manager._pmaskdict.clear() - arch_caches[prof.sub_path] = dep_settings - - xmatch_cache_key = (prof.sub_path, tuple(groups)) - xcache = arch_xmatch_caches.get(xmatch_cache_key) - if xcache is None: - portdb.melt() - portdb.freeze() - xcache = portdb.xcache - xcache.update(shared_xmatch_caches) - arch_xmatch_caches[xmatch_cache_key] = xcache - - trees[root]["porttree"].settings = dep_settings - portdb.settings = dep_settings - portdb.xcache = xcache - # for package.use.mask support inside dep_check - dep_settings.setcpv(pkg) - dep_settings["ACCEPT_KEYWORDS"] = " ".join(groups) - # just in case, prevent config.reset() from nuking these. - dep_settings.backup_changes("ACCEPT_KEYWORDS") - - if not baddepsyntax: - ismasked = not ebuild_archs or \ - pkg.cpv not in portdb.xmatch("match-visible", pkg.cp) - if ismasked: - if not have_pmasked: - have_pmasked = bool(dep_settings._getMaskAtom( - pkg.cpv, pkg.metadata)) - if options.ignore_masked: - continue - #we are testing deps for a masked package; give it some lee-way - suffix="masked" - matchmode = "minimum-all" - else: - suffix="" - matchmode = "minimum-visible" - - if not have_dev_keywords: - have_dev_keywords = \ - bool(dev_keywords.intersection(keywords)) - - if prof.status == "dev": - suffix=suffix+"indev" - - for mytype,mypos in [["DEPEND",len(missingvars)],["RDEPEND",len(missingvars)+1],["PDEPEND",len(missingvars)+2]]: - - mykey=mytype+".bad"+suffix - myvalue = myaux[mytype] - if not myvalue: - continue - - success, atoms = portage.dep_check(myvalue, portdb, - dep_settings, use="all", mode=matchmode, - trees=trees) - - if success: - if atoms: - - # Don't bother with dependency.unknown for - # cases in which *DEPEND.bad is triggered. - for atom in atoms: - # dep_check returns all blockers and they - # aren't counted for *DEPEND.bad, so we - # ignore them here. - if not atom.blocker: - unknown_pkgs.discard( - (mytype, atom.unevaluated_atom)) - - if not prof.sub_path: - # old-style virtuals currently aren't - # resolvable with empty profile, since - # 'virtuals' mappings are unavailable - # (it would be expensive to search - # for PROVIDE in all ebuilds) - atoms = [atom for atom in atoms if not \ - (atom.cp.startswith('virtual/') and \ - not portdb.cp_list(atom.cp))] - - #we have some unsolvable deps - #remove ! deps, which always show up as unsatisfiable - atoms = [str(atom.unevaluated_atom) \ - for atom in atoms if not atom.blocker] - - #if we emptied out our list, continue: - if not atoms: - continue - stats[mykey]=stats[mykey]+1 - fails[mykey].append("%s: %s(%s) %s" % \ - (relative_path, keyword, - prof, repr(atoms))) - else: - stats[mykey]=stats[mykey]+1 - fails[mykey].append("%s: %s(%s) %s" % \ - (relative_path, keyword, + relevant_profiles.extend((keyword, groups, prof) + for prof in profiles[arch]) + + def sort_key(item): + return item[2].sub_path + + relevant_profiles.sort(key=sort_key) + + for keyword, groups, prof in relevant_profiles: + + if not (prof.status == "stable" or \ + (prof.status == "dev" and options.include_dev) or \ + (prof.status == "exp" and options.include_exp_profiles == 'y')): + continue + + dep_settings = arch_caches.get(prof.sub_path) + if dep_settings is None: + dep_settings = portage.config( + config_profile_path=prof.abs_path, + config_incrementals=repoman_incrementals, + config_root=config_root, + local_config=False, + _unmatched_removal=options.unmatched_removal, + env=env, repositories=repoman_settings.repositories) + dep_settings.categories = repoman_settings.categories + if options.without_mask: + dep_settings._mask_manager_obj = \ + copy.deepcopy(dep_settings._mask_manager) + dep_settings._mask_manager._pmaskdict.clear() + arch_caches[prof.sub_path] = dep_settings + + xmatch_cache_key = (prof.sub_path, tuple(groups)) + xcache = arch_xmatch_caches.get(xmatch_cache_key) + if xcache is None: + portdb.melt() + portdb.freeze() + xcache = portdb.xcache + xcache.update(shared_xmatch_caches) + arch_xmatch_caches[xmatch_cache_key] = xcache + + trees[root]["porttree"].settings = dep_settings + portdb.settings = dep_settings + portdb.xcache = xcache + + dep_settings["ACCEPT_KEYWORDS"] = " ".join(groups) + # just in case, prevent config.reset() from nuking these. + dep_settings.backup_changes("ACCEPT_KEYWORDS") + + # This attribute is used in dbapi._match_use() to apply + # use.stable.{mask,force} settings based on the stable + # status of the parent package. This is required in order + # for USE deps of unstable packages to be resolved correctly, + # since otherwise use.stable.{mask,force} settings of + # dependencies may conflict (see bug #456342). + dep_settings._parent_stable = dep_settings._isStable(pkg) + + # Handle package.use*.{force,mask) calculation, for use + # in dep_check. + dep_settings.useforce = dep_settings._use_manager.getUseForce( + pkg, stable=dep_settings._parent_stable) + dep_settings.usemask = dep_settings._use_manager.getUseMask( + pkg, stable=dep_settings._parent_stable) + + if not baddepsyntax: + ismasked = not ebuild_archs or \ + pkg.cpv not in portdb.xmatch("match-visible", pkg.cp) + if ismasked: + if not have_pmasked: + have_pmasked = bool(dep_settings._getMaskAtom( + pkg.cpv, pkg._metadata)) + if options.ignore_masked: + continue + # we are testing deps for a masked package; give it some lee-way + suffix = "masked" + matchmode = "minimum-all" + else: + suffix = "" + matchmode = "minimum-visible" + + if not have_dev_keywords: + have_dev_keywords = \ + bool(dev_keywords.intersection(keywords)) + + if prof.status == "dev": + suffix = suffix + "indev" + + for mytype in Package._dep_keys: + + mykey = "dependency.bad" + suffix + myvalue = myaux[mytype] + if not myvalue: + continue + + success, atoms = portage.dep_check(myvalue, portdb, + dep_settings, use="all", mode=matchmode, + trees=trees) + + if success: + if atoms: + + # Don't bother with dependency.unknown for + # cases in which *DEPEND.bad is triggered. + for atom in atoms: + # dep_check returns all blockers and they + # aren't counted for *DEPEND.bad, so we + # ignore them here. + if not atom.blocker: + unknown_pkgs.discard( + (mytype, atom.unevaluated_atom)) + + if not prof.sub_path: + # old-style virtuals currently aren't + # resolvable with empty profile, since + # 'virtuals' mappings are unavailable + # (it would be expensive to search + # for PROVIDE in all ebuilds) + atoms = [atom for atom in atoms if not \ + (atom.cp.startswith('virtual/') and \ + not portdb.cp_list(atom.cp))] + + # we have some unsolvable deps + # remove ! deps, which always show up as unsatisfiable + atoms = [str(atom.unevaluated_atom) \ + for atom in atoms if not atom.blocker] + + # if we emptied out our list, continue: + if not atoms: + continue + stats[mykey] += 1 + fails[mykey].append("%s: %s: %s(%s) %s" % \ + (relative_path, mytype, keyword, prof, repr(atoms))) + else: + stats[mykey] += 1 + fails[mykey].append("%s: %s: %s(%s) %s" % \ + (relative_path, mytype, keyword, + prof, repr(atoms))) if not baddepsyntax and unknown_pkgs: type_map = {} @@ -2208,11 +2400,11 @@ if options.if_modified == "y" and len(effective_scanlist) < 1: if options.mode == "manifest": sys.exit(dofail) -#dofail will be set to 1 if we have failed in at least one non-warning category -dofail=0 -#dowarn will be set to 1 if we tripped any warnings -dowarn=0 -#dofull will be set if we should print a "repoman full" informational message +# dofail will be set to 1 if we have failed in at least one non-warning category +dofail = 0 +# dowarn will be set to 1 if we tripped any warnings +dowarn = 0 +# dofull will be set if we should print a "repoman full" informational message dofull = options.mode != 'full' for x in qacats: @@ -2240,29 +2432,20 @@ console_writer.style_listener = style_file.new_styles f = formatter.AbstractFormatter(console_writer) -utilities.format_qa_output(f, stats, fails, dofull, dofail, options, qawarnings) +format_outputs = { + 'column': utilities.format_qa_output_column, + 'default': utilities.format_qa_output +} + +format_output = format_outputs.get(options.output_style, + format_outputs['default']) +format_output(f, stats, fails, dofull, dofail, options, qawarnings) style_file.flush() del console_writer, f, style_file qa_output = qa_output.getvalue() qa_output = qa_output.splitlines(True) -def grouplist(mylist,seperator="/"): - """(list,seperator="/") -- Takes a list of elements; groups them into - same initial element categories. Returns a dict of {base:[sublist]} - From: ["blah/foo","spork/spatula","blah/weee/splat"] - To: {"blah":["foo","weee/splat"], "spork":["spatula"]}""" - mygroups={} - for x in mylist: - xs=x.split(seperator) - if xs[0]==".": - xs=xs[1:] - if xs[0] not in mygroups: - mygroups[xs[0]]=[seperator.join(xs[1:])] - else: - mygroups[xs[0]]+=[seperator.join(xs[1:])] - return mygroups - suggest_ignore_masked = False suggest_include_dev = False @@ -2311,65 +2494,65 @@ else: myunadded = [] if vcs == "cvs": try: - myvcstree=portage.cvstree.getentries("./",recursive=1) - myunadded=portage.cvstree.findunadded(myvcstree,recursive=1,basedir="./") + myvcstree = portage.cvstree.getentries("./", recursive=1) + myunadded = portage.cvstree.findunadded(myvcstree, recursive=1, basedir="./") except SystemExit as e: raise # TODO propagate this except: err("Error retrieving CVS tree; exiting.") if vcs == "svn": try: - with os.popen("svn status --no-ignore") as f: + with repoman_popen("svn status --no-ignore") as f: svnstatus = f.readlines() - myunadded = [ "./"+elem.rstrip().split()[1] for elem in svnstatus if elem.startswith("?") or elem.startswith("I") ] + myunadded = ["./" + elem.rstrip().split()[1] for elem in svnstatus if elem.startswith("?") or elem.startswith("I")] except SystemExit as e: raise # TODO propagate this except: err("Error retrieving SVN info; exiting.") if vcs == "git": # get list of files not under version control or missing - myf = os.popen("git ls-files --others") - myunadded = [ "./" + elem[:-1] for elem in myf ] + myf = repoman_popen("git ls-files --others") + myunadded = ["./" + elem[:-1] for elem in myf] myf.close() if vcs == "bzr": try: - with os.popen("bzr status -S .") as f: + with repoman_popen("bzr status -S .") as f: bzrstatus = f.readlines() - myunadded = [ "./"+elem.rstrip().split()[1].split('/')[-1:][0] for elem in bzrstatus if elem.startswith("?") or elem[0:2] == " D" ] + myunadded = ["./" + elem.rstrip().split()[1].split('/')[-1:][0] for elem in bzrstatus if elem.startswith("?") or elem[0:2] == " D"] except SystemExit as e: raise # TODO propagate this except: err("Error retrieving bzr info; exiting.") if vcs == "hg": - with os.popen("hg status --no-status --unknown .") as f: + with repoman_popen("hg status --no-status --unknown .") as f: myunadded = f.readlines() myunadded = ["./" + elem.rstrip() for elem in myunadded] - + # Mercurial doesn't handle manually deleted files as removed from # the repository, so the user need to remove them before commit, # using "hg remove [FILES]" - with os.popen("hg status --no-status --deleted .") as f: + with repoman_popen("hg status --no-status --deleted .") as f: mydeleted = f.readlines() mydeleted = ["./" + elem.rstrip() for elem in mydeleted] - myautoadd=[] + myautoadd = [] if myunadded: - for x in range(len(myunadded)-1,-1,-1): - xs=myunadded[x].split("/") - if xs[-1]=="files": + for x in range(len(myunadded)-1, -1, -1): + xs = myunadded[x].split("/") + if xs[-1] == "files": print("!!! files dir is not added! Please correct this.") sys.exit(-1) - elif xs[-1]=="Manifest": + elif xs[-1] == "Manifest": # It's a manifest... auto add - myautoadd+=[myunadded[x]] + myautoadd += [myunadded[x]] del myunadded[x] if myunadded: print(red("!!! The following files are in your local tree but are not added to the master")) print(red("!!! tree. Please remove them from the local tree or add them to the master tree.")) for x in myunadded: - print(" ",x) + print(" ", x) print() print() sys.exit(1) @@ -2378,7 +2561,7 @@ else: print(red("!!! The following files are removed manually from your local tree but are not")) print(red("!!! removed from the repository. Please remove them, using \"hg remove [FILES]\".")) for x in mydeleted: - print(" ",x) + print(" ", x) print() print() sys.exit(1) @@ -2387,60 +2570,59 @@ else: mycvstree = cvstree.getentries("./", recursive=1) mychanged = cvstree.findchanged(mycvstree, recursive=1, basedir="./") mynew = cvstree.findnew(mycvstree, recursive=1, basedir="./") - myremoved=portage.cvstree.findremoved(mycvstree,recursive=1,basedir="./") + myremoved = portage.cvstree.findremoved(mycvstree, recursive=1, basedir="./") bin_blob_pattern = re.compile("^-kb$") no_expansion = set(portage.cvstree.findoption(mycvstree, bin_blob_pattern, recursive=1, basedir="./")) - if vcs == "svn": - with os.popen("svn status") as f: + with repoman_popen("svn status") as f: svnstatus = f.readlines() - mychanged = [ "./" + elem.split()[-1:][0] for elem in svnstatus if (elem[:1] in "MR" or elem[1:2] in "M")] - mynew = [ "./" + elem.split()[-1:][0] for elem in svnstatus if elem.startswith("A")] - myremoved = [ "./" + elem.split()[-1:][0] for elem in svnstatus if elem.startswith("D")] + mychanged = ["./" + elem.split()[-1:][0] for elem in svnstatus if (elem[:1] in "MR" or elem[1:2] in "M")] + mynew = ["./" + elem.split()[-1:][0] for elem in svnstatus if elem.startswith("A")] + myremoved = ["./" + elem.split()[-1:][0] for elem in svnstatus if elem.startswith("D")] # Subversion expands keywords specified in svn:keywords properties. - with os.popen("svn propget -R svn:keywords") as f: + with repoman_popen("svn propget -R svn:keywords") as f: props = f.readlines() expansion = dict(("./" + prop.split(" - ")[0], prop.split(" - ")[1].split()) \ for prop in props if " - " in prop) elif vcs == "git": - with os.popen("git diff-index --name-only " + with repoman_popen("git diff-index --name-only " "--relative --diff-filter=M HEAD") as f: mychanged = f.readlines() mychanged = ["./" + elem[:-1] for elem in mychanged] - with os.popen("git diff-index --name-only " + with repoman_popen("git diff-index --name-only " "--relative --diff-filter=A HEAD") as f: mynew = f.readlines() mynew = ["./" + elem[:-1] for elem in mynew] - with os.popen("git diff-index --name-only " + with repoman_popen("git diff-index --name-only " "--relative --diff-filter=D HEAD") as f: myremoved = f.readlines() myremoved = ["./" + elem[:-1] for elem in myremoved] if vcs == "bzr": - with os.popen("bzr status -S .") as f: + with repoman_popen("bzr status -S .") as f: bzrstatus = f.readlines() - mychanged = [ "./" + elem.split()[-1:][0].split('/')[-1:][0] for elem in bzrstatus if elem and elem[1:2] == "M" ] - mynew = [ "./" + elem.split()[-1:][0].split('/')[-1:][0] for elem in bzrstatus if elem and ( elem[1:2] in "NK" or elem[0:1] == "R" ) ] - myremoved = [ "./" + elem.split()[-1:][0].split('/')[-1:][0] for elem in bzrstatus if elem.startswith("-") ] - myremoved = [ "./" + elem.split()[-3:-2][0].split('/')[-1:][0] for elem in bzrstatus if elem and ( elem[1:2] == "K" or elem[0:1] == "R" ) ] + mychanged = ["./" + elem.split()[-1:][0].split('/')[-1:][0] for elem in bzrstatus if elem and elem[1:2] == "M"] + mynew = ["./" + elem.split()[-1:][0].split('/')[-1:][0] for elem in bzrstatus if elem and (elem[1:2] in "NK" or elem[0:1] == "R")] + myremoved = ["./" + elem.split()[-1:][0].split('/')[-1:][0] for elem in bzrstatus if elem.startswith("-")] + myremoved = ["./" + elem.split()[-3:-2][0].split('/')[-1:][0] for elem in bzrstatus if elem and (elem[1:2] == "K" or elem[0:1] == "R")] # Bazaar expands nothing. if vcs == "hg": - with os.popen("hg status --no-status --modified .") as f: + with repoman_popen("hg status --no-status --modified .") as f: mychanged = f.readlines() mychanged = ["./" + elem.rstrip() for elem in mychanged] - with os.popen("hg status --no-status --added .") as f: + with repoman_popen("hg status --no-status --added .") as f: mynew = f.readlines() mynew = ["./" + elem.rstrip() for elem in mynew] - with os.popen("hg status --no-status --removed .") as f: + with repoman_popen("hg status --no-status --removed .") as f: myremoved = f.readlines() myremoved = ["./" + elem.rstrip() for elem in myremoved] @@ -2499,21 +2681,54 @@ else: commitmessage = commitmessage.rstrip() changelog_msg = commitmessage portage_version = getattr(portage, "VERSION", None) + gpg_key = repoman_settings.get("PORTAGE_GPG_KEY", "") + dco_sob = repoman_settings.get("DCO_SIGNED_OFF_BY", "") if portage_version is None: sys.stderr.write("Failed to insert portage version in message!\n") sys.stderr.flush() portage_version = "Unknown" - unameout = platform.system() + " " - if platform.system() in ["Darwin", "SunOS"]: - unameout += platform.processor() - else: - unameout += platform.machine() - commitmessage += "\n\n(Portage version: %s/%s/%s" % \ - (portage_version, vcs, unameout) + + report_options = [] if options.force: - commitmessage += ", RepoMan options: --force" - commitmessage += ")" + report_options.append("--force") + if options.ignore_arches: + report_options.append("--ignore-arches") + if include_arches is not None: + report_options.append("--include-arches=\"%s\"" % + " ".join(sorted(include_arches))) + + if vcs == "git": + # Use new footer only for git (see bug #438364). + commit_footer = "\n\nPackage-Manager: portage-%s" % portage_version + if report_options: + commit_footer += "\nRepoMan-Options: " + " ".join(report_options) + if sign_manifests: + commit_footer += "\nManifest-Sign-Key: %s" % (gpg_key, ) + if dco_sob: + commit_footer += "\nSigned-off-by: %s" % (dco_sob, ) + else: + unameout = platform.system() + " " + if platform.system() in ["Darwin", "SunOS"]: + unameout += platform.processor() + else: + unameout += platform.machine() + commit_footer = "\n\n" + if dco_sob: + commit_footer += "Signed-off-by: %s\n" % (dco_sob, ) + commit_footer += "(Portage version: %s/%s/%s" % \ + (portage_version, vcs, unameout) + if report_options: + commit_footer += ", RepoMan options: " + " ".join(report_options) + if sign_manifests: + commit_footer += ", signed Manifest commit with key %s" % \ + (gpg_key, ) + else: + commit_footer += ", unsigned Manifest commit" + commit_footer += ")" + + commitmessage += commit_footer + broken_changelog_manifests = [] if options.echangelog in ('y', 'force'): logging.info("checking for unmodified ChangeLog files") committer_name = utilities.get_committer_name(env=repoman_settings) @@ -2569,6 +2784,8 @@ else: # regenerate Manifest for modified ChangeLog (bug #420735) repoman_settings["O"] = checkdir digestgen(mysettings=repoman_settings, myportdb=portdb) + else: + broken_changelog_manifests.append(x) if myautoadd: print(">>> Auto-Adding missing Manifest/ChangeLog file(s)...") @@ -2578,15 +2795,17 @@ else: portage.writemsg_stdout("(%s)\n" % " ".join(add_cmd), noiselevel=-1) else: - if not (sys.hexversion >= 0x3000000 and sys.hexversion < 0x3020000): - # Python 3.1 produces the following TypeError if raw bytes are - # passed to subprocess.call(): - # File "/usr/lib/python3.1/subprocess.py", line 646, in __init__ - # errread, errwrite) - # File "/usr/lib/python3.1/subprocess.py", line 1157, in _execute_child - # raise child_exception - # TypeError: expected an object with the buffer interface - add_cmd = [_unicode_encode(arg) for arg in add_cmd] + + if sys.hexversion < 0x3020000 and sys.hexversion >= 0x3000000 and \ + not os.path.isabs(add_cmd[0]): + # Python 3.1 _execvp throws TypeError for non-absolute executable + # path passed as bytes (see http://bugs.python.org/issue8513). + fullname = find_binary(add_cmd[0]) + if fullname is None: + raise portage.exception.CommandNotFound(add_cmd[0]) + add_cmd[0] = fullname + + add_cmd = [_unicode_encode(arg) for arg in add_cmd] retcode = subprocess.call(add_cmd) if retcode != os.EX_OK: logging.error( @@ -2631,7 +2850,7 @@ else: elif vcs == "svn": if myfile not in expansion: continue - + # Subversion keywords are case-insensitive in svn:keywords properties, but case-sensitive in contents of files. enabled_keywords = [] for k in expansion[myfile]: @@ -2641,7 +2860,8 @@ else: headerstring = "'\$(%s).*\$'" % "|".join(enabled_keywords) - myout = subprocess_getstatusoutput("egrep -q "+headerstring+" "+myfile) + myout = repoman_getstatusoutput("egrep -q " + headerstring + " " + + portage._shell_quote(myfile)) if myout[0] == 0: myheaders.append(myfile) @@ -2688,7 +2908,7 @@ else: if options.pretend: print("(%s)" % (" ".join(commit_cmd),)) else: - retval = spawn(commit_cmd, env=os.environ) + retval = spawn(commit_cmd, env=commit_env) if retval != os.EX_OK: writemsg_level(("!!! Exiting on %s (shell) " + \ "error code: %s\n") % (vcs, retval), @@ -2729,14 +2949,38 @@ else: gpgvars[k] = v gpgcmd = portage.util.varexpand(gpgcmd, mydict=gpgvars) if options.pretend: - print("("+gpgcmd+")") + print("(" + gpgcmd + ")") else: - rValue = os.system(gpgcmd) + # Encode unicode manually for bug #310789. + gpgcmd = portage.util.shlex_split(gpgcmd) + + if sys.hexversion < 0x3020000 and sys.hexversion >= 0x3000000 and \ + not os.path.isabs(gpgcmd[0]): + # Python 3.1 _execvp throws TypeError for non-absolute executable + # path passed as bytes (see http://bugs.python.org/issue8513). + fullname = find_binary(gpgcmd[0]) + if fullname is None: + raise portage.exception.CommandNotFound(gpgcmd[0]) + gpgcmd[0] = fullname + + gpgcmd = [_unicode_encode(arg, + encoding=_encodings['fs'], errors='strict') for arg in gpgcmd] + rValue = subprocess.call(gpgcmd) if rValue == os.EX_OK: - os.rename(filename+".asc", filename) + os.rename(filename + ".asc", filename) else: raise portage.exception.PortageException("!!! gpg exited with '" + str(rValue) + "' status") + def need_signature(filename): + try: + with open(_unicode_encode(filename, + encoding=_encodings['fs'], errors='strict'), 'rb') as f: + return b"BEGIN PGP SIGNED MESSAGE" not in f.readline() + except IOError as e: + if e.errno in (errno.ENOENT, errno.ESTALE): + return False + raise + # When files are removed and re-added, the cvs server will put /Attic/ # inside the $Header path. This code detects the problem and corrects it # so that the Manifest will generate correctly. See bug #169500. @@ -2771,6 +3015,11 @@ else: repoman_settings["O"] = os.path.join(repodir, x) digestgen(mysettings=repoman_settings, myportdb=portdb) + elif broken_changelog_manifests: + for x in broken_changelog_manifests: + repoman_settings["O"] = os.path.join(repodir, x) + digestgen(mysettings=repoman_settings, myportdb=portdb) + signed = False if sign_manifests: signed = True @@ -2779,7 +3028,7 @@ else: chain(myupdates, myremoved, mymanifests))): repoman_settings["O"] = os.path.join(repodir, x) manifest_path = os.path.join(repoman_settings["O"], "Manifest") - if not os.path.exists(manifest_path): + if not need_signature(manifest_path): continue gpgsign(manifest_path) except portage.exception.PortageException as e: @@ -2809,7 +3058,6 @@ else: sys.exit(retval) if True: - myfiles = mymanifests[:] # If there are no header (SVN/CVS keywords) changes in # the files, this Manifest commit must include the @@ -2821,14 +3069,7 @@ else: fd, commitmessagefile = tempfile.mkstemp(".repoman.msg") mymsg = os.fdopen(fd, "wb") - # strip the closing parenthesis - mymsg.write(_unicode_encode(commitmessage[:-1])) - if signed: - mymsg.write(_unicode_encode( - ", signed Manifest commit with key %s)" % \ - repoman_settings["PORTAGE_GPG_KEY"])) - else: - mymsg.write(b", unsigned Manifest commit)") + mymsg.write(_unicode_encode(commitmessage)) mymsg.close() commit_cmd = [] @@ -2851,9 +3092,8 @@ else: if options.pretend: print("(%s)" % (" ".join(commit_cmd),)) else: - retval = spawn(commit_cmd, env=os.environ) + retval = spawn(commit_cmd, env=commit_env) if retval != os.EX_OK: - if repo_config.sign_commit and vcs == 'git' and \ not git_supports_gpg_sign(): # Inform user that newer git is needed (bug #403323). @@ -2877,4 +3117,3 @@ else: print("repoman was too scared by not seeing any familiar version control file that he forgot to commit anything") print(green("RepoMan sez:"), "\"If everyone were like you, I'd be out of business!\"\n") sys.exit(0) - diff --git a/bin/save-ebuild-env.sh b/bin/save-ebuild-env.sh index 47a2acae5..98cff839e 100644 --- a/bin/save-ebuild-env.sh +++ b/bin/save-ebuild-env.sh @@ -1,8 +1,8 @@ #!/bin/bash -# Copyright 1999-2011 Gentoo Foundation +# Copyright 1999-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -# @FUNCTION: save_ebuild_env +# @FUNCTION: __save_ebuild_env # @DESCRIPTION: # echo the current environment to stdout, filtering out redundant info. # @@ -10,11 +10,12 @@ # be excluded from the output. These function are not needed for installation # or removal of the packages, and can therefore be safely excluded. # -save_ebuild_env() { +__save_ebuild_env() { ( if has --exclude-init-phases $* ; then unset S _E_DOCDESTTREE_ _E_EXEDESTTREE_ \ - PORTAGE_DOCOMPRESS PORTAGE_DOCOMPRESS_SKIP + PORTAGE_DOCOMPRESS_SIZE_LIMIT PORTAGE_DOCOMPRESS \ + PORTAGE_DOCOMPRESS_SKIP if [[ -n $PYTHONPATH && ${PYTHONPATH%%:*} -ef $PORTAGE_PYM_PATH ]] ; then if [[ $PYTHONPATH == *:* ]] ; then @@ -42,35 +43,51 @@ save_ebuild_env() { for x in pkg_setup pkg_nofetch src_unpack src_prepare src_configure \ src_compile src_test src_install pkg_preinst pkg_postinst \ pkg_prerm pkg_postrm ; do - unset -f default_$x _eapi{0,1,2,3,4}_$x + unset -f default_$x __eapi{0,1,2,3,4}_$x done unset x - unset -f assert assert_sigpipe_ok dump_trace die diefunc \ - quiet_mode vecho elog_base eqawarn elog \ - esyslog einfo einfon ewarn eerror ebegin _eend eend KV_major \ - KV_minor KV_micro KV_to_int get_KV unset_colors set_colors has \ - has_phase_defined_up_to \ - hasv hasq qa_source qa_call \ - addread addwrite adddeny addpredict _sb_append_var \ + unset -f assert __assert_sigpipe_ok \ + __dump_trace die \ + __quiet_mode __vecho __elog_base eqawarn elog \ + einfo einfon ewarn eerror ebegin __eend eend KV_major \ + KV_minor KV_micro KV_to_int get_KV __1 __1 has \ + __has_phase_defined_up_to \ + hasv hasq __qa_source __qa_call \ + addread addwrite adddeny addpredict __sb_append_var \ use usev useq has_version portageq \ best_version use_with use_enable register_die_hook \ - keepdir unpack strip_duplicate_slashes econf einstall \ - dyn_setup dyn_unpack dyn_clean into insinto exeinto docinto \ + unpack __strip_duplicate_slashes econf einstall \ + __dyn_setup __dyn_unpack __dyn_clean \ + into insinto exeinto docinto \ insopts diropts exeopts libopts docompress \ - abort_handler abort_prepare abort_configure abort_compile \ - abort_test abort_install dyn_prepare dyn_configure \ - dyn_compile dyn_test dyn_install \ - dyn_preinst dyn_pretend dyn_help debug-print debug-print-function \ - debug-print-section helpers_die inherit EXPORT_FUNCTIONS \ - nonfatal register_success_hook remove_path_entry \ - save_ebuild_env filter_readonly_variables preprocess_ebuild_env \ - set_unless_changed unset_unless_changed source_all_bashrcs \ - ebuild_main ebuild_phase ebuild_phase_with_hooks \ - _ebuild_arg_to_phase _ebuild_phase_funcs default \ - _hasg _hasgq _unpack_tar \ + __abort_handler __abort_prepare __abort_configure __abort_compile \ + __abort_test __abort_install __dyn_prepare __dyn_configure \ + __dyn_compile __dyn_test __dyn_install \ + __dyn_pretend __dyn_help \ + debug-print debug-print-function \ + debug-print-section __helpers_die inherit EXPORT_FUNCTIONS \ + nonfatal register_success_hook \ + __hasg __hasgq \ + __save_ebuild_env __set_colors __filter_readonly_variables \ + __preprocess_ebuild_env \ + __repo_attr __source_all_bashrcs \ + __ebuild_main __ebuild_phase __ebuild_phase_with_hooks \ + __ebuild_arg_to_phase __ebuild_phase_funcs default \ + __unpack_tar __unset_colors \ ${QA_INTERCEPTORS} + ___eapi_has_usex && unset -f usex + ___eapi_has_master_repositories && unset -f master_repositories + ___eapi_has_repository_path && unset -f repository_path + ___eapi_has_available_eclasses && unset -f available_eclasses + ___eapi_has_eclass_path && unset -f eclass_path + ___eapi_has_license_path && unset -f license_path + ___eapi_has_package_manager_build_user && unset -f package_manager_build_user + ___eapi_has_package_manager_build_group && unset -f package_manager_build_group + + unset -f $(compgen -A function ___eapi_) + # portage config variables and variables set directly by portage unset ACCEPT_LICENSE BAD BRACKET BUILD_PREFIX COLS \ DISTCC_DIR DISTDIR DOC_SYMLINKS_DIR \ diff --git a/bin/xattr-helper.py b/bin/xattr-helper.py new file mode 100755 index 000000000..ea83a5e7c --- /dev/null +++ b/bin/xattr-helper.py @@ -0,0 +1,190 @@ +#!/usr/bin/python -b +# Copyright 2012-2014 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +"""Dump and restore extended attributes. + +We use formats like that used by getfattr --dump. This is meant for shell +helpers to save/restore. If you're looking for a python/portage API, see +portage.util.movefile._copyxattr instead. + +https://en.wikipedia.org/wiki/Extended_file_attributes +""" + +import array +import os +import re +import sys + +from portage.util._argparse import ArgumentParser + +if hasattr(os, "getxattr"): + + class xattr(object): + get = os.getxattr + set = os.setxattr + list = os.listxattr + +else: + import xattr + + +_UNQUOTE_RE = re.compile(br'\\[0-7]{3}') +_FS_ENCODING = sys.getfilesystemencoding() + + +if sys.hexversion < 0x3000000: + + def octal_quote_byte(b): + return b'\\%03o' % ord(b) + + def unicode_encode(s): + if isinstance(s, unicode): + s = s.encode(_FS_ENCODING) + return s +else: + + def octal_quote_byte(b): + return ('\\%03o' % ord(b)).encode('ascii') + + def unicode_encode(s): + if isinstance(s, str): + s = s.encode(_FS_ENCODING) + return s + + +def quote(s, quote_chars): + """Convert all |quote_chars| in |s| to escape sequences + + This is normally used to escape any embedded quotation marks. + """ + quote_re = re.compile(b'[' + quote_chars + b']') + result = [] + pos = 0 + s_len = len(s) + + while pos < s_len: + m = quote_re.search(s, pos=pos) + if m is None: + result.append(s[pos:]) + pos = s_len + else: + start = m.start() + result.append(s[pos:start]) + result.append(octal_quote_byte(s[start:start+1])) + pos = start + 1 + + return b''.join(result) + + +def unquote(s): + """Process all escape sequences in |s|""" + result = [] + pos = 0 + s_len = len(s) + + while pos < s_len: + m = _UNQUOTE_RE.search(s, pos=pos) + if m is None: + result.append(s[pos:]) + pos = s_len + else: + start = m.start() + result.append(s[pos:start]) + pos = start + 4 + a = array.array('B') + a.append(int(s[start + 1:pos], 8)) + try: + # Python >= 3.2 + result.append(a.tobytes()) + except AttributeError: + result.append(a.tostring()) + + return b''.join(result) + + +def dump_xattrs(pathnames, file_out): + """Dump the xattr data for |pathnames| to |file_out|""" + # NOTE: Always quote backslashes, in order to ensure that they are + # not interpreted as quotes when they are processed by unquote. + quote_chars = b'\n\r\\\\' + + for pathname in pathnames: + attrs = xattr.list(pathname) + if not attrs: + continue + + file_out.write(b'# file: %s\n' % quote(pathname, quote_chars)) + for attr in attrs: + attr = unicode_encode(attr) + value = xattr.get(pathname, attr) + file_out.write(b'%s="%s"\n' % ( + quote(attr, b'=' + quote_chars), + quote(value, b'\0"' + quote_chars))) + + +def restore_xattrs(file_in): + """Read |file_in| and restore xattrs content from it + + This expects textual data in the format written by dump_xattrs. + """ + pathname = None + for i, line in enumerate(file_in): + if line.startswith(b'# file: '): + pathname = unquote(line.rstrip(b'\n')[8:]) + else: + parts = line.split(b'=', 1) + if len(parts) == 2: + if pathname is None: + raise ValueError('line %d: missing pathname' % (i + 1,)) + attr = unquote(parts[0]) + # strip trailing newline and quotes + value = unquote(parts[1].rstrip(b'\n')[1:-1]) + xattr.set(pathname, attr, value) + elif line.strip(): + raise ValueError('line %d: malformed entry' % (i + 1,)) + + +def main(argv): + + parser = ArgumentParser(description=__doc__) + parser.add_argument('paths', nargs='*', default=[]) + + actions = parser.add_argument_group('Actions') + actions.add_argument('--dump', + action='store_true', + help='Dump the values of all extended ' + 'attributes associated with null-separated' + ' paths read from stdin.') + actions.add_argument('--restore', + action='store_true', + help='Restore extended attributes using' + ' a dump read from stdin.') + + options = parser.parse_args(argv) + + if sys.hexversion >= 0x3000000: + file_in = sys.stdin.buffer.raw + else: + file_in = sys.stdin + if not options.paths: + options.paths += [x for x in file_in.read().split(b'\0') if x] + + if options.dump: + if sys.hexversion >= 0x3000000: + file_out = sys.stdout.buffer + else: + file_out = sys.stdout + dump_xattrs(options.paths, file_out) + + elif options.restore: + restore_xattrs(file_in) + + else: + parser.error('missing action!') + + return os.EX_OK + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/bin/xpak-helper.py b/bin/xpak-helper.py index ef74920db..c4391cde7 100755 --- a/bin/xpak-helper.py +++ b/bin/xpak-helper.py @@ -1,11 +1,12 @@ -#!/usr/bin/python -# Copyright 2009-2011 Gentoo Foundation +#!/usr/bin/python -b +# Copyright 2009-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -import optparse import sys import portage +portage._internal_caller = True from portage import os +from portage.util._argparse import ArgumentParser def command_recompose(args): @@ -45,8 +46,8 @@ def main(argv): usage = "usage: %s COMMAND [args]" % \ os.path.basename(argv[0]) - parser = optparse.OptionParser(description=description, usage=usage) - options, args = parser.parse_args(argv[1:]) + parser = ArgumentParser(description=description, usage=usage) + options, args = parser.parse_known_args(argv[1:]) if not args: parser.error("missing command argument") |