aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/rule.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/rule.cxx')
-rw-r--r--libbuild2/rule.cxx124
1 files changed, 111 insertions, 13 deletions
diff --git a/libbuild2/rule.cxx b/libbuild2/rule.cxx
index acb46e8..dc1c96c 100644
--- a/libbuild2/rule.cxx
+++ b/libbuild2/rule.cxx
@@ -15,17 +15,54 @@ using namespace butl;
namespace build2
{
- // rule (vtable)
+ // rule
//
rule::
~rule ()
{
}
+ 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
+ // called. And if it did, then it should override this function.
+ //
+ assert (false);
+ }
+
+ const target* rule::
+ import (const prerequisite_key&,
+ const optional<string>&,
+ const location&) const
+ {
+ return nullptr;
+ }
+
+ const rule_match*
+ match_adhoc_recipe (action, target&, match_extra&); // algorithm.cxx
+
bool rule::
sub_match (const string& n, operation_id o,
action a, target& t, match_extra& me) const
{
+ // First check for an ad hoc recipe (see match_rule_impl() for details).
+ //
+ if (!t.adhoc_recipes.empty ())
+ {
+ // Use scratch match_extra since if there is no recipe, then we don't
+ // want to keep any changes and if there is, then we want it discarded.
+ //
+ match_extra s (true /* locked */); // Not called from adhoc_rule::match().
+ if (match_adhoc_recipe (action (a.meta_operation (), o), t, s) != nullptr)
+ return false;
+ }
+
const string& h (t.find_hint (o));
return name_rule_map::sub (h, n) && match (a, t, h, me);
}
@@ -48,6 +85,13 @@ namespace build2
sub_match (const string& n, operation_id o,
action a, target& t) const
{
+ if (!t.adhoc_recipes.empty ())
+ {
+ match_extra s (true /* locked */); // Not called from adhoc_rule::match().
+ if (match_adhoc_recipe (action (a.meta_operation (), o), t, s) != nullptr)
+ return false;
+ }
+
return name_rule_map::sub (t.find_hint (o), n) && match (a, t);
}
@@ -65,6 +109,9 @@ namespace build2
{
tracer trace ("file_rule::match");
+ if (match_type_ && !t.is_a<mtime_target> ())
+ return false;
+
// While strictly speaking we should check for the file's existence
// for every action (because that's the condition for us matching),
// for some actions this is clearly a waste. Say, perform_clean: we
@@ -171,7 +218,7 @@ namespace build2
}
const file_rule file_rule::instance;
- const rule_match file_rule::rule_match ("file", file_rule::instance);
+ const rule_match file_rule::rule_match ("build.file", file_rule::instance);
// alias_rule
//
@@ -187,9 +234,25 @@ namespace build2
// Inject dependency on our directory (note: not parent) so that it is
// automatically created on update and removed on clean.
//
- inject_fsdir (a, t, false);
+ inject_fsdir (a, t, true, true, false);
- match_prerequisites (a, t);
+ // Handle the alias match-only level.
+ //
+ match_search ms;
+ if (t.ctx.match_only && *t.ctx.match_only == match_only_level::alias)
+ {
+ ms = [] (action,
+ const target& t,
+ const prerequisite& p,
+ include_type i)
+ {
+ return prerequisite_target (
+ p.is_a<alias> () ? &search (t, p) : nullptr,
+ i);
+ };
+ }
+
+ match_prerequisites (a, t, ms);
return default_recipe;
}
@@ -232,7 +295,7 @@ namespace build2
if (verb >= 2)
text << "mkdir " << d;
else if (verb && t.ctx.current_diag_noise)
- text << "mkdir " << t;
+ print_diag ("mkdir", t);
};
// Note: ignoring the dry_run flag.
@@ -289,16 +352,19 @@ namespace build2
}
void fsdir_rule::
- perform_update_direct (action a, const target& t)
+ perform_update_direct (action a, const fsdir& t)
{
+ assert (t.ctx.phase == run_phase::match);
+
// First create the parent directory. If present, it is always first.
//
- const target* p (t.prerequisite_targets[a].empty ()
- ? nullptr
- : t.prerequisite_targets[a][0]);
-
- if (p != nullptr && p->is_a<fsdir> ())
- perform_update_direct (a, *p);
+ if (const target* p = (t.prerequisite_targets[a].empty ()
+ ? nullptr
+ : t.prerequisite_targets[a][0]))
+ {
+ if (const fsdir* fp = p->is_a<fsdir> ())
+ perform_update_direct (a, *fp);
+ }
// The same code as in perform_update() above.
//
@@ -317,6 +383,8 @@ namespace build2
// Don't fail if we couldn't remove the directory because it is not empty
// (or is current working directory). In this case rmdir() will issue a
// warning when appropriate.
+
+ // The same code as in perform_clean_direct() below.
//
target_state ts (rmdir (t.dir, t, t.ctx.current_diag_noise ? 1 : 2)
? target_state::changed
@@ -328,6 +396,35 @@ namespace build2
return ts;
}
+ void fsdir_rule::
+ perform_clean_direct (action a, const fsdir& t)
+ {
+ assert (t.ctx.phase == run_phase::match);
+
+ // The same code as in perform_clean() above.
+ //
+ // Except that if there are other dependens of this fsdir{} then this will
+ // likely be a noop (because the directory won't be empty) and it makes
+ // sense to just defer cleaning to such other dependents. See
+ // clean_during_match() for backgound. This is similar logic as in
+ // unmatch::safe.
+ //
+ if (t[a].dependents.load (memory_order_relaxed) == 0)
+ {
+ rmdir (t.dir, t, t.ctx.current_diag_noise ? 1 : 2);
+
+ // Then clean the parent directory. If present, it is always first.
+ //
+ if (const target* p = (t.prerequisite_targets[a].empty ()
+ ? nullptr
+ : t.prerequisite_targets[a][0]))
+ {
+ if (const fsdir* fp = p->is_a<fsdir> ())
+ perform_clean_direct (a, *fp);
+ }
+ }
+ }
+
const fsdir_rule fsdir_rule::instance;
// noop_rule
@@ -357,8 +454,9 @@ namespace build2
}
bool adhoc_rule::
- match (action a, target& t, const string& h, match_extra& me) const
+ match (action a, target& xt, const string& h, match_extra& me) const
{
+ const target& t (xt);
return pattern == nullptr || pattern->match (a, t, h, me);
}