From 62e3c70ca0e830e1c29ce0bd09adcebdf41c610d Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 23 Sep 2022 10:34:36 +0200 Subject: Add $is_a(, ), $filter[_out](, ) functions $is_a() returns true if the 's target type is-a . Note that this is a dynamic type check that takes into account target type inheritance. $filter[_out]() return names with target types which are-a (filter) or not are-a (filter_out) one of . In particular, these functions are useful for filtering prerequisite targets ($<) in ad hoc recipes and rules. --- libbuild2/functions-name.cxx | 130 +++++++++++++++++++++++++++++++++++++---- tests/function/name/testscript | 36 ++++++++++++ 2 files changed, 154 insertions(+), 12 deletions(-) diff --git a/libbuild2/functions-name.cxx b/libbuild2/functions-name.cxx index e5bb366..43de039 100644 --- a/libbuild2/functions-name.cxx +++ b/libbuild2/functions-name.cxx @@ -20,25 +20,26 @@ namespace build2 // out of scope). See scope::find_target_type() for details. Allow out- // qualified names (out is discarded). // - static pair> - to_target_name (const scope* s, name&& n, const name& o = name ()) + static pair> + to_target_type (const scope* s, name& n, const name& o = name ()) { if (n.pair && !o.directory ()) fail << "name pair in names"; - optional e; - - if (s != nullptr) - { - auto rp (s->find_target_type (n, location ())); + return s != nullptr + ? s->find_target_type (n, location ()) + : pair> {nullptr, nullopt}; + } - if (rp.first != nullptr) - n.type = rp.first->name; + static pair> + to_target_name (const scope* s, name&& n, const name& o = name ()) + { + auto rp (to_target_type (s, n, o)); - e = move (rp.second); - } + if (rp.first != nullptr) + n.type = rp.first->name; - return make_pair (move (n), move (e)); + return make_pair (move (n), move (rp.second)); } const target& @@ -61,6 +62,72 @@ namespace build2 return to_target (s, move (ns[0]), move (ns[0].pair ? ns[1] : o)); } + static bool + is_a (const scope* s, name&& n, const name& o, names&& t) + { + if (s == nullptr) + fail << "name.is_a() called out of scope"; + + string tts (convert (move (t))); + const target_type* tt (s->find_target_type (tts)); + if (tt == nullptr) + fail << "unknown target type " << tts; + + const target_type* ntt (to_target_type (s, n, o).first); + if (ntt == nullptr) + fail << "unknown target type " << n.type << " in " << n; + + return ntt->is_a (*tt); + } + + static names + filter (const scope* s, names ns, names ts, bool out) + { + if (s == nullptr) + fail << "name." << (out ? "filter_out" : "filter") + << "() called out of scope"; + + small_vector tts; + for (const name& n: ts) + { + if (!n.simple ()) + fail << "invalid target type name " << n; + + if (n.pair) + fail << "pair in target type name " << n; + + const target_type* tt (s->find_target_type (n.value)); + if (tt == nullptr) + fail << "unknown target type " << n.value; + + tts.push_back (tt); + } + + names r; + for (auto i (ns.begin ()); i != ns.end (); ++i) + { + name& n (*i); + bool p (n.pair); + + const target_type* ntt (to_target_type (s, n, p ? *++i : name ()).first); + if (ntt == nullptr) + fail << "unknown target type " << n.type << " in " << n; + + if ((find_if (tts.begin (), tts.end (), + [ntt] (const target_type* tt) + { + return ntt->is_a (*tt); + }) != tts.end ()) != out) + { + r.push_back (move (n)); + if (p) + r.push_back (move (*i)); + } + } + + return r; + } + void name_functions (function_map& m) { @@ -186,6 +253,45 @@ namespace build2 return to_target_name (s, move (n), o).first.proj; }; + // $is_a(, ) + // + // Return true if the 's target type is-a . Note that + // this is a dynamic type check that takes into account target type + // inheritance. + // + fn["is_a"] += [](const scope* s, name n, names t) + { + return is_a (s, move (n), name (), move (t)); + }; + fn["is_a"] += [](const scope* s, names ns, names t) + { + auto i (ns.begin ()); + + name& n (*i); + const name& o (n.pair ? *++i : name ()); + + if (++i != ns.end ()) + fail << "invalid name value: multiple names"; // Like in convert(). + + return is_a (s, move (n), o, move (t)); + }; + + // $filter(, ) + // $filter_out(, ) + // + // Return names with target types which are-a (filter) or not are-a + // (filter_out) one of . See $is_a() for background. + // + fn["filter"] += [](const scope* s, names ns, names ts) + { + return filter (s, move (ns), move (ts), false /* out */); + }; + + fn["filter_out"] += [](const scope* s, names ns, names ts) + { + return filter (s, move (ns), move (ts), true /* out */); + }; + // $size() // // Return the number of elements in the sequence. diff --git a/tests/function/name/testscript b/tests/function/name/testscript index 2fe8e24..6222167 100644 --- a/tests/function/name/testscript +++ b/tests/function/name/testscript @@ -3,6 +3,42 @@ .include ../../common.testscript +: is_a +: +{ + $* <'print $is_a(file{foo}, path_target)' >'true' : basics-true + $* <'print $is_a(alias{foo}, path_target)' >'false' : basics-false + $* <'print $is_a(file{foo}@./, path_target)' >'true' : out + $* <'true' : derived + define txt: file + print $is_a(txt{foo}, path_target) + EOI +} + +: filter +: +{ + $* <'file{foo}@./ txt{baz}' : basics + define txt: file + print $filter(file{foo}@./ alias{bar} dir{./} txt{baz}, file) + EOI + + $* <'file{foo}@./ txt{baz}' : basics-out + define txt: file + print $filter_out(file{foo}@./ alias{bar} dir{./} txt{baz}, alias) + EOI + + $* <'file{foo}@./ dir{./} txt{baz}' : multiple + define txt: file + print $filter(file{foo}@./ alias{bar} dir{./} txt{baz}, file dir) + EOI + + $* <'file{foo}@./ alias{bar}' : multiple-out + define txt: file + print $filter_out(file{foo}@./ alias{bar} dir{./} txt{baz}, txt dir) + EOI +} + : size : { -- cgit v1.1