From 636d69c1b740d8975bb7c7a3b518c280ac224545 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 16 Jun 2023 18:17:07 +0300 Subject: Add --mask-repository pkg-build option --- bpkg/database.hxx | 17 ++- bpkg/package-query.cxx | 119 +++++++++++++------- bpkg/package.cxx | 3 +- bpkg/pkg-build-collect.cxx | 28 ++++- bpkg/pkg-build.cli | 13 +++ bpkg/pkg-build.cxx | 53 +++++++-- bpkg/pkg-checkout.cxx | 16 ++- bpkg/pkg-fetch.cxx | 16 ++- bpkg/pkg-unpack.cxx | 4 +- bpkg/rep-mask.cxx | 266 +++++++++++++++++++++++++++++++++++++++++++++ bpkg/rep-mask.hxx | 66 +++++++++++ bpkg/rep-remove.cxx | 105 ++++++++++++------ 12 files changed, 598 insertions(+), 108 deletions(-) create mode 100644 bpkg/rep-mask.cxx create mode 100644 bpkg/rep-mask.hxx (limited to 'bpkg') diff --git a/bpkg/database.hxx b/bpkg/database.hxx index 3887d99..7ef2d40 100644 --- a/bpkg/database.hxx +++ b/bpkg/database.hxx @@ -717,9 +717,10 @@ namespace bpkg public small_vector, V>, 16> { public: - using value_type = pair, V>; - using base_type = small_vector; - using iterator = typename base_type::iterator; + using value_type = pair, V>; + using base_type = small_vector; + using iterator = typename base_type::iterator; + using const_iterator = typename base_type::const_iterator; using base_type::begin; using base_type::end; @@ -734,6 +735,16 @@ namespace bpkg }); } + const_iterator + find (database& db) const + { + return find_if (begin (), end (), + [&db] (const value_type& i) -> bool + { + return i.first == db; + }); + } + pair insert (database& db, V&& v) { diff --git a/bpkg/package-query.cxx b/bpkg/package-query.cxx index 9675d97..9705579 100644 --- a/bpkg/package-query.cxx +++ b/bpkg/package-query.cxx @@ -6,6 +6,7 @@ #include #include #include +#include using namespace std; @@ -159,6 +160,9 @@ namespace bpkg { const lazy_shared_ptr& lrf (pl.repository_fragment); + if (rep_masked_fragment (lrf)) + continue; + // First check the repository itself. // if (lrf.object_id () == rf->name) @@ -175,20 +179,26 @@ namespace bpkg for (const lazy_weak_ptr& r: cs) { - const auto& frs (r.load ()->fragments); + if (!rep_masked (r)) + { + const auto& frs (r.load ()->fragments); - if (find_if (frs.begin (), frs.end (), pr) != frs.end ()) - return lrf.load (); + if (find_if (frs.begin (), frs.end (), pr) != frs.end ()) + return lrf.load (); + } } if (prereq) { for (const lazy_weak_ptr& r: ps) { - const auto& frs (r.load ()->fragments); + if (!rep_masked (r)) + { + const auto& frs (r.load ()->fragments); - if (find_if (frs.begin (), frs.end (), pr) != frs.end ()) - return lrf.load (); + if (find_if (frs.begin (), frs.end (), pr) != frs.end ()) + return lrf.load (); + } } } } @@ -198,14 +208,17 @@ namespace bpkg // for (const lazy_weak_ptr& cr: cs) { - for (const auto& fr: cr.load ()->fragments) + if (!rep_masked (cr)) { - // Should we consider prerequisites of our complements as our - // prerequisites? I'd say not. - // - if (shared_ptr r = - find (fr.fragment.load (), ap, chain, false)) - return r; + for (const auto& fr: cr.load ()->fragments) + { + // Should we consider prerequisites of our complements as our + // prerequisites? I'd say not. + // + if (shared_ptr r = + find (fr.fragment.load (), ap, chain, false)) + return r; + } } } @@ -213,11 +226,14 @@ namespace bpkg { for (const lazy_weak_ptr& pr: ps) { - for (const auto& fr: pr.load ()->fragments) + if (!rep_masked (pr)) { - if (shared_ptr r = - find (fr.fragment.load (), ap, chain, false)) - return r; + for (const auto& fr: pr.load ()->fragments) + { + if (shared_ptr r = + find (fr.fragment.load (), ap, chain, false)) + return r; + } } } } @@ -345,15 +361,20 @@ namespace bpkg for (shared_ptr ap: pointer_result (query_available (db, name, c))) { - // An available package should come from at least one fetched - // repository fragment. + // All repository fragments the package comes from are equally good, + // so we pick the first unmasked one. // - assert (!ap->locations.empty ()); + for (const auto& pl: ap->locations) + { + const lazy_shared_ptr& lrf ( + pl.repository_fragment); - // All repository fragments the package comes from are equally good, so - // we pick the first one. - // - r.emplace_back (move (ap), ap->locations[0].repository_fragment); + if (!rep_masked_fragment (lrf)) + { + r.emplace_back (move (ap), lrf); + break; + } + } } } @@ -415,6 +436,8 @@ namespace bpkg const lazy_shared_ptr& rf, bool prereq) { + assert (!rep_masked_fragment (rf)); + vector> r; database& db (rf.database ()); @@ -438,6 +461,8 @@ namespace bpkg bool prereq, bool revision) { + assert (!rep_masked_fragment (rf)); + // Filter the result based on the repository fragment to which each // version belongs. // @@ -527,17 +552,22 @@ namespace bpkg const shared_ptr& sp) { available_package_id pid (sp->name, sp->version); + const string& cn (sp->repository_fragment.canonical_name ()); + for (database& ddb: dependent_repo_configs (db)) { shared_ptr ap (ddb.find (pid)); if (ap != nullptr && !ap->stub ()) { - if (shared_ptr f = ddb.find ( - sp->repository_fragment.canonical_name ())) - return make_pair (ap, - lazy_shared_ptr (ddb, - move (f))); + if (shared_ptr f = + ddb.find (cn)) + { + if (!rep_masked_fragment (ddb, f)) + return make_pair (ap, + lazy_shared_ptr (ddb, + move (f))); + } } } @@ -592,15 +622,20 @@ namespace bpkg pointer_result ( query_available (db, name, nullopt /* version_constraint */))) { - // An available package should come from at least one fetched - // repository fragment. + // All repository fragments the package comes from are equally good, + // so we pick the first unmasked one. // - assert (!ap->locations.empty ()); + for (const auto& pl: ap->locations) + { + const lazy_shared_ptr& lrf ( + pl.repository_fragment); - // All repository fragments the package comes from are equally good, so - // we pick the first one. - // - r.emplace_back (move (ap), ap->locations[0].repository_fragment); + if (!rep_masked_fragment (lrf)) + { + r.emplace_back (move (ap), lrf); + break; + } + } } } @@ -638,14 +673,18 @@ namespace bpkg // anyway. // lazy_shared_ptr rf; + const string& cn (sp->repository_fragment.canonical_name ()); for (database& ddb: dependent_repo_configs (db)) { - if (shared_ptr f = ddb.find ( - sp->repository_fragment.canonical_name ())) + if (shared_ptr f = + ddb.find (cn)) { - rf = lazy_shared_ptr (ddb, move (f)); - break; + if (!rep_masked_fragment (ddb, f)) + { + rf = lazy_shared_ptr (ddb, move (f)); + break; + } } } diff --git a/bpkg/package.cxx b/bpkg/package.cxx index c98e7a4..56f4221 100644 --- a/bpkg/package.cxx +++ b/bpkg/package.cxx @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -373,7 +374,7 @@ namespace bpkg { const shared_ptr& rf (prf.repository_fragment); - if (rf->location.directory_based ()) + if (!rep_masked_fragment (db, rf) && rf->location.directory_based ()) fail << "external package " << n << '/' << v << " is already available from " << rf->location.canonical_name (); diff --git a/bpkg/pkg-build-collect.cxx b/bpkg/pkg-build-collect.cxx index 86dcb24..036e5b6 100644 --- a/bpkg/pkg-build-collect.cxx +++ b/bpkg/pkg-build-collect.cxx @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -153,7 +154,23 @@ namespace bpkg // If adjustment or orphan, then new and old are the same. // - if (available == nullptr || available->locations.empty ()) + // Note that in the common case a package version doesn't come from too + // many repositories (8). + // + small_vector, 8> locations; + + if (available != nullptr) // Not adjustment? + { + locations.reserve (available->locations.size ()); + + for (const package_location& pl: available->locations) + { + if (!rep_masked_fragment (pl.repository_fragment)) + locations.push_back (pl); + } + } + + if (locations.empty ()) { assert (selected != nullptr); @@ -169,7 +186,7 @@ namespace bpkg } else { - const package_location& pl (available->locations[0]); + const package_location& pl (locations[0]); if (pl.repository_fragment.object_id () == "") // Special root? { @@ -189,7 +206,7 @@ namespace bpkg // Note that such repository fragments are always preferred over // others (see below). // - for (const package_location& pl: available->locations) + for (const package_location& pl: locations) { const repository_location& rl ( pl.repository_fragment.load ()->location); @@ -1127,7 +1144,7 @@ namespace bpkg void build_packages:: enter (package_name name, build_package pkg) { - assert (!pkg.action); + assert (!pkg.action && pkg.repository_fragment == nullptr); database& db (pkg.db); // Save before the move() call. auto p (map_.emplace (package_key {db, move (name)}, @@ -1157,6 +1174,9 @@ namespace bpkg tracer trace ("collect_build"); + assert (pkg.repository_fragment == nullptr || + !rep_masked_fragment (pkg.repository_fragment)); + // See the above notes. // bool recursive (dep_chain != nullptr); diff --git a/bpkg/pkg-build.cli b/bpkg/pkg-build.cli index c2902ea..3db6e9f 100644 --- a/bpkg/pkg-build.cli +++ b/bpkg/pkg-build.cli @@ -398,6 +398,19 @@ namespace bpkg option in \l{bpkg-rep-fetch(1)} for details." } + strings --mask-repository + { + "", + "For the duration of the command execution pretend the specified + repository was removed as if by performing the \cb{rep-remove} + command. The repository can be specified either as a repository name or + as a repository location (URL or a directory path). Note that the + repository's complement and prerequisite repositories are also + considered masked, recursively, unless they are complements and/or + prerequisites of other unmasked repositories. Repeat this option to + mask multiple repositories." + } + bool --no-refinement { "Don't try to refine the configuration by offering to drop any unused diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx index 7d9aa6d..6101826 100644 --- a/bpkg/pkg-build.cxx +++ b/bpkg/pkg-build.cxx @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -93,11 +94,23 @@ namespace bpkg for (const auto& pl: dap->locations) { - shared_ptr rf (pl.repository_fragment.load ()); + const lazy_shared_ptr& lrf ( + pl.repository_fragment); - if (find (rfs.begin (), rfs.end (), rf) == rfs.end ()) - rfs.push_back (move (rf)); + if (!rep_masked_fragment (lrf)) + { + shared_ptr rf (lrf.load ()); + + if (find (rfs.begin (), rfs.end (), rf) == rfs.end ()) + rfs.push_back (move (rf)); + } } + + // Erase the entry from the map if it contains no fragments, which may + // happen if all the available package repositories are masked. + // + if (rfs.empty ()) + r.erase (i); } } } @@ -202,6 +215,11 @@ namespace bpkg // (see dependent_repo_configs() for details) and this exact version is // available from this repository fragment or from its complement. // + // Note that the orphan definition here is stronger than in the rest of the + // code, since we request the available package to also be present in the + // repository fragment. It feels that such a definition aligns better with + // the user expectations about deorphaning. + // static bool orphan_package (database& db, const shared_ptr& sp) { @@ -217,7 +235,7 @@ namespace bpkg const shared_ptr rf ( ddb.find (cn)); - if (rf != nullptr) + if (rf != nullptr && !rep_masked_fragment (ddb, rf)) { auto af ( find_available_one (sp->name, @@ -1574,6 +1592,8 @@ namespace bpkg if (!current (db)) current_configs.push_back (db); } + + t.commit (); } validate_options (o, ""); // Global package options. @@ -1977,6 +1997,12 @@ namespace bpkg string () /* reason for "fetching ..." */); } + // Now, as repo_configs is filled and the repositories are fetched mask + // the repositories, if any. + // + if (o.mask_repository_specified ()) + rep_mask (o.mask_repository ()); + // Expand the package specs into individual package args, parsing them // into the package scheme, name, and version constraint components, and // also saving associated options and configuration variables. @@ -5883,19 +5909,22 @@ namespace bpkg for (const package_location& l: ap->locations) { - const repository_location& rl ( - l.repository_fragment.load ()->location); - - if (!basis || rl.local ()) // First or local? + if (!rep_masked_fragment (l.repository_fragment)) { - basis = rl.basis (); + const repository_location& rl ( + l.repository_fragment.load ()->location); - if (rl.directory_based ()) - break; + if (!basis || rl.local ()) // First or local? + { + basis = rl.basis (); + + if (rl.directory_based ()) + break; + } } } - assert (basis); + assert (basis); // Shouldn't be here otherwise. // All calls commit the transaction. // diff --git a/bpkg/pkg-checkout.cxx b/bpkg/pkg-checkout.cxx index 9793c41..d7b36e4 100644 --- a/bpkg/pkg-checkout.cxx +++ b/bpkg/pkg-checkout.cxx @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -148,14 +149,17 @@ namespace bpkg for (const package_location& l: ap->locations) { - const repository_location& rl (l.repository_fragment.load ()->location); - - if (rl.version_control_based () && (pl == nullptr || rl.local ())) + if (!rep_masked_fragment (l.repository_fragment)) { - pl = &l; + const repository_location& rl (l.repository_fragment.load ()->location); + + if (rl.version_control_based () && (pl == nullptr || rl.local ())) + { + pl = &l; - if (rl.local ()) - break; + if (rl.local ()) + break; + } } } diff --git a/bpkg/pkg-fetch.cxx b/bpkg/pkg-fetch.cxx index 50f0937..009ddf9 100644 --- a/bpkg/pkg-fetch.cxx +++ b/bpkg/pkg-fetch.cxx @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -224,14 +225,17 @@ namespace bpkg for (const package_location& l: ap->locations) { - const repository_location& rl (l.repository_fragment.load ()->location); - - if (rl.archive_based () && (pl == nullptr || rl.local ())) + if (!rep_masked_fragment (l.repository_fragment)) { - pl = &l; + const repository_location& rl (l.repository_fragment.load ()->location); + + if (rl.archive_based () && (pl == nullptr || rl.local ())) + { + pl = &l; - if (rl.local ()) - break; + if (rl.local ()) + break; + } } } diff --git a/bpkg/pkg-unpack.cxx b/bpkg/pkg-unpack.cxx index 5c88cc6..6e99b36 100644 --- a/bpkg/pkg-unpack.cxx +++ b/bpkg/pkg-unpack.cxx @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -304,7 +305,8 @@ namespace bpkg for (const package_location& l: ap->locations) { - if (l.repository_fragment.load ()->location.directory_based ()) + if (!rep_masked_fragment (l.repository_fragment) && + l.repository_fragment.load ()->location.directory_based ()) { pl = &l; break; diff --git a/bpkg/rep-mask.cxx b/bpkg/rep-mask.cxx new file mode 100644 index 0000000..79aef42 --- /dev/null +++ b/bpkg/rep-mask.cxx @@ -0,0 +1,266 @@ +// file : bpkg/rep-mask.cxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#include + +#include +#include +#include +#include +#include // repo_configs +#include // repository_name() + +using namespace std; +using namespace butl; + +namespace bpkg +{ + static optional> unmasked_repositories; + static optional> unmasked_repository_fragments; + + // Note: defined in rep-remove.cxx. + // + void + rep_remove (database&, + transaction&, + const shared_ptr&, + bool mask); + + // The idea here is to start the transaction, remove all the specified + // repositories recursively in all the configurations specified by + // repo_configs, collect the remaining repositories and repository fragments + // as unmasked, and rollback the transaction. Later on, the rep_masked*() + // functions will refer to the configuration-specific unmasked repositories + // and repository fragments lists to decide if the repository is masked or + // not in the specific configuration. + // + void + rep_mask (const strings& repos) + { + tracer trace ("rep_mask"); + + assert (!repo_configs.empty ()); + + database& mdb (repo_configs.front ()); + tracer_guard tg (mdb, trace); + + // Temporary "suspend" session before modifying the database. + // + session* sess (session::current_pointer ()); + if (sess != nullptr) + session::reset_current (); + + vector> rs; + vector found_repos (repos.size(), false); + + transaction t (mdb); + + for (database& db: repo_configs) + { + for (size_t i (0); i != repos.size (); ++i) + { + // Add a repository, suppressing duplicates, and mark it as found. + // + auto add = [&db, &rs, &found_repos, i] (shared_ptr&& r) + { + if (find_if (rs.begin (), rs.end (), + [&db, &r] (const lazy_weak_ptr& lr) + { + return lr.database () == db && + lr.object_id () == r->name; + }) == rs.end ()) + rs.emplace_back (db, move (r)); + + found_repos[i] = true; + }; + + const string& rp (repos[i]); + + if (repository_name (rp)) + { + if (shared_ptr r = db.find (rp)) + add (move (r)); + } + else + { + using query = query; + + // Verify that the repository URL is not misspelled or empty. + // + try + { + repository_url u (rp); + assert (!u.empty ()); + } + catch (const invalid_argument& e) + { + fail << "repository '" << rp << "' cannot be masked: " + << "invalid repository location: " << e; + } + + for (shared_ptr r: + pointer_result ( + db.query (query::location.url == rp))) + add (move (r)); + } + } + } + + // Fail if any of the specified repositories is not found in any database. + // + for (size_t i (0); i != repos.size (); ++i) + { + if (!found_repos[i]) + fail << "repository '" << repos[i] << "' cannot be masked: not found"; + } + + // First, remove the repository references from the dependent repository + // fragments. Note that rep_remove() removes the dangling repositories. + // + // Note that for efficiency we un-reference all the repositories before + // starting to delete them. + // + for (const lazy_weak_ptr& r: rs) + { + database& db (r.database ()); + const string& nm (r.object_id ()); + + // Remove from complements of the dependents. + // + for (const auto& rf: db.query ( + query::complement::name == nm)) + { + const shared_ptr& f (rf); + repository_fragment::dependencies& cs (f->complements); + + auto i (cs.find (r)); + assert (i != cs.end ()); + + cs.erase (i); + db.update (f); + } + + // Remove from prerequisites of the dependents. + // + for (const auto& rf: + db.query ( + query::prerequisite::name == + nm)) + { + const shared_ptr& f (rf); + repository_fragment::dependencies& ps (f->prerequisites); + + auto i (ps.find (r)); + assert (i != ps.end ()); + + ps.erase (i); + db.update (f); + } + } + + // Remove the now dangling repositories. + // + for (const lazy_weak_ptr& r: rs) + rep_remove (r.database (), t, r.load (), true /* mask */); + + // Collect the repositories and fragments which have remained after the + // removal. + // + unmasked_repositories = database_map (); + unmasked_repository_fragments = database_map (); + + for (database& db: repo_configs) + { + // Add the repository location canonical name to the database-specific + // unmasked repositories or repository fragments lists. Note that + // repository location is used only for tracing. + // + auto add = [&db, &trace] (string&& n, + database_map& m, + const repository_location& loc, + const char* what) + { + auto i (m.find (db)); + if (i == m.end ()) + i = m.insert (db, strings ()).first; + + l4 ([&]{trace << "unmasked " << what << ": '" << n + << "' '" << loc.url () << "'" << db;}); + + i->second.push_back (move (n)); + }; + + for (shared_ptr r: pointer_result (db.query ())) + add (move (r->name), + *unmasked_repositories, + r->location, + "repository"); + + for (shared_ptr f: + pointer_result (db.query ())) + add (move (f->name), + *unmasked_repository_fragments, + f->location, + "repository fragment"); + } + + // Rollback the transaction and restore the session, if present. + // + t.rollback (); + + if (sess != nullptr) + session::current_pointer (sess); + } + + static inline bool + masked (database& db, + const string& name, + const optional>& m) + { + if (!m) + return false; + + auto i (m->find (db)); + if (i != m->end ()) + { + const strings& ns (i->second); + return find (ns.begin (), ns.end (), name) == ns.end (); + } + + return true; + } + + bool + rep_masked (database& db, const shared_ptr& r) + { + return masked (db, r->name, unmasked_repositories); + } + + bool + rep_masked (const lazy_weak_ptr& r) + { + // Should not be transient. + // + assert (!(r.lock ().get_eager () != nullptr && !r.loaded ())); + + return masked (r.database (), r.object_id (), unmasked_repositories); + } + + bool + rep_masked_fragment (database& db, const shared_ptr& f) + { + return masked (db, f->name, unmasked_repository_fragments); + } + + bool + rep_masked_fragment (const lazy_shared_ptr& f) + { + // Should not be transient. + // + assert (!(f.get_eager () != nullptr && !f.loaded ())); + + return masked (f.database (), + f.object_id (), + unmasked_repository_fragments); + } +} diff --git a/bpkg/rep-mask.hxx b/bpkg/rep-mask.hxx new file mode 100644 index 0000000..dd9cedf --- /dev/null +++ b/bpkg/rep-mask.hxx @@ -0,0 +1,66 @@ +// file : bpkg/rep-mask.hxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#ifndef BPKG_REP_MASK_HXX +#define BPKG_REP_MASK_HXX + +#include +#include // database, repository +#include + +namespace bpkg +{ + // Note: not a command (at least not yet). + // + // Mask repositories to pretend they don't exist in the configurations that + // are used as the repository information sources (repo_configs). Also mask + // their complement and prerequisite repositories, recursively, excluding + // those which are complements and/or prerequisites of other unmasked + // repositories. The repositories can be specified either as repository + // location canonical names or URLs. Issue diagnostics and fail if any of + // the specified repositories don't exist in any configuration. + // + // Notes: + // + // - A repository may end up being masked in one configuration but not in + // another. + // + // - Using a canonical name potentially masks repositories with different + // URLs in different configurations (think of local and remote pkg + // repository locations). + // + // - Using a URL potentially masks repositories with different canonical + // names in the same configuration (think of directory and local git + // repository locations). + // + // NOTE: repo_configs needs to be filled prior to the function call. + // + void + rep_mask (const strings&); + + // Return true if a repository is masked in the specified configuration. + // + bool + rep_masked (database&, const shared_ptr&); + + // Note: the argument must refer to a persistent object which incorporates + // the configuration information (database). + // + bool + rep_masked (const lazy_weak_ptr&); + + // Return true if a repository fragment in the specified configuration + // belongs to the masked repositories only and is therefore masked (see + // package.hxx for the fragment/repository relationship details). + // + bool + rep_masked_fragment (database&, const shared_ptr&); + + // Note: the argument must refer to a persistent object which incorporates + // the configuration information (database). + // + bool + rep_masked_fragment (const lazy_shared_ptr&); +} + +#endif // BPKG_REP_MASK_HXX diff --git a/bpkg/rep-remove.cxx b/bpkg/rep-remove.cxx index 700534f..ad10f56 100644 --- a/bpkg/rep-remove.cxx +++ b/bpkg/rep-remove.cxx @@ -143,8 +143,20 @@ namespace bpkg rm_r (td, true /* dir_itself */, 3, rm_error_mode::warn); } + static void + rep_remove_fragment (database&, + transaction&, + const shared_ptr&, + bool mask); + + // In the mask repositories mode don't cleanup the repository state in the + // filesystem (see rep-mask.hxx for the details on repository masking). + // void - rep_remove (database& db, transaction& t, const shared_ptr& r) + rep_remove (database& db, + transaction& t, + const shared_ptr& r, + bool mask) { assert (!r->name.empty ()); // Can't be the root repository. @@ -164,7 +176,7 @@ namespace bpkg // Remove dangling repository fragments. // for (const repository::fragment_type& fr: r->fragments) - rep_remove_fragment (db, t, fr.fragment.load ()); + rep_remove_fragment (db, t, fr.fragment.load (), mask); // If there are no repositories stayed in the database then no repository // fragments should stay either. @@ -172,8 +184,8 @@ namespace bpkg if (db.query_value () == 0) assert (db.query_value () == 0); - // Cleanup the repository state if present and there are no more - // repositories referring this state. + // Unless in the mask repositories mode, cleanup the repository state if + // present and there are no more repositories referring this state. // // Note that this step is irreversible on failure. If something goes wrong // we will end up with a state-less fetched repository and the @@ -184,44 +196,58 @@ namespace bpkg // then remove them after committing the transaction. Though, we still may // fail in the middle due to the filesystem error. // - dir_path d (repository_state (r->location)); - - if (!d.empty ()) + if (!mask) { - dir_path sd (db.config_orig / repos_dir / d); + dir_path d (repository_state (r->location)); - if (exists (sd)) + if (!d.empty ()) { - // There is no way to get the list of repositories that share this - // state other than traversing all repositories of this type. - // - bool rm (true); - - using query = query; + dir_path sd (db.config_orig / repos_dir / d); - for (shared_ptr rp: - pointer_result ( - db.query ( - query::name != "" && - query::location.type == to_string (r->location.type ())))) + if (exists (sd)) { - if (repository_state (rp->location) == d) + // There is no way to get the list of repositories that share this + // state other than traversing all repositories of this type. + // + bool rm (true); + + using query = query; + + for (shared_ptr rp: + pointer_result ( + db.query ( + query::name != "" && + query::location.type == to_string (r->location.type ())))) { - rm = false; - break; + if (repository_state (rp->location) == d) + { + rm = false; + break; + } } - } - if (rm) - rmdir (db.config_orig, sd); + if (rm) + rmdir (db.config_orig, sd); + } } } } void + rep_remove (database& db, transaction& t, const shared_ptr& r) + { + rep_remove (db, t, r, false /* mask */); + } + + // In the mask repositories mode don't remove the repository fragment from + // locations of the available packages it contains (see rep-mask.hxx for the + // details on repository masking). + // + static void rep_remove_fragment (database& db, transaction& t, - const shared_ptr& rf) + const shared_ptr& rf, + bool mask) { tracer trace ("rep_remove_fragment"); @@ -235,11 +261,12 @@ namespace bpkg "fragment=" + query::_val (rf->name)) != 0) return; - // Remove the repository fragment from locations of the available packages - // it contains. Note that this must be done before the repository fragment - // removal. + // Unless in the mask repositories mode, remove the repository fragment + // from locations of the available packages it contains. Note that this + // must be done before the repository fragment removal. // - rep_remove_package_locations (db, t, rf->name); + if (!mask) + rep_remove_package_locations (db, t, rf->name); // Remove the repository fragment. // @@ -255,8 +282,8 @@ namespace bpkg // if (db.query_value () == 0) { - assert (db.query_value () == 0); - assert (db.query_value () == 0); + assert (db.query_value () == 0); + assert (mask || db.query_value () == 0); } // Remove dangling complements and prerequisites. @@ -264,10 +291,10 @@ namespace bpkg // Prior to removing a prerequisite/complement we need to make sure it // still exists, which may not be the case due to the dependency cycle. // - auto remove = [&db, &t] (const lazy_weak_ptr& rp) + auto remove = [&db, &t, mask] (const lazy_weak_ptr& rp) { if (shared_ptr r = db.find (rp.object_id ())) - rep_remove (db, t, r); + rep_remove (db, t, r, mask); }; for (const lazy_weak_ptr& cr: rf->complements) @@ -284,6 +311,14 @@ namespace bpkg } void + rep_remove_fragment (database& db, + transaction& t, + const shared_ptr& rf) + { + return rep_remove_fragment (db, t, rf, false /* mask */); + } + + void rep_remove_clean (const common_options& o, database& db, bool quiet) { tracer trace ("rep_remove_clean"); -- cgit v1.1