aboutsummaryrefslogtreecommitdiff
path: root/libbuild2
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2020-12-04 08:39:35 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2020-12-04 08:39:35 +0200
commit4168cda2363f3d796d0b9922852e249aac3131ba (patch)
tree3e74f8926ad2efe57ac8ffbeb03a8585f285e618 /libbuild2
parent864d84abcf1579b81f54d8d3f79520137d81f629 (diff)
Mark Buildfile functions as pure or impure
Diffstat (limited to 'libbuild2')
-rw-r--r--libbuild2/bin/functions.cxx4
-rw-r--r--libbuild2/cc/functions.cxx18
-rw-r--r--libbuild2/config/functions.cxx4
-rw-r--r--libbuild2/function.cxx111
-rw-r--r--libbuild2/function.hxx143
-rw-r--r--libbuild2/function.test.cxx42
-rw-r--r--libbuild2/functions-builtin.cxx30
-rw-r--r--libbuild2/functions-filesystem.cxx42
-rw-r--r--libbuild2/functions-name.cxx34
-rw-r--r--libbuild2/functions-path.cxx83
-rw-r--r--libbuild2/functions-process-path.cxx8
-rw-r--r--libbuild2/functions-process.cxx65
-rw-r--r--libbuild2/functions-project-name.cxx18
-rw-r--r--libbuild2/functions-regex.cxx36
-rw-r--r--libbuild2/functions-string.cxx30
-rw-r--r--libbuild2/functions-target-triplet.cxx12
-rw-r--r--libbuild2/install/functions.cxx4
17 files changed, 385 insertions, 299 deletions
diff --git a/libbuild2/bin/functions.cxx b/libbuild2/bin/functions.cxx
index 59fcdf2..1c6c0f4 100644
--- a/libbuild2/bin/functions.cxx
+++ b/libbuild2/bin/functions.cxx
@@ -24,12 +24,14 @@ namespace build2
// bin.lib value on. As a result, it can be omitted in which case the
// function call scope is used (covers project-local lib{} targets).
//
+ // Note that this function is not pure.
+ //
// @@ TODO: support for target (note that if it's out of project, then
// it's imported, which means it might still be qualified.)
//
// @@ TODO: support utility libraries (see link_member()).
//
- f[".link_member"] = [] (const scope* bs, names ns)
+ f.insert (".link_member", false) += [] (const scope* bs, names ns)
{
string t (convert<string> (move (ns)));
diff --git a/libbuild2/cc/functions.cxx b/libbuild2/cc/functions.cxx
index 98f7f97..c8b9d09 100644
--- a/libbuild2/cc/functions.cxx
+++ b/libbuild2/cc/functions.cxx
@@ -150,8 +150,10 @@ namespace build2
// after all the specified library targets have been matched. Normally
// it is used in ad hoc recipes to implement custom compilation.
//
+ // Note that this function is not pure.
//
- f[".lib_poptions"].insert<lib_data, names, names> (
+ f.insert (".lib_poptions", false).
+ insert<lib_data, names, names> (
&lib_thunk<appended_libraries>,
lib_data {
x,
@@ -189,8 +191,10 @@ namespace build2
// after all the specified library targets have been matched. Normally
// it is used in ad hoc recipes to implement custom linking.
//
- f[".lib_libs"].insert<lib_data,
- names, names, optional<names>, optional<names>> (
+ // Note that this function is not pure.
+ //
+ f.insert (".lib_libs", false).
+ insert<lib_data, names, names, optional<names>, optional<names>> (
&lib_thunk<appended_libraries>,
lib_data {
x,
@@ -237,13 +241,15 @@ namespace build2
//
// Note that passing multiple targets at once is not a mere convenience:
// this also allows for more effective duplicate suppression.
-
+ //
// Note also that this function can only be called during execution
// after all the specified library targets have been matched. Normally
// it is used in ad hoc recipes to implement custom linking.
//
- f[".lib_rpaths"].insert<lib_data,
- names, names, optional<names>, optional<names>> (
+ // Note that this function is not pure.
+ //
+ f.insert (".lib_rpaths", false).
+ insert<lib_data, names, names, optional<names>, optional<names>> (
&lib_thunk<rpathed_libraries>,
lib_data {
x,
diff --git a/libbuild2/config/functions.cxx b/libbuild2/config/functions.cxx
index aaf74ec..5e85238 100644
--- a/libbuild2/config/functions.cxx
+++ b/libbuild2/config/functions.cxx
@@ -27,7 +27,9 @@ namespace build2
// config module creation was requested for other meta-operations with
// config.config.module=true in bootstrap.build.
//
- f[".save"] = [] (const scope* s)
+ // Note that this function is not pure.
+ //
+ f.insert (".save", false) += [] (const scope* s)
{
if (s == nullptr)
fail << "config.save() called out of scope" << endf;
diff --git a/libbuild2/function.cxx b/libbuild2/function.cxx
index 25dacf9..eaf3f9e 100644
--- a/libbuild2/function.cxx
+++ b/libbuild2/function.cxx
@@ -80,21 +80,6 @@ namespace build2
return i != map_.end () && i->first.compare (0, n, name) == 0;
}
- auto function_map::
- insert (string name, function_overload f) -> iterator
- {
- // Sanity checks.
- //
- assert (f.arg_min <= f.arg_max &&
- f.arg_types.size () <= f.arg_max &&
- f.impl != nullptr);
-
- auto i (map_.emplace (move (name), move (f)));
-
- i->second.name = i->first.c_str ();
- return i;
- }
-
pair<value, bool> function_map::
call (const scope* base,
const string& name,
@@ -120,16 +105,17 @@ namespace build2
// See the overall function machinery description for the ranking
// semantics.
//
- auto ip (map_.equal_range (name));
+ const function_overloads* all_ovls (find (name));
size_t rank (~0);
small_vector<const function_overload*, 2> ovls;
+ if (all_ovls != nullptr)
{
size_t count (args.size ());
- for (auto it (ip.first); it != ip.second; ++it)
+ for (auto it (all_ovls->begin ()); it != all_ovls->end (); ++it)
{
- const function_overload& f (it->second);
+ const function_overload& f (*it);
// Argument count match.
//
@@ -257,8 +243,11 @@ namespace build2
dr << fail (loc) << "unmatched call to "; print_call (dr.os);
- for (auto i (ip.first); i != ip.second; ++i)
- dr << info << "candidate: " << i->second;
+ if (all_ovls != nullptr)
+ {
+ for (auto i (all_ovls->begin ()); i != all_ovls->end (); ++i)
+ dr << info << "candidate: " << *i;
+ }
// If this is an unqualified name, then also print qualified
// functions that end with this name. But skip functions that we
@@ -271,14 +260,20 @@ namespace build2
for (auto i (begin ()); i != end (); ++i)
{
const string& q (i->first);
- const function_overload& f (i->second);
- if ((f.alt_name == nullptr || f.alt_name != name) &&
- q.size () > n)
+ if (q.size () > n)
{
- size_t p (q.size () - n);
- if (q[p - 1] == '.' && q.compare (p, n, name) == 0)
- dr << info << "candidate: " << i->second;
+ for (auto j (i->second.begin ()); j != i->second.end (); ++j)
+ {
+ const function_overload& f (*j);
+
+ if (f.alt_name == nullptr || f.alt_name != name)
+ {
+ size_t p (q.size () - n);
+ if (q[p - 1] == '.' && q.compare (p, n, name) == 0)
+ dr << info << "candidate: " << f;
+ }
+ }
}
}
}
@@ -300,30 +295,8 @@ namespace build2
}
}
- value function_family::
- default_thunk (const scope* base,
- vector_view<value> args,
- const function_overload& f)
- {
- // Call the cast thunk.
- //
- struct cast_data // Prefix of function_cast::data.
- {
- value (*const thunk) (const scope*, vector_view<value>, const void*);
- };
-
- auto d (reinterpret_cast<const cast_data*> (&f.data));
- return d->thunk (base, move (args), d);
- }
-
-#if !defined(_WIN32)
- constexpr const optional<const value_type*>* function_args<>::types;
-#else
- const optional<const value_type*>* const function_args<>::types = nullptr;
-#endif
-
- void function_family::entry::
- insert (string n, function_overload f) const
+ auto function_family::
+ insert (string n, bool pure) const -> entry
{
// Figure out qualification.
//
@@ -332,31 +305,47 @@ namespace build2
if (p == string::npos)
{
- if (!qual.empty ())
+ if (!qual_.empty ())
{
- qn = qual;
+ qn = qual_;
qn += '.';
qn += n;
}
}
else if (p == 0)
{
- assert (!qual.empty ());
- n.insert (0, qual);
+ assert (!qual_.empty ());
+ n.insert (0, qual_);
}
- auto i (qn.empty () ? map_.end () : map_.insert (move (qn), f));
- auto j (map_.insert (move (n), move (f)));
+ return entry {
+ map_.insert (move (n), pure),
+ qn.empty () ? nullptr : &map_.insert (move (qn), pure),
+ thunk_};
+ }
- // If we have both, then set alternative names.
+ value function_family::
+ default_thunk (const scope* base,
+ vector_view<value> args,
+ const function_overload& f)
+ {
+ // Call the cast thunk.
//
- if (i != map_.end ())
+ struct cast_data // Prefix of function_cast::data.
{
- i->second.alt_name = j->first.c_str ();
- j->second.alt_name = i->first.c_str ();
- }
+ value (*const thunk) (const scope*, vector_view<value>, const void*);
+ };
+
+ auto d (reinterpret_cast<const cast_data*> (&f.data));
+ return d->thunk (base, move (args), d);
}
+#if !defined(_WIN32)
+ constexpr const optional<const value_type*>* function_args<>::types;
+#else
+ const optional<const value_type*>* const function_args<>::types = nullptr;
+#endif
+
// Static-initialize the function map and populate with builtin functions.
//
diff --git a/libbuild2/function.hxx b/libbuild2/function.hxx
index 43b8024..8fdf8f4 100644
--- a/libbuild2/function.hxx
+++ b/libbuild2/function.hxx
@@ -75,14 +75,15 @@ namespace build2
//
// // Register length() and string.length().
// //
- // f["length"] = &string::size;
+ // f["length"] += &string::size;
//
// // Register string.max_size().
// //
- // f[".max_size"] = []() {return string ().max_size ();};
+ // f[".max_size"] += []() {return string ().max_size ();};
//
- // For more examples/ideas, study the existing function families (reside
- // in the functions-*.cxx files).
+ // The use of += instead of = is meant to suggest that we are adding an
+ // overload. For more examples/ideas, study the existing function families
+ // (reside in the functions-*.cxx files).
//
// Note that normally there will be a function overload that has all the
// parameters untyped with an implementation that falls back to one of the
@@ -90,6 +91,12 @@ namespace build2
// from the argument value "syntax" (e.g., presence of a trailing slash for
// a directory path).
//
+ // A function is pure if for the same set of arguments it always produces
+ // the same result and has no (observable) side effects. Those functions
+ // that are not pure should be explicitly marked as such, for example:
+ //
+ // f.insert ("date", false /* pure */) += &date;
+ //
struct function_overload;
using function_impl = value (const scope*,
@@ -116,13 +123,13 @@ namespace build2
using types = vector_view<const optional<const value_type*>>;
- const size_t arg_min;
- const size_t arg_max;
- const types arg_types;
+ size_t arg_min;
+ size_t arg_max;
+ types arg_types;
// Function implementation.
//
- function_impl* const impl;
+ function_impl* impl;
// Auxiliary data storage. Note that it is expected to be trivially
// copyable and destructible.
@@ -166,18 +173,57 @@ namespace build2
LIBBUILD2_SYMEXPORT ostream&
operator<< (ostream&, const function_overload&); // Print signature.
+ struct function_overloads: small_vector<function_overload, 8>
+ {
+ const char* name; // Set to point to key by function_map::insert() below.
+ bool pure = true;
+
+ function_overload&
+ insert (function_overload f)
+ {
+ // Sanity checks.
+ //
+ assert (f.arg_min <= f.arg_max &&
+ f.arg_types.size () <= f.arg_max &&
+ f.impl != nullptr);
+
+ push_back (move (f));
+ back ().name = name;
+ return back ();
+ }
+ };
+
class LIBBUILD2_SYMEXPORT function_map
{
public:
- using map_type = std::multimap<string, function_overload>;
+ using map_type = std::map<string, function_overloads>;
using iterator = map_type::iterator;
using const_iterator = map_type::const_iterator;
- iterator
- insert (string name, function_overload);
+ function_overloads&
+ insert (string name, bool pure)
+ {
+ auto p (map_.emplace (move (name), function_overloads ()));
- void
- erase (iterator i) {map_.erase (i);}
+ function_overloads& r (p.first->second);
+
+ if (p.second)
+ {
+ r.name = p.first->first.c_str ();
+ r.pure = pure;
+ }
+ else
+ assert (r.pure == pure);
+
+ return r;
+ }
+
+ const function_overloads*
+ find (const string& name) const
+ {
+ auto i (map_.find (name));
+ return i != map_.end () ? &i->second : nullptr;
+ }
value
call (const scope* base,
@@ -263,6 +309,9 @@ namespace build2
entry
operator[] (string name) const;
+ entry
+ insert (string name, bool pure = true) const;
+
static bool
defined (function_map& map, string qual)
{
@@ -762,20 +811,18 @@ namespace build2
struct LIBBUILD2_SYMEXPORT function_family::entry
{
- function_map& map_;
- string name;
- const string& qual;
- function_impl* thunk;
+ function_overloads& overloads;
+ function_overloads* alt_overloads;
+ function_impl* thunk;
template <typename R, typename... A>
void
- operator= (R (*impl) (A...)) &&
+ operator+= (R (*impl) (A...)) const
{
using args = function_args<A...>;
using cast = function_cast_func<R, A...>;
- insert (move (name),
- function_overload (
+ insert (function_overload (
nullptr,
args::min,
args::max,
@@ -786,13 +833,12 @@ namespace build2
template <typename R, typename... A>
void
- operator= (R (*impl) (const scope*, A...)) &&
+ operator+= (R (*impl) (const scope*, A...)) const
{
using args = function_args<A...>;
using cast = function_cast_func<R, const scope*, A...>;
- insert (move (name),
- function_overload (
+ insert (function_overload (
nullptr,
args::min,
args::max,
@@ -812,20 +858,19 @@ namespace build2
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 6
template <typename L>
void
- operator= (const L&) &&
+ operator+= (const L&) const
{
- move (*this).coerce_lambda (&L::operator());
+ this->coerce_lambda (&L::operator());
}
template <typename L, typename R, typename... A>
void
- coerce_lambda (R (L::*op) (A...) const) &&
+ coerce_lambda (R (L::*op) (A...) const) const
{
using args = function_args<A...>;
using cast = function_cast_lamb<L, R, A...>;
- insert (move (name),
- function_overload (
+ insert (function_overload (
nullptr,
args::min,
args::max,
@@ -836,13 +881,12 @@ namespace build2
template <typename L, typename R, typename... A>
void
- coerce_lambda (R (L::*op) (const scope*, A...) const) &&
+ coerce_lambda (R (L::*op) (const scope*, A...) const) const
{
using args = function_args<A...>;
using cast = function_cast_lamb<L, R, const scope*, A...>;
- insert (move (name),
- function_overload (
+ insert (function_overload (
nullptr,
args::min,
args::max,
@@ -853,9 +897,9 @@ namespace build2
#else
template <typename L>
void
- operator= (const L& l) &&
+ operator+= (const L& l) const
{
- move (*this).operator= (decay_lambda (&L::operator(), l));
+ this->operator+= (decay_lambda (&L::operator(), l));
}
template <typename L, typename R, typename... A>
@@ -875,13 +919,12 @@ namespace build2
//
template <typename R, typename T>
void
- operator= (R (T::*mf) () const) &&
+ operator+= (R (T::*mf) () const) const
{
using args = function_args<T>;
using cast = function_cast_memf<R, T>;
- insert (move (name),
- function_overload (
+ insert (function_overload (
nullptr,
args::min,
args::max,
@@ -894,13 +937,12 @@ namespace build2
//
template <typename R, typename T>
void
- operator= (R T::*dm) &&
+ operator+= (R T::*dm) const
{
using args = function_args<T>;
using cast = function_cast_memd<R, T>;
- insert (move (name),
- function_overload (
+ insert (function_overload (
nullptr,
args::min,
args::max,
@@ -915,12 +957,11 @@ namespace build2
//
template <typename D, typename... A>
void
- insert (function_impl* i, D d) &&
+ insert (function_impl* i, D d) const
{
using args = function_args<A...>;
- insert (move (name),
- function_overload (
+ insert (function_overload (
nullptr,
args::min,
args::max,
@@ -931,13 +972,27 @@ namespace build2
private:
void
- insert (string, function_overload) const;
+ insert (function_overload f) const
+ {
+ function_overload* f1 (alt_overloads != nullptr
+ ? &alt_overloads->insert (f)
+ : nullptr);
+ function_overload& f2 (overloads.insert (move (f)));
+
+ // If we have both, then set alternative names.
+ //
+ if (f1 != nullptr)
+ {
+ f1->alt_name = f2.name;
+ f2.alt_name = f1->name;
+ }
+ }
};
inline auto function_family::
operator[] (string name) const -> entry
{
- return entry {map_, move (name), qual_, thunk_};
+ return insert (move (name));
}
}
diff --git a/libbuild2/function.test.cxx b/libbuild2/function.test.cxx
index 514ac1e..f8a2c16 100644
--- a/libbuild2/function.test.cxx
+++ b/libbuild2/function.test.cxx
@@ -52,38 +52,37 @@ namespace build2
function_family f (functions, "dummy");
- f["fail"] = []() {fail << "failed" << endf;};
- f["fail_arg"] = [](names a) {return convert<uint64_t> (move (a[0]));};
+ f["fail"] += []() {fail << "failed" << endf;};
+ f["fail_arg"] += [](names a) {return convert<uint64_t> (move (a[0]));};
- f["nullable"] = [](names* a) {return a == nullptr;};
- f["optional"] = [](optional<names> a) {return !a;};
+ f["nullable"] += [](names* a) {return a == nullptr;};
+ f["optional"] += [](optional<names> a) {return !a;};
- f["dummy0"] = []() {return "abc";};
- f["dummy1"] = [](string s) {return s;};
- f["dummy2"] = [](uint64_t x, uint64_t y) {return x + y;};
+ f["dummy0"] += []() {return "abc";};
+ f["dummy1"] += [](string s) {return s;};
+ f["dummy2"] += [](uint64_t x, uint64_t y) {return x + y;};
- f["ambig"] = [](names a, optional<string>) {return a;};
- f["ambig"] = [](names a, optional<uint64_t>) {return a;};
+ f["ambig"] += [](names a, optional<string>) {return a;};
+ f["ambig"] += [](names a, optional<uint64_t>) {return a;};
- f["reverse"] = [](names a) {return a;};
+ f["reverse"] += [](names a) {return a;};
- f["scoped"] = [](const scope*, names a) {return a;};
- f["scoped_void"] = [](const scope*, names) {};
- f["scoped"] = &scoped;
- f["scoped_void"] = &scoped_void;
+ f["scoped"] += [](const scope*, names a) {return a;};
+ f["scoped_void"] += [](const scope*, names) {};
+ f["scoped"] += &scoped;
+ f["scoped_void"] += &scoped_void;
- f[".qual"] = []() {return "abc";};
+ f[".qual"] += []() {return "abc";};
- f[".length"] = &path::size; // Member function.
- f[".type"] = &name::type; // Data member.
+ f[".length"] += &path::size; // Member function.
+ f[".type"] += &name::type; // Data member.
- f[".abs"] = [](dir_path d) {return d.absolute ();};
+ f[".abs"] += [](dir_path d) {return d.absolute ();};
// Variadic function with first required argument of type bool. Returns
// number of arguments passed.
//
- functions.insert (
- "variadic",
+ functions.insert ("variadic", true).insert (
function_overload (
nullptr,
1,
@@ -96,8 +95,7 @@ namespace build2
// Dump arguments.
//
- functions.insert (
- "dump",
+ functions.insert ("dump", true).insert (
function_overload (
nullptr,
0,
diff --git a/libbuild2/functions-builtin.cxx b/libbuild2/functions-builtin.cxx
index 97cc449..4689ac2 100644
--- a/libbuild2/functions-builtin.cxx
+++ b/libbuild2/functions-builtin.cxx
@@ -19,7 +19,9 @@ namespace build2
// Note that we may want to extend the scope argument to a more general
// notion of "lookup context" (scope, target, prerequisite).
//
- f["defined"] = [](const scope* s, names name)
+ // Note that this function is not pure.
+ //
+ f.insert ("defined", false) += [](const scope* s, names name)
{
if (s == nullptr)
fail << "defined() called out of scope" << endf;
@@ -29,7 +31,9 @@ namespace build2
// Return variable visibility if it has been entered and NULL otherwise.
//
- f["visibility"] = [](const scope* s, names name)
+ // Note that this function is not pure.
+ //
+ f.insert ("visibility", false) += [](const scope* s, names name)
{
if (s == nullptr)
fail << "visibility() called out of scope" << endf;
@@ -42,25 +46,25 @@ namespace build2
: nullopt);
};
- f["type"] = [](value* v) {return v->type != nullptr ? v->type->name : "";};
- f["null"] = [](value* v) {return v->null;};
- f["empty"] = [](value* v) {return v->null || v->empty ();};
+ f["type"] += [](value* v) {return v->type != nullptr ? v->type->name : "";};
+ f["null"] += [](value* v) {return v->null;};
+ f["empty"] += [](value* v) {return v->null || v->empty ();};
- f["identity"] = [](value* v) {return move (*v);};
+ f["identity"] += [](value* v) {return move (*v);};
// string
//
- f["string"] = [](bool b) {return b ? "true" : "false";};
- f["string"] = [](int64_t i) {return to_string (i);};
- f["string"] = [](uint64_t i) {return to_string (i);};
- f["string"] = [](name n) {return to_string (n);};
+ f["string"] += [](bool b) {return b ? "true" : "false";};
+ f["string"] += [](int64_t i) {return to_string (i);};
+ f["string"] += [](uint64_t i) {return to_string (i);};
+ f["string"] += [](name n) {return to_string (n);};
// Quote a value returning its string representation. If escape is true,
// then also escape (with a backslash) the quote characters being added
// (this is useful if the result will be re-parsed, for example as a
// Testscript command line).
//
- f["quote"] = [](value* v, optional<value> escape)
+ f["quote"] += [](value* v, optional<value> escape)
{
if (v->null)
return string ();
@@ -81,7 +85,9 @@ namespace build2
// Return NULL if the environment variable is not set, untyped value
// otherwise.
//
- f["getenv"] = [](names name)
+ // Note that this function is not pure.
+ //
+ f.insert ("getenv", false) += [](names name)
{
optional<string> v (getenv (convert<string> (move (name))));
diff --git a/libbuild2/functions-filesystem.cxx b/libbuild2/functions-filesystem.cxx
index 54c5315..696b1cf 100644
--- a/libbuild2/functions-filesystem.cxx
+++ b/libbuild2/functions-filesystem.cxx
@@ -78,6 +78,9 @@ namespace build2
void
filesystem_functions (function_map& m)
{
+ // @@ Maybe we should have the ability to mark the whole family as not
+ // pure?
+
function_family f (m, "filesystem");
// path_search
@@ -86,25 +89,32 @@ namespace build2
// absolute path, then the start directory is ignored (if present).
// Otherwise, the start directory must be specified and be absolute.
//
- f["path_search"] = [](path pattern, optional<dir_path> start)
+ // Note that this function is not pure.
+ //
{
- return path_search (pattern, start);
- };
+ auto e (f.insert ("path_search", false));
- f["path_search"] = [](path pattern, names start)
- {
- return path_search (pattern, convert<dir_path> (move (start)));
- };
+ e += [](path pattern, optional<dir_path> start)
+ {
+ return path_search (pattern, start);
+ };
- f["path_search"] = [](names pattern, optional<dir_path> start)
- {
- return path_search (convert<path> (move (pattern)), start);
- };
+ e += [](path pattern, names start)
+ {
+ return path_search (pattern, convert<dir_path> (move (start)));
+ };
+
+ e += [](names pattern, optional<dir_path> start)
+ {
+ return path_search (convert<path> (move (pattern)), start);
+ };
+
+ e += [](names pattern, names start)
+ {
+ return path_search (convert<path> (move (pattern)),
+ convert<dir_path> (move (start)));
+ };
+ }
- f["path_search"] = [](names pattern, names start)
- {
- return path_search (convert<path> (move (pattern)),
- convert<dir_path> (move (start)));
- };
}
}
diff --git a/libbuild2/functions-name.cxx b/libbuild2/functions-name.cxx
index 8b61c81..e821e52 100644
--- a/libbuild2/functions-name.cxx
+++ b/libbuild2/functions-name.cxx
@@ -59,11 +59,11 @@ namespace build2
//
function_family fn (m, "name");
- fn["name"] = [](const scope* s, name n)
+ fn["name"] += [](const scope* s, name n)
{
return to_target_name (s, move (n)).first.value;
};
- fn["name"] = [](const scope* s, names ns)
+ fn["name"] += [](const scope* s, names ns)
{
return to_target_name (s, convert<name> (move (ns))).first.value;
};
@@ -71,40 +71,40 @@ namespace build2
// Note: returns NULL if extension is unspecified (default) and empty if
// specified as no extension.
//
- fn["extension"] = [](const scope* s, name n)
+ fn["extension"] += [](const scope* s, name n)
{
return to_target_name (s, move (n)).second;
};
- fn["extension"] = [](const scope* s, names ns)
+ fn["extension"] += [](const scope* s, names ns)
{
return to_target_name (s, convert<name> (move (ns))).second;
};
- fn["directory"] = [](const scope* s, name n)
+ fn["directory"] += [](const scope* s, name n)
{
return to_target_name (s, move (n)).first.dir;
};
- fn["directory"] = [](const scope* s, names ns)
+ fn["directory"] += [](const scope* s, names ns)
{
return to_target_name (s, convert<name> (move (ns))).first.dir;
};
- fn["target_type"] = [](const scope* s, name n)
+ fn["target_type"] += [](const scope* s, name n)
{
return to_target_name (s, move (n)).first.type;
};
- fn["target_type"] = [](const scope* s, names ns)
+ fn["target_type"] += [](const scope* s, names ns)
{
return to_target_name (s, convert<name> (move (ns))).first.type;
};
// Note: returns NULL if no project specified.
//
- fn["project"] = [](const scope* s, name n)
+ fn["project"] += [](const scope* s, name n)
{
return to_target_name (s, move (n)).first.proj;
};
- fn["project"] = [](const scope* s, names ns)
+ fn["project"] += [](const scope* s, names ns)
{
return to_target_name (s, convert<name> (move (ns))).first.proj;
};
@@ -113,7 +113,12 @@ namespace build2
//
function_family ft (m, "target");
- ft["path"] = [](const scope* s, names ns)
+ // Note that while this function is not technically pure, we don't mark it
+ // as such since it can only be called (normally form a recipe) after the
+ // target has been matched, meaning that this target is a prerequisite and
+ // therefore this impurity has been accounted for.
+ //
+ ft["path"] += [](const scope* s, names ns)
{
if (s == nullptr)
fail << "target.path() called out of scope";
@@ -155,7 +160,10 @@ namespace build2
// This one can only be called on a single target since we don't support
// containers of process_path's (though we probably could).
//
- fn["process_path"] = [](const scope* s, names ns)
+ // Note that while this function is not technically pure, we don't mark it
+ // as such for the same reasons as $path() above.
+ //
+ fn["process_path"] += [](const scope* s, names ns)
{
if (s == nullptr)
fail << "target.process_path() called out of scope";
@@ -184,7 +192,7 @@ namespace build2
//
function_family fb (m, "builtin");
- fb[".concat"] = [](dir_path d, name n)
+ fb[".concat"] += [](dir_path d, name n)
{
d /= n.dir;
n.dir = move (d);
diff --git a/libbuild2/functions-path.cxx b/libbuild2/functions-path.cxx
index 2d5fad4..fdf712c 100644
--- a/libbuild2/functions-path.cxx
+++ b/libbuild2/functions-path.cxx
@@ -144,9 +144,9 @@ namespace build2
// string
//
- f["string"] = [](path p) {return move (p).string ();};
+ f["string"] += [](path p) {return move (p).string ();};
- f["string"] = [](paths v)
+ f["string"] += [](paths v)
{
strings r;
for (auto& p: v)
@@ -154,7 +154,7 @@ namespace build2
return r;
};
- f["string"] = [](dir_paths v)
+ f["string"] += [](dir_paths v)
{
strings r;
for (auto& p: v)
@@ -164,9 +164,9 @@ namespace build2
// representation
//
- f["representation"] = [](path p) {return move (p).representation ();};
+ f["representation"] += [](path p) {return move (p).representation ();};
- f["representation"] = [](paths v)
+ f["representation"] += [](paths v)
{
strings r;
for (auto& p: v)
@@ -174,7 +174,7 @@ namespace build2
return r;
};
- f["representation"] = [](dir_paths v)
+ f["representation"] += [](dir_paths v)
{
strings r;
for (auto& p: v)
@@ -186,24 +186,24 @@ namespace build2
//
// @@ TODO: add ability to specify alternative separator.
//
- f["canonicalize"] = [](path p) {p.canonicalize (); return p;};
- f["canonicalize"] = [](dir_path p) {p.canonicalize (); return p;};
+ f["canonicalize"] += [](path p) {p.canonicalize (); return p;};
+ f["canonicalize"] += [](dir_path p) {p.canonicalize (); return p;};
- f["canonicalize"] = [](paths v)
+ f["canonicalize"] += [](paths v)
{
for (auto& p: v)
p.canonicalize ();
return v;
};
- f["canonicalize"] = [](dir_paths v)
+ f["canonicalize"] += [](dir_paths v)
{
for (auto& p: v)
p.canonicalize ();
return v;
};
- f[".canonicalize"] = [](names ns)
+ f[".canonicalize"] += [](names ns)
{
// For each path decide based on the presence of a trailing slash
// whether it is a directory. Return as untyped list of (potentially
@@ -221,19 +221,22 @@ namespace build2
// normalize
//
- f["normalize"] = [](path p, optional<value> a)
+ // @@ TODO: normalize(true) is not pure, redo as a separate actualize()
+ // function.
+ //
+ f["normalize"] += [](path p, optional<value> a)
{
p.normalize (a && convert<bool> (move (*a)));
return p;
};
- f["normalize"] = [](dir_path p, optional<value> a)
+ f["normalize"] += [](dir_path p, optional<value> a)
{
p.normalize (a && convert<bool> (move (*a)));
return p;
};
- f["normalize"] = [](paths v, optional<value> a)
+ f["normalize"] += [](paths v, optional<value> a)
{
bool act (a && convert<bool> (move (*a)));
@@ -242,7 +245,7 @@ namespace build2
return v;
};
- f["normalize"] = [](dir_paths v, optional<value> a)
+ f["normalize"] += [](dir_paths v, optional<value> a)
{
bool act (a && convert<bool> (move (*a)));
@@ -251,7 +254,7 @@ namespace build2
return v;
};
- f[".normalize"] = [](names ns, optional<value> a)
+ f[".normalize"] += [](names ns, optional<value> a)
{
bool act (a && convert<bool> (move (*a)));
@@ -271,9 +274,9 @@ namespace build2
// directory
//
- f["directory"] = &path::directory;
+ f["directory"] += &path::directory;
- f["directory"] = [](paths v)
+ f["directory"] += [](paths v)
{
dir_paths r;
for (const path& p: v)
@@ -281,14 +284,14 @@ namespace build2
return r;
};
- f["directory"] = [](dir_paths v)
+ f["directory"] += [](dir_paths v)
{
for (dir_path& p: v)
p = p.directory ();
return v;
};
- f[".directory"] = [](names ns)
+ f[".directory"] += [](names ns)
{
// For each path decide based on the presence of a trailing slash
// whether it is a directory. Return as list of directory names.
@@ -305,23 +308,23 @@ namespace build2
// base
//
- f["base"] = &path::base;
+ f["base"] += &path::base;
- f["base"] = [](paths v)
+ f["base"] += [](paths v)
{
for (path& p: v)
p = p.base ();
return v;
};
- f["base"] = [](dir_paths v)
+ f["base"] += [](dir_paths v)
{
for (dir_path& p: v)
p = p.base ();
return v;
};
- f[".base"] = [](names ns)
+ f[".base"] += [](names ns)
{
// For each path decide based on the presence of a trailing slash
// whether it is a directory. Return as untyped list of (potentially
@@ -339,28 +342,28 @@ namespace build2
// leaf
//
- f["leaf"] = &path::leaf;
+ f["leaf"] += &path::leaf;
- f["leaf"] = [](path p, dir_path d)
+ f["leaf"] += [](path p, dir_path d)
{
return leaf (p, move (d));
};
- f["leaf"] = [](paths v, optional<dir_path> d)
+ f["leaf"] += [](paths v, optional<dir_path> d)
{
for (path& p: v)
p = leaf (p, d);
return v;
};
- f["leaf"] = [](dir_paths v, optional<dir_path> d)
+ f["leaf"] += [](dir_paths v, optional<dir_path> d)
{
for (dir_path& p: v)
p = leaf (p, d);
return v;
};
- f[".leaf"] = [](names ns, optional<dir_path> d)
+ f[".leaf"] += [](names ns, optional<dir_path> d)
{
// For each path decide based on the presence of a trailing slash
// whether it is a directory. Return as untyped list of (potentially
@@ -378,9 +381,9 @@ namespace build2
// extension
//
- f["extension"] = &extension;
+ f["extension"] += &extension;
- f[".extension"] = [](names ns)
+ f[".extension"] += [](names ns)
{
return extension (convert<path> (move (ns)));
};
@@ -415,19 +418,19 @@ namespace build2
//
// Name matching.
//
- f[".match"] = [](string name, string pattern)
+ f[".match"] += [](string name, string pattern)
{
return path_match (name, pattern);
};
// Path matching.
//
- f["match"] = [](path ent, path pat, optional<dir_path> start)
+ f["match"] += [](path ent, path pat, optional<dir_path> start)
{
return path_match (ent, pat, start);
};
- f["match"] = [](path ent, names pat, optional<names> start)
+ f["match"] += [](path ent, names pat, optional<names> start)
{
return path_match (ent,
convert<path> (move (pat)),
@@ -436,7 +439,7 @@ namespace build2
: optional<dir_path> ());
};
- f["match"] = [](names ent, path pat, optional<names> start)
+ f["match"] += [](names ent, path pat, optional<names> start)
{
return path_match (convert<path> (move (ent)),
pat,
@@ -448,7 +451,7 @@ namespace build2
// The semantics depends on the presence of the start directory or the
// first two argument syntactic representation.
//
- f[".match"] = [](names ent, names pat, optional<names> start)
+ f[".match"] += [](names ent, names pat, optional<names> start)
{
auto path_arg = [] (const names& a) -> bool
{
@@ -472,15 +475,15 @@ namespace build2
//
function_family b (m, "builtin", &path_thunk);
- b[".concat"] = &concat_path_string;
- b[".concat"] = &concat_dir_path_string;
+ b[".concat"] += &concat_path_string;
+ b[".concat"] += &concat_dir_path_string;
- b[".concat"] = [](path l, names ur)
+ b[".concat"] += [](path l, names ur)
{
return concat_path_string (move (l), convert<string> (move (ur)));
};
- b[".concat"] = [](dir_path l, names ur)
+ b[".concat"] += [](dir_path l, names ur)
{
return concat_dir_path_string (move (l), convert<string> (move (ur)));
};
diff --git a/libbuild2/functions-process-path.cxx b/libbuild2/functions-process-path.cxx
index 86d4445..199ad0d 100644
--- a/libbuild2/functions-process-path.cxx
+++ b/libbuild2/functions-process-path.cxx
@@ -16,8 +16,8 @@ namespace build2
// As discussed in value_traits<process_path>, we always have recall.
//
- f["recall"] = &process_path::recall;
- f["effect"] = [](process_path p)
+ f["recall"] += &process_path::recall;
+ f["effect"] += [](process_path p)
{
return move (p.effect.empty () ? p.recall : p.effect);
};
@@ -26,8 +26,8 @@ namespace build2
{
function_family f (m, "process_path_ex");
- f["name"] = &process_path_ex::name;
- f["checksum"] = &process_path_ex::checksum;
+ f["name"] += &process_path_ex::name;
+ f["checksum"] += &process_path_ex::checksum;
}
}
}
diff --git a/libbuild2/functions-process.cxx b/libbuild2/functions-process.cxx
index 73ca916..0870874 100644
--- a/libbuild2/functions-process.cxx
+++ b/libbuild2/functions-process.cxx
@@ -414,12 +414,14 @@ namespace build2
//
// Run builtin or external program and return trimmed stdout.
//
- f[".run"] = [](const scope* s, names args)
+ // Note that this function is not pure.
+ //
+ f.insert (".run", false) += [](const scope* s, names args)
{
return run (s, move (args));
};
- f["run"] = [](const scope* s, process_path pp)
+ f.insert ("run", false) += [](const scope* s, process_path pp)
{
return run_process (s, pp, strings ());
};
@@ -433,38 +435,41 @@ namespace build2
// (as a whole) against <pat> and, if successful, returned, optionally
// processed with <fmt>, as an element of a list.
//
- f[".run_regex"] = [](const scope* s, names a, string p, optional<string> f)
+ // Note that this function is not pure.
+ //
{
- return run_regex (s, move (a), p, f);
- };
+ auto e (f.insert (".run_regex", false));
- f[".run_regex"] = [] (const scope* s, names a, names p, optional<names> f)
- {
- return run_regex (s,
- move (a),
- convert<string> (move (p)),
- f ? convert<string> (move (*f)) : nullopt_string);
- };
+ e += [](const scope* s, names a, string p, optional<string> f)
+ {
+ return run_regex (s, move (a), p, f);
+ };
- f["run_regex"] = [](const scope* s,
- process_path pp,
- string p,
- optional<string> f)
+ e += [] (const scope* s, names a, names p, optional<names> f)
+ {
+ return run_regex (s,
+ move (a),
+ convert<string> (move (p)),
+ f ? convert<string> (move (*f)) : nullopt_string);
+ };
+ }
{
- return run_process_regex (s, pp, strings (), p, f);
- };
+ auto e (f.insert ("run_regex", false));
- f["run_regex"] = [](const scope* s,
- process_path pp,
- names p,
- optional<names> f)
- {
- return run_process_regex (s,
- pp, strings (),
- convert<string> (move (p)),
- (f
- ? convert<string> (move (*f))
- : nullopt_string));
- };
+ e += [](const scope* s, process_path pp, string p, optional<string> f)
+ {
+ return run_process_regex (s, pp, strings (), p, f);
+ };
+
+ e += [](const scope* s, process_path pp, names p, optional<names> f)
+ {
+ return run_process_regex (s,
+ pp, strings (),
+ convert<string> (move (p)),
+ (f
+ ? convert<string> (move (*f))
+ : nullopt_string));
+ };
+ }
}
}
diff --git a/libbuild2/functions-project-name.cxx b/libbuild2/functions-project-name.cxx
index 86206b0..145e62c 100644
--- a/libbuild2/functions-project-name.cxx
+++ b/libbuild2/functions-project-name.cxx
@@ -13,46 +13,46 @@ namespace build2
{
function_family f (m, "project_name");
- f["string"] = [](project_name p) {return move (p).string ();};
+ f["string"] += [](project_name p) {return move (p).string ();};
- f["base"] = [](project_name p, optional<string> ext)
+ f["base"] += [](project_name p, optional<string> ext)
{
return ext ? p.base (ext->c_str ()) : p.base ();
};
- f["base"] = [](project_name p, names ext)
+ f["base"] += [](project_name p, names ext)
{
return p.base (convert<string> (move (ext)).c_str ());
};
- f["extension"] = &project_name::extension;
- f["variable"] = &project_name::variable;
+ f["extension"] += &project_name::extension;
+ f["variable"] += &project_name::variable;
// Project name-specific overloads from builtins.
//
function_family b (m, "builtin");
- b[".concat"] = [](project_name n, string s)
+ b[".concat"] += [](project_name n, string s)
{
string r (move (n).string ());
r += s;
return r;
};
- b[".concat"] = [](string s, project_name n)
+ b[".concat"] += [](string s, project_name n)
{
s += n.string ();
return s;
};
- b[".concat"] = [](project_name n, names ns)
+ b[".concat"] += [](project_name n, names ns)
{
string r (move (n).string ());
r += convert<string> (move (ns));
return r;
};
- b[".concat"] = [](names ns, project_name n)
+ b[".concat"] += [](names ns, project_name n)
{
string r (convert<string> (move (ns)));
r += n.string ();
diff --git a/libbuild2/functions-regex.cxx b/libbuild2/functions-regex.cxx
index a59d808..46d71ba 100644
--- a/libbuild2/functions-regex.cxx
+++ b/libbuild2/functions-regex.cxx
@@ -521,12 +521,12 @@ namespace build2
// sub-strings that match the marked sub-expressions and
// NULL if no match
//
- f[".match"] = [](value s, string re, optional<names> flags)
+ f[".match"] += [](value s, string re, optional<names> flags)
{
return match (move (s), re, move (flags));
};
- f[".match"] = [](value s, names re, optional<names> flags)
+ f[".match"] += [](value s, names re, optional<names> flags)
{
return match (move (s), convert<string> (move (re)), move (flags));
};
@@ -540,12 +540,12 @@ namespace build2
//
// icase - match ignoring case
//
- f[".find_match"] = [](names s, string re, optional<names> flags)
+ f[".find_match"] += [](names s, string re, optional<names> flags)
{
return find_match (move (s), re, move (flags));
};
- f[".find_match"] = [](names s, names re, optional<names> flags)
+ f[".find_match"] += [](names s, names re, optional<names> flags)
{
return find_match (move (s), convert<string> (move (re)), move (flags));
};
@@ -573,12 +573,12 @@ namespace build2
// If both return_match and return_subs flags are specified then the
// sub-string that matches the whole regular expression comes first.
//
- f[".search"] = [](value s, string re, optional<names> flags)
+ f[".search"] += [](value s, string re, optional<names> flags)
{
return search (move (s), re, move (flags));
};
- f[".search"] = [](value s, names re, optional<names> flags)
+ f[".search"] += [](value s, names re, optional<names> flags)
{
return search (move (s), convert<string> (move (re)), move (flags));
};
@@ -593,12 +593,12 @@ namespace build2
//
// icase - match ignoring case
//
- f[".find_search"] = [](names s, string re, optional<names> flags)
+ f[".find_search"] += [](names s, string re, optional<names> flags)
{
return find_search (move (s), re, move (flags));
};
- f[".find_search"] = [](names s, names re, optional<names> flags)
+ f[".find_search"] += [](names s, names re, optional<names> flags)
{
return find_search (move (s),
convert<string> (move (re)),
@@ -625,12 +625,12 @@ namespace build2
// If both format_first_only and format_no_copy flags are specified then
// the result will only contain the replacement of the first match.
//
- f[".replace"] = [](value s, string re, string fmt, optional<names> flags)
+ f[".replace"] += [](value s, string re, string fmt, optional<names> flags)
{
return replace (move (s), re, fmt, move (flags));
};
- f[".replace"] = [](value s, names re, names fmt, optional<names> flags)
+ f[".replace"] += [](value s, names re, names fmt, optional<names> flags)
{
return replace (move (s),
convert<string> (move (re)),
@@ -658,7 +658,7 @@ namespace build2
// Note that if format_no_copy is specified, unmatched lines are not
// copied either.
//
- f[".replace_lines"] = [](value s,
+ f[".replace_lines"] += [](value s,
string re,
string fmt,
optional<names> flags)
@@ -666,7 +666,7 @@ namespace build2
return replace_lines (move (s), re, move (fmt), move (flags));
};
- f[".replace_lines"] = [](value s,
+ f[".replace_lines"] += [](value s,
names re,
names* fmt,
optional<names> flags)
@@ -695,12 +695,12 @@ namespace build2
//
// format_no_copy - do not copy unmatched value parts into the result
//
- f[".split"] = [](value s, string re, string fmt, optional<names> flags)
+ f[".split"] += [](value s, string re, string fmt, optional<names> flags)
{
return split (move (s), re, fmt, move (flags));
};
- f[".split"] = [](value s, names re, names fmt, optional<names> flags)
+ f[".split"] += [](value s, names re, names fmt, optional<names> flags)
{
return split (move (s),
convert<string> (move (re)),
@@ -730,7 +730,7 @@ namespace build2
// the result will be a concatenation of only the first match
// replacements.
//
- f[".merge"] = [](names s,
+ f[".merge"] += [](names s,
string re,
string fmt,
optional<string> delim,
@@ -739,7 +739,7 @@ namespace build2
return merge (move (s), re, fmt, move (delim), move (flags));
};
- f[".merge"] = [](names s,
+ f[".merge"] += [](names s,
names re,
names fmt,
optional<names> delim,
@@ -775,12 +775,12 @@ namespace build2
// the result elements will only contain the replacement of the first
// match.
//
- f[".apply"] = [](names s, string re, string fmt, optional<names> flags)
+ f[".apply"] += [](names s, string re, string fmt, optional<names> flags)
{
return apply (move (s), re, fmt, move (flags));
};
- f[".apply"] = [](names s, names re, names fmt, optional<names> flags)
+ f[".apply"] += [](names s, names re, names fmt, optional<names> flags)
{
return apply (move (s),
convert<string> (move (re)),
diff --git a/libbuild2/functions-string.cxx b/libbuild2/functions-string.cxx
index c5cb216..b430ebf 100644
--- a/libbuild2/functions-string.cxx
+++ b/libbuild2/functions-string.cxx
@@ -13,31 +13,31 @@ namespace build2
{
function_family f (m, "string");
- f["string"] = [](string s) {return s;};
+ f["string"] += [](string s) {return s;};
// @@ Shouldn't it concatenate elements into the single string?
// @@ Doesn't seem to be used so far. Can consider removing.
//
- // f["string"] = [](strings v) {return v;};
+ // f["string"] += [](strings v) {return v;};
// Compare ASCII strings ignoring case and returning the boolean value.
//
- f["icasecmp"] = [](string x, string y)
+ f["icasecmp"] += [](string x, string y)
{
return icasecmp (x, y) == 0;
};
- f["icasecmp"] = [](string x, names y)
+ f["icasecmp"] += [](string x, names y)
{
return icasecmp (x, convert<string> (move (y))) == 0;
};
- f["icasecmp"] = [](names x, string y)
+ f["icasecmp"] += [](names x, string y)
{
return icasecmp (convert<string> (move (x)), y) == 0;
};
- f[".icasecmp"] = [](names x, names y)
+ f[".icasecmp"] += [](names x, names y)
{
return icasecmp (convert<string> (move (x)),
convert<string> (move (y))) == 0;
@@ -45,34 +45,34 @@ namespace build2
// Trim.
//
- f["trim"] = [](string s)
+ f["trim"] += [](string s)
{
return trim (move (s));
};
- f[".trim"] = [](names s)
+ f[".trim"] += [](names s)
{
return names {name (trim (convert<string> (move (s))))};
};
// Convert ASCII strings into lower/upper case.
//
- f["lcase"] = [](string s)
+ f["lcase"] += [](string s)
{
return lcase (move (s));
};
- f[".lcase"] = [](names s)
+ f[".lcase"] += [](names s)
{
return names {name (lcase (convert<string> (move (s))))};
};
- f["ucase"] = [](string s)
+ f["ucase"] += [](string s)
{
return ucase (move (s));
};
- f[".ucase"] = [](names s)
+ f[".ucase"] += [](names s)
{
return names {name (ucase (convert<string> (move (s))))};
};
@@ -81,15 +81,15 @@ namespace build2
//
function_family b (m, "builtin");
- b[".concat"] = [](string l, string r) {l += r; return l;};
+ b[".concat"] += [](string l, string r) {l += r; return l;};
- b[".concat"] = [](string l, names ur)
+ b[".concat"] += [](string l, names ur)
{
l += convert<string> (move (ur));
return l;
};
- b[".concat"] = [](names ul, string r)
+ b[".concat"] += [](names ul, string r)
{
string l (convert<string> (move (ul)));
l += r;
diff --git a/libbuild2/functions-target-triplet.cxx b/libbuild2/functions-target-triplet.cxx
index ff9a15d..4b0ec02 100644
--- a/libbuild2/functions-target-triplet.cxx
+++ b/libbuild2/functions-target-triplet.cxx
@@ -13,22 +13,22 @@ namespace build2
{
function_family f (m, "target_triplet");
- f["string"] = [](target_triplet t) {return t.string ();};
- f["representation"] = [](target_triplet t) {return t.representation ();};
+ f["string"] += [](target_triplet t) {return t.string ();};
+ f["representation"] += [](target_triplet t) {return t.representation ();};
// Target triplet-specific overloads from builtins.
//
function_family b (m, "builtin");
- b[".concat"] = [](target_triplet l, string sr) {return l.string () + sr;};
- b[".concat"] = [](string sl, target_triplet r) {return sl + r.string ();};
+ b[".concat"] += [](target_triplet l, string sr) {return l.string () + sr;};
+ b[".concat"] += [](string sl, target_triplet r) {return sl + r.string ();};
- b[".concat"] = [](target_triplet l, names ur)
+ b[".concat"] += [](target_triplet l, names ur)
{
return l.string () + convert<string> (move (ur));
};
- b[".concat"] = [](names ul, target_triplet r)
+ b[".concat"] += [](names ul, target_triplet r)
{
return convert<string> (move (ul)) + r.string ();
};
diff --git a/libbuild2/install/functions.cxx b/libbuild2/install/functions.cxx
index c780061..5668efe 100644
--- a/libbuild2/install/functions.cxx
+++ b/libbuild2/install/functions.cxx
@@ -18,7 +18,9 @@ namespace build2
// Resolve potentially relative install.* value to an absolute directory
// based on (other) install.* values visible from the calling scope.
//
- f[".resolve"] = [] (const scope* s, dir_path d)
+ // Note that this function is not pure.
+ //
+ f.insert (".resolve", false) += [] (const scope* s, dir_path d)
{
if (s == nullptr)
fail << "install.resolve() called out of scope" << endf;