From: Fabian Groffen Date: Mon, 9 Jul 2007 17:24:57 +0000 (-0000) Subject: Merged from trunk 7163:7207 X-Git-Url: http://git.tremily.us/gitweb.cgi?a=commitdiff_plain;h=e39cf1b37da8a8fab9943499a42d31fd0b3a2ce4;p=portage.git Merged from trunk 7163:7207 +------------------------------------------------------------------------+ | 7164 | Don't store unslotted system packages in the world file. | | zmedico | | |----------+-------------------------------------------------------------| | 7166 | Fix emerge by ebuild path so that it works properly. | | zmedico | | |----------+-------------------------------------------------------------| | 7168 | add modified copy of gentoolkits glsa.py (r410) | | genone | | |----------+-------------------------------------------------------------| | 7169 | Enable keyword expansion | | genone | | |----------+-------------------------------------------------------------| | 7170 | Add first draft of PackageSet backend modules | | genone | | |----------+-------------------------------------------------------------| | 7171 | Fix emerge by tbz2 path so that it works properly. | | zmedico | | |----------+-------------------------------------------------------------| | 7172 | fix copyright year | | genone | | |----------+-------------------------------------------------------------| | 7174 | Remove the PKG_*ARG* color classes in order to simplify | | zmedico | things since it's questionable whether many people will | | | want to use them. Also, make the PKG_*SYSTEM colors default | | | to the traditional colors since people are generally most | | | interested in world packages. | |----------+-------------------------------------------------------------| | 7176 | ignore generated docs | | SpankMan | | |----------+-------------------------------------------------------------| | 7177 | tighten up --quiet output for --unmerge | | SpankMan | | |----------+-------------------------------------------------------------| | 7178 | Count the number of merges processed by MergeTask and use | | zmedico | that to determine whether or not post_merge() should run. | |----------+-------------------------------------------------------------| | 7180 | Don't exclude system virtuals from world since they can | | zmedico | match multiple old-style virtuals but only one of them will | | | be pulled in by update or depclean. | |----------+-------------------------------------------------------------| | 7183 | Misc color fixes. Thanks to Arfrever Frehtes Taifersar | | zmedico | Arahesis for this patch from | | | bug#183861, comment #9. | |----------+-------------------------------------------------------------| | 7185 | When depgraph updates preferences for old-style virtuals, | | zmedico | also update them in the global config so that they are | | | consistent everywhere. Optimize config.setinst() to | | | minimize the work done. | |----------+-------------------------------------------------------------| | 7187 | Add a depgaph.loadResumeCommand() method to handle loading | | zmedico | and validation of all resume data. | |----------+-------------------------------------------------------------| | 7189 | `find` can accept a list of paths so condense this nested | | SpankMan | for loop, and dont split off debug information for ET_REL | | | files as these are never pulled in via debug utilities (and | | | can trigger weird behavior in binutils when linked into | | | things, like glibcs crt1.o) | |----------+-------------------------------------------------------------| | 7191 | Now that the "white" color code really is white, alias it | | zmedico | to bold where it's used by emerge since white looks bad on | | | a terminal with a white background. | |----------+-------------------------------------------------------------| | 7193 | allow split debuf for kernel modules | | SpankMan | | |----------+-------------------------------------------------------------| | 7195 | For bug #184412, make depclean more consistent with the | | zmedico | update algorithm by keeping the highest version that as | | | visible in the portage tree and pruning any versions that | | | are either masked or no longer exist in the portage tree. | |----------+-------------------------------------------------------------| | 7197 | Allow system virtuals to be pruned from the world file in | | zmedico | cases where the matched package is the only provider of | | | that virtual. | |----------+-------------------------------------------------------------| | 7199 | Add color classes PROMPT_CHOICE_{DEFAULT,OTHER} for | | zmedico | emerge's Yes/No prompt. | |----------+-------------------------------------------------------------| | 7201 | For bug #184566, enable the emerge --info action to run an | | zmedico | option pkg_info() function from each installed ebuild. | |----------+-------------------------------------------------------------| | 7203 | Make dblink invalidate the vardbapi caches for each merge | | zmedico | and unmerge. Don't use portage.listdir() since mtime | | | granularity can prevent cachedir from invalidating it's | | | cache properly and this redundant cache layer isn't needed | | | anyway. This should solve the intermittent failure of | | | AUTOCLEAN to properly clean packages. | |----------+-------------------------------------------------------------| | 7204 | Pass the file path into the PermissionDenied exception. | | zmedico | | |----------+-------------------------------------------------------------| | 7206 | simplify logic a bit | | genone | | |----------+-------------------------------------------------------------| | 7207 | Fix interaction of resume opts with spinner output handling | | zmedico | so that output always looks correct. | +------------------------------------------------------------------------+ svn path=/main/branches/prefix/; revision=7209 --- diff --git a/bin/ebuild.sh b/bin/ebuild.sh index bb17ad89a..edf130f40 100755 --- a/bin/ebuild.sh +++ b/bin/ebuild.sh @@ -1554,7 +1554,12 @@ for myarg in ${EBUILD_SH_ARGS} ; do qa_call pkg_nofetch exit 1 ;; - prerm|postrm|postinst|config) + prerm|postrm|postinst|config|info) + if [ "${myarg}" == "info" ] && \ + [ "$(type -t pkg_${myarg})" != "function" ]; then + ewarn "pkg_${myarg}() is not defined: '${EBUILD##*/}'" + continue + fi export SANDBOX_ON="0" if [ "$PORTAGE_DEBUG" != "1" ]; then [ "$(type -t pre_pkg_${myarg})" == "function" ] && qa_call pre_pkg_${myarg} diff --git a/bin/prepstrip b/bin/prepstrip index 3b68cdd05..df187c127 100755 --- a/bin/prepstrip +++ b/bin/prepstrip @@ -1,5 +1,5 @@ #!@BASH@ -# Copyright 1999-2006 Gentoo Foundation +# Copyright 1999-2007 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Id$ @@ -51,7 +51,6 @@ save_elf_debug() { ${OBJCOPY} --only-keep-debug "${x}" "${y}" ${OBJCOPY} --add-gnu-debuglink="${y}" "${x}" chmod a-x,o-w "${y}" - } # The existance of the section .symtab tells us that a binary is stripped. @@ -66,7 +65,10 @@ if [[ -n "${f}" ]] ; then fi # Now we look for unstripped binaries. -for x in $(scanelf -yqRBF '#k%F' -k '.symtab' "$@") $(for y in "$@"; do find "${y}" -type f -name '*.a' -print ; done); do +for x in \ + $(scanelf -yqRBF '#k%F' -k '.symtab' "$@") \ + $(find "$@" -type f -name '*.a') +do if [[ ${banner} -eq 1 ]] ; then [[ -z ${NOSTRIP} ]] && vecho "strip: ${STRIP} ${PORTAGE_STRIP_FLAGS}" banner=0 @@ -91,7 +93,13 @@ for x in $(scanelf -yqRBF '#k%F' -k '.symtab' "$@") $(for y in "$@"; do find "${ [[ ${stripitbaby} -eq 1 ]] && ${STRIP} -g "${x}" elif [[ ${f} == *"SB executable"* || ${f} == *"SB shared object"* || ${f} == *"SB relocatable"* ]] ; then vecho " ${x:${#ED}}" - save_elf_debug "${x}" + if [[ ${f} != *"SB relocatable"* ]] || [[ ${x} == *.ko ]] ; then + # only split debug info for final linked objects + # or kernel modules as debuginfo for intermediatary + # files (think crt*.o from gcc/glibc) is useless and + # actually causes problems + save_elf_debug "${x}" + fi [[ -n ${NOSTRIP} ]] && continue if [[ ${stripitbaby} -eq 1 ]] ; then [[ ${f} == *"SB relocatable"* ]] \ diff --git a/man/color.map.5 b/man/color.map.5 index 10fa1a09a..7a0f1b2ec 100644 --- a/man/color.map.5 +++ b/man/color.map.5 @@ -1,4 +1,4 @@ -.TH "COLOR.MAP" "5" "Jun 2007" "Portage 2.1.3" "Portage" +.TH "COLOR.MAP" "5" "Jul 2007" "Portage 2.1.3" "Portage" .SH "NAME" color.map \- custom color settings for Portage .SH "SYNOPSIS" @@ -8,80 +8,133 @@ This file contains variables that define color classes used by Portage. Portage will check this file first for color classes settings. If no setting of given color class is found in /etc/portage/color.map, Portage uses default value defined internally. +.SH "SYNTAX" +\fBVARIABLE\fR = \fI[space delimited list of attributes]\fR .SH "VARIABLES" .TP -\fBBAD\fR = \fI"0xFF5555"\fR +\fBBAD\fR = \fI"red"\fR Defines color used for some words occuring in bad context. .TP -\fBBRACKET\fR = \fI"0x5555FF"\fR +\fBBRACKET\fR = \fI"blue"\fR Defines color used for brackets. .TP -\fBGOOD\fR = \fI"0x55FF55"\fR +\fBGOOD\fR = \fI"green"\fR Defines color used for some words occuring in good context. .TP -\fBHILITE\fR = \fI"0x00AAAA"\fR +\fBHILITE\fR = \fI"teal"\fR Defines color used for highlighted words. .TP -\fBINFORM\fR = \fI"0x00AA00"\fR +\fBINFORM\fR = \fI"darkgreen"\fR Defines color used for informational words. .TP -\fBMERGE_LIST_PROGRESS\fR = \fI"0xFFFF55"\fR -Defines color used for numbers used in merge progress lists. +\fBMERGE_LIST_PROGRESS\fR = \fI"yellow"\fR +Defines color used for numbers indicating merge progress. .TP -\fBPKG_MERGE\fR = \fI"0x00AA00"\fR +\fBPKG_MERGE\fR = \fI"darkgreen"\fR Defines color used for packages planned to be merged. .TP -\fBPKG_MERGE_ARG\fR = \fI"0x00AA00"\fR -Defines color used for packages planned to be merged and given as command line -arguments. +\fBPKG_MERGE_SYSTEM\fR = \fI"darkgreen"\fR +Defines color used for system packages planned to be merged. .TP -\fBPKG_MERGE_SYSTEM\fR = \fI"0x55FF55"\fR -Defines color used for packages planned to be merged and belonging to -system set. +\fBPKG_MERGE_WORLD\fR = \fI"green"\fR +Defines color used for world packages planned to be merged. .TP -\fBPKG_MERGE_ARG_SYSTEM\fR = \fI"0x55FF55"\fR -Defines color used for system packages planned to be merged and given as -command line arguments. -.TP -\fBPKG_MERGE_WORLD\fR = \fI"0x55FF55"\fR -Defines color used for packages planned to be merged and belonging to -world set. -.TP -\fBPKG_MERGE_ARG_WORLD\fR = \fI"0x55FF55"\fR -Defines color used for world packages planned to be merged and given as -command line arguments. -.TP -\fBPKG_NOMERGE\fR = \fI"0x0000AA"\fR +\fBPKG_NOMERGE\fR = \fI"darkblue"\fR Defines color used for packages not planned to be merged. .TP -\fBPKG_NOMERGE_ARG\fR = \fI"0x0000AA"\fR -Defines color used for packages not planned to be merged and given as command -line arguments. +\fBPKG_NOMERGE_SYSTEM\fR = \fI"darkblue"\fR +Defines color used for system packages not planned to be merged. .TP -\fBPKG_NOMERGE_SYSTEM\fR = \fI"0x5555FF"\fR -Defines color used for packages not planned to be merged and belonging to -system set. +\fBPKG_NOMERGE_WORLD\fR = \fI"blue"\fR +Defines color used for world packages not planned to be merged. .TP -\fBPKG_NOMERGE_ARG_SYSTEM\fR = \fI"0x5555FF"\fR -Defines color used for system packages not planned to be merged and given as -command line arguments. +\fBPROMPT_CHOICE_DEFAULT\fR = \fI"green"\fR +Defines color used for the default choice at a prompt. .TP -\fBPKG_NOMERGE_WORLD\fR = \fI"0x5555FF"\fR -Defines color used for packages not planned to be merged and belonging to -world set. +\fBPROMPT_CHOICE_OTHER\fR = \fI"red"\fR +Defines color used for a non\-default choice at a prompt. .TP -\fBPKG_NOMERGE_ARG_WORLD\fR = \fI"0x5555FF"\fR -Defines color used for world packages not planned to be merged and given as -command line arguments. -.TP -\fBSECURITY_WARN\fR = \fI"0xFF5555"\fR +\fBSECURITY_WARN\fR = \fI"red"\fR Defines color used for security warnings. .TP -\fBUNMERGE_WARN\fR = \fI"0xFF5555"\fR +\fBUNMERGE_WARN\fR = \fI"red"\fR Defines color used for unmerge warnings. .TP -\fBWARN\fR = \fI"0xFFFF55"\fR +\fBWARN\fR = \fI"yellow"\fR Defines color used for warnings. +.SH "LIST OF VALID ATTRIBUTES" +.TP +.B Foreground colors +.RS +.TP +.B black +.TP +.B darkgray +.TP +.B darkred +.TP +.B red +.TP +.B darkgreen +.TP +.B green +.TP +\fBbrown\fR = \fBdarkyellow\fR +.TP +.B yellow +.TP +.B darkblue +.TP +.B blue +.TP +.B purple +.TP +.B fuchsia +.TP +.B teal +.TP +.B turquoise +.TP +.B lightgray +.TP +.B white +.RE +.TP +.B Background colors +.RS +.TP +.B bg_black +.TP +.B bg_darkred +.TP +.B bg_darkgreen +.TP +\fBbg_brown\fR = \fBbg_darkyellow\fR +.TP +.B bg_darkblue +.TP +.B bg_purple +.TP +.B bg_teal +.TP +.B bg_lightgray +.RE +.TP +.B Other attributes +.RS +.TP +.B bold +.TP +.B faint +.TP +.B standout +.TP +.B underline +.TP +.B blink +.TP +.B reverse +.RE .SH "REPORTING BUGS" Please report bugs via http://bugs.gentoo.org/ .SH "AUTHORS" @@ -96,6 +149,7 @@ Contains variables customizing colors. .B /etc/make.conf Contains other variables. .SH "SEE ALSO" +.BR console_codes (4), .BR make.conf (5), .BR portage (5), .BR emerge (1), diff --git a/pym/emerge/__init__.py b/pym/emerge/__init__.py index a4e23aa99..6966b7928 100644 --- a/pym/emerge/__init__.py +++ b/pym/emerge/__init__.py @@ -41,6 +41,8 @@ from portage.output import blue, bold, colorize, darkblue, darkgreen, darkred, g from portage.output import create_color_func good = create_color_func("GOOD") bad = create_color_func("BAD") +# white looks bad on terminals with white background +from portage.output import bold as white import portage.dep portage.dep._dep_check_strict = True @@ -134,7 +136,11 @@ def userquery(prompt, responses=None, colours=None): KeyboardInterrupt is converted to SystemExit to avoid tracebacks being printed.""" if responses is None: - responses, colours = ["Yes", "No"], [green, red] + responses = ["Yes", "No"] + colours = [ + create_color_func("PROMPT_CHOICE_DEFAULT"), + create_color_func("PROMPT_CHOICE_OTHER") + ] elif colours is None: colours=[bold] colours=(colours*len(responses))[:len(responses)] @@ -337,28 +343,20 @@ def create_depgraph_params(myopts, myaction): # recurse: go into the dependencies # deep: go into the dependencies of already merged packages # empty: pretend nothing is merged - myparams = ["recurse"] - add=[] - sub=[] + myparams = set(["recurse"]) if "--update" in myopts or \ "--newuse" in myopts or \ "--reinstall" in myopts or \ "--noreplace" in myopts or \ myaction in ("system", "world"): - add.extend(["selective"]) + myparams.add("selective") if "--emptytree" in myopts: - add.extend(["empty"]) - sub.extend(["selective"]) + myparams.add("empty") + myparams.remove("selective") if "--nodeps" in myopts: - sub.extend(["recurse"]) + myparams.remove("recurse") if "--deep" in myopts: - add.extend(["deep"]) - for x in add: - if (x not in myparams) and (x not in sub): - myparams.append(x) - for x in sub: - if x in myparams: - myparams.remove(x) + myparams.add("deep") return myparams # search functionality @@ -689,16 +687,20 @@ class RootConfig(object): system_set = SystemSet(self.settings) self.sets["system"] = system_set -def create_world_atom(pkg_key, metadata, args_set, world_set, portdb): +def create_world_atom(pkg_key, metadata, args_set, sets, portdb): """Create a new atom for the world file if one does not exist. If the argument atom is precise enough to identify a specific slot then a slot - atom will be returned. The system set is not used in deciding which - atoms to store since system atoms can only match one slot while world - atoms can be greedy with respect to slots.""" + atom will be returned. Atoms that are in the system set may also be stored + in world since system atoms can only match one slot while world atoms can + be greedy with respect to slots. Unslotted system packages will not be + stored in world.""" arg_atom = args_set.findAtomForPackage(pkg_key, metadata) cp = portage.dep_getkey(arg_atom) new_world_atom = cp - if arg_atom != cp: + available_slots = set(portdb.aux_get(cpv, ["SLOT"])[0] \ + for cpv in portdb.match(cp)) + slotted = len(available_slots) > 1 or "0" not in available_slots + if slotted and arg_atom != cp: # If the user gave a specific atom, store it as a # slot atom in the world file. slot_atom = "%s:%s" % (cp, metadata["SLOT"]) @@ -713,9 +715,23 @@ def create_world_atom(pkg_key, metadata, args_set, world_set, portdb): matched_slots.add(portdb.aux_get(cpv, ["SLOT"])[0]) if len(matched_slots) == 1: new_world_atom = slot_atom - if new_world_atom == world_set.findAtomForPackage(pkg_key, metadata): + if new_world_atom == sets["world"].findAtomForPackage(pkg_key, metadata): # Both atoms would be identical, so there's nothing to add. return None + if not slotted: + # Unlike world atoms, system atoms are not greedy for slots, so they + # can't be safely excluded from world if they are slotted. + system_atom = sets["system"].findAtomForPackage(pkg_key, metadata) + if system_atom: + if not portage.dep_getkey(system_atom).startswith("virtual/"): + return None + # System virtuals aren't safe to exclude from world since they can + # match multiple old-style virtuals but only one of them will be + # pulled in by update or depclean. + providers = portdb.mysettings.getvirtuals().get( + portage.dep_getkey(system_atom)) + if providers and len(providers) == 1 and providers[0] == cp: + return None return new_world_atom def filter_iuse_defaults(iuse): @@ -1481,25 +1497,38 @@ class depgraph(object): else: print "\n\n!!! Binary package '"+str(x)+"' does not exist." print "!!! Please ensure the tbz2 exists as specified.\n" - sys.exit(1) + return 0, myfavorites mytbz2=portage.xpak.tbz2(x) mykey=mytbz2.getelements("CATEGORY")[0]+"/"+os.path.splitext(os.path.basename(x))[0] if os.path.realpath(x) != \ os.path.realpath(self.trees[myroot]["bintree"].getname(mykey)): print colorize("BAD", "\n*** You need to adjust PKGDIR to emerge this package.\n") - sys.exit(1) + return 0, myfavorites if not self.create(["binary", myroot, mykey], - None, "--onlydeps" not in self.myopts): + None, "--onlydeps" not in self.myopts, + myuse=mytbz2.getelements("USE"), arg=x): return (0,myfavorites) arg_atoms.append((x, "="+mykey)) elif ext==".ebuild": - x = os.path.realpath(x) - mykey=os.path.basename(os.path.normpath(x+"/../.."))+"/"+os.path.splitext(os.path.basename(x))[0] + ebuild_path = portage.util.normalize_path(os.path.abspath(x)) + pkgdir = os.path.dirname(ebuild_path) + tree_root = os.path.dirname(os.path.dirname(pkgdir)) + cp = pkgdir[len(tree_root)+1:] + e = portage.exception.PackageNotFound( + ("%s is not in a valid portage tree " + \ + "hierarchy or does not exist") % x) + if not portage.isvalidatom(cp): + raise e + cat = portage.catsplit(cp)[0] + mykey = cat + "/" + os.path.basename(ebuild_path[:-7]) + if not portage.isvalidatom("="+mykey): + raise e ebuild_path = portdb.findname(mykey) if ebuild_path: - if os.path.realpath(ebuild_path) != x: + if ebuild_path != os.path.join(os.path.realpath(tree_root), + cp, os.path.basename(ebuild_path)): print colorize("BAD", "\n*** You need to adjust PORTDIR or PORTDIR_OVERLAY to emerge this package.\n") - sys.exit(1) + return 0, myfavorites if mykey not in portdb.xmatch( "match-visible", portage.dep_getkey(mykey)): print colorize("BAD", "\n*** You are emerging a masked package. It is MUCH better to use") @@ -1511,7 +1540,7 @@ class depgraph(object): raise portage.exception.PackageNotFound( "%s is not in a valid portage tree hierarchy or does not exist" % x) if not self.create(["ebuild", myroot, mykey], - None, "--onlydeps" not in self.myopts): + None, "--onlydeps" not in self.myopts, arg=x): return (0,myfavorites) arg_atoms.append((x, "="+mykey)) else: @@ -1718,9 +1747,14 @@ class depgraph(object): if p_status == "merge": # Update old-style virtuals if this package provides any. # These are needed for dep_virtual calls inside dep_check. - p_db = self.trees[p_root][self.pkg_tree_map[p_type]].dbapi + p_db = self.mydbapi[p_root] # contains cached metadata try: self.pkgsettings[p_root].setinst(p_key, p_db) + # For consistency, also update the global virtuals. + settings = self.roots[p_root].settings + settings.unlock() + settings.setinst(p_key, p_db) + settings.lock() except portage.exception.InvalidDependString, e: provide = p_db.aux_get(p_key, ["PROVIDE"])[0] show_invalid_depstring_notice(myparent, provide, str(e)) @@ -2532,8 +2566,6 @@ class depgraph(object): if verbosity is None: verbosity = ("--quiet" in self.myopts and 1 or \ "--verbose" in self.myopts and 3 or 2) - if "--resume" in self.myopts and favorites: - self._sets["args"].update(favorites) favorites_set = AtomSet() favorites_set.update(favorites) changelogs=[] @@ -2742,32 +2774,10 @@ class depgraph(object): mydbapi = self.trees[myroot][self.pkg_tree_map[pkg_type]].dbapi pkg_status = x[3] pkg_merge = ordered and pkg_status != "nomerge" - binary_package = True - if "ebuild" == pkg_type: - if "merge" == x[3] or \ - not vartree.dbapi.cpv_exists(pkg_key): - """An ebuild "merge" node or a --onlydeps "nomerge" - node.""" - binary_package = False - pkgsettings.setcpv(pkg_key, mydb=portdb) - if pkg_key not in self.useFlags[myroot]: - self.useFlags[myroot][pkg_key] = \ - pkgsettings["USE"].split() - else: - # An ebuild "nomerge" node, so USE come from the vardb. - mydbapi = vartree.dbapi - # reuse cached metadata from when the depgraph was built - if "--resume" in self.myopts: - # Populate the fakedb with relevant metadata, just like - # would have happened when the depgraph was originally - # built. - metadata = dict(izip(self._mydbapi_keys, - mydbapi.aux_get(pkg_key, self._mydbapi_keys))) - self.mydbapi[myroot].cpv_inject(pkg_key, metadata=metadata) - else: - metadata = dict(izip(self._mydbapi_keys, - self.mydbapi[myroot].aux_get( - pkg_key, self._mydbapi_keys))) + binary_package = pkg_type != "ebuild" + metadata = dict(izip(self._mydbapi_keys, + self.mydbapi[myroot].aux_get( + pkg_key, self._mydbapi_keys))) mydbapi = self.mydbapi[myroot] # use the cached metadata if pkg_key not in self.useFlags[myroot]: """If this is a --resume then the USE flags need to be @@ -3016,54 +3026,37 @@ class depgraph(object): root_config = self.roots[myroot] system_set = root_config.sets["system"] world_set = root_config.sets["world"] - args_set = self._sets["args"] - pkg_arg = False pkg_system = False pkg_world = False try: - if myroot == self.target_root: - pkg_arg = args_set.findAtomForPackage(pkg_key, metadata) pkg_system = system_set.findAtomForPackage(pkg_key, metadata) pkg_world = world_set.findAtomForPackage(pkg_key, metadata) - if not pkg_world and myroot == self.target_root: + if not pkg_world and myroot == self.target_root and \ + favorites_set.findAtomForPackage(pkg_key, metadata): # Maybe it will be added to world now. - pkg_world = favorites_set.findAtomForPackage(pkg_key, metadata) + if create_world_atom(pkg_key, metadata, + favorites_set, root_config.sets, portdb): + pkg_world = True except portage.exception.InvalidDependString: # This is reported elsewhere if relevant. pass def pkgprint(pkg): if pkg_merge: - if pkg_arg: - if pkg_world: - return colorize("PKG_MERGE_ARG_WORLD", pkg) - elif pkg_system: - return colorize("PKG_MERGE_ARG_SYSTEM", pkg) - else: - return colorize("PKG_MERGE_ARG", pkg) + if pkg_system: + return colorize("PKG_MERGE_SYSTEM", pkg) + elif pkg_world: + return colorize("PKG_MERGE_WORLD", pkg) else: - if pkg_world: - return colorize("PKG_MERGE_WORLD", pkg) - elif pkg_system: - return colorize("PKG_MERGE_SYSTEM", pkg) - else: - return colorize("PKG_MERGE", pkg) + return colorize("PKG_MERGE", pkg) else: - if pkg_arg: - if pkg_world: - return colorize("PKG_NOMERGE_ARG_WORLD", pkg) - elif pkg_system: - return colorize("PKG_NOMERGE_ARG_SYSTEM", pkg) - else: - return colorize("PKG_NOMERGE_ARG", pkg) + if pkg_system: + return colorize("PKG_NOMERGE_SYSTEM", pkg) + elif pkg_world: + return colorize("PKG_NOMERGE_WORLD", pkg) else: - if pkg_world: - return colorize("PKG_NOMERGE_WORLD", pkg) - elif pkg_system: - return colorize("PKG_NOMERGE_SYSTEM", pkg) - else: - return colorize("PKG_NOMERGE", pkg) + return colorize("PKG_NOMERGE", pkg) if x[1]!="/": if myoldbest: @@ -3250,7 +3243,8 @@ class depgraph(object): "--oneshot", "--onlydeps", "--pretend"): if x in self.myopts: return - world_set = WorldSet(self.settings) + root_config = self.roots[self.target_root] + world_set = root_config.sets["world"] world_set.lock() world_set.load() args_set = self._sets["args"] @@ -3264,7 +3258,7 @@ class depgraph(object): self.mydbapi[root].aux_get(pkg_key, self._mydbapi_keys))) try: myfavkey = create_world_atom(pkg_key, metadata, - args_set, world_set, portdb) + args_set, root_config.sets, portdb) if myfavkey: if myfavkey in added_favorites: continue @@ -3281,6 +3275,37 @@ class depgraph(object): world_set.save() world_set.unlock() + def loadResumeCommand(self, resume_data): + """ + Add a resume command to the graph and validate it in the process. This + will raise a PackageNotFound exception if a package is not available. + """ + self._sets["args"].update(resume_data.get("favorites", [])) + mergelist = resume_data.get("mergelist", []) + fakedb = self.mydbapi + trees = self.trees + for x in mergelist: + if len(x) != 4: + continue + pkg_type, myroot, pkg_key, action = x + if pkg_type not in self.pkg_tree_map: + continue + if action != "merge": + continue + mydb = trees[myroot][self.pkg_tree_map[pkg_type]].dbapi + try: + metadata = dict(izip(self._mydbapi_keys, + mydb.aux_get(pkg_key, self._mydbapi_keys))) + except KeyError: + # It does no exist or it is corrupt. + raise portage.exception.PackageNotFound(pkg_key) + fakedb[myroot].cpv_inject(pkg_key, metadata=metadata) + if pkg_type == "ebuild": + pkgsettings = self.pkgsettings[myroot] + pkgsettings.setcpv(pkg_key, mydb=fakedb[myroot]) + fakedb[myroot].aux_update(pkg_key, {"USE":pkgsettings["USE"]}) + self.spinner.update() + class PackageCounters(object): def __init__(self): @@ -3354,6 +3379,7 @@ class MergeTask(object): if self.target_root != "/": self.pkgsettings["/"] = \ portage.config(clone=trees["/"]["vartree"].settings) + self.curval = 0 def merge(self, mylist, favorites, mtimedb): from portage.elog import elog_process @@ -3388,7 +3414,6 @@ class MergeTask(object): del mtimedb["resume"]["mergelist"][0] del mylist[0] mtimedb.commit() - validate_merge_list(self.trees, mylist) mymergelist = mylist # Verify all the manifests now so that the user is notified of failure @@ -3539,6 +3564,7 @@ class MergeTask(object): print "!!! Fetch for",y,"failed, continuing..." print failed_fetches.append(pkg_key) + self.curval += 1 continue portage.doebuild_environment(y, "setup", myroot, @@ -3690,6 +3716,7 @@ class MergeTask(object): if "--fetchonly" in self.myopts or \ "--fetch-all-uri" in self.myopts: + self.curval += 1 continue short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Merge Binary" @@ -3711,7 +3738,8 @@ class MergeTask(object): world_set.lock() world_set.load() myfavkey = create_world_atom(pkg_key, metadata, - args_set, world_set, portdb) + args_set, {"world":world_set, "system":system_set}, + portdb) if myfavkey: world_set.add(myfavkey) print ">>> Recording",myfavkey,"in \"world\" favorites file..." @@ -3795,6 +3823,7 @@ class MergeTask(object): # in the event that portage is not allowed to exit normally # due to power failure, SIGKILL, etc... mtimedb.commit() + self.curval += 1 if "--pretend" not in self.myopts: emergelog(xterm_titles, " *** Finished. Cleaning up...") @@ -4035,9 +4064,13 @@ def unmerge(settings, myopts, vartree, unmerge_action, unmerge_files, if "--pretend" not in myopts and "--ask" not in myopts: countdown(int(settings["EMERGE_WARNING_DELAY"]), colorize("UNMERGE_WARN", "Press Ctrl-C to Stop")) - print "\n "+white(x) + if "--quiet" not in myopts: + print "\n "+white(x) + else: + print white(x)+": ", for mytype in ["selected","protected","omitted"]: - portage.writemsg_stdout((mytype + ": ").rjust(14), noiselevel=-1) + if "--quiet" not in myopts: + portage.writemsg_stdout((mytype + ": ").rjust(14), noiselevel=-1) if pkgmap[x][mytype]: for mypkg in pkgmap[x][mytype]: mysplit=portage.catpkgsplit(mypkg) @@ -4052,7 +4085,10 @@ def unmerge(settings, myopts, vartree, unmerge_action, unmerge_files, portage.writemsg_stdout( colorize("GOOD", myversion + " "), noiselevel=-1) else: - portage.writemsg_stdout("none", noiselevel=-1) + portage.writemsg_stdout("none ", noiselevel=-1) + if "--quiet" not in myopts: + portage.writemsg_stdout("\n", noiselevel=-1) + if "--quiet" in myopts: portage.writemsg_stdout("\n", noiselevel=-1) portage.writemsg_stdout("\n>>> " + colorize("UNMERGE_WARN", "'Selected'") + \ @@ -4347,20 +4383,6 @@ def is_valid_package_atom(x): testatom = x return portage.isvalidatom(testatom) -def validate_merge_list(trees, mergelist): - """Validate the list to make sure all the packages are still available. - This is needed for --resume.""" - for (pkg_type, myroot, pkg_key, action) in mergelist: - if pkg_type == "binary" and \ - not trees[myroot]["bintree"].dbapi.match("="+pkg_key) or \ - pkg_type == "ebuild" and \ - not trees[myroot]["porttree"].dbapi.xmatch( - "match-all", "="+pkg_key): - print red("!!! Error: The resume list contains packages that are no longer") - print red("!!! available to be emerged. Please restart/continue") - print red("!!! the merge operation manually.") - sys.exit(1) - def show_blocker_docs_link(): print print "For more information about " + bad("Blocked Packages") + ", please refer to the following" @@ -5180,7 +5202,12 @@ def action_info(settings, trees, myopts, myfiles): # Loop through each package # Only print settings if they differ from global settings - header_printed = False + header_title = "Package Settings" + print header_width * "=" + print header_title.rjust(int(header_width/2 + len(header_title)/2)) + print header_width * "=" + from portage.output import EOutput + out = EOutput() for pkg in mypkgs: # Get all package specific variables auxvalues = vardb.aux_get(pkg, auxkeys) @@ -5210,16 +5237,6 @@ def action_info(settings, trees, myopts, myfiles): # If a difference was found, print the info for # this package. if diff_values: - - # If we have not yet printed the header, - # print it now - if not header_printed: - header_title = "Package Settings" - print header_width * "=" - print header_title.rjust(int(header_width/2 + len(header_title)/2)) - print header_width * "=" - header_printed = True - # Print package info print "%s was built with the following:" % pkg for myvar in mydesiredvars + ["USE"]: @@ -5228,6 +5245,15 @@ def action_info(settings, trees, myopts, myfiles): mylist.sort() print "%s=\"%s\"" % (myvar, " ".join(mylist)) print + print ">>> Attempting to run pkg_info() for '%s'" % pkg + ebuildpath = vardb.findname(pkg) + if not ebuildpath or not os.path.exists(ebuildpath): + out.ewarn("No ebuild found for '%s'" % pkg) + continue + portage.doebuild(ebuildpath, "info", pkgsettings["ROOT"], + pkgsettings, debug=(settings.get("PORTAGE_DEBUG", "") == 1), + mydbapi=trees[settings["ROOT"]]["vartree"].dbapi, + tree="vartree") def action_search(settings, portdb, vartree, myopts, myfiles, spinner): if not myfiles: @@ -5269,6 +5295,7 @@ def action_depclean(settings, trees, ldpath_mtimes, xterm_titles = "notitles" not in settings.features myroot = settings["ROOT"] + portdb = trees[myroot]["porttree"].dbapi dep_check_trees = {} dep_check_trees[myroot] = {} dep_check_trees[myroot]["vartree"] = \ @@ -5323,6 +5350,12 @@ def action_depclean(settings, trees, ldpath_mtimes, # deep world update would pull in. Don't prune if this atom comes # directly from world though, since world atoms are greedy when # they don't specify a slot. + visible_in_portdb = [cpv for cpv in pkgs if portdb.match("="+cpv)] + if visible_in_portdb: + # For consistency with the update algorithm, keep the highest + # visible version and prune any versions that are either masked + # or no longer exist in the portage tree. + pkgs = visible_in_portdb pkgs = [portage.best(pkgs)] for pkg in pkgs: if fakedb.cpv_exists(pkg): @@ -5407,6 +5440,12 @@ def action_build(settings, trees, mtimedb, myopts, myaction, myfiles, spinner): ldpath_mtimes = mtimedb["ldpath"] favorites=[] + merge_count = 0 + pretend = "--pretend" in myopts + fetchonly = "--fetchonly" in myopts or "--fetch-all-uri" in myopts + if pretend or fetchonly: + # make the mtimedb readonly + mtimedb.filename = None if "--quiet" not in myopts and \ ("--pretend" in myopts or "--ask" in myopts or \ "--tree" in myopts or "--verbose" in myopts): @@ -5441,16 +5480,6 @@ def action_build(settings, trees, mtimedb, mtimedb["resume"] = mtimedb["resume_backup"] del mtimedb["resume_backup"] mtimedb.commit() - # XXX: "myopts" is a list for backward compatibility. - myresumeopts = dict([(k,True) for k in mtimedb["resume"]["myopts"]]) - - for opt in ("--skipfirst", "--ask", "--tree"): - myresumeopts.pop(opt, None) - - for myopt, myarg in myopts.iteritems(): - if myopt not in myresumeopts: - myresumeopts[myopt] = myarg - myopts=myresumeopts # Adjust config according to options of the command being resumed. for myroot in trees: @@ -5460,12 +5489,33 @@ def action_build(settings, trees, mtimedb, mysettings.lock() del myroot, mysettings - myparams = create_depgraph_params(myopts, myaction) - if "--quiet" not in myopts and "--nodeps" not in myopts: + # "myopts" is a list for backward compatibility. + resume_opts = mtimedb["resume"].get("myopts", []) + if isinstance(resume_opts, list): + resume_opts = dict((k,True) for k in resume_opts) + for opt in ("--skipfirst", "--ask", "--tree"): + resume_opts.pop(opt, None) + myopts.update(resume_opts) + show_spinner = "--quiet" not in myopts and "--nodeps" not in myopts + if not show_spinner: + spinner.update = spinner.update_quiet + if show_spinner: print "Calculating dependencies ", + myparams = create_depgraph_params(myopts, myaction) mydepgraph = depgraph(settings, trees, myopts, myparams, spinner) - if "--quiet" not in myopts and "--nodeps" not in myopts: + try: + mydepgraph.loadResumeCommand(mtimedb["resume"]) + except portage.exception.PackageNotFound: + if show_spinner: + print + from portage.output import EOutput + out = EOutput() + out.eerror("Error: The resume list contains packages that are no longer") + out.eerror(" available to be emerged. Please restart/continue") + out.eerror(" the merge operation manually.") + return 1 + if show_spinner: print "\b\b... done!" else: if ("--resume" in myopts): @@ -5512,7 +5562,6 @@ def action_build(settings, trees, mtimedb, "--verbose" in myopts) and \ not ("--quiet" in myopts and "--ask" not in myopts): if "--resume" in myopts: - validate_merge_list(trees, mtimedb["resume"]["mergelist"]) mymergelist = mtimedb["resume"]["mergelist"] if "--skipfirst" in myopts: mymergelist = mymergelist[1:] @@ -5569,7 +5618,6 @@ def action_build(settings, trees, mtimedb, if ("--pretend" in myopts) and not ("--fetchonly" in myopts or "--fetch-all-uri" in myopts): if ("--resume" in myopts): - validate_merge_list(trees, mtimedb["resume"]["mergelist"]) mymergelist = mtimedb["resume"]["mergelist"] if "--skipfirst" in myopts: mymergelist = mymergelist[1:] @@ -5609,8 +5657,7 @@ def action_build(settings, trees, mtimedb, del mydepgraph retval = mergetask.merge( mtimedb["resume"]["mergelist"], favorites, mtimedb) - if retval != os.EX_OK: - return retval + merge_count = mergetask.curval else: if "resume" in mtimedb and \ "mergelist" in mtimedb["resume"] and \ @@ -5648,20 +5695,23 @@ def action_build(settings, trees, mtimedb, del mydepgraph mergetask = MergeTask(settings, trees, myopts) retval = mergetask.merge(pkglist, favorites, mtimedb) - if retval != os.EX_OK: - return retval + merge_count = mergetask.curval + + if retval == os.EX_OK and not (pretend or fetchonly): + mtimedb.pop("resume", None) + if "yes" == settings.get("AUTOCLEAN"): + portage.writemsg_stdout(">>> Auto-cleaning packages...\n") + vartree = trees[settings["ROOT"]]["vartree"] + unmerge(settings, myopts, vartree, "clean", ["world"], + ldpath_mtimes, autoclean=1) + else: + portage.writemsg_stdout(colorize("WARN", "WARNING:") + + " AUTOCLEAN is disabled. This can cause serious" + + " problems due to overlapping packages.\n") - if mtimedb.has_key("resume"): - del mtimedb["resume"] - if settings["AUTOCLEAN"] and "yes"==settings["AUTOCLEAN"]: - portage.writemsg_stdout(">>> Auto-cleaning packages...\n") - vartree = trees[settings["ROOT"]]["vartree"] - unmerge(settings, myopts, vartree, "clean", ["world"], - ldpath_mtimes, autoclean=1) - else: - portage.writemsg_stdout(colorize("WARN", "WARNING:") - + " AUTOCLEAN is disabled. This can cause serious" - + " problems due to overlapping packages.\n") + if merge_count and not (pretend or fetchonly): + post_emerge(trees, mtimedb, retval) + return retval def multiple_actions(action1, action2): sys.stderr.write("\n!!! Multiple actions requested... Please choose one only.\n") @@ -5969,7 +6019,6 @@ def emerge_main(): for x in myfiles: ext = os.path.splitext(x)[1] if (ext == ".ebuild" or ext == ".tbz2") and os.path.exists(os.path.abspath(x)): - print "emerging by path implies --oneshot... adding --oneshot to options." print colorize("BAD", "\n*** emerging by path is broken and may not always work!!!\n") break @@ -6183,9 +6232,7 @@ def emerge_main(): display_news_notification(trees) retval = action_build(settings, trees, mtimedb, myopts, myaction, myfiles, spinner) - if "--pretend" not in myopts: - post_emerge(trees, mtimedb, retval) - else: + if "--pretend" in myopts: display_news_notification(trees) return retval diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index 20730b1c0..6515df00c 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -1868,22 +1868,29 @@ class config(object): self.getvirtuals() # Grab the virtuals this package provides and add them into the tree virtuals. provides = mydbapi.aux_get(mycpv, ["PROVIDE"])[0] + if not provides: + return if isinstance(mydbapi, portdbapi): + self.setcpv(mycpv, mydb=mydbapi) myuse = self["USE"] else: myuse = mydbapi.aux_get(mycpv, ["USE"])[0] virts = flatten(portage.dep.use_reduce(portage.dep.paren_reduce(provides), uselist=myuse.split())) + modified = False cp = dep_getkey(mycpv) for virt in virts: virt = dep_getkey(virt) - if not self.treeVirtuals.has_key(virt): - self.treeVirtuals[virt] = [] - # XXX: Is this bad? -- It's a permanent modification - if cp not in self.treeVirtuals[virt]: - self.treeVirtuals[virt].append(cp) + providers = self.treeVirtuals.get(virt) + if providers is None: + providers = [] + self.treeVirtuals[virt] = providers + if cp not in providers: + providers.append(cp) + modified = True - self.virtuals = self.__getvirtuals_compile() + if modified: + self.virtuals = self.__getvirtuals_compile() def regenerate(self,useonly=0,use_cache=1): @@ -3619,7 +3626,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, features = mysettings.features validcommands = ["help","clean","prerm","postrm","cleanrm","preinst","postinst", - "config","setup","depend","fetch","digest", + "config","info","setup","depend","fetch","digest", "unpack","compile","test","install","rpm","qmerge","merge", "package","unmerge", "manifest"] @@ -3844,7 +3851,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, writemsg("!!! post postinst failed; exiting.\n", noiselevel=-1) return phase_retval - elif mydo in ["prerm","postrm","config"]: + elif mydo in ("prerm", "postrm", "config", "info"): mysettings.load_infodir(mysettings["O"]) return spawn(EBUILD_SH_BINARY + " " + mydo, mysettings, debug=debug, free=1, logfile=logfile) diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py index 1cc0dacfc..ad16e7661 100644 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@ -342,12 +342,18 @@ class vardbapi(dbapi): cpc = self.cpcache[mycp] if cpc[0] == mystat: return cpc[1] - list = listdir(self.getpath(mysplit[0]), EmptyOnError=1) + cat_dir = self.getpath(mysplit[0]) + try: + dir_list = os.listdir(cat_dir) + except EnvironmentError, e: + from portage.exception import PermissionDenied + if e.errno == PermissionDenied.errno: + raise PermissionDenied(cat_dir) + del e + dir_list = [] - if (list is None): - return [] returnme = [] - for x in list: + for x in dir_list: if x.startswith("."): continue if x[0] == '-': @@ -892,7 +898,7 @@ class dblink(object): if not os.path.exists(self.dbdir): return try: - for x in listdir(self.dbdir): + for x in os.listdir(self.dbdir): os.unlink(self.dbdir+"/"+x) os.rmdir(self.dbdir) except OSError, e: @@ -904,6 +910,12 @@ class dblink(object): print sys.exit(1) + # Due to mtime granularity, mtime checks do not always properly + # invalidate vardbapi caches. + self.vartree.dbapi.mtdircache.pop(self.cat, None) + self.vartree.dbapi.matchcache.pop(self.cat, None) + self.vartree.dbapi.cpcache.pop(self.mysplit[0], None) + def clearcontents(self): """ For a given db entry (self), erase the CONTENTS values. @@ -1792,6 +1804,11 @@ class dblink(object): self.dbdir = self.dbpkgdir self.delete() _movefile(self.dbtmpdir, self.dbpkgdir, mysettings=self.settings) + # Due to mtime granularity, mtime checks do not always properly + # invalidate vardbapi caches. + self.vartree.dbapi.mtdircache.pop(self.cat, None) + self.vartree.dbapi.matchcache.pop(self.cat, None) + self.vartree.dbapi.cpcache.pop(self.mysplit[0], None) contents = self.getcontents() #write out our collection of md5sums diff --git a/pym/portage/glsa.py b/pym/portage/glsa.py new file mode 100644 index 000000000..ee6343640 --- /dev/null +++ b/pym/portage/glsa.py @@ -0,0 +1,596 @@ +# Copyright 2003-2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Id$ + +import os +import sys +import urllib +import time +import codecs +import re +import xml.dom.minidom + +from portage.versions import pkgsplit, catpkgsplit, pkgcmp, best +from portage.util import grabfile +from portage.const import CACHE_PATH + +# Note: the space for rgt and rlt is important !! +# FIXME: use slot deps instead, requires GLSA format versioning +opMapping = {"le": "<=", "lt": "<", "eq": "=", "gt": ">", "ge": ">=", + "rge": ">=~", "rle": "<=~", "rgt": " >~", "rlt": " <~"} +NEWLINE_ESCAPE = "!;\\n" # some random string to mark newlines that should be preserved +SPACE_ESCAPE = "!;_" # some random string to mark spaces that should be preserved + +# TODO: use the textwrap module instead +def wrap(text, width, caption=""): + """ + Wraps the given text at column I{width}, optionally indenting + it so that no text is under I{caption}. It's possible to encode + hard linebreaks in I{text} with L{NEWLINE_ESCAPE}. + + @type text: String + @param text: the text to be wrapped + @type width: Integer + @param width: the column at which the text should be wrapped + @type caption: String + @param caption: this string is inserted at the beginning of the + return value and the paragraph is indented up to + C{len(caption)}. + @rtype: String + @return: the wrapped and indented paragraph + """ + rValue = "" + line = caption + text = text.replace(2*NEWLINE_ESCAPE, NEWLINE_ESCAPE+" "+NEWLINE_ESCAPE) + words = text.split() + indentLevel = len(caption)+1 + + for w in words: + if line != "" and line[-1] == "\n": + rValue += line + line = " "*indentLevel + if len(line)+len(w.replace(NEWLINE_ESCAPE, ""))+1 > width: + rValue += line+"\n" + line = " "*indentLevel+w.replace(NEWLINE_ESCAPE, "\n") + elif w.find(NEWLINE_ESCAPE) >= 0: + if len(line.strip()) > 0: + rValue += line+" "+w.replace(NEWLINE_ESCAPE, "\n") + else: + rValue += line+w.replace(NEWLINE_ESCAPE, "\n") + line = " "*indentLevel + else: + if len(line.strip()) > 0: + line += " "+w + else: + line += w + if len(line) > 0: + rValue += line.replace(NEWLINE_ESCAPE, "\n") + rValue = rValue.replace(SPACE_ESCAPE, " ") + return rValue + +def get_glsa_list(myconfig): + """ + Returns a list of all available GLSAs in the given repository + by comparing the filelist there with the pattern described in + the config. + + @type myconfig: portage.config + @param myconfig: Portage settings instance + + @rtype: List of Strings + @return: a list of GLSA IDs in this repository + """ + rValue = [] + + if myconfig.has_key("GLSA_DIR"): + repository = myconfig["GLSA_DIR"] + else: + repository = os.path.join(myconfig["PORTDIR"], "metadata", "glsa") + + if not os.access(repository, os.R_OK): + return [] + dirlist = os.listdir(repository) + prefix = "glsa-" + suffix = ".xml" + + for f in dirlist: + try: + if f[:len(prefix)] == prefix: + rValue.append(f[len(prefix):-1*len(suffix)]) + except IndexError: + pass + return rValue + +def getListElements(listnode): + """ + Get all
  • elements for a given
      or
        node. + + @type listnode: xml.dom.Node + @param listnode:
          or
            list to get the elements for + @rtype: List of Strings + @return: a list that contains the value of the
          1. elements + """ + rValue = [] + if not listnode.nodeName in ["ul", "ol"]: + raise GlsaFormatException("Invalid function call: listnode is not
              or
                ") + for li in listnode.childNodes: + if li.nodeType != xml.dom.Node.ELEMENT_NODE: + continue + rValue.append(getText(li, format="strip")) + return rValue + +def getText(node, format): + """ + This is the main parser function. It takes a node and traverses + recursive over the subnodes, getting the text of each (and the + I{link} attribute for and ). Depending on the I{format} + parameter the text might be formatted by adding/removing newlines, + tabs and spaces. This function is only useful for the GLSA DTD, + it's not applicable for other DTDs. + + @type node: xml.dom.Node + @param node: the root node to start with the parsing + @type format: String + @param format: this should be either I{strip}, I{keep} or I{xml} + I{keep} just gets the text and does no formatting. + I{strip} replaces newlines and tabs with spaces and + replaces multiple spaces with one space. + I{xml} does some more formatting, depending on the + type of the encountered nodes. + @rtype: String + @return: the (formatted) content of the node and its subnodes + """ + rValue = "" + if format in ["strip", "keep"]: + if node.nodeName in ["uri", "mail"]: + rValue += node.childNodes[0].data+": "+node.getAttribute("link") + else: + for subnode in node.childNodes: + if subnode.nodeName == "#text": + rValue += subnode.data + else: + rValue += getText(subnode, format) + else: + for subnode in node.childNodes: + if subnode.nodeName == "p": + for p_subnode in subnode.childNodes: + if p_subnode.nodeName == "#text": + rValue += p_subnode.data.strip() + elif p_subnode.nodeName in ["uri", "mail"]: + rValue += p_subnode.childNodes[0].data + rValue += " ( "+p_subnode.getAttribute("link")+" )" + rValue += NEWLINE_ESCAPE + elif subnode.nodeName == "ul": + for li in getListElements(subnode): + rValue += "-"+SPACE_ESCAPE+li+NEWLINE_ESCAPE+" " + elif subnode.nodeName == "ol": + i = 0 + for li in getListElements(subnode): + i = i+1 + rValue += str(i)+"."+SPACE_ESCAPE+li+NEWLINE_ESCAPE+" " + elif subnode.nodeName == "code": + rValue += getText(subnode, format="keep").replace("\n", NEWLINE_ESCAPE) + if rValue[-1*len(NEWLINE_ESCAPE):] != NEWLINE_ESCAPE: + rValue += NEWLINE_ESCAPE + elif subnode.nodeName == "#text": + rValue += subnode.data + else: + raise GlsaFormatException("Invalid Tag found: ", subnode.nodeName) + if format == "strip": + rValue = rValue.strip(" \n\t") + rValue = re.sub("[\s]{2,}", " ", rValue) + return str(rValue) + +def getMultiTagsText(rootnode, tagname, format): + """ + Returns a list with the text of all subnodes of type I{tagname} + under I{rootnode} (which itself is not parsed) using the given I{format}. + + @type rootnode: xml.dom.Node + @param rootnode: the node to search for I{tagname} + @type tagname: String + @param tagname: the name of the tags to search for + @type format: String + @param format: see L{getText} + @rtype: List of Strings + @return: a list containing the text of all I{tagname} childnodes + """ + rValue = [] + for e in rootnode.getElementsByTagName(tagname): + rValue.append(getText(e, format)) + return rValue + +def makeAtom(pkgname, versionNode): + """ + creates from the given package name and information in the + I{versionNode} a (syntactical) valid portage atom. + + @type pkgname: String + @param pkgname: the name of the package for this atom + @type versionNode: xml.dom.Node + @param versionNode: a or Node that + contains the version information for this atom + @rtype: String + @return: the portage atom + """ + rValue = opMapping[versionNode.getAttribute("range")] \ + + pkgname \ + + "-" + getText(versionNode, format="strip") + return str(rValue) + +def makeVersion(versionNode): + """ + creates from the information in the I{versionNode} a + version string (format ). + + @type versionNode: xml.dom.Node + @param versionNode: a or Node that + contains the version information for this atom + @rtype: String + @return: the version string + """ + return opMapping[versionNode.getAttribute("range")] \ + + getText(versionNode, format="strip") + +def match(atom, dbapi, match_type="default"): + """ + wrapper that calls revisionMatch() or portage.dbapi.match() depending on + the given atom. + + @type atom: string + @param atom: a <~ or >~ atom or a normal portage atom that contains the atom to match against + @type dbapi: portage.dbapi + @param dbapi: one of the portage databases to use as information source + @type match_type: string + @param match_type: if != "default" passed as first argument to dbapi.xmatch + to apply the wanted visibility filters + + @rtype: list of strings + @return: a list with the matching versions + """ + if atom[2] == "~": + return revisionMatch(atom, dbapi, match_type=match_type) + elif match_type == "default" or not hasattr(dbapi, "xmatch"): + return dbapi.match(atom) + else: + return dbapi.xmatch(match_type, atom) + +def revisionMatch(revisionAtom, dbapi, match_type="default"): + """ + handler for the special >~, >=~, <=~ and <~ atoms that are supposed to behave + as > and < except that they are limited to the same version, the range only + applies to the revision part. + + @type revisionAtom: string + @param revisionAtom: a <~ or >~ atom that contains the atom to match against + @type dbapi: portage.dbapi + @param dbapi: one of the portage databases to use as information source + @type match_type: string + @param match_type: if != "default" passed as first argument to portdb.xmatch + to apply the wanted visibility filters + + @rtype: list of strings + @return: a list with the matching versions + """ + if match_type == "default" or not hasattr(dbapi, "xmatch"): + mylist = dbapi.match(re.sub("-r[0-9]+$", "", revisionAtom[2:])) + else: + mylist = dbapi.xmatch(match_type, re.sub("-r[0-9]+$", "", revisionAtom[2:])) + rValue = [] + for v in mylist: + r1 = pkgsplit(v)[-1][1:] + r2 = pkgsplit(revisionAtom[3:])[-1][1:] + if eval(r1+" "+revisionAtom[0:2]+" "+r2): + rValue.append(v) + return rValue + + +def getMinUpgrade(vulnerableList, unaffectedList, portdbapi, vardbapi, minimize=True): + """ + Checks if the systemstate is matching an atom in + I{vulnerableList} and returns string describing + the lowest version for the package that matches an atom in + I{unaffectedList} and is greater than the currently installed + version or None if the system is not affected. Both + I{vulnerableList} and I{unaffectedList} should have the + same base package. + + @type vulnerableList: List of Strings + @param vulnerableList: atoms matching vulnerable package versions + @type unaffectedList: List of Strings + @param unaffectedList: atoms matching unaffected package versions + @type portdbapi: portage.dbapi.porttree.portdbapi + @param portdbapi: Ebuild repository + @type vardbapi: portage.dbapi.vartree.vardbapi + @param vardbapi: Installed package repository + @type minimize: Boolean + @param minimize: True for a least-change upgrade, False for emerge-like algorithm + + @rtype: String | None + @return: the lowest unaffected version that is greater than + the installed version. + """ + rValue = None + v_installed = [] + u_installed = [] + for v in vulnerableList: + v_installed += match(v, vardbapi) + + for u in unaffectedList: + u_installed += match(u, vardbapi) + + install_unaffected = True + for i in v_installed: + if i not in u_installed: + install_unaffected = False + + if install_unaffected: + return rValue + + for u in unaffectedList: + mylist = match(u, portdbapi, match_type="match-all") + for c in mylist: + c_pv = catpkgsplit(c) + i_pv = catpkgsplit(best(v_installed)) + if pkgcmp(c_pv[1:], i_pv[1:]) > 0 \ + and (rValue == None \ + or not match("="+rValue, portdbapi) \ + or (minimize ^ (pkgcmp(c_pv[1:], catpkgsplit(rValue)[1:]) > 0)) \ + and match("="+c, portdbapi)) \ + and portdbapi.aux_get(c, ["SLOT"]) == vardbapi.aux_get(best(v_installed), ["SLOT"]): + rValue = c_pv[0]+"/"+c_pv[1]+"-"+c_pv[2] + if c_pv[3] != "r0": # we don't like -r0 for display + rValue += "-"+c_pv[3] + return rValue + + +# simple Exception classes to catch specific errors +class GlsaTypeException(Exception): + def __init__(self, doctype): + Exception.__init__(self, "wrong DOCTYPE: %s" % doctype) + +class GlsaFormatException(Exception): + pass + +class GlsaArgumentException(Exception): + pass + +# GLSA xml data wrapper class +class Glsa: + """ + This class is a wrapper for the XML data and provides methods to access + and display the contained data. + """ + def __init__(self, myid, myconfig, vardbapi, portdbapi): + """ + Simple constructor to set the ID, store the config and gets the + XML data by calling C{self.read()}. + + @type myid: String + @param myid: String describing the id for the GLSA object (standard + GLSAs have an ID of the form YYYYMM-nn) or an existing + filename containing a GLSA. + @type myconfig: portage.config + @param myconfig: the config that should be used for this object. + @type vardbapi: portage.dbapi.vartree.vardbapi + @param vardbapi: installed package repository + @type portdbapi: portage.dbapi.porttree.portdbapi + @param portdbapi: ebuild repository + """ + if re.match(r'\d{6}-\d{2}', myid): + self.type = "id" + elif os.path.exists(myid): + self.type = "file" + else: + raise GlsaArgumentException("Given ID "+myid+" isn't a valid GLSA ID or filename.") + self.nr = myid + self.config = myconfig + self.vardbapi = vardbapi + self.portdbapi = portdbapi + self.read() + + def read(self): + """ + Here we build the filename from the config and the ID and pass + it to urllib to fetch it from the filesystem or a remote server. + + @rtype: None + @return: None + """ + if self.config.has_key("GLSA_DIR"): + repository = "file://" + self.config["GLSA_DIR"]+"/" + else: + repository = "file://" + self.config["PORTDIR"] + "/metadata/glsa/" + if self.type == "file": + myurl = "file://"+self.nr + else: + myurl = repository + "glsa-%s.xml" % str(self.nr) + self.parse(urllib.urlopen(myurl)) + return None + + def parse(self, myfile): + """ + This method parses the XML file and sets up the internal data + structures by calling the different helper functions in this + module. + + @type myfile: String + @param myfile: Filename to grab the XML data from + @rtype: None + @returns: None + """ + self.DOM = xml.dom.minidom.parse(myfile) + if not self.DOM.doctype: + raise GlsaTypeException(None) + elif self.DOM.doctype.systemId != "http://www.gentoo.org/dtd/glsa.dtd": + raise GlsaTypeException(self.DOM.doctype.systemId) + myroot = self.DOM.getElementsByTagName("glsa")[0] + if self.type == "id" and myroot.getAttribute("id") != self.nr: + raise GlsaFormatException("filename and internal id don't match:" + myroot.getAttribute("id") + " != " + self.nr) + + # the simple (single, required, top-level, #PCDATA) tags first + self.title = getText(myroot.getElementsByTagName("title")[0], format="strip") + self.synopsis = getText(myroot.getElementsByTagName("synopsis")[0], format="strip") + self.announced = getText(myroot.getElementsByTagName("announced")[0], format="strip") + self.revised = getText(myroot.getElementsByTagName("revised")[0], format="strip") + + # now the optional and 0-n toplevel, #PCDATA tags and references + try: + self.access = getText(myroot.getElementsByTagName("access")[0], format="strip") + except IndexError: + self.access = "" + self.bugs = getMultiTagsText(myroot, "bug", format="strip") + self.references = getMultiTagsText(myroot.getElementsByTagName("references")[0], "uri", format="keep") + + # and now the formatted text elements + self.description = getText(myroot.getElementsByTagName("description")[0], format="xml") + self.workaround = getText(myroot.getElementsByTagName("workaround")[0], format="xml") + self.resolution = getText(myroot.getElementsByTagName("resolution")[0], format="xml") + self.impact_text = getText(myroot.getElementsByTagName("impact")[0], format="xml") + self.impact_type = myroot.getElementsByTagName("impact")[0].getAttribute("type") + try: + self.background = getText(myroot.getElementsByTagName("background")[0], format="xml") + except IndexError: + self.background = "" + + # finally the interesting tags (product, affected, package) + self.glsatype = myroot.getElementsByTagName("product")[0].getAttribute("type") + self.product = getText(myroot.getElementsByTagName("product")[0], format="strip") + self.affected = myroot.getElementsByTagName("affected")[0] + self.packages = {} + for p in self.affected.getElementsByTagName("package"): + name = p.getAttribute("name") + if not self.packages.has_key(name): + self.packages[name] = [] + tmp = {} + tmp["arch"] = p.getAttribute("arch") + tmp["auto"] = (p.getAttribute("auto") == "yes") + tmp["vul_vers"] = [makeVersion(v) for v in p.getElementsByTagName("vulnerable")] + tmp["unaff_vers"] = [makeVersion(v) for v in p.getElementsByTagName("unaffected")] + tmp["vul_atoms"] = [makeAtom(name, v) for v in p.getElementsByTagName("vulnerable")] + tmp["unaff_atoms"] = [makeAtom(name, v) for v in p.getElementsByTagName("unaffected")] + self.packages[name].append(tmp) + # TODO: services aren't really used yet + self.services = self.affected.getElementsByTagName("service") + return None + + def dump(self, outstream=sys.stdout): + """ + Dumps a plaintext representation of this GLSA to I{outfile} or + B{stdout} if it is ommitted. You can specify an alternate + I{encoding} if needed (default is latin1). + + @type outstream: File + @param outfile: Stream that should be used for writing + (defaults to sys.stdout) + """ + width = 76 + outstream.write(("GLSA %s: \n%s" % (self.nr, self.title)).center(width)+"\n") + outstream.write((width*"=")+"\n") + outstream.write(wrap(self.synopsis, width, caption="Synopsis: ")+"\n") + outstream.write("Announced on: %s\n" % self.announced) + outstream.write("Last revised on: %s\n\n" % self.revised) + if self.glsatype == "ebuild": + for k in self.packages.keys(): + pkg = self.packages[k] + for path in pkg: + vul_vers = "".join(path["vul_vers"]) + unaff_vers = "".join(path["unaff_vers"]) + outstream.write("Affected package: %s\n" % k) + outstream.write("Affected archs: ") + if path["arch"] == "*": + outstream.write("All\n") + else: + outstream.write("%s\n" % path["arch"]) + outstream.write("Vulnerable: %s\n" % vul_vers) + outstream.write("Unaffected: %s\n\n" % unaff_vers) + elif self.glsatype == "infrastructure": + pass + if len(self.bugs) > 0: + outstream.write("\nRelated bugs: ") + for i in range(0, len(self.bugs)): + outstream.write(self.bugs[i]) + if i < len(self.bugs)-1: + outstream.write(", ") + else: + outstream.write("\n") + if self.background: + outstream.write("\n"+wrap(self.background, width, caption="Background: ")) + outstream.write("\n"+wrap(self.description, width, caption="Description: ")) + outstream.write("\n"+wrap(self.impact_text, width, caption="Impact: ")) + outstream.write("\n"+wrap(self.workaround, width, caption="Workaround: ")) + outstream.write("\n"+wrap(self.resolution, width, caption="Resolution: ")) + myreferences = "" + for r in self.references: + myreferences += (r.replace(" ", SPACE_ESCAPE)+NEWLINE_ESCAPE+" ") + outstream.write("\n"+wrap(myreferences, width, caption="References: ")) + outstream.write("\n") + + def isVulnerable(self): + """ + Tests if the system is affected by this GLSA by checking if any + vulnerable package versions are installed. Also checks for affected + architectures. + + @rtype: Boolean + @returns: True if the system is affected, False if not + """ + vList = [] + rValue = False + for k in self.packages.keys(): + pkg = self.packages[k] + for path in pkg: + if path["arch"] == "*" or self.config["ARCH"] in path["arch"].split(): + for v in path["vul_atoms"]: + rValue = rValue \ + or (len(match(v, self.vardbapi)) > 0 \ + and getMinUpgrade(path["vul_atoms"], path["unaff_atoms"], \ + self.portdbapi, self.vardbapi)) + return rValue + + def isApplied(self): + """ + Looks if the GLSA IDis in the GLSA checkfile to check if this + GLSA was already applied. + + @rtype: Boolean + @returns: True if the GLSA was applied, False if not + """ + aList = grabfile(os.path.join(os.sep, self.config["ROOT"], CACHE_PATH.lstrip(os.sep))) + return (self.nr in aList) + + def inject(self): + """ + Puts the ID of this GLSA into the GLSA checkfile, so it won't + show up on future checks. Should be called after a GLSA is + applied or on explicit user request. + + @rtype: None + @returns: None + """ + if not self.isApplied(): + checkfile = open(os.path.join(os.sep, self.config["ROOT"], CACHE_PATH.lstrip(os.sep)), "a+") + checkfile.write(self.nr+"\n") + checkfile.close() + return None + + def getMergeList(self, least_change=True): + """ + Returns the list of package-versions that have to be merged to + apply this GLSA properly. The versions are as low as possible + while avoiding downgrades (see L{getMinUpgrade}). + + @type least_change: Boolean + @param least_change: True if the smallest possible upgrade should be selected, + False for an emerge-like algorithm + @rtype: List of Strings + @return: list of package-versions that have to be merged + """ + rValue = [] + for pkg in self.packages.keys(): + for path in self.packages[pkg]: + update = getMinUpgrade(path["vul_atoms"], path["unaff_atoms"], \ + self.portdbapi, self.vardbapi, minimize=least_change) + if update: + rValue.append(update) + return ["="+x for x in rValue] diff --git a/pym/portage/mail.py b/pym/portage/mail.py index 7c326b039..0c39b4cc9 100644 --- a/pym/portage/mail.py +++ b/pym/portage/mail.py @@ -1,7 +1,7 @@ # portage.py -- core Portage functionality # Copyright 1998-2004 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -# $Id: portage.py 3483 2006-06-10 21:40:40Z genone $ +# $Id$ import portage.exception, socket, smtplib, os, sys, time from email.MIMEText import MIMEText as TextMessage diff --git a/pym/portage/output.py b/pym/portage/output.py index 470fcb9b2..384625bea 100644 --- a/pym/portage/output.py +++ b/pym/portage/output.py @@ -117,17 +117,17 @@ codes["darkteal"] = codes["turquoise"] # Some terminals have darkyellow instead of brown. codes["0xAAAA00"] = codes["brown"] codes["darkyellow"] = codes["0xAAAA00"] -codes["fuscia"] = codes["fuchsia"] -codes["white"] = codes["bold"] - -codes["bg_black"] = esc_seq + "40m" -codes["bg_red"] = esc_seq + "41m" -codes["bg_green"] = esc_seq + "42m" -codes["bg_brown"] = esc_seq + "43m" -codes["bg_blue"] = esc_seq + "44m" -codes["bg_magenta"] = esc_seq + "45m" -codes["bg_cyan"] = esc_seq + "46m" -codes["bg_white"] = esc_seq + "47m" + +codes["bg_black"] = esc_seq + "40m" +codes["bg_darkred"] = esc_seq + "41m" +codes["bg_darkgreen"] = esc_seq + "42m" +codes["bg_brown"] = esc_seq + "43m" +codes["bg_darkblue"] = esc_seq + "44m" +codes["bg_purple"] = esc_seq + "45m" +codes["bg_teal"] = esc_seq + "46m" +codes["bg_lightgray"] = esc_seq + "47m" + +codes["bg_darkyellow"] = codes["bg_brown"] # Colors from /sbin/functions.sh codes["GOOD"] = codes["green"] @@ -142,17 +142,13 @@ codes["UNMERGE_WARN"] = codes["red"] codes["SECURITY_WARN"] = codes["red"] codes["MERGE_LIST_PROGRESS"] = codes["yellow"] codes["PKG_MERGE"] = codes["darkgreen"] -codes["PKG_MERGE_ARG"] = codes["darkgreen"] -codes["PKG_MERGE_SYSTEM"] = codes["green"] +codes["PKG_MERGE_SYSTEM"] = codes["darkgreen"] codes["PKG_MERGE_WORLD"] = codes["green"] -codes["PKG_MERGE_ARG_SYSTEM"] = codes["green"] -codes["PKG_MERGE_ARG_WORLD"] = codes["green"] codes["PKG_NOMERGE"] = codes["darkblue"] -codes["PKG_NOMERGE_ARG"] = codes["darkblue"] -codes["PKG_NOMERGE_SYSTEM"] = codes["blue"] +codes["PKG_NOMERGE_SYSTEM"] = codes["darkblue"] codes["PKG_NOMERGE_WORLD"] = codes["blue"] -codes["PKG_NOMERGE_ARG_SYSTEM"] = codes["blue"] -codes["PKG_NOMERGE_ARG_WORLD"] = codes["blue"] +codes["PROMPT_CHOICE_DEFAULT"] = codes["green"] +codes["PROMPT_CHOICE_OTHER"] = codes["red"] def parse_color_map(onerror=None): """ @@ -294,7 +290,7 @@ def colorize(color_key, text): return text compat_functions_colors = ["bold","white","teal","turquoise","darkteal", - "fuscia","fuchsia","purple","blue","darkblue","green","darkgreen","yellow", + "fuchsia","purple","blue","darkblue","green","darkgreen","yellow", "brown","darkyellow","red","darkred"] def create_color_func(color_key): diff --git a/pym/portage/sets/__init__.py b/pym/portage/sets/__init__.py new file mode 100644 index 000000000..e0be0047c --- /dev/null +++ b/pym/portage/sets/__init__.py @@ -0,0 +1,120 @@ +# Copyright 2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Id$ + +import os + +from portage.const import PRIVATE_PATH, USER_CONFIG_PATH +from portage.exception import InvalidAtom +from portage.dep import isvalidatom + +OPERATIONS = ["merge", "unmerge", "edit"] +DEFAULT_SETS = ["world", "system", "everything", "security"] \ + +["package_"+x for x in ["mask", "unmask", "use", "keywords"]] + +class PackageSet(object): + _operations = ["merge"] + + def __init__(self, name): + self._name = name + self._nodes = [] + self._loaded = False + + def supportsOperation(self, op): + if not op in OPERATIONS: + raise ValueError(op) + return op in self._operations + + def getNodes(self): + if not self._loaded: + self.load() + self._loaded = True + return self._nodes + + def _setNodes(self, nodes): + nodes = map(str.strip, nodes) + for n in nodes[:]: + if n == "": + nodes.remove(n) + elif not isvalidatom(n): + raise InvalidAtom(n) + self._nodes = nodes + + def getName(self): + return self._name + + def addNode(self, node): + if self.supportsOperation("edit"): + self.load() + self._nodes.append(node) + self.write() + else: + raise NotImplementedError() + + def removeNode(self, node): + if self.supportsOperation("edit"): + self.load() + self._nodes.remove(node) + self.write() + else: + raise NotImplementedError() + + def write(self): + raise NotImplementedError() + + def load(self): + raise NotImplementedError() + +class EmptyPackageSet(PackageSet): + _operations = ["merge", "unmerge"] + +def make_default_sets(configroot, root, profile_paths, settings=None, + vdbapi=None, portdbapi=None): + from portage.sets.files import StaticFileSet, ConfigFileSet + from portage.sets.profiles import PackagesSystemSet + from portage.sets.security import AffectedSet + from portage.sets.dbapi import EverythingSet + + rValue = set() + rValue.add(StaticFileSet("world", os.path.join(root, PRIVATE_PATH, "world"))) + for suffix in ["mask", "unmask", "keywords", "use"]: + myname = "package_"+suffix + myset = ConfigFileSet(myname, os.path.join(configroot, USER_CONFIG_PATH.lstrip(os.sep), "package."+suffix)) + rValue.add(myset) + rValue.add(PackagesSystemSet("system", profile_paths)) + if settings != None and portdbapi != None: + rValue.add(AffectedSet("security", settings, vdbapi, portdbapi)) + else: + rValue.add(EmptyPackageSet("security")) + if vdbapi != None: + rValue.add(EverythingSet("everything", vdbapi)) + else: + rValue.add(EmptyPackageSet("everything")) + + return rValue + +def make_extra_static_sets(configroot): + from portage.sets.files import StaticFileSet + + rValue = set() + mydir = os.path.join(configroot, USER_CONFIG_PATH.lstrip(os.sep), "sets") + try: + mysets = os.listdir(mydir) + except (OSError, IOError): + return rValue + for myname in mysets: + if myname in DEFAULT_SETS: + continue + rValue.add(StaticFileSet(fname, os.path.join(mydir, myname))) + return rValue + +# adhoc test code +if __name__ == "__main__": + import portage + l = make_default_sets("/", "/", portage.settings.profiles, portage.settings, portage.db["/"]["vartree"].dbapi, portage.db["/"]["porttree"].dbapi) + l.update(make_extra_static_sets("/")) + for x in l: + print x.getName()+":" + for n in sorted(x.getNodes()): + print "- "+n + print diff --git a/pym/portage/sets/dbapi.py b/pym/portage/sets/dbapi.py new file mode 100644 index 000000000..dcc0552ef --- /dev/null +++ b/pym/portage/sets/dbapi.py @@ -0,0 +1,43 @@ +# Copyright 2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Id$ + +from portage.versions import catsplit + +from portage.sets import PackageSet + +class EverythingSet(PackageSet): + _operations = ["merge", "unmerge"] + + def __init__(self, name, vdbapi): + super(EverythingSet, self).__init__(name) + self._db = vdbapi + + def load(self): + mynodes = [] + for cp in self._db.cp_all(): + if len(self._db.cp_list(cp)) > 1: + for cpv in self._db.cp_list(cp): + myslot = self._db.aux_get(cpv, ["SLOT"])[0] + mynodes.append(cp+":"+myslot) + else: + mynodes.append(cp) + self._setNodes(mynodes) + +class CategorySet(PackageSet): + _operations = ["merge", "unmerge"] + + def __init__(self, name, category, portdbapi, only_visible=True): + super(CategorySet, self).__init__(name) + self._db = portdbapi + self._category = category + self._check = only_visible + + def load(self): + mynodes = [] + for cp in self._db.cp_all(): + if catsplit(cp)[0] == self._category: + if (not self._check) or len(self._db.match(cp)) > 0: + mynodes.append(cp) + self._setNodes(mynodes) + diff --git a/pym/portage/sets/files.py b/pym/portage/sets/files.py new file mode 100644 index 000000000..8e1bca99c --- /dev/null +++ b/pym/portage/sets/files.py @@ -0,0 +1,30 @@ +# Copyright 2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Id$ + +from portage.util import grabfile_package, grabdict_package, write_atomic + +from portage.sets import PackageSet + +class StaticFileSet(PackageSet): + _operations = ["merge", "unmerge", "edit"] + + def __init__(self, name, filename): + super(StaticFileSet, self).__init__(name) + self._filename = filename + + def write(self): + write_atomic(self._filename, "\n".join(self._nodes)+"\n") + + def load(self): + self._setNodes(grabfile_package(self._filename, recursive=True)) + +class ConfigFileSet(StaticFileSet): + _operations = ["merge", "unmerge"] + + def write(self): + raise NotImplementedError() + + def load(self): + self._setNodes(grabdict_package(self._filename, recursive=True).keys()) + diff --git a/pym/portage/sets/profiles.py b/pym/portage/sets/profiles.py new file mode 100644 index 000000000..fe7ab201a --- /dev/null +++ b/pym/portage/sets/profiles.py @@ -0,0 +1,20 @@ +# Copyright 2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Id$ + +import os +from portage.util import grabfile_package, stack_lists + +from portage.sets import PackageSet + +class PackagesSystemSet(PackageSet): + _operations = ["merge"] + + def __init__(self, name, profile_paths): + super(PackagesSystemSet, self).__init__(name) + self._profile_paths = profile_paths + + def load(self): + mylist = [grabfile_package(os.path.join(x, "packages")) for x in self._profile_paths] + mylist = stack_lists(mylist, incremental=1) + self._setNodes([x[1:] for x in mylist if x[0] == "*"]) diff --git a/pym/portage/sets/security.py b/pym/portage/sets/security.py new file mode 100644 index 000000000..d7db89d95 --- /dev/null +++ b/pym/portage/sets/security.py @@ -0,0 +1,43 @@ +# Copyright 2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Id$ + +import portage.glsa as glsa + +from portage.sets import PackageSet + +class SecuritySet(PackageSet): + _operations = ["merge"] + + def __init__(self, name, settings, vardbapi, portdbapi): + super(SecuritySet, self).__init__(name) + self._settings = settings + self._vardbapi = vardbapi + self._portdbapi = portdbapi + + def loadCheckfile(self): + self._appliedlist = grabfile(os.path.join(os.sep, self._settings["ROOT"], CACHE_PATH, "glsa")) + + def load(self): + glsaindexlist = glsa.get_glsa_list(self._settings) + nodelist = [] + for glsaid in glsaindexlist: + myglsa = glsa.Glsa(glsaid, self._settings, self._vardbapi, self._portdbapi) + #print glsaid, myglsa.isVulnerable(), myglsa.isApplied(), myglsa.getMergeList() + if self.useGlsa(myglsa): + nodelist += myglsa.getMergeList(least_change=False) + self._setNodes(nodelist) + + def useGlsaId(self, glsaid): + return True + + def useGlsa(self, myglsa): + return True + +class NewGlsaSet(SecuritySet): + def useGlsa(self, myglsa): + return not myglsa.isApplied() + +class AffectedSet(SecuritySet): + def useGlsa(self, myglsa): + return myglsa.isVulnerable() diff --git a/pym/portage/sets/shell.py b/pym/portage/sets/shell.py new file mode 100644 index 000000000..8874743c9 --- /dev/null +++ b/pym/portage/sets/shell.py @@ -0,0 +1,21 @@ +# Copyright 2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Id$ + +import subprocess, os + +from portage.sets import PackageSet + +class CommandOutputSet(PackageSet): + _operations = ["merge", "unmerge"] + + def __init__(self, name, command): + super(CommandOutputSet, self).__init__(name) + self._command = command + + def load(self): + pipe = subprocess.Popen(self._command, stdout=subprocess.PIPE, shell=True) + if pipe.wait() == os.EX_OK: + text = pipe.stdout.read() + self._setNodes(text.split("\n")) +