aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2023-11-01 09:42:57 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2023-11-01 09:42:57 +0200
commite745077de53aa0e21affea66c2f55bba172ec286 (patch)
treed5e52e449f8981c19aef7223c9365f7d9f2f5bc3
parenta49b6fff2ba25bf9150f89a4ecfc2c707ba633de (diff)
Add support for adjusting match options of post hoc prerequisites
-rw-r--r--libbuild2/algorithm.cxx79
-rw-r--r--libbuild2/context.hxx8
-rw-r--r--libbuild2/operation.cxx88
-rw-r--r--libbuild2/rule.cxx5
-rw-r--r--libbuild2/rule.hxx6
-rw-r--r--libbuild2/target.hxx12
-rw-r--r--libbuild2/target.ixx1
7 files changed, 153 insertions, 46 deletions
diff --git a/libbuild2/algorithm.cxx b/libbuild2/algorithm.cxx
index bc4b835..4ec4db5 100644
--- a/libbuild2/algorithm.cxx
+++ b/libbuild2/algorithm.cxx
@@ -966,6 +966,37 @@ namespace build2
}
static void
+ apply_posthoc_impl (
+ action a, target& t,
+ const pair<const string, reference_wrapper<const rule>>& m,
+ context::posthoc_target& pt)
+ {
+ const scope& bs (t.base_scope ());
+
+ // Apply rules in project environment.
+ //
+ auto_project_env penv;
+ if (const scope* rs = bs.root_scope ())
+ penv = auto_project_env (*rs);
+
+ const rule& ru (m.second);
+ match_extra& me (t[a].match_extra);
+ me.posthoc_prerequisite_targets = &pt.prerequisite_targets;
+
+ auto df = make_diag_frame (
+ [a, &t, &m](const diag_record& dr)
+ {
+ if (verb != 0)
+ dr << info << "while applying rule " << m.first << " to "
+ << diag_do (a, t) << " for post hoc prerequisites";
+ });
+
+ // Note: for now no adhoc_apply_posthoc().
+ //
+ ru.apply_posthoc (a, t, me);
+ }
+
+ static void
reapply_impl (action a,
target& t,
const pair<const string, reference_wrapper<const rule>>& m)
@@ -980,6 +1011,7 @@ namespace build2
const rule& ru (m.second);
match_extra& me (t[a].match_extra);
+ // Note: me.posthoc_prerequisite_targets carried over.
auto df = make_diag_frame (
[a, &t, &m](const diag_record& dr)
@@ -994,11 +1026,15 @@ namespace build2
ru.reapply (a, t, me);
}
- // If anything goes wrong, set target state to failed and return false.
+ // If anything goes wrong, set target state to failed and return nullopt.
+ // Otherwise return the pointer to the new posthoc_target entry if any post
+ // hoc prerequisites were present or NULL otherwise. Note that the returned
+ // entry is stable (because we use a list) and should only be accessed
+ // during the match phase if the holding the target lock.
//
// Note: must be called while holding target_lock.
//
- static bool
+ static optional<context::posthoc_target*>
match_posthoc (action a, target& t)
{
// The plan is to, while holding the lock, search and collect all the post
@@ -1024,11 +1060,18 @@ namespace build2
// In the end, matching (and execution) "inline" (i.e., as we match/
// execute the corresponding target) appears to be unworkable in the
// face of cycles.
-
+ //
+ // Note also that this delayed match also helps with allowing the rule to
+ // adjust match options of post hoc prerequisites without needing the
+ // rematch support (see match_extra::posthoc_prerequisites).
+ //
// @@ Anything we need to do for group members (see through)? Feels quite
// far-fetched.
//
- vector<const target*> pts;
+ using posthoc_target = context::posthoc_target;
+ using posthoc_prerequisite_target = posthoc_target::prerequisite_target;
+
+ vector<posthoc_prerequisite_target> pts;
try
{
for (const prerequisite& p: group_prerequisites (t))
@@ -1053,14 +1096,17 @@ namespace build2
}
}
- pts.push_back (&search (t, p)); // May fail.
+ pts.push_back (
+ posthoc_prerequisite_target {
+ &search (t, p), // May fail.
+ match_extra::all_options});
}
}
}
catch (const failed&)
{
t.state[a].state = target_state::failed;
- return false;
+ return nullopt;
}
if (!pts.empty ())
@@ -1068,11 +1114,11 @@ namespace build2
context& ctx (t.ctx);
mlock l (ctx.current_posthoc_targets_mutex);
- ctx.current_posthoc_targets.push_back (
- context::posthoc_target {a, t, move (pts)});
+ ctx.current_posthoc_targets.push_back (posthoc_target {a, t, move (pts)});
+ return &ctx.current_posthoc_targets.back (); // Stable.
}
- return true;
+ return nullptr;
}
// If step is true then perform only one step of the match/apply sequence.
@@ -1230,7 +1276,20 @@ namespace build2
if (t.has_group_prerequisites ()) // Ok since already matched.
{
- if (!match_posthoc (a, t))
+ if (optional<context::posthoc_target*> p = match_posthoc (a, t))
+ {
+ if (*p != nullptr)
+ {
+ // It would have been more elegant to do this before calling
+ // apply_impl() and then expose the post hoc prerequisites to
+ // apply(). The problem is the group may not be resolved until
+ // the call to apply(). And so we resort to the separate
+ // apply_posthoc() function.
+ //
+ apply_posthoc_impl (a, t, *s.rule, **p);
+ }
+ }
+ else
s.state = target_state::failed;
}
diff --git a/libbuild2/context.hxx b/libbuild2/context.hxx
index 8898c92..2dec54a 100644
--- a/libbuild2/context.hxx
+++ b/libbuild2/context.hxx
@@ -432,9 +432,15 @@ namespace build2
//
struct posthoc_target
{
+ struct prerequisite_target
+ {
+ const build2::target* target;
+ uint64_t match_options;
+ };
+
build2::action action;
reference_wrapper<const build2::target> target;
- vector<const build2::target*> prerequisite_targets;
+ vector<prerequisite_target> prerequisite_targets;
};
list<posthoc_target> current_posthoc_targets;
diff --git a/libbuild2/operation.cxx b/libbuild2/operation.cxx
index 7b6dc3c..6f88e38 100644
--- a/libbuild2/operation.cxx
+++ b/libbuild2/operation.cxx
@@ -360,11 +360,14 @@ namespace build2
bool posthoc_fail (false);
if (!ctx.current_posthoc_targets.empty () && (!fail || ctx.keep_going))
{
+ using posthoc_target = context::posthoc_target;
+ using posthoc_prerequisite_target = posthoc_target::prerequisite_target;
+
// Note that on each iteration we may end up with new entries at the
// back. Since we start and end each iteration in serial execution, we
// don't need to mess with the mutex.
//
- for (const context::posthoc_target& p: ctx.current_posthoc_targets)
+ for (const posthoc_target& p: ctx.current_posthoc_targets)
{
action a (p.action); // May not be the same as argument action.
const target& t (p.target);
@@ -383,18 +386,21 @@ namespace build2
//
// @@ PERF: match in parallel (need match_direct_async(), etc).
//
- for (const target* pt: p.prerequisite_targets)
+ for (const posthoc_prerequisite_target& pt: p.prerequisite_targets)
{
- target_state s (match_direct_sync (a, *pt,
- match_extra::all_options,
- false /* fail */));
-
- if (s == target_state::failed)
+ if (pt.target != nullptr)
{
- posthoc_fail = true;
+ target_state s (match_direct_sync (a, *pt.target,
+ pt.match_options,
+ false /* fail */));
- if (!ctx.keep_going)
- break;
+ if (s == target_state::failed)
+ {
+ posthoc_fail = true;
+
+ if (!ctx.keep_going)
+ break;
+ }
}
}
@@ -495,7 +501,10 @@ namespace build2
bool posthoc_fail (false);
auto execute_posthoc = [&ctx, &posthoc_fail] ()
{
- for (const context::posthoc_target& p: ctx.current_posthoc_targets)
+ using posthoc_target = context::posthoc_target;
+ using posthoc_prerequisite_target = posthoc_target::prerequisite_target;
+
+ for (const posthoc_target& p: ctx.current_posthoc_targets)
{
action a (p.action); // May not be the same as argument action.
const target& t (p.target);
@@ -509,16 +518,20 @@ namespace build2
});
#if 0
- for (const target* pt: p.prerequisite_targets)
+ for (const posthoc_prerequisite_target& pt: p.prerequisite_targets)
{
- target_state s (execute_direct_sync (a, *pt, false /* fail */));
-
- if (s == target_state::failed)
+ if (pt.target != nullptr)
{
- posthoc_fail = true;
+ target_state s (
+ execute_direct_sync (a, *pt.target, false /* fail */));
- if (!ctx.keep_going)
- break;
+ if (s == target_state::failed)
+ {
+ posthoc_fail = true;
+
+ if (!ctx.keep_going)
+ break;
+ }
}
}
#else
@@ -528,16 +541,20 @@ namespace build2
atomic_count tc (0);
wait_guard wg (ctx, tc);
- for (const target* pt: p.prerequisite_targets)
+ for (const posthoc_prerequisite_target& pt: p.prerequisite_targets)
{
- target_state s (execute_direct_async (a, *pt, 0, tc, false /*fail*/));
-
- if (s == target_state::failed)
+ if (pt.target != nullptr)
{
- posthoc_fail = true;
+ target_state s (
+ execute_direct_async (a, *pt.target, 0, tc, false /*fail*/));
- if (!ctx.keep_going)
- break;
+ if (s == target_state::failed)
+ {
+ posthoc_fail = true;
+
+ if (!ctx.keep_going)
+ break;
+ }
}
}
@@ -545,18 +562,21 @@ namespace build2
// Process the result.
//
- for (const target* pt: p.prerequisite_targets)
+ for (const posthoc_prerequisite_target& pt: p.prerequisite_targets)
{
- // Similar to below, no need to wait.
- //
- target_state s (pt->executed_state (a, false /* fail */));
-
- if (s == target_state::failed)
+ if (pt.target != nullptr)
{
- // Note: no need to keep going.
+ // Similar to below, no need to wait.
//
- posthoc_fail = true;
- break;
+ target_state s (pt.target->executed_state (a, false /* fail */));
+
+ if (s == target_state::failed)
+ {
+ // Note: no need to keep going.
+ //
+ posthoc_fail = true;
+ break;
+ }
}
}
#endif
diff --git a/libbuild2/rule.cxx b/libbuild2/rule.cxx
index 04d6b38..a3e3268 100644
--- a/libbuild2/rule.cxx
+++ b/libbuild2/rule.cxx
@@ -23,6 +23,11 @@ namespace build2
}
void rule::
+ apply_posthoc (action, target&, match_extra&) const
+ {
+ }
+
+ void rule::
reapply (action, target&, match_extra&) const
{
// Unless the rule overrode cur_options, this function should never get
diff --git a/libbuild2/rule.hxx b/libbuild2/rule.hxx
index acd22fe..7e5ddb1 100644
--- a/libbuild2/rule.hxx
+++ b/libbuild2/rule.hxx
@@ -34,6 +34,9 @@ namespace build2
// implementations. It is also a way for us to later pass more information
// without breaking source compatibility.
//
+ // A rule may adjust post hoc prerequisites by overriding apply_posthoc().
+ // See match_extra::posthoc_prerequisite_targets for background and details.
+ //
// A rule may support match options and if such a rule is rematched with
// different options, then reapply() is called. See
// match_extra::{cur,new}_options for background and details.
@@ -50,6 +53,9 @@ namespace build2
apply (action, target&, match_extra&) const = 0;
virtual void
+ apply_posthoc (action, target&, match_extra&) const;
+
+ virtual void
reapply (action, target&, match_extra&) const;
rule () = default;
diff --git a/libbuild2/target.hxx b/libbuild2/target.hxx
index f537d59..83e6994 100644
--- a/libbuild2/target.hxx
+++ b/libbuild2/target.hxx
@@ -269,6 +269,15 @@ namespace build2
static constexpr uint64_t all_options = ~uint64_t (0);
+ // The list of post hoc prerequisite targets for this target. Only not
+ // NULL in rule::apply_posthoc() and rule::reapply() functions and only if
+ // there are post hoc prerequisites. Primarily useful for adjusting match
+ // options for post hoc prerequisites (but can also be used to blank some
+ // of them out).
+ //
+ vector<context::posthoc_target::prerequisite_target>*
+ posthoc_prerequisite_targets;
+
// Auxiliary data storage.
//
// A rule (whether matches or not) may use this pad to pass data between
@@ -340,7 +349,8 @@ namespace build2
explicit
match_extra (bool l = true, bool f = false)
: locked (l), fallback (f),
- cur_options (all_options), new_options (0) {}
+ cur_options (all_options), new_options (0),
+ posthoc_prerequisite_targets (nullptr) {}
void
reinit (bool fallback);
diff --git a/libbuild2/target.ixx b/libbuild2/target.ixx
index 03cf444..ad09cd4 100644
--- a/libbuild2/target.ixx
+++ b/libbuild2/target.ixx
@@ -142,6 +142,7 @@ namespace build2
fallback = f;
cur_options = all_options;
new_options = 0;
+ posthoc_prerequisite_targets = nullptr;
}
inline void match_extra::