aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2023-06-13 21:05:35 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2023-06-16 11:10:04 +0300
commit32e4cffb444d4ad936c9037eecea9bd78f81f3d0 (patch)
treebbbb4452a88f47449c86a7ff85b9617ab7b621da
parentc98f1215387f993329a96785a2a9102d93cb0328 (diff)
Add --mask-repository pkg-build optionmask-repos-old
-rw-r--r--bpkg/manifest-utility.cxx92
-rw-r--r--bpkg/manifest-utility.hxx38
-rw-r--r--bpkg/manifest-utility.ixx14
-rw-r--r--bpkg/package-query.cxx124
-rw-r--r--bpkg/package.cxx38
-rw-r--r--bpkg/package.hxx12
-rw-r--r--bpkg/pkg-build-collect.cxx24
-rw-r--r--bpkg/pkg-build.cli9
-rw-r--r--bpkg/pkg-build.cxx71
-rw-r--r--bpkg/pkg-checkout.cxx15
-rw-r--r--bpkg/pkg-fetch.cxx15
-rw-r--r--bpkg/pkg-unpack.cxx5
-rw-r--r--bpkg/rep-fetch.cxx4
-rw-r--r--bpkg/rep-remove.cxx22
-rw-r--r--bpkg/rep-remove.hxx3
-rw-r--r--bpkg/system-package-manager.cxx3
-rw-r--r--tests/pkg-build.testscript449
17 files changed, 868 insertions, 70 deletions
diff --git a/bpkg/manifest-utility.cxx b/bpkg/manifest-utility.cxx
index 76db3a7..a96f3b9 100644
--- a/bpkg/manifest-utility.cxx
+++ b/bpkg/manifest-utility.cxx
@@ -23,6 +23,98 @@ namespace bpkg
const path signature_file ("signature.manifest");
const path manifest_file ("manifest");
+ // Masked repositories.
+ //
+ static strings masked_repository_names;
+ static vector<repository_url> masked_repository_urls;
+
+ void
+ mask_repository (const string& r)
+ {
+ if (repository_name (r))
+ {
+ // Verify that the canonical name is of the pkg or dir type.
+ //
+ size_t p (r.find (':'));
+ if (p == string::npos)
+ fail << "invalid repository name '" << r << "': expected to start "
+ << "with colon-separated type";
+
+ try
+ {
+ repository_type t (to_repository_type (string (r, 0, p)));
+
+ if (t != repository_type::pkg && t != repository_type::dir)
+ fail << "invalid repository name '" << r << "': only repositories "
+ << "of pkg and dir types can be masked";
+
+ masked_repository_names.push_back (r);
+ }
+ catch (const invalid_argument& e)
+ {
+ fail << "invalid repository name '" << r << "': " << e;
+ }
+ }
+ else
+ {
+ repository_location rl (parse_location (r, nullopt /* type */));
+
+ if (rl.type () != repository_type::pkg &&
+ rl.type () != repository_type::dir)
+ fail << "invalid repository location '" << r << "': only repositories "
+ << "of pkg and dir types can be masked";
+
+ masked_repository_urls.push_back (rl.url ());
+ }
+ }
+
+ bool
+ masked_repository (const repository_location& rl)
+ {
+ // Optimize for the common case when no repositories are masked.
+ //
+ if (masked_repository_names.empty () && masked_repository_urls.empty ())
+ return false;
+
+ const string& n (rl.canonical_name ());
+ const repository_url& u (rl.url ());
+
+ bool r (find (masked_repository_names.begin (),
+ masked_repository_names.end (),
+ n) != masked_repository_names.end () ||
+ find_if (masked_repository_urls.begin (),
+ masked_repository_urls.end (),
+ [&u] (const repository_url& ru) {return ru == u;}) !=
+ masked_repository_urls.end ());
+
+ // Note that the parse_location() function called by mask_repository()
+ // could potentially mis-guess the type of the git repository (for
+ // example, mistakenly deciding it is a pkg repository). If that's the
+ // case, we would silently mask it, despite the fact we don't support
+ // masking of git repositories. By checking the type here we at least
+ // don't erroneously consider a git repository as masked and fail, which
+ // is better late then never. The solution feels more as a hack but to
+ // properly fix that we just need to add support for masking git
+ // repositories.
+ //
+ if (r &&
+ rl.type () != repository_type::pkg &&
+ rl.type () != repository_type::dir)
+ fail << "invalid repository location '" << u << "': only repositories "
+ << "of pkg and dir types can be masked";
+
+ return r;
+ }
+
+ bool
+ masked_repositories ()
+ {
+ return !masked_repository_names.empty () ||
+ !masked_repository_urls.empty ();
+ }
+
+ // package
+ //
vector<package_info>
package_b_info (const common_options& o,
const dir_paths& ds,
diff --git a/bpkg/manifest-utility.hxx b/bpkg/manifest-utility.hxx
index a5ea962..522ec2b 100644
--- a/bpkg/manifest-utility.hxx
+++ b/bpkg/manifest-utility.hxx
@@ -112,6 +112,42 @@ namespace bpkg
bool
repository_name (const string&);
+ // To pretend that repositories don't exist in the configurations, mask them
+ // by specifying either repository location canonical name or URL.
+ //
+ // Note that specifying a canonical name masks potentially multiple
+ // repositories (think of local and remote pkg repository locations), while
+ // specifying a URL masks a single repository.
+ //
+ // Also note that there is no straightforward way to deduce the list of
+ // repositories a specific repository fragment belongs to. To keep things
+ // simple, we currently only support masking of pkg and dir repositories.
+ // Such repositories have the only non-shared fragment which name and
+ // location are equal to the ones of the containing repository. Once we
+ // decide to also support repositories which support fragmentation (git),
+ // the potential implementation can preload all the masked fragments from
+ // all the databases, so that the masked_repository_fragment() predicate can
+ // refer to them.
+ //
+ void
+ mask_repository (const string&);
+
+ // Return true if a repository is masked either by name or by URL.
+ //
+ bool
+ masked_repository (const repository_location&);
+
+ // Return true if a repository fragment belongs to the masked repositories
+ // only (see package.hxx for the fragment/repository relationship details).
+ //
+ bool
+ masked_repository_fragment (const repository_location&);
+
+ // Return true if any repositories are masked.
+ //
+ bool
+ masked_repositories ();
+
// Return the versions of packages as provided by the build2 version module
// together with the build2 project info the versions originate from (in
// case the caller may want to reuse it). Return nullopt as a package
@@ -190,4 +226,6 @@ namespace bpkg
bool err_path_relative = false);
}
+#include <bpkg/manifest-utility.ixx>
+
#endif // BPKG_MANIFEST_UTILITY_HXX
diff --git a/bpkg/manifest-utility.ixx b/bpkg/manifest-utility.ixx
new file mode 100644
index 0000000..d623747
--- /dev/null
+++ b/bpkg/manifest-utility.ixx
@@ -0,0 +1,14 @@
+// file : bpkg/manifest-utility.ixx -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+namespace bpkg
+{
+ // NOTE: the implementation relies on the fact that only pkg and dir
+ // repositories are currently supported.
+ //
+ inline bool
+ masked_repository_fragment (const repository_location& rl)
+ {
+ return masked_repository (rl);
+ }
+}
diff --git a/bpkg/package-query.cxx b/bpkg/package-query.cxx
index 9675d97..4377f5c 100644
--- a/bpkg/package-query.cxx
+++ b/bpkg/package-query.cxx
@@ -136,6 +136,8 @@ namespace bpkg
repository_fragments& chain,
bool prereq)
{
+ assert (!masked_repository_fragment (rf));
+
// Prerequisites are not searched through recursively.
//
assert (!prereq || chain.empty ());
@@ -159,6 +161,9 @@ namespace bpkg
{
const lazy_shared_ptr<repository_fragment>& lrf (pl.repository_fragment);
+ if (masked_repository_fragment (lrf))
+ continue;
+
// First check the repository itself.
//
if (lrf.object_id () == rf->name)
@@ -175,20 +180,26 @@ namespace bpkg
for (const lazy_weak_ptr<repository>& r: cs)
{
- const auto& frs (r.load ()->fragments);
+ if (!masked_repository (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 (!masked_repository (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 +209,17 @@ namespace bpkg
//
for (const lazy_weak_ptr<repository>& cr: cs)
{
- for (const auto& fr: cr.load ()->fragments)
+ if (!masked_repository (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 +227,14 @@ namespace bpkg
{
for (const lazy_weak_ptr<repository>& pr: ps)
{
- for (const auto& fr: pr.load ()->fragments)
+ if (!masked_repository (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;
+ }
}
}
}
@@ -239,6 +256,8 @@ namespace bpkg
result<available_package>&& apr,
bool prereq)
{
+ assert (!masked_repository_fragment (r));
+
vector<shared_ptr<available_package>> aps;
for (shared_ptr<available_package> ap: pointer_result (apr))
@@ -258,6 +277,8 @@ namespace bpkg
using result = pair<shared_ptr<available_package>,
shared_ptr<repository_fragment>>;
+ assert (!masked_repository_fragment (r));
+
for (shared_ptr<available_package> ap: pointer_result (apr))
{
if (shared_ptr<repository_fragment> pr = filter (r, ap, prereq))
@@ -345,15 +366,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 (!masked_repository_fragment (lrf))
+ {
+ r.emplace_back (move (ap), lrf);
+ break;
+ }
+ }
}
}
@@ -415,6 +441,8 @@ namespace bpkg
const lazy_shared_ptr<repository_fragment>& rf,
bool prereq)
{
+ assert (!masked_repository_fragment (rf));
+
vector<shared_ptr<available_package>> r;
database& db (rf.database ());
@@ -438,6 +466,8 @@ namespace bpkg
bool prereq,
bool revision)
{
+ assert (!masked_repository_fragment (rf));
+
// Filter the result based on the repository fragment to which each
// version belongs.
//
@@ -527,17 +557,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 (!masked_repository_fragment (f))
+ return make_pair (ap,
+ lazy_shared_ptr<repository_fragment> (ddb,
+ move (f)));
+ }
}
}
@@ -592,15 +627,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 (!masked_repository_fragment (lrf))
+ {
+ r.emplace_back (move (ap), lrf);
+ break;
+ }
+ }
}
}
@@ -638,14 +678,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 (!masked_repository_fragment (f))
+ {
+ rf = lazy_shared_ptr<repository_fragment> (ddb, move (f));
+ break;
+ }
}
}
diff --git a/bpkg/package.cxx b/bpkg/package.cxx
index c98e7a4..c19f95d 100644
--- a/bpkg/package.cxx
+++ b/bpkg/package.cxx
@@ -17,6 +17,41 @@ namespace bpkg
{
const version wildcard_version (0, "0", nullopt, nullopt, 0);
+ // repository
+ //
+ bool
+ masked_repository (const lazy_weak_ptr<repository>& rf)
+ {
+ // Optimize for the common case when no repositories are masked to avoid
+ // an unnecessary load of the lazy pointer.
+ //
+ return masked_repositories () && masked_repository (rf.load ()->location);
+ }
+
+ bool
+ masked_repository (const shared_ptr<repository>& rf)
+ {
+ return masked_repository (rf->location);
+ }
+
+ // repository_fragment
+ //
+ bool
+ masked_repository_fragment (const lazy_shared_ptr<repository_fragment>& rf)
+ {
+ // Optimize for the common case when no repositories are masked to avoid
+ // an unnecessary load of the lazy pointer.
+ //
+ return masked_repositories () &&
+ masked_repository_fragment (rf.load ()->location);
+ }
+
+ bool
+ masked_repository_fragment (const shared_ptr<repository_fragment>& rf)
+ {
+ return masked_repository_fragment (rf->location);
+ }
+
// configuration
//
configuration::
@@ -373,7 +408,8 @@ namespace bpkg
{
const shared_ptr<repository_fragment>& rf (prf.repository_fragment);
- if (rf->location.directory_based ())
+ if (!masked_repository_fragment (rf) &&
+ rf->location.directory_based ())
fail << "external package " << n << '/' << v
<< " is already available from "
<< rf->location.canonical_name ();
diff --git a/bpkg/package.hxx b/bpkg/package.hxx
index 060f13a..d18742e 100644
--- a/bpkg/package.hxx
+++ b/bpkg/package.hxx
@@ -454,6 +454,18 @@ namespace bpkg
operator size_t () const {return result;}
};
+ bool
+ masked_repository (const lazy_weak_ptr<repository>&);
+
+ bool
+ masked_repository (const shared_ptr<repository>&);
+
+ bool
+ masked_repository_fragment (const lazy_shared_ptr<repository_fragment>&);
+
+ bool
+ masked_repository_fragment (const shared_ptr<repository_fragment>&);
+
// repository
//
#pragma db object pointer(shared_ptr) session
diff --git a/bpkg/pkg-build-collect.cxx b/bpkg/pkg-build-collect.cxx
index 86dcb24..73b5dc0 100644
--- a/bpkg/pkg-build-collect.cxx
+++ b/bpkg/pkg-build-collect.cxx
@@ -153,7 +153,20 @@ namespace bpkg
// If adjustment or orphan, then new and old are the same.
//
- if (available == nullptr || available->locations.empty ())
+ small_vector<reference_wrapper<const package_location>, 8> locations;
+
+ if (available != nullptr)
+ {
+ locations.reserve (available->locations.size ());
+
+ for (const package_location& pl: available->locations)
+ {
+ if (!masked_repository_fragment (pl.repository_fragment))
+ locations.push_back (pl);
+ }
+ }
+
+ if (locations.empty ())
{
assert (selected != nullptr);
@@ -169,7 +182,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 +202,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 +1140,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 +1170,9 @@ namespace bpkg
tracer trace ("collect_build");
+ assert (pkg.repository_fragment == nullptr ||
+ !masked_repository_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 05d4077..244ebac 100644
--- a/bpkg/pkg-build.cli
+++ b/bpkg/pkg-build.cli
@@ -397,6 +397,15 @@ namespace bpkg
option in \l{bpkg-rep-fetch(1)} for details."
}
+ strings --mask-repository
+ {
+ "<rep>",
+ "Pretend, for the duration of the command execution, that repository
+ doesn't exist in the configuration. The repository can be specified
+ either as a repository name or as a repository location (URL or a
+ directory path). 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 a469a2b..d48f6af 100644
--- a/bpkg/pkg-build.cxx
+++ b/bpkg/pkg-build.cxx
@@ -75,6 +75,17 @@ namespace bpkg
const available_package_id& id,
config_repo_fragments& r)
{
+#ifndef NDEBUG
+ if (masked_repositories ())
+ {
+ for (const auto& v: r)
+ {
+ for (const shared_ptr<repository_fragment>& f: v.second)
+ assert (!masked_repository_fragment (f));
+ }
+ }
+#endif
+
for (database& ddb: dependent_repo_configs (db))
{
shared_ptr<available_package> dap (ddb.find<available_package> (id));
@@ -93,11 +104,24 @@ 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 (!masked_repository_fragment (lrf))
+ {
+ shared_ptr<repository_fragment> rf (lrf.load ());
+
+ if (find (rfs.begin (), rfs.end (), rf) == rfs.end ())
+ rfs.push_back (move (rf));
+ }
}
+
+ // If we didn't add any repository fragments from this database since
+ // all of the available package repositories are masked, then just
+ // erase the entry in the map if it contains no fragments.
+ //
+ if (rfs.empty ())
+ r.erase (i);
}
}
}
@@ -171,8 +195,12 @@ namespace bpkg
for (database& ddb: dependent_repo_configs (db))
{
- if (ddb.find<repository_fragment> (cn) != nullptr)
- return false;
+ if (shared_ptr<repository_fragment> f =
+ ddb.find<repository_fragment> (cn))
+ {
+ if (!masked_repository_fragment (f))
+ return false;
+ }
}
return true;
@@ -1485,6 +1513,11 @@ namespace bpkg
validate_options (o, ""); // Global package options.
+ // Mask the repositories.
+ //
+ for (const string& r: o.mask_repository ())
+ mask_repository (r);
+
// Note that the session spans all our transactions. The idea here is that
// selected_package objects in build_packages below will be cached in this
// session. When subsequent transactions modify any of these objects, they
@@ -1741,6 +1774,10 @@ namespace bpkg
info << "repository location cannot be specified for "
<< "dependencies";
+ if (masked_repositories ())
+ fail << "package repository location may not be specified "
+ << "together with --mask-repository option";
+
string pks (p > 1 ? string (a, 0, p - 1) : empty_string);
for (size_t i (0); i != dbs.size (); ++i)
@@ -2338,6 +2375,10 @@ namespace bpkg
//
database& pdb (ps.db);
+ // Should have failed earlier.
+ //
+ assert (!masked_repository (ps.location));
+
// Expand the [[<packages>]@]<location> spec. Fail if the repository
// is not found in this configuration, that can be the case in the
// presence of --no-fetch option.
@@ -5763,18 +5804,24 @@ 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 (!masked_repository_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;
+ }
}
}
+ // We should have failed earlier if all the package repository
+ // fragments are masked.
+ //
assert (basis);
// All calls commit the transaction.
diff --git a/bpkg/pkg-checkout.cxx b/bpkg/pkg-checkout.cxx
index 9793c41..7a78cb6 100644
--- a/bpkg/pkg-checkout.cxx
+++ b/bpkg/pkg-checkout.cxx
@@ -148,14 +148,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 (!masked_repository_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..2917c51 100644
--- a/bpkg/pkg-fetch.cxx
+++ b/bpkg/pkg-fetch.cxx
@@ -224,14 +224,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 (!masked_repository_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..1d55ab6 100644
--- a/bpkg/pkg-unpack.cxx
+++ b/bpkg/pkg-unpack.cxx
@@ -70,6 +70,8 @@ namespace bpkg
bool purge,
bool simulate)
{
+ assert (!masked_repository_fragment (rl));
+
// Make the package path absolute and normalized. If the package is inside
// the configuration, use the relative path. This way we can move the
// configuration around.
@@ -304,7 +306,8 @@ namespace bpkg
for (const package_location& l: ap->locations)
{
- if (l.repository_fragment.load ()->location.directory_based ())
+ if (!masked_repository_fragment (l.repository_fragment) &&
+ l.repository_fragment.load ()->location.directory_based ())
{
pl = &l;
break;
diff --git a/bpkg/rep-fetch.cxx b/bpkg/rep-fetch.cxx
index 6f31478..e267412 100644
--- a/bpkg/rep-fetch.cxx
+++ b/bpkg/rep-fetch.cxx
@@ -871,6 +871,8 @@ namespace bpkg
}
}
+ assert (!masked_repository_fragment (rfl));
+
shared_ptr<repository_fragment> rf (
db.find<repository_fragment> (rfl.canonical_name ()));
@@ -1229,6 +1231,8 @@ namespace bpkg
tracer_guard tg (db, trace);
+ assert (!masked_repository (r));
+
// Check that the repository is not fetched yet and register it as fetched
// otherwise.
//
diff --git a/bpkg/rep-remove.cxx b/bpkg/rep-remove.cxx
index 700534f..0c28159 100644
--- a/bpkg/rep-remove.cxx
+++ b/bpkg/rep-remove.cxx
@@ -284,6 +284,28 @@ namespace bpkg
}
void
+ rep_mask (const strings& repos)
+ {
+ // @@ Do we want to fail if a name or url is not present in all the dbs?
+ //
+
+ for (database& db: repo_configs)
+ {
+ for (const string& repo: repos)
+ {
+ if (repository_name (repo))
+ {
+ if (shared_ptr<repository> r
+ }
+ else
+ {
+
+ }
+ }
+ }
+ }
+
+ void
rep_remove_clean (const common_options& o, database& db, bool quiet)
{
tracer trace ("rep_remove_clean");
diff --git a/bpkg/rep-remove.hxx b/bpkg/rep-remove.hxx
index 0fc82e8..41d8578 100644
--- a/bpkg/rep-remove.hxx
+++ b/bpkg/rep-remove.hxx
@@ -57,6 +57,9 @@ namespace bpkg
rep_remove_package_locations (database&,
transaction&,
const string& fragment_name);
+
+ void
+ rep_mask (const strings&);
}
#endif // BPKG_REP_REMOVE_HXX
diff --git a/bpkg/system-package-manager.cxx b/bpkg/system-package-manager.cxx
index 373e8ff..6f8026d 100644
--- a/bpkg/system-package-manager.cxx
+++ b/bpkg/system-package-manager.cxx
@@ -13,6 +13,7 @@
#include <bpkg/package-odb.hxx>
#include <bpkg/database.hxx>
#include <bpkg/diagnostics.hxx>
+#include <bpkg/manifest-utility.hxx>
#include <bpkg/pkg-bindist-options.hxx>
@@ -566,6 +567,8 @@ namespace bpkg
for (const auto& a: aps)
{
+ assert (!masked_repository_fragment (a.second));
+
const shared_ptr<available_package>& ap (a.first);
for (const distribution_name_value& nv: ap->distribution_values)
diff --git a/tests/pkg-build.testscript b/tests/pkg-build.testscript
index 90bc490..f9fdd0f 100644
--- a/tests/pkg-build.testscript
+++ b/tests/pkg-build.testscript
@@ -1811,6 +1811,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 +3246,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 +3832,17 @@ test.arguments += --sys-no-query
$* libbar --recursive --yes
}
+ : unavailable-masked
+ :
+ : As above but using --mask-repository instead of rep-remove.
+ :
+ {
+ $clone_cfg;
+
+ $* libbar --recursive --yes --mask-repository $rep/t0a \
+ --mask-repository $rep/t0b --mask-repository $rep/t0c
+ }
+
-$pkg_drop libbar libbaz libfoo
}
@@ -14194,6 +14276,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;
+
+ $* ?libbaz --patch --yes --mask-repository $rep/t0c 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 +16466,65 @@ 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
:
{
@@ -17724,6 +17891,93 @@ 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.
+ #
+ # 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";
+
+ $* --deorphan --mask-repository $cn ?libfoo 2>>~%EOE%;
+ deorphan/update/unhold libfoo/1.1.0
+ reconfigure libbar (dependent of libfoo)
+ disfigured libbar/1.1.0
+ disfigured libfoo/1.1.0
+ 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
+
+ # 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
+
+ $pkg_drop libbar
+ }
+
: drop
:
{
@@ -17758,6 +18012,49 @@ else
EOE
}
+ : 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";
+
+ $* --deorphan ?libfoo ?libbar --mask-repository $cn 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
+ }
+
: no-dependent
:
{
@@ -17782,6 +18079,40 @@ else
EOE
}
+ : 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";
+
+ $* --deorphan ?libfoo --mask-repository $cn 2>>EOE
+ drop libfoo/1.1.0 (unused)
+ disfigured libfoo/1.1.0
+ purged libfoo/1.1.0
+ EOE
+ }
+
: preference
:
{
@@ -18126,6 +18457,7 @@ else
EOO
$rep_remove $~/libbar/;
+
$rep_add $rep/t2 $rep/t4b $rep/t14c && $rep_fetch;
$* --deorphan --immediate libbar 2>>~%EOE%;
@@ -18151,6 +18483,58 @@ 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";
+
+ $* --deorphan --immediate libbar --mask-repository $cn \
+ --mask-repository $rep/t14b --mask-repository $rep/t14f \
+ --mask-repository $rep/t14i 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
:
{
@@ -18461,6 +18845,71 @@ 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;
+ $* libfoo 2>!;
+
+ $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";
+
+ $* --deorphan --mask-repository $cn 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';
+
+ # While at it, use the 'deorphan all held packages' form.
+ #
+ $* --deorphan --mask-repository $cn \
+ --mask-repository $rep/t4c --mask-repository $rep/t4b 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';
+
+ $pkg_drop libfoo
+ }
+
: preference
:
{