aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/adhoc-rule-regex-pattern.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/adhoc-rule-regex-pattern.cxx')
-rw-r--r--libbuild2/adhoc-rule-regex-pattern.cxx209
1 files changed, 153 insertions, 56 deletions
diff --git a/libbuild2/adhoc-rule-regex-pattern.cxx b/libbuild2/adhoc-rule-regex-pattern.cxx
index c621b67..2d60520 100644
--- a/libbuild2/adhoc-rule-regex-pattern.cxx
+++ b/libbuild2/adhoc-rule-regex-pattern.cxx
@@ -3,7 +3,7 @@
#include <libbuild2/adhoc-rule-regex-pattern.hxx>
-#include <libbutl/regex.mxx>
+#include <libbutl/regex.hxx>
#include <libbuild2/algorithm.hxx>
@@ -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,41 +207,44 @@ 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.
+ //
+ // Note: only cache if locked.
//
- string& ns (me.buffer);
+ static_assert (sizeof (string) <= match_extra::data_size,
+ "match data too large");
- auto append_name = [&ns, first = true] (const target_key& tk,
- const element& e) mutable
+ string tmp;
+ string& ns (me.locked ? me.data (string ()) : tmp);
+
+ auto append_name = [&ns,
+ first = true,
+ storage = string ()] (const target_key& tk,
+ const element& e) mutable
{
if (!first)
ns += '/';
else
first = false;
- ns += *tk.name;
-
- // The same semantics as in variable_type_map::find().
- //
- if (tk.ext && !tk.ext->empty () &&
- (e.match_ext ||
- tk.type->fixed_extension == &target_extension_none ||
- tk.type->fixed_extension == &target_extension_must))
- {
- ns += '.';
- ns += *tk.ext;
- }
+ ns += tk.effective_name (storage, e.match_ext);
};
// 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));
@@ -274,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;
}
@@ -302,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)
{
@@ -331,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_)
{
@@ -391,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
{
@@ -401,17 +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.
+ // ad hoc so that it doesn't interfere with the $< list. Also
+ // 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::