From dde6df65ecbc522dc23cb8d24be3e4dd52657da8 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 13 Jul 2012 16:33:49 -0700 Subject: [PATCH] slotmove: fix handling for EAPI 4-slot-abi This is just a really minimal fix, in order to prevent slotmove from behaving incorrectly with packages that use EAPI 4-slot-abi. Any slotmove commands that try so specify a sub-slot are treated as invalid for now, since that will required additional EAPI conditional logic, as reported in bug #426476. --- pym/portage/_global_updates.py | 14 +- pym/portage/dbapi/__init__.py | 28 +++- .../tests/emerge/test_global_updates.py | 6 +- pym/portage/tests/update/__init__.py | 2 + pym/portage/tests/update/__test__ | 0 .../tests/update/test_move_slot_ent.py | 149 ++++++++++++++++++ pym/portage/update.py | 23 ++- 7 files changed, 206 insertions(+), 16 deletions(-) create mode 100644 pym/portage/tests/update/__init__.py create mode 100644 pym/portage/tests/update/__test__ create mode 100644 pym/portage/tests/update/test_move_slot_ent.py diff --git a/pym/portage/_global_updates.py b/pym/portage/_global_updates.py index 51750431d..c0f3df0b6 100644 --- a/pym/portage/_global_updates.py +++ b/pym/portage/_global_updates.py @@ -1,4 +1,4 @@ -# Copyright 2010 Gentoo Foundation +# Copyright 2010-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import print_function @@ -32,11 +32,15 @@ def _global_updates(trees, prev_mtimes, quiet=False, if_mtime_changed=True): """ # only do this if we're root and not running repoman/ebuild digest - retupd = False if secpass < 2 or \ "SANDBOX_ACTIVE" in os.environ or \ len(trees) != 1: - return retupd + return False + + return _do_global_updates(trees, prev_mtimes, + quiet=quiet, if_mtime_changed=if_mtime_changed) + +def _do_global_updates(trees, prev_mtimes, quiet=False, if_mtime_changed=True): root = trees._running_eroot mysettings = trees[root]["vartree"].settings portdb = trees[root]["porttree"].dbapi @@ -61,6 +65,7 @@ def _global_updates(trees, prev_mtimes, quiet=False, if_mtime_changed=True): repo_map = {} timestamps = {} + retupd = False update_notice_printed = False for repo_name in portdb.getRepositories(): repo = portdb.getRepositoryPath(repo_name) @@ -237,8 +242,7 @@ def _global_updates(trees, prev_mtimes, quiet=False, if_mtime_changed=True): # Update progress above is indicated by characters written to stdout so # we print a couple new lines here to separate the progress output from # what follows. - print() - print() + writemsg_stdout("\n\n") if do_upgrade_packagesmessage and bindb and \ bindb.cpv_all(): diff --git a/pym/portage/dbapi/__init__.py b/pym/portage/dbapi/__init__.py index 34b1d4705..ac8d31169 100644 --- a/pym/portage/dbapi/__init__.py +++ b/pym/portage/dbapi/__init__.py @@ -8,7 +8,7 @@ import re import portage portage.proxy.lazyimport.lazyimport(globals(), 'portage.dbapi.dep_expand:dep_expand@_dep_expand', - 'portage.dep:match_from_list,_match_slot', + 'portage.dep:Atom,match_from_list,_match_slot', 'portage.output:colorize', 'portage.util:cmp_sort_key,writemsg', 'portage.versions:catsplit,catpkgsplit,vercmp,_pkg_str', @@ -312,27 +312,39 @@ class dbapi(object): def move_slot_ent(self, mylist, repo_match=None): """This function takes a sequence: Args: - mylist: a sequence of (package, originalslot, newslot) + mylist: a sequence of (atom, originalslot, newslot) repo_match: callable that takes single repo_name argument and returns True if the update should be applied Returns: The number of slotmoves this function did """ - pkg = mylist[1] + atom = mylist[1] origslot = mylist[2] newslot = mylist[3] - origmatches = self.match(pkg) + + try: + atom.with_slot + except AttributeError: + atom = Atom(atom).with_slot(origslot) + else: + atom = atom.with_slot(origslot) + + origmatches = self.match(atom) moves = 0 if not origmatches: return moves for mycpv in origmatches: - slot = self.aux_get(mycpv, ["SLOT"])[0] - if slot != origslot: + try: + mycpv = self._pkg_str(mycpv, atom.repo) + except (KeyError, InvalidData): continue - if repo_match is not None \ - and not repo_match(self.aux_get(mycpv, ['repository'])[0]): + if repo_match is not None and not repo_match(mycpv.repo): continue moves += 1 + if "/" not in newslot and \ + mycpv.slot_abi and \ + mycpv.slot_abi not in (mycpv.slot, newslot): + newslot = "%s/%s" % (newslot, mycpv.slot_abi) mydata = {"SLOT": newslot+"\n"} self.aux_update(mycpv, mydata) return moves diff --git a/pym/portage/tests/emerge/test_global_updates.py b/pym/portage/tests/emerge/test_global_updates.py index 735562100..eb5431059 100644 --- a/pym/portage/tests/emerge/test_global_updates.py +++ b/pym/portage/tests/emerge/test_global_updates.py @@ -1,4 +1,4 @@ -# Copyright 2011 Gentoo Foundation +# Copyright 2011-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from portage.tests import TestCase @@ -15,6 +15,8 @@ slotmove invalid_atom 0 3 slotmove !=invalid/blocker-3* 0 3 slotmove =valid/atom-3* 0 3 invalid_extra_token slotmove =valid/atom-3* 0 3 +slotmove =valid/atom-3* 0 3/3.1 +slotmove =valid/atom-3* 0/0 3 move valid/atom1 valid/atom2 invalid_extra_token move valid/atom1 invalid_atom2 move invalid_atom1 valid/atom2 @@ -28,7 +30,7 @@ move valid/atom1 valid/atom2 ['slotmove', Atom('=valid/atom-3*'), '0', '3'], ['move', Atom('valid/atom1'), Atom('valid/atom2')], ], - 10, + 12, ), ) diff --git a/pym/portage/tests/update/__init__.py b/pym/portage/tests/update/__init__.py new file mode 100644 index 000000000..418ad862b --- /dev/null +++ b/pym/portage/tests/update/__init__.py @@ -0,0 +1,2 @@ +# Copyright 2012 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 diff --git a/pym/portage/tests/update/__test__ b/pym/portage/tests/update/__test__ new file mode 100644 index 000000000..e69de29bb diff --git a/pym/portage/tests/update/test_move_slot_ent.py b/pym/portage/tests/update/test_move_slot_ent.py new file mode 100644 index 000000000..64475bcd5 --- /dev/null +++ b/pym/portage/tests/update/test_move_slot_ent.py @@ -0,0 +1,149 @@ +# Copyright 2012 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +import textwrap + +import portage +from portage import os +from portage.tests import TestCase +from portage.tests.resolver.ResolverPlayground import ResolverPlayground +from portage.util import ensure_dirs +from portage._global_updates import _do_global_updates + +class MoveSlotEntTestCase(TestCase): + + def testMoveSlotEnt(self): + + ebuilds = { + + "dev-libs/A-2::dont_apply_updates" : { + "EAPI": "4-slot-abi", + "SLOT": "0/2.30", + }, + + "dev-libs/B-2::dont_apply_updates" : { + "SLOT": "0", + }, + + "dev-libs/C-2.1::dont_apply_updates" : { + "EAPI": "4-slot-abi", + "SLOT": "0/2.1", + }, + + } + + installed = { + + "dev-libs/A-1::test_repo" : { + "EAPI": "4-slot-abi", + "SLOT": "0/2.30", + }, + + "dev-libs/B-1::test_repo" : { + "SLOT": "0", + }, + + "dev-libs/C-1::test_repo" : { + "EAPI": "4-slot-abi", + "SLOT": "0/1", + }, + + } + + binpkgs = { + + "dev-libs/A-1::test_repo" : { + "EAPI": "4-slot-abi", + "SLOT": "0/2.30", + }, + + "dev-libs/A-2::dont_apply_updates" : { + "EAPI": "4-slot-abi", + "SLOT": "0/2.30", + }, + + "dev-libs/B-1::test_repo" : { + "SLOT": "0", + }, + + "dev-libs/B-2::dont_apply_updates" : { + "SLOT": "0", + }, + + "dev-libs/C-1::test_repo" : { + "EAPI": "4-slot-abi", + "SLOT": "0/1", + }, + + "dev-libs/C-2.1::dont_apply_updates" : { + "EAPI": "4-slot-abi", + "SLOT": "0/2.1", + }, + + } + + updates = textwrap.dedent(""" + slotmove dev-libs/A 0 2 + slotmove dev-libs/B 0 1 + slotmove dev-libs/C 0 1 + """) + + playground = ResolverPlayground(binpkgs=binpkgs, + ebuilds=ebuilds, installed=installed) + + settings = playground.settings + trees = playground.trees + eroot = settings["EROOT"] + portdir = settings["PORTDIR"] + portdb = trees[eroot]["porttree"].dbapi + vardb = trees[eroot]["vartree"].dbapi + bindb = trees[eroot]["bintree"].dbapi + + updates_dir = os.path.join(portdir, "profiles", "updates") + + try: + ensure_dirs(updates_dir) + with open(os.path.join(updates_dir, "1Q-2010"), 'w') as f: + f.write(updates) + + # Create an empty updates directory, so that this + # repo doesn't inherit updates from the main repo. + ensure_dirs(os.path.join( + portdb.getRepositoryPath("dont_apply_updates"), + "profiles", "updates")) + + global_noiselimit = portage.util.noiselimit + portage.util.noiselimit = -2 + try: + _do_global_updates(trees, {}) + finally: + portage.util.noiselimit = global_noiselimit + + # 0/2.30 -> 2/2.30 + self.assertEqual("2/2.30", + vardb.aux_get("dev-libs/A-1", ["SLOT"])[0]) + self.assertEqual("2/2.30", + bindb.aux_get("dev-libs/A-1", ["SLOT"])[0]) + + # 0 -> 1 + self.assertEqual("1", + vardb.aux_get("dev-libs/B-1", ["SLOT"])[0]) + self.assertEqual("1", + bindb.aux_get("dev-libs/B-1", ["SLOT"])[0]) + + # 0/1 -> 1 (equivalent to 1/1) + self.assertEqual("1", + vardb.aux_get("dev-libs/C-1", ["SLOT"])[0]) + self.assertEqual("1", + bindb.aux_get("dev-libs/C-1", ["SLOT"])[0]) + + # dont_apply_updates + self.assertEqual("0/2.30", + bindb.aux_get("dev-libs/A-2", ["SLOT"])[0]) + self.assertEqual("0", + bindb.aux_get("dev-libs/B-2", ["SLOT"])[0]) + self.assertEqual("0/2.1", + bindb.aux_get("dev-libs/C-2.1", ["SLOT"])[0]) + + finally: + playground.cleanup() diff --git a/pym/portage/update.py b/pym/portage/update.py index 34e466366..a5cb92eda 100644 --- a/pym/portage/update.py +++ b/pym/portage/update.py @@ -22,6 +22,8 @@ portage.proxy.lazyimport.lazyimport(globals(), ) from portage.const import USER_CONFIG_PATH +from portage.dep import _get_slot_re +from portage.eapi import _get_eapi_attrs from portage.exception import DirectoryNotFound, InvalidAtom, PortageException from portage.localization import _ @@ -30,7 +32,7 @@ if sys.hexversion >= 0x3000000: ignored_dbentries = ("CONTENTS", "environment.bz2") -def update_dbentry(update_cmd, mycontent): +def update_dbentry(update_cmd, mycontent, eapi=None): if update_cmd[0] == "move": old_value = str(update_cmd[1]) if old_value in mycontent: @@ -143,6 +145,8 @@ def grab_updates(updpath, prev_mtimes=None): def parse_updates(mycontent): """Valid updates are returned as a list of split update commands.""" + eapi_attrs = _get_eapi_attrs(None) + slot_re = _get_slot_re(eapi_attrs) myupd = [] errors = [] mylines = mycontent.splitlines() @@ -194,6 +198,23 @@ def parse_updates(mycontent): errors.append(_("ERROR: Malformed update entry '%s'") % myline) continue + invalid_slot = False + for slot in (origslot, newslot): + m = slot_re.match(slot) + if m is None: + invalid_slot = True + break + if "/" in slot: + # Don't support EAPI 4-slot-abi style SLOT in slotmove + # yet, since the relevant code isn't aware of EAPI yet + # (see bug #426476). + invalid_slot = True + break + + if invalid_slot: + errors.append(_("ERROR: Malformed update entry '%s'") % myline) + continue + # The list of valid updates is filtered by continue statements above. myupd.append(mysplit) return myupd, errors -- 2.26.2