switched install/remove to SolverRequester
authorJán Kupec <jkupec@suse.cz>
Wed, 31 Mar 2010 11:26:27 +0000 (13:26 +0200)
committerJán Kupec <jkupec@suse.cz>
Wed, 31 Mar 2010 11:26:27 +0000 (13:26 +0200)
src/CMakeLists.txt
src/SolverRequester.cc [new file with mode: 0644]
src/SolverRequester.h [new file with mode: 0644]
src/Zypper.cc
src/install.cc [deleted file]
src/misc.cc
src/misc.h

index 05de037..c8d280b 100644 (file)
@@ -18,10 +18,10 @@ SET (zypper_HEADERS
   info.h
   Table.h
   locks.h
-  install.h
   update.h
   solve-commit.h
   PackageArgs.h
+  SolverRequester.h
   Summary.h
   callbacks/keyring.h
   callbacks/media.h
@@ -39,10 +39,10 @@ SET( zypper_SRCS
   info.cc
   Table.cc
   locks.cc
-  install.cc
   update.cc
   solve-commit.cc
   PackageArgs.cc
+  SolverRequester.cc
   Summary.cc
   callbacks/media.cc
   ${zypper_HEADERS}
diff --git a/src/SolverRequester.cc b/src/SolverRequester.cc
new file mode 100644 (file)
index 0000000..7d96647
--- /dev/null
@@ -0,0 +1,260 @@
+/*---------------------------------------------------------------------------*\
+                          ____  _ _ __ _ __  ___ _ _
+                         |_ / || | '_ \ '_ \/ -_) '_|
+                         /__|\_, | .__/ .__/\___|_|
+                             |__/|_|  |_|
+\*---------------------------------------------------------------------------*/
+
+
+/** \file SolverRequester.cc
+ * 
+ */
+
+#include "zypp/ZYppFactory.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/Capability.h"
+#include "zypp/PoolQuery.h"
+#include "zypp/PoolItemBest.h"
+#include "zypp/ui/Selectable.h"
+#include "zypp/Resolver.h"
+
+#include "Zypper.h" // temporarily!!!
+#include "misc.h"
+
+#include "SolverRequester.h"
+
+using namespace std;
+using namespace zypp;
+using namespace zypp::ui;
+
+void SolverRequester::install(const PackageArgs & args)
+{ install_remove(args, true); }
+
+void SolverRequester::remove(const PackageArgs & args)
+{ install_remove(args, true); }
+
+void SolverRequester::install_remove(const PackageArgs & args, bool doinst)
+{
+  if (args.empty())
+    return;
+
+  for_(it, args.doCaps().begin(), args.doCaps().end())
+    install(it->first, it->second);
+
+  for_(it, args.dontCaps().begin(), args.dontCaps().end())
+    remove(it->first);
+}
+
+// ----------------------------------------------------------------------------
+
+/**
+ * For given Capability & repo & Options:
+ *
+ * 1) if the --capability was not specified, try to install 'by name' first.
+ *    I.e. via ui::Selectable and/or PoolItem.
+ *    note: wildcards must be supported here, use PoolQuery with match_glob
+ *          to find the selectables
+ *
+ * 2) if no package could be found by name and --name was not specified,
+ *    or --capability was specified, install 'by capability',
+ *    i.e. using ResPool::addRequires(cap)
+ *
+ * NOTES
+ * - If the argument contains repository, issue request forcing the repo
+ * - In both cases check for already installed packages, and if found, hand over
+ *   to the \ref update(const Capability&, const string&) method.
+ *
+ * TODO
+ * - maybe a check for glob wildcards in cap name would make sense before trying
+ *   by-cap
+ */
+void SolverRequester::install(const Capability & cap, const string & repoalias)
+{
+#warning get rid of zypper here
+  Zypper & zypper = *Zypper::instance();
+
+  sat::Solvable::SplitIdent splid(cap.detail().name());
+  ResKind capkind = splid.kind();
+  string capname = splid.name().asString();
+
+  // first try by name
+
+  if (!_opts.force_by_cap)
+  {
+    PoolQuery q = pkg_spec_to_poolquery(cap, _opts.from_repos);
+    if (!repoalias.empty())
+      q.addRepo(repoalias);
+
+    // get the best matching items and tag them for installation.
+    PoolItemBest bestMatches(q.begin(), q.end());
+    if (!bestMatches.empty())
+    {
+      for_(sit, bestMatches.begin(), bestMatches.end())
+      {
+        if (asSelectable()(*sit)->hasInstalledObj())
+          update(cap, repoalias);
+        else
+          asSelectable()(*sit)->setOnSystem(*sit, ResStatus::USER);
+        // TODO handle patches (isBroken), patterns (isSatisfied instead of hasInstalledObj)
+      }
+    }
+    else if (_opts.force_by_name)
+    {
+      //! \todo report this via error class
+      // translators: meaning a package %s or provider of capability %s
+      zypper.out().error(str::form(_("'%s' not found."), capname.c_str()));
+      WAR << str::form("'%s' not found", capname.c_str()) << endl;
+      zypper.setExitCode(ZYPPER_EXIT_INF_CAP_NOT_FOUND);
+      if (zypper.globalOpts().non_interactive)
+        ZYPP_THROW(ExitRequestException());
+    }
+  }
+
+  if (_opts.force_by_name)
+    return;
+
+  // try by capability
+  // TODO tell that we're falling back to search by capability
+
+  // is there a provider for the requested capability?
+  sat::WhatProvides q(cap);
+  if (q.empty())
+  {
+    // translators: meaning a package %s or provider of capability %s
+    zypper.out().error(str::form(_("'%s' not found."), cap.asString().c_str()));
+    WAR << str::form("'%s' not found", cap.asString().c_str()) << endl;
+    zypper.setExitCode(ZYPPER_EXIT_INF_CAP_NOT_FOUND);
+    if (zypper.globalOpts().non_interactive)
+      ZYPP_THROW(ExitRequestException());
+    return;
+  }
+
+  // is the provider already installed?
+  set<PoolItem> providers = get_installed_providers(cap);
+  // already installed, try to update()
+  for_(it, providers.begin(), providers.end())
+  {
+    if (it->resolvable()->name() == capname)
+      zypper.out().info(
+          str::form(_("'%s' is already installed."), capname.c_str()));
+    else
+      zypper.out().info(str::form(
+          // translators: %s are package names
+          _("'%s' providing '%s' is already installed."),
+          (*it)->name().c_str(), cap.asString().c_str()));
+
+    MIL << "provider '" << *it << "' of '" << cap << "' already installed;"
+        << " will try to update" << endl;
+
+    Capability pcap((*it)->name(), (*it)->kind());
+    update(pcap, repoalias);
+  }
+
+  if (providers.empty())
+  {
+    DBG << "adding requirement " << cap << endl;
+    Resolver_Ptr resolver = zypp::getZYpp()->resolver();
+    resolver->addRequire(cap);
+  }
+}
+
+// ----------------------------------------------------------------------------
+
+/**
+ * Remove packages based on given Capability & Options from the system.
+ */
+void SolverRequester::remove(const Capability & cap)
+{
+#warning get rid of zypper here
+  Zypper & zypper = *Zypper::instance();
+
+  sat::Solvable::SplitIdent splid(cap.detail().name());
+  ResKind capkind = splid.kind();
+  string capname = splid.name().asString();
+
+  // first try by name
+
+  if (!_opts.force_by_cap)
+  {
+    PoolQuery q = pkg_spec_to_poolquery(cap, "");
+    q.setInstalledOnly();
+
+    if (!q.empty())
+    {
+      for_(it, q.poolItemBegin(), q.poolItemEnd())
+      {
+        DBG << "Marking for deletion: " << *it << endl;
+        it->status().setToBeUninstalled(ResStatus::USER);
+      }
+      // TODO handle patches (cannot uninstall!), patterns (remove content(?))
+    }
+    else if (_opts.force_by_name)
+    {
+      //! \todo report this via error class
+      // translators: meaning a package %s or provider of capability %s
+      zypper.out().error(str::form(_("'%s' not found."), capname.c_str()));
+      WAR << str::form("'%s' not found", capname.c_str()) << endl;
+      zypper.setExitCode(ZYPPER_EXIT_INF_CAP_NOT_FOUND);
+      if (zypper.globalOpts().non_interactive)
+        ZYPP_THROW(ExitRequestException());
+    }
+  }
+
+  if (_opts.force_by_name)
+    return;
+
+  // try by capability
+  // TODO tell that we're falling back to search by capability
+
+  // is there a provider for the requested capability?
+  sat::WhatProvides q(cap);
+  if (q.empty())
+  {
+    // translators: meaning a package %s or provider of capability %s
+    zypper.out().error(str::form(_("'%s' not found."), cap.asString().c_str()));
+    WAR << str::form("'%s' not found", cap.asString().c_str()) << endl;
+    zypper.setExitCode(ZYPPER_EXIT_INF_CAP_NOT_FOUND);
+    if (zypper.globalOpts().non_interactive)
+      ZYPP_THROW(ExitRequestException());
+    return;
+  }
+
+  // is the provider already installed?
+  set<PoolItem> providers = get_installed_providers(cap);
+
+  // not installed, nothing to do
+  if (providers.empty())
+  {
+    // translators: meaning a package %s or provider of capability %s
+    zypper.out().info(str::form(_("No provider of '%s' is installed."), cap.asString().c_str()));
+    MIL << "no provider of " << cap << "is installed" << endl;
+  }
+  else
+  {
+    MIL << "adding conflict " << cap << endl;
+    Resolver_Ptr resolver = zypp::getZYpp()->resolver();
+    resolver->addConflict(cap);
+  }
+}
+
+// ----------------------------------------------------------------------------
+
+/**
+ * Asserting that at least one provider of given \a cap is already installed,
+ * this method checks for available update candidates and tries to select
+ * the best for installation (thus update). Reports any problems or interesting
+ * info back to user.
+ *
+ * NOTES
+ * - If the argument contains repository, issue request forcing the repo and
+ *   - if --force was specified, insist on installing from that repo (even
+ *     downgrade or change vendor or low priority) TODO
+ *   - if --force was NOT used, only upgrade without vendor change or priority
+ *     violation. If downgrade or vendor change is needed to get the highest
+ *     version, report back to user and advice to use --force,
+ *     if that's what is really wanted. TODO
+ */
+void SolverRequester::update(const Capability & cap, const string & repoalias)
+{
+  // TODO report 'newest already installed' (see mark_by_name)
+}
diff --git a/src/SolverRequester.h b/src/SolverRequester.h
new file mode 100644 (file)
index 0000000..d59a97d
--- /dev/null
@@ -0,0 +1,67 @@
+/*---------------------------------------------------------------------------*\
+                          ____  _ _ __ _ __  ___ _ _
+                         |_ / || | '_ \ '_ \/ -_) '_|
+                         /__|\_, | .__/ .__/\___|_|
+                             |__/|_|  |_|
+\*---------------------------------------------------------------------------*/
+
+
+/** \file SolverRequester.h
+ * 
+ */
+
+#ifndef SOLVERREQUESTER_H_
+#define SOLVERREQUESTER_H_
+
+#include "PackageArgs.h"
+#include "utils/misc.h" // for ResKindSet; might make sense to move this elsewhere
+
+/**
+ *
+ */
+class SolverRequester
+{
+public:
+  struct Options
+  {
+    Options()
+      : force(false)
+      , force_by_cap(false)
+      , force_by_name(false)
+    {}
+
+    bool force;
+    bool force_by_cap;
+    bool force_by_name;
+    /** aliases of the repos from which the packages should be installed */
+    std::list<std::string> from_repos;
+  };
+
+public:
+  SolverRequester(const Options & opts = Options())
+    : _opts(opts)
+  {}
+
+public:
+  /** Request installation of specified objects. */
+  void install(const PackageArgs & args);
+  /** Request removal of specified objects. */
+  void remove(const PackageArgs & args);
+  /** Request update of specified objects. */
+  void update(const PackageArgs & args);
+  /** Update all objects of given kinds, wherever possible. */
+  void update(const ResKindSet & kinds);
+
+private:
+  void install_remove(const PackageArgs & args, bool doinst);
+  void install(const zypp::Capability & cap, const std::string & repoalias);
+  void remove(const zypp::Capability & cap);
+  void update(const zypp::Capability & cap, const std::string & repoalias);
+
+  // TODO provide also public versions of these, taking optional Options and
+  // reporting errors via an output argument.
+
+  Options _opts;
+};
+
+#endif /* SOLVERREQUESTER_H_ */
index d1712c2..90269fd 100644 (file)
@@ -38,6 +38,7 @@
 #include "main.h"
 #include "Zypper.h"
 #include "Command.h"
+#include "SolverRequester.h"
 
 #include "Table.h"
 #include "utils/misc.h"
@@ -46,7 +47,6 @@
 #include "utils/misc.h"
 
 #include "repos.h"
-#include "install.h"
 #include "update.h"
 #include "solve-commit.h"
 #include "misc.h"
@@ -3230,6 +3230,28 @@ void Zypper::doCommand()
 
     bool install_not_remove = command() == ZypperCommand::INSTALL;
 
+    // can't remove patch
+    if (kind == ResKind::patch && !install_not_remove)
+    {
+      out().error(
+          _("Cannot uninstall patches."),
+          _("Installed status of a patch is determined solely based on its dependencies.\n"
+            "Patches are not installed in sense of copied files, database records,\n"
+            "or similar."));
+      setExitCode(ZYPPER_EXIT_ERR_INVALID_ARGS);
+      throw ExitRequestException("not implemented");
+    }
+
+    // can't remove pattern (for now)
+    if (kind == ResKind::pattern && !install_not_remove)
+    {
+      //! \todo define and implement pattern removal (bnc #407040)
+      out().error(
+          _("Uninstallation of a pattern is currently not defined and implemented."));
+      setExitCode(ZYPPER_EXIT_ERR_INVALID_ARGS);
+      throw ExitRequestException("not implemented");
+    }
+
     // parse the download options to check for errors
     get_download_option(*this);
 
@@ -3355,9 +3377,50 @@ void Zypper::doCommand()
     // needed to compute status of PPP
     resolve(*this);
 
+    // parse package arguments
+    PackageArgs args(kind);
+
     // tell the solver what we want
-    install_remove(*this, _arguments, install_not_remove, kind);
-    install_remove(*this, rpms_files_caps, true, kind);
+
+    SolverRequester::Options sropts;
+    if (copts.find("force") != copts.end())
+      sropts.force = true;
+    sropts.force_by_cap  = copts.find("capability") != copts.end();
+    sropts.force_by_name = copts.find("name") != copts.end();
+    if (sropts.force)
+      sropts.force_by_name = true;
+
+    if (sropts.force_by_cap && sropts.force_by_name)
+    {
+      // translators: meaning --capability contradicts --force/--name
+      out().error(str::form(_("%s contradicts %s"),
+          "--capability", (sropts.force ? "--force" : "--name")));
+      setExitCode(ZYPPER_EXIT_ERR_INVALID_ARGS);
+      ZYPP_THROW(ExitRequestException());
+    }
+
+    if (install_not_remove && sropts.force_by_cap && sropts.force)
+    {
+      // translators: meaning --force with --capability
+      out().error(str::form(_("%s cannot currently be used with %s"),
+          "--force", "--capability"));
+      setExitCode(ZYPPER_EXIT_ERR_INVALID_ARGS);
+      ZYPP_THROW(ExitRequestException());
+    }
+
+    if (install_not_remove && (optit = copts.find("from")) != copts.end())
+      repo_specs_to_aliases(*this, optit->second, sropts.from_repos);
+
+    SolverRequester sr(sropts);
+    if (install_not_remove)
+      sr.install(args);
+    else
+      sr.remove(args);
+    PackageArgs rpm_args(rpms_files_caps);
+    sr.install(rpm_args);
+
+    // give user feedback from package selection
+    // TODO feedback goes here
 
     solve_and_commit(*this);
 
diff --git a/src/install.cc b/src/install.cc
deleted file mode 100755 (executable)
index cc60124..0000000
+++ /dev/null
@@ -1,688 +0,0 @@
-#include <boost/format.hpp>
-//#include <iostream>
-#include "zypp/ZYppFactory.h"
-#include "zypp/base/LogTools.h"
-#include "zypp/base/Algorithm.h"
-#include "zypp/base/Functional.h"
-#include "zypp/Filter.h"
-#include "zypp/PoolQuery.h"
-#include "zypp/PoolItemBest.h"
-
-#include "utils/misc.h"
-
-#include "install.h"
-#include "update.h"
-#include "repos.h"
-#include "PackageArgs.h"
-
-using namespace std;
-using namespace zypp;
-using namespace boost;
-
-extern ZYpp::Ptr God;
-
-
-static PoolItem findInstalledItemInRepos(const PoolItem & installed)
-{
-  const zypp::ResPool & pool(zypp::ResPool::instance());
-  PoolItem result;
-  invokeOnEach(
-    pool.byIdentBegin(installed), pool.byIdentEnd(installed),
-    functor::chain(
-      filter::SameItemAs(installed),
-      resfilter::ByUninstalled()),
-    functor::getFirst(result));
-  XXX << "findInstalledItemInRepos(" << installed << ") => " << result << endl;
-  return result;
-}
-
-// TODO edition - need solver API
-static void
-mark_for_install(Zypper & zypper,
-                      const ResObject::Kind &kind,
-                      const string &name,
-                      const string & repo = "",
-                      const string & arch = "",
-                      bool report_already_installed = true)
-{
-  // name and kind match:
-  DBG << "Iterating over [" << kind << "] " << name;
-  if (!repo.empty())
-    DBG << ", repo: " << repo;
-  if (!arch.empty())
-    DBG << ", arch: " << arch;
-  DBG << "...";
-
-  PoolQuery q;
-  q.addAttribute(sat::SolvAttr::name, name);
-  q.addKind(kind);
-  if (!repo.empty())
-    q.addRepo(repo);
-  if (!arch.empty())
-    q.addAttribute(sat::SolvAttr::arch, arch);
-  q.setCaseSensitive(false);
-  q.setMatchExact();
-
-//  if (q.empty())
-  if (q.begin() == q.end())
-  {
-    DBG << "... done" << endl;
-    zypper.out().error(
-      // translators: meaning a package %s or provider of capability %s
-      str::form(_("'%s' not found"), name.c_str()));
-    WAR << str::form("'%s' not found", name.c_str()) << endl;
-    zypper.setExitCode(ZYPPER_EXIT_INF_CAP_NOT_FOUND);
-    if (zypper.globalOpts().non_interactive)
-      ZYPP_THROW(ExitRequestException());
-    return;
-  }
-
-  ui::Selectable::Ptr s = *q.selectableBegin();
-  DBG << "... done" << endl;
-
-  bool force = copts.count("force");
-
-
-  PoolItem candidate = s->updateCandidateObj();
-  if (!candidate)
-    candidate = s->installedObj();
-
-  if (s->installedObj() &&
-      identical(s->installedObj(), candidate) &&
-      !force)
-  {
-    // if it is broken install anyway, even if it is installed
-    if (candidate.isBroken())
-      candidate.status().setToBeInstalled(zypp::ResStatus::USER);
-    else if (report_already_installed)
-      zypper.out().info(boost::str(format(
-        // translators: e.g. skipping package 'zypper' (the newest version already installed)
-        //! \todo capitalize the first letter
-        _("skipping %s '%s' (the newest version already installed)"))
-        % kind_to_string_localized(kind,1) % name));
-  }
-  else
-  {
-    if (force && !s->installedEmpty())
-    {
-      PoolItem repoitem = findInstalledItemInRepos(s->installedObj());
-      if (!repoitem)
-      {
-        zypper.out().info(str::form(
-            // translators: %s-%s.%s is name-version.arch
-            _("Package %s-%s.%s not found in repositories, cannot reinstall."),
-            s->name().c_str(), s->installedObj().resolvable()->edition().c_str(),
-            s->installedObj().resolvable()->arch().c_str()));
-        zypper.setExitCode(ZYPPER_EXIT_ERR_ZYPP);
-        return;
-      }
-      else if (candidate.status().isInstalled())
-        candidate = repoitem;
-    }
-
-    if (!candidate.status().setToBeInstalled(zypp::ResStatus::USER) && !force)
-    {
-        zypper.out().error(boost::str(
-          format(_("Failed to add '%s' to the list of packages to be installed."))
-          % name));
-        ERR << "Could not set " << name << " as to-be-installed" << endl;
-    }
-  }
-}
-
-// ----------------------------------------------------------------------------
-
-struct DeleteProcess
-{
-  bool found;
-  DeleteProcess ()
-    : found(false)
-  { }
-
-  bool operator() ( const PoolItem& provider )
-  {
-    found = true;
-    DBG << "Marking for deletion: " << provider << endl;
-    bool result = provider.status().setToBeUninstalled( zypp::ResStatus::USER );
-    if (!result) {
-      Zypper::instance()->out().error(boost::str(format(
-          _("Failed to add '%s' to the list of packages to be removed."))
-          % provider.resolvable()->name()));
-      ERR << "Could not set " << provider.resolvable()->name()
-          << " as to-be-uninstalled" << endl;
-    }
-    return true;                // get all of them
-  }
-};
-
-// ----------------------------------------------------------------------------
-
-// mark all matches
-static void mark_for_uninstall(Zypper & zypper,
-                        const ResObject::Kind &kind,
-                        const std::string &name)
-{
-  const ResPool &pool = God->pool();
-  // name and kind match:
-
-  DeleteProcess deleter;
-  DBG << "Iterating over " << name << endl;
-  invokeOnEach( pool.byIdentBegin( kind, name ),
-                pool.byIdentEnd( kind, name ),
-                resfilter::ByInstalled(),
-                zypp::functor::functorRef<bool,const zypp::PoolItem&> (deleter)
-                );
-  DBG << "... done" << endl;
-  if (!deleter.found) {
-    zypper.out().error(
-      // translators: meaning a package %s or provider of capability %s
-      str::form(_("'%s' not found"), name.c_str()));
-    WAR << str::form("'%s' not found", name.c_str()) << endl;
-    zypper.setExitCode(ZYPPER_EXIT_INF_CAP_NOT_FOUND);
-    return;
-  }
-}
-
-// ----------------------------------------------------------------------------
-
-static void
-mark_by_name (Zypper & zypper,
-              bool install_not_remove,
-              const ResObject::Kind &kind,
-              const string &name,
-              const string & repo = "",
-              const string & arch = "",
-              bool report_already_installed = true) // might be a CLI option
-{
-  if (install_not_remove)
-    mark_for_install(zypper, kind, name, repo, arch, report_already_installed);
-  else
-    mark_for_uninstall(zypper, kind, name);
-}
-
-
-// don't try NAME-EDITION yet, could be confused by
-// dbus-1-x11, java-1_4_2-gcj-compat, ...
-/*
-
-ma@: Look at the Capability::guessPackageSpec implementaion. This might be
-what you want unless you also want to support globbing like 'libz*-12.3-14'.
-In this case you need PoolQuery instead of WhatProvides lookups.
-
-There is no rule that an edition starts with a number, so your regex approach
-won't work. If your string is correctly parsed as a name-capability, you can
-check whether it matches a package name. If not, replace the last '-' by a '=',
-and check whether the namepart now matches a package (-versionwithoutrelease).
-If not, replace the one but last '-' and try again (-version-release).
-
-That's basically what guessPackageSpec does, but it also supports embeded
-arch specs: "libzypp-1.2.3-4.5.arch" or "libzypp.arch-1.2.3-4.5".
-
-bool mark_by_name_edition (...)
-  static const regex rx_name_edition("(.*?)-([0-9].*)");
-
-  smatch m;
-  if (! is_cap && regex_match (capstr, m, rx_name_edition)) {
-    capstr = m.str(1) + " = " + m.str(2);
-    is_cap = true;
-  }
-*/
-
-// ----------------------------------------------------------------------------
-
-static void
-mark_by_capability (Zypper & zypper,
-                    bool install_not_remove,
-                    const ResKind & kind,
-                    const Capability & cap)
-{
-  if (!cap.empty()) {
-    DBG << "Capability: " << cap << endl;
-
-    Resolver_Ptr resolver = zypp::getZYpp()->resolver();
-    if (install_not_remove) {
-      DBG << "Adding requirement " << cap << endl;
-      resolver->addRequire (cap);
-    }
-    else {
-      DBG << "Adding conflict " << cap << endl;
-      resolver->addConflict (cap);
-    }
-  }
-}
-
-// ----------------------------------------------------------------------------
-
-static void
-mark_selectable(Zypper & zypper,
-                ui::Selectable & s,
-                bool install_not_remove,
-                bool force,
-                const string & repo = "",
-                const string & arch = "")
-{
-  PoolItem theone = s.updateCandidateObj();
-  if (!theone)
-    theone = s.installedObj();
-
-  DBG << "the One: " << theone << endl;
-
-  //! \todo handle multiple installed case
-  bool theoneinstalled; // is the One installed ?
-  if (!traits::isPseudoInstalled(s.kind()))
-    theoneinstalled = !s.installedEmpty() &&
-      identical(s.installedObj(), theone);
-  else if (s.kind() == ResKind::patch)
-    theoneinstalled = theone.isRelevant() && theone.isSatisfied();
-  else
-    theoneinstalled = theone.isSatisfied();
-
-  bool anyinstalled = theoneinstalled;
-  PoolItem installed;
-  if (theoneinstalled)
-    installed = theone;
-  else
-  {
-    if (!traits::isPseudoInstalled(s.kind()))
-    {
-      anyinstalled = s.hasInstalledObj();
-      installed = s.installedObj();
-    }
-    else if (s.kind() == ResKind::patch)
-    {
-      for_(it, s.availableBegin(), s.availableEnd())
-      {
-        if (it->isRelevant() && it->isSatisfied())
-        {
-          if (installed)
-          {
-            if (it->resolvable()->edition() > installed->edition())
-              installed = *it;
-          }
-          else
-          {
-            installed = *it;
-            anyinstalled = true;
-          }
-        }
-      }
-    }
-    else
-    {
-      for_(it, s.availableBegin(), s.availableEnd())
-      {
-        if (it->status().isSatisfied())
-        {
-          if (installed)
-          {
-            if (it->resolvable()->edition() > installed->edition())
-              installed = *it;
-          }
-          else
-          {
-            installed = *it;
-            anyinstalled = true;
-          }
-        }
-      }
-    }
-  }
-
-  if (install_not_remove)
-  {
-    if (theoneinstalled && !force)
-    {
-      DBG << "the One (" << theone << ") is already installed, skipping." << endl;
-      zypper.out().info(str::form(
-          _("'%s' is already installed."), s.name().c_str()));
-
-      // report why an update was not found for the package (bnc #522223)
-      if (!traits::isPseudoInstalled(s.kind()))
-        selectable_update_report(zypper, s);
-
-      return;
-    }
-
-    if (theoneinstalled && force)
-    {
-      s.setStatus(ui::S_Install);
-      DBG << s << " install: forcing reinstall" << endl;
-    }
-    else
-    {
-      Capability c;
-
-      // require version greater than the installed. The solver should pick up
-      // the latest installable in this case, which is what we want for all
-      // kinds (and for patches having the latest installed should automatically
-      // satisfy all older version, or make them irrelevant)
-
-      //! could be a problem for patches if there would a greater version
-      //! of a patch appear that would be irrelevant at the same time. Should
-      //! not happen usually.
-
-      //! this causes bnc #539360 if wrong update candidate is found
-      //! this request installation of installed > installed_version, which
-      //! can drag in a package with higher version even from repo with lower
-      //! priority
-      //! hopefully using ui::Selectable::updateCandidateObj() will fix this
-      if (anyinstalled)
-        c = Capability(s.name(), Rel::GT, installed->edition(), s.kind());
-      // require any version
-      else
-        c = Capability(s.name(), s.kind());
-
-      God->resolver()->addRequire(c);
-      DBG << s << " install: adding requirement " << c << endl;
-    }
-  }
-  // removing is simpler, as usually
-  //! \todo but not that simple - simply adding a conflict with a pattern
-  //! or patch does not make the packages it requires to be removed.
-  //! we still need to define what response of zypper -t foo bar should be for PPP
-  else
-  {
-    if (!anyinstalled)
-    {
-      zypper.out().info(str::form(
-          _("'%s' is not installed."), s.name().c_str()));
-      DBG << s << " remove: not installed, skipping." << endl;
-      return;
-    }
-
-    Capability c(s.name(), s.kind());
-    God->resolver()->addConflict(c);
-    DBG << s << " remove: adding conflict " << c << endl;
-  }
-}
-
-// ----------------------------------------------------------------------------
-
-void install_remove(Zypper & zypper,
-                    const Zypper::ArgList & args,
-                    bool install_not_remove,
-                    const ResKind & kind)
-{
-  if (args.empty())
-    return;
-
-  if (kind == ResKind::patch && !install_not_remove)
-  {
-    zypper.out().error(
-        _("Cannot uninstall patches."),
-        _("Installed status of a patch is determined solely based on its dependencies.\n"
-          "Patches are not installed in sense of copied files, database records,\n"
-          "or similar."));
-    zypper.setExitCode(ZYPPER_EXIT_ERR_INVALID_ARGS);
-    throw ExitRequestException("not implemented");
-  }
-
-  if (kind == ResKind::pattern && !install_not_remove)
-  {
-    //! \todo define and implement pattern removal (bnc #407040)
-    zypper.out().error(
-        _("Uninstallation of a pattern is currently not defined and implemented."));
-    zypper.setExitCode(ZYPPER_EXIT_ERR_INVALID_ARGS);
-    throw ExitRequestException("not implemented");
-  }
-
-  bool force_by_capability = zypper.cOpts().count("capability");
-  bool force_by_name = zypper.cOpts().count("name");
-  bool force = zypper.cOpts().count("force");
-  if (force)
-    force_by_name = true;
-
-  if (force_by_capability && force_by_name)
-  {
-    zypper.out().error(boost::str(
-      format(_("%s contradicts %s")) % "--capability" % (force? "--force" : "--name")));
-
-    zypper.setExitCode(ZYPPER_EXIT_ERR_INVALID_ARGS);
-    ZYPP_THROW(ExitRequestException());
-  }
-
-  if (install_not_remove && force_by_capability && force)
-  {
-    // translators: meaning --force with --capability
-    zypper.out().error(boost::str(format(_("%s cannot currently be used with %s"))
-      % "--force" % "--capability"));
-    zypper.setExitCode(ZYPPER_EXIT_ERR_INVALID_ARGS);
-    ZYPP_THROW(ExitRequestException());
-  }
-
-  PackageArgs pargs(args);
-
-  // --from
-
-  parsed_opts::const_iterator optit;
-  if (install_not_remove &&
-      (optit = zypper.cOpts().find("from")) != zypper.cOpts().end())
-  {
-    list<string> not_found;
-    list<RepoInfo> repos;
-    get_repos(zypper, optit->second.begin(), optit->second.end(), repos, not_found);
-    if (!not_found.empty())
-    {
-      report_unknown_repos(zypper.out(), not_found);
-      zypper.setExitCode(ZYPPER_EXIT_ERR_INVALID_ARGS);
-      return;
-    }
-
-    // for each argument search (glob) & mark
-    for_(cit, pargs.doCaps().begin(), pargs.doCaps().end())
-    {
-      sat::Solvable::SplitIdent splid(cit->first.detail().name());
-      ResKind capkind = splid.kind();
-      string capname = splid.name().asString();
-
-      PoolQuery q;
-      q.addKind(capkind);
-      q.addAttribute(sat::SolvAttr::name, capname);
-      for_(it, repos.begin(), repos.end())
-        q.addRepo(it->alias());
-      q.setMatchGlob();
-
-      // Get the best matching items and tag them for
-      // installation.
-      PoolItemBest bestMatches(q.begin(), q.end());
-      if (!bestMatches.empty())
-      {
-        for_(sit, bestMatches.begin(), bestMatches.end())
-        {
-          ui::asSelectable()(*sit)->setOnSystem(*sit, ResStatus::USER);
-        }
-      }
-      else
-      {
-        // translators: meaning a package %s or provider of capability %s
-        zypper.out().error(str::form(_("'%s' not found."), capname.c_str()));
-        WAR << str::form("'%s' not found", capname.c_str()) << endl;
-        zypper.setExitCode(ZYPPER_EXIT_INF_CAP_NOT_FOUND);
-        if (zypper.globalOpts().non_interactive)
-          ZYPP_THROW(ExitRequestException());
-      }
-    }
-    return;
-  }
-
-  for_(it, pargs.doCaps().begin(), pargs.doCaps().end())
-  {
-    // For given PackageArgs:
-    //
-    // 1) if the capability only contains name and kind, and --capability was
-    //    not specified, or --name was specified, try to install 'by-name'
-    //    first, i.e. via ui::Selectable and/or PoolItem.
-    //    note: wildcards must be supported here, use PoolQuery with match_glob
-    //          to find the selectables
-    //
-    // 2) if no package could be found by name, or the capability contains
-    //    op+version/arch, or --capability was specified,
-    //    install 'by-capability', i.e. using ResPool::addRequires(cap)
-    //
-    // NOTES
-    // * In both cases check for already installed packages, available
-    //   candidates, and report any problems back to user.
-    // * If the argument contains repository, issue request forcing the repo and
-    //   - if --force was specified, insist on installing from that repo (even
-    //     downgrade or change vendor   TODO
-    //   - if --force was NOT used, only install new, or upgrade without vendor
-    //     change. If downgrade or vendor change is needed, report back to user
-    //     and advice to use --force, if that's what is really wanted. TODO
-
-    sat::Solvable::SplitIdent splid(it->first.detail().name());
-    ResKind capkind = splid.kind();
-    string capname = splid.name().asString();
-
-    // mark by name by force
-    if (force_by_name)
-    {
-      //! \todo FIXME this does not work: mark_by_name does not pick the arch nor repo. Make an API in zypp for this
-      mark_by_name (zypper, true, capkind, capname, it->second, it->first.detail().arch().asString());
-      continue;
-    }
-
-    // try to install by name first (if version or arch was not specified)
-    if (!(force_by_capability || it->first.detail().hasArch() || it->first.detail().isVersioned()))
-    {
-      PoolQuery q;
-      q.addKind(kind);
-      q.addAttribute(sat::SolvAttr::name, capname);
-      q.setMatchGlob();
-      bool found = false;
-      for_(s, q.selectableBegin(), q.selectableEnd())
-      {
-        //! FIXME mark_selectable works via addRequires, change it to mark via selectable!!!
-        // otherwise the solver is free to install any provider of the symbol
-        mark_selectable(zypper, **s, true, force);
-        found = true;
-      }
-      // done with this requirement, skip to next argument
-      if (found)
-        continue;
-    }
-
-    // try by capability
-    sat::WhatProvides q(it->first);
-
-    // is there a provider for the requested capability?
-    if (q.empty())
-    {
-      // translators: meaning a package %s or provider of capability %s
-      zypper.out().error(str::form(_("'%s' not found."), it->first.asString().c_str()));
-      WAR << str::form("'%s' not found", it->first.asString().c_str()) << endl;
-      zypper.setExitCode(ZYPPER_EXIT_INF_CAP_NOT_FOUND);
-      if (zypper.globalOpts().non_interactive)
-        ZYPP_THROW(ExitRequestException());
-      continue;
-    }
-
-    // is the provider already installed?
-    bool installed = false;
-    string provider;
-    for_(solvit, q.poolItemBegin(), q.poolItemEnd())
-    {
-      if (traits::isPseudoInstalled(solvit->resolvable()->kind()))
-        installed = solvit->isSatisfied();
-      else
-        installed = solvit->status().isInstalled();
-      if (installed)
-      {
-        provider = solvit->resolvable()->name();
-        break;
-      }
-    }
-
-    // already installed, nothing to do
-    if (installed)
-    {
-      if (provider == capname)
-        zypper.out().info(str::form(
-            _("'%s' is already installed."), capname.c_str()));
-      else
-        zypper.out().info(str::form(
-            // translators: %s are package names
-            _("'%s' providing '%s' is already installed."),
-            provider.c_str(), it->first.asString().c_str()));
-
-      MIL << str::form("skipping '%s': already installed", it->first.asString().c_str()) << endl;
-      continue;
-    }
-
-    mark_by_capability (zypper, true, capkind, it->first);
-  }
-
-  for_(it, pargs.dontCaps().begin(), pargs.dontCaps().end())
-  {
-    sat::Solvable::SplitIdent splid(it->first.detail().name());
-    ResKind capkind = splid.kind();
-    string capname = splid.name().asString();
-
-    // mark by name by force
-    if (force_by_name)
-    {
-      mark_by_name (zypper, true, capkind, capname,
-          it->second, it->first.detail().arch().asString());
-      continue;
-    }
-
-    // try to remove by name first (if version or arch was not specified)
-    if (!(force_by_capability || it->first.detail().hasArch() || it->first.detail().isVersioned()))
-    {
-      PoolQuery q;
-      q.addKind(kind);
-      q.addAttribute(sat::SolvAttr::name, capname);
-      q.setMatchGlob();
-      bool found = false;
-      for_(s, q.selectableBegin(), q.selectableEnd())
-      {
-        mark_selectable(zypper, **s, false, force);
-        found = true;
-      }
-      // done with this requirement, skip to next argument
-      if (found)
-        continue;
-    }
-
-    // try by capability
-    sat::WhatProvides q(it->first);
-
-    // is there a provider for the requested capability?
-    if (q.empty())
-    {
-      // translators: meaning a package %s or provider of capability %s
-      zypper.out().error(str::form(_("'%s' not found."), it->first.asString().c_str()));
-      WAR << str::form("'%s' not found", it->first.asString().c_str()) << endl;
-      zypper.setExitCode(ZYPPER_EXIT_INF_CAP_NOT_FOUND);
-      if (zypper.globalOpts().non_interactive)
-        ZYPP_THROW(ExitRequestException());
-      continue;
-    }
-
-    // is the provider installed?
-    bool installed = false;
-    string provider;
-    for_(solvit, q.poolItemBegin(), q.poolItemEnd())
-    {
-      if (traits::isPseudoInstalled(solvit->resolvable()->kind()))
-        installed = solvit->isSatisfied();
-      else
-        installed = solvit->status().isInstalled();
-      if (installed)
-      {
-        provider = solvit->resolvable()->name();
-        break;
-      }
-    }
-
-    // not installed, nothing to do
-    if (!installed)
-    {
-      // translators: meaning a package %s or provider of capability %s
-      zypper.out().info(str::form(_("'%s' is not installed."), capname.c_str()));
-      MIL << str::form("skipping '%s': not installed", capname.c_str()) << endl;
-      continue;
-    }
-
-    mark_by_capability (zypper, false, capkind, it->first);
-  }
-}
index 9f2559f..f5306a3 100644 (file)
 #include "zypp/SrcPackage.h"
 #include "zypp/Package.h"
 #include "zypp/Capabilities.h"
+#include "zypp/ui/Selectable.h"
 
 
 #include "zypp/RepoInfo.h"
 
 #include "zypp/PoolQuery.h"
+#include "zypp/PoolItemBest.h"
 
 #include "Zypper.h"
 #include "main.h"
@@ -33,6 +35,7 @@
 
 using namespace std;
 using namespace zypp;
+using namespace zypp::ui;
 using namespace boost;
 
 extern ZYpp::Ptr God;
@@ -482,6 +485,40 @@ pkg_spec_to_poolquery(const Capability & cap, const string & repo)
   return pkg_spec_to_poolquery(cap, repos);
 }
 
+set<PoolItem>
+get_installed_providers(const Capability & cap)
+{
+  set<PoolItem> providers;
+
+  sat::WhatProvides q(cap);
+  for_(it, q.selectableBegin(), q.selectableEnd())
+  {
+    Selectable::constPtr s;
+    if (traits::isPseudoInstalled(s->kind()))
+    {
+      PoolItem best;
+      for_(ait, s->availableBegin(), s->availableEnd())
+      {
+        // this works also for patches - isSatisfied excludes !isRelevant
+        if (ait->isSatisfied())
+          // we don't care about repo priorities, vendorst and stuff like that
+          // here. All we want to know is what is the highest available version
+          // that already is satisified.
+          // TODO such funtion could be part of Selectable (or does theObj return such object?)
+          // TODO but we should care about repos
+          if (!best || best->edition() < (*ait)->edition())
+            best = *ait;
+      }
+      providers.insert(best);
+    }
+    else if (s->hasInstalledObj())
+      providers.insert(s->installedObj());
+  }
+
+  return providers;
+}
+
+
 // Local Variables:
 // c-basic-offset: 2
 // End:
index b16965d..73cb653 100644 (file)
@@ -73,4 +73,7 @@ pkg_spec_to_poolquery(
     const zypp::Capability & cap,
     const std::string & repo = std::string());
 
+std::set<zypp::PoolItem>
+get_installed_providers(const zypp::Capability & cap);
+
 #endif