diff --git a/client/solo/commands/rescue.py b/client/solo/commands/rescue.py index ba1f8b8c1..1f8aefa1f 100644 --- a/client/solo/commands/rescue.py +++ b/client/solo/commands/rescue.py @@ -730,6 +730,25 @@ Tools to rescue the running system. prefix="equo.rescue.spmsync") os.close(tmp_fd) + try: + spm_wanted_packages = spm.get_user_selected_packages() + except Exception as err: + entropy.tools.print_traceback() + entropy_client.output( + "%s: %s" % ( + darkred(_("Cannot read list of user selected packages")), + err, + ), + level="warning" + ) + spm_wanted_packages = frozenset() + + spm_wanted_map = {} + for _spm_wanted_pkg in spm_wanted_packages: + _stripped = entropy.dep.dep_getkey(_spm_wanted_pkg) + obj = spm_wanted_map.setdefault(_stripped, set()) + obj.add(_spm_wanted_pkg) + for _spm_package, _spm_package_id in to_be_added: counter += 1 entropy_client.output( @@ -792,8 +811,28 @@ Tools to rescue the running system. new_package_id = inst_repo.handlePackage( data, revision = data['revision']) - inst_repo.storeInstalledPackage(new_package_id, - etpConst['spmdbid']) + + def _spm_match_installed(x): + try: + return spm.match_installed_package(x) + except KeyError: + pass + + spm_package_key = entropy.dep.dep_getkey(_spm_package) + spm_wanted_candidates = spm_wanted_map.get(spm_package_key, set()) + + # Avoid calling spm.match_installed_package() which can be + # a little slow. Now the list is reduced. + spm_wanted_matches = (_spm_match_installed(x) + for x in spm_wanted_candidates + if x is not None) + if _spm_package in spm_wanted_matches: + source = etpConst['install_sources']['user'] + else: + source = etpConst['install_sources']['automatic_dependency'] + + inst_repo.storeInstalledPackage( + new_package_id, etpConst['spmdbid'], source) inst_repo.commit() try: diff --git a/lib/entropy/spm/plugins/interfaces/portage_plugin/__init__.py b/lib/entropy/spm/plugins/interfaces/portage_plugin/__init__.py index ed0ced207..922f7154d 100644 --- a/lib/entropy/spm/plugins/interfaces/portage_plugin/__init__.py +++ b/lib/entropy/spm/plugins/interfaces/portage_plugin/__init__.py @@ -1937,6 +1937,29 @@ class PortagePlugin(SpmPlugin): return list(filter(catfilter, packages)) + def get_user_selected_packages(self, root = None): + """ + Reimplemented from SpmPlugin class. + """ + world_atoms = set() + + with self._PortageWorldSetLocker(self, root = root): + world_file = self.get_user_installed_packages_file(root = root) + enc = etpConst['conf_encoding'] + + try: + with codecs.open(world_file, "r", encoding=enc) \ + as world_f: + world_atoms |= set((x.strip() for x in \ + world_f.readlines() if x.strip())) + except (OSError, IOError) as err: + if err.errno != errno.ENOENT: + raise self.Error(err) + except UnicodeDecodeError as err: + raise self.Error("Portage world file is malformed") + + return frozenset(world_atoms) + def get_package_sets(self, builtin_sets): """ Reimplemented from SpmPlugin class. diff --git a/lib/entropy/spm/plugins/skel.py b/lib/entropy/spm/plugins/skel.py index 2f49b6af9..5b9d17dfc 100644 --- a/lib/entropy/spm/plugins/skel.py +++ b/lib/entropy/spm/plugins/skel.py @@ -589,6 +589,22 @@ class SpmPlugin(Singleton): """ raise NotImplementedError() + def get_user_selected_packages(self, root = None): + """ + Return installed list of packages that were selected by user as + selected (wanted). + Packages returned by this function can have a different representation + from what e.g. I{get_installed_packages()} returns, that is, no + package matching is done for performance. + + @keyword root: specify an alternative root directory "/" + @type root: string + @return: list of installed packages found + @rtype: frozenset + @raise entropy.exceptions.SPMError: when something went bad + """ + raise NotImplementedError() + def get_package_sets(self, builtin_sets): """ Package sets are groups of packages meant to ease user installation and