aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/algorithm.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/algorithm.cxx')
-rw-r--r--libbuild2/algorithm.cxx162
1 files changed, 102 insertions, 60 deletions
diff --git a/libbuild2/algorithm.cxx b/libbuild2/algorithm.cxx
index acff325..54ddf78 100644
--- a/libbuild2/algorithm.cxx
+++ b/libbuild2/algorithm.cxx
@@ -88,12 +88,12 @@ namespace build2
}
const target&
- search (const target& t, name n, const scope& s)
+ search (const target& t, name n, const scope& s, const target_type* tt)
{
assert (t.ctx.phase == run_phase::match);
- auto rp (s.find_target_type (n, location ()));
- const target_type* tt (rp.first);
+ auto rp (s.find_target_type (n, location (), tt));
+ tt = rp.first;
optional<string>& ext (rp.second);
if (tt == nullptr)
@@ -291,8 +291,8 @@ namespace build2
target&
add_adhoc_member (target& t,
const target_type& tt,
- const dir_path& dir,
- const dir_path& out,
+ dir_path dir,
+ dir_path out,
string n)
{
tracer trace ("add_adhoc_member");
@@ -305,8 +305,8 @@ namespace build2
pair<target&, ulock> r (
t.ctx.targets.insert_locked (tt,
- dir,
- out,
+ move (dir),
+ move (out),
move (n),
nullopt /* ext */,
target_decl::implied,
@@ -334,8 +334,12 @@ namespace build2
if (const scope* rs = bs.root_scope ())
penv = auto_project_env (*rs);
+ match_extra& me (t[a].match_extra);
+
// First check for an ad hoc recipe.
//
+ // Note that a fallback recipe is preferred over a non-fallback rule.
+ //
if (!t.adhoc_recipes.empty ())
{
auto df = make_diag_frame (
@@ -345,70 +349,60 @@ namespace build2
dr << info << "while matching ad hoc recipe to " << diag_do (a, t);
});
- auto match = [a, &t] (const adhoc_rule& r, bool fallback) -> bool
+ auto match = [a, &t, &me] (const adhoc_rule& r, bool fallback) -> bool
{
- match_extra me {fallback};
+ me.init (fallback);
- bool m;
if (auto* f = (a.outer ()
? t.ctx.current_outer_oif
: t.ctx.current_inner_oif)->adhoc_match)
- m = f (r, a, t, string () /* hint */, me);
+ return f (r, a, t, string () /* hint */, me);
else
- m = r.match (a, t, string () /* hint */, me);
-
- if (m)
- t[a].match_extra = move (me);
-
- return m;
+ return r.match (a, t, string () /* hint */, me);
};
// The action could be Y-for-X while the ad hoc recipes are always for
// X. So strip the Y-for part for comparison (but not for the match()
// calls; see below for the hairy inner/outer semantics details).
//
- action ca (a.outer ()
- ? action (a.meta_operation (), a.outer_operation ())
- : a);
+ action ca (a.inner ()
+ ? a
+ : action (a.meta_operation (), a.outer_operation ()));
auto b (t.adhoc_recipes.begin ()), e (t.adhoc_recipes.end ());
auto i (find_if (
b, e,
- [&match, ca] (const adhoc_recipe& r)
+ [&match, ca] (const shared_ptr<adhoc_rule>& r)
{
- auto& as (r.actions);
+ auto& as (r->actions);
return (find (as.begin (), as.end (), ca) != as.end () &&
- match (*r.rule, false));
+ match (*r, false));
}));
if (i == e)
{
// See if we have a fallback implementation.
//
+ // See the adhoc_rule::reverse_fallback() documentation for details on
+ // what's going on here.
+ //
i = find_if (
b, e,
- [&match, ca, &t] (const adhoc_recipe& r)
+ [&match, ca, &t] (const shared_ptr<adhoc_rule>& r)
{
- // See the adhoc_rule::match() documentation for details on what's
- // going on here.
- //
- auto& as (r.actions);
- if (find (as.begin (), as.end (), ca) == as.end ())
- {
- for (auto sa: as)
- {
- optional<action> ra (r.rule->reverse_fallback (sa, t.type ()));
+ auto& as (r->actions);
- if (ra && *ra == ca && match (*r.rule, true))
- return true;
- }
- }
- return false;
+ // Note that the rule could be there but not match (see above),
+ // thus this extra check.
+ //
+ return (find (as.begin (), as.end (), ca) == as.end () &&
+ r->reverse_fallback (ca, t.type ()) &&
+ match (*r, true));
});
}
if (i != e)
- return &i->rule->rule_match;
+ return &(*i)->rule_match;
}
// If this is an outer operation (Y-for-X), then we look for rules
@@ -457,7 +451,7 @@ namespace build2
const auto& rules (i->second); // Hint map.
- // @@ TODO
+ // @@ TODO hint
//
// Different rules can be used for different operations (update vs
// test is a good example). So, at some point, we will probably have
@@ -477,14 +471,49 @@ namespace build2
for (auto i (rs.first); i != rs.second; ++i)
{
- const auto& r (*i);
- const string& n (r.first);
- const rule& ru (r.second);
+ const rule_match* r (&*i);
+
+ // In a somewhat hackish way we reuse operation wildcards to plumb
+ // the ad hoc rule's reverse operation fallback logic.
+ //
+ // The difficulty is two-fold:
+ //
+ // 1. It's difficult to add the fallback flag to the rule map
+ // because of rule_match which is used throughout.
+ //
+ // 2. Even if we could do that, we pass the reverse action to
+ // reverse_fallback() rather than it returning (a list) of
+ // reverse actions, which would be necessary to register them.
+ //
+ using fallback_rule = adhoc_rule_pattern::fallback_rule;
+
+ auto find_fallback = [mo, o, tt] (const fallback_rule& fr)
+ -> const rule_match*
+ {
+ for (const shared_ptr<adhoc_rule>& ar: fr.rules)
+ if (ar->reverse_fallback (action (mo, o), *tt))
+ return &ar->rule_match;
+
+ return nullptr;
+ };
+
+ if (oi == 0)
+ {
+ if (auto* fr =
+ dynamic_cast<const fallback_rule*> (&r->second.get ()))
+ {
+ if ((r = find_fallback (*fr)) == nullptr)
+ continue;
+ }
+ }
+
+ const string& n (r->first);
+ const rule& ru (r->second);
if (&ru == skip)
continue;
- match_extra me {false};
+ me.init (oi == 0 /* fallback */);
{
auto df = make_diag_frame (
[a, &t, &n](const diag_record& dr)
@@ -505,8 +534,20 @@ namespace build2
diag_record dr;
for (++i; i != rs.second; ++i)
{
- const string& n1 (i->first);
- const rule& ru1 (i->second);
+ const rule_match* r1 (&*i);
+
+ if (oi == 0)
+ {
+ if (auto* fr =
+ dynamic_cast<const fallback_rule*> (&r1->second.get ()))
+ {
+ if ((r1 = find_fallback (*fr)) == nullptr)
+ continue;
+ }
+ }
+
+ const string& n1 (r1->first);
+ const rule& ru1 (r1->second);
{
auto df = make_diag_frame (
@@ -523,7 +564,8 @@ namespace build2
//
// @@ Can't we temporarily swap things out in target?
//
- match_extra me1 {false};
+ match_extra me1;
+ me1.init (oi == 0);
if (!ru1.match (a, t, hint, me1))
continue;
}
@@ -539,10 +581,7 @@ namespace build2
}
if (!ambig)
- {
- t[a].match_extra = move (me);
- return &r;
- }
+ return r;
else
dr << info << "use rule hint to disambiguate this match";
}
@@ -550,6 +589,8 @@ namespace build2
}
}
+ me.free ();
+
if (!try_match)
{
diag_record dr;
@@ -624,7 +665,7 @@ namespace build2
if (const scope* rs = bs.root_scope ())
penv = auto_project_env (*rs);
- const rule& r (m.second);
+ const rule& ru (m.second);
match_extra& me (t[a].match_extra);
auto df = make_diag_frame (
@@ -635,15 +676,16 @@ namespace build2
<< diag_do (a, t);
});
- if (auto* f = (a.outer ()
- ? t.ctx.current_outer_oif
- : t.ctx.current_inner_oif)->adhoc_apply)
- {
- if (auto* ar = dynamic_cast<const adhoc_rule*> (&r))
- return f (*ar, a, t, me);
- }
+ auto* f ((a.outer ()
+ ? t.ctx.current_outer_oif
+ : t.ctx.current_inner_oif)->adhoc_apply);
+
+ auto* ar (f == nullptr ? nullptr : dynamic_cast<const adhoc_rule*> (&ru));
+
+ recipe re (ar != nullptr ? f (*ar, a, t, me) : ru.apply (a, t, me));
- return r.apply (a, t, me);
+ me.free ();
+ return re;
}
// If step is true then perform only one step of the match/apply sequence.