diff options
Diffstat (limited to 'libbuild2/adhoc-rule-regex-pattern.cxx')
-rw-r--r-- | libbuild2/adhoc-rule-regex-pattern.cxx | 187 |
1 files changed, 146 insertions, 41 deletions
diff --git a/libbuild2/adhoc-rule-regex-pattern.cxx b/libbuild2/adhoc-rule-regex-pattern.cxx index b0de827..2d60520 100644 --- a/libbuild2/adhoc-rule-regex-pattern.cxx +++ b/libbuild2/adhoc-rule-regex-pattern.cxx @@ -86,7 +86,9 @@ namespace build2 tt = n.untyped () ? &file::static_type : s.find_target_type (n.type); if (tt == nullptr) - fail (loc) << "unknown target type " << n.type; + fail (loc) << "unknown target type " << n.type << + info << "perhaps the module that defines this target type is " + << "not loaded by project " << *s.root_scope (); } bool e (n.pattern && @@ -126,10 +128,13 @@ namespace build2 } bool adhoc_rule_regex_pattern:: - match (action a, target& t, const string&, match_extra& me) const + match (action a, const target& t, const string&, match_extra& me) const { tracer trace ("adhoc_rule_regex_pattern::match"); + // Note: target may not be locked in which case we should not modify + // target or match_extra (see adhoc_rule::match() for background). + // The plan is as follows: First check the "type signature" of the target // and its prerequisites (the primary target type has already been matched // by the rule matching machinery). If there is a match, then concatenate @@ -158,11 +163,23 @@ namespace build2 auto find_prereq = [a, &t] (const target_type& tt) -> optional<target_key> { // We use the standard logic that one would use in the rule::match() - // implementation. + // implementation. Except we support the unmatch and match values in + // the update variable. + // + // Note: assuming group prerequisites are immutable (not locked). // for (prerequisite_member p: group_prerequisite_members (a, t)) { - if (include (a, t, p) == include_type::normal && p.is_a (tt)) + // Note that here we don't validate the update operation override + // value (since we may not match). Instead the rule does this in + // apply(). + // + // Note: assuming include()'s use of target only relied on immutable + // data (not locked). + // + lookup l; + if (include (a, t, p, a.operation () == update_id ? &l : nullptr) == + include_type::normal && p.is_a (tt)) return p.key ().tk; } return nullopt; @@ -190,11 +207,21 @@ namespace build2 // iterators pointing to the string being matched. Which means this string // must be kept around until we are done with replacing the subsitutions. // In fact, we cannot even move it because this may invalidate the - // iterators (e.g., in case of a small string optimization). So the plan - // is to store the string in match_extra::buffer and regex_match_results - // (which we can move) in the auxiliary data storage. + // iterators (e.g., in case of a small string optimization). We also + // cannot set the data ahead of time because we may not match. Plus, + // resorting to a dynamic memory allocation even if we don't match feels + // heavy-handed. + // + // So the plan is to store the string in match_extra::data() and + // regex_match_results (which we can move) in the auxiliary data storage. // - string& ns (me.buffer); + // Note: only cache if locked. + // + static_assert (sizeof (string) <= match_extra::data_size, + "match data too large"); + + string tmp; + string& ns (me.locked ? me.data (string ()) : tmp); auto append_name = [&ns, first = true, @@ -212,10 +239,12 @@ namespace build2 // Primary target (always a pattern). // auto te (targets_.end ()), ti (targets_.begin ()); - append_name (t.key (), *ti); + append_name (t.key (), *ti); // Immutable (not locked). // Match ad hoc group members. // + // Note: shouldn't be in effect for an explicit group (not locked). + // while ((ti = find_if (ti + 1, te, pattern)) != te) { const target* at (find_adhoc_member (t, ti->type)); @@ -265,9 +294,8 @@ namespace build2 return false; } - static_assert (sizeof (regex_match_results) <= target::data_size, - "insufficient space"); - t.data (move (mr)); + if (me.locked) + t.data (a, move (mr)); return true; } @@ -293,9 +321,15 @@ namespace build2 } void adhoc_rule_regex_pattern:: - apply_adhoc_members (action, target& t, match_extra&) const + apply_group_members (action a, target& t, const scope& bs, + match_extra&) const { - const auto& mr (t.data<regex_match_results> ()); + if (targets_.size () == 1) // The group/primary target is always present. + return; + + group* g (t.is_a<group> ()); + + const auto& mr (t.data<regex_match_results> (a)); for (auto i (targets_.begin () + 1); i != targets_.end (); ++i) { @@ -322,39 +356,113 @@ namespace build2 d.normalize (); } - // @@ TODO: currently this uses type as the ad hoc member identity. + string n (substitute ( + t, + mr, + e.name.value, + (g != nullptr + ? "explicit target group member" + : "ad hoc target group member"))); + + // @@ TODO: save location in constructor? // - add_adhoc_member ( - t, - e.type, - move (d), - dir_path () /* out */, - substitute (t, mr, e.name.value, "ad hoc target group member")); + location loc; + + optional<string> ext (target::split_name (n, loc)); + + if (g != nullptr) + { + auto& ms (g->members); + + // These are conceptually static but they behave more like dynamic in + // that we likely need to insert the target, set its group, etc. In a + // sense, they are rule-static, but group-dynamic. + // + // Note: a custom version of the dyndep_rule::inject_group_member() + // logic. + // + auto l (search_new_locked ( + bs.ctx, + e.type, + move (d), + dir_path (), // Always in out. + move (n), + ext ? &*ext : nullptr, + &bs)); + + const target& t (l.first); // Note: non-const only if have lock. + + if (l.second) + { + l.first.group = g; + l.second.unlock (); + } + else + { + if (find (ms.begin (), ms.end (), &t) != ms.end ()) + continue; + + if (t.group != g) // Note: atomic. + { + // We can only update the group under lock. + // + target_lock tl (lock (a, t)); + + if (!tl) + fail << "group " << *g << " member " << t << " is already matched" << + info << "static group members specified by pattern rules cannot " + << "be used as prerequisites directly, only via group"; + + if (t.group == nullptr) + tl.target->group = g; + else if (t.group != g) + { + fail << "group " << *g << " member " << t + << " is already member of group " << *t.group; + } + } + } + + ms.push_back (&t); + } + else + { + add_adhoc_member_identity ( + t, + e.type, + move (d), + dir_path (), // Always in out. + move (n), + move (ext), + loc); + } } } void adhoc_rule_regex_pattern:: - apply_prerequisites (action a, target& t, match_extra&) const + apply_prerequisites (action a, target& t, + const scope& bs, + match_extra&) const { - const auto& mr (t.data<regex_match_results> ()); - - // Resolve and cache target scope lazily. - // - auto base_scope = [&t, bs = (const scope*) nullptr] () mutable - -> const scope& - { - if (bs == nullptr) - bs = &t.base_scope (); - - return *bs; - }; + const auto& mr (t.data<regex_match_results> (a)); // Re-create the same clean semantics as in match_prerequisite_members(). // bool clean (a.operation () == clean_id && !t.is_a<alias> ()); auto& pts (t.prerequisite_targets[a]); - size_t start (pts.size ()); + + // Avoid duplicating fsdir{} that may have already been injected by + // inject_fsdir() (in which case it is expected to be first). + // + const target* dir (nullptr); + if (!pts.empty ()) + { + const prerequisite_target& pt (pts.front ()); + + if (pt.target != nullptr && pt.adhoc () && pt.target->is_a<fsdir> ()) + dir = pt.target; + } for (const element& e: prereqs_) { @@ -382,7 +490,7 @@ namespace build2 n = name (e.name.dir, e.name.type, substitute (t, mr, e.name.value, "prerequisite")); - s = &base_scope (); + s = &bs; } else { @@ -392,18 +500,15 @@ namespace build2 const target& pt (search (t, move (n), *s, &e.type)); - if (clean && !pt.in (*base_scope ().root_scope ())) + if (&pt == dir || (clean && !pt.in (*bs.root_scope ()))) continue; // @@ TODO: it could be handy to mark a prerequisite (e.g., a tool) // ad hoc so that it doesn't interfere with the $< list. Also - // clean=false. + // clean=false. Also update=match|unmatch. // pts.push_back (prerequisite_target (&pt, false /* adhoc */)); } - - if (start != pts.size ()) - match_members (a, t, pts, start); } void adhoc_rule_regex_pattern:: |