aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2021-09-15 20:08:28 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2021-09-17 15:04:11 +0300
commitc92a512cb7c7b5f35fb24e9bd36f50be8e26e763 (patch)
tree0dbc69a8652cdc89ebc767bb67027fab003306f1
parentcec96e9c34e608c5f88b73adce5a32fce76d09e6 (diff)
Change semantics for ?sys:<pkg> in pkg-build if no package configuration is explicitly specified
Now, if configuration is not specified for a system dependency package, then it is assumed to be specified for all current configurations and their explicitly linked configurations, recursively, including private configurations that can potentially be created during this run.
-rw-r--r--bpkg/pkg-build.cxx587
-rw-r--r--tests/pkg-build.testscript395
-rw-r--r--tests/pkg-system.testscript18
3 files changed, 804 insertions, 196 deletions
diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx
index 30713ec..db8dafb 100644
--- a/bpkg/pkg-build.cxx
+++ b/bpkg/pkg-build.cxx
@@ -770,6 +770,8 @@ namespace bpkg
using build_package_refs =
small_vector<reference_wrapper<const build_package>, 16>;
+ using add_priv_cfg_function = void (database&, dir_path&&);
+
struct build_packages: build_package_list
{
// Packages collection of whose prerequisites has been postponed due the
@@ -796,6 +798,14 @@ namespace bpkg
assert (p.second);
}
+ // Return true if a package is already in the map.
+ //
+ bool
+ entered (database& db, const package_name& name)
+ {
+ return map_.find (db, name) != map_.end ();
+ }
+
// Collect the package being built. Return its pointer if this package
// version was, in fact, added to the map and NULL if it was already there
// or the existing version was preferred. So can be used as bool.
@@ -817,7 +827,7 @@ namespace bpkg
build_package pkg,
const function<find_database_function>& fdb,
const repointed_dependents& rpt_depts,
- private_configs& priv_cfgs,
+ const function<add_priv_cfg_function>& apc,
postponed_packages* recursively = nullptr,
build_package_refs* dep_chain = nullptr)
{
@@ -996,7 +1006,7 @@ namespace bpkg
recursively,
fdb,
rpt_depts,
- priv_cfgs,
+ apc,
*dep_chain);
return &p;
@@ -1024,7 +1034,7 @@ namespace bpkg
postponed_packages* postponed,
const function<find_database_function>& fdb,
const repointed_dependents& rpt_depts,
- private_configs& priv_cfgs,
+ const function<add_priv_cfg_function>& apc,
build_package_refs& dep_chain)
{
tracer trace ("collect_build_prerequisites");
@@ -1417,7 +1427,7 @@ namespace bpkg
// containing configuration database, for their subsequent re-
// link.
//
- priv_cfgs.emplace_back (sdb, move (cd));
+ apc (sdb, move (cd));
db = &sdb.find_attached (*lc->id);
}
@@ -1623,7 +1633,7 @@ namespace bpkg
move (bp),
fdb,
rpt_depts,
- priv_cfgs,
+ apc,
postponed,
&dep_chain));
@@ -1686,7 +1696,7 @@ namespace bpkg
const repointed_dependents& rpt_depts,
build_packages::postponed_packages& postponed,
const function<find_database_function>& fdb,
- private_configs& priv_cfgs)
+ const function<add_priv_cfg_function>& apc)
{
for (const auto& rd: rpt_depts)
{
@@ -1752,7 +1762,7 @@ namespace bpkg
move (p),
fdb,
rpt_depts,
- priv_cfgs,
+ apc,
&postponed,
&dep_chain);
}
@@ -1848,7 +1858,7 @@ namespace bpkg
postponed_packages& postponed,
const function<find_database_function>& fdb,
const repointed_dependents& rpt_depts,
- private_configs& priv_cfgs)
+ const function<add_priv_cfg_function>& apc)
{
auto mi (map_.find (db, name));
assert (mi != map_.end ());
@@ -1860,7 +1870,7 @@ namespace bpkg
&postponed,
fdb,
rpt_depts,
- priv_cfgs,
+ apc,
dep_chain);
}
@@ -1869,7 +1879,7 @@ namespace bpkg
postponed_packages& pkgs,
const function<find_database_function>& fdb,
const repointed_dependents& rpt_depts,
- private_configs& priv_cfgs)
+ const function<add_priv_cfg_function>& apc)
{
// Try collecting postponed packages for as long as we are making
// progress.
@@ -1887,7 +1897,7 @@ namespace bpkg
prog ? &npkgs : nullptr,
fdb,
rpt_depts,
- priv_cfgs,
+ apc,
dep_chain);
}
@@ -2550,12 +2560,21 @@ namespace bpkg
// List of dependency packages (specified with ? on the command line).
//
+ // If configuration is not specified for a system dependency package (db is
+ // NULL), then the dependency is assumed to be specified for all current
+ // configurations and their explicitly linked configurations, recursively,
+ // including private configurations that can potentially be created during
+ // this run.
+ //
+ // The selected package is not NULL if the database is not NULL and the
+ // dependency package is present in this database.
+ //
struct dependency_package
{
- database& db;
+ database* db; // Can only be NULL if system.
package_name name;
optional<version_constraint> constraint; // nullopt if unspecified.
- shared_ptr<selected_package> selected; // NULL if not present.
+ shared_ptr<selected_package> selected;
bool system;
bool patch; // Only for an empty version.
bool keep_out;
@@ -2672,8 +2691,9 @@ namespace bpkg
}
// Search for the user expectations regarding this dependency by
- // matching the name and configuration type and fail if there are
- // multiple candidates.
+ // matching the package name and configuration type, if configuration is
+ // specified, preferring entries with configuration specified and fail
+ // if there are multiple candidates.
//
if (!cur_dbs.empty ())
{
@@ -2681,30 +2701,42 @@ namespace bpkg
j != deps.end ();
++j)
{
- if (j->name == nm && j->db.type == db.type)
+ if (j->name == nm && (j->db == nullptr || j->db->type == db.type))
{
- if (i == deps.end ())
+ if (i == deps.end () || i->db == nullptr)
+ {
i = j;
- else
+ }
+ else if (j->db != nullptr)
+ {
fail << "multiple " << db.type << " configurations specified "
<< "for dependency package " << nm <<
- info << i->db.config_orig <<
- info << j->db.config_orig;
+ info << i->db->config_orig <<
+ info << j->db->config_orig;
+ }
}
}
}
}
else
{
- i = find_if (deps.begin (), deps.end (),
- [&db, &nm] (const dependency_package& i)
- {
- return i.name == nm && i.db == db;
- });
+ for (dependency_packages::const_iterator j (deps.begin ());
+ j != deps.end ();
+ ++j)
+ {
+ if (j->name == nm && (i->db == nullptr || *i->db == db))
+ {
+ if (i == deps.end () || i->db == nullptr)
+ i = j;
+
+ if (i->db != nullptr)
+ break;
+ }
+ }
}
bool user_exp (i != deps.end ());
- bool copy_dep (user_exp && i->db != db);
+ bool copy_dep (user_exp && i->db != nullptr && *i->db != db);
// Collect the dependents for checking the version constraints, using
// their repository fragments for discovering available dependency package
@@ -2719,7 +2751,7 @@ namespace bpkg
if (copy_dep)
{
- for (database& db: i->db.dependent_configs ())
+ for (database& db: i->db->dependent_configs ())
{
if (find (cur_dbs.begin (), cur_dbs.end (), db) != cur_dbs.end ())
dep_dbs.push_back (db);
@@ -2772,7 +2804,7 @@ namespace bpkg
//
assert (i != deps.end ());
- database& ddb (i->db);
+ database& ddb (i->db != nullptr ? *i->db : db);
const optional<version_constraint>& dvc (i->constraint); // May be nullopt.
bool dsys (i->system);
@@ -3671,16 +3703,13 @@ namespace bpkg
add_db (*db);
}
+ // Note that unspecified package configuration in the multi-
+ // configurations mode is an error, unless this is a system
+ // dependency. We, however, do not parse the package scheme at this
+ // stage and so delay the potential failure.
+ //
if (dbs.empty ())
- {
- if (multi_config ())
- fail << "no configuration specified for " << a <<
- info << "configuration must be explicitly specified for each "
- << "package in multi-configurations mode" <<
- info << "use --config-uuid to specify its configuration";
-
dbs.push_back (mdb);
- }
if (!a.empty () && a[0] == '?')
{
@@ -3900,7 +3929,10 @@ namespace bpkg
//
struct pkg_arg
{
- reference_wrapper<database> db;
+ // NULL for system dependency with unspecified configuration.
+ //
+ database* db;
+
package_scheme scheme;
package_name name;
optional<version_constraint> constraint;
@@ -3909,71 +3941,6 @@ namespace bpkg
strings config_vars;
};
- // Create the parsed package argument.
- //
- auto arg_package = [] (database& db,
- package_scheme sc,
- package_name nm,
- optional<version_constraint> vc,
- pkg_options os,
- strings vs) -> pkg_arg
- {
- assert (!vc || !vc->empty ()); // May not be empty if present.
-
- pkg_arg r {
- db, sc, move (nm), move (vc), string (), move (os), move (vs)};
-
- switch (sc)
- {
- case package_scheme::sys:
- {
- if (!r.constraint)
- r.constraint = version_constraint (wildcard_version);
-
- // The system package may only have an exact/wildcard version
- // specified.
- //
- assert (r.constraint->min_version == r.constraint->max_version);
-
- assert (db.system_repository);
-
- const system_package* sp (db.system_repository->find (r.name));
-
- // Will deal with all the duplicates later.
- //
- if (sp == nullptr || !sp->authoritative)
- {
- assert (db.system_repository);
-
- db.system_repository->insert (r.name,
- *r.constraint->min_version,
- true /* authoritative */);
- }
-
- break;
- }
- case package_scheme::none: break; // Nothing to do.
- }
-
- return r;
- };
-
- // Create the unparsed package argument.
- //
- auto arg_raw = [] (database& db,
- string v,
- pkg_options os,
- strings vs) -> pkg_arg
- {
- return pkg_arg {db,
- package_scheme::none,
- package_name (),
- nullopt /* constraint */,
- move (v),
- move (os),
- move (vs)};
- };
-
auto arg_parsed = [] (const pkg_arg& a) {return !a.name.empty ();};
auto arg_sys = [&arg_parsed] (const pkg_arg& a)
@@ -4086,6 +4053,96 @@ namespace bpkg
return r;
};
+ // Add the system package authoritative information to the database's
+ // system repository, unless it already contains authoritative information
+ // for this package.
+ //
+ // Note that it is assumed that all the possible duplicates are handled
+ // elsewhere/later.
+ //
+ auto add_system_package = [] (database& db,
+ const package_name& nm,
+ const version& v)
+ {
+ assert (db.system_repository);
+
+ const system_package* sp (db.system_repository->find (nm));
+
+ if (sp == nullptr || !sp->authoritative)
+ db.system_repository->insert (nm, v, true /* authoritative */);
+ };
+
+ // Create the parsed package argument. Issue diagnostics and fail if the
+ // package specification is invalid.
+ //
+ auto arg_package = [&arg_string, &add_system_package]
+ (database* db,
+ package_scheme sc,
+ package_name nm,
+ optional<version_constraint> vc,
+ pkg_options os,
+ strings vs) -> pkg_arg
+ {
+ assert (!vc || !vc->empty ()); // May not be empty if present.
+
+ if (db == nullptr)
+ assert (sc == package_scheme::sys && os.dependency ());
+
+ pkg_arg r {
+ db, sc, move (nm), move (vc), string (), move (os), move (vs)};
+
+ // Verify that the package database is specified in the multi-config
+ // mode, unless this is a system dependency package.
+ //
+ if (multi_config () &&
+ !os.config_uuid_specified () &&
+ !(db == nullptr &&
+ sc == package_scheme::sys &&
+ os.dependency ()))
+ fail << "no configuration specified for " << arg_string (r) <<
+ info << "configuration must be explicitly specified for each "
+ << "package in multi-configurations mode" <<
+ info << "use --config-uuid to specify its configuration";
+
+ switch (sc)
+ {
+ case package_scheme::sys:
+ {
+ if (!r.constraint)
+ r.constraint = version_constraint (wildcard_version);
+
+ // The system package may only have an exact/wildcard version
+ // specified.
+ //
+ assert (r.constraint->min_version == r.constraint->max_version);
+
+ if (db != nullptr)
+ add_system_package (*db, r.name, *r.constraint->min_version);
+
+ break;
+ }
+ case package_scheme::none: break; // Nothing to do.
+ }
+
+ return r;
+ };
+
+ // Create the unparsed package argument.
+ //
+ auto arg_raw = [] (database& db,
+ string v,
+ pkg_options os,
+ strings vs) -> pkg_arg
+ {
+ return pkg_arg {&db,
+ package_scheme::none,
+ package_name (),
+ nullopt /* constraint */,
+ move (v),
+ move (os),
+ move (vs)};
+ };
+
vector<pkg_arg> pkg_args;
{
// Cache the system stubs to create the imaginary system repository at
@@ -4153,11 +4210,22 @@ namespace bpkg
if (sys && vc)
stubs.push_back (make_shared<available_package> (n));
- pkg_args.push_back (arg_package (ps.db,
+ pkg_options& o (ps.options);
+
+ // Disregard the (main) database for a system dependency with
+ // unspecified configuration.
+ //
+ bool no_db (sys &&
+ o.dependency () &&
+ !o.config_name_specified () &&
+ !o.config_id_specified () &&
+ !o.config_uuid_specified ());
+
+ pkg_args.push_back (arg_package (no_db ? nullptr : &ps.db.get (),
sc,
move (n),
move (vc),
- move (ps.options),
+ move (o),
move (ps.config_vars)));
}
else // Add unparsed.
@@ -4268,7 +4336,7 @@ namespace bpkg
info << "package " << pv.first << " is not present in "
<< "configuration";
else
- pkg_args.push_back (arg_package (pdb,
+ pkg_args.push_back (arg_package (&pdb,
package_scheme::none,
pv.first,
version_constraint (pv.second),
@@ -4383,7 +4451,11 @@ namespace bpkg
// Don't move options and variables as they may be reused.
//
- pkg_args.push_back (arg_package (pdb,
+ // Note that this cannot be a system dependency with unspecified
+ // configuration since location is specified and so we always pass
+ // the database to the constructor.
+ //
+ pkg_args.push_back (arg_package (&pdb,
sc,
move (n),
move (vc),
@@ -4398,9 +4470,9 @@ namespace bpkg
imaginary_stubs = move (stubs);
}
- // List of packages specified on the command line.
+ // List of package configurations specified on the command line.
//
- vector<config_package> conf_pkgs;
+ vector<config_package> pkg_confs;
// Separate the packages specified on the command line into to hold and to
// up/down-grade as dependencies, and save dependents whose dependencies
@@ -4414,14 +4486,34 @@ namespace bpkg
// Check if the package is a duplicate. Return true if it is but
// harmless.
//
- map<config_package, pkg_arg> package_map;
+ struct config_package_key // Like config_package but with NULL'able db.
+ {
+ package_name name;
+ database* db; // Can be NULL for system dependency.
+
+ config_package_key (package_name n, database* d)
+ : name (move (n)), db (d) {}
+
+ bool
+ operator< (const config_package_key& v) const
+ {
+ if (int r = name.compare (v.name))
+ return r < 0;
+
+ return db != nullptr && v.db != nullptr ? *db < *v.db :
+ db == nullptr && v.db == nullptr ? false :
+ db == nullptr;
+ }
+ };
+
+ map<config_package_key, pkg_arg> package_map;
auto check_dup = [&package_map, &arg_string, &arg_parsed]
(const pkg_arg& pa) -> bool
{
assert (arg_parsed (pa));
- auto r (package_map.emplace (config_package {pa.db, pa.name}, pa));
+ auto r (package_map.emplace (config_package_key {pa.name, pa.db}, pa));
const pkg_arg& a (r.first->second);
assert (arg_parsed (a));
@@ -4463,9 +4555,7 @@ namespace bpkg
for (auto i (pkg_args.begin ()); i != pkg_args.end (); )
{
pkg_arg& pa (*i);
- database& pdb (pa.db);
-
- lazy_shared_ptr<repository_fragment> root (pdb, empty_string);
+ database* pdb (pa.db);
// Reduce all the potential variations (archive, directory, package
// name, package name/version) to a single available_package object.
@@ -4478,6 +4568,10 @@ namespace bpkg
if (!arg_parsed (pa))
{
+ assert (pdb != nullptr); // Unparsed and so can't be system.
+
+ lazy_shared_ptr<repository_fragment> root (*pdb, empty_string);
+
const char* package (pa.value.c_str ());
// Is this a package archive?
@@ -4604,7 +4698,7 @@ namespace bpkg
//
if (optional<version> v =
package_iteration (o,
- pdb,
+ *pdb,
t,
d,
m.name,
@@ -4686,6 +4780,10 @@ namespace bpkg
if (!pa.options.dependency ())
{
+ assert (pdb != nullptr);
+
+ 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 pick the
// latest one just to make sure the package is recognized.
@@ -4697,7 +4795,7 @@ namespace bpkg
assert (!arg_sys (pa));
if (pa.options.patch () &&
- (sp = pdb.find<selected_package> (pa.name)) != nullptr)
+ (sp = pdb->find<selected_package> (pa.name)) != nullptr)
{
c = patch_constraint (sp);
@@ -4756,7 +4854,12 @@ namespace bpkg
l4 ([&]{trace << "stashing recursive package "
<< arg_string (pa);});
- rec_pkgs.push_back (recursive_package {pdb, pa.name, *u, *r});
+ // The above options are meaningless for system packages, so we
+ // just ignore them for a system dependency with unspecified
+ // configuration.
+ //
+ if (pdb != nullptr)
+ rec_pkgs.push_back (recursive_package {*pdb, pa.name, *u, *r});
}
}
@@ -4782,11 +4885,14 @@ namespace bpkg
check_any_available (repo_configs, t, &dr);
}
- // Save before the name move.
- //
- sp = pdb.find<selected_package> (pa.name);
+ if (pdb != nullptr)
+ {
+ // Save before the name move.
+ //
+ sp = pdb->find<selected_package> (pa.name);
- conf_pkgs.emplace_back (pdb, pa.name);
+ pkg_confs.emplace_back (*pdb, pa.name);
+ }
dep_pkgs.push_back (
dependency_package {pdb,
@@ -4806,6 +4912,10 @@ namespace bpkg
// Add the held package to the list.
//
+ assert (pdb != nullptr);
+
+ lazy_shared_ptr<repository_fragment> root (*pdb, empty_string);
+
// Load the package that may have already been selected (if not done
// yet) and figure out what exactly we need to do here. The end goal
// is the available_package object corresponding to the actual
@@ -4813,10 +4923,10 @@ namespace bpkg
// the same as the selected package).
//
if (sp == nullptr)
- sp = pdb.find<selected_package> (pa.name);
+ sp = pdb->find<selected_package> (pa.name);
if (sp != nullptr && sp->state == package_state::broken)
- fail << "unable to build broken package " << pa.name << pdb <<
+ fail << "unable to build broken package " << pa.name << *pdb <<
info << "use 'pkg-purge --force' to remove";
bool found (true);
@@ -4919,7 +5029,7 @@ namespace bpkg
// Let's help the new user out here a bit.
//
- check_any_available (pdb, t, &dr);
+ check_any_available (*pdb, t, &dr);
}
else
{
@@ -4942,7 +5052,7 @@ namespace bpkg
{
assert (sp != nullptr && sp->system () == arg_sys (pa));
- auto rp (make_available (o, pdb, sp));
+ auto rp (make_available (o, *pdb, sp));
ap = move (rp.first);
af = move (rp.second); // Could be NULL (orphan).
}
@@ -4963,7 +5073,7 @@ namespace bpkg
//
build_package p {
build_package::build,
- pdb,
+ *pdb,
move (sp),
move (ap),
move (af),
@@ -4994,7 +5104,7 @@ namespace bpkg
p.constraints.emplace_back (
mdb, "command line", move (*pa.constraint));
- conf_pkgs.emplace_back (p.db, p.name ());
+ pkg_confs.emplace_back (p.db, p.name ());
hold_pkgs.push_back (move (p));
}
@@ -5129,7 +5239,7 @@ namespace bpkg
// Also note that we rely on "small function object" optimization here.
//
const function<find_database_function> find_prereq_database (
- [&conf_pkgs] (database& db,
+ [&pkg_confs] (database& db,
const package_name& nm,
bool buildtime) -> database*
{
@@ -5137,7 +5247,7 @@ namespace bpkg
linked_databases ddbs (db.dependency_configs (nm, buildtime));
- for (const config_package& cp: conf_pkgs)
+ for (const config_package& cp: pkg_confs)
{
if (cp.name == nm &&
find (ddbs.begin (), ddbs.end (), cp.db) != ddbs.end ())
@@ -5283,6 +5393,20 @@ namespace bpkg
transaction t (mdb);
+ // Collect all configurations where dependency packages can
+ // potentially be built or amended during this run.
+ //
+ linked_databases dep_dbs;
+
+ for (database& cdb: current_configs)
+ {
+ for (database& db: cdb.dependency_configs ())
+ {
+ if (find (dep_dbs.begin (), dep_dbs.end (), db) == dep_dbs.end ())
+ dep_dbs.push_back (db);
+ }
+ }
+
// Temporarily add the replacement prerequisites to the repointed
// dependent prerequisites sets and persist the changes.
//
@@ -5318,6 +5442,63 @@ namespace bpkg
db.update (sp);
}
+ // Pre-enter dependency to keep track of the desired versions and
+ // options specified on the command line. In particular, if the
+ // version is specified and the dependency is used as part of the
+ // plan, then the desired version must be used. We also need it to
+ // distinguish user-driven dependency up/down-grades from the
+ // dependent-driven ones, not to warn/refuse.
+ //
+ // Also, if a dependency package already has selected package that
+ // is held, then we need to unhold it.
+ //
+ auto enter = [&mdb, &pkgs] (database& db,
+ const dependency_package& p)
+ {
+ build_package bp {
+ nullopt, // Action.
+ db,
+ nullptr, // Selected package.
+ nullptr, // Available package/repo fragment.
+ nullptr,
+ false, // Hold package.
+ p.constraint.has_value (), // Hold version.
+ {}, // Constraints.
+ p.system,
+ p.keep_out,
+ false, // Configure-only.
+ p.checkout_root,
+ p.checkout_purge,
+ p.config_vars,
+ {config_package {mdb, ""}}, // Required by (command line).
+ false, // Required by dependents.
+ 0}; // State flags.
+
+ if (p.constraint)
+ bp.constraints.emplace_back (
+ mdb, "command line", *p.constraint);
+
+ pkgs.enter (p.name, move (bp));
+ };
+
+ // Add the system dependency to the database's system repository and
+ // pre-enter it to the build package map.
+ //
+ auto enter_system_dependency = [&add_system_package, &enter]
+ (database& db, const dependency_package& p)
+ {
+ // The system package may only have an exact/wildcard version
+ // specified.
+ //
+ add_system_package (db,
+ p.name,
+ (p.constraint
+ ? *p.constraint->min_version
+ : wildcard_version));
+
+ enter (db, p);
+ };
+
// Private configurations that were created during collection of the
// package builds.
//
@@ -5336,48 +5517,57 @@ namespace bpkg
//
private_configs priv_cfgs;
+ // Add a newly created private configuration to the private
+ // configurations and the dependency databases lists and pre-enter
+ // builds of system dependencies with unspecified configuration for
+ // this configuration.
+ //
+ const function<add_priv_cfg_function> add_priv_cfg (
+ [&priv_cfgs, &dep_dbs, &dep_pkgs, &enter_system_dependency]
+ (database& pdb, dir_path&& cfg)
+ {
+ database& db (pdb.find_attached (pdb.config / cfg,
+ false /* self */));
+
+ priv_cfgs.emplace_back (pdb, move (cfg));
+
+ dep_dbs.push_back (db);
+
+ for (const dependency_package& p: dep_pkgs)
+ {
+ if (p.db == nullptr)
+ enter_system_dependency (db, p);
+ }
+ });
+
build_packages::postponed_packages postponed;
if (scratch)
{
pkgs.clear ();
- // Pre-enter dependencies to keep track of the desired versions and
- // options specified on the command line. In particular, if the
- // version is specified and the dependency is used as part of the
- // plan, then the desired version must be used. We also need it to
- // distinguish user-driven dependency up/down-grades from the
- // dependent-driven ones, not to warn/refuse.
- //
- // Also, if a dependency package already has selected package that
- // is held, then we need to unhold it.
+ // Pre-enter dependencies with specified configurations.
//
for (const dependency_package& p: dep_pkgs)
{
- build_package bp {
- nullopt, // Action.
- p.db,
- nullptr, // Selected package.
- nullptr, // Available package/repository frag.
- nullptr,
- false, // Hold package.
- p.constraint.has_value (), // Hold version.
- {}, // Constraints.
- p.system,
- p.keep_out,
- false, // Configure-only.
- p.checkout_root,
- p.checkout_purge,
- p.config_vars,
- {config_package {mdb, ""}}, // Required by (command line).
- false, // Required by dependents.
- 0}; // State flags.
-
- if (p.constraint)
- bp.constraints.emplace_back (
- mdb, "command line", *p.constraint);
+ if (p.db != nullptr)
+ enter (*p.db, p);
+ }
- pkgs.enter (p.name, move (bp));
+ // Pre-enter system dependencies with unspecified configuration for
+ // all dependency configurations, excluding those which already have
+ // this dependency pre-entered.
+ //
+ for (const dependency_package& p: dep_pkgs)
+ {
+ if (p.db == nullptr)
+ {
+ for (database& db: dep_dbs)
+ {
+ if (!pkgs.entered (db, p.name))
+ enter_system_dependency (db, p);
+ }
+ }
}
// Pre-collect user selection to make sure dependency-forced
@@ -5389,7 +5579,7 @@ namespace bpkg
p,
find_prereq_database,
rpt_depts,
- priv_cfgs);
+ add_priv_cfg);
// Collect all the prerequisites of the user selection.
//
@@ -5400,7 +5590,7 @@ namespace bpkg
postponed,
find_prereq_database,
rpt_depts,
- priv_cfgs);
+ add_priv_cfg);
// Note that we need to collect unheld after prerequisites, not to
// overwrite the pre-entered entries before they are used to provide
@@ -5408,8 +5598,26 @@ namespace bpkg
//
for (const dependency_package& p: dep_pkgs)
{
- if (p.selected != nullptr && p.selected->hold_package)
- pkgs.collect_unhold (p.db, p.selected);
+ auto unhold = [&p, &pkgs] (database& db)
+ {
+ shared_ptr<selected_package> sp (
+ p.db != nullptr
+ ? p.selected
+ : db.find<selected_package> (p.name));
+
+ if (sp != nullptr && sp->hold_package)
+ pkgs.collect_unhold (db, sp);
+ };
+
+ if (p.db != nullptr)
+ {
+ unhold (*p.db);
+ }
+ else
+ {
+ for (database& db: dep_dbs)
+ unhold (db);
+ }
}
// Collect dependents whose dependencies need to be repointed to
@@ -5419,7 +5627,7 @@ namespace bpkg
rpt_depts,
postponed,
find_prereq_database,
- priv_cfgs);
+ add_priv_cfg);
scratch = false;
}
@@ -5478,7 +5686,7 @@ namespace bpkg
move (p),
find_prereq_database,
rpt_depts,
- priv_cfgs,
+ add_priv_cfg,
&postponed /* recursively */,
&dep_chain);
}
@@ -5491,7 +5699,7 @@ namespace bpkg
postponed,
find_prereq_database,
rpt_depts,
- priv_cfgs);
+ add_priv_cfg);
// Now that we have collected all the package versions that we need to
// build, arrange them in the "dependency order", that is, with every
@@ -5537,12 +5745,30 @@ namespace bpkg
//
for (const dependency_package& p: dep_pkgs)
{
- if (p.selected != nullptr && p.selected->hold_package)
- pkgs.order (p.db,
- p.name,
- nullopt /* buildtime */,
- find_prereq_database,
- false /* reorder */);
+ auto order_unheld = [&p, &pkgs, &find_prereq_database] (database& db)
+ {
+ shared_ptr<selected_package> sp (
+ p.db != nullptr
+ ? p.selected
+ : db.find<selected_package> (p.name));
+
+ if (sp != nullptr && sp->hold_package)
+ pkgs.order (db,
+ p.name,
+ nullopt /* buildtime */,
+ find_prereq_database,
+ false /* reorder */);
+ };
+
+ if (p.db != nullptr)
+ {
+ order_unheld (*p.db);
+ }
+ else
+ {
+ for (database& db: dep_dbs)
+ order_unheld (db);
+ }
}
// Now, as we are done with package builds collecting/ordering, erase
@@ -5750,7 +5976,7 @@ namespace bpkg
// make sure that the unsatisfiable dependency, if left, is
// reported.
//
- auto need_refinement = [&eval_dep, &deps, &rec_pkgs, &o] (
+ auto need_refinement = [&eval_dep, &deps, &rec_pkgs, &dep_dbs, &o] (
bool diag = false) -> bool
{
// Examine the new dependency set for any up/down-grade/drops.
@@ -5769,18 +5995,7 @@ namespace bpkg
// up/down-grading or dropping packages in configurations that
// only contain dependents, some of which we may only reconfigure.
//
- linked_databases dbs;
-
- for (database& cdb: current_configs)
- {
- for (database& db: cdb.dependency_configs ())
- {
- if (find (dbs.begin (), dbs.end (), db) == dbs.end ())
- dbs.push_back (db);
- }
- }
-
- for (database& db: dbs)
+ for (database& db: dep_dbs)
{
for (shared_ptr<selected_package> sp:
pointer_result (db.query<selected_package> (q)))
diff --git a/tests/pkg-build.testscript b/tests/pkg-build.testscript
index b413a14..fac4b1c 100644
--- a/tests/pkg-build.testscript
+++ b/tests/pkg-build.testscript
@@ -5360,7 +5360,8 @@ else
libbuild2-bar [h1/.bpkg/build2/] configured 1.0.0
EOO
- $* libbar '?sys:foo/1.2.0' ?libbaz +{ --config-name h2 } <<EOI 2>>~%EOE%;
+ $* libbar '?sys:foo/1.2.0' +{ --config-name h1 } \
+ ?libbaz +{ --config-name h2 } <<EOI 2>>~%EOE%;
y
EOI
% new libbaz/1.0.0 \[h2.\]%
@@ -6337,5 +6338,397 @@ else
sed -n -e 's/^config.libbaz = (.+)$/\1/p' \
cfg2/libbaz-1.0.0/build/config.build >'false'
}
+
+ : system-dependency
+ :
+ {
+ $cfg_create -d cfg --uuid $cfg_uuid &cfg/***;
+ $cfg_create -d cfg2 --uuid $cfg2_uuid &cfg2/***;
+ $cfg_create -d cfg3 --uuid $cfg3_uuid --type host &cfg3/***;
+
+ $cfg_link -d cfg cfg3;
+ $cfg_link -d cfg2 cfg3;
+
+ $rep_add $rep/t7a && $rep_fetch;
+ $rep_add -d cfg2 $rep/t7a && $rep_fetch -d cfg2;
+
+ test.arguments += -d cfg2; # Now refers 2 current dirs: cfg/ and cfg2/.
+
+ $* libbox +{ --config-uuid $cfg_uuid --config-uuid $cfg2_uuid } \
+ '?sys:foo' <<EOI 2>>~%EOE%;
+ y
+ EOI
+ % new libbaz/1.0.0 \[cfg.\] \(required by libbox \[cfg.\]\)%
+ % configure sys:foo/\* \[cfg3.\]%
+ % new libbox/1.0.0 \[cfg.\]%
+ % new libbaz/1.0.0 \[cfg2.\] \(required by libbox \[cfg2.\]\)%
+ % new libbox/1.0.0 \[cfg2.\]%
+ %continue\? \[Y/n\] fetched libbaz/1.0.0 \[cfg.\]%
+ %unpacked libbaz/1.0.0 \[cfg.\]%
+ %fetched libbox/1.0.0 \[cfg.\]%
+ %unpacked libbox/1.0.0 \[cfg.\]%
+ %fetched libbaz/1.0.0 \[cfg2.\]%
+ %unpacked libbaz/1.0.0 \[cfg2.\]%
+ %fetched libbox/1.0.0 \[cfg2.\]%
+ %unpacked libbox/1.0.0 \[cfg2.\]%
+ %configured libbaz/1.0.0 \[cfg.\]%
+ %configured sys:foo/\* \[cfg3.\]%
+ %configured libbox/1.0.0 \[cfg.\]%
+ %configured libbaz/1.0.0 \[cfg2.\]%
+ %configured libbox/1.0.0 \[cfg2.\]%
+ %info: cfg.+libbox-1.0.0.+ is up to date%
+ %info: cfg2.+libbox-1.0.0.+ is up to date%
+ %updated libbox/1.0.0 \[cfg.\]%
+ %updated libbox/1.0.0 \[cfg2.\]%
+ EOE
+
+ $pkg_status -d cfg -r >>/EOO;
+ !libbox configured 1.0.0
+ foo [cfg3/] configured,system !* available 1.0.0
+ libbaz configured 1.0.0
+ EOO
+
+ $pkg_status -d cfg2 -r >>/EOO
+ !libbox configured 1.0.0
+ foo [cfg3/] configured,system !* available 1.0.0
+ libbaz configured 1.0.0
+ EOO
+ }
+ }
+
+ : system
+ :
+ {
+ +$clone_root_cfg && $rep_add $rep/t7a && $rep_fetch
+
+ : no-config
+ :
+ {
+ +$clone_cfg
+
+ : linked
+ :
+ {
+ $clone_cfg;
+
+ $cfg_create -d cfg2 --type host --name cfg2 &cfg2/***;
+ $cfg_link -d cfg cfg2;
+
+ $* libbox '?sys:foo' <<EOI 2>>~%EOE%;
+ y
+ EOI
+ % configure sys:foo/\* \[cfg2.\]%
+ new libbaz/1.0.0 (required by libbox)
+ new libbox/1.0.0
+ continue? [Y/n] fetched libbaz/1.0.0
+ unpacked libbaz/1.0.0
+ fetched libbox/1.0.0
+ unpacked libbox/1.0.0
+ %configured sys:foo/\* \[cfg2.\]%
+ configured libbaz/1.0.0
+ configured libbox/1.0.0
+ %info: cfg.+libbox-1.0.0.+ is up to date%
+ updated libbox/1.0.0
+ EOE
+
+ $pkg_status -r >>/EOO;
+ !libbox configured 1.0.0
+ foo [cfg2/] configured,system !* available 1.0.0
+ libbaz configured 1.0.0
+ EOO
+
+ $pkg_drop libbox
+ }
+
+ : private-host
+ :
+ {
+ $clone_cfg;
+
+ $* libbox '?sys:foo' &cfg/.bpkg/host/*** <<EOI 2>>~%EOE%;
+ y
+ EOI
+ % configure sys:foo/\* \[cfg..bpkg.host.\]%
+ new libbaz/1.0.0 (required by libbox)
+ new libbox/1.0.0
+ continue? [Y/n] fetched libbaz/1.0.0
+ unpacked libbaz/1.0.0
+ fetched libbox/1.0.0
+ unpacked libbox/1.0.0
+ %configured sys:foo/\* \[cfg..bpkg.host.\]%
+ configured libbaz/1.0.0
+ configured libbox/1.0.0
+ %info: cfg.+libbox-1.0.0.+ is up to date%
+ updated libbox/1.0.0
+ EOE
+
+ $pkg_status -r >>/EOO;
+ !libbox configured 1.0.0
+ foo [cfg/.bpkg/host/] configured,system !* available 1.0.0
+ libbaz configured 1.0.0
+ EOO
+
+ $pkg_drop libbox
+ }
+
+ : private-module
+ :
+ {
+ $clone_cfg;
+
+ $* libbox '?sys:libbuild2-bar' &cfg/.bpkg/host/*** &cfg/.bpkg/build2/*** <<EOI 2>>~%EOE%;
+ y
+ EOI
+ % configure sys:libbuild2-bar/\* \[cfg..bpkg.build2.\]%
+ % new libbaz/1.0.0 \[cfg..bpkg.host.\] \(required by foo \[cfg..bpkg.host.\]\)%
+ % new foo/1.0.0 \[cfg..bpkg.host.\] \(required by libbox\)%
+ new libbaz/1.0.0 (required by libbox)
+ new libbox/1.0.0
+ %continue\? \[Y/n\] fetched libbaz/1.0.0 \[cfg..bpkg.host.\]%
+ %unpacked libbaz/1.0.0 \[cfg..bpkg.host.\]%
+ %fetched foo/1.0.0 \[cfg..bpkg.host.\]%
+ %unpacked foo/1.0.0 \[cfg..bpkg.host.\]%
+ fetched libbaz/1.0.0
+ unpacked libbaz/1.0.0
+ fetched libbox/1.0.0
+ unpacked libbox/1.0.0
+ %configured sys:libbuild2-bar/\* \[cfg..bpkg.build2.\]%
+ %configured libbaz/1.0.0 \[cfg..bpkg.host.\]%
+ %configured foo/1.0.0 \[cfg..bpkg.host.\]%
+ configured libbaz/1.0.0
+ configured libbox/1.0.0
+ %info: cfg.+libbox-1.0.0.+ is up to date%
+ updated libbox/1.0.0
+ EOE
+
+ $pkg_status -r >>/EOO;
+ !libbox configured 1.0.0
+ foo [cfg/.bpkg/host/] configured 1.0.0
+ libbaz [cfg/.bpkg/host/] configured 1.0.0
+ libbuild2-bar [cfg/.bpkg/build2/] configured,system !* available 1.0.0
+ libbaz configured 1.0.0
+ EOO
+
+ $pkg_drop libbox
+ }
+ }
+
+ : config
+ :
+ {
+ +$clone_cfg
+
+ : linked
+ :
+ {
+ $clone_cfg;
+
+ $cfg_create -d cfg2 --type host --name cfg2 &cfg2/***;
+ $cfg_link -d cfg cfg2;
+
+ $cfg_create -d cfg3 --type host --name cfg3 &cfg3/***;
+ $cfg_link -d cfg cfg3;
+
+ $* libbox '?sys:foo' +{ --config-name cfg3 } <<EOI 2>>~%EOE%;
+ y
+ EOI
+ % configure sys:foo/\* \[cfg3.\]%
+ new libbaz/1.0.0 (required by libbox)
+ new libbox/1.0.0
+ continue? [Y/n] fetched libbaz/1.0.0
+ unpacked libbaz/1.0.0
+ fetched libbox/1.0.0
+ unpacked libbox/1.0.0
+ %configured sys:foo/\* \[cfg3.\]%
+ configured libbaz/1.0.0
+ configured libbox/1.0.0
+ %info: cfg.+libbox-1.0.0.+ is up to date%
+ updated libbox/1.0.0
+ EOE
+
+ $pkg_status -r >>/EOO;
+ !libbox configured 1.0.0
+ foo [cfg3/] configured,system !* available 1.0.0
+ libbaz configured 1.0.0
+ EOO
+
+ $pkg_drop libbox
+ }
+
+ : src-sys
+ :
+ {
+ $clone_cfg;
+
+ $cfg_create -d cfg2 --type host --name cfg2 &cfg2/***;
+ $cfg_link -d cfg cfg2;
+
+ $* libbox --yes 2>!;
+
+ $pkg_status -r >>/EOO;
+ !libbox configured 1.0.0
+ foo [cfg2/] configured 1.0.0
+ libbaz [cfg2/] configured 1.0.0
+ libbuild2-bar [cfg2/.bpkg/build2/] configured 1.0.0
+ libbaz configured 1.0.0
+ EOO
+
+ $* '?sys:foo' <<EOI 2>>~%EOE%;
+ y
+ y
+ EOI
+ % drop libbuild2-bar/1.0.0 \[cfg2..bpkg.build2.\] \(unused\)%
+ % drop libbaz/1.0.0 \[cfg2.\] \(unused\)%
+ % reconfigure sys:foo/\* \[cfg2.\]%
+ % reconfigure libbox \(dependent of foo \[cfg2.\]\)%
+ continue? [Y/n] update dependent packages? [Y/n] disfigured libbox/1.0.0
+ %disfigured foo/1.0.0 \[cfg2.\]%
+ %disfigured libbaz/1.0.0 \[cfg2.\]%
+ %disfigured libbuild2-bar/1.0.0 \[cfg2..bpkg.build2.\]%
+ %purged libbuild2-bar/1.0.0 \[cfg2..bpkg.build2.\]%
+ %purged libbaz/1.0.0 \[cfg2.\]%
+ %purged foo/1.0.0 \[cfg2.\]%
+ %configured sys:foo/\* \[cfg2.\]%
+ configured libbox/1.0.0
+ %info: cfg.+libbox-1.0.0.+ is up to date%
+ updated libbox/1.0.0
+ EOE
+
+ $pkg_status -r >>/EOO;
+ !libbox configured 1.0.0
+ foo [cfg2/] configured,system !* available 1.0.0
+ libbaz configured 1.0.0
+ EOO
+
+ $pkg_drop libbox
+ }
+
+ : src-sys-upgrade
+ :
+ {
+ $clone_cfg;
+
+ $cfg_create -d cfg2 --type host --name cfg2 &cfg2/***;
+ $cfg_link -d cfg cfg2;
+
+ $* libbox --yes 2>!;
+
+ $pkg_status -r >>/EOO;
+ !libbox configured 1.0.0
+ foo [cfg2/] configured 1.0.0
+ libbaz [cfg2/] configured 1.0.0
+ libbuild2-bar [cfg2/.bpkg/build2/] configured 1.0.0
+ libbaz configured 1.0.0
+ EOO
+
+ $* '?sys:foo/1.1.0' <<EOI 2>>~%EOE%;
+ y
+ y
+ EOI
+ % drop libbuild2-bar/1.0.0 \[cfg2..bpkg.build2.\] \(unused\)%
+ % drop libbaz/1.0.0 \[cfg2.\] \(unused\)%
+ % reconfigure sys:foo/1.1.0 \[cfg2.\]%
+ % reconfigure libbox \(dependent of foo \[cfg2.\]\)%
+ continue? [Y/n] update dependent packages? [Y/n] disfigured libbox/1.0.0
+ %disfigured foo/1.0.0 \[cfg2.\]%
+ %disfigured libbaz/1.0.0 \[cfg2.\]%
+ %disfigured libbuild2-bar/1.0.0 \[cfg2..bpkg.build2.\]%
+ %purged libbuild2-bar/1.0.0 \[cfg2..bpkg.build2.\]%
+ %purged libbaz/1.0.0 \[cfg2.\]%
+ %purged foo/1.0.0 \[cfg2.\]%
+ %configured sys:foo/1.1.0 \[cfg2.\]%
+ configured libbox/1.0.0
+ %info: cfg.+libbox-1.0.0.+ is up to date%
+ updated libbox/1.0.0
+ EOE
+
+ $pkg_status -r >>/EOO;
+ !libbox configured 1.0.0
+ foo [cfg2/] configured,system !1.1.0
+ libbaz configured 1.0.0
+ EOO
+
+ $pkg_drop libbox
+ }
+
+ : src-sys-unhold
+ :
+ {
+ $clone_cfg;
+
+ $cfg_create -d cfg2 --type host --name cfg2 &cfg2/***;
+ $cfg_link -d cfg cfg2;
+
+ $rep_add -d cfg2 $rep/t7a && $rep_fetch -d cfg2;
+
+ $* libbox foo +{ --config-name cfg2 } --yes 2>!;
+
+ $pkg_status -r >>/EOO;
+ !libbox configured 1.0.0
+ !foo [cfg2/] configured 1.0.0
+ libbaz [cfg2/] configured 1.0.0
+ libbuild2-bar [cfg2/.bpkg/build2/] configured 1.0.0
+ libbaz configured 1.0.0
+ EOO
+
+ $* '?sys:foo' <<EOI 2>>~%EOE%;
+ y
+ y
+ EOI
+ % drop libbuild2-bar/1.0.0 \[cfg2..bpkg.build2.\] \(unused\)%
+ % drop libbaz/1.0.0 \[cfg2.\] \(unused\)%
+ % reconfigure/unhold sys:foo/\* \[cfg2.\]%
+ % reconfigure libbox \(dependent of foo \[cfg2.\]\)%
+ continue? [Y/n] update dependent packages? [Y/n] disfigured libbox/1.0.0
+ %disfigured foo/1.0.0 \[cfg2.\]%
+ %disfigured libbaz/1.0.0 \[cfg2.\]%
+ %disfigured libbuild2-bar/1.0.0 \[cfg2..bpkg.build2.\]%
+ %purged libbuild2-bar/1.0.0 \[cfg2..bpkg.build2.\]%
+ %purged libbaz/1.0.0 \[cfg2.\]%
+ %purged foo/1.0.0 \[cfg2.\]%
+ %configured sys:foo/\* \[cfg2.\]%
+ configured libbox/1.0.0
+ %info: cfg.+libbox-1.0.0.+ is up to date%
+ updated libbox/1.0.0
+ EOE
+
+ $pkg_status -r >>/EOO;
+ !libbox configured 1.0.0
+ foo [cfg2/] configured,system !* available 1.0.0
+ libbaz configured 1.0.0
+ EOO
+
+ $pkg_drop libbox
+ }
+
+ : sys-unhold
+ :
+ {
+ $clone_cfg;
+
+ $cfg_create -d cfg2 --type host --name cfg2 &cfg2/***;
+ $cfg_link -d cfg cfg2;
+
+ $rep_add -d cfg2 $rep/t7a && $rep_fetch -d cfg2;
+
+ $* libbox 'sys:foo' +{ --config-name cfg2 } --yes 2>!;
+
+ $pkg_status -r >>/EOO;
+ !libbox configured 1.0.0
+ !foo [cfg2/] configured,system !* available 1.0.0
+ libbaz configured 1.0.0
+ EOO
+
+ $* '?sys:foo';
+
+ $pkg_status -r >>/EOO;
+ !libbox configured 1.0.0
+ foo [cfg2/] configured,system !* available 1.0.0
+ libbaz configured 1.0.0
+ EOO
+
+ $pkg_drop libbox
+ }
+ }
}
}
diff --git a/tests/pkg-system.testscript b/tests/pkg-system.testscript
index 3ff1b55..e300afd 100644
--- a/tests/pkg-system.testscript
+++ b/tests/pkg-system.testscript
@@ -90,28 +90,28 @@ rep_remove += -d cfg 2>!
{
$clone_cfg;
- $pkg_build 'sys:libbar' '?sys:libbar' 2>>EOE != 0;
+ $pkg_build 'sys:libbar' 'sys:libbar/1.0.0' 2>>EOE != 0;
error: duplicate package libbar
info: first mentioned as sys:libbar
- info: second mentioned as ?sys:libbar
+ info: second mentioned as sys:libbar/1.0.0
EOE
- $pkg_build '?sys:libbar' 'sys:libbar' 2>>EOE != 0;
+ $pkg_build '?sys:libbar' '?sys:libbar/1.0.0' 2>>EOE != 0;
error: duplicate package libbar
info: first mentioned as ?sys:libbar
- info: second mentioned as sys:libbar
+ info: second mentioned as ?sys:libbar/1.0.0
EOE
- $pkg_build '?sys:libbar' libbar 2>>EOE != 0;
+ $pkg_build 'sys:libbar' libbar 2>>EOE != 0;
error: duplicate package libbar
- info: first mentioned as ?sys:libbar
+ info: first mentioned as sys:libbar
info: second mentioned as libbar
EOE
- $pkg_build libbar '?sys:libbar' 2>>EOE != 0;
+ $pkg_build ?libbar '?sys:libbar' +{ --config-id 0 } 2>>EOE != 0;
error: duplicate package libbar
- info: first mentioned as libbar
- info: second mentioned as ?sys:libbar
+ info: first mentioned as ?libbar
+ info: second mentioned as ?sys:libbar +{ --config-id 0 }
EOE
$pkg_build 'sys:libbar' libbar 2>>EOE != 0;