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/rep-mask.cxx | 266 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 bpkg/rep-mask.cxx (limited to 'bpkg/rep-mask.cxx') 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); + } +} -- cgit v1.1