diff options
Diffstat (limited to 'libbuild2/rule.cxx')
-rw-r--r-- | libbuild2/rule.cxx | 116 |
1 files changed, 103 insertions, 13 deletions
diff --git a/libbuild2/rule.cxx b/libbuild2/rule.cxx index 210f1ef..dc1c96c 100644 --- a/libbuild2/rule.cxx +++ b/libbuild2/rule.cxx @@ -15,13 +15,27 @@ 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>&, @@ -30,10 +44,25 @@ namespace build2 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); } @@ -56,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); } @@ -73,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 @@ -179,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 // @@ -195,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, true, 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; } @@ -240,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. @@ -297,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. // @@ -325,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 @@ -336,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 @@ -365,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); } |