aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2006-07-24 02:30:10 +0000
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2006-07-24 02:30:10 +0000
commit0ba0735d30ba1589001f29a8a527a232dc6d57ed (patch)
treeb09ba944c59d3fcda6febb1414b7ab564a5531a1
parent20b6ad5283ef187e8f5154362e2f2201a30ffa8f (diff)
downloadpaludis-0ba0735d30ba1589001f29a8a527a232dc6d57ed.tar.gz
paludis-0ba0735d30ba1589001f29a8a527a232dc6d57ed.tar.xz
Move uninstalling to a task
-rw-r--r--paludis/tasks/Makefile.am6
-rw-r--r--paludis/tasks/uninstall_task.cc197
-rw-r--r--paludis/tasks/uninstall_task.hh126
-rw-r--r--src/paludis/install.cc14
-rw-r--r--src/paludis/uninstall.cc228
5 files changed, 453 insertions, 118 deletions
diff --git a/paludis/tasks/Makefile.am b/paludis/tasks/Makefile.am
index bfd5442..6280f28 100644
--- a/paludis/tasks/Makefile.am
+++ b/paludis/tasks/Makefile.am
@@ -21,8 +21,10 @@ check_SCRIPTS =
lib_LIBRARIES = libpaludistasks.a
paludis_tasks_includedir = $(includedir)/paludis/tasks
paludis_tasks_include_HEADERS = \
- install_task.hh
+ install_task.hh \
+ uninstall_task.hh
libpaludistasks_a_SOURCES = $(paludis_tasks_include_HEADERS) \
- install_task.cc
+ install_task.cc \
+ uninstall_task.cc
diff --git a/paludis/tasks/uninstall_task.cc b/paludis/tasks/uninstall_task.cc
new file mode 100644
index 0000000..b5c04e8
--- /dev/null
+++ b/paludis/tasks/uninstall_task.cc
@@ -0,0 +1,197 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "uninstall_task.hh"
+#include <paludis/environment.hh>
+#include <paludis/util/collection_concrete.hh>
+#include <list>
+
+using namespace paludis;
+
+namespace paludis
+{
+ template<>
+ struct Implementation<UninstallTask> :
+ InternalCounted<Implementation<UninstallTask> >
+ {
+ Environment * const env;
+ InstallOptions install_options;
+
+ std::list<std::string> raw_targets;
+ std::list<PackageDepAtom::Pointer> targets;
+
+ bool pretend;
+ bool preserve_world;
+
+ Implementation<UninstallTask>(Environment * const e) :
+ env(e),
+ install_options(false, false),
+ pretend(false),
+ preserve_world(false)
+ {
+ }
+ };
+}
+
+UninstallTask::UninstallTask(Environment * const e) :
+ PrivateImplementationPattern<UninstallTask>(new Implementation<UninstallTask>(e))
+{
+}
+
+UninstallTask::~UninstallTask()
+{
+}
+
+void
+UninstallTask::set_pretend(const bool v)
+{
+ _imp->pretend = v;
+}
+
+void
+UninstallTask::set_no_config_protect(const bool v)
+{
+ _imp->install_options.set<io_noconfigprotect>(v);
+}
+
+void
+UninstallTask::set_preserve_world(const bool v)
+{
+ _imp->preserve_world = v;
+}
+
+void
+UninstallTask::add_target(const std::string & target)
+{
+ Context context("When adding uninstall target '" + target + "':");
+
+ /* we might have a dep atom, but we might just have a simple package name
+ * without a category. either should work. */
+ if (std::string::npos != target.find('/'))
+ _imp->targets.push_back(PackageDepAtom::Pointer(new PackageDepAtom(target)));
+ else
+ _imp->targets.push_back(PackageDepAtom::Pointer(new PackageDepAtom(
+ _imp->env->package_database()->fetch_unique_qualified_package_name(
+ PackageNamePart(target)))));
+
+ _imp->raw_targets.push_back(target);
+}
+
+namespace
+{
+ struct WorldCallbacks :
+ public Environment::WorldCallbacks
+ {
+ UninstallTask * const t;
+
+ WorldCallbacks(UninstallTask * const tt) :
+ t(tt)
+ {
+ }
+
+ virtual void remove_callback(const PackageDepAtom * a)
+ {
+ t->on_update_world(*a);
+ }
+ };
+}
+
+void
+UninstallTask::execute()
+{
+ Context context("When executing install task:");
+
+ on_build_unmergelist_pre();
+
+ PackageDatabaseEntryCollection::Pointer unmerge(new PackageDatabaseEntryCollection::Concrete);
+ for (std::list<PackageDepAtom::Pointer>::const_iterator t(_imp->targets.begin()),
+ t_end(_imp->targets.end()) ; t != t_end ; ++t)
+ {
+ PackageDatabaseEntryCollection::ConstPointer r(_imp->env->package_database()->query(
+ *t, is_installed_only));
+ if (r->empty())
+ throw NoSuchPackageError(stringify(**t));
+ else if (r->size() > 1)
+ throw AmbiguousUnmergeTargetError(stringify(**t), r);
+ else
+ unmerge->insert(*r->begin());
+ }
+
+ on_build_unmergelist_post();
+
+ on_display_unmerge_list_pre();
+
+ for (PackageDatabaseEntryCollection::Iterator i(unmerge->begin()),
+ i_end(unmerge->end()) ; i != i_end ; ++i)
+ on_display_unmerge_list_entry(*i);
+
+ on_display_unmerge_list_post();
+
+ if (_imp->pretend)
+ return;
+
+ if (_imp->preserve_world)
+ on_preserve_world();
+ else
+ {
+ on_update_world_pre();
+
+ AllDepAtom::Pointer all(new AllDepAtom);
+ for (std::list<PackageDepAtom::Pointer>::const_iterator t(_imp->targets.begin()),
+ t_end(_imp->targets.end()) ; t != t_end ; ++t)
+ all->add_child(*t);
+
+ WorldCallbacks w(this);
+ _imp->env->remove_appropriate_from_world(all, &w);
+
+ on_update_world_post();
+ }
+
+ _imp->env->perform_hook(Hook("uninstall_all_pre")("TARGETS", join(unmerge->begin(), unmerge->end(), " ")));
+ on_uninstall_all_pre();
+
+ for (PackageDatabaseEntryCollection::Iterator i(unmerge->begin()),
+ i_end(unmerge->end()) ; i != i_end ; ++i)
+ {
+ std::string cpvr(stringify(i->get<pde_name>()) + "-" +
+ stringify(i->get<pde_version>()) + "::" +
+ stringify(i->get<pde_repository>()));
+
+ _imp->env->perform_hook(Hook("uninstall_pre")("TARGET", cpvr));
+ on_uninstall_pre(*i);
+
+ const Repository::UninstallableInterface * const uninstall_interface(
+ _imp->env->package_database()->fetch_repository(i->get<pde_repository>())->
+ get_interface<repo_uninstallable>());
+ if (! uninstall_interface)
+ throw InternalError(PALUDIS_HERE, "Trying to uninstall from a non-uninstallable repo");
+ uninstall_interface->uninstall(i->get<pde_name>(), i->get<pde_version>(), _imp->install_options);
+
+ on_uninstall_post(*i);
+ _imp->env->perform_hook(Hook("uninstall_post")("TARGET", cpvr));
+ }
+
+ on_uninstall_all_post();
+ _imp->env->perform_hook(Hook("uninstall_all_post")("TARGETS", join(unmerge->begin(), unmerge->end(), " ")));
+}
+
+AmbiguousUnmergeTargetError::~AmbiguousUnmergeTargetError() throw ()
+{
+}
+
diff --git a/paludis/tasks/uninstall_task.hh b/paludis/tasks/uninstall_task.hh
new file mode 100644
index 0000000..a7f04a2
--- /dev/null
+++ b/paludis/tasks/uninstall_task.hh
@@ -0,0 +1,126 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2006 Ciaran McCreesh <ciaran.mccreesh@blueyonder.co.uk>
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_TASKS_UNINSTALL_TASK_HH
+#define PALUDIS_GUARD_PALUDIS_TASKS_UNINSTALL_TASK_HH 1
+
+#include <paludis/dep_atom.hh>
+#include <paludis/package_database_entry.hh>
+#include <paludis/util/instantiation_policy.hh>
+#include <paludis/util/private_implementation_pattern.hh>
+
+namespace paludis
+{
+ class Environment;
+
+ class AmbiguousUnmergeTargetError :
+ public Exception
+ {
+ private:
+ const std::string _t;
+ const PackageDatabaseEntryCollection::ConstPointer _p;
+
+ public:
+ AmbiguousUnmergeTargetError(const std::string & target,
+ const PackageDatabaseEntryCollection::ConstPointer matches) throw () :
+ Exception("Ambiguous unmerge target '" + target + "'"),
+ _t(target),
+ _p(matches)
+ {
+ }
+
+ ~AmbiguousUnmergeTargetError() throw ();
+
+ typedef PackageDatabaseEntryCollection::Iterator Iterator;
+
+ Iterator begin() const
+ {
+ return _p->begin();
+ }
+
+ Iterator end() const
+ {
+ return _p->end();
+ }
+
+ const std::string & target() const
+ {
+ return _t;
+ }
+ };
+
+ class UninstallTask :
+ PrivateImplementationPattern<UninstallTask>,
+ InstantiationPolicy<UninstallTask, instantiation_method::NonCopyableTag>
+ {
+ public:
+ ///\name Basic operations
+ ///\{
+
+ UninstallTask(Environment * const env);
+ virtual ~UninstallTask();
+
+ ///\}
+
+ ///\name Behaviour options
+ ///\{
+
+ void set_no_config_protect(const bool value);
+ void set_pretend(const bool value);
+ void set_preserve_world(const bool value);
+
+ ///\}
+
+ ///\name Add targets
+ ///\{
+
+ void add_target(const std::string &);
+
+ ///\}
+
+ ///\name Event callbacks
+ ///\{
+
+ virtual void on_build_unmergelist_pre() = 0;
+ virtual void on_build_unmergelist_post() = 0;
+
+ virtual void on_display_unmerge_list_pre() = 0;
+ virtual void on_display_unmerge_list_post() = 0;
+ virtual void on_display_unmerge_list_entry(const PackageDatabaseEntry &) = 0;
+
+ virtual void on_uninstall_all_pre() = 0;
+ virtual void on_uninstall_pre(const PackageDatabaseEntry &) = 0;
+ virtual void on_uninstall_post(const PackageDatabaseEntry &) = 0;
+ virtual void on_uninstall_all_post() = 0;
+
+ virtual void on_update_world_pre() = 0;
+ virtual void on_update_world(const PackageDepAtom &) = 0;
+ virtual void on_update_world_post() = 0;
+ virtual void on_preserve_world() = 0;
+
+ ///\}
+
+ /**
+ * Run the task.
+ */
+ void execute();
+ };
+}
+
+#endif
diff --git a/src/paludis/install.cc b/src/paludis/install.cc
index edccb49..6c19b37 100644
--- a/src/paludis/install.cc
+++ b/src/paludis/install.cc
@@ -62,19 +62,6 @@ namespace
}
};
- void
- world_add_callback(const PackageDepAtom * const p)
- {
- cout << "* adding " << colour(cl_package_name, stringify(*p)) << endl;
- }
-
- void
- world_skip_callback(const PackageDepAtom * const p, const std::string & why)
- {
- cout << "* skipping " << colour(cl_package_name, stringify(*p))
- << " (" << why << ")" << endl;
- }
-
class OurInstallTask :
public InstallTask
{
@@ -518,7 +505,6 @@ do_install()
try
{
-
for (CommandLine::ParametersIterator q(CommandLine::get_instance()->begin_parameters()),
q_end(CommandLine::get_instance()->end_parameters()) ; q != q_end ; ++q)
task.add_target(*q);
diff --git a/src/paludis/uninstall.cc b/src/paludis/uninstall.cc
index fc2205d..685a35c 100644
--- a/src/paludis/uninstall.cc
+++ b/src/paludis/uninstall.cc
@@ -19,15 +19,17 @@
#include "colour.hh"
#include "uninstall.hh"
+
+#include <paludis/default_environment.hh>
+#include <paludis/tasks/uninstall_task.hh>
+
#include <iostream>
-#include <paludis/paludis.hh>
-#include <paludis/util/collection_concrete.hh>
/** \file
* Handle the --uninstall action for the main paludis program.
*/
-namespace p = paludis;
+using namespace paludis;
using std::cerr;
using std::cout;
@@ -35,13 +37,93 @@ using std::endl;
namespace
{
- struct WorldCallbacks :
- public p::Environment::WorldCallbacks
+ class OurUninstallTask :
+ public UninstallTask
{
- void remove_callback(const p::PackageDepAtom * const p)
- {
- cout << "* removing " << colour(cl_package_name, stringify(*p)) << endl;
- }
+ private:
+ int _count, _current_count;
+
+ public:
+ OurUninstallTask() :
+ UninstallTask(DefaultEnvironment::get_instance()),
+ _count(0),
+ _current_count(0)
+ {
+ };
+
+ virtual void on_build_unmergelist_pre()
+ {
+ cout << "Building unmerge list... " << std::flush;
+ }
+
+ virtual void on_build_unmergelist_post()
+ {
+ cout << "done" << endl;
+ }
+
+ virtual void on_display_unmerge_list_pre()
+ {
+ cout << endl << colour(cl_heading, "These packages will be uninstalled:")
+ << endl << endl;
+ }
+
+ virtual void on_display_unmerge_list_post()
+ {
+ cout << endl << endl <<
+ "Total: " << _count << (_count == 1 ? " package" : " packages") << endl;
+ }
+
+ virtual void on_display_unmerge_list_entry(const PackageDatabaseEntry & d)
+ {
+ cout << "* " << colour(cl_package_name, stringify(d)) << endl;
+ ++_count;
+ }
+
+ virtual void on_uninstall_all_pre()
+ {
+ }
+
+ virtual void on_uninstall_pre(const PackageDatabaseEntry & d)
+ {
+ cout << endl << colour(cl_heading, "Uninstalling " +
+ stringify(d.get<pde_name>()) + "-" + stringify(d.get<pde_version>()) +
+ "::" + stringify(d.get<pde_repository>())) << endl << endl;
+
+ cerr << xterm_title("(" + stringify(++_current_count) + " of " +
+ stringify(_count) + ") Installing " +
+ stringify(d.get<pde_name>()) + "-" + stringify(d.get<pde_version>()) +
+ "::" + stringify(d.get<pde_repository>()));
+ }
+
+ virtual void on_uninstall_post(const PackageDatabaseEntry &)
+ {
+ }
+
+ virtual void on_uninstall_all_post()
+ {
+ }
+
+ virtual void on_update_world_pre()
+ {
+ cout << endl << colour(cl_heading, "Updating world file") << endl << endl;
+ }
+
+ virtual void on_update_world(const PackageDepAtom & a)
+ {
+ cout << "* removing " << colour(cl_package_name, a.package()) << endl;
+ }
+
+ virtual void on_update_world_post()
+ {
+ cout << endl;
+ }
+
+ virtual void on_preserve_world()
+ {
+ cout << endl << colour(cl_heading, "Updating world file") << endl << endl;
+ cout << "* --preserve-world was specified, skipping world changes" << endl;
+ cout << endl;
+ }
};
}
@@ -50,111 +132,53 @@ do_uninstall()
{
int return_code(0);
- p::Context context("When performing uninstall action from command line:");
- p::Environment * const env(p::DefaultEnvironment::get_instance());
+ Context context("When performing uninstall action from command line:");
- cout << colour(cl_heading, "These packages will be uninstalled:") << endl << endl;
+ OurUninstallTask task;
- std::list<p::PackageDepAtom::Pointer> targets;
+ task.set_pretend(CommandLine::get_instance()->a_pretend.specified());
+ task.set_no_config_protect(CommandLine::get_instance()->a_no_config_protection.specified());
+ task.set_preserve_world(CommandLine::get_instance()->a_preserve_world.specified());
- CommandLine::ParametersIterator q(CommandLine::get_instance()->begin_parameters()),
- q_end(CommandLine::get_instance()->end_parameters());
-
- for ( ; q != q_end ; ++q)
+ try
{
- /* we might have a dep atom, but we might just have a simple package name
- * without a category. either should work. */
- if (std::string::npos != q->find('/'))
- targets.push_back(p::PackageDepAtom::Pointer(new p::PackageDepAtom(*q)));
- else
- targets.push_back(p::PackageDepAtom::Pointer(new p::PackageDepAtom(
- env->package_database()->fetch_unique_qualified_package_name(
- p::PackageNamePart(*q)))));
- }
+ for (CommandLine::ParametersIterator q(CommandLine::get_instance()->begin_parameters()),
+ q_end(CommandLine::get_instance()->end_parameters()) ; q != q_end ; ++q)
+ task.add_target(*q);
- bool ok(true);
- p::PackageDatabaseEntryCollection::Pointer unmerge(new p::PackageDatabaseEntryCollection::Concrete);
- for (std::list<p::PackageDepAtom::Pointer>::iterator t(targets.begin()), t_end(targets.end()) ;
- t != t_end ; ++t)
+ task.execute();
+
+ cout << endl;
+ }
+ catch (const AmbiguousUnmergeTargetError & e)
{
- p::PackageDatabaseEntryCollection::ConstPointer r(
- env->package_database()->query(*t, p::is_installed_only));
- if (r->empty())
- {
- cout << "* No match for " << colour(cl_masked, **t) << endl;
- ok = false;
- }
- else if (r->size() > 1)
- {
- cout << "* Multiple matches for " << colour(cl_masked, **t) << ":" << endl;
- for (p::PackageDatabaseEntryCollection::Iterator p(r->begin()),
- p_end(r->end()) ; p != p_end ; ++p)
- cout << " * " << colour(cl_package_name, *p) << endl;
- ok = false;
- }
- else
- {
- cout << "* " << colour(cl_package_name, *r->begin()) << endl;
- unmerge->insert(*r->begin());
- }
+ cout << endl;
+ cerr << "Query error:" << endl;
+ cerr << " * " << e.backtrace("\n * ");
+ cerr << "Ambiguous unmerge target '" << e.target() << "'. Did you mean:" << endl;
+ for (AmbiguousUnmergeTargetError::Iterator o(e.begin()),
+ o_end(e.end()) ; o != o_end ; ++o)
+ cerr << " * =" << colour(cl_package_name, *o) << endl;
+ cerr << endl;
+ return 1;
}
-
- int current_count = 0, max_count = std::distance(unmerge->begin(), unmerge->end());
-
- cout << endl << "Total: " << max_count <<
- (max_count == 1 ? " package" : " packages") << endl << endl;
-
- if (! ok)
- return 1 | return_code;
-
- if (CommandLine::get_instance()->a_pretend.specified())
- return return_code;
-
- cout << endl << colour(cl_heading, "Updating world file") << endl << endl;
- if (! CommandLine::get_instance()->a_preserve_world.specified())
+ catch (const PackageUninstallActionError & e)
{
- p::AllDepAtom::Pointer all(new p::AllDepAtom);
- for (std::list<p::PackageDepAtom::Pointer>::const_iterator t(targets.begin()),
- t_end(targets.end()) ; t != t_end ; ++t)
- all->add_child(*t);
+ cout << endl;
+ cerr << "Uninstall error:" << endl;
+ cerr << " * " << e.backtrace("\n * ");
+ cerr << e.message() << endl;
- WorldCallbacks w;
- env->remove_appropriate_from_world(all, &w);
+ return_code |= 1;
}
- else
- cout << "* --preserve-world was specified, skipping world removes" << endl;
-
- p::InstallOptions opts(false, false);
- if (CommandLine::get_instance()->a_no_config_protection.specified())
- opts.set<p::io_noconfigprotect>(true);
-
- env->perform_hook(p::Hook("uninstall_all_pre")("TARGETS", join(unmerge->begin(), unmerge->end(), " ")));
- for (p::PackageDatabaseEntryCollection::Iterator pkg(unmerge->begin()), pkg_end(unmerge->end()) ;
- pkg != pkg_end ; ++pkg)
+ catch (const NoSuchPackageError & e)
{
- std::string cpvr = p::stringify(pkg->get<p::pde_name>()) + "-" +
- p::stringify(pkg->get<p::pde_version>()) + "::" +
- p::stringify(pkg->get<p::pde_repository>());
-
- cout << endl << colour(cl_heading,
- "Uninstalling " + cpvr) << endl << endl;
-
- // TODO: some way to reset this properly would be nice.
- cerr << xterm_title("(" + p::stringify(++current_count) + " of " +
- p::stringify(max_count) + ") Uninstalling " + cpvr);
-
- env->perform_hook(p::Hook("uninstall_pre")("TARGET", cpvr));
- const p::Repository::UninstallableInterface * const uninstall_interface(
- env->package_database()->fetch_repository(pkg->get<p::pde_repository>())->
- get_interface<p::repo_uninstallable>());
- if (! uninstall_interface)
- throw p::InternalError(PALUDIS_HERE, "Trying to uninstall from a non-uninstallable repo");
- uninstall_interface->uninstall(pkg->get<p::pde_name>(), pkg->get<p::pde_version>(), opts);
- env->perform_hook(p::Hook("uninstall_post")("TARGET", cpvr));
+ cout << endl;
+ cerr << "Query error:" << endl;
+ cerr << " * " << e.backtrace("\n * ");
+ cerr << "No such package '" << e.name() << "'" << endl;
+ return 1;
}
- env->perform_hook(p::Hook("uninstall_all_post")("TARGETS", join(unmerge->begin(), unmerge->end(), " ")));
-
- cout << endl;
return return_code;
}