aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2023-06-01 21:26:04 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2023-06-15 00:15:24 +0300
commitc98f1215387f993329a96785a2a9102d93cb0328 (patch)
tree2dbf5eae475650c53f6ded1841405032e90b5c65
parent5f2a12b015f957c33a7d8edbd06d2fe3594f8b3b (diff)
Add --deorphan pkg-build option
-rw-r--r--bpkg/pkg-build-collect.cxx1
-rw-r--r--bpkg/pkg-build-collect.hxx13
-rw-r--r--bpkg/pkg-build.cli71
-rw-r--r--bpkg/pkg-build.cxx860
-rw-r--r--tests/common/satisfy/libfoo-1.1.0+1.tar.gzbin353 -> 349 bytes
-rw-r--r--tests/common/satisfy/libfoo-1.1.0+2.tar.gzbin0 -> 348 bytes
-rw-r--r--tests/common/satisfy/libfoo-1.1.0+3.tar.gzbin0 -> 347 bytes
-rw-r--r--tests/common/satisfy/libfoo-1.1.1.tar.gzbin0 -> 404 bytes
l---------tests/common/satisfy/t14a/libfoo-1.0.0.tar.gz1
l---------tests/common/satisfy/t14a/repositories.manifest1
l---------tests/common/satisfy/t14b/libfoo-1.1.0.tar.gz1
l---------tests/common/satisfy/t14b/repositories.manifest1
l---------tests/common/satisfy/t14c/libfoo-1.1.0+1.tar.gz1
l---------tests/common/satisfy/t14c/repositories.manifest1
l---------tests/common/satisfy/t14d/libfoo-1.1.0+2.tar.gz1
l---------tests/common/satisfy/t14d/repositories.manifest1
l---------tests/common/satisfy/t14e/libfoo-1.1.0+3.tar.gz1
l---------tests/common/satisfy/t14e/repositories.manifest1
l---------tests/common/satisfy/t14f/libfoo-1.1.1.tar.gz1
l---------tests/common/satisfy/t14f/repositories.manifest1
l---------tests/common/satisfy/t14i/libfoo-1.2.0.tar.gz1
l---------tests/common/satisfy/t14i/repositories.manifest1
-rw-r--r--tests/pkg-build.testscript1223
l---------tests/pkg-build/t14a1
l---------tests/pkg-build/t14b1
l---------tests/pkg-build/t14c1
l---------tests/pkg-build/t14d1
l---------tests/pkg-build/t14e1
l---------tests/pkg-build/t14f1
l---------tests/pkg-build/t14i1
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
index 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
Binary files differ
diff --git a/tests/common/satisfy/libfoo-1.1.0+2.tar.gz b/tests/common/satisfy/libfoo-1.1.0+2.tar.gz
new file mode 100644
index 0000000..1ffeaea
--- /dev/null
+++ b/tests/common/satisfy/libfoo-1.1.0+2.tar.gz
Binary files differ
diff --git a/tests/common/satisfy/libfoo-1.1.0+3.tar.gz b/tests/common/satisfy/libfoo-1.1.0+3.tar.gz
new file mode 100644
index 0000000..8892b7b
--- /dev/null
+++ b/tests/common/satisfy/libfoo-1.1.0+3.tar.gz
Binary files differ
diff --git a/tests/common/satisfy/libfoo-1.1.1.tar.gz b/tests/common/satisfy/libfoo-1.1.1.tar.gz
new file mode 100644
index 0000000..2e3a1f8
--- /dev/null
+++ b/tests/common/satisfy/libfoo-1.1.1.tar.gz
Binary files differ
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