// 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 overall plan is as follows: // // - Start the transaction. // // - Remove all the specified repositories recursively in all the // configurations specified by repo_configs (repos) and/or configurations // specified explicitly via UUIDs (config_uuid_repos). // // - Collect the remaining repositories and repository fragments as unmasked. // // - 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/fragment is masked or not in the specific configuration. // void rep_mask (const strings& repos, const strings& config_uuid_repos, linked_databases& current_configs) { 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); // Add a repository from a database, suppressing duplicates. // auto add_repo = [&rs] (database& db, 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)); }; // Collect the repositories masked in all configurations. // 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, &found_repos, i, &add_repo] (shared_ptr&& r) { add_repo (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"; } // Collect the repositories masked in specific configurations. // for (const string& cr: config_uuid_repos) { auto bad = [&cr] (const string& d) { fail << "configuration repository '" << cr << "' cannot be masked: " << d; }; size_t p (cr.find ('=')); if (p == string::npos) bad ("missing '='"); uuid uid; string uid_str (cr, 0, p); try { uid = uuid (uid_str); } catch (const invalid_argument& e) { bad ("invalid configuration uuid '" + uid_str + "': " + e.what ()); } database* db (nullptr); for (database& cdb: current_configs) { if ((db = cdb.try_find_dependency_config (uid)) != nullptr) break; } if (db == nullptr) bad ("no configuration with uuid " + uid.string () + " is linked with " + (current_configs.size () == 1 ? mdb.config_orig.representation () : "specified current configurations")); string rp (cr, p + 1); if (repository_name (rp)) { if (shared_ptr r = db->find (rp)) add_repo (*db, move (r)); else bad ("repository name '" + rp + "' not found in configuration " + uid.string ()); } 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) { bad ("invalid repository location '" + rp + "': " + e.what ()); } bool found (false); for (shared_ptr r: pointer_result ( db->query (query::location.url == rp))) { add_repo (*db, move (r)); found = true; } if (!found) bad ("repository location '" + rp + "' not found in configuration " + uid.string ()); } } // 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); } }