From a5418d62f79f7d59f6b0a7efc88dcd7e99c8248a Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 27 Oct 2023 07:22:00 +0200 Subject: WIP: install: file_rule::reapply_impl() --- libbuild2/cc/install-rule.cxx | 34 +++++++++----- libbuild2/install/rule.cxx | 107 +++++++++++++++++++++++++++++++++++++----- libbuild2/install/rule.hxx | 14 +++++- 3 files changed, 130 insertions(+), 25 deletions(-) diff --git a/libbuild2/cc/install-rule.cxx b/libbuild2/cc/install-rule.cxx index fff3bdf..f6e6b71 100644 --- a/libbuild2/cc/install-rule.cxx +++ b/libbuild2/cc/install-rule.cxx @@ -259,7 +259,9 @@ namespace build2 } } - recipe r (file_rule::apply_impl (a, t, me)); + recipe r (file_rule::apply_impl ( + a, t, me, + me.cur_options != match_extra::all_options /* reapply */)); if (r == nullptr) { @@ -326,26 +328,34 @@ namespace build2 << ' ' << me.cur_options << ' ' << me.new_options; // @@ TMP - // If we are rematched with the buildtime option, propagate it to our - // prerequisite libraries. + me.cur_options |= me.new_options; + + // We also need to update options in install_match_data. // + t.data (a).options = me.cur_options; + if ((me.new_options & lib::option_install_buildtime) != 0) { + // If we are rematched with the buildtime option, propagate it to our + // prerequisite libraries. + // for (const target* pt: t.prerequisite_targets[a]) { if (pt != nullptr && (pt->is_a () || pt->is_a () || pt->is_a () || pt->is_a ())) - rematch_sync (a, *pt, lib::option_install_buildtime); + { + // Go for all options instead of just install_buildtime to avoid + // any further relocking/reapply (we only support runtime-only or + // everything). + // + rematch_sync (a, *pt, match_extra::all_options); + } } - } - - // @@ TODO: match additional prerequisites if required. - - me.cur_options |= me.new_options; - // We also need to update options in install_match_data. - // - t.data (a).options = me.cur_options; + // Also match any additional prerequisites (e.g., headers). + // + file_rule::reapply_impl (a, t, me); + } } bool install_rule:: diff --git a/libbuild2/install/rule.cxx b/libbuild2/install/rule.cxx index 788a13f..49675a8 100644 --- a/libbuild2/install/rule.cxx +++ b/libbuild2/install/rule.cxx @@ -359,10 +359,12 @@ namespace build2 } recipe file_rule:: - apply_impl (action a, target& t, match_extra& me) const + apply_impl (action a, target& t, match_extra& me, bool reapply) const { tracer trace ("install::file_rule::apply"); + assert (!reapply || a.operation () != update_id); + // Note that we are called both as the outer part during the update-for- // un/install pre-operation and as the inner part during the un/install // operation itself. @@ -419,6 +421,8 @@ namespace build2 auto pms (group_prerequisite_members (a, t, members_mode::never)); for (auto i (pms.begin ()), e (pms.end ()); i != e; ++i) { + // NOTE: see essentially the same logic in reapply_impl() below. + // const prerequisite& p (i->prerequisite); // Ignore excluded. @@ -445,27 +449,27 @@ namespace build2 pair fr (filter (*is, a, t, i, me)); const target* pt (fr.first); + uint64_t options (fr.second); + + lookup l; + if (pt == nullptr) { l5 ([&]{trace << "ignoring " << p << " (filtered out)";}); - continue; } - + // // See if we were explicitly instructed not to touch this target (the // same semantics as in alias_rule). // // Note: not the same as lookup_install() above. // - auto l ((*pt)[var_install (*p.scope.root_scope ())]); - if (l && cast (l).string () == "false") + else if ((l = (*pt)[var_install (*p.scope.root_scope ())]) && + cast (l).string () == "false") { l5 ([&]{trace << "ignoring " << *pt << " (not installable)";}); - continue; + pt = nullptr; } - - uint64_t options (fr.second); - - if (pt->is_a ()) + else if (pt->is_a ()) { // If the matched rule returned noop_recipe, then the target state // is set to unchanged as an optimization. Use this knowledge to @@ -487,8 +491,14 @@ namespace build2 pt = nullptr; } - if (pt != nullptr) - pts.push_back (prerequisite_target (pt, pi)); + if (pt != nullptr || reapply) + { + // Use auxiliary data for a NULL entry to distinguish between + // filtered out (1) and ignored for other reasons (0). + // + pts.push_back ( + prerequisite_target (pt, pi, fr.first == nullptr ? 1 : 0)); + } } #if 1 @@ -514,6 +524,79 @@ namespace build2 } } + void file_rule:: + reapply_impl (action a, target& t, match_extra& me) const + { + tracer trace ("install::file_rule::reapply"); + + assert (a.operation () != update_id); + + optional is; + + // Iterate over prerequisites and prerequisite targets in parallel. + // + auto& pts (t.prerequisite_targets[a]); + size_t j (0), n (pts.size ()), en (0); + + auto pms (group_prerequisite_members (a, t, members_mode::never)); + for (auto i (pms.begin ()), e (pms.end ()); + i != e && j != n; + ++i, ++j, ++en) + { + // The same logic as in apply() above except that we skip + // prerequisites that were not filtered out. + // + const prerequisite& p (i->prerequisite); + + include_type pi (include (a, t, p)); + if (!pi) + continue; + + if (p.proj) + continue; + + prerequisite_target& pto (pts[j]); + + if (pto.target != nullptr || pto.data == 0) + continue; + + if (!is) + is = a.operation () != update_id ? install_scope (t) : nullptr; + + pair fr (filter (*is, a, t, i, me)); + + const target* pt (fr.first); + uint64_t options (fr.second); + + lookup l; + + if (pt == nullptr) + { + l5 ([&]{trace << "ignoring " << p << " (filtered out)";}); + } + else if ((l = (*pt)[var_install (*p.scope.root_scope ())]) && + cast (l).string () == "false") + { + l5 ([&]{trace << "ignoring " << *pt << " (not installable)";}); + pt = nullptr; + } + else if (pt->is_a ()) + { + if (match_sync (a, *pt, unmatch::unchanged, options).first) + pt = nullptr; + } + else if (!try_match_sync (a, *pt, options).first) + { + l5 ([&]{trace << "ignoring " << *pt << " (no rule)";}); + pt = nullptr; + } + + pto = prerequisite_target (pt, pi, fr.first == nullptr ? 1 : 0); + } + + assert (en == n); // Did not call apply() with true for reapply? + } + target_state file_rule:: perform_update (action a, const target& t) { diff --git a/libbuild2/install/rule.hxx b/libbuild2/install/rule.hxx index 2ddacc6..4a35503 100644 --- a/libbuild2/install/rule.hxx +++ b/libbuild2/install/rule.hxx @@ -167,8 +167,20 @@ namespace build2 // Implementation of apply() that returns empty_recipe (i.e., NULL) if // the target is not installable. // + // If the implementation may call reapply_impl(), then the reapply + // argument to apply_impl() must be true. Note that in this case, the + // *_impl() functions use the prerequisite_target::data member for own + // housekeeping. + // recipe - apply_impl (action, target&, match_extra&) const; + apply_impl (action, target&, match_extra&, bool reapply = false) const; + + // Implementation of reapply() that re-tries prerequisites that have + // been filtered out during the reapply() call. Note that currently not + // supported for update, only for install/uninstall. + // + void + reapply_impl (action, target&, match_extra&) const; static target_state perform_update (action, const target&); -- cgit v1.1