aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2021-12-23 21:10:53 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2022-01-17 18:45:14 +0300
commit56e0a851185136dbdd6f1eaa75f44da774a61e51 (patch)
treeb2d195419cf20e90bd8663d43c3a8071ed2ebef5
parentf806768c53361f5404148cd4d216b10421a1288f (diff)
Add support for multiple dependencies in alternative
-rw-r--r--bpkg/pkg-build.cxx1085
-rw-r--r--bpkg/pkg-configure.cxx160
-rw-r--r--bpkg/pkg-verify.cxx81
-rw-r--r--tests/common/dependency-alternatives/t8a/foo-1.0.0.tar.gzbin0 -> 373 bytes
-rw-r--r--tests/common/dependency-alternatives/t8a/libbar-1.0.0.tar.gzbin0 -> 350 bytes
-rw-r--r--tests/common/dependency-alternatives/t8a/libbaz-1.0.0.tar.gzbin0 -> 360 bytes
-rw-r--r--tests/common/dependency-alternatives/t8a/repositories.manifest1
-rw-r--r--tests/pkg-build.testscript35
l---------tests/pkg-build/t8a1
-rw-r--r--tests/pkg-configure.testscript49
l---------tests/pkg-configure/t8a1
11 files changed, 769 insertions, 644 deletions
diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx
index da29cc9..df5350a 100644
--- a/bpkg/pkg-build.cxx
+++ b/bpkg/pkg-build.cxx
@@ -1139,616 +1139,621 @@ namespace bpkg
for (const dependency_alternatives_ex& das: ap->dependencies)
{
- if (das.conditional ()) // @@ TODO
+ if (das.conditional ()) // @@ DEP
fail << "conditional dependencies are not yet supported";
- if (das.size () != 1) // @@ TODO
+ if (das.size () != 1) // @@ DEP
fail << "multiple dependency alternatives not yet supported";
- const dependency_alternative& da (das.front ());
-
- assert (da.size () == 1); // @@ DEP
-
- const dependency& dp (da[0]);
- const package_name& dn (dp.name);
-
- if (das.buildtime)
+ bool postpone (false);
+ for (const dependency& dp: das.front ())
{
- // Handle special names.
- //
- if (dn == "build2")
- {
- if (dp.constraint && !satisfy_build2 (options, dp))
- fail << "unable to satisfy constraint (" << dp
- << ") for package " << name <<
- info << "available build2 version is " << build2_version;
-
- continue;
- }
- else if (dn == "bpkg")
- {
- if (dp.constraint && !satisfy_bpkg (options, dp))
- fail << "unable to satisfy constraint (" << dp
- << ") for package " << name <<
- info << "available bpkg version is " << bpkg_version;
+ const package_name& dn (dp.name);
- continue;
- }
- else if (pdb.type == build2_config_type)
+ if (das.buildtime)
{
- // Note that the dependent is not necessarily a build system
- // module.
+ // Handle special names.
//
- fail << "build-time dependency " << dn << " in build system "
- << "module configuration" <<
- info << "build system modules cannot have build-time "
- << "dependencies";
- }
- }
+ if (dn == "build2")
+ {
+ if (dp.constraint && !satisfy_build2 (options, dp))
+ fail << "unable to satisfy constraint (" << dp
+ << ") for package " << name <<
+ info << "available build2 version is " << build2_version;
- bool system (false);
- bool dep_optional (false);
+ continue;
+ }
+ else if (dn == "bpkg")
+ {
+ if (dp.constraint && !satisfy_bpkg (options, dp))
+ fail << "unable to satisfy constraint (" << dp
+ << ") for package " << name <<
+ info << "available bpkg version is " << bpkg_version;
- // If the user specified the desired dependency version constraint,
- // then we will use it to overwrite the constraint imposed by the
- // dependent package, checking that it is still satisfied.
- //
- // Note that we can't just rely on the execution plan refinement that
- // will pick up the proper dependency version at the end of the day.
- // We may just not get to the plan execution simulation, failing due
- // to inability for dependency versions collected by two dependents to
- // satisfy each other constraints (for an example see the
- // pkg-build/dependency/apply-constraints/resolve-conflict{1,2}
- // tests).
-
- // Points to the desired dependency version constraint, if specified,
- // and is NULL otherwise. Can be used as boolean flag.
- //
- const version_constraint* dep_constr (nullptr);
+ continue;
+ }
+ else if (pdb.type == build2_config_type)
+ {
+ // Note that the dependent is not necessarily a build system
+ // module.
+ //
+ fail << "build-time dependency " << dn << " in build system "
+ << "module configuration" <<
+ info << "build system modules cannot have build-time "
+ << "dependencies";
+ }
+ }
- database* ddb (fdb (pdb, dn, das.buildtime));
+ bool system (false);
+ bool dep_optional (false);
- auto i (ddb != nullptr
- ? map_.find (*ddb, dn)
- : map_.find_dependency (pdb, dn, das.buildtime));
+ // If the user specified the desired dependency version constraint,
+ // then we will use it to overwrite the constraint imposed by the
+ // dependent package, checking that it is still satisfied.
+ //
+ // Note that we can't just rely on the execution plan refinement
+ // that will pick up the proper dependency version at the end of the
+ // day. We may just not get to the plan execution simulation,
+ // failing due to inability for dependency versions collected by two
+ // dependents to satisfy each other constraints (for an example see
+ // the pkg-build/dependency/apply-constraints/resolve-conflict{1,2}
+ // tests).
+
+ // Points to the desired dependency version constraint, if
+ // specified, and is NULL otherwise. Can be used as boolean flag.
+ //
+ const version_constraint* dep_constr (nullptr);
- if (i != map_.end ())
- {
- const build_package& bp (i->second.package);
+ database* ddb (fdb (pdb, dn, das.buildtime));
- dep_optional = !bp.action; // Is pre-entered.
+ auto i (ddb != nullptr
+ ? map_.find (*ddb, dn)
+ : map_.find_dependency (pdb, dn, das.buildtime));
- if (dep_optional &&
- //
- // The version constraint is specified,
- //
- bp.hold_version && *bp.hold_version)
+ if (i != map_.end ())
{
- assert (bp.constraints.size () == 1);
+ const build_package& bp (i->second.package);
+
+ dep_optional = !bp.action; // Is pre-entered.
+
+ if (dep_optional &&
+ //
+ // The version constraint is specified,
+ //
+ bp.hold_version && *bp.hold_version)
+ {
+ assert (bp.constraints.size () == 1);
- const build_package::constraint_type& c (bp.constraints[0]);
+ const build_package::constraint_type& c (bp.constraints[0]);
- dep_constr = &c.value;
- system = bp.system;
+ dep_constr = &c.value;
+ system = bp.system;
- // If the user-specified dependency constraint is the wildcard
- // version, then it satisfies any dependency constraint.
- //
- if (!wildcard (*dep_constr) &&
- !satisfies (*dep_constr, dp.constraint))
- fail << "unable to satisfy constraints on package " << dn <<
- info << name << pdb << " depends on (" << dn << " "
- << *dp.constraint << ")" <<
- info << c.dependent << c.db << " depends on (" << dn << " "
+ // If the user-specified dependency constraint is the wildcard
+ // version, then it satisfies any dependency constraint.
+ //
+ if (!wildcard (*dep_constr) &&
+ !satisfies (*dep_constr, dp.constraint))
+ fail << "unable to satisfy constraints on package " << dn <<
+ info << name << pdb << " depends on (" << dn << " "
+ << *dp.constraint << ")" <<
+ info << c.dependent << c.db << " depends on (" << dn << " "
<< c.value << ")" <<
- info << "specify " << dn << " version to satisfy " << name
+ info << "specify " << dn << " version to satisfy " << name
<< " constraint";
+ }
}
- }
- const dependency& d (!dep_constr
- ? dp
- : dependency {dn, *dep_constr});
+ const dependency& d (!dep_constr
+ ? dp
+ : dependency {dn, *dep_constr});
- // First see if this package is already selected. If we already have
- // it in the configuration and it satisfies our dependency version
- // constraint, then we don't want to be forcing its upgrade (or,
- // worse, downgrade).
- //
- // If the prerequisite configuration is explicitly specified by the
- // user, then search for the prerequisite in this specific
- // configuration. Otherwise, search recursively in the explicitly
- // linked configurations of the dependent configuration.
- //
- // Note that for the repointed dependent we will always find the
- // prerequisite replacement rather than the prerequisite being
- // replaced.
- //
- pair<shared_ptr<selected_package>, database*> spd (
- ddb != nullptr
- ? make_pair (ddb->find<selected_package> (dn), ddb)
- : find_dependency (pdb, dn, das.buildtime));
-
- if (ddb == nullptr)
- ddb = &pdb;
+ // First see if this package is already selected. If we already have
+ // it in the configuration and it satisfies our dependency version
+ // constraint, then we don't want to be forcing its upgrade (or,
+ // worse, downgrade).
+ //
+ // If the prerequisite configuration is explicitly specified by the
+ // user, then search for the prerequisite in this specific
+ // configuration. Otherwise, search recursively in the explicitly
+ // linked configurations of the dependent configuration.
+ //
+ // Note that for the repointed dependent we will always find the
+ // prerequisite replacement rather than the prerequisite being
+ // replaced.
+ //
+ pair<shared_ptr<selected_package>, database*> spd (
+ ddb != nullptr
+ ? make_pair (ddb->find<selected_package> (dn), ddb)
+ : find_dependency (pdb, dn, das.buildtime));
- shared_ptr<selected_package>& dsp (spd.first);
+ if (ddb == nullptr)
+ ddb = &pdb;
- pair<shared_ptr<available_package>,
- lazy_shared_ptr<repository_fragment>> rp;
+ shared_ptr<selected_package>& dsp (spd.first);
- shared_ptr<available_package>& dap (rp.first);
+ pair<shared_ptr<available_package>,
+ lazy_shared_ptr<repository_fragment>> rp;
- bool force (false);
+ shared_ptr<available_package>& dap (rp.first);
- if (dsp != nullptr)
- {
- // Switch to the selected package configuration.
- //
- ddb = spd.second;
+ bool force (false);
- // If we are collecting prerequisites of the repointed dependent,
- // then only proceed further if this is either a replacement or
- // unamended prerequisite and we are up/down-grading (only for the
- // latter).
- //
- if (rpt_prereq_flags != nullptr)
+ if (dsp != nullptr)
{
- auto i (rpt_prereq_flags->find (config_package {*ddb, dn}));
-
- bool unamended (i == rpt_prereq_flags->end ());
- bool replacement (!unamended && i->second);
+ // Switch to the selected package configuration.
+ //
+ ddb = spd.second;
- // We can never end up with the prerequisite being replaced, since
- // the fdb() function should always return the replacement instead
- // (see above).
+ // If we are collecting prerequisites of the repointed dependent,
+ // then only proceed further if this is either a replacement or
+ // unamended prerequisite and we are up/down-grading (only for the
+ // latter).
//
- assert (unamended || replacement);
+ if (rpt_prereq_flags != nullptr)
+ {
+ auto i (rpt_prereq_flags->find (config_package {*ddb, dn}));
- if (!(replacement || (unamended && ud)))
- continue;
- }
+ bool unamended (i == rpt_prereq_flags->end ());
+ bool replacement (!unamended && i->second);
- if (dsp->state == package_state::broken)
- fail << "unable to build broken package " << dn << *ddb <<
- info << "use 'pkg-purge --force' to remove";
+ // We can never end up with the prerequisite being replaced,
+ // since the fdb() function should always return the replacement
+ // instead (see above).
+ //
+ assert (unamended || replacement);
- // If the constraint is imposed by the user we also need to make sure
- // that the system flags are the same.
- //
- if (satisfies (dsp->version, d.constraint) &&
- (!dep_constr || dsp->system () == system))
- {
- system = dsp->system ();
+ if (!(replacement || (unamended && ud)))
+ continue;
+ }
- // First try to find an available package for this exact version.
- // In particular, this handles the case where a package moves from
- // one repository to another (e.g., from testing to stable). For a
- // system package we pick the latest one (its exact version
- // doesn't really matter).
- //
- rp = find_available_one (dependent_repo_configs (*ddb),
- dn,
- (!system
- ? version_constraint (dsp->version)
- : optional<version_constraint> ()));
-
- // A stub satisfies any version constraint so we weed them out
- // (returning stub as an available package feels wrong).
- //
- if (dap == nullptr || dap->stub ())
- rp = make_available (options, *ddb, dsp);
- }
- else
- // Remember that we may be forcing up/downgrade; we will deal with
- // it below.
- //
- force = true;
- }
+ if (dsp->state == package_state::broken)
+ fail << "unable to build broken package " << dn << *ddb <<
+ info << "use 'pkg-purge --force' to remove";
- // If this is a build-time dependency and we build it for the first
- // time, then we need to find a suitable configuration (of the host or
- // build2 type) to build it in.
- //
- // If the current configuration (ddb) is of the suitable type, then we
- // use that. Otherwise, we go through its immediate explicit links. If
- // only one of them has the suitable type, then we use that. If there
- // are multiple of them, then we fail advising the user to pick one
- // explicitly. If there are none, then we create the private
- // configuration and use that. If the current configuration is
- // private, then search/create in the parent configuration instead.
- //
- // Note that if the user has explicitly specified the configuration
- // for this dependency on the command line (using --config-*), then
- // this configuration is used as the starting point for this search.
- //
- if (das.buildtime &&
- dsp == nullptr &&
- ddb->type != buildtime_dependency_type (dn))
- {
- database* db (nullptr);
- database& sdb (ddb->private_ () ? ddb->parent_config () : *ddb);
+ // If the constraint is imposed by the user we also need to make
+ // sure that the system flags are the same.
+ //
+ if (satisfies (dsp->version, d.constraint) &&
+ (!dep_constr || dsp->system () == system))
+ {
+ system = dsp->system ();
- const string& type (buildtime_dependency_type (dn));
+ // First try to find an available package for this exact
+ // version. In particular, this handles the case where a package
+ // moves from one repository to another (e.g., from testing to
+ // stable). For a system package we pick the latest one (its
+ // exact version doesn't really matter).
+ //
+ rp = find_available_one (dependent_repo_configs (*ddb),
+ dn,
+ (!system
+ ? version_constraint (dsp->version)
+ : optional<version_constraint> ()));
+
+ // A stub satisfies any version constraint so we weed them out
+ // (returning stub as an available package feels wrong).
+ //
+ if (dap == nullptr || dap->stub ())
+ rp = make_available (options, *ddb, dsp);
+ }
+ else
+ // Remember that we may be forcing up/downgrade; we will deal
+ // with it below.
+ //
+ force = true;
+ }
- // Skip the self-link.
+ // If this is a build-time dependency and we build it for the first
+ // time, then we need to find a suitable configuration (of the host
+ // or build2 type) to build it in.
+ //
+ // If the current configuration (ddb) is of the suitable type, then
+ // we use that. Otherwise, we go through its immediate explicit
+ // links. If only one of them has the suitable type, then we use
+ // that. If there are multiple of them, then we fail advising the
+ // user to pick one explicitly. If there are none, then we create
+ // the private configuration and use that. If the current
+ // configuration is private, then search/create in the parent
+ // configuration instead.
//
- const linked_configs& lcs (sdb.explicit_links ());
- for (auto i (lcs.begin_linked ()); i != lcs.end (); ++i)
+ // Note that if the user has explicitly specified the configuration
+ // for this dependency on the command line (using --config-*), then
+ // this configuration is used as the starting point for this search.
+ //
+ if (das.buildtime &&
+ dsp == nullptr &&
+ ddb->type != buildtime_dependency_type (dn))
{
- database& ldb (i->db);
+ database* db (nullptr);
+ database& sdb (ddb->private_ () ? ddb->parent_config () : *ddb);
+
+ const string& type (buildtime_dependency_type (dn));
- if (ldb.type == type)
+ // Skip the self-link.
+ //
+ const linked_configs& lcs (sdb.explicit_links ());
+ for (auto i (lcs.begin_linked ()); i != lcs.end (); ++i)
{
- if (db == nullptr)
- db = &ldb;
- else
- fail << "multiple possible " << type << " configurations for "
- << "build-time dependency (" << dp << ")" <<
- info << db->config_orig <<
- info << ldb.config_orig <<
- info << "use --config-* to select the configuration";
+ database& ldb (i->db);
+
+ if (ldb.type == type)
+ {
+ if (db == nullptr)
+ db = &ldb;
+ else
+ fail << "multiple possible " << type << " configurations for "
+ << "build-time dependency (" << dp << ")" <<
+ info << db->config_orig <<
+ info << ldb.config_orig <<
+ info << "use --config-* to select the configuration";
+ }
}
- }
- // If no suitable configuration is found, then create and link it,
- // unless the --no-private-config options is specified. In the
- // latter case, print the dependency chain to stdout and exit with
- // the specified code.
- //
- if (db == nullptr)
- {
- if (options.no_private_config_specified ())
- try
+ // If no suitable configuration is found, then create and link it,
+ // unless the --no-private-config options is specified. In the
+ // latter case, print the dependency chain to stdout and exit with
+ // the specified code.
+ //
+ if (db == nullptr)
{
- // Note that we don't have the dependency package version yet.
- // We could probably rearrange the code and obtain the available
- // dependency package by now, given that it comes from the main
- // database and may not be specified as system (we would have
- // the configuration otherwise). However, let's not complicate
- // the code further and instead print the package name and the
- // constraint, if present.
- //
- // Also, in the future, we may still need the configuration to
- // obtain the available dependency package for some reason (may
- // want to fetch repositories locally, etc).
- //
- cout << d << '\n';
-
- // Note that we also need to clean the dependency chain, to
- // prevent the exception guard from printing it to stderr.
- //
- for (build_package_refs dc (move (dep_chain)); !dc.empty (); )
+ if (options.no_private_config_specified ())
+ try
{
- const build_package& p (dc.back ());
+ // Note that we don't have the dependency package version yet.
+ // We could probably rearrange the code and obtain the
+ // available dependency package by now, given that it comes
+ // from the main database and may not be specified as system
+ // (we would have the configuration otherwise). However, let's
+ // not complicate the code further and instead print the
+ // package name and the constraint, if present.
+ //
+ // Also, in the future, we may still need the configuration to
+ // obtain the available dependency package for some reason
+ // (may want to fetch repositories locally, etc).
+ //
+ cout << d << '\n';
+
+ // Note that we also need to clean the dependency chain, to
+ // prevent the exception guard from printing it to stderr.
+ //
+ for (build_package_refs dc (move (dep_chain)); !dc.empty (); )
+ {
+ const build_package& p (dc.back ());
+
+ cout << p.available_name_version () << ' '
+ << p.db.get ().config << '\n';
- cout << p.available_name_version () << ' '
- << p.db.get ().config << '\n';
+ dc.pop_back ();
+ }
- dc.pop_back ();
+ throw failed (options.no_private_config ());
+ }
+ catch (const io_error&)
+ {
+ fail << "unable to write to stdout";
}
- throw failed (options.no_private_config ());
- }
- catch (const io_error&)
- {
- fail << "unable to write to stdout";
- }
+ const strings mods {"cc"};
- const strings mods {"cc"};
+ const strings vars {
+ "config.config.load=~" + type,
+ "config.config.persist+='config.*'@unused=drop"};
- const strings vars {
- "config.config.load=~" + type,
- "config.config.persist+='config.*'@unused=drop"};
+ dir_path cd (bpkg_dir / dir_path (type));
- dir_path cd (bpkg_dir / dir_path (type));
+ // Wipe a potentially existing un-linked private configuration
+ // left from a previous faulty run. Note that trying to reuse it
+ // would be a bad idea since it can be half-prepared, with an
+ // outdated database schema version, etc.
+ //
+ cfg_create (options,
+ sdb.config_orig / cd,
+ optional<string> (type) /* name */,
+ type /* type */,
+ mods,
+ vars,
+ false /* existing */,
+ true /* wipe */);
+
+ // Note that we will copy the name from the configuration unless
+ // it clashes with one of the existing links.
+ //
+ shared_ptr<configuration> lc (cfg_link (sdb,
+ sdb.config / cd,
+ true /* relative */,
+ nullopt /* name */,
+ true /* sys_rep */));
+
+ // Save the newly-created private configuration, together with
+ // the containing configuration database, for their subsequent
+ // re- link.
+ //
+ apc (sdb, move (cd));
- // Wipe a potentially existing un-linked private configuration
- // left from a previous faulty run. Note that trying to reuse it
- // would be a bad idea since it can be half-prepared, with an
- // outdated database schema version, etc.
- //
- cfg_create (options,
- sdb.config_orig / cd,
- optional<string> (type) /* name */,
- type /* type */,
- mods,
- vars,
- false /* existing */,
- true /* wipe */);
-
- // Note that we will copy the name from the configuration unless
- // it clashes with one of the existing links.
- //
- shared_ptr<configuration> lc (cfg_link (sdb,
- sdb.config / cd,
- true /* relative */,
- nullopt /* name */,
- true /* sys_rep */));
-
- // Save the newly-created private configuration, together with the
- // containing configuration database, for their subsequent re-
- // link.
- //
- apc (sdb, move (cd));
+ db = &sdb.find_attached (*lc->id);
+ }
- db = &sdb.find_attached (*lc->id);
+ ddb = db; // Switch to the dependency configuration.
}
- ddb = db; // Switch to the dependency configuration.
- }
-
- // Note that building a dependent which is not a build2 module in the
- // same configuration with the build2 module it depends upon is an
- // error.
- //
- if (das.buildtime &&
- !build2_module (name) &&
- build2_module (dn) &&
- pdb == *ddb)
- {
- // Note that the dependent package information is printed by the
- // above exception guard.
+ // Note that building a dependent which is not a build2 module in
+ // the same configuration with the build2 module it depends upon is
+ // an error.
//
- fail << "unable to build build system module " << dn << " in its "
- << "dependent package configuration " << pdb.config_orig <<
- info << "use --config-* to select suitable configuration";
- }
+ if (das.buildtime &&
+ !build2_module (name) &&
+ build2_module (dn) &&
+ pdb == *ddb)
+ {
+ // Note that the dependent package information is printed by the
+ // above exception guard.
+ //
+ fail << "unable to build build system module " << dn << " in its "
+ << "dependent package configuration " << pdb.config_orig <<
+ info << "use --config-* to select suitable configuration";
+ }
- // If we didn't get the available package corresponding to the
- // selected package, look for any that satisfies the constraint.
- //
- if (dap == nullptr)
- {
- // And if we have no repository fragment to look in, then that means
- // the package is an orphan (we delay this check until we actually
- // need the repository fragment to allow orphans without
- // prerequisites).
- //
- if (af == nullptr)
- fail << "package " << pkg.available_name_version_db ()
- << " is orphaned" <<
- info << "explicitly upgrade it to a new version";
-
- // We look for prerequisites only in the repositories of this
- // package (and not in all the repositories of this configuration).
- // At first this might look strange, but it also kind of makes
- // sense: we only use repositories "approved" for this package
- // version. Consider this scenario as an example: hello/1.0.0 and
- // libhello/1.0.0 in stable and libhello/2.0.0 in testing. As a
- // prerequisite of hello, which version should libhello resolve to?
- // While one can probably argue either way, resolving it to 1.0.0 is
- // the conservative choice and the user can always override it by
- // explicitly building libhello.
- //
- // Note though, that if this is a test package, then its special
- // test dependencies (main packages that refer to it) should be
- // searched upstream through the complement repositories
- // recursively, since the test packages may only belong to the main
- // package's repository and its complements.
- //
- // @@ Currently we don't implement the reverse direction search for
- // the test dependencies, effectively only supporting the common
- // case where the main and test packages belong to the same
- // repository. Will need to fix this eventually.
- //
- // Note that this logic (naturally) does not apply if the package is
- // already selected by the user (see above).
- //
- // Also note that for the user-specified dependency version
- // constraint we rely on the satisfying package version be present
- // in repositories of the first dependent met. As a result, we may
- // fail too early if such package version doesn't belong to its
- // repositories, but belongs to the ones of some dependent that
- // we haven't met yet. Can we just search all repositories for an
- // available package of the appropriate version and just take it,
- // if present? We could, but then which repository should we pick?
- // The wrong choice can introduce some unwanted repositories and
- // package versions into play. So instead, we will postpone
- // collecting the problematic dependent, expecting that some other
- // one will find the appropriate version in its repositories.
+ // If we didn't get the available package corresponding to the
+ // selected package, look for any that satisfies the constraint.
//
- // For a system package we pick the latest version just to make sure
- // the package is recognized. An unrecognized package means the
- // broken/stale repository (see below).
- //
- rp = find_available_one (dn, !system ? d.constraint : nullopt, af);
-
if (dap == nullptr)
{
- if (dep_constr && !system && postponed)
- {
- postponed->insert (&pkg);
- break;
- }
-
- diag_record dr (fail);
-
- // Issue diagnostics differently based on the presence of
- // available packages for the unsatisfied dependency.
+ // And if we have no repository fragment to look in, then that
+ // means the package is an orphan (we delay this check until we
+ // actually need the repository fragment to allow orphans without
+ // prerequisites).
+ //
+ if (af == nullptr)
+ fail << "package " << pkg.available_name_version_db ()
+ << " is orphaned" <<
+ info << "explicitly upgrade it to a new version";
+
+ // We look for prerequisites only in the repositories of this
+ // package (and not in all the repositories of this
+ // configuration). At first this might look strange, but it also
+ // kind of makes sense: we only use repositories "approved" for
+ // this package version. Consider this scenario as an example:
+ // hello/1.0.0 and libhello/1.0.0 in stable and libhello/2.0.0 in
+ // testing. As a prerequisite of hello, which version should
+ // libhello resolve to? While one can probably argue either way,
+ // resolving it to 1.0.0 is the conservative choice and the user
+ // can always override it by explicitly building libhello.
+ //
+ // Note though, that if this is a test package, then its special
+ // test dependencies (main packages that refer to it) should be
+ // searched upstream through the complement repositories
+ // recursively, since the test packages may only belong to the main
+ // package's repository and its complements.
+ //
+ // @@ Currently we don't implement the reverse direction search for
+ // the test dependencies, effectively only supporting the common
+ // case where the main and test packages belong to the same
+ // repository. Will need to fix this eventually.
//
- // Note that there can't be any stubs, since they satisfy any
- // constraint and we won't be here if they were.
+ // Note that this logic (naturally) does not apply if the package
+ // is already selected by the user (see above).
//
- vector<shared_ptr<available_package>> aps (
- find_available (dn, nullopt /* version_constraint */, af));
+ // Also note that for the user-specified dependency version
+ // constraint we rely on the satisfying package version be present
+ // in repositories of the first dependent met. As a result, we may
+ // fail too early if such package version doesn't belong to its
+ // repositories, but belongs to the ones of some dependent that
+ // we haven't met yet. Can we just search all repositories for an
+ // available package of the appropriate version and just take it,
+ // if present? We could, but then which repository should we pick?
+ // The wrong choice can introduce some unwanted repositories and
+ // package versions into play. So instead, we will postpone
+ // collecting the problematic dependent, expecting that some other
+ // one will find the appropriate version in its repositories.
+ //
+ // For a system package we pick the latest version just to make
+ // sure the package is recognized. An unrecognized package means
+ // the broken/stale repository (see below).
+ //
+ rp = find_available_one (dn, !system ? d.constraint : nullopt, af);
- if (!aps.empty ())
+ if (dap == nullptr)
{
- dr << "unable to satisfy dependency constraint (" << dn;
+ if (dep_constr && !system && postponed)
+ {
+ postponed->insert (&pkg);
+ postpone = true;
+ break;
+ }
+
+ diag_record dr (fail);
- // We need to be careful not to print the wildcard-based
- // constraint.
+ // Issue diagnostics differently based on the presence of
+ // available packages for the unsatisfied dependency.
//
- if (d.constraint && (!dep_constr || !wildcard (*dep_constr)))
- dr << ' ' << *d.constraint;
+ // Note that there can't be any stubs, since they satisfy any
+ // constraint and we won't be here if they were.
+ //
+ vector<shared_ptr<available_package>> aps (
+ find_available (dn, nullopt /* version_constraint */, af));
- dr << ") of package " << name << pdb <<
- info << "available " << dn << " versions:";
+ if (!aps.empty ())
+ {
+ dr << "unable to satisfy dependency constraint (" << dn;
- for (const shared_ptr<available_package>& ap: aps)
- dr << ' ' << ap->version;
- }
- else
- {
- dr << "no package available for dependency " << dn
- << " of package " << name << pdb;
- }
+ // We need to be careful not to print the wildcard-based
+ // constraint.
+ //
+ if (d.constraint && (!dep_constr || !wildcard (*dep_constr)))
+ dr << ' ' << *d.constraint;
- // Avoid printing this if the dependent package is external since
- // it's more often confusing than helpful (they are normally not
- // fetched manually).
- //
- if (!af->location.empty () &&
- !af->location.directory_based () &&
- (!dep_constr || system))
- dr << info << "repository " << af->location << " appears to "
- << "be broken" <<
- info << "or the repository state could be stale" <<
- info << "run 'bpkg rep-fetch' to update";
- }
+ dr << ") of package " << name << pdb <<
+ info << "available " << dn << " versions:";
- // If all that's available is a stub then we need to make sure the
- // package is present in the system repository and it's version
- // satisfies the constraint. If a source package is available but
- // there is a system package specified on the command line and it's
- // version satisfies the constraint then the system package should
- // be preferred. To recognize such a case we just need to check if
- // the authoritative system version is set and it satisfies the
- // constraint. If the corresponding system package is non-optional
- // it will be preferred anyway.
- //
- if (dap->stub ())
- {
- // Note that the constraint can safely be printed as it can't
- // be a wildcard (produced from the user-specified dependency
- // version constraint). If it were, then the system version
- // wouldn't be NULL and would satisfy itself.
+ for (const shared_ptr<available_package>& ap: aps)
+ dr << ' ' << ap->version;
+ }
+ else
+ {
+ dr << "no package available for dependency " << dn
+ << " of package " << name << pdb;
+ }
+
+ // Avoid printing this if the dependent package is external
+ // since it's more often confusing than helpful (they are
+ // normally not fetched manually).
+ //
+ if (!af->location.empty () &&
+ !af->location.directory_based () &&
+ (!dep_constr || system))
+ dr << info << "repository " << af->location << " appears to "
+ << "be broken" <<
+ info << "or the repository state could be stale" <<
+ info << "run 'bpkg rep-fetch' to update";
+ }
+
+ // If all that's available is a stub then we need to make sure the
+ // package is present in the system repository and it's version
+ // satisfies the constraint. If a source package is available but
+ // there is a system package specified on the command line and
+ // it's version satisfies the constraint then the system package
+ // should be preferred. To recognize such a case we just need to
+ // check if the authoritative system version is set and it
+ // satisfies the constraint. If the corresponding system package
+ // is non-optional it will be preferred anyway.
//
- if (dap->system_version (*ddb) == nullptr)
- fail << "dependency " << d << " of package " << name << " is "
- << "not available in source" <<
- info << "specify ?sys:" << dn << " if it is available from "
- << "the system";
-
- if (!satisfies (*dap->system_version (*ddb), d.constraint))
- fail << "dependency " << d << " of package " << name << " is "
- << "not available in source" <<
- info << package_string (dn,
- *dap->system_version (*ddb),
- true /* system */)
- << " does not satisfy the constrains";
-
- system = true;
- }
- else
- {
- auto p (dap->system_version_authoritative (*ddb));
+ if (dap->stub ())
+ {
+ // Note that the constraint can safely be printed as it can't be
+ // a wildcard (produced from the user-specified dependency
+ // version constraint). If it were, then the system version
+ // wouldn't be NULL and would satisfy itself.
+ //
+ if (dap->system_version (*ddb) == nullptr)
+ fail << "dependency " << d << " of package " << name << " is "
+ << "not available in source" <<
+ info << "specify ?sys:" << dn << " if it is available from "
+ << "the system";
+
+ if (!satisfies (*dap->system_version (*ddb), d.constraint))
+ fail << "dependency " << d << " of package " << name << " is "
+ << "not available in source" <<
+ info << package_string (dn,
+ *dap->system_version (*ddb),
+ true /* system */)
+ << " does not satisfy the constrains";
- if (p.first != nullptr &&
- p.second && // Authoritative.
- satisfies (*p.first, d.constraint))
system = true;
+ }
+ else
+ {
+ auto p (dap->system_version_authoritative (*ddb));
+
+ if (p.first != nullptr &&
+ p.second && // Authoritative.
+ satisfies (*p.first, d.constraint))
+ system = true;
+ }
}
- }
- build_package bp {
- build_package::build,
- *ddb,
- dsp,
- dap,
- move (rp.second),
- nullopt, // Hold package.
- nullopt, // Hold version.
- {}, // Constraints.
- system,
- false, // Keep output directory.
- false, // Disfigure (from-scratch reconf).
- false, // Configure-only.
- nullopt, // Checkout root.
- false, // Checkout purge.
- strings (), // Configuration variables.
- {config_package {pdb, name}}, // Required by (dependent).
- true, // Required by dependents.
- 0}; // State flags.
-
- // Add our constraint, if we have one.
- //
- // Note that we always add the constraint implied by the dependent.
- // The user-implied constraint, if present, will be added when merging
- // from the pre-entered entry. So we will have both constraints for
- // completeness.
- //
- if (dp.constraint)
- bp.constraints.emplace_back (pdb, name.string (), *dp.constraint);
-
- // Now collect this prerequisite. If it was actually collected
- // (i.e., it wasn't already there) and we are forcing a downgrade or
- // upgrade, then refuse for a held version, warn for a held package,
- // and print the info message otherwise, unless the verbosity level is
- // less than two.
- //
- // Note though that while the prerequisite was collected it could have
- // happen because it is an optional package and so not being
- // pre-collected earlier. Meanwhile the package was specified
- // explicitly and we shouldn't consider that as a dependency-driven
- // up/down-grade enforcement.
- //
- // Here is an example of the situation we need to handle properly:
- //
- // repo: foo/2(->bar/2), bar/0+1
- // build sys:bar/1
- // build foo ?sys:bar/2
- //
- const build_package* p (
- collect_build (options,
- move (bp),
- fdb,
- rpt_depts,
- apc,
- postponed,
- &dep_chain));
-
- if (p != nullptr && force && !dep_optional)
- {
- // Fail if the version is held. Otherwise, warn if the package is
- // held.
+ build_package bp {
+ build_package::build,
+ *ddb,
+ dsp,
+ dap,
+ move (rp.second),
+ nullopt, // Hold package.
+ nullopt, // Hold version.
+ {}, // Constraints.
+ system,
+ false, // Keep output directory.
+ false, // Disfigure (from-scratch reconf).
+ false, // Configure-only.
+ nullopt, // Checkout root.
+ false, // Checkout purge.
+ strings (), // Configuration variables.
+ {config_package {pdb, name}}, // Required by (dependent).
+ true, // Required by dependents.
+ 0}; // State flags.
+
+ // Add our constraint, if we have one.
+ //
+ // Note that we always add the constraint implied by the dependent.
+ // The user-implied constraint, if present, will be added when
+ // merging from the pre-entered entry. So we will have both
+ // constraints for completeness.
+ //
+ if (dp.constraint)
+ bp.constraints.emplace_back (pdb, name.string (), *dp.constraint);
+
+ // Now collect this prerequisite. If it was actually collected
+ // (i.e., it wasn't already there) and we are forcing a downgrade or
+ // upgrade, then refuse for a held version, warn for a held package,
+ // and print the info message otherwise, unless the verbosity level
+ // is less than two.
+ //
+ // Note though that while the prerequisite was collected it could
+ // have happen because it is an optional package and so not being
+ // pre-collected earlier. Meanwhile the package was specified
+ // explicitly and we shouldn't consider that as a dependency-driven
+ // up/down-grade enforcement.
+ //
+ // Here is an example of the situation we need to handle properly:
+ //
+ // repo: foo/2(->bar/2), bar/0+1
+ // build sys:bar/1
+ // build foo ?sys:bar/2
//
- bool f (dsp->hold_version);
- bool w (!f && dsp->hold_package);
+ const build_package* p (
+ collect_build (options,
+ move (bp),
+ fdb,
+ rpt_depts,
+ apc,
+ postponed,
+ &dep_chain));
- if (f || w || verb >= 2)
+ if (p != nullptr && force && !dep_optional)
{
- const version& av (p->available_version ());
+ // Fail if the version is held. Otherwise, warn if the package is
+ // held.
+ //
+ bool f (dsp->hold_version);
+ bool w (!f && dsp->hold_package);
- bool u (av > dsp->version);
- bool c (d.constraint);
+ if (f || w || verb >= 2)
+ {
+ const version& av (p->available_version ());
- diag_record dr;
+ bool u (av > dsp->version);
+ bool c (d.constraint);
- (f ? dr << fail :
- w ? dr << warn :
- dr << info)
- << "package " << name << pdb << " dependency on "
- << (c ? "(" : "") << d << (c ? ")" : "") << " is forcing "
- << (u ? "up" : "down") << "grade of " << *dsp << *ddb << " to ";
+ diag_record dr;
- // Print both (old and new) package names in full if the system
- // attribution changes.
- //
- if (dsp->system ())
- dr << p->available_name_version ();
- else
- dr << av; // Can't be a system version so is never wildcard.
+ (f ? dr << fail :
+ w ? dr << warn :
+ dr << info)
+ << "package " << name << pdb << " dependency on "
+ << (c ? "(" : "") << d << (c ? ")" : "") << " is forcing "
+ << (u ? "up" : "down") << "grade of " << *dsp << *ddb
+ << " to ";
+
+ // Print both (old and new) package names in full if the system
+ // attribution changes.
+ //
+ if (dsp->system ())
+ dr << p->available_name_version ();
+ else
+ dr << av; // Can't be a system version so is never wildcard.
- if (dsp->hold_version)
- dr << info << "package version " << *dsp << *ddb << " is held";
+ if (dsp->hold_version)
+ dr << info << "package version " << *dsp << *ddb << " is held";
- if (f)
- dr << info << "explicitly request version "
- << (u ? "up" : "down") << "grade to continue";
+ if (f)
+ dr << info << "explicitly request version "
+ << (u ? "up" : "down") << "grade to continue";
+ }
}
}
+
+ if (postpone)
+ break;
}
dep_chain.pop_back ();
@@ -2479,30 +2484,28 @@ namespace bpkg
for (const dependency_alternatives_ex& das:
reverse_iterate (ap->dependencies))
{
- assert (!das.conditional () && das.size () == 1); // @@ TODO
-
- const dependency_alternative& da (das.front ());
+ assert (!das.conditional () && das.size () == 1); // @@ DEP
- assert (da.size () == 1); // @@ DEP
-
- const dependency& d (da[0]);
- const package_name& dn (d.name);
+ for (const dependency& d: das.front ())
+ {
+ const package_name& dn (d.name);
- // Skip special names.
- //
- if (das.buildtime && (dn == "build2" || dn == "bpkg"))
- continue;
+ // Skip special names.
+ //
+ if (das.buildtime && (dn == "build2" || dn == "bpkg"))
+ continue;
- // Note that for the repointed dependent we only order its new and
- // unamended prerequisites here. Its replaced prerequisites will
- // be ordered below.
- //
- update (order (pdb,
- d.name,
- das.buildtime,
- chain,
- fdb,
- false /* reorder */));
+ // Note that for the repointed dependent we only order its new
+ // and unamended prerequisites here. Its replaced prerequisites
+ // will be ordered below.
+ //
+ update (order (pdb,
+ d.name,
+ das.buildtime,
+ chain,
+ fdb,
+ false /* reorder */));
+ }
}
}
}
diff --git a/bpkg/pkg-configure.cxx b/bpkg/pkg-configure.cxx
index 1aa3615..ebd6545 100644
--- a/bpkg/pkg-configure.cxx
+++ b/bpkg/pkg-configure.cxx
@@ -30,91 +30,125 @@ namespace bpkg
for (const dependency_alternatives_ex& das: deps)
{
- assert (!das.conditional ()); //@@ TODO
+ // @@ DEP Currently we just pick the first alternative with dependencies
+ // that can all be resolved to the configured packages, satisfying
+ // the respective constraints. Later, we should also evaluate the
+ // alternative enable conditions.
+ //
+ assert (!das.conditional ());
bool satisfied (false);
for (const dependency_alternative& da: das)
{
- assert (da.size () == 1); // @@ DEP
+ // Cache the selected packages which correspond to the alternative
+ // dependencies, pairing them with the respective constraints. If the
+ // alternative turns out to be fully resolvable, we will add the
+ // cached packages into the dependent's prerequisites map.
+ //
+ small_vector<
+ pair<lazy_shared_ptr<selected_package>,
+ const optional<version_constraint>&>, 1> prerequisites;
- const dependency& d (da[0]);
- const package_name& n (d.name);
+ dependency_alternative::const_iterator b (da.begin ());
+ dependency_alternative::const_iterator i (b);
+ dependency_alternative::const_iterator e (da.end ());
- if (das.buildtime)
+ assert (b != e);
+
+ for (; i != e; ++i)
{
- // Handle special names.
- //
- if (n == "build2")
- {
- if (d.constraint && !satisfy_build2 (o, d))
- fail << "unable to satisfy constraint (" << d
- << ") for package " << package <<
- info << "available build2 version is " << build2_version;
+ const dependency& d (*i);
+ const package_name& n (d.name);
- satisfied = true;
- break;
- }
- else if (n == "bpkg")
+ if (das.buildtime)
{
- if (d.constraint && !satisfy_bpkg (o, d))
- fail << "unable to satisfy constraint (" << d
- << ") for package " << package <<
- info << "available bpkg version is " << bpkg_version;
-
- satisfied = true;
- break;
+ // Handle special names.
+ //
+ if (n == "build2")
+ {
+ if (d.constraint && !satisfy_build2 (o, d))
+ fail << "unable to satisfy constraint (" << d
+ << ") for package " << package <<
+ info << "available build2 version is " << build2_version;
+
+ continue;
+ }
+ else if (n == "bpkg")
+ {
+ if (d.constraint && !satisfy_bpkg (o, d))
+ fail << "unable to satisfy constraint (" << d
+ << ") for package " << package <<
+ info << "available bpkg version is " << bpkg_version;
+
+ continue;
+ }
}
- }
- database* ddb (fdb ? fdb (db, n, das.buildtime) : nullptr);
+ database* ddb (fdb ? fdb (db, n, das.buildtime) : nullptr);
- pair<shared_ptr<selected_package>, database*> spd (
- ddb != nullptr
- ? make_pair (ddb->find<selected_package> (n), ddb)
- : find_dependency (db, n, das.buildtime));
+ pair<shared_ptr<selected_package>, database*> spd (
+ ddb != nullptr
+ ? make_pair (ddb->find<selected_package> (n), ddb)
+ : find_dependency (db, n, das.buildtime));
- if (const shared_ptr<selected_package>& dp = spd.first)
- {
- if (dp->state != package_state::configured)
- continue;
+ const shared_ptr<selected_package>& dp (spd.first);
- if (!satisfies (dp->version, d.constraint))
- continue;
+ if (dp == nullptr ||
+ dp->state != package_state::configured ||
+ !satisfies (dp->version, d.constraint))
+ break;
// See the package_prerequisites definition for details on creating
// the map keys with the database passed.
//
- auto p (
- r.emplace (lazy_shared_ptr<selected_package> (*spd.second, dp),
- d.constraint));
-
- // Currently we can only capture a single constraint, so if we
- // already have a dependency on this package and one constraint is
- // not a subset of the other, complain.
- //
- if (!p.second)
- {
- auto& c (p.first->second);
-
- bool s1 (satisfies (c, d.constraint));
- bool s2 (satisfies (d.constraint, c));
-
- if (!s1 && !s2)
- fail << "multiple dependencies on package " << n <<
- info << n << " " << *c <<
- info << n << " " << *d.constraint;
-
- if (s2 && !s1)
- c = d.constraint;
- }
-
- satisfied = true;
- break;
+ prerequisites.emplace_back (
+ lazy_shared_ptr<selected_package> (*spd.second, dp),
+ d.constraint);
+ }
+
+ // Try the next alternative if there are unresolved dependencies for
+ // this alternative.
+ //
+ if (i != e)
+ continue;
+
+ // Now add the selected packages resolved for the alternative into the
+ // dependent's prerequisites map and skip the remaining alternatives.
+ //
+ for (auto& pr: prerequisites)
+ {
+ const package_name& pn (pr.first.object_id ());
+ const optional<version_constraint>& pc (pr.second);
+
+ auto p (r.emplace (pr.first, pc));
+
+ // Currently we can only capture a single constraint, so if we
+ // already have a dependency on this package and one constraint is
+ // not a subset of the other, complain.
+ //
+ if (!p.second)
+ {
+ auto& c (p.first->second);
+
+ bool s1 (satisfies (c, pc));
+ bool s2 (satisfies (pc, c));
+
+ if (!s1 && !s2)
+ fail << "multiple dependencies on package " << pn <<
+ info << pn << " " << *c <<
+ info << pn << " " << *pc;
+
+ if (s2 && !s1)
+ c = pc;
+ }
}
+
+ satisfied = true;
+ break;
}
if (!satisfied)
- fail << "no configured package satisfies dependency on " << das;
+ fail << "unable to satisfy dependency on " << das;
}
return r;
diff --git a/bpkg/pkg-verify.cxx b/bpkg/pkg-verify.cxx
index a65eeef..f2d9881 100644
--- a/bpkg/pkg-verify.cxx
+++ b/bpkg/pkg-verify.cxx
@@ -68,58 +68,67 @@ namespace bpkg
{
for (const dependency_alternative& da: das)
{
- assert (da.size () == 1); // @@ DEP
-
- const dependency& d (da[0]);
- const package_name& dn (d.name);
-
- if (dn != "build2" && dn != "bpkg")
- continue;
-
- if (das.size () != 1)
+ for (const dependency& d: da)
{
- if (diag_level != 0)
- error (p.name (), nv.value_line, nv.value_column)
- << "alternatives in " << dn << " dependency";
+ const package_name& dn (d.name);
- throw failed ();
- }
+ if (dn != "build2" && dn != "bpkg")
+ continue;
- if (dn == "build2")
- {
- if (d.constraint && !satisfy_build2 (co, d))
+ if (da.size () != 1)
{
if (diag_level != 0)
- {
- diag_record dr (error);
- dr << "unable to satisfy constraint (" << d << ")";
+ error (p.name (), nv.value_line, nv.value_column)
+ << "multiple names in " << dn << " dependency";
- if (!what.empty ())
- dr << " for package " << what;
+ throw failed ();
+ }
- dr << info << "available build2 version is "
- << build2_version;
- }
+ if (das.size () != 1)
+ {
+ if (diag_level != 0)
+ error (p.name (), nv.value_line, nv.value_column)
+ << "alternatives in " << dn << " dependency";
throw failed ();
}
- }
- else
- {
- if (d.constraint && !satisfy_bpkg (co, d))
+
+ if (dn == "build2")
{
- if (diag_level != 0)
+ if (d.constraint && !satisfy_build2 (co, d))
{
- diag_record dr (error);
- dr << "unable to satisfy constraint (" << d << ")";
+ if (diag_level != 0)
+ {
+ diag_record dr (error);
+ dr << "unable to satisfy constraint (" << d << ")";
- if (!what.empty ())
- dr << " for package " << what;
+ if (!what.empty ())
+ dr << " for package " << what;
- dr << "available bpkg version is " << bpkg_version;
+ dr << info << "available build2 version is "
+ << build2_version;
+ }
+
+ throw failed ();
}
+ }
+ else
+ {
+ if (d.constraint && !satisfy_bpkg (co, d))
+ {
+ if (diag_level != 0)
+ {
+ diag_record dr (error);
+ dr << "unable to satisfy constraint (" << d << ")";
- throw failed ();
+ if (!what.empty ())
+ dr << " for package " << what;
+
+ dr << "available bpkg version is " << bpkg_version;
+ }
+
+ throw failed ();
+ }
}
}
}
diff --git a/tests/common/dependency-alternatives/t8a/foo-1.0.0.tar.gz b/tests/common/dependency-alternatives/t8a/foo-1.0.0.tar.gz
new file mode 100644
index 0000000..bca3658
--- /dev/null
+++ b/tests/common/dependency-alternatives/t8a/foo-1.0.0.tar.gz
Binary files differ
diff --git a/tests/common/dependency-alternatives/t8a/libbar-1.0.0.tar.gz b/tests/common/dependency-alternatives/t8a/libbar-1.0.0.tar.gz
new file mode 100644
index 0000000..ce7f270
--- /dev/null
+++ b/tests/common/dependency-alternatives/t8a/libbar-1.0.0.tar.gz
Binary files differ
diff --git a/tests/common/dependency-alternatives/t8a/libbaz-1.0.0.tar.gz b/tests/common/dependency-alternatives/t8a/libbaz-1.0.0.tar.gz
new file mode 100644
index 0000000..1dd802e
--- /dev/null
+++ b/tests/common/dependency-alternatives/t8a/libbaz-1.0.0.tar.gz
Binary files differ
diff --git a/tests/common/dependency-alternatives/t8a/repositories.manifest b/tests/common/dependency-alternatives/t8a/repositories.manifest
new file mode 100644
index 0000000..5b70556
--- /dev/null
+++ b/tests/common/dependency-alternatives/t8a/repositories.manifest
@@ -0,0 +1 @@
+: 1
diff --git a/tests/pkg-build.testscript b/tests/pkg-build.testscript
index 058a08b..213290d 100644
--- a/tests/pkg-build.testscript
+++ b/tests/pkg-build.testscript
@@ -136,6 +136,12 @@
# | |-- libbox-1.1.0.tar.gz -> * foo ^1.0.0
# | `-- repositories.manifest
# |
+# |-- t8a
+# | |-- foo-1.0.0.tar.gz -> {libbar libbaz} ^1.0.0
+# | |-- libbar-1.0.0.tar.gz
+# | |-- libbaz-1.0.0.tar.gz
+# | `-- repositories.manifest
+# |
# `-- git
# |-- libbar.git -> style-basic.git (prerequisite repository)
# |-- libbaz.git
@@ -164,6 +170,7 @@ posix = ($cxx.target.class != 'windows')
cp -r $src/t6 $out/t6 && $rep_create $out/t6 &$out/t6/packages.manifest
cp -r $src/t7a $out/t7a && $rep_create $out/t7a &$out/t7a/packages.manifest
cp -r $src/t7b $out/t7b && $rep_create $out/t7b &$out/t7b/packages.manifest
+ cp -r $src/t8a $out/t8a && $rep_create $out/t8a &$out/t8a/packages.manifest
# Create git repositories.
#
@@ -2972,6 +2979,34 @@ test.options += --no-progress
-$pkg_drop libbar libbaz libfoo
}
+
+ : alternative
+ :
+ {
+ +$clone_root_cfg && $rep_add $rep/t8a && $rep_fetch
+
+ : multiple-dependencies
+ :
+ {
+ $clone_cfg;
+
+ $* foo --yes 2>>~%EOE%;
+ fetched libbaz/1.0.0
+ unpacked libbaz/1.0.0
+ fetched libbar/1.0.0
+ unpacked libbar/1.0.0
+ fetched foo/1.0.0
+ unpacked foo/1.0.0
+ configured libbaz/1.0.0
+ configured libbar/1.0.0
+ configured foo/1.0.0
+ %info: .+foo-1.0.0.+ is up to date%
+ updated foo/1.0.0
+ EOE
+
+ $pkg_drop foo
+ }
+ }
}
: dependent
diff --git a/tests/pkg-build/t8a b/tests/pkg-build/t8a
new file mode 120000
index 0000000..8fa2bda
--- /dev/null
+++ b/tests/pkg-build/t8a
@@ -0,0 +1 @@
+../common/dependency-alternatives/t8a/ \ No newline at end of file
diff --git a/tests/pkg-configure.testscript b/tests/pkg-configure.testscript
index 5a7d8aa..eff0a2e 100644
--- a/tests/pkg-configure.testscript
+++ b/tests/pkg-configure.testscript
@@ -35,6 +35,9 @@
# | | |-- driver.cxx
# | | `-- test.out
# | `-- version
+# |
+# |-- t8a (see pkg-build for details)
+# |
# `-- stable
# |-- libbar-1.0.0.tar.gz -> libfoo
# |-- libbar-1.1.0.tar.gz -> libfoo >= 1.1.0
@@ -63,6 +66,8 @@
#
cp -r $src/stable $out/stable
$rep_create $out/stable &$out/stable/packages.manifest
+
+ cp -r $src/t8a $out/t8a && $rep_create $out/t8a &$out/t8a/packages.manifest
end
test.arguments += config.cxx=$quote($recall($cxx.path) $cxx.config.mode, true)
@@ -280,7 +285,7 @@ if ($posix && "$uid" != '0')
$pkg_fetch libbar/1.0.0 && $pkg_unpack libbar;
$* libbar 2>>EOE != 0;
- error: no configured package satisfies dependency on libfoo
+ error: unable to satisfy dependency on libfoo
EOE
$pkg_status libbar/1.0.0 1>'libbar unpacked 1.0.0';
@@ -288,7 +293,7 @@ if ($posix && "$uid" != '0')
$pkg_unpack libfoo;
$* libbar 2>>EOE != 0;
- error: no configured package satisfies dependency on libfoo
+ error: unable to satisfy dependency on libfoo
EOE
$* libfoo 2>'configured libfoo/1.0.0';
@@ -317,7 +322,7 @@ if ($posix && "$uid" != '0')
$pkg_unpack libbar;
$* libbar 2>>EOE != 0;
- error: no configured package satisfies dependency on libfoo >= 1.1.0
+ error: unable to satisfy dependency on libfoo >= 1.1.0
EOE
$pkg_disfigure libfoo 2>'disfigured libfoo/1.0.0';
@@ -344,7 +349,7 @@ if ($posix && "$uid" != '0')
$pkg_unpack libbar;
$* libbar 2>>EOE != 0;
- error: no configured package satisfies dependency on libfox | libfoo >= 1.2.0
+ error: unable to satisfy dependency on libfox | libfoo >= 1.2.0
EOE
$pkg_disfigure libfoo 2>'disfigured libfoo/1.1.0';
@@ -411,3 +416,39 @@ if ($posix && "$uid" != '0')
test -d cfg/libhello != 0
}
}
+
+: dependency-alternatives
+:
+{
+ +$clone_root_cfg && $rep_add $rep/t8a && $rep_fetch --trust-yes
+
+ : multiple-dependencies
+ :
+ {
+ $clone_cfg;
+
+ $pkg_fetch foo/1.0.0 && $pkg_unpack foo;
+
+ $pkg_fetch libbar/1.0.0 && $pkg_unpack libbar;
+ $* libbar 2>!;
+
+ # Make sure that dependent configuration fails if some of the alternative
+ # dependencies is not configured.
+ #
+ $* foo 2>>EOE != 0;
+ error: unable to satisfy dependency on {libbar ^1.0.0 libbaz ^1.0.0}
+ EOE
+
+ $pkg_fetch libbaz/1.0.0 && $pkg_unpack libbaz;
+ $* libbaz 2>!;
+
+ $* foo 2>'configured foo/1.0.0';
+
+ $pkg_disfigure foo 2>!;
+ $pkg_purge foo 2>!;
+ $pkg_disfigure libbaz 2>!;
+ $pkg_purge libbaz 2>!;
+ $pkg_disfigure libbar 2>!;
+ $pkg_purge libbar 2>!
+ }
+}
diff --git a/tests/pkg-configure/t8a b/tests/pkg-configure/t8a
new file mode 120000
index 0000000..8fa2bda
--- /dev/null
+++ b/tests/pkg-configure/t8a
@@ -0,0 +1 @@
+../common/dependency-alternatives/t8a/ \ No newline at end of file