diff --git a/client/doc/mansrc/equo-mark.1.txt b/client/doc/mansrc/equo-mark.1.txt new file mode 100644 index 000000000..4fc2b0815 --- /dev/null +++ b/client/doc/mansrc/equo-mark.1.txt @@ -0,0 +1,54 @@ +equo-mark(1) +============ +:man source: equo {equoversion} +:man manual: equo {equoversion} + + +NAME +---- +equo-mark - set properties on installed packages + + +SYNOPSIS +-------- +equo mark [-h] {auto,manual} ... + + +INTRODUCTION +------------ +Set properties on installed packages + + + +OPTIONS +------- +"equo mark" supports the following options which alters its behaviour. + + +OPTIONAL ARGUMENTS +~~~~~~~~~~~~~~~~~~ +*--help*:: + show this help message and exit + +ACTION +~~~~~~ +*auto*:: + mark package as installed to satisfy a dependency + +*manual*:: + mark package as installed by user + + + +AUTHORS +------- +Fabio Erculiani (lxnay@sabayon.org) + +REPORTING BUGS +-------------- +Report bugs to https://bugs.sabayon.org or directly to the author at +lxnay@sabayon.org. + +SEE ALSO +-------- + equo(1) diff --git a/client/doc/mansrc/equo.1.txt b/client/doc/mansrc/equo.1.txt index c1e832128..64b9fa09c 100644 --- a/client/doc/mansrc/equo.1.txt +++ b/client/doc/mansrc/equo.1.txt @@ -68,6 +68,9 @@ COMMANDS *lxnay*:: bow to lxnay +*mark*:: + set properties on installed packages + *mask*:: mask one or more packages @@ -117,7 +120,7 @@ COMMANDS unmask one or more packages *unusedpackages [unused]*:: - look for unused packages (pay attention) + show unused packages (pay attention) *update [up]*:: update repositories @@ -145,9 +148,9 @@ lxnay@sabayon.org. SEE ALSO -------- equo-cache(1), equo-cleanup(1), equo-conf(1), equo-config(1), equo-deptest(1) - equo-download(1), equo-hop(1), equo-install(1), equo-libtest(1), equo-mask(1) - equo-match(1), equo-notice(1), equo-pkg(1), equo-preservedlibs(1), equo-query(1) - equo-remove(1), equo-repo(1), equo-rescue(1), equo-search(1), equo-security(1) - equo-source(1), equo-status(1), equo-ugc(1), equo-unmask(1), equo-unusedpackages(1) - equo-update(1), equo-upgrade(1) + equo-download(1), equo-hop(1), equo-install(1), equo-libtest(1), equo-mark(1) + equo-mask(1), equo-match(1), equo-notice(1), equo-pkg(1), equo-preservedlibs(1) + equo-query(1), equo-remove(1), equo-repo(1), equo-rescue(1), equo-search(1) + equo-security(1), equo-source(1), equo-status(1), equo-ugc(1), equo-unmask(1) + equo-unusedpackages(1), equo-update(1), equo-upgrade(1) diff --git a/client/solo/commands/mark.py b/client/solo/commands/mark.py new file mode 100644 index 000000000..7521308e7 --- /dev/null +++ b/client/solo/commands/mark.py @@ -0,0 +1,241 @@ +# -*- coding: utf-8 -*- +""" + + @author: Slawomir Nizio + @contact: lxnay@sabayon.org + @copyright: Slawomir Nizio + @license: GPL-2 + + B{Entropy Command Line Client}. + +""" +import sys +import argparse + +from entropy.i18n import _ +from entropy.const import etpConst, const_convert_to_unicode +from entropy.output import darkred, red, blue, brown, teal, purple + +from solo.commands.descriptor import SoloCommandDescriptor +from solo.commands.command import SoloCommand, exclusivelock + + +class SoloMark(SoloCommand): + """ + Main Solo Mark command. + """ + + NAME = "mark" + ALIASES = [] + ALLOW_UNPRIVILEGED = False + + INTRODUCTION = """\ +Set properties on installed packages +""" + SEE_ALSO = "" + + def __init__(self, args): + SoloCommand.__init__(self, args) + self._commands = {} + + def man(self): + """ + Overridden from SoloCommand. + """ + return self._man() + + def _get_parser(self): + """ + Overridden from SoloCommand. + """ + _commands = {} + descriptor = SoloCommandDescriptor.obtain_descriptor( + SoloMark.NAME) + + parser = argparse.ArgumentParser( + description=descriptor.get_description(), + formatter_class=argparse.RawDescriptionHelpFormatter, + prog="%s %s" % (sys.argv[0], SoloMark.NAME)) + + subparsers = parser.add_subparsers( + title="action", + description=_("specify property to mark on a package"), + help=_("available commands")) + + def _add_standard_arguments(p, _cmd_dict): + p.add_argument( + "packages", nargs='+', + metavar="", help=_("package name")) + + p.add_argument( + "--pretend", "-p", action="store_true", + default=False, + help=_("show what would be done")) + _cmd_dict["--pretend"] = {} + _cmd_dict["-p"] = {} + + p.add_argument( + "--ignore-missing", action="store_true", + default=False, + help=_("ignore packages that are not installed on system")) + _cmd_dict["--ignore-missing"] = {} + + # It behaves differently than --multimatch from match.py, + # thus a different name. + p.add_argument( + "--multiple-versions", action="store_true", + default=False, + help=_("allow matching multiple versions of a package")) + _cmd_dict["--multiple-versions"] = {} + + auto_parser = subparsers.add_parser( + "auto", + help=_("mark package as installed to satisfy a dependency")) + _cmd_dict = {} + _add_standard_arguments(auto_parser, _cmd_dict) + auto_parser.set_defaults(func=self._auto) + _commands["auto"] = _cmd_dict + + manual_parser = subparsers.add_parser( + "manual", + help=_("mark package as installed by user")) + _cmd_dict = {} + _add_standard_arguments(manual_parser, _cmd_dict) + manual_parser.set_defaults(func=self._manual) + _commands["manual"] = _cmd_dict + + self._commands = _commands + return parser + + def parse(self): + """ + Parse command + """ + parser = self._get_parser() + try: + nsargs = parser.parse_args(self._args) + except IOError as err: + sys.stderr.write("%s\n" % (err,)) + return parser.print_help, [] + + # Python 3.3 bug #16308 + if not hasattr(nsargs, "func"): + return parser.print_help, [] + + self._nsargs = nsargs + return self._call_exclusive, [nsargs.func] + + def bashcomp(self, last_arg): + """ + Overridden from SoloCommand. + """ + self._get_parser() # this will generate self._commands + return self._hierarchical_bashcomp(last_arg, [], self._commands) + + @exclusivelock + def _auto(self, entropy_client, inst_repo): + source_key = "automatic_dependency" + return self._apply_source( + entropy_client, inst_repo, source_key, **self._get_opts()) + + @exclusivelock + def _manual(self, entropy_client, inst_repo): + source_key = "user" + return self._apply_source( + entropy_client, inst_repo, source_key, **self._get_opts()) + + def _get_opts(self): + opts = { + 'packages': self._nsargs.packages, + 'pretend': self._nsargs.pretend, + 'ignore_missing': self._nsargs.ignore_missing, + 'multiple_versions': self._nsargs.multiple_versions + } + return opts + + def _apply_source(self, entropy_client, inst_repo, + source_key, packages, pretend, + ignore_missing, multiple_versions): + + source = etpConst['install_sources'][source_key] + + reverse_install_sources = { + 0: _("unknown"), + 1: _("manual"), + 2: _("dependency") + } + other_source = _("other") + + source_txt = reverse_install_sources.get(source, other_source) + + packages = entropy_client.packages_expand(packages) + package_ids = {} + + allfound = True + for package in packages: + if package in package_ids: + continue + pkg_ids, _rc = inst_repo.atomMatch( + package, multiMatch=multiple_versions) + + if not multiple_versions: + pkg_ids = set([pkg_ids]) + + if _rc == 0: + package_ids[package] = pkg_ids + else: + allfound = False + entropy_client.output( + "!!! %s: %s %s." % ( + purple(_("Warning")), + teal(const_convert_to_unicode(package)), + purple(_("is not installed")), + )) + + if not allfound: + entropy_client.output( + darkred(_("Some packages were not found")), + level="info" if ignore_missing else "error", + importance=1) + if not ignore_missing: + return 1 + + for package in packages: + if package not in package_ids: + # Package was not found. + continue + + for pkg_id in package_ids[package]: + + pkg_atom = inst_repo.retrieveAtom(pkg_id) + current_source = inst_repo.getInstalledPackageSource(pkg_id) + current_source_txt = reverse_install_sources.get( + current_source, other_source) + + if current_source == source: + txt = "%s: %s" % ( + brown(pkg_atom), + _("no change")) + entropy_client.output( + txt, + header=blue(" @@ ")) + else: + txt = "%s: %s => %s" % ( + brown(pkg_atom), + current_source_txt, + source_txt) + entropy_client.output( + txt, + header=red(" !! ")) + + if not pretend: + inst_repo.setInstalledPackageSource(pkg_id, source) + + return 0 + +SoloCommandDescriptor.register( + SoloCommandDescriptor( + SoloMark, + SoloMark.NAME, + _("set properties on installed packages")) + ) diff --git a/lib/entropy/db/skel.py b/lib/entropy/db/skel.py index bab8a3de5..c3919f84f 100644 --- a/lib/entropy/db/skel.py +++ b/lib/entropy/db/skel.py @@ -4772,6 +4772,18 @@ class EntropyRepositoryBase(TextInterface, EntropyRepositoryPluginStore): """ raise NotImplementedError() + def setInstalledPackageSource(self, package_id, source): + """ + Set package source. Please refer to I{getInstalledPackageSource()} for + more details about this field. + + @param package_id: package indentifier + @type package_id: int + @param source: install source identified + @type slot: int + """ + raise NotImplementedError() + def dropInstalledPackageFromStore(self, package_id): """ Note: this is used by installed packages repository (also known as diff --git a/lib/entropy/db/sql.py b/lib/entropy/db/sql.py index ad434f7af..3f6e1809b 100644 --- a/lib/entropy/db/sql.py +++ b/lib/entropy/db/sql.py @@ -5059,6 +5059,14 @@ class EntropySQLRepository(EntropyRepositoryBase): return source[0] return None + def setInstalledPackageSource(self, package_id, source): + """ + Reimplemented from EntropyRepositoryBase. + """ + self._cursor().execute(""" + UPDATE installedtable SET source = ? WHERE idpackage = ? + """, (source, package_id,)) + def dropInstalledPackageFromStore(self, package_id): """ Reimplemented from EntropyRepositoryBase. diff --git a/lib/entropy/db/sqlite.py b/lib/entropy/db/sqlite.py index be527ad59..e2faf4930 100644 --- a/lib/entropy/db/sqlite.py +++ b/lib/entropy/db/sqlite.py @@ -2329,6 +2329,15 @@ class EntropySQLiteRepository(EntropySQLRepository): del cached return obj + def setInstalledPackageSource(self, package_id, source): + """ + Reimplemented from EntropySQLRepository. + We must handle live cache. + """ + super(EntropySQLiteRepository, self).setInstalledPackageSource( + package_id, source) + self._clearLiveCache("getInstalledPackageSource") + def dropInstalledPackageFromStore(self, package_id): """ Reimplemented from EntropySQLRepository.