aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2023-06-16 18:17:07 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2023-06-19 13:49:27 +0300
commit636d69c1b740d8975bb7c7a3b518c280ac224545 (patch)
treebe9552beaea26413280bac12dabf07127bec4e28
parent5e6c781d4a1fafdb5e81c17b792cd36b7433ce90 (diff)
Add --mask-repository pkg-build option
-rw-r--r--bpkg/database.hxx17
-rw-r--r--bpkg/package-query.cxx119
-rw-r--r--bpkg/package.cxx3
-rw-r--r--bpkg/pkg-build-collect.cxx28
-rw-r--r--bpkg/pkg-build.cli13
-rw-r--r--bpkg/pkg-build.cxx53
-rw-r--r--bpkg/pkg-checkout.cxx16
-rw-r--r--bpkg/pkg-fetch.cxx16
-rw-r--r--bpkg/pkg-unpack.cxx4
-rw-r--r--bpkg/rep-mask.cxx266
-rw-r--r--bpkg/rep-mask.hxx66
-rw-r--r--bpkg/rep-remove.cxx105
-rw-r--r--tests/pkg-build.testscript778
13 files changed, 1376 insertions, 108 deletions
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<pair<reference_wrapper<database>, V>, 16>
{
public:
- using value_type = pair<reference_wrapper<database>, V>;
- using base_type = small_vector<value_type, 16>;
- using iterator = typename base_type::iterator;
+ using value_type = pair<reference_wrapper<database>, V>;
+ using base_type = small_vector<value_type, 16>;
+ 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<iterator, bool>
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 <bpkg/package.hxx>
#include <bpkg/package-odb.hxx>
#include <bpkg/database.hxx>
+#include <bpkg/rep-mask.hxx>
using namespace std;
@@ -159,6 +160,9 @@ namespace bpkg
{
const lazy_shared_ptr<repository_fragment>& 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<repository>& 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<repository>& 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<repository>& 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<repository_fragment> 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<repository_fragment> r =
+ find (fr.fragment.load (), ap, chain, false))
+ return r;
+ }
}
}
@@ -213,11 +226,14 @@ namespace bpkg
{
for (const lazy_weak_ptr<repository>& pr: ps)
{
- for (const auto& fr: pr.load ()->fragments)
+ if (!rep_masked (pr))
{
- if (shared_ptr<repository_fragment> r =
- find (fr.fragment.load (), ap, chain, false))
- return r;
+ for (const auto& fr: pr.load ()->fragments)
+ {
+ if (shared_ptr<repository_fragment> r =
+ find (fr.fragment.load (), ap, chain, false))
+ return r;
+ }
}
}
}
@@ -345,15 +361,20 @@ namespace bpkg
for (shared_ptr<available_package> 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<repository_fragment>& 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<repository_fragment>& rf,
bool prereq)
{
+ assert (!rep_masked_fragment (rf));
+
vector<shared_ptr<available_package>> 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<selected_package>& 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<available_package> ap (ddb.find<available_package> (pid));
if (ap != nullptr && !ap->stub ())
{
- if (shared_ptr<repository_fragment> f = ddb.find<repository_fragment> (
- sp->repository_fragment.canonical_name ()))
- return make_pair (ap,
- lazy_shared_ptr<repository_fragment> (ddb,
- move (f)));
+ if (shared_ptr<repository_fragment> f =
+ ddb.find<repository_fragment> (cn))
+ {
+ if (!rep_masked_fragment (ddb, f))
+ return make_pair (ap,
+ lazy_shared_ptr<repository_fragment> (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<repository_fragment>& 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<repository_fragment> rf;
+ const string& cn (sp->repository_fragment.canonical_name ());
for (database& ddb: dependent_repo_configs (db))
{
- if (shared_ptr<repository_fragment> f = ddb.find<repository_fragment> (
- sp->repository_fragment.canonical_name ()))
+ if (shared_ptr<repository_fragment> f =
+ ddb.find<repository_fragment> (cn))
{
- rf = lazy_shared_ptr<repository_fragment> (ddb, move (f));
- break;
+ if (!rep_masked_fragment (ddb, f))
+ {
+ rf = lazy_shared_ptr<repository_fragment> (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 <bpkg/database.hxx>
#include <bpkg/checksum.hxx>
+#include <bpkg/rep-mask.hxx>
#include <bpkg/pkg-verify.hxx>
#include <bpkg/diagnostics.hxx>
#include <bpkg/satisfaction.hxx>
@@ -373,7 +374,7 @@ namespace bpkg
{
const shared_ptr<repository_fragment>& 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 <bpkg/package.hxx>
#include <bpkg/package-odb.hxx>
#include <bpkg/database.hxx>
+#include <bpkg/rep-mask.hxx>
#include <bpkg/diagnostics.hxx>
#include <bpkg/satisfaction.hxx>
@@ -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<reference_wrapper<const package_location>, 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
+ {
+ "<rep>",
+ "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 <bpkg/common-options.hxx>
#include <bpkg/cfg-link.hxx>
+#include <bpkg/rep-mask.hxx>
#include <bpkg/pkg-purge.hxx>
#include <bpkg/pkg-fetch.hxx>
#include <bpkg/rep-fetch.hxx>
@@ -93,11 +94,23 @@ namespace bpkg
for (const auto& pl: dap->locations)
{
- shared_ptr<repository_fragment> rf (pl.repository_fragment.load ());
+ const lazy_shared_ptr<repository_fragment>& 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<repository_fragment> 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<selected_package>& sp)
{
@@ -217,7 +235,7 @@ namespace bpkg
const shared_ptr<repository_fragment> rf (
ddb.find<repository_fragment> (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 <bpkg/package-odb.hxx>
#include <bpkg/database.hxx>
#include <bpkg/checksum.hxx>
+#include <bpkg/rep-mask.hxx>
#include <bpkg/diagnostics.hxx>
#include <bpkg/manifest-utility.hxx>
@@ -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 <bpkg/package-odb.hxx>
#include <bpkg/checksum.hxx>
#include <bpkg/database.hxx>
+#include <bpkg/rep-mask.hxx>
#include <bpkg/diagnostics.hxx>
#include <bpkg/manifest-utility.hxx>
@@ -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 <bpkg/package-odb.hxx>
#include <bpkg/database.hxx>
#include <bpkg/checksum.hxx>
+#include <bpkg/rep-mask.hxx>
#include <bpkg/diagnostics.hxx>
#include <bpkg/manifest-utility.hxx>
@@ -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 <bpkg/rep-mask.hxx>
+
+#include <bpkg/package.hxx>
+#include <bpkg/package-odb.hxx>
+#include <bpkg/database.hxx>
+#include <bpkg/diagnostics.hxx>
+#include <bpkg/package-query.hxx> // repo_configs
+#include <bpkg/manifest-utility.hxx> // repository_name()
+
+using namespace std;
+using namespace butl;
+
+namespace bpkg
+{
+ static optional<database_map<strings>> unmasked_repositories;
+ static optional<database_map<strings>> unmasked_repository_fragments;
+
+ // Note: defined in rep-remove.cxx.
+ //
+ void
+ rep_remove (database&,
+ transaction&,
+ const shared_ptr<repository>&,
+ 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<lazy_weak_ptr<repository>> rs;
+ vector<bool> 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<repository>&& r)
+ {
+ if (find_if (rs.begin (), rs.end (),
+ [&db, &r] (const lazy_weak_ptr<repository>& 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<repository> r = db.find<repository> (rp))
+ add (move (r));
+ }
+ else
+ {
+ using query = query<repository>;
+
+ // 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<repository> r:
+ pointer_result (
+ db.query<repository> (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<repository>& r: rs)
+ {
+ database& db (r.database ());
+ const string& nm (r.object_id ());
+
+ // Remove from complements of the dependents.
+ //
+ for (const auto& rf: db.query<repository_complement_dependent> (
+ query<repository_complement_dependent>::complement::name == nm))
+ {
+ const shared_ptr<repository_fragment>& 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<repository_prerequisite_dependent> (
+ query<repository_prerequisite_dependent>::prerequisite::name ==
+ nm))
+ {
+ const shared_ptr<repository_fragment>& 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<repository>& 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<strings> ();
+ unmasked_repository_fragments = database_map<strings> ();
+
+ 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<strings>& 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<repository> r: pointer_result (db.query<repository> ()))
+ add (move (r->name),
+ *unmasked_repositories,
+ r->location,
+ "repository");
+
+ for (shared_ptr<repository_fragment> f:
+ pointer_result (db.query<repository_fragment> ()))
+ 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<database_map<strings>>& 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<repository>& r)
+ {
+ return masked (db, r->name, unmasked_repositories);
+ }
+
+ bool
+ rep_masked (const lazy_weak_ptr<repository>& 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<repository_fragment>& f)
+ {
+ return masked (db, f->name, unmasked_repository_fragments);
+ }
+
+ bool
+ rep_masked_fragment (const lazy_shared_ptr<repository_fragment>& 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 <bpkg/types.hxx>
+#include <bpkg/forward.hxx> // database, repository
+#include <bpkg/utility.hxx>
+
+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<repository>&);
+
+ // Note: the argument must refer to a persistent object which incorporates
+ // the configuration information (database).
+ //
+ bool
+ rep_masked (const lazy_weak_ptr<repository>&);
+
+ // 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<repository_fragment>&);
+
+ // Note: the argument must refer to a persistent object which incorporates
+ // the configuration information (database).
+ //
+ bool
+ rep_masked_fragment (const lazy_shared_ptr<repository_fragment>&);
+}
+
+#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<repository_fragment>&,
+ 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<repository>& r)
+ rep_remove (database& db,
+ transaction& t,
+ const shared_ptr<repository>& 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<repository_count> () == 0)
assert (db.query_value<repository_fragment_count> () == 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<repository>;
+ dir_path sd (db.config_orig / repos_dir / d);
- for (shared_ptr<repository> rp:
- pointer_result (
- db.query<repository> (
- 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<repository>;
+
+ for (shared_ptr<repository> rp:
+ pointer_result (
+ db.query<repository> (
+ 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<repository>& 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<repository_fragment>& rf)
+ const shared_ptr<repository_fragment>& 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<repository_fragment_count> () == 0)
{
- assert (db.query_value<repository_count> () == 0);
- assert (db.query_value<available_package_count> () == 0);
+ assert (db.query_value<repository_count> () == 0);
+ assert (mask || db.query_value<available_package_count> () == 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<repository>& rp)
+ auto remove = [&db, &t, mask] (const lazy_weak_ptr<repository>& rp)
{
if (shared_ptr<repository> r = db.find<repository> (rp.object_id ()))
- rep_remove (db, t, r);
+ rep_remove (db, t, r, mask);
};
for (const lazy_weak_ptr<repository>& cr: rf->complements)
@@ -284,6 +311,14 @@ namespace bpkg
}
void
+ rep_remove_fragment (database& db,
+ transaction& t,
+ const shared_ptr<repository_fragment>& 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");
diff --git a/tests/pkg-build.testscript b/tests/pkg-build.testscript
index a00ba5a..c3ae96f 100644
--- a/tests/pkg-build.testscript
+++ b/tests/pkg-build.testscript
@@ -582,6 +582,20 @@ test.arguments += --sys-no-query
info: use 'bpkg rep-add' to add a repository
EOE
+ : mask-repository-not-found
+ :
+ $clone_root_cfg;
+ $* --mask-repository 'https://example.com/1' libfoo 2>>EOE != 0
+ error: repository 'https://example.com/1' cannot be masked: not found
+ EOE
+
+ : mask-repository-empty
+ :
+ $clone_root_cfg;
+ $* --mask-repository '' libfoo 2>>EOE != 0
+ error: repository '' cannot be masked: invalid repository location: empty URL
+ EOE
+
: archive
:
$clone_root_cfg;
@@ -1811,6 +1825,12 @@ test.arguments += --sys-no-query
$pkg_status libbar >'!libbar configured 1.2.0';
+ # While at it, test using --mask-repository instead of rep-remove.
+ #
+ $* --upgrade --mask-repository $rep/t2 --mask-repository $rep/t5 2>>/EOE != 0;
+ error: libbar is not available
+ EOE
+
$rep_remove $rep/t2 $rep/t5;
$* --upgrade 2>>/EOE != 0;
@@ -3240,6 +3260,71 @@ test.arguments += --sys-no-query
$pkg_drop libbar libbox
}
+ : satisfy-masked
+ :
+ : As above but using --mask-repository instead of rep-remove.
+ :
+ {
+ $clone_cfg;
+ $rep_fetch $rep/t0b;
+
+ $* libbar/0.0.1 2>!;
+
+ $pkg_status libbaz >'libbaz configured 0.0.1 available 0.1.0 0.0.4 0.0.3 0.0.2';
+
+ $* libbar/0.0.2 ?libbaz 2>>EOE;
+ disfigured libbar/0.0.1
+ disfigured libbaz/0.0.1
+ disfigured libfox/0.0.1
+ purged libfox/0.0.1
+ fetched libfoo/1.0.0
+ unpacked libfoo/1.0.0
+ fetched libbaz/0.0.2
+ unpacked libbaz/0.0.2
+ fetched libbar/0.0.2
+ unpacked libbar/0.0.2
+ configured libfoo/1.0.0
+ configured libbaz/0.0.2
+ configured libbar/0.0.2
+ EOE
+
+ $pkg_status libbaz >'libbaz configured 0.0.2 available 0.1.0 0.0.4 0.0.3';
+
+ # Test that the selected package, that is "better" than the available
+ # one, is left.
+ #
+ $* --mask-repository $rep/t0b libbox ?libbaz 2>>EOE;
+ fetched libbox/0.0.1
+ unpacked libbox/0.0.1
+ configured libbox/0.0.1
+ EOE
+
+ $pkg_status libbaz >'libbaz configured 0.0.2 available 0.1.0 0.0.4 0.0.3';
+
+ # Test that the selected package is left as there is no satisfactory
+ # available package.
+ #
+ $* --mask-repository $rep/t0b --mask-repository $rep/t0a ?libbaz;
+
+ # Test that the above behavior is not triggered for the system package.
+ #
+ $* --mask-repository $rep/t0b --mask-repository $rep/t0a '?sys:libbaz' 2>>EOE;
+ disfigured libbar/0.0.2
+ disfigured libbox/0.0.1
+ disfigured libbaz/0.0.2
+ disfigured libfoo/1.0.0
+ purged libfoo/1.0.0
+ purged libbaz/0.0.2
+ configured sys:libbaz/*
+ configured libbox/0.0.1
+ configured libbar/0.0.2
+ EOE
+
+ $pkg_status libbaz >'libbaz configured,system !* available 0.1.0 0.0.4 0.0.3 0.0.2 0.0.1';
+
+ $pkg_drop libbar libbox
+ }
+
: unsatisfied
:
{
@@ -3761,6 +3846,17 @@ test.arguments += --sys-no-query
$* libbar --recursive --yes
}
+ : unavailable-masked
+ :
+ : As above but using --mask-repository instead of rep-remove.
+ :
+ {
+ $clone_cfg;
+
+ $* libbar --mask-repository $rep/t0a --mask-repository $rep/t0b \
+ --mask-repository $rep/t0c --recursive --yes
+ }
+
-$pkg_drop libbar libbaz libfoo
}
@@ -14194,6 +14290,32 @@ test.arguments += --sys-no-query
EOE
}
+ : unavailable-masked
+ :
+ : As above but using --mask-repository instead of rep-remove.
+ :
+ {
+ $clone_cfg;
+
+ $* '?sys:libbaz/0.0.3' 2>>EOE;
+ disfigured libbox/0.0.1
+ disfigured libfix/0.0.3
+ disfigured libbaz/0.0.3
+ disfigured libfoo/1.0.0
+ purged libfoo/1.0.0
+ purged libbaz/0.0.3
+ configured sys:libbaz/0.0.3
+ configured libfix/0.0.3
+ configured libbox/0.0.1
+ EOE
+
+ $rep_fetch $rep/t0b;
+
+ $* --mask-repository $rep/t0c ?libbaz --patch --yes 2>>EOE != 0
+ error: patch version for sys:libbaz/0.0.3 is not available from its dependents' repositories
+ EOE
+ }
+
-$pkg_drop libbox libfix libbaz libfoo
}
@@ -16358,6 +16480,64 @@ else
EOO
}
+ : orphan-repointed-masked
+ :
+ : As above but using --mask-repository instead of rep-remove.
+ :
+ {
+ $cfg_create -d h1 --type host --name h1 &h1/***;
+ $cfg_create -d h2 --type host --name h2 &h2/***;
+
+ $cfg_link -d h1 h2;
+
+ $rep_add -d h1 $rep/t7b && $rep_fetch -d h1;
+
+ test.arguments = $regex.apply($test.arguments, cfg, h1);
+
+ $* foo --yes 2>!;
+
+ $rep_add -d h1 $rep/t7a && $rep_fetch -d h1;
+ $rep_add -d h2 $rep/t7a && $rep_fetch -d h2;
+
+ $* libbaz +{ --config-name h2 } --mask-repository $rep/t7b 2>>EOE != 0;
+ error: package foo/1.1.0 is orphaned
+ info: explicitly upgrade it to a new version
+ info: while satisfying foo/1.1.0
+ EOE
+
+ # While at it, test foo deorphaning.
+ #
+ $* foo +{ --deorphan } libbaz +{ --config-name h2 } --yes --plan "" \
+ --mask-repository $rep/t7b 2>>~%EOE%;
+ % new libbuild2-bar/1.0.0 \[h1.\.bpkg.build2.\] \(required by foo\)%
+ % new libbaz/1.0.0 \[h2.\]%
+ drop libbaz/1.1.0 (unused)
+ deorphan/downgrade foo/1.0.0
+ disfigured foo/1.1.0
+ disfigured libbaz/1.1.0
+ %fetched libbuild2-bar/1.0.0 \[h1.\.bpkg.build2.\]%
+ %unpacked libbuild2-bar/1.0.0 \[h1.\.bpkg.build2.\]%
+ %fetched libbaz/1.0.0 \[h2.\]%
+ %unpacked libbaz/1.0.0 \[h2.\]%
+ purged libbaz/1.1.0
+ fetched foo/1.0.0
+ unpacked foo/1.0.0
+ %configured libbuild2-bar/1.0.0 \[h1.\.bpkg.build2.\]%
+ %configured libbaz/1.0.0 \[h2.\]%
+ configured foo/1.0.0
+ %info: h2.+libbaz-1.0.0.+ is up to date%
+ %info: h1.+foo-1.0.0.+ is up to date%
+ %updated libbaz/1.0.0 \[h2.\]%
+ updated foo/1.0.0
+ EOE
+
+ $pkg_status -d h1 -r >>/EOO
+ !foo configured 1.0.0 available 1.1.0
+ !libbaz [h2/] configured 1.0.0 available 1.1.0
+ libbuild2-bar [h1/.bpkg/build2/] configured 1.0.0
+ EOO
+ }
+
: unhold-repointed
:
{
@@ -17777,6 +17957,146 @@ else
$pkg_drop libbar
}
+ : basics-masked
+ :
+ : As above but using --mask-repository instead of rep-remove.
+ :
+ {
+ $clone_root_cfg;
+
+ cp -r $src/libfoo-1.1.0 libfoo;
+ $rep_add --type dir libfoo/ && $rep_fetch;
+
+ $* libfoo 2>!;
+ $rep_fetch $rep/t4b;
+
+ $* libbar 2>!;
+
+ $pkg_status -r libbar >>EOO;
+ !libbar configured 1.1.0
+ !libfoo configured 1.1.0
+ EOO
+
+ echo "" >+ libfoo/manifest;
+ $rep_fetch;
+
+ $pkg_status -r libbar >>EOO;
+ !libbar configured 1.1.0
+ !libfoo configured 1.1.0 available 1.1.0#1
+ EOO
+
+ # Deorphan libfoo/1.1.0 to libfoo/1.1.0#1.
+ #
+ # Note that libfoo/1.1.0 is considered as an orphan since its version
+ # is replaced with 1.1.0#1 in its existing repository fragment. This is
+ # in contrast to the subsequent tests where the package repository is
+ # removed.
+ #
+ $* --deorphan libfoo 2>>~%EOE%;
+ deorphan/upgrade libfoo/1.1.0#1
+ reconfigure libbar (dependent of libfoo)
+ disfigured libbar/1.1.0
+ disfigured libfoo/1.1.0
+ using libfoo/1.1.0#1 (external)
+ configured libfoo/1.1.0#1
+ configured libbar/1.1.0
+ %info: .+libfoo.+ is up to date%
+ %info: .+libbar-1.1.0.+ is up to date%
+ updated libfoo/1.1.0#1
+ updated libbar/1.1.0
+ EOE
+
+ $pkg_status -r libbar >>EOO;
+ !libbar configured 1.1.0
+ !libfoo configured 1.1.0#1
+ EOO
+
+ # Noop.
+ #
+ $* --deorphan libfoo 2>'info: nothing to build';
+
+ $pkg_status -r libbar >>EOO;
+ !libbar configured 1.1.0
+ !libfoo configured 1.1.0#1
+ EOO
+
+ # Deorphan libfoo/1.1.0#1 to ?libfoo/1.1.0.
+ #
+ # Note that on Windows the local repository canonical name path part
+ # is converted to lower case.
+ #
+ cn = "$canonicalize([dir_path] $~/libfoo)";
+ if! $posix
+ cn = $lcase([string] $cn)
+ end;
+ cn = "dir:$cn";
+
+ $* --mask-repository $cn --deorphan ?libfoo 2>>~%EOE%;
+ deorphan/downgrade/unhold libfoo/1.1.0
+ reconfigure libbar (dependent of libfoo)
+ disfigured libbar/1.1.0
+ disfigured libfoo/1.1.0#1
+ fetched libfoo/1.1.0
+ unpacked libfoo/1.1.0
+ configured libfoo/1.1.0
+ configured libbar/1.1.0
+ %info: .+libbar-1.1.0.+ is up to date%
+ updated libbar/1.1.0
+ EOE
+
+ $pkg_status -r libbar >>EOO;
+ !libbar configured 1.1.0
+ libfoo configured 1.1.0 available 1.1.0#1
+ EOO
+
+ # Noop.
+ #
+ $* --mask-repository $cn --deorphan ?libfoo;
+
+ $pkg_status -r libbar >>EOO;
+ !libbar configured 1.1.0
+ libfoo configured 1.1.0 available 1.1.0#1
+ EOO
+
+ # Deorphan libfoo/1.1.0#1 to ?libfoo/1.1.0.
+ #
+ $* libfoo 2>!;
+
+ $pkg_status -r libbar >>EOO;
+ !libbar configured 1.1.0
+ !libfoo configured 1.1.0#1
+ EOO
+
+ $* --mask-repository $cn --deorphan ?libfoo 2>>~%EOE%;
+ deorphan/downgrade/unhold libfoo/1.1.0
+ reconfigure libbar (dependent of libfoo)
+ disfigured libbar/1.1.0
+ disfigured libfoo/1.1.0#1
+ fetched libfoo/1.1.0
+ unpacked libfoo/1.1.0
+ configured libfoo/1.1.0
+ configured libbar/1.1.0
+ %info: .+libbar-1.1.0.+ is up to date%
+ updated libbar/1.1.0
+ EOE
+
+ $pkg_status -r libbar >>EOO;
+ !libbar configured 1.1.0
+ libfoo configured 1.1.0 available 1.1.0#1
+ EOO
+
+ # Noop.
+ #
+ $* --mask-repository $cn --deorphan ?libfoo;
+
+ $pkg_status -r libbar >>EOO;
+ !libbar configured 1.1.0
+ libfoo configured 1.1.0 available 1.1.0#1
+ EOO
+
+ $pkg_drop libbar
+ }
+
: drop
:
{
@@ -17813,6 +18133,51 @@ else
$pkg_status -ar 2>'info: no packages in the configuration'
}
+ : drop-masked
+ :
+ : As above but using --mask-repository instead of rep-remove.
+ :
+ {
+ $clone_root_cfg;
+
+ cp -r $src/libfoo-1.1.0 libfoo;
+ $rep_add --type dir libfoo/ && $rep_fetch;
+
+ $* libfoo 2>!;
+ $rep_fetch $rep/t4b;
+
+ $* libbar 2>!;
+
+ echo "" >+ libfoo/manifest;
+ $rep_fetch;
+ $* libfoo 2>!;
+
+ $pkg_status -r libbar >>EOO;
+ !libbar configured 1.1.0
+ !libfoo configured 1.1.0#1
+ EOO
+
+ # Note that on Windows the local repository canonical name path part
+ # is converted to lower case.
+ #
+ cn = "$canonicalize([dir_path] $~/libfoo)";
+ if! $posix
+ cn = $lcase([string] $cn)
+ end;
+ cn = "dir:$cn";
+
+ $* --mask-repository $cn --deorphan ?libfoo ?libbar 2>>EOE;
+ drop libfoo/1.1.0#1 (unused)
+ drop libbar/1.1.0 (unused)
+ disfigured libbar/1.1.0
+ disfigured libfoo/1.1.0#1
+ purged libfoo/1.1.0#1
+ purged libbar/1.1.0
+ EOE
+
+ $pkg_status -ar 2>'info: no packages in the configuration'
+ }
+
: no-dependent
:
{
@@ -17839,6 +18204,42 @@ else
$pkg_status -ar 2>'info: no packages in the configuration'
}
+ : no-dependent-masked
+ :
+ : As above but using --mask-repository instead of rep-remove.
+ :
+ {
+ $clone_root_cfg;
+
+ cp -r $src/libfoo-1.1.0 libfoo;
+ $rep_add --type dir libfoo/ && $rep_fetch;
+
+ echo "" >+ libfoo/manifest;
+ $rep_fetch;
+ $* libfoo 2>!;
+
+ $rep_fetch $rep/t4b;
+
+ $pkg_status libfoo >'!libfoo configured 1.1.0';
+
+ # Note that on Windows the local repository canonical name path part
+ # is converted to lower case.
+ #
+ cn = "$canonicalize([dir_path] $~/libfoo)";
+ if! $posix
+ cn = $lcase([string] $cn)
+ end;
+ cn = "dir:$cn";
+
+ $* --mask-repository $cn --deorphan ?libfoo 2>>EOE;
+ drop libfoo/1.1.0 (unused)
+ disfigured libfoo/1.1.0
+ purged libfoo/1.1.0
+ EOE
+
+ $pkg_status -ar 2>'info: no packages in the configuration'
+ }
+
: preference
:
{
@@ -18272,6 +18673,56 @@ else
$pkg_drop libbar
}
+ : immediate-masked
+ :
+ : As above but using --mask-repository instead of rep-remove.
+ :
+ {
+ $clone_root_cfg;
+ cp -rp ../libbar ./;
+
+ $rep_add --type dir libbar/ && $rep_fetch;
+ $* libbar ?libfoo/1.1.0 2>!;
+
+ $pkg_status -or libbar >>EOO;
+ !libbar configured 1.0.0 available (1.0.0)
+ libfoo configured !1.1.0 available [1.2.0] [1.1.1] (1.1.0)
+ EOO
+
+ $rep_add $rep/t2 $rep/t4b $rep/t14c && $rep_fetch;
+
+ # Note that on Windows the local repository canonical name path part
+ # is converted to lower case.
+ #
+ cn = "$canonicalize([dir_path] $~/libbar)";
+ if! $posix
+ cn = $lcase([string] $cn)
+ end;
+ cn = "dir:$cn";
+
+ $* --mask-repository $cn --deorphan --immediate libbar 2>>~%EOE%;
+ deorphan/downgrade libfoo/1.0.0
+ deorphan/update libbar/1.0.0
+ disfigured libbar/1.0.0
+ disfigured libfoo/1.1.0
+ fetched libfoo/1.0.0
+ unpacked libfoo/1.0.0
+ fetched libbar/1.0.0
+ unpacked libbar/1.0.0
+ configured libfoo/1.0.0
+ configured libbar/1.0.0
+ %info: .+libbar.+ is up to date%
+ updated libbar/1.0.0
+ EOE
+
+ $pkg_status -or libbar >>EOO;
+ !libbar configured 1.0.0 available 1.1.0 (1.0.0)
+ libfoo configured !1.0.0 available [1.2.0] [1.1.1] 1.1.0+1 [1.1.0] (1.0.0)
+ EOO
+
+ $pkg_drop libbar
+ }
+
: recursive
:
{
@@ -18616,6 +19067,107 @@ else
$pkg_drop libfoo
}
+ : basics-masked
+ :
+ : As above but using --mask-repository instead of rep-remove.
+ :
+ {
+ $clone_root_cfg;
+
+ cp -r $src/libfoo-1.1.0 libfoo;
+ sed -i -e 's/(version:).+/\1 1.0.0/' libfoo/manifest;
+
+ $rep_add --type dir libfoo/ && $rep_fetch;
+
+ $* libfoo 2>!;
+
+ echo "" >+ libfoo/manifest;
+ $rep_fetch;
+
+ $pkg_status -ro libfoo >>EOO;
+ !libfoo configured 1.0.0 available 1.0.0#1
+ EOO
+
+ # Deorphan libfoo/1.0.0 to libfoo/1.0.0#1.
+ #
+ $* --deorphan libfoo 2>>~%EOE%;
+ deorphan/upgrade libfoo/1.0.0#1
+ disfigured libfoo/1.0.0
+ using libfoo/1.0.0#1 (external)
+ configured libfoo/1.0.0#1
+ %info: .+libfoo.+ is up to date%
+ updated libfoo/1.0.0#1
+ EOE
+
+ $pkg_status -ro libfoo >>EOO;
+ !libfoo configured 1.0.0#1 available (1.0.0#1)
+ EOO
+
+ # Noop.
+ #
+ $* --deorphan libfoo 2>'info: nothing to build';
+
+ $rep_fetch $rep/t4a $rep/t4c;
+
+ $pkg_status -ro libfoo >>EOO;
+ !libfoo configured 1.0.0#1 available 1.1.0 (1.0.0#1) 1.0.0
+ EOO
+
+ # Deorphan libfoo/1.0.0#1 to libfoo/1.0.0.
+ #
+ # Note that on Windows the local repository canonical name path part is
+ # converted to lower case.
+ #
+ cn = "$canonicalize([dir_path] $~/libfoo)";
+ if! $posix
+ cn = $lcase([string] $cn)
+ end;
+ cn = "dir:$cn";
+
+ $* --mask-repository $cn --deorphan libfoo 2>>~%EOE%;
+ deorphan/downgrade libfoo/1.0.0
+ disfigured libfoo/1.0.0#1
+ fetched libfoo/1.0.0
+ unpacked libfoo/1.0.0
+ configured libfoo/1.0.0
+ %info: .+libfoo-1.0.0.+ is up to date%
+ updated libfoo/1.0.0
+ EOE
+
+ $pkg_status libfoo >'!libfoo configured 1.0.0 available 1.1.0 1.0.0#1';
+
+ # Noop.
+ #
+ $* --mask-repository $cn --deorphan libfoo 2>'info: nothing to build';
+
+ $pkg_status libfoo >'!libfoo configured 1.0.0 available 1.1.0 1.0.0#1';
+
+ # Deorphan libfoo/1.0.0 to libfoo/1.1.0.
+ #
+ # While at it, use the 'deorphan all held packages' form.
+ #
+ $* --mask-repository $cn --mask-repository $rep/t4c --deorphan 2>>~%EOE%;
+ deorphan/upgrade libfoo/1.1.0
+ disfigured libfoo/1.0.0
+ fetched libfoo/1.1.0
+ unpacked libfoo/1.1.0
+ configured libfoo/1.1.0
+ %info: .+libfoo-1.1.0.+ is up to date%
+ updated libfoo/1.1.0
+ EOE
+
+ $pkg_status libfoo >'!libfoo configured 1.1.0';
+
+ # Noop.
+ #
+ $* --mask-repository $cn --mask-repository $rep/t4c --deorphan libfoo \
+ 2>'info: nothing to build';
+
+ $pkg_status libfoo >'!libfoo configured 1.1.0';
+
+ $pkg_drop libfoo
+ }
+
: preference
:
{
@@ -19051,5 +19603,231 @@ else
$pkg_drop libfoo
}
+
+ : preference-all-held-masked
+ :
+ : As above but using --mask-repository instead of rep-remove.
+ :
+ {
+ $clone_root_cfg;
+
+ tar (!$posix ? --force-local : ) -xf $src/t14d/libfoo-1.1.0+2.tar.gz &libfoo-1.1.0+2/***;
+ mv libfoo-1.1.0+2 libfoo;
+
+ $rep_add --type dir libfoo/ && $rep_fetch;
+
+ $* libfoo 2>!;
+
+ $rep_fetch $rep/t14a $rep/t14b $rep/t14c $rep/t14d $rep/t14e $rep/t14f $rep/t14i;
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.1.0+2 available 1.2.0 1.1.1 1.1.0+3 (1.1.0+2) 1.1.0+1 1.1.0 1.0.0
+ EOO
+
+ # Deorphan libfoo/1.1.0+2 to the exactly same version.
+ #
+ # Note that on Windows the local repository canonical name path part is
+ # converted to lower case.
+ #
+ cn = "$canonicalize([dir_path] $~/libfoo)";
+ if! $posix
+ cn = $lcase([string] $cn)
+ end;
+ cn = "dir:$cn";
+
+ mask = --mask-repository $cn;
+
+ $* $mask --deorphan 2>>~%EOE%;
+ deorphan/update libfoo/1.1.0+2
+ disfigured libfoo/1.1.0+2
+ fetched libfoo/1.1.0+2
+ unpacked libfoo/1.1.0+2
+ configured libfoo/1.1.0+2
+ %info: .+libfoo-1.1.0\+2.+ is up to date%
+ updated libfoo/1.1.0+2
+ EOE
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.1.0+2 available 1.2.0 1.1.1 1.1.0+3 (1.1.0+2) 1.1.0+1 1.1.0 1.0.0
+ EOO
+
+ # Noop.
+ #
+ $* $mask --deorphan 2>'info: nothing to build';
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.1.0+2 available 1.2.0 1.1.1 1.1.0+3 (1.1.0+2) 1.1.0+1 1.1.0 1.0.0
+ EOO
+
+ # Deorphan libfoo/1.1.0+2 to the later revision of same version (1.1.0+3).
+ #
+ mask += --mask-repository $rep/t14d;
+
+ $* $mask --deorphan 2>>~%EOE%;
+ deorphan/upgrade libfoo/1.1.0+3
+ disfigured libfoo/1.1.0+2
+ fetched libfoo/1.1.0+3
+ unpacked libfoo/1.1.0+3
+ configured libfoo/1.1.0+3
+ %info: .+libfoo-1.1.0\+3.+ is up to date%
+ updated libfoo/1.1.0+3
+ EOE
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.1.0+3 available 1.2.0 1.1.1 (1.1.0+3) 1.1.0+2 1.1.0+1 1.1.0 1.0.0
+ EOO
+
+ # Noop.
+ #
+ $* $mask --deorphan 2>'info: nothing to build';
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.1.0+3 available 1.2.0 1.1.1 (1.1.0+3) 1.1.0+2 1.1.0+1 1.1.0 1.0.0
+ EOO
+
+ # Deorphan libfoo/1.1.0+3 to the later patch of same version (1.1.1).
+ #
+ mask += --mask-repository $rep/t14e;
+
+ $* $mask --deorphan 2>>~%EOE%;
+ deorphan/upgrade libfoo/1.1.1
+ disfigured libfoo/1.1.0+3
+ fetched libfoo/1.1.1
+ unpacked libfoo/1.1.1
+ configured libfoo/1.1.1
+ %info: .+libfoo-1.1.1.+ is up to date%
+ updated libfoo/1.1.1
+ EOE
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.1.1 available 1.2.0 (1.1.1) 1.1.0+3 1.1.0+2 1.1.0+1 1.1.0 1.0.0
+ EOO
+
+ # Noop.
+ #
+ $* $mask --deorphan 2>'info: nothing to build';
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.1.1 available 1.2.0 (1.1.1) 1.1.0+3 1.1.0+2 1.1.0+1 1.1.0 1.0.0
+ EOO
+
+ # Deorphan libfoo/1.1.1 to later minor of same version (1.2.0).
+ #
+ mask += --mask-repository $rep/t14f;
+
+ $* $mask --deorphan 2>>~%EOE%;
+ deorphan/upgrade libfoo/1.2.0
+ disfigured libfoo/1.1.1
+ fetched libfoo/1.2.0
+ unpacked libfoo/1.2.0
+ configured libfoo/1.2.0
+ %info: .+libfoo-1.2.0.+ is up to date%
+ updated libfoo/1.2.0
+ EOE
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.2.0 available (1.2.0) 1.1.1 1.1.0+3 1.1.0+2 1.1.0+1 1.1.0 1.0.0
+ EOO
+
+ # Noop.
+ #
+ $* $mask --deorphan 2>'info: nothing to build';
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.2.0 available (1.2.0) 1.1.1 1.1.0+3 1.1.0+2 1.1.0+1 1.1.0 1.0.0
+ EOO
+
+ # Deorphan libfoo/1.2.0 to latest available version (1.1.0+1).
+ #
+ mask += --mask-repository $rep/t14i;
+
+ $* $mask --deorphan 2>>~%EOE%;
+ deorphan/downgrade libfoo/1.1.0+1
+ disfigured libfoo/1.2.0
+ fetched libfoo/1.1.0+1
+ unpacked libfoo/1.1.0+1
+ configured libfoo/1.1.0+1
+ %info: .+libfoo-1.1.0\+1.+ is up to date%
+ updated libfoo/1.1.0+1
+ EOE
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.1.0+1 available 1.2.0 1.1.1 1.1.0+3 1.1.0+2 (1.1.0+1) 1.1.0 1.0.0
+ EOO
+
+ # Noop.
+ #
+ $* $mask --deorphan 2>'info: nothing to build';
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.1.0+1 available 1.2.0 1.1.1 1.1.0+3 1.1.0+2 (1.1.0+1) 1.1.0 1.0.0
+ EOO
+
+ # Deorphan libfoo/1.1.0+1 to latest available version (1.1.0).
+ #
+ mask += --mask-repository $rep/t14c;
+
+ $* $mask --deorphan 2>>~%EOE%;
+ deorphan/downgrade libfoo/1.1.0
+ disfigured libfoo/1.1.0+1
+ fetched libfoo/1.1.0
+ unpacked libfoo/1.1.0
+ configured libfoo/1.1.0
+ %info: .+libfoo-1.1.0.+ is up to date%
+ updated libfoo/1.1.0
+ EOE
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.1.0 available 1.2.0 1.1.1 1.1.0+3 1.1.0+2 1.1.0+1 (1.1.0) 1.0.0
+ EOO
+
+ # Noop.
+ #
+ $* $mask --deorphan 2>'info: nothing to build';
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.1.0 available 1.2.0 1.1.1 1.1.0+3 1.1.0+2 1.1.0+1 (1.1.0) 1.0.0
+ EOO
+
+ # Deorphan libfoo/1.1.0 to latest available version (1.0.0).
+ #
+ mask += --mask-repository $rep/t14b;
+
+ $* $mask --deorphan 2>>~%EOE%;
+ deorphan/downgrade libfoo/1.0.0
+ disfigured libfoo/1.1.0
+ fetched libfoo/1.0.0
+ unpacked libfoo/1.0.0
+ configured libfoo/1.0.0
+ %info: .+libfoo-1.0.0.+ is up to date%
+ updated libfoo/1.0.0
+ EOE
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.0.0 available 1.2.0 1.1.1 1.1.0+3 1.1.0+2 1.1.0+1 1.1.0 (1.0.0)
+ EOO
+
+ # Noop.
+ #
+ $* $mask --deorphan 2>'info: nothing to build';
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.0.0 available 1.2.0 1.1.1 1.1.0+3 1.1.0+2 1.1.0+1 1.1.0 (1.0.0)
+ EOO
+
+ # Deorphan fails (none available).
+ #
+ mask += --mask-repository $rep/t14a;
+
+ $* $mask --deorphan 2>>/EOE != 0;
+ error: libfoo is not available
+ EOE
+
+ $pkg_status -o libfoo >>EOO;
+ !libfoo configured 1.0.0 available 1.2.0 1.1.1 1.1.0+3 1.1.0+2 1.1.0+1 1.1.0 (1.0.0)
+ EOO
+
+ $pkg_drop libfoo
+ }
}
}