diff options
Diffstat (limited to 'libbuild2/target.ixx')
-rw-r--r-- | libbuild2/target.ixx | 234 |
1 files changed, 200 insertions, 34 deletions
diff --git a/libbuild2/target.ixx b/libbuild2/target.ixx index cfc3847..39b81e7 100644 --- a/libbuild2/target.ixx +++ b/libbuild2/target.ixx @@ -3,12 +3,13 @@ #include <cstring> // memcpy() -#include <libbuild2/filesystem.hxx> // mtime() - #include <libbuild2/export.hxx> namespace build2 { + LIBBUILD2_SYMEXPORT timestamp + mtime (const char*); // filesystem.cxx + // target_key // inline const string& target_key:: @@ -52,20 +53,102 @@ namespace build2 return r; } + // rule_hints + // + inline const string& rule_hints:: + find (const target_type& tt, operation_id o, bool ut) const + { + // Look for fallback during the same iteration. + // + const value_type* f (nullptr); + + for (const value_type& v: map) + { + if (!(v.type == nullptr ? ut : tt.is_a (*v.type))) + continue; + + if (v.operation == o) + return v.hint; + + if (f == nullptr && + v.operation == default_id && + (o == update_id || o == clean_id)) + f = &v; + } + + return f != nullptr ? f->hint : empty_string; + } + + inline void rule_hints:: + insert (const target_type* tt, operation_id o, string h) + { + auto i (find_if (map.begin (), map.end (), + [tt, o] (const value_type& v) + { + return v.operation == o && v.type == tt; + })); + + if (i == map.end ()) + map.push_back (value_type {tt, o, move (h)}); + else + i->hint = move (h); + } + + inline const string& target:: + find_hint (operation_id o) const + { + using flag = target_type::flag; + + const target_type& tt (type ()); + + // First check the target itself. + // + if (!rule_hints.empty ()) + { + // If this is a group that "gave" its untyped hints to the members, then + // ignore untyped entries. + // + bool ut ((tt.flags & flag::member_hint) != flag::member_hint); + + const string& r (rule_hints.find (tt, o, ut)); + if (!r.empty ()) + return r; + } + + // Then check the group. + // + if (const target* g = group) + { + if (!g->rule_hints.empty ()) + { + // If the group "gave" its untyped hints to the members, then don't + // ignore untyped entries. + // + bool ut ((g->type ().flags & flag::member_hint) == flag::member_hint); + + return g->rule_hints.find (tt, o, ut); + } + } + + return empty_string; + } + // match_extra // inline void match_extra:: - init (bool f) + reinit (bool f) { + clear_data (); fallback = f; - buffer.clear (); + cur_options = all_options; + new_options = 0; + posthoc_prerequisite_targets = nullptr; } inline void match_extra:: free () { - string s; - buffer.swap (s); + clear_data (); } // target @@ -155,24 +238,41 @@ namespace build2 } inline bool target:: - matched (action a) const + matched (action a, memory_order mo) const { - assert (ctx.phase == run_phase::execute); + assert (ctx.phase == run_phase::match || + ctx.phase == run_phase::execute); const opstate& s (state[a]); + size_t c (s.task_count.load (mo)); + size_t b (ctx.count_base ()); // Note: cannot do (c - b)! - // Note that while the target could be being executed, we should see at - // least offset_matched since it must have been "achieved" before the - // phase switch. - // - size_t c (s.task_count.load (memory_order_relaxed) - ctx.count_base ()); - - return c >= offset_matched; + if (ctx.phase == run_phase::match) + { + // While it will normally be applied, it could also be already executed + // or being relocked to reapply match options (see lock_impl() for + // background). + // + // Note that we can't just do >= offset_applied since offset_busy can + // also mean it is being matched. + // + // See also matched_state_impl(), mtime() for similar logic. + // + return (c == (b + offset_applied) || + c == (b + offset_executed) || + (c >= (b + offset_busy) && + s.match_extra.cur_options_.load (memory_order_relaxed) != 0)); + } + else + { + // Note that while the target could be being executed, we should see at + // least offset_matched since it must have been "achieved" before the + // phase switch. + // + return c >= (b + offset_matched); + } } - LIBBUILD2_SYMEXPORT target_state - group_action (action, const target&); // <libbuild2/algorithm.hxx> - inline bool target:: group_state (action a) const { @@ -180,16 +280,32 @@ namespace build2 // raw state is not group provided the recipe is group_recipe and the // state is unknown (see mtime() for a discussion on why we do it). // + // Note that additionally s.state may not be target_state::group even + // after execution due to deferment (see execute_impl() for details). + // + // @@ Hm, I wonder why not just return s.recipe_group_action now that we + // cache it. + // + + // This special hack allows us to do things like query an ad hoc member's + // state or mtime without matching/executing the member, only the group. + // Requiring matching/executing the member would be too burdensome and + // this feels harmless (ad hoc membership cannot be changed during the + // execute phase). + // + // Note: this test must come first since the member may not be matched and + // thus its state uninitialized. + // + if (ctx.phase == run_phase::execute && adhoc_group_member ()) + return true; + const opstate& s (state[a]); if (s.state == target_state::group) return true; if (s.state == target_state::unknown && group != nullptr) - { - if (recipe_function* const* f = s.recipe.target<recipe_function*> ()) - return *f == &group_action; - } + return s.recipe_group_action; return false; } @@ -203,15 +319,22 @@ namespace build2 // Note: already synchronized. // - size_t o (s.task_count.load (memory_order_relaxed) - ctx.count_base ()); + size_t c (s.task_count.load (memory_order_relaxed)); + size_t b (ctx.count_base ()); // Note: cannot do (c - b)! - if (o == offset_tried) + if (c == (b + offset_tried)) return make_pair (false, target_state::unknown); else { - // Normally applied but can also be already executed. + // The same semantics as in target::matched(). Note that in the executed + // case we are guaranteed to be synchronized since we are in the match + // phase. // - assert (o == offset_applied || o == offset_executed); + assert (c == (b + offset_applied) || + c == (b + offset_executed) || + (c >= (b + offset_busy) && + s.match_extra.cur_options_.load (memory_order_relaxed) != 0)); + return make_pair (true, (group_state (a) ? group->state[a] : s).state); } } @@ -330,15 +453,27 @@ namespace build2 // include() // LIBBUILD2_SYMEXPORT include_type - include_impl (action, const target&, const prerequisite&, const target*); + include_impl (action, const target&, + const prerequisite&, const target*, + lookup*); inline include_type - include (action a, const target& t, const prerequisite& p, const target* m) + include (action a, const target& t, const prerequisite& p, lookup* l) { // Most of the time no prerequisite-specific variables will be specified, // so let's optimize for that. // - return p.vars.empty () ? include_type (true) : include_impl (a, t, p, m); + return p.vars.empty () + ? include_type (true) + : include_impl (a, t, p, nullptr, l); + } + + inline include_type + include (action a, const target& t, const prerequisite_member& pm, lookup* l) + { + return pm.prerequisite.vars.empty () + ? include_type (true) + : include_impl (a, t, pm.prerequisite, pm.member, l); } // group_prerequisites @@ -423,7 +558,12 @@ namespace build2 // assert (!member->adhoc_group_member ()); - return prerequisite_type (*member); + // Feels like copying the prerequisite's variables to member is more + // correct than not (consider for_install, for example). + // + prerequisite_type p (*member); + p.vars = prerequisite.vars; + return p; } inline prerequisite_key prerequisite_member:: @@ -456,6 +596,25 @@ namespace build2 } template <typename T> + inline void prerequisite_members_range<T>::iterator:: + switch_mode () + { + g_ = resolve_members (*i_); + + if (g_.members != nullptr) + { + // See empty see through groups as groups. + // + for (j_ = 1; j_ <= g_.count && g_.members[j_ - 1] == nullptr; ++j_) ; + + if (j_ > g_.count) + g_.count = 0; + } + else + assert (r_->mode_ != members_mode::always); // Group can't be resolved. + } + + template <typename T> inline auto prerequisite_members_range<T>::iterator:: operator++ () -> iterator& { @@ -480,7 +639,7 @@ namespace build2 if (r_->mode_ != members_mode::never && i_ != r_->e_ && - i_->type.see_through) + i_->type.see_through ()) switch_mode (); } @@ -587,15 +746,20 @@ namespace build2 inline timestamp mtime_target:: load_mtime (const path& p) const { - assert (ctx.phase == run_phase::execute && - !group_state (action () /* inner */)); + // We can only enforce "not group state" during the execute phase. During + // match (e.g., the target is being matched), we will just have to pay + // attention. + // + assert (ctx.phase == run_phase::match || + (ctx.phase == run_phase::execute && + !group_state (action () /* inner */))); duration::rep r (mtime_.load (memory_order_consume)); if (r == timestamp_unknown_rep) { assert (!p.empty ()); - r = build2::mtime (p).time_since_epoch ().count (); + r = build2::mtime (p.string ().c_str ()).time_since_epoch ().count (); mtime_.store (r, memory_order_release); } @@ -605,6 +769,8 @@ namespace build2 inline bool mtime_target:: newer (timestamp mt, target_state s) const { + assert (s != target_state::unknown); // Should be executed. + timestamp mp (mtime ()); // What do we do if timestamps are equal? This can happen, for example, |