From fdfd3e0644095022b61316e220fce3f5a7a1a9ef Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Tue, 20 Jun 2023 20:22:29 +0300 Subject: Add --mask-repository-uuid pkg-build option --- bpkg/pkg-build.cli | 13 +++++ bpkg/pkg-build.cxx | 6 +- bpkg/rep-mask.cxx | 136 +++++++++++++++++++++++++++++++++++++++------ bpkg/rep-mask.hxx | 21 ++++--- tests/config.testscript | 3 +- tests/pkg-build.testscript | 40 ++++++++++--- 6 files changed, 185 insertions(+), 34 deletions(-) diff --git a/bpkg/pkg-build.cli b/bpkg/pkg-build.cli index 3db6e9f..a365082 100644 --- a/bpkg/pkg-build.cli +++ b/bpkg/pkg-build.cli @@ -411,6 +411,19 @@ namespace bpkg mask multiple repositories." } + strings --mask-repository-uuid + { + "", + "For the duration of the command execution pretend the specified + repository was removed from the specified configuration. Similar to + \cb{--mask-repository} but only masks the repository in a single + configuration. The option value is a key-value pair in the form: + + \c{\i{config-uuid}\b{=}\i{rep}} + + 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 6101826..848dc39 100644 --- a/bpkg/pkg-build.cxx +++ b/bpkg/pkg-build.cxx @@ -2000,8 +2000,10 @@ namespace bpkg // 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 ()); + if (o.mask_repository_specified () || o.mask_repository_uuid_specified ()) + rep_mask (o.mask_repository (), + o.mask_repository_uuid (), + current_configs); // Expand the package specs into individual package args, parsing them // into the package scheme, name, and version constraint components, and diff --git a/bpkg/rep-mask.cxx b/bpkg/rep-mask.cxx index 79aef42..d7f9c6a 100644 --- a/bpkg/rep-mask.cxx +++ b/bpkg/rep-mask.cxx @@ -26,16 +26,26 @@ namespace bpkg const shared_ptr&, bool mask); - // The idea here is to start the transaction, remove all the specified - // repositories recursively in all the configurations specified by - // repo_configs, collect the remaining repositories and repository fragments - // as unmasked, and rollback the transaction. Later on, the rep_masked*() - // functions will refer to the configuration-specific unmasked repositories - // and repository fragments lists to decide if the repository is masked or - // not in the specific configuration. + // The overall plan is as follows: + // + // - Start the transaction. + // + // - Remove all the specified repositories recursively in all the + // configurations specified by repo_configs (repos) and/or configurations + // specified explicitly via UUIDs (config_uuid_repos). + // + // - Collect the remaining repositories and repository fragments as unmasked. + // + // - Rollback the transaction. + // + // Later on, the rep_masked*() functions will refer to the configuration- + // specific unmasked repositories and repository fragments lists to decide + // if the repository/fragment is masked or not in the specific configuration. // void - rep_mask (const strings& repos) + rep_mask (const strings& repos, + const strings& config_uuid_repos, + linked_databases& current_configs) { tracer trace ("rep_mask"); @@ -55,22 +65,31 @@ namespace bpkg transaction t (mdb); + // Add a repository from a database, suppressing duplicates. + // + auto add_repo = [&rs] (database& db, shared_ptr&& r) + { + if (find_if (rs.begin (), rs.end (), + [&db, &r] (const lazy_weak_ptr& lr) + { + return lr.database () == db && lr.object_id () == r->name; + }) == rs.end ()) + rs.emplace_back (db, move (r)); + }; + + // Collect the repositories masked in all configurations. + // for (database& db: repo_configs) { for (size_t i (0); i != repos.size (); ++i) { // Add a repository, suppressing duplicates, and mark it as found. // - auto add = [&db, &rs, &found_repos, i] (shared_ptr&& r) + auto add = [&db, + &found_repos, i, + &add_repo] (shared_ptr&& r) { - if (find_if (rs.begin (), rs.end (), - [&db, &r] (const lazy_weak_ptr& lr) - { - return lr.database () == db && - lr.object_id () == r->name; - }) == rs.end ()) - rs.emplace_back (db, move (r)); - + add_repo (db, move (r)); found_repos[i] = true; }; @@ -114,6 +133,89 @@ namespace bpkg fail << "repository '" << repos[i] << "' cannot be masked: not found"; } + // Collect the repositories masked in specific configurations. + // + for (const string& cr: config_uuid_repos) + { + auto bad = [&cr] (const string& d) + { + fail << "configuration repository '" << cr << "' cannot be masked: " + << d; + }; + + size_t p (cr.find ('=')); + + if (p == string::npos) + bad ("missing '='"); + + uuid uid; + string uid_str (cr, 0, p); + + try + { + uid = uuid (uid_str); + } + catch (const invalid_argument& e) + { + bad ("invalid configuration uuid '" + uid_str + "': " + e.what ()); + } + + database* db (nullptr); + + for (database& cdb: current_configs) + { + if ((db = cdb.try_find_dependency_config (uid)) != nullptr) + break; + } + + if (db == nullptr) + bad ("no configuration with uuid " + uid.string () + + " is linked with " + + (current_configs.size () == 1 + ? mdb.config_orig.representation () + : "specified current configurations")); + + string rp (cr, p + 1); + + if (repository_name (rp)) + { + if (shared_ptr r = db->find (rp)) + add_repo (*db, move (r)); + else + bad ("repository name '" + rp + "' not found in configuration " + + uid.string ()); + } + else + { + using query = query; + + // Verify that the repository URL is not misspelled or empty. + // + try + { + repository_url u (rp); + assert (!u.empty ()); + } + catch (const invalid_argument& e) + { + bad ("invalid repository location '" + rp + "': " + e.what ()); + } + + bool found (false); + for (shared_ptr r: + pointer_result ( + db->query (query::location.url == rp))) + { + add_repo (*db, move (r)); + found = true; + } + + if (!found) + bad ("repository location '" + rp + "' not found in configuration " + + uid.string ()); + } + } + // First, remove the repository references from the dependent repository // fragments. Note that rep_remove() removes the dangling repositories. // diff --git a/bpkg/rep-mask.hxx b/bpkg/rep-mask.hxx index dd9cedf..2185af2 100644 --- a/bpkg/rep-mask.hxx +++ b/bpkg/rep-mask.hxx @@ -13,15 +13,20 @@ 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. + // are used as the repository information sources (repo_configs; + // repositories argument) and/or specific configurations + // (config_uuid_repositories argument). 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: // + // - The current configurations are only used to resolve the configuration + // UUIDs, if any. + // // - A repository may end up being masked in one configuration but not in // another. // @@ -36,7 +41,9 @@ namespace bpkg // NOTE: repo_configs needs to be filled prior to the function call. // void - rep_mask (const strings&); + rep_mask (const strings& repositories, // + const strings& config_uuid_repositories, // = + linked_databases& current_configs); // Return true if a repository is masked in the specified configuration. // diff --git a/tests/config.testscript b/tests/config.testscript index 70eef42..442cf36 100644 --- a/tests/config.testscript +++ b/tests/config.testscript @@ -9,7 +9,8 @@ # shared between multiple bpkg processes. Also we need to make sure that # configurations are not cloned while being used by bpkg. # -+$cfg_create -d cfg 2>- &cfg/*** +cfg_uuid = "00000000-0000-0000-0000-000000000001" ++$cfg_create -d cfg --uuid $cfg_uuid 2>- &cfg/*** # The most commonly used configuration cloning command that copies it from the # parent scope working directory. diff --git a/tests/pkg-build.testscript b/tests/pkg-build.testscript index c3ae96f..7c3da32 100644 --- a/tests/pkg-build.testscript +++ b/tests/pkg-build.testscript @@ -596,6 +596,27 @@ test.arguments += --sys-no-query error: repository '' cannot be masked: invalid repository location: empty URL EOE + : mask-repository-uuid-db-not-found + : + $clone_root_cfg; + $* --mask-repository-uuid '00000000-0000-0000-0000-123456789012=repo' libfoo 2>>/EOE != 0 + error: configuration repository '00000000-0000-0000-0000-123456789012=repo' cannot be masked: no configuration with uuid 00000000-0000-0000-0000-123456789012 is linked with cfg/ + EOE + + : mask-repository-uuid-empty + : + $clone_root_cfg; + $* --mask-repository-uuid "$cfg_uuid=" libfoo 2>>EOE != 0 + error: configuration repository '00000000-0000-0000-0000-000000000001=' cannot be masked: invalid repository location '': empty URL + EOE + + : mask-repository-uuid-not-found + : + $clone_root_cfg; + $* --mask-repository-uuid "$cfg_uuid=https://example.com/1" libfoo 2>>/EOE != 0 + error: configuration repository '00000000-0000-0000-0000-000000000001=https://example.com/1' cannot be masked: repository location 'https://example.com/1' not found in configuration 00000000-0000-0000-0000-000000000001 + EOE + : archive : $clone_root_cfg; @@ -1825,12 +1846,17 @@ test.arguments += --sys-no-query $pkg_status libbar >'!libbar configured 1.2.0'; - # While at it, test using --mask-repository instead of rep-remove. + # 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 + $* --upgrade --mask-repository-uuid "$cfg_uuid=($rep/t2)" \ + --mask-repository-uuid "$cfg_uuid=($rep/t5)" 2>>/EOE != 0; + error: libbar is not available + EOE + $rep_remove $rep/t2 $rep/t5; $* --upgrade 2>>/EOE != 0; @@ -3262,7 +3288,7 @@ test.arguments += --sys-no-query : satisfy-masked : - : As above but using --mask-repository instead of rep-remove. + : As above but using --mask-repository* instead of rep-remove. : { $clone_cfg; @@ -3304,7 +3330,7 @@ test.arguments += --sys-no-query # Test that the selected package is left as there is no satisfactory # available package. # - $* --mask-repository $rep/t0b --mask-repository $rep/t0a ?libbaz; + $* --mask-repository $rep/t0b --mask-repository-uuid "$cfg_uuid=($rep/t0a)" ?libbaz; # Test that the above behavior is not triggered for the system package. # @@ -3848,13 +3874,13 @@ test.arguments += --sys-no-query : unavailable-masked : - : As above but using --mask-repository instead of rep-remove. + : 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 + --mask-repository-uuid "$cfg_uuid=($rep/t0c)" --recursive --yes } -$pkg_drop libbar libbaz libfoo @@ -17959,7 +17985,7 @@ else : basics-masked : - : As above but using --mask-repository instead of rep-remove. + : As above but using --mask-repository* instead of rep-remove. : { $clone_root_cfg; @@ -18051,7 +18077,7 @@ else # Noop. # - $* --mask-repository $cn --deorphan ?libfoo; + $* --mask-repository-uuid "$cfg_uuid=$cn" --deorphan ?libfoo; $pkg_status -r libbar >>EOO; !libbar configured 1.1.0 -- cgit v1.1