aboutsummaryrefslogtreecommitdiff
path: root/bpkg/drop.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-12-09 11:32:50 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-12-09 11:32:50 +0200
commitcbd8296c7b86f7fc368d1133da3be3670b7923be (patch)
tree1f8c10aeaf0e19a7d198b06800c2e2a72e760499 /bpkg/drop.cxx
parent82a8cf379ee19dbc66c62bfe78b436d4ab6b497a (diff)
Clean up command names, add aliases
Diffstat (limited to 'bpkg/drop.cxx')
-rw-r--r--bpkg/drop.cxx512
1 files changed, 0 insertions, 512 deletions
diff --git a/bpkg/drop.cxx b/bpkg/drop.cxx
deleted file mode 100644
index 87c475d..0000000
--- a/bpkg/drop.cxx
+++ /dev/null
@@ -1,512 +0,0 @@
-// file : bpkg/drop.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <bpkg/drop>
-
-#include <map>
-#include <list>
-#include <vector>
-#include <iostream> // cout
-#include <functional> // reference_wrapper
-
-#include <bpkg/types>
-#include <bpkg/package>
-#include <bpkg/package-odb>
-#include <bpkg/utility>
-#include <bpkg/database>
-#include <bpkg/diagnostics>
-#include <bpkg/satisfaction>
-#include <bpkg/manifest-utility>
-
-#include <bpkg/common-options>
-
-#include <bpkg/pkg-purge>
-#include <bpkg/pkg-disfigure>
-
-using namespace std;
-using namespace butl;
-
-namespace bpkg
-{
- enum class drop_reason
- {
- user, // User selection.
- dependent, // Dependent of a user or another dependent.
- prerequisite // Prerequisite of a user, dependent, or another prerequisite.
- };
-
- struct drop_package
- {
- shared_ptr<selected_package> package;
- drop_reason reason;
- };
-
- // List of packages that are dependent on the user selection.
- //
- struct dependent_name
- {
- string name;
- string prq_name; // Prerequisite package name.
- };
- using dependent_names = vector<dependent_name>;
-
- // A "dependency-ordered" list of packages and their prerequisites.
- // That is, every package on the list only possibly depending on the
- // ones after it. In a nutshell, the usage is as follows: we first add
- // the packages specified by the user (the "user selection"). We then
- // collect all the dependent packages of the user selection, if any.
- // These will either have to be dropped as well or we cannot continue.
- // If the user gave the go ahead to drop the dependents, then, for our
- // purposes, this list of dependents can from now own be treated as if
- // it was a part of the user selection. The next step is to collect all
- // the non-held prerequisites of the user selection with the goal of
- // figuring out which ones will no longer be needed and offering to
- // drop them as well. This part is a bit tricky and has to be done in
- // three steps: We first collect all the prerequisites that we could
- // possibly be dropping. We then order all the packages. And, finally,
- // we filter out prerequisites that we cannot drop. See the comment to
- // the call to collect_prerequisites() for details on why it has to be
- // done this way.
- //
- struct drop_packages: list<reference_wrapper<drop_package>>
- {
- // Collect a package to be dropped, by default, as a user selection.
- //
- bool
- collect (shared_ptr<selected_package> p, drop_reason r = drop_reason::user)
- {
- string n (p->name); // Because of move(p) below.
- return map_.emplace (move (n), data_type {end (), {move (p), r}}).second;
- }
-
- // Collect all the dependets of the user selection retutning the list
- // of their names. Dependents of dependents are collected recursively.
- //
- dependent_names
- collect_dependents (database& db)
- {
- dependent_names dns;
-
- for (const auto& pr: map_)
- {
- const drop_package& dp (pr.second.package);
-
- // Unconfigured package cannot have any dependents.
- //
- if (dp.reason == drop_reason::user &&
- dp.package->state == package_state::configured)
- collect_dependents (db, dns, dp.package);
- }
-
- return dns;
- }
-
- void
- collect_dependents (database& db,
- dependent_names& dns,
- const shared_ptr<selected_package>& p)
- {
- using query = query<package_dependent>;
-
- for (auto& pd: db.query<package_dependent> (query::name == p->name))
- {
- const string& dn (pd.name);
-
- if (map_.find (dn) == map_.end ())
- {
- shared_ptr<selected_package> dp (db.load<selected_package> (dn));
- dns.push_back (dependent_name {dn, p->name});
- collect (dp, drop_reason::dependent);
- collect_dependents (db, dns, dp);
- }
- }
- }
-
- // Collect prerequisites of the user selection and its dependents,
- // returning true if any were collected. Prerequisites of prerequisites
- // are collected recursively.
- //
- bool
- collect_prerequisites (database& db)
- {
- bool r (false);
-
- for (const auto& pr: map_)
- {
- const drop_package& dp (pr.second.package);
-
- // Unconfigured package cannot have any prerequisites.
- //
- if ((dp.reason == drop_reason::user ||
- dp.reason == drop_reason::dependent) &&
- dp.package->state == package_state::configured)
- r = collect_prerequisites (db, dp.package) || r;
- }
-
- return r;
- }
-
- bool
- collect_prerequisites (database& db, const shared_ptr<selected_package>& p)
- {
- bool r (false);
-
- for (const auto& pair: p->prerequisites)
- {
- const string& pn (pair.first.object_id ());
-
- if (map_.find (pn) == map_.end ())
- {
- shared_ptr<selected_package> pp (db.load<selected_package> (pn));
-
- if (!pp->hold_package) // Prune held packages.
- {
- collect (pp, drop_reason::prerequisite);
- collect_prerequisites (db, pp);
- r = true;
- }
- }
- }
-
- return r;
- }
-
- // Order the previously-collected package with the specified name
- // returning its positions.
- //
- iterator
- order (const string& name)
- {
- // Every package that we order should have already been collected.
- //
- auto mi (map_.find (name));
- assert (mi != map_.end ());
-
- // If this package is already in the list, then that would also
- // mean all its prerequisites are in the list and we can just
- // return its position.
- //
- iterator& pos (mi->second.position);
- if (pos != end ())
- return pos;
-
- // Order all the prerequisites of this package and compute the
- // position of its "earliest" prerequisite -- this is where it
- // will be inserted.
- //
- drop_package& dp (mi->second.package);
- const shared_ptr<selected_package>& p (dp.package);
-
- // Unless this package needs something to be before it, add it to
- // the end of the list.
- //
- iterator i (end ());
-
- // Figure out if j is before i, in which case set i to j. The goal
- // here is to find the position of our "earliest" prerequisite.
- //
- auto update = [this, &i] (iterator j)
- {
- for (iterator k (j); i != j && k != end ();)
- if (++k == i)
- i = j;
- };
-
- // Only configured packages have prerequisites.
- //
- if (p->state == package_state::configured)
- {
- for (const auto& pair: p->prerequisites)
- {
- const string& pn (pair.first.object_id ());
-
- // The prerequisites may not necessarily be in the map (e.g.,
- // a held package that we prunned).
- //
- if (map_.find (pn) != map_.end ())
- update (order (pn));
- }
- }
-
- return pos = insert (i, dp);
- }
-
- // Remove prerequisite packages that we cannot possibly drop, returning
- // true if any remain.
- //
- bool
- filter_prerequisites (database& db)
- {
- bool r (false);
-
- // Iterate from "more" to "less"-dependent.
- //
- for (auto i (begin ()); i != end (); )
- {
- const drop_package& dp (*i);
-
- if (dp.reason == drop_reason::prerequisite)
- {
- const shared_ptr<selected_package>& p (dp.package);
-
- bool keep (true);
-
- // Get our dependents (which, BTW, could only have been before us
- // on the list). If they are all in the map, then we can be dropped.
- //
- using query = query<package_dependent>;
-
- for (auto& pd: db.query<package_dependent> (query::name == p->name))
- {
- if (map_.find (pd.name) == map_.end ())
- {
- keep = false;
- break;
- }
- }
-
- if (!keep)
- {
- i = erase (i);
- map_.erase (p->name);
- continue;
- }
-
- r = true;
- }
-
- ++i;
- }
-
- return r;
- }
-
- private:
- struct data_type
- {
- iterator position; // Note: can be end(), see collect().
- drop_package package;
- };
-
- map<string, data_type> map_;
- };
-
- int
- drop (const drop_options& o, cli::scanner& args)
- {
- tracer trace ("drop");
-
- if (o.yes () && o.no ())
- fail << "both --yes|-y and --no|-n specified";
-
- const dir_path& c (o.directory ());
- level4 ([&]{trace << "configuration: " << c;});
-
- if (!args.more ())
- fail << "package name argument expected" <<
- info << "run 'bpkg help drop' for more information";
-
- database db (open (c, trace));
-
- // Note that the session spans all our transactions. The idea here is
- // that drop_package objects in the drop_packages list below will be
- // cached in this session. When subsequent transactions modify any of
- // these objects, they will modify the cached instance, which means
- // our list will always "see" their updated state.
- //
- session s;
-
- // Assemble the list of packages we will need to drop.
- //
- drop_packages pkgs;
- bool drop_prq (false);
- {
- transaction t (db.begin ());
-
- // The first step is to load and collect all the packages specified
- // by the user.
- //
- strings names;
- while (args.more ())
- {
- string n (args.next ());
- level4 ([&]{trace << "package " << n;});
-
- shared_ptr<selected_package> p (db.find<selected_package> (n));
-
- if (p == nullptr)
- fail << "package " << n << " does not exist in configuration " << c;
-
- if (p->state == package_state::broken)
- fail << "unable to drop broken package " << n <<
- info << "use 'pkg-purge --force' to remove";
-
- if (pkgs.collect (move (p)))
- names.push_back (move (n));
- }
-
- // The next step is to see if there are any dependents that are not
- // already on the list. We will either have to drop those as well or
- // abort.
- //
- dependent_names dnames (pkgs.collect_dependents (db));
- if (!dnames.empty () && !o.drop_dependent ())
- {
- {
- diag_record dr (text);
-
- dr << "following dependent packages will have to be dropped "
- << "as well:";
-
- for (const dependent_name& dn: dnames)
- dr << text << dn.name
- << " (because dropping " << dn.prq_name << ")";
- }
-
- if (o.yes ())
- fail << "refusing to drop dependent packages with just --yes" <<
- info << "specify --drop-dependent to confirm";
-
- if (o.no () || !yn_prompt ("drop dependent packages? [y/N]", 'n'))
- return 1;
- }
-
- // Collect all the prerequisites that are not held. These will be
- // the candidates to drop as well. Note that we cannot make the
- // final decision who we can drop until we have the complete and
- // ordered list of all the packages that we could potentially be
- // dropping. The ordered part is important: we will have to decide
- // about the "more dependent" prerequisite before we can decide
- // about the "less dependent" one since the former could be depending
- // on the latter and, if that's the case and "more" cannot be dropped,
- // then neither can "less".
- //
- pkgs.collect_prerequisites (db);
-
- // Now that we have collected all the packages we could possibly be
- // dropping, arrange them in the "dependency order", that is, with
- // every package on the list only possibly depending on the ones
- // after it.
- //
- // First order the user selection so that we stay as close to the
- // order specified by the user as possible. Then order the dependent
- // packages. Since each of them depends on one or more packages from
- // the user selection, it will be inserted before the first package
- // on which it depends.
- //
- for (const string& n: names)
- pkgs.order (n);
-
- for (const dependent_name& dn: dnames)
- pkgs.order (dn.name);
-
- // Filter out prerequisites that we cannot possibly drop (e.g., they
- // have dependents other than the ones we are dropping). If there are
- // some that we can drop, ask the user for confirmation.
- //
- if (pkgs.filter_prerequisites (db) && !(drop_prq = o.yes ()) && !o.no ())
- {
- {
- diag_record dr (text);
-
- dr << "following prerequisite packages were automatically "
- << "built and will no longer be necessary:";
-
- for (const drop_package& dp: pkgs)
- {
- if (dp.reason == drop_reason::prerequisite)
- dr << text << dp.package->name;
- }
- }
-
- drop_prq = yn_prompt ("drop prerequisite packages? [Y/n]", 'y');
- }
-
- t.commit ();
- }
-
- // Print what we are going to do, then ask for the user's confirmation.
- //
- if (o.print_only () || !(o.yes () || o.no ()))
- {
- for (const drop_package& dp: pkgs)
- {
- // Skip prerequisites if we weren't instructed to drop them.
- //
- if (dp.reason == drop_reason::prerequisite && !drop_prq)
- continue;
-
- const shared_ptr<selected_package>& p (dp.package);
-
- if (o.print_only ())
- cout << "drop " << p->name << endl;
- else if (verb)
- text << "drop " << p->name;
- }
-
- if (o.print_only ())
- return 0;
- }
-
- // Ask the user if we should continue.
- //
- if (o.no () || !(o.yes () || yn_prompt ("continue? [Y/n]", 'y')))
- return 1;
-
- // All that's left to do is first disfigure configured packages and
- // then purge all of them. We do both left to right (i.e., from more
- // dependent to less dependent). For disfigure this order is required.
- // For purge, it will be the order closest to the one specified by the
- // user.
- //
- for (const drop_package& dp: pkgs)
- {
- // Skip prerequisites if we weren't instructed to drop them.
- //
- if (dp.reason == drop_reason::prerequisite && !drop_prq)
- continue;
-
- const shared_ptr<selected_package>& p (dp.package);
-
- if (p->state != package_state::configured)
- continue;
-
- // Each package is disfigured in its own transaction, so that we
- // always leave the configuration in a valid state.
- //
- transaction t (db.begin ());
- pkg_disfigure (c, o, t, p); // Commits the transaction.
- assert (p->state == package_state::unpacked);
-
- if (verb)
- text << "disfigured " << p->name;
- }
-
- if (o.disfigure_only ())
- return 0;
-
- // Purge.
- //
- for (const drop_package& dp: pkgs)
- {
- // Skip prerequisites if we weren't instructed to drop them.
- //
- if (dp.reason == drop_reason::prerequisite && !drop_prq)
- continue;
-
- const shared_ptr<selected_package>& p (dp.package);
-
- assert (p->state == package_state::fetched ||
- p->state == package_state::unpacked);
-
- transaction t (db.begin ());
- pkg_purge (c, t, p); // Commits the transaction, p is now transient.
-
- if (verb)
- text << "purged " << p->name;
- }
-
- return 0;
- }
-}