diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2022-09-23 10:34:36 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2022-09-23 10:34:36 +0200 |
commit | 62e3c70ca0e830e1c29ce0bd09adcebdf41c610d (patch) | |
tree | 13f775d29657404ffd111fb3a9d5eebe15eae4d6 | |
parent | 7d137fd6a9ceb54574481082e9944de168b06b78 (diff) |
Add $is_a(<name>, <target-type>), $filter[_out](<names>, <target-types>) functions
$is_a() returns true if the <name>'s target type is-a <target-type>. 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 <target-types>.
In particular, these functions are useful for filtering prerequisite
targets ($<) in ad hoc recipes and rules.
-rw-r--r-- | libbuild2/functions-name.cxx | 130 | ||||
-rw-r--r-- | 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<name, optional<string>> - to_target_name (const scope* s, name&& n, const name& o = name ()) + static pair<const target_type*, optional<string>> + to_target_type (const scope* s, name& n, const name& o = name ()) { if (n.pair && !o.directory ()) fail << "name pair in names"; - optional<string> e; - - if (s != nullptr) - { - auto rp (s->find_target_type (n, location ())); + return s != nullptr + ? s->find_target_type (n, location ()) + : pair<const target_type*, optional<string>> {nullptr, nullopt}; + } - if (rp.first != nullptr) - n.type = rp.first->name; + static pair<name, optional<string>> + 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<string> (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<const target_type*, 1> 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(<name>, <target-type>) + // + // Return true if the <name>'s target type is-a <target-type>. 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(<names>, <target-types>) + // $filter_out(<names>, <target-types>) + // + // Return names with target types which are-a (filter) or not are-a + // (filter_out) one of <target-types>. 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(<names>) // // 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 + $* <<EOI >'true' : derived + define txt: file + print $is_a(txt{foo}, path_target) + EOI +} + +: filter +: +{ + $* <<EOI >'file{foo}@./ txt{baz}' : basics + define txt: file + print $filter(file{foo}@./ alias{bar} dir{./} txt{baz}, file) + EOI + + $* <<EOI >'file{foo}@./ txt{baz}' : basics-out + define txt: file + print $filter_out(file{foo}@./ alias{bar} dir{./} txt{baz}, alias) + EOI + + $* <<EOI >'file{foo}@./ dir{./} txt{baz}' : multiple + define txt: file + print $filter(file{foo}@./ alias{bar} dir{./} txt{baz}, file dir) + EOI + + $* <<EOI >'file{foo}@./ alias{bar}' : multiple-out + define txt: file + print $filter_out(file{foo}@./ alias{bar} dir{./} txt{baz}, txt dir) + EOI +} + : size : { |