diff options
30 files changed, 2006 insertions, 183 deletions
diff --git a/bpkg/pkg-build-collect.cxx b/bpkg/pkg-build-collect.cxx index 69c0eea..86dcb24 100644 --- a/bpkg/pkg-build-collect.cxx +++ b/bpkg/pkg-build-collect.cxx @@ -117,6 +117,7 @@ namespace bpkg (*action == build && (selected->system () != system || selected->version != available_version () || + deorphan () || (!system && (!config_vars.empty () || disfigure))))); } diff --git a/bpkg/pkg-build-collect.hxx b/bpkg/pkg-build-collect.hxx index f237b11..144fbd3 100644 --- a/bpkg/pkg-build-collect.hxx +++ b/bpkg/pkg-build-collect.hxx @@ -345,6 +345,19 @@ namespace bpkg // static const uint16_t build_reevaluate = 0x0008; + // Set if this build action is for deorphaning of an existing package. + // + // Note that to deorphan a package we need to re-fetch it from an existing + // repository fragment (even if its version doesn't change). + // + static const uint16_t build_deorphan = 0x0010; + + bool + deorphan () const + { + return (flags & build_deorphan) != 0; + } + bool configure_only () const; diff --git a/bpkg/pkg-build.cli b/bpkg/pkg-build.cli index 6aee9b0..05d4077 100644 --- a/bpkg/pkg-build.cli +++ b/bpkg/pkg-build.cli @@ -20,10 +20,11 @@ namespace bpkg "\h|SYNOPSIS| - \c{\b{bpkg pkg-build}|\b{build} [<options>] [\b{--upgrade}|\b{-u} | \b{--patch}|\b{-p}]\n + \c{\b{bpkg pkg-build}|\b{build} [<options>] [\b{--upgrade}|\b{-u} | \b{--patch}|\b{-p}] [\b{--deorphan}]\n \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ [<cfg-var>... \b{--}] <pkg-spec>...\n - \b{bpkg pkg-build}|\b{build} [<options>] \ \b{--upgrade}|\b{-u} | \b{--patch}|\b{-p}\n - \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ [<cfg-var>... \b{--}]} + \b{bpkg pkg-build}|\b{build} [<options>] (\b{--upgrade}|\b{-u} | \b{--patch}|\b{-p}) [\b{--deorphan}]\n + \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ [<cfg-var>... \b{--}]\n + \b{bpkg pkg-build}|\b{build} [<options>] \ \b{--deorphan} [<cfg-var>... \b{--}]} \c{<pkg-spec> = [<flags>](([<scheme>\b{:}]<pkg>[<ver-spec>])\b{,}...[\b{@}<rep-loc>] | \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ [\b{@}]<rep-loc> \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ | \n @@ -37,24 +38,28 @@ namespace bpkg The \cb{pkg-build} command builds one or more packages including all their dependencies. Besides building new packages, this command is also - used to upgrade or downgrade packages that are already present in the - configuration. And unless the \c{\b{--keep-unused}|\b{-K}} option is + used to upgrade or downgrade and/or deorphan packages that are already + present in the configuration (see below for details on orphan + packages). And unless the \c{\b{--keep-unused}|\b{-K}} option is specified, \cb{pkg-build} will also drop dependency packages that would otherwise no longer be used. The first form (one or more packages are specified) builds new or upgrades (by default or if \cb{--upgrade} is specified) or patches (if - \cb{--patch} is specified) the specified packages. The second form (no - arguments but either \cb{--upgrade} or \cb{--patch} is specified) - upgrades or patches all the held packages in the configuration (see - below for details on held package). - - In both forms specifying the \c{\b{--immediate}|\b{-i}} or + \cb{--patch} is specified) and/or deorphans (if \cb{--deorphan} is + specified) the specified packages. The second form (no arguments but + either \cb{--upgrade} or \cb{--patch} is specified) upgrades or patches + all the held packages in the configuration (see below for details on held + package). The third form (no arguments but \cb{--deorphan} is specified) + deorphans all the held packages in the configuration. + + In all forms specifying the \c{\b{--immediate}|\b{-i}} or \c{\b{--recursive}|\b{-r}} option causes \cb{pkg-build} to also upgrade - or patch the immediate or all dependencies of the specified (first form) - or held (second form) packages, respectively. Note also that in the first - form these options can only be specified with an explicit \cb{--upgrade} - or \cb{--patch}. + or patch and/or deorphan the immediate or all dependencies of the + specified (first form) or held (second and third forms) packages, + respectively. Note also that in the first form these options can only be + specified with an explicit \cb{--upgrade}, \cb{--patch}, or + \cb{--deorphan}. Each package can be specified as just the name (<pkg>) with optional version specification (<ver-spec>), in which case the source code for the @@ -144,6 +149,19 @@ namespace bpkg will have their versions held, that is, they will not be automatically upgraded. + It may happen that the repository where the source code for a package has + been fetched from is wiped from the configuration (e.g., as a result of + \l{bpkg-rep-fetch(1)} or \l{bpkg-rep-remove(1)}). We call such packages + \i{orphans}. Unless \cb{--deorphan} is specified, an orphan upgrade, + downgrade, or patching may leave it unchanged if there is no more + suitable version of the package available. If the \cb{--deorphan} option + is specified in the absence of the \cb{--upgrade} and \cb{--patch} + options and the package version is not explicitly specified, then the + version matching the orphan best in the following preference order will + be (re-)fetched from an existing repository and built: same version, + latest iteration, same revision, latest revision, latest patch, latest + available (see \l{bpkg#package-version Package Version} for details). + As an illustration, let's assume in the following example that the stable repository contains packages \cb{foo} \cb{1.0.0} as well as \cb{libfoo} \cb{1.0.0} and \cb{1.1.0} while testing \- \cb{libfoo} @@ -203,18 +221,25 @@ namespace bpkg all the constraints." } + bool --deorphan + { + "Replace orphan packages with the best matching available package + versions which satisfy all the constraints." + } + bool --immediate|-i { - "Also upgrade or patch immediate dependencies." + "Also upgrade, patch, or deorphan immediate dependencies." } bool --recursive|-r { - "Also upgrade or patch all dependencies, recursively." + "Also upgrade, patch, or deorphan all dependencies, recursively." } // Sometimes we may want to upgrade/patch the package itself but to - // patch/upgrade its dependencies. + // patch/upgrade its dependencies. Also we may want to deorphan + // dependencies, potentially upgrading/patching the package itself. // bool --upgrade-immediate { @@ -226,6 +251,11 @@ namespace bpkg "Patch immediate dependencies." } + bool --deorphan-immediate + { + "Deorphan immediate dependencies." + } + bool --upgrade-recursive { "Upgrade all dependencies, recursively." @@ -236,6 +266,11 @@ namespace bpkg "Patch all dependencies, recursively." } + bool --deorphan-recursive + { + "Deorphan all dependencies, recursively." + } + bool --dependency { "Build, upgrade, or downgrade a package as a dependency rather than to diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx index 2796550..a469a2b 100644 --- a/bpkg/pkg-build.cxx +++ b/bpkg/pkg-build.cxx @@ -102,30 +102,29 @@ namespace bpkg } } - // Return a patch version constraint for the selected package if it has a - // standard version, otherwise, if requested, issue a warning and return - // nullopt. + // Return a patch version constraint for the specified package version if it + // is a standard version. Otherwise, if requested, issue a warning and + // return nullopt. // // Note that the function may also issue a warning and return nullopt if the - // selected package minor version reached the limit (see - // standard-version.cxx for details). + // package minor version reached the limit (see standard-version.cxx for + // details). // static optional<version_constraint> - patch_constraint (const shared_ptr<selected_package>& sp, bool quiet = false) + patch_constraint (const package_name& nm, + const version& pv, + bool quiet = false) { - const package_name& nm (sp->name); - const version& sv (sp->version); - // Note that we don't pass allow_stub flag so the system wildcard version // will (naturally) not be patched. // - string vs (sv.string ()); + string vs (pv.string ()); optional<standard_version> v (parse_standard_version (vs)); if (!v) { if (!quiet) - warn << "unable to patch " << package_string (nm, sv) << + warn << "unable to patch " << package_string (nm, pv) << info << "package is not using semantic/standard version"; return nullopt; @@ -142,13 +141,43 @@ namespace bpkg catch (const invalid_argument&) { if (!quiet) - warn << "unable to patch " << package_string (nm, sv) << + warn << "unable to patch " << package_string (nm, pv) << info << "minor version limit reached"; return nullopt; } } + static inline optional<version_constraint> + patch_constraint (const shared_ptr<selected_package>& sp, bool quiet = false) + { + return patch_constraint (sp->name, sp->version, quiet); + } + + // Return true if the selected package is not configured as system and its + // repository fragment is not present in the ultimate dependent + // configurations (see dependent_repo_configs() for details) of this + // package. + // + static bool + orphan_package (database& db, const shared_ptr<selected_package>& sp) + { + assert (sp != nullptr); + + if (sp->system ()) + return false; + + const string& cn (sp->repository_fragment.canonical_name ()); + + for (database& ddb: dependent_repo_configs (db)) + { + if (ddb.find<repository_fragment> (cn) != nullptr) + return false; + } + + return true; + } + // List of dependency packages (specified with ? on the command line). // // If configuration is not specified for a system dependency package (db is @@ -167,7 +196,12 @@ namespace bpkg optional<version_constraint> constraint; // nullopt if unspecified. shared_ptr<selected_package> selected; bool system; - bool patch; // Only for an empty version. + + // true -- upgrade, false -- patch. + // + optional<bool> upgrade; // Only for absent constraint. + + bool deorphan; bool keep_out; bool disfigure; optional<dir_path> checkout_root; @@ -182,8 +216,27 @@ namespace bpkg // this dependency. If the result is a NULL available_package, then it is // either no longer used and can be dropped, or no changes to the dependency // are necessary. Otherwise, the result is available_package to - // upgrade/downgrade to as well as the repository fragment it must come - // from, and the system flag. + // upgrade/downgrade/deorphan to as well as the repository fragment it must + // come from, the system flag, and the database it must be configured in. + // + // If in the deorphan mode it turns out that the package is not an orphan + // and there is no version constraint specified and upgrade/patch is not + // requested, then assume that no changes are necessary for the dependency. + // Otherwise, if the package version is not constrained and no upgrade/patch + // is requested, then pick the version that matches the dependency version + // best in the following preference order: same version (up to iteration), + // latest iteration (think of deorphaning after renaming a directory + // repository), same revision (zero iteration), latest revision, latest + // patch, latest available package. Otherwise, always upgrade/downgrade the + // orphan or fail if no satisfactory version is available. Note that in the + // both cases (deorphan and upgrade/downgrade+deorphan) we may end up with + // the available package version being the same as the selected package + // version. In this case the dependency needs to be re-fetched from an + // existing repository. Also note that if the dependency needs to be + // deorphaned the caller may need to cache the original orphan version. This + // way on the subsequent calls this function still considers this package as + // an orphan and uses its original version to deduce the best match, which + // may change due, for example, a change of the constraining dependents set. // // If the package version that satisfies explicitly specified dependency // version constraint can not be found in the dependents repositories, then @@ -194,13 +247,19 @@ namespace bpkg // struct evaluate_result { - // The system flag is meaningless if the unused flag is true. + // The system and orphan members are meaningless if the unused flag is + // true. // reference_wrapper<database> db; shared_ptr<available_package> available; lazy_shared_ptr<bpkg::repository_fragment> repository_fragment; bool unused; bool system; + + // Original orphan version which needs to be deorphaned. May only present + // for the deorphan mode. + // + optional<version> orphan; }; struct dependent_constraint @@ -216,18 +275,21 @@ namespace bpkg }; using dependent_constraints = vector<dependent_constraint>; + using deorphaned_dependencies = map<package_key, version>; - static optional<evaluate_result> + static evaluate_result evaluate_dependency (database&, const shared_ptr<selected_package>&, const optional<version_constraint>& desired, bool desired_sys, database& desired_db, const shared_ptr<selected_package>& desired_db_sp, - bool patch, + optional<bool> upgrade, + bool deorphan, bool explicitly, const config_repo_fragments&, const dependent_constraints&, + const deorphaned_dependencies&, bool ignore_unsatisfiable); // If there are no user expectations regarding this dependency, then we give @@ -242,6 +304,7 @@ namespace bpkg const shared_ptr<selected_package>& sp, const dependency_packages& deps, bool no_move, + const deorphaned_dependencies& deorphaned_deps, bool ignore_unsatisfiable) { tracer trace ("evaluate_dependency"); @@ -256,7 +319,8 @@ namespace bpkg nullptr /* available */, nullptr /* repository_fragment */, false /* unused */, - false /* system */}; + false /* system */, + nullopt /* orphan */}; }; // Only search for the user expectations regarding this dependency if it @@ -391,7 +455,8 @@ namespace bpkg nullptr /* available */, nullptr /* repository_fragment */, true /* unused */, - false /* system */}; + false /* system */, + nullopt /* orphan */}; } // The requested dependency database, version constraint, and system flag. @@ -455,10 +520,12 @@ namespace bpkg dsys, ddb, dsp, - i->patch, + i->upgrade, + i->deorphan, true /* explicitly */, repo_frags, dpt_constrs, + deorphaned_deps, ignore_unsatisfiable); } @@ -485,17 +552,19 @@ namespace bpkg } }; - static optional<evaluate_result> + static evaluate_result evaluate_dependency (database& db, const shared_ptr<selected_package>& sp, const optional<version_constraint>& dvc, bool dsys, database& ddb, const shared_ptr<selected_package>& dsp, - bool patch, + optional<bool> upgrade, + bool deorphan, bool explicitly, const config_repo_fragments& rfs, const dependent_constraints& dpt_constrs, + const deorphaned_dependencies& deorphaned_deps, bool ignore_unsatisfiable) { tracer trace ("evaluate_dependency"); @@ -508,7 +577,8 @@ namespace bpkg nullptr /* available */, nullptr /* repository_fragment */, false /* unused */, - false /* system */}; + false /* system */, + nullopt /* orphan */}; }; // Build the list of available packages for the potential up/down-grade @@ -520,6 +590,7 @@ namespace bpkg // picking the latest one just to make sure the package is recognized. // optional<version_constraint> c; + bool patch (upgrade && !*upgrade); if (!dvc) { @@ -544,9 +615,62 @@ namespace bpkg if (afs.empty () && dsys && c) afs = find_available (nm, nullopt, rfs); + // In the deorphan mode check that the dependency is an orphan or was + // deorphaned on some previous refinement iteration. If that's not the + // case, then just disable the deorphan mode for this dependency and, if + // the version is not constrained and upgrade/patch is not requested, bail + // out indicating that no change is required. + // + // Note that in the move mode (dsp != sp) we deorphan the dependency in + // its destination configuration, if present. In the worst case scenario + // both the source and destination selected packages may need to be + // deorphaned since the source selected package may also stay if some + // dependents were not repointed to the new dependency (remember that the + // move mode is actually a copy mode). We, however, have no easy way to + // issue recommendations for both the old and the new dependencies at the + // moment. Given that in the common case the old dependency get dropped, + // let's keep it simple and do nothing about the old dependency and see + // how it goes. + // + const version* deorphaned (nullptr); + + if (deorphan) + { + bool orphan (dsp != nullptr && !dsp->system () && !dsys); + + if (orphan) + { + auto i (deorphaned_deps.find (package_key (ddb, nm))); + + if (i == deorphaned_deps.end ()) + { + orphan = orphan_package (ddb, dsp); + } + else + deorphaned = &i->second; + } + + if (!orphan) + { + if (!dvc && !upgrade) + { + l5 ([&]{trace << *sp << db << ": non-orphan";}); + return no_change (); + } + + deorphan = false; + } + } + // Go through up/down-grade candidates and pick the first one that - // satisfies all the dependents. Collect (and sort) unsatisfied dependents - // per the unsatisfiable version in case we need to print them. + // satisfies all the dependents. In the deorphan mode if the package + // version is not constrained and upgrade/patch is not requested, then + // pick the version that matches the dependency version best (see the + // function description for details). Collect (and sort) unsatisfied + // dependents per the unsatisfiable version in case we need to print them. + // + // NOTE: don't forget to update the find_orphan_match() lambda if changing + // anything deorphan-related here. // using sp_set = set<config_selected_package>; @@ -558,14 +682,72 @@ namespace bpkg (ddb.system_repository && ddb.system_repository->find (nm) != nullptr)); - for (auto& af: afs) + // Version to deorphan (original orphan version). + // + const version* dov (deorphaned != nullptr ? deorphaned : + deorphan ? &dsp->version : + nullptr); + + optional<version> dovr; // Revision of the above. + optional<version_constraint> dopc; // Patch constraint for the above. + + if (deorphan && !dvc && !upgrade) // Pick orphan version best match? + { + dovr = version (dov->epoch, + dov->upstream, + dov->release, + dov->revision, + 0 /* iteration */); + + dopc = patch_constraint (nm, *dovr, true /* quiet */); + } + + using available = pair<shared_ptr<available_package>, + lazy_shared_ptr<repository_fragment>>; + + available deorphan_latest_iteration; + available deorphan_same_revision; + available deorphan_latest_revision; + available deorphan_patch; + available deorphan_available; + + // If the dependency is deorphaned to the same version as on the previous + // call, then return the "no change" result. Otherwise, return the + // deorphan result. + // + auto deorphan_result = [&sp, &db, + &ddb, &dsp, + dsys, + deorphaned, dov, + &no_change, + &trace] (available&& a, const char* what) + { + if (deorphaned != nullptr && dsp->version == a.first->version) + { + l5 ([&]{trace << *sp << db << ": already deorphaned";}); + return no_change (); + } + + l5 ([&]{trace << *sp << db << ": deorphan to " << what << ' ' + << package_string (sp->name, a.first->version) + << ddb;}); + + return evaluate_result { + ddb, move (a.first), move (a.second), + false /* unused */, + dsys, + *dov}; + }; + + for (available& af: afs) { shared_ptr<available_package>& ap (af.first); const version& av (!dsys ? ap->version : *ap->system_version (ddb)); // If we aim to upgrade to the latest version and it tends to be less // then the selected one, then what we currently have is the best that - // we can get, and so we return the "no change" result. + // we can get, and so we return the "no change" result, unless we are + // deorphaning. // // Note that we also handle a package stub here. // @@ -576,14 +758,13 @@ namespace bpkg // For the selected system package we still need to pick a source // package version to downgrade to. // - if (!dsp->system ()) + if (!dsp->system () && !deorphan) { l5 ([&]{trace << *dsp << ddb << ": best";}); return no_change (); } - // We can not upgrade the (system) package to a stub version, so just - // skip it. + // We can not upgrade the package to a stub version, so just skip it. // if (ap->stub ()) { @@ -629,28 +810,103 @@ namespace bpkg continue; } - // If the best satisfactory version and the desired system flag perfectly - // match the ones of the selected package, then no package change is - // required. Otherwise, recommend an up/down-grade. - // - if (dsp != nullptr && av == dsp->version && dsp->system () == dsys) + if (dovr) // Deorphan picking the best match? { - l5 ([&]{trace << *dsp << ddb << ": unchanged";}); - return no_change (); + // If the orphan version is encountered, then we are done. Otherwise, + // save the version if it matches any of the orphan match preferences. + // + if (av == *dov) + return deorphan_result (move (af), "same version"); + + if (deorphan_latest_iteration.first == nullptr && + av.compare (*dovr, true /* revision */, false /* iteration */) == 0) + deorphan_latest_iteration = af; + + if (av == *dovr) + { + // Can only appear once. + // + assert (deorphan_same_revision.first == nullptr); + + deorphan_same_revision = af; + } + + if (deorphan_latest_revision.first == nullptr && + av.compare (*dovr, false /* revision */) == 0) + deorphan_latest_revision = af; + + if (deorphan_patch.first == nullptr && dopc && satisfies (av, *dopc)) + deorphan_patch = af; + + if (deorphan_available.first == nullptr) + deorphan_available = af; + + // If the available version is less then the orphan revision then we + // can bail out from the loop, since all the versions from the + // preference list has already been encountered, if present. + // + if (av.compare (*dovr, false /* revision */) < 0) + { + assert (deorphan_latest_iteration.first != nullptr || + deorphan_same_revision.first != nullptr || + deorphan_latest_revision.first != nullptr || + deorphan_patch.first != nullptr || + deorphan_available.first != nullptr); + break; + } } + else + { + // If the best satisfactory version and the desired system flag + // perfectly match the ones of the selected package, then no package + // change is required, unless we are deorphaning. Otherwise, recommend + // an upgrade/downgrade/deorphaning. + // + if (dsp != nullptr && + av == dsp->version && + dsp->system () == dsys && + !deorphan) + { + l5 ([&]{trace << *dsp << ddb << ": unchanged";}); + return no_change (); + } - l5 ([&]{trace << *sp << db << ": update to " - << package_string (nm, av, dsys) << ddb;}); + l5 ([&]{trace << *sp << db << ": update" + << (deorphan ? "/deorphan" : "") << " to " + << package_string (nm, av, dsys) << ddb;}); - return evaluate_result { - ddb, move (ap), move (af.second), false /* unused */, dsys}; + return evaluate_result { + ddb, move (ap), move (af.second), + false /* unused */, + dsys, + deorphan ? *dov : optional<version> ()}; + } } + if (deorphan_latest_iteration.first != nullptr) + return deorphan_result (move (deorphan_latest_iteration), + "latest iteration"); + + if (deorphan_same_revision.first != nullptr) + return deorphan_result (move (deorphan_same_revision), + "same revision"); + + if (deorphan_latest_revision.first != nullptr) + return deorphan_result (move (deorphan_latest_revision), + "latest revision"); + + if (deorphan_patch.first != nullptr) + return deorphan_result (move (deorphan_patch), "patch"); + + if (deorphan_available.first != nullptr) + return deorphan_result (move (deorphan_available), "latest available"); + // If we aim to upgrade to the latest version, then what we currently have // is the only thing that we can get, and so returning the "no change" - // result, unless we need to upgrade a package configured as system. + // result, unless we need to upgrade a package configured as system or to + // deorphan. // - if (!dvc && dsp != nullptr && !dsp->system ()) + if (!dvc && dsp != nullptr && !dsp->system () && !deorphan) { assert (!dsys); // Version cannot be empty for the system package. @@ -682,9 +938,10 @@ namespace bpkg if (!dvc && patch) { - // Otherwise, we should have bailed out earlier (see above). + // Otherwise, we should have bailed out earlier returning "no change" + // (see above). // - assert (dsp != nullptr && dsp->system ()); + assert (dsp != nullptr && (dsp->system () || deorphan)); // Patch (as any upgrade) of a system package is always explicit, so // we always fail and never treat the package as being up to date. @@ -699,10 +956,10 @@ namespace bpkg << " is not available from its dependents' repositories"; else // The only available package is a stub. { - // Note that we don't advise to "build" the package as a system one as - // it is already as such (see above). + // Otherwise, we should have bailed out earlier, returning "no change" + // rather then setting the stub flag to true (see above). // - assert (!dvc && !dsys && dsp != nullptr && dsp->system ()); + assert (!dvc && !dsys && dsp != nullptr && (dsp->system () || deorphan)); fail << package_string (nm, dvc) << ddb << " is not available in " << "source from its dependents' repositories"; @@ -748,31 +1005,38 @@ namespace bpkg } // List of dependent packages whose immediate/recursive dependencies must be - // upgraded (specified with -i/-r on the command line). + // upgraded and/or deorphaned (specified with -i/-r on the command line). // struct recursive_package { - database& db; - package_name name; - bool upgrade; // true -- upgrade, false -- patch. - bool recursive; // true -- recursive, false -- immediate. + database& db; + package_name name; + + // Recursive/immediate upgrade/patch. Note the upgrade member is only + // meaningful if recursive is present. + // + optional<bool> recursive; // true -- recursive, false -- immediate. + bool upgrade; // true -- upgrade, false -- patch. + + // Recursive/immediate deorphaning. + // + optional<bool> deorphan; // true -- recursive, false -- immediate. }; using recursive_packages = vector<recursive_package>; // Recursively check if immediate dependencies of this dependent must be - // upgraded or patched. Return true if it must be upgraded, false if - // patched, and nullopt otherwise. + // upgraded or patched and/or deorphaned. // // Cache the results of this function calls to avoid multiple traversals of // the same dependency graphs. // - struct upgrade_dependency_key + struct upgrade_dependencies_key { package_key dependent; bool recursion; bool - operator< (const upgrade_dependency_key& v) const + operator< (const upgrade_dependencies_key& v) const { if (recursion != v.recursion) return recursion < v.recursion; @@ -781,20 +1045,27 @@ namespace bpkg } }; - using upgrade_dependency_cache = map<upgrade_dependency_key, optional<bool>>; + struct upgrade_deorphan + { + optional<bool> upgrade; // true -- upgrade, false -- patch. + bool deorphan; + }; + + using upgrade_dependencies_cache = map<upgrade_dependencies_key, + upgrade_deorphan>; - static optional<bool> + static upgrade_deorphan upgrade_dependencies (database& db, const package_name& nm, const recursive_packages& rs, - upgrade_dependency_cache& cache, + upgrade_dependencies_cache& cache, bool recursion = false) { // If the result of the upgrade_dependencies() call for these dependent // and recursion flag value is cached, then return that. Otherwise, cache // the calculated result prior to returning it to the caller. // - upgrade_dependency_key k {package_key (db, nm), recursion}; + upgrade_dependencies_key k {package_key (db, nm), recursion}; { auto i (cache.find (k)); @@ -808,13 +1079,21 @@ namespace bpkg return i.name == nm && i.db == db; })); - optional<bool> r; + upgrade_deorphan r {nullopt /* upgrade */, false /* deorphan */}; - if (i != rs.end () && i->recursive >= recursion) + if (i != rs.end ()) { - r = i->upgrade; + if (i->recursive && *i->recursive >= recursion) + r.upgrade = i->upgrade; + + if (i->deorphan && *i->deorphan >= recursion) + r.deorphan = true; - if (*r) // Upgrade (vs patch)? + // If we both upgrade and deorphan, then we can bail out since the value + // may not change any further (upgrade wins patch and deorphaning can't + // be canceled). + // + if (r.upgrade && *r.upgrade && r.deorphan) { cache[move (k)] = r; return r; @@ -829,21 +1108,26 @@ namespace bpkg // configured packages due to a dependency cycle (see order() for // details). // - if (optional<bool> u = upgrade_dependencies (ddb, - pd.name, - rs, - cache, - true /* recursion */)) + upgrade_deorphan ud ( + upgrade_dependencies (ddb, pd.name, rs, cache, true /* recursion */)); + + if (ud.upgrade || ud.deorphan) { - if (!r || *r < *u) // Upgrade wins patch. - { - r = u; + // Upgrade wins patch. + // + if (ud.upgrade && (!r.upgrade || *r.upgrade < *ud.upgrade)) + r.upgrade = *ud.upgrade; - if (*r) // Upgrade (vs patch)? - { - cache[move (k)] = r; - return r; - } + if (ud.deorphan) + r.deorphan = true; + + // If we both upgrade and deorphan, then we can bail out (see above + // for details). + // + if (r.upgrade && *r.upgrade && r.deorphan) + { + cache[move (k)] = r; + return r; } } } @@ -868,8 +1152,9 @@ namespace bpkg evaluate_recursive (database& db, const shared_ptr<selected_package>& sp, const recursive_packages& recs, + const deorphaned_dependencies& deorphaned_deps, bool ignore_unsatisfiable, - upgrade_dependency_cache& cache) + upgrade_dependencies_cache& cache) { tracer trace ("evaluate_recursive"); @@ -886,7 +1171,7 @@ namespace bpkg // (immediate) dependents that have a hit (direct or indirect) in recs. // Note, however, that we collect constraints from all the dependents. // - optional<bool> upgrade; + upgrade_deorphan ud {nullopt /* upgrade */, false /* deorphan */}; for (database& ddb: db.dependent_configs ()) { @@ -896,10 +1181,17 @@ namespace bpkg dpt_constrs.emplace_back (ddb, p, move (pd.constraint)); - if (optional<bool> u = upgrade_dependencies (ddb, pd.name, recs, cache)) + upgrade_deorphan u (upgrade_dependencies (ddb, pd.name, recs, cache)); + + if (u.upgrade || u.deorphan) { - if (!upgrade || *upgrade < *u) // Upgrade wins patch. - upgrade = u; + // Upgrade wins patch. + // + if (u.upgrade && (!ud.upgrade || *ud.upgrade < *u.upgrade)) + ud.upgrade = *u.upgrade; + + if (u.deorphan) + ud.deorphan = true; } else continue; @@ -915,7 +1207,7 @@ namespace bpkg } } - if (!upgrade) + if (!ud.upgrade && !ud.deorphan) { l5 ([&]{trace << *sp << db << ": no hit";}); return nullopt; @@ -930,10 +1222,12 @@ namespace bpkg false /* desired_sys */, db, sp, - !*upgrade /* patch */, + ud.upgrade, + ud.deorphan, false /* explicitly */, repo_frags, dpt_constrs, + deorphaned_deps, ignore_unsatisfiable)); // Translate the "no change" result into nullopt. @@ -965,13 +1259,14 @@ namespace bpkg dr << fail << "both --immediate|-i and --recursive|-r specified"; // The --immediate or --recursive option can only be specified with an - // explicit --upgrade or --patch. + // explicit --upgrade, --patch, or --deorphan. // if (const char* n = (o.immediate () ? "--immediate" : o.recursive () ? "--recursive" : nullptr)) { - if (!o.upgrade () && !o.patch ()) - dr << fail << n << " requires explicit --upgrade|-u or --patch|-p"; + if (!o.upgrade () && !o.patch () && !o.deorphan ()) + dr << fail << n << " requires explicit --upgrade|-u, --patch|-p, or " + << "--deorphan"; } if (((o.upgrade_immediate () ? 1 : 0) + @@ -981,6 +1276,10 @@ namespace bpkg dr << fail << "multiple --(upgrade|patch)-(immediate|recursive) " << "specified"; + if (o.deorphan_immediate () && o.deorphan_recursive ()) + dr << fail << "both --deorphan-immediate and --deorphan-recursive " + << "specified"; + if (multi_config ()) { if (const char* opt = o.config_name_specified () ? "--config-name" : @@ -1007,13 +1306,16 @@ namespace bpkg dst.recursive (src.recursive ()); // If -r|-i was specified at the package level, then so should - // -u|-p. + // -u|-p and --deorphan. // if (!(dst.upgrade () || dst.patch ())) { dst.upgrade (src.upgrade ()); dst.patch (src.patch ()); } + + if (!dst.deorphan ()) + dst.deorphan (src.deorphan ()); } if (!(dst.upgrade_immediate () || dst.upgrade_recursive () || @@ -1025,6 +1327,12 @@ namespace bpkg dst.patch_recursive (src.patch_recursive ()); } + if (!(dst.deorphan_immediate () || dst.deorphan_recursive ())) + { + dst.deorphan_immediate (src.deorphan_immediate ()); + dst.deorphan_recursive (src.deorphan_recursive ()); + } + dst.dependency (src.dependency () || dst.dependency ()); dst.keep_out (src.keep_out () || dst.keep_out ()); dst.disfigure (src.disfigure () || dst.disfigure ()); @@ -1068,19 +1376,22 @@ namespace bpkg static bool compare_options (const pkg_options& x, const pkg_options& y) { - return x.keep_out () == y.keep_out () && - x.disfigure () == y.disfigure () && - x.dependency () == y.dependency () && - x.upgrade () == y.upgrade () && - x.patch () == y.patch () && - x.immediate () == y.immediate () && - x.recursive () == y.recursive () && - x.upgrade_immediate () == y.upgrade_immediate () && - x.upgrade_recursive () == y.upgrade_recursive () && - x.patch_immediate () == y.patch_immediate () && - x.patch_recursive () == y.patch_recursive () && - x.checkout_root () == y.checkout_root () && - x.checkout_purge () == y.checkout_purge (); + return x.keep_out () == y.keep_out () && + x.disfigure () == y.disfigure () && + x.dependency () == y.dependency () && + x.upgrade () == y.upgrade () && + x.patch () == y.patch () && + x.deorphan () == y.deorphan () && + x.immediate () == y.immediate () && + x.recursive () == y.recursive () && + x.upgrade_immediate () == y.upgrade_immediate () && + x.upgrade_recursive () == y.upgrade_recursive () && + x.patch_immediate () == y.patch_immediate () && + x.patch_recursive () == y.patch_recursive () && + x.deorphan_immediate () == y.deorphan_immediate () && + x.deorphan_recursive () == y.deorphan_recursive () && + x.checkout_root () == y.checkout_root () && + x.checkout_purge () == y.checkout_purge (); } int @@ -1122,7 +1433,7 @@ namespace bpkg fail << "both --sys-no-query and --sys-install specified" << info << "run 'bpkg help pkg-build' for more information"; - if (!args.more () && !o.upgrade () && !o.patch ()) + if (!args.more () && !o.upgrade () && !o.patch () && !o.deorphan ()) fail << "package name argument expected" << info << "run 'bpkg help pkg-build' for more information"; @@ -1668,16 +1979,19 @@ namespace bpkg const pkg_options& o (a.options); - add_bool ("--keep-out", o.keep_out ()); - add_bool ("--disfigure", o.disfigure ()); - add_bool ("--upgrade", o.upgrade ()); - add_bool ("--patch", o.patch ()); - add_bool ("--immediate", o.immediate ()); - add_bool ("--recursive", o.recursive ()); - add_bool ("--upgrade-immediate", o.upgrade_immediate ()); - add_bool ("--upgrade-recursive", o.upgrade_recursive ()); - add_bool ("--patch-immediate", o.patch_immediate ()); - add_bool ("--patch-recursive", o.patch_recursive ()); + add_bool ("--keep-out", o.keep_out ()); + add_bool ("--disfigure", o.disfigure ()); + add_bool ("--upgrade", o.upgrade ()); + add_bool ("--patch", o.patch ()); + add_bool ("--deorphan", o.deorphan ()); + add_bool ("--immediate", o.immediate ()); + add_bool ("--recursive", o.recursive ()); + add_bool ("--upgrade-immediate", o.upgrade_immediate ()); + add_bool ("--upgrade-recursive", o.upgrade_recursive ()); + add_bool ("--patch-immediate", o.patch_immediate ()); + add_bool ("--patch-recursive", o.patch_recursive ()); + add_bool ("--deorphan-immediate", o.deorphan_immediate ()); + add_bool ("--deorphan-recursive", o.deorphan_recursive ()); if (o.checkout_root_specified ()) add_string ("--checkout-root", o.checkout_root ().string ()); @@ -2324,6 +2638,93 @@ namespace bpkg transaction t (mdb); + // Return the available package that matches the specified orphan best + // in the following version preference order: same version, latest + // iteration, same revision, latest revision, latest patch, latest + // available (see evaluate_dependency() description for details). Also + // return the repository fragment the package comes from. Return a pair + // of NULLs if no suitable package has been found. + // + auto find_orphan_match = + [] (const shared_ptr<selected_package>& sp, + const lazy_shared_ptr<repository_fragment>& root) + { + using available = pair<shared_ptr<available_package>, + lazy_shared_ptr<repository_fragment>>; + + assert (sp != nullptr); + + const package_name& n (sp->name); + const version& v (sp->version); + optional<version_constraint> vc {version_constraint (v)}; + + version vr (v.epoch, + v.upstream, + v.release, + v.revision, + 0 /* iteration */); + + optional<version_constraint> vrc {version_constraint (vr)}; + + optional<version_constraint> pc ( + patch_constraint (n, vr, true /* quiet */)); + + // Note: explicit revision makes query_available() to always consider + // revisions (but not iterations) regardless of the revision argument + // value. + // + optional<version_constraint> verc { + version_constraint (version (v.epoch, + v.upstream, + v.release, + v.revision ? v.revision : 0, + 0 /* iteration */))}; + + optional<version_constraint> vlc { + version_constraint (version (v.epoch, + v.upstream, + v.release, + nullopt, + 0 /* iteration */))}; + + // Find the latest available non-stub package, optionally matching a + // constraint and considering revision. If a package is found, then + // cache it together with the repository fragment it comes from and + // return true. + // + available find_result; + auto find = [&n, + &root, + &find_result] (const optional<version_constraint>& c, + bool revision = false) -> bool + { + available r ( + find_available_one (n, c, root, false /* prereq */, revision)); + + const shared_ptr<available_package>& ap (r.first); + + if (ap != nullptr && !ap->stub ()) + { + find_result = move (r); + return true; + } + else + return false; + }; + + if (find (vc, true) || // Same iteration. + find (verc, false) || // Latest iteration. + find (vrc, true) || // Same revision. + find (vlc, false) || // Latest revision. + find (pc) || // Patch. + find (nullopt)) // Latest available. + { + return find_result; + } + + return available (); + }; + // Here is what happens here: for unparsed package args we are going to // try and guess whether we are dealing with a package archive, package // directory, or package name/version by first trying it as an archive, @@ -2509,6 +2910,7 @@ namespace bpkg // shared_ptr<selected_package> sp; bool patch (false); + bool deorphan (false); if (ap == nullptr) { @@ -2548,12 +2950,13 @@ namespace bpkg lazy_shared_ptr<repository_fragment> root (*pdb, empty_string); - // Either get the user-specified version or the latest allowed - // for a source code package. For a system package we will try - // to find the available package that matches the user-specified - // system version (preferable for the configuration negotiation - // machinery) and, if fail, fallback to picking the latest one - // just to make sure the package is recognized. + // Get the user-specified version, the latest allowed version, + // or the orphan best match for a source code package. For a + // system package we will try to find the available package that + // matches the user-specified system version (preferable for the + // configuration negotiation machinery) and, if fail, fallback + // to picking the latest one just to make sure the package is + // recognized. // optional<version_constraint> c; @@ -2583,7 +2986,39 @@ namespace bpkg else if (!sys || !wildcard (*pa.constraint)) c = pa.constraint; - auto rp (find_available_one (pa.name, c, root)); + if (pa.options.deorphan ()) + { + if (!sys) + { + if (sp == nullptr) + sp = pdb->find<selected_package> (pa.name); + + if (sp != nullptr && orphan_package (*pdb, sp)) + deorphan = true; + } + + // If the package is not an orphan, its version is not + // constrained and upgrade/patch is not requested, then just + // skip the package. + // + if (!deorphan && + !pa.constraint && + !pa.options.upgrade () && + !pa.options.patch ()) + { + ++i; + continue; + } + } + + pair<shared_ptr<available_package>, + lazy_shared_ptr<repository_fragment>> rp ( + deorphan && + !pa.constraint && + !pa.options.upgrade () && + !pa.options.patch () + ? find_orphan_match (sp, root) + : find_available_one (pa.name, c, root)); if (rp.first == nullptr && sys && c) rp = find_available_one (pa.name, nullopt, root); @@ -2606,22 +3041,55 @@ namespace bpkg continue; // Save (both packages to hold and dependencies) as dependents for - // recursive upgrade. + // recursive upgrade/deorphaning. // { - optional<bool> u; - optional<bool> r; + // Recursive/immediate upgrade/patch. + // + optional<bool> r; // true -- recursive, false -- immediate. + optional<bool> u; // true -- upgrade, false -- patch. + + // Recursive/immediate deorphaning. + // + optional<bool> d; // true -- recursive, false -- immediate. const auto& po (pa.options); - if (po.upgrade_immediate ()) { u = true; r = false; } - else if (po.upgrade_recursive ()) { u = true; r = true; } - else if ( po.patch_immediate ()) { u = false; r = false; } - else if ( po.patch_recursive ()) { u = false; r = true; } - else if ( po.immediate ()) { u = po.upgrade (); r = false; } - else if ( po.recursive ()) { u = po.upgrade (); r = true; } + // Note that, for example, --upgrade-immediate wins over the + // --upgrade --recursive options pair. + // + if (po.immediate ()) + { + if (po.upgrade () || po.patch ()) + { + r = false; + u = po.upgrade (); + } - if (r) + if (po.deorphan ()) + d = false; + } + else if (po.recursive ()) + { + if (po.upgrade () || po.patch ()) + { + r = true; + u = po.upgrade (); + } + + if (po.deorphan ()) + d = true; + } + + if (po.upgrade_immediate ()) { u = true; r = false; } + else if (po.upgrade_recursive ()) { u = true; r = true; } + else if ( po.patch_immediate ()) { u = false; r = false; } + else if ( po.patch_recursive ()) { u = false; r = true; } + + if (po.deorphan_immediate ()) { d = false; } + else if (po.deorphan_recursive ()) { d = true; } + + if (r || d) { l4 ([&]{trace << "stash recursive package " << arg_string (pa);}); @@ -2630,7 +3098,9 @@ namespace bpkg // configuration. // if (pdb != nullptr) - rec_pkgs.push_back (recursive_package {*pdb, pa.name, *u, *r}); + rec_pkgs.push_back (recursive_package {*pdb, pa.name, + r, u && *u, + d}); } } @@ -2680,7 +3150,10 @@ namespace bpkg move (pa.constraint), move (sp), sys, - pa.options.patch (), + (pa.options.upgrade () || pa.options.patch () + ? pa.options.upgrade () + : optional<bool> ()), + pa.options.deorphan (), pa.options.keep_out (), pa.options.disfigure (), (pa.options.checkout_root_specified () @@ -2754,11 +3227,12 @@ namespace bpkg break; // Otherwise, our only chance is that the already selected object - // satisfies the version constraint. + // satisfies the version constraint, unless we are deorphaning. // - if (sp != nullptr && - !sp->system () && - satisfies (sp->version, pa.constraint)) + if (sp != nullptr && + !sp->system () && + satisfies (sp->version, pa.constraint) && + !deorphan) break; // Derive ap from sp below. found = false; @@ -2778,14 +3252,17 @@ namespace bpkg // we have a newer version, we treat it as an upgrade request; // otherwise, why specify the package in the first place? We just // need to check if what we already have is "better" (i.e., - // newer). + // newer), unless we are deorphaning. // - if (sp != nullptr && !sp->system () && ap->version < sp->version) + if (sp != nullptr && + !sp->system () && + ap->version < sp->version && + !deorphan) ap = nullptr; // Derive ap from sp below. } else { - if (sp == nullptr || sp->system ()) + if (sp == nullptr || sp->system () || deorphan) found = false; // Otherwise, derive ap from sp below. @@ -2892,7 +3369,7 @@ namespace bpkg move (pa.config_vars), {package_key {mdb, ""}}, // Required by (command line). false, // Required by dependents. - 0}; // State flags. + deorphan ? build_package::build_deorphan : uint16_t (0)}; l4 ([&]{trace << "stash held package " << p.available_name_version_db ();}); @@ -2919,7 +3396,7 @@ namespace bpkg // command line option to enable this behavior. // if (hold_pkgs.empty () && dep_pkgs.empty () && - (o.upgrade () || o.patch ())) + (o.upgrade () || o.patch () || o.deorphan ())) { for (database& cdb: current_configs) { @@ -2953,7 +3430,26 @@ namespace bpkg continue; } - auto apr (find_available_one (name, pc, root)); + bool deorphan (false); + + if (o.deorphan ()) + { + // If the package is not an orphan and upgrade/patch is not + // requested, then just skip the package. + // + if (orphan_package (cdb, sp)) + deorphan = true; + else if (!o.upgrade () && !o.patch ()) + continue; + } + + // In the deorphan mode with no upgrade/patch requested pick the + // version that matches the orphan best. Otherwise, pick the patch + // or the latest available version, as requested. + // + auto apr (deorphan && !o.upgrade () && !o.patch () + ? find_orphan_match (sp, root) + : find_available_one (name, pc, root)); shared_ptr<available_package> ap (move (apr.first)); if (ap == nullptr || ap->stub ()) @@ -2961,11 +3457,13 @@ namespace bpkg diag_record dr (fail); dr << name << " is not available"; - if (ap != nullptr) + if (ap != nullptr) // Stub? + { dr << " in source" << info << "consider building it as " - << package_string (name, version (), true /* system */) - << " if it is available from the system"; + << package_string (name, version (), true /* system */) + << " if it is available from the system"; + } // Let's help the new user out here a bit. // @@ -3003,19 +3501,28 @@ namespace bpkg strings (), // Configuration variables. {package_key {mdb, ""}}, // Required by (command line). false, // Required by dependents. - 0}; // State flags. + deorphan ? build_package::build_deorphan : uint16_t (0)}; l4 ([&]{trace << "stash held package " << p.available_name_version_db ();}); hold_pkgs.push_back (move (p)); - // If there are also -i|-r, then we are also upgrading dependencies - // of all held packages. + // If there are also -i|-r, then we are also upgrading and/or + // deorphaning dependencies of all held packages. // if (o.immediate () || o.recursive ()) - rec_pkgs.push_back ( - recursive_package {cdb, name, o.upgrade (), o.recursive ()}); + { + rec_pkgs.push_back (recursive_package { + cdb, name, + (o.upgrade () || o.patch () + ? o.recursive () + : optional<bool> ()), + o.upgrade (), + (o.deorphan () + ? o.recursive () + : optional<bool> ())}); + } } } } @@ -3130,8 +3637,10 @@ namespace bpkg lazy_shared_ptr<bpkg::repository_fragment> repository_fragment; bool system; + bool deorphan; }; vector<dep> deps; + deorphaned_dependencies deorphaned_deps; replaced_versions replaced_vers; postponed_dependencies postponed_deps; @@ -3642,7 +4151,7 @@ namespace bpkg strings (), // Configuration variables. {package_key {mdb, ""}}, // Required by (command line). false, // Required by dependents. - 0}; // State flags. + d.deorphan ? build_package::build_deorphan : uint16_t (0)}; build_package_refs dep_chain; @@ -3906,7 +4415,8 @@ namespace bpkg auto eval_dep = [&dep_pkgs, &rec_pkgs, &o, - cache = upgrade_dependency_cache {}] ( + &deorphaned_deps, + cache = upgrade_dependencies_cache {}] ( database& db, const shared_ptr<selected_package>& sp, bool ignore_unsatisfiable = true) mutable @@ -3921,6 +4431,7 @@ namespace bpkg sp, dep_pkgs, o.no_move (), + deorphaned_deps, ignore_unsatisfiable); // If none, then see for the recursive dependency upgrade @@ -3933,6 +4444,7 @@ namespace bpkg r = evaluate_recursive (db, sp, rec_pkgs, + deorphaned_deps, ignore_unsatisfiable, cache); @@ -3969,11 +4481,12 @@ namespace bpkg bool s (false); database& db (i->db); + const package_name& nm (i->name); // Here we scratch if evaluate changed its mind or if the resulting // version doesn't match what we expect it to be. // - if (auto sp = db.find<selected_package> (i->name)) + if (auto sp = db.find<selected_package> (nm)) { const version& dv (target_version (db, i->available, i->system)); @@ -3989,6 +4502,8 @@ namespace bpkg if (s) { scratch_exe = true; // Rebuild the plan from scratch. + + deorphaned_deps.erase (package_key (db, nm)); i = deps.erase (i); } else @@ -4022,8 +4537,12 @@ namespace bpkg // make sure that the unsatisfiable dependency, if left, is // reported. // - auto need_refinement = [&eval_dep, &deps, &rec_pkgs, &dep_dbs, &o] ( - bool diag = false) -> bool + auto need_refinement = [&eval_dep, + &deps, + &rec_pkgs, + &dep_dbs, + &deorphaned_deps, + &o] (bool diag = false) -> bool { // Examine the new dependency set for any up/down-grade/drops. // @@ -4054,11 +4573,20 @@ namespace bpkg continue; if (!diag) + { deps.push_back (dep {er->db, sp->name, move (er->available), move (er->repository_fragment), - er->system}); + er->system, + er->orphan.has_value ()}); + + if (er->orphan) + { + deorphaned_deps[package_key (er->db, sp->name)] = + move (*er->orphan); + } + } r = true; } @@ -4655,6 +5183,8 @@ namespace bpkg { assert (p.available != nullptr); // This is a package build. + bool deorphan (p.deorphan ()); + // Even if we already have this package selected, we have to // make sure it is configured and updated. // @@ -4690,8 +5220,8 @@ namespace bpkg ? "reconfigure" : (p.reconfigure () ? (o.configure_only () || p.configure_only () - ? "reconfigure" - : "reconfigure/update") + ? (deorphan ? "deorphan" : "reconfigure") + : (deorphan ? "deorphan/update" : "reconfigure/update")) : "update"); if (p.reconfigure ()) @@ -4705,9 +5235,9 @@ namespace bpkg { act += p.system ? "reconfigure" - : sp->version < p.available_version () - ? "upgrade" - : "downgrade"; + : (sp->version < p.available_version () + ? (deorphan ? "deorphan/upgrade" : "upgrade") + : (deorphan ? "deorphan/downgrade" : "downgrade")); // For a non-system package up/downgrade the skeleton must // already be initialized. @@ -5207,9 +5737,11 @@ namespace bpkg } // Fetch or checkout if this is a new package or if we are - // up/down-grading. + // up/down-grading or deorphaning. // - if (sp == nullptr || sp->version != p.available_version ()) + if (sp == nullptr || + sp->version != p.available_version () || + p.deorphan ()) { sp = nullptr; // For the directory case below. diff --git a/tests/common/satisfy/libfoo-1.1.0+1.tar.gz b/tests/common/satisfy/libfoo-1.1.0+1.tar.gz Binary files differindex 8cc49aa..3eb8670 100644 --- a/tests/common/satisfy/libfoo-1.1.0+1.tar.gz +++ b/tests/common/satisfy/libfoo-1.1.0+1.tar.gz diff --git a/tests/common/satisfy/libfoo-1.1.0+2.tar.gz b/tests/common/satisfy/libfoo-1.1.0+2.tar.gz Binary files differnew file mode 100644 index 0000000..1ffeaea --- /dev/null +++ b/tests/common/satisfy/libfoo-1.1.0+2.tar.gz diff --git a/tests/common/satisfy/libfoo-1.1.0+3.tar.gz b/tests/common/satisfy/libfoo-1.1.0+3.tar.gz Binary files differnew file mode 100644 index 0000000..8892b7b --- /dev/null +++ b/tests/common/satisfy/libfoo-1.1.0+3.tar.gz diff --git a/tests/common/satisfy/libfoo-1.1.1.tar.gz b/tests/common/satisfy/libfoo-1.1.1.tar.gz Binary files differnew file mode 100644 index 0000000..2e3a1f8 --- /dev/null +++ b/tests/common/satisfy/libfoo-1.1.1.tar.gz diff --git a/tests/common/satisfy/t14a/libfoo-1.0.0.tar.gz b/tests/common/satisfy/t14a/libfoo-1.0.0.tar.gz new file mode 120000 index 0000000..32e5a3c --- /dev/null +++ b/tests/common/satisfy/t14a/libfoo-1.0.0.tar.gz @@ -0,0 +1 @@ +../libfoo-1.0.0.tar.gz
\ No newline at end of file diff --git a/tests/common/satisfy/t14a/repositories.manifest b/tests/common/satisfy/t14a/repositories.manifest new file mode 120000 index 0000000..0d4767a --- /dev/null +++ b/tests/common/satisfy/t14a/repositories.manifest @@ -0,0 +1 @@ +../repositories.manifest
\ No newline at end of file diff --git a/tests/common/satisfy/t14b/libfoo-1.1.0.tar.gz b/tests/common/satisfy/t14b/libfoo-1.1.0.tar.gz new file mode 120000 index 0000000..c004b2a --- /dev/null +++ b/tests/common/satisfy/t14b/libfoo-1.1.0.tar.gz @@ -0,0 +1 @@ +../libfoo-1.1.0.tar.gz
\ No newline at end of file diff --git a/tests/common/satisfy/t14b/repositories.manifest b/tests/common/satisfy/t14b/repositories.manifest new file mode 120000 index 0000000..0d4767a --- /dev/null +++ b/tests/common/satisfy/t14b/repositories.manifest @@ -0,0 +1 @@ +../repositories.manifest
\ No newline at end of file diff --git a/tests/common/satisfy/t14c/libfoo-1.1.0+1.tar.gz b/tests/common/satisfy/t14c/libfoo-1.1.0+1.tar.gz new file mode 120000 index 0000000..ca9c01a --- /dev/null +++ b/tests/common/satisfy/t14c/libfoo-1.1.0+1.tar.gz @@ -0,0 +1 @@ +../libfoo-1.1.0+1.tar.gz
\ No newline at end of file diff --git a/tests/common/satisfy/t14c/repositories.manifest b/tests/common/satisfy/t14c/repositories.manifest new file mode 120000 index 0000000..0d4767a --- /dev/null +++ b/tests/common/satisfy/t14c/repositories.manifest @@ -0,0 +1 @@ +../repositories.manifest
\ No newline at end of file diff --git a/tests/common/satisfy/t14d/libfoo-1.1.0+2.tar.gz b/tests/common/satisfy/t14d/libfoo-1.1.0+2.tar.gz new file mode 120000 index 0000000..a89d2cc --- /dev/null +++ b/tests/common/satisfy/t14d/libfoo-1.1.0+2.tar.gz @@ -0,0 +1 @@ +../libfoo-1.1.0+2.tar.gz
\ No newline at end of file diff --git a/tests/common/satisfy/t14d/repositories.manifest b/tests/common/satisfy/t14d/repositories.manifest new file mode 120000 index 0000000..0d4767a --- /dev/null +++ b/tests/common/satisfy/t14d/repositories.manifest @@ -0,0 +1 @@ +../repositories.manifest
\ No newline at end of file diff --git a/tests/common/satisfy/t14e/libfoo-1.1.0+3.tar.gz b/tests/common/satisfy/t14e/libfoo-1.1.0+3.tar.gz new file mode 120000 index 0000000..616029d --- /dev/null +++ b/tests/common/satisfy/t14e/libfoo-1.1.0+3.tar.gz @@ -0,0 +1 @@ +../libfoo-1.1.0+3.tar.gz
\ No newline at end of file diff --git a/tests/common/satisfy/t14e/repositories.manifest b/tests/common/satisfy/t14e/repositories.manifest new file mode 120000 index 0000000..0d4767a --- /dev/null +++ b/tests/common/satisfy/t14e/repositories.manifest @@ -0,0 +1 @@ +../repositories.manifest
\ No newline at end of file diff --git a/tests/common/satisfy/t14f/libfoo-1.1.1.tar.gz b/tests/common/satisfy/t14f/libfoo-1.1.1.tar.gz new file mode 120000 index 0000000..b9ba788 --- /dev/null +++ b/tests/common/satisfy/t14f/libfoo-1.1.1.tar.gz @@ -0,0 +1 @@ +../libfoo-1.1.1.tar.gz
\ No newline at end of file diff --git a/tests/common/satisfy/t14f/repositories.manifest b/tests/common/satisfy/t14f/repositories.manifest new file mode 120000 index 0000000..0d4767a --- /dev/null +++ b/tests/common/satisfy/t14f/repositories.manifest @@ -0,0 +1 @@ +../repositories.manifest
\ No newline at end of file diff --git a/tests/common/satisfy/t14i/libfoo-1.2.0.tar.gz b/tests/common/satisfy/t14i/libfoo-1.2.0.tar.gz new file mode 120000 index 0000000..55398c5 --- /dev/null +++ b/tests/common/satisfy/t14i/libfoo-1.2.0.tar.gz @@ -0,0 +1 @@ +../libfoo-1.2.0.tar.gz
\ No newline at end of file diff --git a/tests/common/satisfy/t14i/repositories.manifest b/tests/common/satisfy/t14i/repositories.manifest new file mode 120000 index 0000000..0d4767a --- /dev/null +++ b/tests/common/satisfy/t14i/repositories.manifest @@ -0,0 +1 @@ +../repositories.manifest
\ No newline at end of file diff --git a/tests/pkg-build.testscript b/tests/pkg-build.testscript index d30186a..90bc490 100644 --- a/tests/pkg-build.testscript +++ b/tests/pkg-build.testscript @@ -384,6 +384,34 @@ # | |-- bix-1.0.0.tar.gz -> bar {prefer {...} accept (...)} # | `-- repositories.manifest # | +# |-- t14a +# | |-- libfoo-1.0.0.tar.gz +# | `-- repositories.manifest +# | +# |-- t14b +# | |-- libfoo-1.1.0.tar.gz +# | `-- repositories.manifest +# | +# |-- t14c +# | |-- libfoo-1.1.0+1.tar.gz +# | `-- repositories.manifest +# | +# |-- t14d +# | |-- libfoo-1.1.0+2.tar.gz +# | `-- repositories.manifest +# | +# |-- t14e +# | |-- libfoo-1.1.0+3.tar.gz +# | `-- repositories.manifest +# | +# |-- t14f +# | |-- libfoo-1.1.1.tar.gz +# | `-- repositories.manifest +# | +# |-- t14i +# | |-- libfoo-1.2.0.tar.gz +# | `-- repositories.manifest +# | # `-- git # |-- libbar.git -> style-basic.git (prerequisite repository) # |-- libbaz.git @@ -433,6 +461,13 @@ posix = ($cxx.target.class != 'windows') cp -r $src/t13m $out/t13m && $rep_create $out/t13m &$out/t13m/packages.manifest cp -r $src/t13n $out/t13n && $rep_create $out/t13n &$out/t13n/packages.manifest cp -r $src/t13o $out/t13o && $rep_create $out/t13o &$out/t13o/packages.manifest + cp -r $src/t14a $out/t14a && $rep_create $out/t14a &$out/t14a/packages.manifest + cp -r $src/t14b $out/t14b && $rep_create $out/t14b &$out/t14b/packages.manifest + cp -r $src/t14c $out/t14c && $rep_create $out/t14c &$out/t14c/packages.manifest + cp -r $src/t14d $out/t14d && $rep_create $out/t14d &$out/t14d/packages.manifest + cp -r $src/t14e $out/t14e && $rep_create $out/t14e &$out/t14e/packages.manifest + cp -r $src/t14f $out/t14f && $rep_create $out/t14f &$out/t14f/packages.manifest + cp -r $src/t14i $out/t14i && $rep_create $out/t14i &$out/t14i/packages.manifest # Create git repositories. # @@ -16285,11 +16320,42 @@ else $rep_add -d h2 $rep/t7a && $rep_fetch -d h2; - $* libbaz +{ --config-name h2 } 2>>EOE != 0 + $* libbaz +{ --config-name h2 } 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 "" 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 + !libbaz [h2/] configured 1.0.0 + libbuild2-bar [h1/.bpkg/build2/] configured 1.0.0 + EOO } : unhold-repointed @@ -17565,3 +17631,1158 @@ else } } } + +: deorphan +: +{ + test.arguments += --yes --plan "" + + : dependency + : + { + : unhold + : + { + : basics + : + { + $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 + + $rep_remove $~/libfoo/; + + # Deorphan libfoo/1.1.0 to ?libfoo/1.1.0. + # + $* --deorphan ?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 + EOO + + # Deorphan libfoo/1.1.0#1 to ?libfoo/1.1.0. + # + $rep_add --type dir libfoo/ && $rep_fetch; + $* libfoo 2>!; + + $pkg_status -r libbar >>EOO; + !libbar configured 1.1.0 + !libfoo configured 1.1.0#1 + EOO + + $rep_remove $~/libfoo/; + + $* --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 + EOO + + $pkg_drop libbar + } + + : drop + : + { + $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 + + $rep_remove $~/libfoo/; + + $* --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 + } + + : no-dependent + : + { + $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; + $rep_remove $~/libfoo/; + + $pkg_status libfoo >'!libfoo configured 1.1.0'; + + $* --deorphan ?libfoo 2>>EOE + drop libfoo/1.1.0 (unused) + disfigured libfoo/1.1.0 + purged libfoo/1.1.0 + EOE + } + + : preference + : + { + $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>!; + + tar (!$posix ? --force-local : ) -xf $src/t2/libbar-1.0.0.tar.gz &libbar-1.0.0/***; + mv libbar-1.0.0 libbar; + + cat <<"EOI" >=libbar/repositories.manifest; + : 1 + : + location: $rep/t14a + role: prerequisite + : + location: $rep/t14b + role: prerequisite + : + location: $rep/t14c + role: prerequisite + : + location: $rep/t14d + role: prerequisite + : + location: $rep/t14e + role: prerequisite + : + location: $rep/t14f + role: prerequisite + : + location: $rep/t14i + role: prerequisite + EOI + + $rep_add --type dir libbar/ && $rep_fetch; + $* libbar 2>!; + + $pkg_status -or libbar >>EOO; + !libbar configured 1.0.0 available (1.0.0) + !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/unhold libfoo/1.1.0+2 to the same version. + # + $rep_remove $~/libfoo/; + + $* --deorphan ?libfoo 2>>~%EOE%; + deorphan/update/unhold libfoo/1.1.0+2 + reconfigure libbar (dependent of libfoo) + disfigured libbar/1.0.0 + disfigured libfoo/1.1.0+2 + fetched libfoo/1.1.0+2 + unpacked libfoo/1.1.0+2 + configured libfoo/1.1.0+2 + 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.0.0) + 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 highest revision (up to 1.1.0+3). + # + cat <<"EOI" >=libbar/repositories.manifest; + : 1 + : + location: $rep/t14a + role: prerequisite + : + location: $rep/t14b + role: prerequisite + : + location: $rep/t14c + role: prerequisite + : + location: $rep/t14e + role: prerequisite + : + location: $rep/t14f + role: prerequisite + : + location: $rep/t14i + role: prerequisite + EOI + + $rep_fetch; + + $* --deorphan ?libfoo 2>>~%EOE%; + deorphan/upgrade libfoo/1.1.0+3 + reconfigure libbar (dependent of libfoo) + disfigured libbar/1.0.0 + disfigured libfoo/1.1.0+2 + fetched libfoo/1.1.0+3 + unpacked libfoo/1.1.0+3 + configured libfoo/1.1.0+3 + 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.0.0) + libfoo configured 1.1.0+3 available [1.2.0] [1.1.1] (1.1.0+3) [1.1.0+1] [1.1.0] [1.0.0] + EOO + + # Deorphan libfoo/1.1.0+3 to the highest revision (down to 1.1.0+1). + # + cat <<"EOI" >=libbar/repositories.manifest; + : 1 + : + location: $rep/t14a + role: prerequisite + : + location: $rep/t14b + role: prerequisite + : + location: $rep/t14c + role: prerequisite + : + location: $rep/t14f + role: prerequisite + : + location: $rep/t14i + role: prerequisite + EOI + + $rep_fetch; + + $* --deorphan ?libfoo 2>>~%EOE%; + deorphan/downgrade libfoo/1.1.0+1 + reconfigure libbar (dependent of libfoo) + disfigured libbar/1.0.0 + disfigured libfoo/1.1.0+3 + fetched libfoo/1.1.0+1 + unpacked libfoo/1.1.0+1 + configured libfoo/1.1.0+1 + 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.0.0) + libfoo configured 1.1.0+1 available [1.2.0] [1.1.1] (1.1.0+1) [1.1.0] [1.0.0] + EOO + + # Deorphan libfoo/1.1.0+1 to highest revision (down to 1.1.0). + # + cat <<"EOI" >=libbar/repositories.manifest; + : 1 + : + location: $rep/t14a + role: prerequisite + : + location: $rep/t14b + role: prerequisite + : + location: $rep/t14f + role: prerequisite + : + location: $rep/t14i + role: prerequisite + EOI + + $rep_fetch; + + $* --deorphan ?libfoo 2>>~%EOE%; + deorphan/downgrade libfoo/1.1.0 + reconfigure libbar (dependent of libfoo) + disfigured libbar/1.0.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.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.0.0) + libfoo configured 1.1.0 available [1.2.0] [1.1.1] (1.1.0) [1.0.0] + EOO + + # Deorphan libfoo/1.1.0 to patch (up to 1.1.1). + # + cat <<"EOI" >=libbar/repositories.manifest; + : 1 + : + location: $rep/t14a + role: prerequisite + : + location: $rep/t14f + role: prerequisite + : + location: $rep/t14i + role: prerequisite + EOI + + $rep_fetch; + + $* --deorphan ?libfoo 2>>~%EOE%; + deorphan/upgrade libfoo/1.1.1 + reconfigure libbar (dependent of libfoo) + disfigured libbar/1.0.0 + disfigured libfoo/1.1.0 + fetched libfoo/1.1.1 + unpacked libfoo/1.1.1 + configured libfoo/1.1.1 + 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.0.0) + libfoo configured 1.1.1 available [1.2.0] (1.1.1) [1.0.0] + EOO + + # Deorphan libfoo/1.1.1 to latest (up to 1.2.0). + # + cat <<"EOI" >=libbar/repositories.manifest; + : 1 + : + location: $rep/t14a + role: prerequisite + : + location: $rep/t14i + role: prerequisite + EOI + + $rep_fetch; + + $* --deorphan ?libfoo 2>>~%EOE%; + deorphan/upgrade libfoo/1.2.0 + reconfigure libbar (dependent of libfoo) + disfigured libbar/1.0.0 + disfigured libfoo/1.1.1 + fetched libfoo/1.2.0 + unpacked libfoo/1.2.0 + configured libfoo/1.2.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.0.0) + libfoo configured 1.2.0 available (1.2.0) [1.0.0] + EOO + + # Deorphan libfoo/1.2.0 to latest (down to 1.0.0). + # + cat <<"EOI" >=libbar/repositories.manifest; + : 1 + : + location: $rep/t14a + role: prerequisite + EOI + + $rep_fetch; + + $* --deorphan ?libfoo 2>>~%EOE%; + deorphan/downgrade libfoo/1.0.0 + reconfigure libbar (dependent of libfoo) + disfigured libbar/1.0.0 + disfigured libfoo/1.2.0 + fetched libfoo/1.0.0 + unpacked libfoo/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.0.0) + libfoo configured 1.0.0 available (1.0.0) + EOO + + # Deorphan fails (none available). + # + cat <<"EOI" >=libbar/repositories.manifest; + : 1 + EOI + + $rep_fetch; + + $* --deorphan ?libfoo 2>>/EOE != 0; + error: unknown package libfoo + EOE + + $pkg_status -or libbar >>EOO; + !libbar configured 1.0.0 available (1.0.0) + libfoo configured 1.0.0 + EOO + + $pkg_drop libbar + } + } + + : recursive + : + { + +tar (!$posix ? --force-local : ) -xf $src/t2/libbar-1.0.0.tar.gz &libbar-1.0.0/*** + +mv libbar-1.0.0 libbar + + +cat <<"EOI" >=libbar/repositories.manifest + : 1 + : + location: $rep/t14b + role: prerequisite + : + location: $rep/t14f + role: prerequisite + : + location: $rep/t14i + role: prerequisite + EOI + + : immediate + : + { + $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_remove $~/libbar/; + $rep_add $rep/t2 $rep/t4b $rep/t14c && $rep_fetch; + + $* --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.1.0+1 [1.1.0] (1.0.0) + EOO + + $pkg_drop libbar + } + + : recursive + : + { + $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_remove $~/libbar/; + $rep_add $rep/t2 $rep/t4b $rep/t14c && $rep_fetch; + + $* --deorphan --recursive 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.1.0+1 [1.1.0] (1.0.0) + EOO + + $pkg_drop libbar + } + + : deorphan-immediate + : + { + $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 + + cat <<"EOI" >=libbar/repositories.manifest; + : 1 + : + location: $rep/t14f + role: prerequisite + : + location: $rep/t14i + role: prerequisite + EOI + + $rep_fetch; + + $* --deorphan-immediate libbar 2>>~%EOE%; + deorphan/upgrade libfoo/1.1.1 + reconfigure/update libbar/1.0.0 + disfigured libbar/1.0.0 + disfigured libfoo/1.1.0 + fetched libfoo/1.1.1 + unpacked libfoo/1.1.1 + configured libfoo/1.1.1 + 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.0.0) + libfoo configured !1.1.1 available [1.2.0] (1.1.1) + EOO + + $pkg_drop libbar + } + + : deorphan-recursive + : + { + $clone_root_cfg; + cp -rp ../libbar ./; + + $rep_add --type dir libbar/ && $rep_fetch; + $* libbar ?libfoo/1.1.0 2>!; + + $rep_add $rep/t3 && $rep_fetch; + $* libbaz 2>!; + + $pkg_status -or libbaz >>EOO; + !libbaz configured 1.0.0 available (1.0.0) + !libbar configured 1.0.0 available (1.0.0) + libfoo configured !1.1.0 available [1.2.0] [1.1.1] (1.1.0) [1.0.0] + EOO + + cat <<"EOI" >=libbar/repositories.manifest; + : 1 + : + location: $rep/t14f + role: prerequisite + : + location: $rep/t14i + role: prerequisite + EOI + + $rep_fetch; + + $* --deorphan-recursive libbaz 2>>~%EOE%; + deorphan/upgrade libfoo/1.1.1 + reconfigure libbar (dependent of libfoo) + reconfigure/update libbaz/1.0.0 + disfigured libbaz/1.0.0 + disfigured libbar/1.0.0 + disfigured libfoo/1.1.0 + fetched libfoo/1.1.1 + unpacked libfoo/1.1.1 + configured libfoo/1.1.1 + configured libbar/1.0.0 + configured libbaz/1.0.0 + %info: .+libbaz.+ is up to date% + %info: .+libbar.+ is up to date% + updated libbaz/1.0.0 + updated libbar/1.0.0 + EOE + + $pkg_status -or libbaz >>EOO; + !libbaz configured 1.0.0 available (1.0.0) + !libbar configured 1.0.0 available (1.0.0) + libfoo configured !1.1.1 available [1.2.0] (1.1.1) [1.0.0] + EOO + + $pkg_drop libbaz libbar + } + } + + : recursive-all-held + : + : As above but uses 'deorphan all held packages form'. + : + { + +tar (!$posix ? --force-local : ) -xf $src/t2/libbar-1.0.0.tar.gz &libbar-1.0.0/*** + +mv libbar-1.0.0 libbar + + +cat <<"EOI" >=libbar/repositories.manifest + : 1 + : + location: $rep/t14b + role: prerequisite + : + location: $rep/t14f + role: prerequisite + : + location: $rep/t14i + role: prerequisite + EOI + + : immediate + : + { + $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_remove $~/libbar/; + $rep_add $rep/t2 $rep/t4b $rep/t14c && $rep_fetch; + + $* --deorphan --immediate 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.1.0+1 [1.1.0] (1.0.0) + EOO + + $pkg_drop libbar + } + + : recursive + : + { + $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_remove $~/libbar/; + $rep_add $rep/t2 $rep/t4b $rep/t14c && $rep_fetch; + + $* --deorphan --recursive 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.1.0+1 [1.1.0] (1.0.0) + EOO + + $pkg_drop libbar + } + } + } + + : held + : + { + : basics + : + { + $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. + # + $rep_remove $~/libfoo/; + + $* --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'; + + # Deorphan libfoo/1.0.0 to libfoo/1.1.0. + # + $rep_remove $rep/t4c; + + # While at it, use the 'deorphan all held packages' form. + # + $* --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'; + + $pkg_drop libfoo + } + + : preference + : + { + $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 same version. + # + $rep_remove $~/libfoo/; + + $* --deorphan libfoo 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 + + # Deorphan libfoo/1.1.0+2 to the highest revision (up to 1.1.0+3). + # + $rep_remove $rep/t14d; + + $* --deorphan libfoo 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+1 1.1.0 1.0.0 + EOO + + # Deorphan libfoo/1.1.0+3 to the highest revision (down to 1.1.0+1). + # + $rep_remove $rep/t14e; + + $* --deorphan libfoo 2>>~%EOE%; + deorphan/downgrade libfoo/1.1.0+1 + disfigured libfoo/1.1.0+3 + 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+1) 1.1.0 1.0.0 + EOO + + # Deorphan libfoo/1.1.0+1 to highest revision (down to 1.1.0). + # + $rep_remove $rep/t14c; + + $* --deorphan libfoo 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) 1.0.0 + EOO + + # Deorphan libfoo/1.1.0 to patch (up to 1.1.1). + # + $rep_remove $rep/t14b; + + $* --deorphan libfoo 2>>~%EOE%; + deorphan/upgrade libfoo/1.1.1 + disfigured libfoo/1.1.0 + 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.0.0 + EOO + + # Deorphan libfoo/1.1.1 to latest (up to 1.2.0). + # + $rep_remove $rep/t14f; + + $* --deorphan libfoo 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.0.0 + EOO + + # Deorphan libfoo/1.2.0 to latest (down to 1.0.0). + # + $rep_remove $rep/t14i; + + $* --deorphan libfoo 2>>~%EOE%; + deorphan/downgrade libfoo/1.0.0 + disfigured libfoo/1.2.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.0.0) + EOO + + # Deorphan fails (none available). + # + $rep_remove $rep/t14a; + + $* --deorphan libfoo 2>>/EOE != 0; + error: unknown package libfoo + info: configuration cfg/ has no repositories + info: use 'bpkg rep-add' to add a repository + EOE + + $pkg_status -o libfoo >>EOO; + !libfoo configured 1.0.0 + EOO + + $pkg_drop libfoo + } + + : preference-all-held + : + : As above but uses 'deorphan all held packages form'. + : + { + $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 same version. + # + $rep_remove $~/libfoo/; + + $* --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 + + # Deorphan libfoo/1.1.0+2 to the highest revision (up to 1.1.0+3). + # + $rep_remove $rep/t14d; + + $* --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+1 1.1.0 1.0.0 + EOO + + # Deorphan libfoo/1.1.0+3 to the highest revision (down to 1.1.0+1). + # + $rep_remove $rep/t14e; + + $* --deorphan 2>>~%EOE%; + deorphan/downgrade libfoo/1.1.0+1 + disfigured libfoo/1.1.0+3 + 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+1) 1.1.0 1.0.0 + EOO + + # Deorphan libfoo/1.1.0+1 to highest revision (down to 1.1.0). + # + $rep_remove $rep/t14c; + + $* --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) 1.0.0 + EOO + + # Deorphan libfoo/1.1.0 to patch (up to 1.1.1). + # + $rep_remove $rep/t14b; + + $* --deorphan 2>>~%EOE%; + deorphan/upgrade libfoo/1.1.1 + disfigured libfoo/1.1.0 + 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.0.0 + EOO + + # Deorphan libfoo/1.1.1 to latest (up to 1.2.0). + # + $rep_remove $rep/t14f; + + $* --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.0.0 + EOO + + # Deorphan libfoo/1.2.0 to latest (down to 1.0.0). + # + $rep_remove $rep/t14i; + + $* --deorphan 2>>~%EOE%; + deorphan/downgrade libfoo/1.0.0 + disfigured libfoo/1.2.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.0.0) + EOO + + # Deorphan fails (none available). + # + $rep_remove $rep/t14a; + + $* --deorphan 2>>/EOE != 0; + error: libfoo is not available + info: configuration cfg/ has no repositories + info: use 'bpkg rep-add' to add a repository + EOE + + $pkg_status -o libfoo >>EOO; + !libfoo configured 1.0.0 + EOO + + $pkg_drop libfoo + } + } +} diff --git a/tests/pkg-build/t14a b/tests/pkg-build/t14a new file mode 120000 index 0000000..34b7111 --- /dev/null +++ b/tests/pkg-build/t14a @@ -0,0 +1 @@ +../common/satisfy/t14a
\ No newline at end of file diff --git a/tests/pkg-build/t14b b/tests/pkg-build/t14b new file mode 120000 index 0000000..eeff0af --- /dev/null +++ b/tests/pkg-build/t14b @@ -0,0 +1 @@ +../common/satisfy/t14b
\ No newline at end of file diff --git a/tests/pkg-build/t14c b/tests/pkg-build/t14c new file mode 120000 index 0000000..01ab194 --- /dev/null +++ b/tests/pkg-build/t14c @@ -0,0 +1 @@ +../common/satisfy/t14c
\ No newline at end of file diff --git a/tests/pkg-build/t14d b/tests/pkg-build/t14d new file mode 120000 index 0000000..463084d --- /dev/null +++ b/tests/pkg-build/t14d @@ -0,0 +1 @@ +../common/satisfy/t14d
\ No newline at end of file diff --git a/tests/pkg-build/t14e b/tests/pkg-build/t14e new file mode 120000 index 0000000..a9f72b7 --- /dev/null +++ b/tests/pkg-build/t14e @@ -0,0 +1 @@ +../common/satisfy/t14e
\ No newline at end of file diff --git a/tests/pkg-build/t14f b/tests/pkg-build/t14f new file mode 120000 index 0000000..94c4598 --- /dev/null +++ b/tests/pkg-build/t14f @@ -0,0 +1 @@ +../common/satisfy/t14f
\ No newline at end of file diff --git a/tests/pkg-build/t14i b/tests/pkg-build/t14i new file mode 120000 index 0000000..bcc36b2 --- /dev/null +++ b/tests/pkg-build/t14i @@ -0,0 +1 @@ +../common/satisfy/t14i
\ No newline at end of file |