From 6c65d006e9951e3cedf9b05341697842fcadafd1 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 16 May 2022 21:31:16 +0300 Subject: Add dependency alternatives positions to selected package prerequisites --- bpkg/package.hxx | 67 +++++++++++++++++++++++++++++++++++++--- bpkg/package.xml | 7 +++++ bpkg/pkg-build.cxx | 54 +++++++++++++++++++++++++------- bpkg/pkg-configure.cxx | 83 ++++++++++++++++++++++++++++++++++++++------------ bpkg/pkg-configure.hxx | 14 +++++++-- bpkg/pkg-status.cxx | 2 +- 6 files changed, 188 insertions(+), 39 deletions(-) diff --git a/bpkg/package.hxx b/bpkg/package.hxx index b473917..9a7c572 100644 --- a/bpkg/package.hxx +++ b/bpkg/package.hxx @@ -27,7 +27,7 @@ // #define DB_SCHEMA_VERSION_BASE 7 -#pragma db model version(DB_SCHEMA_VERSION_BASE, 18, closed) +#pragma db model version(DB_SCHEMA_VERSION_BASE, 19, closed) namespace bpkg { @@ -1032,21 +1032,63 @@ namespace bpkg } // A map of "effective" prerequisites (i.e., pointers to other selected - // packages) to optional version constraint. Note that because it is a - // single constraint, we don't support multiple dependencies on the same - // package (e.g., two ranges of versions). See pkg_configure(). + // packages) to optional version constraint (plus some other info). Note + // that because it is a single constraint, we don't support multiple + // dependencies on the same package (e.g., two ranges of versions). See + // pkg_configure(). // // Note also that the pointer can refer to a selected package in another // database. // class selected_package; + #pragma db value + struct prerequisite_info + { + // The "tightest" version constraint among all dependencies resolved to + // this prerequisite. + // + optional constraint; + + // Position of the first dependency alternative with a configuration + // clause, if any. + // + // Specifically, if there is such an alternative then this is a pair of + // 1-based indexes of the respective depends value (first) and the + // dependency alternative (second) in the dependent's manifest. Otherwise, + // this is a pair of zeros. + // + // For example, for the following dependent the position for libfoo/1.2.0 + // prerequisite will be {2,2}: + // + // libbar: depends: libfoo >= 1.1.0 + // depends: libfox | libfoo >= 1.2.0 {require {...}} + // + pair config_position; + + // Database mapping. + // + #pragma db member(constraint) column("") + + #pragma db member(config_position) transient + + #pragma db member(config_dependency_index) \ + virtual(size_t) \ + access(config_position.first) \ + default(0) + + #pragma db member(config_alternative_index) \ + virtual(size_t) \ + access(config_position.second) \ + default(0) + }; + // Note that the keys for this map need to be created with the database // passed to their constructor, which is required for persisting them (see // _selected_package_ref() implementation for details). // using package_prerequisites = std::map, - optional, + prerequisite_info, compare_lazy_ptr>; // Database mapping for lazy_shared_ptr to configuration @@ -1486,6 +1528,21 @@ namespace bpkg #pragma db column("pp.package") package_name name; + #pragma db transient + pair config_position; + + #pragma db member(config_dependency_index) \ + column("pp.config_dependency_index") \ + virtual(size_t) \ + access(config_position.first) \ + default(0) + + #pragma db member(config_alternative_index) \ + column("pp.config_alternative_index") \ + virtual(size_t) \ + access(config_position.second) \ + default(0) + #pragma db column("pp.") optional constraint; }; diff --git a/bpkg/package.xml b/bpkg/package.xml index 904d0f6..8959529 100644 --- a/bpkg/package.xml +++ b/bpkg/package.xml @@ -1,4 +1,11 @@ + + + + + + + diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx index 8d125ce..aa3b58e 100644 --- a/bpkg/pkg-build.cxx +++ b/bpkg/pkg-build.cxx @@ -613,6 +613,11 @@ namespace bpkg // optional dependencies; + // Indexes of the selected dependency alternatives stored in the above + // dependencies member. + // + optional> alternatives; + // If we end up collecting the prerequisite builds for this package, then // this member stores the skeleton of the package being built. // @@ -2713,9 +2718,10 @@ namespace bpkg // const dependencies& deps (ap->dependencies); - // Must both be either present or not. + // Must all be either present or not. // - assert (pkg.dependencies.has_value () == pkg.skeleton.has_value ()); + assert (pkg.dependencies.has_value () == pkg.skeleton.has_value () && + pkg.dependencies.has_value () == pkg.alternatives.has_value ()); // Note that the selected alternatives list can be filled partially (see // build_package::dependencies for details). In this case we continue @@ -2726,9 +2732,13 @@ namespace bpkg l5 ([&]{trace << "begin " << pkg.available_name_version_db ();}); pkg.dependencies = dependencies (); + pkg.alternatives = vector (); if (size_t n = deps.size ()) + { pkg.dependencies->reserve (n); + pkg.alternatives->reserve (n); + } optional src_root (pkg.external_dir ()); @@ -2746,7 +2756,10 @@ namespace bpkg else l5 ([&]{trace << "resume " << pkg.available_name_version_db ();}); - dependencies& sdeps (*pkg.dependencies); + dependencies& sdeps (*pkg.dependencies); + vector& salts (*pkg.alternatives); + + assert (sdeps.size () == salts.size ()); // Must be parallel. // Check if there is nothing to collect anymore. // @@ -2796,6 +2809,7 @@ namespace bpkg if (toolchain_buildtime_dependency (options, das, &nm)) { sdeps.push_back (move (sdas)); + salts.push_back (0); // Keep parallel to sdeps. continue; } @@ -2824,6 +2838,7 @@ namespace bpkg if (edas.empty ()) { sdeps.push_back (move (sdas)); + salts.push_back (0); // Keep parallel to sdeps. continue; } @@ -3626,6 +3641,7 @@ namespace bpkg b.available, move (b.repository_fragment), nullopt, // Dependencies. + nullopt, // Dependencies alternatives. nullopt, // Package skeleton. nullopt, // Postponed dependency alternatives. false, // Recursive collection. @@ -4079,8 +4095,8 @@ namespace bpkg // present. // bool selected (false); - auto select = [&sdeps, &sdas, &skel, di, &selected] - (const dependency_alternative& da) + auto select = [&sdeps, &salts, &sdas, &skel, di, &selected] + (const dependency_alternative& da, size_t dai) { assert (sdas.empty ()); @@ -4094,6 +4110,7 @@ namespace bpkg da /* dependencies */); sdeps.push_back (move (sdas)); + salts.push_back (dai); if (da.reflect) skel.evaluate_reflect (*da.reflect, di); @@ -4234,7 +4251,7 @@ namespace bpkg return true; } - select (da); + select (da, dai); // Make sure no more true alternatives are selected during // this function call. @@ -4288,7 +4305,7 @@ namespace bpkg break; } - select (da); + select (da, dai); } // If an alternative is selected, then we are done. @@ -4450,6 +4467,7 @@ namespace bpkg move (rp.first), move (rp.second), nullopt, // Dependencies. + nullopt, // Dependencies alternatives. nullopt, // Package skeleton. nullopt, // Postponed dependency alternatives. false, // Recursive collection. @@ -4537,6 +4555,7 @@ namespace bpkg nullptr, nullptr, nullopt, // Dependencies. + nullopt, // Dependencies alternatives. nullopt, // Package skeleton. nullopt, // Postponed dependency alternatives. false, // Recursive collection. @@ -4627,6 +4646,7 @@ namespace bpkg nullptr, nullptr, nullopt, // Dependencies. + nullopt, // Dependencies alternatives. nullopt, // Package skeleton. nullopt, // Postponed dependency alternatives. false, // Recursive collection. @@ -4882,6 +4902,7 @@ namespace bpkg move (di.available), move (di.repository_fragment), nullopt, // Dependencies. + nullopt, // Dependencies alternatives. nullopt, // Package skeleton. nullopt, // Postponed dependency alternatives. false, // Recursive collection. @@ -4952,6 +4973,7 @@ namespace bpkg b->recursive_collection = true; b->dependencies = dependencies (); + b->alternatives = vector (); optional src_root (b->external_dir ()); @@ -5083,6 +5105,7 @@ namespace bpkg { const bpkg::dependencies& deps (b->available->dependencies); bpkg::dependencies& sdeps (*b->dependencies); + vector& salts (*b->alternatives); size_t di (sdeps.size ()); @@ -5121,6 +5144,7 @@ namespace bpkg assert (j != pdas.end ()); const dependency_alternative& da (j->first); + size_t dai (j->second); // Select the dependency alternative and position to the next // depends value. @@ -5136,6 +5160,7 @@ namespace bpkg da /* dependencies */); sdeps.push_back (move (sdas)); + salts.push_back (dai); // Evaluate reflect, if present. // @@ -5771,6 +5796,7 @@ namespace bpkg nullptr, // No available pkg/repo fragment. nullptr, nullopt, // Dependencies. + nullopt, // Dependencies alternatives. nullopt, // Package skeleton. nullopt, // Postponed dependency alternatives. false, // Recursive collection. @@ -8914,6 +8940,7 @@ namespace bpkg move (ap), move (af), nullopt, // Dependencies. + nullopt, // Dependencies alternatives. nullopt, // Package skeleton. nullopt, // Postponed dependency alternatives. false, // Recursive collection. @@ -9026,6 +9053,7 @@ namespace bpkg move (ap), move (apr.second), nullopt, // Dependencies. + nullopt, // Dependencies alternatives. nullopt, // Package skeleton. nullopt, // Postponed dependency alternatives. false, // Recursive collection. @@ -9271,9 +9299,8 @@ namespace bpkg // Temporarily add the replacement prerequisites to the repointed // dependent prerequisites sets and persist the changes. // - // Note that we don't copy the prerequisite constraints into the - // replacements, since they are unused in the collecting/ordering - // logic. + // Note that we don't copy the prerequisite information into the + // replacements, since it is unused in the collecting/ordering logic. // for (auto& rd: rpt_depts) { @@ -9291,7 +9318,7 @@ namespace bpkg auto i (sp->prerequisites.emplace ( lazy_shared_ptr (cp.db.get (), cp.name), - nullopt)); + prerequisite_info {nullopt, make_pair (0, 0)})); // The selected package should only contain the old // prerequisites at this time, so adding a replacement should @@ -9358,6 +9385,7 @@ namespace bpkg nullptr, // Available package/repo fragment. nullptr, nullopt, // Dependencies. + nullopt, // Dependencies alternatives. nullopt, // Package skeleton. nullopt, // Postponed dependency alternatives. false, // Recursive collection. @@ -9636,6 +9664,7 @@ namespace bpkg d.available, d.repository_fragment, nullopt, // Dependencies. + nullopt, // Dependencies alternatives. nullopt, // Package skeleton. nullopt, // Postponed dependency alternatives. false, // Recursive collection. @@ -11307,6 +11336,7 @@ namespace bpkg t, sp, *p.dependencies, + &*p.alternatives, move (*p.skeleton), nullptr /* previous_prerequisites */, simulate, @@ -11328,6 +11358,7 @@ namespace bpkg t, sp, ap->dependencies, + nullptr /* alternatives */, package_skeleton (o, pdb, *ap, @@ -11369,6 +11400,7 @@ namespace bpkg t, sp, dap->dependencies, + nullptr /* alternatives */, package_skeleton (o, pdb, *dap, diff --git a/bpkg/pkg-configure.cxx b/bpkg/pkg-configure.cxx index 65cbf05..ee123d5 100644 --- a/bpkg/pkg-configure.cxx +++ b/bpkg/pkg-configure.cxx @@ -48,6 +48,7 @@ namespace bpkg database& db, transaction&, const dependencies& deps, + const vector* alts, package_skeleton&& ps, const vector* prev_prereqs, bool simulate, @@ -57,6 +58,11 @@ namespace bpkg strings vars; vector srcs; + // Alternatives argument must be parallel to the dependencies argument if + // specified. + // + assert (alts == nullptr || alts->size () == deps.size ()); + for (size_t di (0); di != deps.size (); ++di) { // Skip the toolchain build-time dependencies and dependencies without @@ -64,19 +70,38 @@ namespace bpkg // const dependency_alternatives_ex& das (deps[di]); - if (das.empty () || toolchain_buildtime_dependency (o, das, &ps.name ())) + if (das.empty ()) continue; - small_vector, 2> edas; + small_vector, + size_t>, + 2> edas; - for (const dependency_alternative& da: das) + if (alts == nullptr) { - if (!da.enable || ps.evaluate_enable (*da.enable, di)) - edas.push_back (da); + if (toolchain_buildtime_dependency (o, das, &ps.name ())) + continue; + + for (size_t i (0); i != das.size (); ++i) + { + const dependency_alternative& da (das[i]); + + if (!da.enable || ps.evaluate_enable (*da.enable, di)) + edas.push_back (make_pair (ref (da), i)); + } + + if (edas.empty ()) + continue; + } + else + { + // Must only contain the selected alternative. + // + assert (das.size () == 1); + + edas.push_back (make_pair (ref (das.front ()), (*alts)[di])); } - if (edas.empty ()) - continue; // Pick the first alternative with dependencies that can all be resolved // to the configured packages, satisfying the respective constraints. @@ -91,16 +116,19 @@ namespace bpkg for (const vector* pps (prev_prereqs);;) { bool satisfied (false); - for (const dependency_alternative& da: edas) + for (const auto& eda: edas) { + const dependency_alternative& da (eda.first); + size_t dai (eda.second); + // 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, - const optional&>, 1> prerequisites; + pair, prerequisite_info>, + 1> prerequisites; dependency_alternative::const_iterator b (da.begin ()); dependency_alternative::const_iterator i (b); @@ -132,9 +160,13 @@ namespace bpkg // See the package_prerequisites definition for details on // creating the map keys with the database passed. // + bool conf (da.prefer || da.require); + prerequisites.emplace_back ( lazy_shared_ptr (*spd.second, dp), - d.constraint); + prerequisite_info {d.constraint, + make_pair (conf ? di + 1 : 0, + conf ? dai + 1 : 0)}); } // Try the next alternative if there are unresolved dependencies for @@ -150,9 +182,9 @@ namespace bpkg for (auto& pr: prerequisites) { const package_name& pn (pr.first.object_id ()); - const optional& pc (pr.second); + const prerequisite_info& pi (pr.second); - auto p (prereqs.emplace (pr.first, pc)); + auto p (prereqs.emplace (pr.first, pi)); // Currently we can only capture a single constraint, so if we // already have a dependency on this package and one constraint is @@ -160,18 +192,28 @@ namespace bpkg // if (!p.second) { - auto& c (p.first->second); + auto& c1 (p.first->second.constraint); + auto& c2 (pi.constraint); - bool s1 (satisfies (c, pc)); - bool s2 (satisfies (pc, c)); + bool s1 (satisfies (c1, c2)); + bool s2 (satisfies (c2, c1)); if (!s1 && !s2) fail << "multiple dependencies on package " << pn << - info << pn << " " << *c << - info << pn << " " << *pc; + info << pn << " " << *c1 << + info << pn << " " << *c2; if (s2 && !s1) - c = pc; + c1 = c2; + + // Keep position of the first dependency alternative with a + // configuration clause. + // + pair& p1 (p.first->second.config_position); + pair p2 (pi.config_position); + + if (p1.first == 0 && p2.first != 0) + p1 = p2; } // If the prerequisite is configured in the linked configuration, @@ -287,6 +329,7 @@ namespace bpkg transaction& t, const shared_ptr& p, const dependencies& deps, + const vector* alts, package_skeleton&& ps, const vector* pps, bool simulate, @@ -322,6 +365,7 @@ namespace bpkg db, t, deps, + alts, move (ps), pps, simulate, @@ -559,6 +603,7 @@ namespace bpkg t, p, ap->dependencies, + nullptr /* alternatives */, package_skeleton (o, db, *ap, diff --git a/bpkg/pkg-configure.hxx b/bpkg/pkg-configure.hxx index 23dab83..cbfad7a 100644 --- a/bpkg/pkg-configure.hxx +++ b/bpkg/pkg-configure.hxx @@ -33,9 +33,16 @@ namespace bpkg // Configure the package, update its state, and commit the transaction. // - // The package dependency constraints are expected to be complete. Empty - // dependency alternatives lists are allowed and are ignored (see pkg-build - // for the use-case). + // The package dependency constraints are expected to be complete. + // + // The dependencies argument may contain pre-selected dependency + // alternatives (with the potential empty entries for the toolchain + // build-time dependencies or for dependencies with all the alternatives + // disabled; see pkg-build for the use-case). In this case the number of + // dependency alternatives for each dependency must be 1 (or 0) and the + // alternatives argument must be specified. The alternatives argument must + // be parallel to the dependencies argument and specify indexes of the + // selected alternatives. // // If prerequisites corresponding to the previous configured state of the // package are specified, then for each depends value try to select an @@ -49,6 +56,7 @@ namespace bpkg transaction&, const shared_ptr&, const dependencies&, + const vector* alternatives, package_skeleton&&, const vector* prerequisites, bool simulate, diff --git a/bpkg/pkg-status.cxx b/bpkg/pkg-status.cxx index 45ea519..09a9424 100644 --- a/bpkg/pkg-status.cxx +++ b/bpkg/pkg-status.cxx @@ -144,7 +144,7 @@ namespace bpkg { shared_ptr d (pair.first.load ()); database& db (pair.first.database ()); - const optional& c (pair.second); + const optional& c (pair.second.constraint); r.push_back (package {db, rdb, d->name, version (), move (d), c}); } return r; -- cgit v1.1