aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2022-09-23 10:34:36 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2022-09-23 10:34:36 +0200
commit62e3c70ca0e830e1c29ce0bd09adcebdf41c610d (patch)
tree13f775d29657404ffd111fb3a9d5eebe15eae4d6
parent7d137fd6a9ceb54574481082e9944de168b06b78 (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.cxx130
-rw-r--r--tests/function/name/testscript36
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
:
{