diff options
-rw-r--r-- | bpkg/manifest-utility.cxx | 92 | ||||
-rw-r--r-- | bpkg/manifest-utility.hxx | 38 | ||||
-rw-r--r-- | bpkg/manifest-utility.ixx | 14 | ||||
-rw-r--r-- | bpkg/package-query.cxx | 124 | ||||
-rw-r--r-- | bpkg/package.cxx | 38 | ||||
-rw-r--r-- | bpkg/package.hxx | 12 | ||||
-rw-r--r-- | bpkg/pkg-build-collect.cxx | 24 | ||||
-rw-r--r-- | bpkg/pkg-build.cli | 9 | ||||
-rw-r--r-- | bpkg/pkg-build.cxx | 71 | ||||
-rw-r--r-- | bpkg/pkg-checkout.cxx | 15 | ||||
-rw-r--r-- | bpkg/pkg-fetch.cxx | 15 | ||||
-rw-r--r-- | bpkg/pkg-unpack.cxx | 5 | ||||
-rw-r--r-- | bpkg/rep-fetch.cxx | 4 | ||||
-rw-r--r-- | bpkg/rep-remove.cxx | 22 | ||||
-rw-r--r-- | bpkg/rep-remove.hxx | 3 | ||||
-rw-r--r-- | bpkg/system-package-manager.cxx | 3 | ||||
-rw-r--r-- | tests/pkg-build.testscript | 449 |
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 : { |