aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2023-10-27 07:22:00 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2023-10-27 07:22:00 +0200
commita5418d62f79f7d59f6b0a7efc88dcd7e99c8248a (patch)
tree84686c37b666eae4a52b0fda763afeeefca3461f
parent152f83d22861129dba48404632b61b3f00f18715 (diff)
WIP: install: file_rule::reapply_impl()
-rw-r--r--libbuild2/cc/install-rule.cxx34
-rw-r--r--libbuild2/install/rule.cxx107
-rw-r--r--libbuild2/install/rule.hxx14
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<install_match_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<liba> () || pt->is_a<libs> () ||
pt->is_a<libua> () || pt->is_a<libus> ()))
- 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<install_match_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<const target*, uint64_t> 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<path> (l).string () == "false")
+ else if ((l = (*pt)[var_install (*p.scope.root_scope ())]) &&
+ cast<path> (l).string () == "false")
{
l5 ([&]{trace << "ignoring " << *pt << " (not installable)";});
- continue;
+ pt = nullptr;
}
-
- uint64_t options (fr.second);
-
- if (pt->is_a<file> ())
+ else if (pt->is_a<file> ())
{
// 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<const scope*> 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<const target*, uint64_t> 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<path> (l).string () == "false")
+ {
+ l5 ([&]{trace << "ignoring " << *pt << " (not installable)";});
+ pt = nullptr;
+ }
+ else if (pt->is_a<file> ())
+ {
+ 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&);