From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from pigeon.gentoo.org ([208.92.234.80] helo=lists.gentoo.org) by finch.gentoo.org with esmtp (Exim 4.60) (envelope-from ) id 1QLgs7-0000Lx-F9 for garchives@archives.gentoo.org; Sun, 15 May 2011 19:20:59 +0000 Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id C4BD21C04E; Sun, 15 May 2011 19:20:51 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) by pigeon.gentoo.org (Postfix) with ESMTP id 355B01C04E for ; Sun, 15 May 2011 19:20:51 +0000 (UTC) Received: from pelican.gentoo.org (unknown [66.219.59.40]) (using TLSv1 with cipher ADH-CAMELLIA256-SHA (256/256 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 6CA671B404C for ; Sun, 15 May 2011 19:20:50 +0000 (UTC) Received: from localhost.localdomain (localhost [127.0.0.1]) by pelican.gentoo.org (Postfix) with ESMTP id C805680504 for ; Sun, 15 May 2011 19:20:49 +0000 (UTC) From: "Zac Medico" To: gentoo-commits@lists.gentoo.org Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Zac Medico" Message-ID: Subject: [gentoo-commits] proj/portage:master commit in: man/, pym/_emerge/ X-VCS-Repository: proj/portage X-VCS-Files: man/emerge.1 pym/_emerge/depgraph.py pym/_emerge/help.py pym/_emerge/main.py X-VCS-Directories: man/ pym/_emerge/ X-VCS-Committer: zmedico X-VCS-Committer-Name: Zac Medico X-VCS-Revision: c492b1b3ed631b6802ef1192f59d2ef93967fb0a Date: Sun, 15 May 2011 19:20:49 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: quoted-printable X-Archives-Salt: X-Archives-Hash: 051ba63bfcea9e2263a3318381003e49 commit: c492b1b3ed631b6802ef1192f59d2ef93967fb0a Author: Sebastian Luther gmx de> AuthorDate: Sun May 15 19:01:03 2011 +0000 Commit: Zac Medico gentoo org> CommitDate: Sun May 15 19:01:03 2011 +0000 URL: http://git.overlays.gentoo.org/gitweb/?p=3Dproj/portage.git;a= =3Dcommit;h=3Dc492b1b3 Implement --autounmask-write Enabling this option together with --autounmask writes proposed changes to config files, honoring CONFIG_PROTECT. --- man/emerge.1 | 4 + pym/_emerge/depgraph.py | 271 ++++++++++++++++++++++++++++++++++-------= ------ pym/_emerge/help.py | 7 ++ pym/_emerge/main.py | 10 ++ 4 files changed, 216 insertions(+), 76 deletions(-) diff --git a/man/emerge.1 b/man/emerge.1 index 539b2e3..0f2acc1 100644 --- a/man/emerge.1 +++ b/man/emerge.1 @@ -301,6 +301,10 @@ the specified configuration file(s). Currently, this only works for unstable KEYWORDS masks, LICENSE masks, and package.use settings. .TP +.BR "\-\-autounmask\-write [ y | n ]" +If \-\-autounmask is enabled, changes are written +to config files, respecting \fBCONFIG_PROTECT\fR. +.TP .BR \-\-backtrack=3DCOUNT Specifies an integer number of times to backtrack if dependency calculation fails due to a conflict or an diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 16cb7fc..8558436 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -3,6 +3,7 @@ =20 from __future__ import print_function =20 +import codecs import difflib import gc import logging @@ -14,12 +15,12 @@ from itertools import chain =20 import portage from portage import os, OrderedDict -from portage import _unicode_decode -from portage.const import PORTAGE_PACKAGE_ATOM +from portage import _unicode_decode, _unicode_encode, _encodings +from portage.const import PORTAGE_PACKAGE_ATOM, USER_CONFIG_PATH from portage.dbapi import dbapi from portage.dep import Atom, extract_affecting_use, check_required_use,= human_readable_required_use, _repo_separator from portage.eapi import eapi_has_strong_blocks, eapi_has_required_use -from portage.exception import InvalidAtom, InvalidDependString +from portage.exception import InvalidAtom, InvalidDependString, PortageE= xception from portage.output import colorize, create_color_func, \ darkgreen, green bad =3D create_color_func("BAD") @@ -27,8 +28,9 @@ from portage.package.ebuild.getmaskingstatus import \ _getmaskingstatus, _MaskReason from portage._sets import SETPREFIX from portage._sets.base import InternalPackageSet +from portage.util import ConfigProtect, shlex_split, new_protect_filenam= e from portage.util import cmp_sort_key, writemsg, writemsg_stdout -from portage.util import writemsg_level +from portage.util import writemsg_level, write_atomic from portage.util.digraph import digraph from portage.versions import catpkgsplit =20 @@ -5493,55 +5495,14 @@ class depgraph(object): =20 return display(self, mylist, favorites, verbosity) =20 - def display_problems(self): + def _display_autounmask(self): """ - Display problems with the dependency graph such as slot collisions. - This is called internally by display() to show the problems _after_ - the merge list where it is most likely to be seen, but if display() - is not going to be called then this method should be called explicitly - to ensure that the user is notified of problems with the graph. - - All output goes to stderr, except for unsatisfied dependencies which - go to stdout for parsing by programs such as autounmask. + Display --autounmask message and optionally write them to config files + (using CONFIG_PROTECT). The message includes the comments and the chan= ges. """ =20 - # Note that show_masked_packages() sends it's output to - # stdout, and some programs such as autounmask parse the - # output in cases when emerge bails out. However, when - # show_masked_packages() is called for installed packages - # here, the message is a warning that is more appropriate - # to send to stderr, so temporarily redirect stdout to - # stderr. TODO: Fix output code so there's a cleaner way - # to redirect everything to stderr. - sys.stdout.flush() - sys.stderr.flush() - stdout =3D sys.stdout - try: - sys.stdout =3D sys.stderr - self._display_problems() - finally: - sys.stdout =3D stdout - sys.stdout.flush() - sys.stderr.flush() - - # This goes to stdout for parsing by programs like autounmask. - for pargs, kwargs in self._dynamic_config._unsatisfied_deps_for_displa= y: - self._show_unsatisfied_dep(*pargs, **kwargs) - - def _display_problems(self): - if self._dynamic_config._circular_deps_for_display is not None: - self._show_circular_deps( - self._dynamic_config._circular_deps_for_display) - - # The user is only notified of a slot conflict if - # there are no unresolvable blocker conflicts. - if self._dynamic_config._unsatisfied_blockers_for_display is not None: - self._show_unsatisfied_blockers( - self._dynamic_config._unsatisfied_blockers_for_display) - elif self._dynamic_config._slot_collision_info: - self._show_slot_collision_notice() - else: - self._show_missed_update() + autounmask_write =3D self._frozen_config.myopts.get("--autounmask-writ= e", "n") =3D=3D True + pretend =3D "--pretend" in self._frozen_config.myopts =20 def check_if_latest(pkg): is_latest =3D True @@ -5569,11 +5530,16 @@ class depgraph(object): =20 return is_latest, is_latest_in_slot =20 + #Set of roots we have autounmask changes for. + roots =3D set() =20 - unstable_keyword_msg =3D [] + unstable_keyword_msg =3D {} for pkg in self._dynamic_config._needed_unstable_keywords: self._show_merge_list() if pkg in self._dynamic_config.digraph: + root =3D pkg.root + roots.add(root) + unstable_keyword_msg.setdefault(root, []) is_latest, is_latest_in_slot =3D check_if_latest(pkg) pkgsettings =3D self._frozen_config.pkgsettings[pkg.root] mreasons =3D _get_masking_status(pkg, pkgsettings, pkg.root_config, @@ -5583,18 +5549,21 @@ class depgraph(object): reason.unmask_hint.key =3D=3D 'unstable keyword': keyword =3D reason.unmask_hint.value =20 - unstable_keyword_msg.append(self._get_dep_chain_as_comment(pkg)) + unstable_keyword_msg[root].append(self._get_dep_chain_as_comment(p= kg)) if is_latest: - unstable_keyword_msg.append(">=3D%s %s\n" % (pkg.cpv, keyword)) + unstable_keyword_msg[root].append(">=3D%s %s\n" % (pkg.cpv, keywo= rd)) elif is_latest_in_slot: - unstable_keyword_msg.append(">=3D%s:%s %s\n" % (pkg.cpv, pkg.meta= data["SLOT"], keyword)) + unstable_keyword_msg[root].append(">=3D%s:%s %s\n" % (pkg.cpv, pk= g.metadata["SLOT"], keyword)) else: - unstable_keyword_msg.append("=3D%s %s\n" % (pkg.cpv, keyword)) + unstable_keyword_msg[root].append("=3D%s %s\n" % (pkg.cpv, keywor= d)) =20 - use_changes_msg =3D [] + use_changes_msg =3D {} for pkg, needed_use_config_change in self._dynamic_config._needed_use_= config_changes.items(): self._show_merge_list() if pkg in self._dynamic_config.digraph: + root =3D pkg.root + roots.add(root) + use_changes_msg.setdefault(root, []) is_latest, is_latest_in_slot =3D check_if_latest(pkg) changes =3D needed_use_config_change[1] adjustments =3D [] @@ -5603,42 +5572,192 @@ class depgraph(object): adjustments.append(flag) else: adjustments.append("-" + flag) - use_changes_msg.append(self._get_dep_chain_as_comment(pkg, unsatisfi= ed_dependency=3DTrue)) + use_changes_msg[root].append(self._get_dep_chain_as_comment(pkg, uns= atisfied_dependency=3DTrue)) if is_latest: - use_changes_msg.append(">=3D%s %s\n" % (pkg.cpv, " ".join(adjustmen= ts))) + use_changes_msg[root].append(">=3D%s %s\n" % (pkg.cpv, " ".join(adj= ustments))) elif is_latest_in_slot: - use_changes_msg.append(">=3D%s:%s %s\n" % (pkg.cpv, pkg.metadata["S= LOT"], " ".join(adjustments))) + use_changes_msg[root].append(">=3D%s:%s %s\n" % (pkg.cpv, pkg.metad= ata["SLOT"], " ".join(adjustments))) else: - use_changes_msg.append("=3D%s %s\n" % (pkg.cpv, " ".join(adjustment= s))) + use_changes_msg[root].append("=3D%s %s\n" % (pkg.cpv, " ".join(adju= stments))) =20 - license_msg =3D [] + license_msg =3D {} for pkg, missing_licenses in self._dynamic_config._needed_license_chan= ges.items(): self._show_merge_list() if pkg in self._dynamic_config.digraph: + root =3D pkg.root + roots.add(root) + license_msg.setdefault(root, []) is_latest, is_latest_in_slot =3D check_if_latest(pkg) =20 - license_msg.append(self._get_dep_chain_as_comment(pkg)) + license_msg[root].append(self._get_dep_chain_as_comment(pkg)) if is_latest: - license_msg.append(">=3D%s %s\n" % (pkg.cpv, " ".join(sorted(missin= g_licenses)))) + license_msg[root].append(">=3D%s %s\n" % (pkg.cpv, " ".join(sorted(= missing_licenses)))) elif is_latest_in_slot: - license_msg.append(">=3D%s:%s %s\n" % (pkg.cpv, pkg.metadata["SLOT"= ], " ".join(sorted(missing_licenses)))) + license_msg[root].append(">=3D%s:%s %s\n" % (pkg.cpv, pkg.metadata[= "SLOT"], " ".join(sorted(missing_licenses)))) else: - license_msg.append("=3D%s %s\n" % (pkg.cpv, " ".join(sorted(missing= _licenses)))) + license_msg[root].append("=3D%s %s\n" % (pkg.cpv, " ".join(sorted(m= issing_licenses)))) =20 - if unstable_keyword_msg: - writemsg_stdout("\nThe following " + colorize("BAD", "keyword changes= ") + \ - " are necessary to proceed:\n", noiselevel=3D-1) - writemsg_stdout("".join(unstable_keyword_msg), noiselevel=3D-1) + def find_config_file(abs_user_config, file_name): + """ + Searches /etc/portage for an appropiate file to append changes to. + If the file_name is a file it is returned, if it is a directoy, the + last file in it is returned. =20 - if use_changes_msg: - writemsg_stdout("\nThe following " + colorize("BAD", "USE changes") += \ - " are necessary to proceed:\n", noiselevel=3D-1) - writemsg_stdout("".join(use_changes_msg), noiselevel=3D-1) + file_name - String containg a file name like "package.use" + return value - String. Absolte path of file to write to. None if + no suitable file exists. + """ + file_path =3D os.path.join(abs_user_config, file_name) + if os.path.exists(file_path): + if os.path.isfile(file_path): + return file_path + elif os.path.isdir(file_path): + try: + files =3D sorted(f for f in os.listdir(file_path) \ + if os.path.isfile(os.path.join(file_path, f))) + if len(files) !=3D 0: + return os.path.join(file_path, files[-1]) + except OSError: + pass + + + write_to_file =3D autounmask_write and not pretend + #Make sure we have a file to write to before doing any write. + file_to_write_to =3D {} + problems =3D [] + if write_to_file: + for root in roots: + abs_user_config =3D os.path.join(root, USER_CONFIG_PATH) + + if root in unstable_keyword_msg: + file_to_write_to[(abs_user_config, "package.keywords")] =3D \ + find_config_file(abs_user_config, "package.keywords") =20 - if license_msg: - writemsg_stdout("\nThe following " + colorize("BAD", "license changes= ") + \ + if root in use_changes_msg: + file_to_write_to[(abs_user_config, "package.use")] =3D \ + find_config_file(abs_user_config, "package.use") + + if root in license_msg: + file_to_write_to[(abs_user_config, "package.license")] =3D \ + find_config_file(abs_user_config, "package.license") + + for (abs_user_config, f), path in file_to_write_to.items(): + if path is None: + problems.append("!!! No file to write for '%s'\n" % os.path.join(ab= s_user_config, f)) + + write_to_file =3D not problems + + + protect_obj =3D {} + if write_to_file: + for root in roots: + settings =3D self._frozen_config.pkgsettings[root] + protect_obj[root] =3D ConfigProtect(root, \ + shlex_split(settings.get("CONFIG_PROTECT", "")), + shlex_split(settings.get("CONFIG_PROTECT_MASK", ""))) + + def write_changes(root, change_type, changes, file_to_write_to): + writemsg_stdout("\nThe following " + colorize("BAD", "%s changes" % c= hange_type) + \ " are necessary to proceed:\n", noiselevel=3D-1) - writemsg_stdout("".join(license_msg), noiselevel=3D-1) + writemsg_stdout("".join(changes), noiselevel=3D-1) + if write_to_file: + try: + file_contents =3D codecs.open( + _unicode_encode(file_to_write_to, + encoding=3D_encodings['fs'], errors=3D'strict'), + mode=3D'r', encoding=3D_encodings['content'], + errors=3D'replace').readlines() + except IOError as e: + problems.append("!!! Failed to read '%s': %s\n" % (file_to_write_to= , e)) + else: + file_contents.extend(changes) + if protect_obj[root].isprotected(file_to_write_to): + file_to_write_to =3D new_protect_filename(file_to_write_to) + try: + write_atomic(file_to_write_to, "".join(file_contents)) + except PortageException: + problems.append("!!! Failed to write '%s'\n" % file_to_write_to) + + for root in roots: + abs_user_config =3D os.path.join(root, USER_CONFIG_PATH) + if len(roots) > 1: + writemsg_stdout("\nFor %s:\n" % abs_user_config, noiselevel=3D-1) + + if root in unstable_keyword_msg: + write_changes(root, "keyword", unstable_keyword_msg[root], + file_to_write_to.get((abs_user_config, "package.keywords"))) + + if root in use_changes_msg: + write_changes(root, "USE", use_changes_msg[root], + file_to_write_to.get((abs_user_config, "package.use"))) + + if root in license_msg: + write_changes(root, "license", license_msg[root], + file_to_write_to.get((abs_user_config, "package.license"))) + + if problems: + writemsg_stdout("\nThe following problems occured while writing autou= nmask changes:\n", \ + noiselevel=3D-1) + writemsg_stdout("".join(problems), noiselevel=3D-1) + elif write_to_file and roots: + writemsg_stdout("\nAutounmask changes successfully written. Remeber t= o run etc-update.\n", \ + noiselevel=3D-1) + elif not pretend and not autounmask_write and roots: + writemsg_stdout("\nUse --autounmask-write to write changes to config = files (honoring CONFIG_PROTECT).\n", \ + noiselevel=3D-1) + + + def display_problems(self): + """ + Display problems with the dependency graph such as slot collisions. + This is called internally by display() to show the problems _after_ + the merge list where it is most likely to be seen, but if display() + is not going to be called then this method should be called explicitly + to ensure that the user is notified of problems with the graph. + + All output goes to stderr, except for unsatisfied dependencies which + go to stdout for parsing by programs such as autounmask. + """ + + # Note that show_masked_packages() sends it's output to + # stdout, and some programs such as autounmask parse the + # output in cases when emerge bails out. However, when + # show_masked_packages() is called for installed packages + # here, the message is a warning that is more appropriate + # to send to stderr, so temporarily redirect stdout to + # stderr. TODO: Fix output code so there's a cleaner way + # to redirect everything to stderr. + sys.stdout.flush() + sys.stderr.flush() + stdout =3D sys.stdout + try: + sys.stdout =3D sys.stderr + self._display_problems() + finally: + sys.stdout =3D stdout + sys.stdout.flush() + sys.stderr.flush() + + # This goes to stdout for parsing by programs like autounmask. + for pargs, kwargs in self._dynamic_config._unsatisfied_deps_for_displa= y: + self._show_unsatisfied_dep(*pargs, **kwargs) + + def _display_problems(self): + if self._dynamic_config._circular_deps_for_display is not None: + self._show_circular_deps( + self._dynamic_config._circular_deps_for_display) + + # The user is only notified of a slot conflict if + # there are no unresolvable blocker conflicts. + if self._dynamic_config._unsatisfied_blockers_for_display is not None: + self._show_unsatisfied_blockers( + self._dynamic_config._unsatisfied_blockers_for_display) + elif self._dynamic_config._slot_collision_info: + self._show_slot_collision_notice() + else: + self._show_missed_update() + + self._display_autounmask() =20 # TODO: Add generic support for "set problem" handlers so that # the below warnings aren't special cases for world only. diff --git a/pym/_emerge/help.py b/pym/_emerge/help.py index 46b29ec..ddaa626 100644 --- a/pym/_emerge/help.py +++ b/pym/_emerge/help.py @@ -320,6 +320,13 @@ def help(myopts, havecolor=3D1): for line in wrap(desc, desc_width): print(desc_indent + line) print() + print(" " + green("--autounmask-write") + " [ %s | %s ]" % \ + (turquoise("y"), turquoise("n"))) + desc =3D "If --autounmask is enabled, changes are written " + \ + "to config files, respecting CONFIG_PROTECT." + for line in wrap(desc, desc_width): + print(desc_indent + line) + print() print(" " + green("--backtrack") + " " + turquoise("COUNT")) desc =3D "Specifies an integer number of times to backtrack if " + \ "dependency calculation fails due to a conflict or an " + \ diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py index ee0fc4e..7921d7d 100644 --- a/pym/_emerge/main.py +++ b/pym/_emerge/main.py @@ -427,6 +427,7 @@ def insert_optional_args(args): default_arg_opts =3D { '--ask' : y_or_n, '--autounmask' : y_or_n, + '--autounmask-write' : y_or_n, '--buildpkg' : y_or_n, '--complete-graph' : y_or_n, '--deep' : valid_integers, @@ -598,6 +599,12 @@ def parse_opts(tmpcmdline, silent=3DFalse): "choices" : true_y_or_n }, =20 + "--autounmask-write": { + "help" : "write changes made by --autounmask to disk", + "type" : "choice", + "choices" : true_y_or_n + }, + "--accept-properties": { "help":"temporarily override ACCEPT_PROPERTIES", "action":"store" @@ -916,6 +923,9 @@ def parse_opts(tmpcmdline, silent=3DFalse): if myoptions.autounmask in true_y: myoptions.autounmask =3D True =20 + if myoptions.autounmask_write in true_y: + myoptions.autounmask_write =3D True + if myoptions.buildpkg in true_y: myoptions.buildpkg =3D True else: