aboutsummaryrefslogtreecommitdiff
path: root/bpkg/pkg-drop.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'bpkg/pkg-drop.cxx')
-rw-r--r--bpkg/pkg-drop.cxx366
1 files changed, 265 insertions, 101 deletions
diff --git a/bpkg/pkg-drop.cxx b/bpkg/pkg-drop.cxx
index 6ea6769..d8fa4ea 100644
--- a/bpkg/pkg-drop.cxx
+++ b/bpkg/pkg-drop.cxx
@@ -7,6 +7,8 @@
#include <list>
#include <iostream> // cout
+#include <libbutl/path-pattern.hxx>
+
#include <bpkg/package.hxx>
#include <bpkg/package-odb.hxx>
#include <bpkg/database.hxx>
@@ -33,6 +35,7 @@ namespace bpkg
struct drop_package
{
+ database& db;
shared_ptr<selected_package> package;
drop_reason reason;
};
@@ -41,45 +44,50 @@ namespace bpkg
//
struct dependent_name
{
+ database& db;
package_name name;
+ database& prq_db;
package_name 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.
+ // 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 and need to either issue
+ // diagnostics and fail or exit with the specified (via --dependent-exit)
+ // code. 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)
+ collect (database& db,
+ shared_ptr<selected_package> p,
+ drop_reason r = drop_reason::user)
{
package_name n (p->name); // Because of move(p) below.
- return map_.emplace (move (n), data_type {end (), {move (p), r}}).second;
+ return map_.emplace (package_key {db, move (n)},
+ data_type {end (), {db, move (p), r}}).second;
}
- // Collect all the dependets of the user selection returning the list
+ // Collect all the dependents of the user selection returning the list
// of their names. Dependents of dependents are collected recursively.
//
dependent_names
- collect_dependents (database& db)
+ collect_dependents ()
{
dependent_names dns;
@@ -91,7 +99,7 @@ namespace bpkg
//
if (dp.reason != drop_reason::dependent &&
dp.package->state == package_state::configured)
- collect_dependents (db, dns, dp.package);
+ collect_dependents (pr.first.db, dp.package, dns);
}
return dns;
@@ -99,21 +107,22 @@ namespace bpkg
void
collect_dependents (database& db,
- dependent_names& dns,
- const shared_ptr<selected_package>& p)
+ const shared_ptr<selected_package>& p,
+ dependent_names& dns)
{
- using query = query<package_dependent>;
-
- for (auto& pd: db.query<package_dependent> (query::name == p->name))
+ for (database& ddb: db.dependent_configs ())
{
- const package_name& dn (pd.name);
-
- if (map_.find (dn) == map_.end ())
+ for (auto& pd: query_dependents_cache (ddb, p->name, db))
{
- 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);
+ const package_name& dn (pd.name);
+
+ if (map_.find (ddb, dn) == map_.end ())
+ {
+ shared_ptr<selected_package> dp (ddb.load<selected_package> (dn));
+ dns.push_back (dependent_name {ddb, dn, db, p->name});
+ collect (ddb, dp, drop_reason::dependent);
+ collect_dependents (ddb, dp, dns);
+ }
}
}
}
@@ -123,7 +132,7 @@ namespace bpkg
// are collected recursively.
//
bool
- collect_prerequisites (database& db)
+ collect_prerequisites ()
{
bool r (false);
@@ -136,29 +145,30 @@ namespace bpkg
if ((dp.reason == drop_reason::user ||
dp.reason == drop_reason::dependent) &&
dp.package->state == package_state::configured)
- r = collect_prerequisites (db, dp.package) || r;
+ r = collect_prerequisites (dp.package) || r;
}
return r;
}
bool
- collect_prerequisites (database& db, const shared_ptr<selected_package>& p)
+ collect_prerequisites (const shared_ptr<selected_package>& p)
{
bool r (false);
for (const auto& pair: p->prerequisites)
{
const lazy_shared_ptr<selected_package>& lpp (pair.first);
+ database& pdb (lpp.database ());
- if (map_.find (lpp.object_id ()) == map_.end ())
+ if (map_.find (pdb, lpp.object_id ()) == map_.end ())
{
shared_ptr<selected_package> pp (lpp.load ());
if (!pp->hold_package) // Prune held packages.
{
- collect (pp, drop_reason::prerequisite);
- collect_prerequisites (db, pp);
+ collect (pdb, pp, drop_reason::prerequisite);
+ collect_prerequisites (pp);
r = true;
}
}
@@ -171,11 +181,11 @@ namespace bpkg
// returning its positions.
//
iterator
- order (const package_name& name)
+ order (database& db, const package_name& name)
{
// Every package that we order should have already been collected.
//
- auto mi (map_.find (name));
+ auto mi (map_.find (db, name));
assert (mi != map_.end ());
// If this package is already in the list, then that would also
@@ -214,13 +224,14 @@ namespace bpkg
{
for (const auto& pair: p->prerequisites)
{
+ database& pdb (pair.first.database ());
const package_name& 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));
+ if (map_.find (pdb, pn) != map_.end ())
+ update (order (pdb, pn));
}
}
@@ -231,7 +242,7 @@ namespace bpkg
// true if any remain.
//
bool
- filter_prerequisites (database& db)
+ filter_prerequisites ()
{
bool r (false);
@@ -244,27 +255,32 @@ namespace bpkg
if (dp.reason == drop_reason::prerequisite)
{
const shared_ptr<selected_package>& p (dp.package);
+ database& db (dp.db);
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))
+ for (database& ddb: db.dependent_configs ())
{
- if (map_.find (pd.name) == map_.end ())
+ for (auto& pd: query_dependents (ddb, p->name, db))
{
- keep = false;
- break;
+ if (map_.find (ddb, pd.name) == map_.end ())
+ {
+ keep = false;
+ break;
+ }
}
+
+ if (!keep)
+ break;
}
if (!keep)
{
i = erase (i);
- map_.erase (p->name);
+ map_.erase (package_key {db, p->name});
continue;
}
@@ -284,15 +300,24 @@ namespace bpkg
drop_package package;
};
- map<package_name, data_type> map_;
+ class package_map: public map<package_key, data_type>
+ {
+ public:
+ using base_type = map<package_key, data_type>;
+
+ iterator
+ find (database& db, const package_name& pn)
+ {
+ return base_type::find (package_key {db, pn});
+ }
+ };
+ package_map map_;
};
// Drop ordered list of packages.
//
static int
- pkg_drop (const dir_path& c,
- const pkg_drop_options& o,
- database& db,
+ pkg_drop (const pkg_drop_options& o,
const drop_packages& pkgs,
bool drop_prq,
bool need_prompt)
@@ -330,11 +355,11 @@ namespace bpkg
}
if (o.print_only ())
- cout << "drop " << p->name << endl;
+ cout << "drop " << p->name << dp.db << endl;
else if (verb)
// Print indented for better visual separation.
//
- text << " drop " << p->name;
+ text << " drop " << p->name << dp.db;
}
if (o.print_only ())
@@ -347,24 +372,50 @@ namespace bpkg
!(o.yes () || !need_prompt || yn_prompt ("continue? [Y/n]", 'y')))
return 1;
+ bool result (verb && !o.no_result ());
+ bool progress (!result &&
+ ((verb == 1 && !o.no_progress () && stderr_term) ||
+ o.progress ()));
+
+ size_t prog_i, prog_n, prog_percent;
+
// 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)
+ // Note: similar code in pkg-build.
+ //
+ auto disfigure_pred = [drop_prq] (const drop_package& dp)
{
// Skip prerequisites if we weren't instructed to drop them.
//
if (dp.reason == drop_reason::prerequisite && !drop_prq)
- continue;
+ return false;
- const shared_ptr<selected_package>& p (dp.package);
+ if (dp.package->state != package_state::configured)
+ return false;
+
+ return true;
+ };
+
+ if (progress)
+ {
+ prog_i = 0;
+ prog_n = static_cast<size_t> (count_if (pkgs.begin (), pkgs.end (),
+ disfigure_pred));
+ prog_percent = 100;
+ }
- if (p->state != package_state::configured)
+ for (const drop_package& dp: pkgs)
+ {
+ if (!disfigure_pred (dp))
continue;
+ database& db (dp.db);
+ const shared_ptr<selected_package>& p (dp.package);
+
// Each package is disfigured in its own transaction, so that we always
// leave the configuration in a valid state.
//
@@ -372,15 +423,46 @@ namespace bpkg
// Commits the transaction.
//
- pkg_disfigure (c, o, t, p, true /* clean */, false /* simulate */);
+ pkg_disfigure (o, db, t,
+ p,
+ true /* clean */,
+ true /* disfigure */,
+ false /* simulate */);
assert (p->state == package_state::unpacked ||
p->state == package_state::transient);
- if (verb && !o.no_result ())
- text << (p->state == package_state::transient
- ? "purged "
- : "disfigured ") << p->name;
+ if (result || progress)
+ {
+ const char* what (p->state == package_state::transient
+ ? "purged"
+ : "disfigured");
+ if (result)
+ text << what << ' ' << p->name << db;
+ else if (progress)
+ {
+ size_t p ((++prog_i * 100) / prog_n);
+
+ if (prog_percent != p)
+ {
+ prog_percent = p;
+
+ diag_progress_lock pl;
+ diag_progress = ' ';
+ diag_progress += to_string (p);
+ diag_progress += "% of packages ";
+ diag_progress += what;
+ }
+ }
+ }
+ }
+
+ // Clear the progress if shown.
+ //
+ if (progress)
+ {
+ diag_progress_lock pl;
+ diag_progress.clear ();
}
if (o.disfigure_only ())
@@ -403,14 +485,16 @@ namespace bpkg
assert (p->state == package_state::fetched ||
p->state == package_state::unpacked);
+ database& db (dp.db);
+
transaction t (db);
// Commits the transaction, p is now transient.
//
- pkg_purge (c, t, p, false /* simulate */);
+ pkg_purge (db, t, p, false /* simulate */);
- if (verb && !o.no_result ())
- text << "purged " << p->name;
+ if (result)
+ text << "purged " << p->name << db;
}
return 0;
@@ -424,19 +508,52 @@ namespace bpkg
const dir_path& c (o.directory ());
l4 ([&]{trace << "configuration: " << c;});
- if (o.yes () && o.no ())
- fail << "both --yes|-y and --no|-n specified";
+ {
+ diag_record dr;
+
+ if (o.yes () && o.no ())
+ {
+ dr << fail << "both --yes|-y and --no|-n specified";
+ }
+ else if (o.drop_dependent () && o.keep_dependent ())
+ {
+ dr << fail << "both --drop-dependent and --keep-dependent|-K "
+ << "specified";
+ }
+ else if (o.drop_dependent () && o.dependent_exit_specified ())
+ {
+ dr << fail << "both --drop-dependent and --dependent-exit "
+ << "specified";
+ }
+ else if (o.keep_dependent () && o.dependent_exit_specified ())
+ {
+ dr << fail << "both --keep-dependent|-K and --dependent-exit "
+ << "specified";
+ }
+ else if (o.all ())
+ {
+ if (o.all_pattern_specified ())
+ dr << fail << "both --all|-a and --all-pattern specified";
+
+ if (args.more ())
+ dr << fail << "both --all|-a and package argument specified";
+ }
+ else if (o.all_pattern_specified ())
+ {
+ if (args.more ())
+ dr << fail << "both --all-pattern and package argument specified";
+ }
+ else if (!args.more ())
+ {
+ dr << fail << "package name argument expected";
+ }
- if (o.drop_dependent () && o.keep_dependent ())
- fail << "both --drop-dependent and --keep-dependent|-K "
- << "specified" <<
- info << "run 'bpkg help pkg-drop' for more information";
+ if (!dr.empty ())
+ dr << info << "run 'bpkg help pkg-drop' for more information";
+ }
- if (!args.more ())
- fail << "package name argument expected" <<
- info << "run 'bpkg help pkg-drop' for more information";
- database db (open (c, trace));
+ database db (c, trace, true /* pre_attach */);
// Note that the session spans all our transactions. The idea here is
// that drop_package objects in the drop_packages list below will be
@@ -464,33 +581,79 @@ namespace bpkg
// by the user.
//
vector<package_name> names;
- while (args.more ())
- {
- package_name n (parse_package_name (args.next (),
- false /* allow_version */));
-
- l4 ([&]{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;
+ auto add = [&names, &pkgs, &db] (shared_ptr<selected_package>&& p)
+ {
+ package_name n (p->name);
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)))
+ if (pkgs.collect (db, move (p)))
names.push_back (move (n));
+ };
+
+ if (o.all () || o.all_pattern_specified ())
+ {
+ using query = query<selected_package>;
+
+ for (shared_ptr<selected_package> p:
+ pointer_result (
+ db.query<selected_package> (query::hold_package)))
+ {
+ l4 ([&]{trace << *p;});
+
+ if (o.all_pattern_specified ())
+ {
+ for (const string& pat: o.all_pattern ())
+ {
+ if (path_match (p->name.string (), pat))
+ {
+ add (move (p));
+ break;
+ }
+ }
+ }
+ else // --all
+ add (move (p));
+ }
+
+ if (names.empty ())
+ info << "nothing to drop";
+ }
+ else
+ {
+ while (args.more ())
+ {
+ package_name n (parse_package_name (args.next (),
+ false /* allow_version */));
+
+ l4 ([&]{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;
+
+ add (move (p));
+ }
}
// 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.
+ // issue diagnostics and fail or silently indicate that with an exit
+ // code.
//
- dependent_names dnames (pkgs.collect_dependents (db));
+ dependent_names dnames (pkgs.collect_dependents ());
if (!dnames.empty () && !o.drop_dependent ())
{
+ if (o.dependent_exit_specified ())
+ {
+ t.commit ();
+ return o.dependent_exit ();
+ }
+
{
diag_record dr;
@@ -503,7 +666,8 @@ namespace bpkg
<< "as well:";
for (const dependent_name& dn: dnames)
- dr << text << dn.name << " (requires " << dn.prq_name << ")";
+ dr << text << dn.name << dn.db << " (requires " << dn.prq_name
+ << dn.prq_db << ")";
}
if (o.yes ())
@@ -526,7 +690,7 @@ namespace bpkg
// on the latter and, if that's the case and "more" cannot be dropped,
// then neither can "less".
//
- pkgs.collect_prerequisites (db);
+ pkgs.collect_prerequisites ();
// Now that we have collected all the packages we could possibly be
// dropping, arrange them in the "dependency order", that is, with
@@ -540,17 +704,17 @@ namespace bpkg
// on which it depends.
//
for (const package_name& n: names)
- pkgs.order (n);
+ pkgs.order (db, n);
for (const dependent_name& dn: dnames)
- pkgs.order (dn.name);
+ pkgs.order (dn.db, 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) &&
- !o.keep_unused () &&
+ if (pkgs.filter_prerequisites () &&
+ !o.keep_unused () &&
!(drop_prq = o.yes ()) && !o.no ())
{
{
@@ -563,7 +727,7 @@ namespace bpkg
{
if (dp.reason == drop_reason::prerequisite)
dr << text << (dp.package->system () ? "sys:" : "")
- << dp.package->name;
+ << dp.package->name << dp.db;
}
}
@@ -576,6 +740,6 @@ namespace bpkg
t.commit ();
}
- return pkg_drop (c, o, db, pkgs, drop_prq, need_prompt);
+ return pkg_drop (o, pkgs, drop_prq, need_prompt);
}
}