From 4168cda2363f3d796d0b9922852e249aac3131ba Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 4 Dec 2020 08:39:35 +0200 Subject: Mark Buildfile functions as pure or impure --- libbuild2/bin/functions.cxx | 4 +- libbuild2/cc/functions.cxx | 18 +++-- libbuild2/config/functions.cxx | 4 +- libbuild2/function.cxx | 111 ++++++++++++------------- libbuild2/function.hxx | 143 +++++++++++++++++++++++---------- libbuild2/function.test.cxx | 42 +++++----- libbuild2/functions-builtin.cxx | 30 ++++--- libbuild2/functions-filesystem.cxx | 42 ++++++---- libbuild2/functions-name.cxx | 34 +++++--- libbuild2/functions-path.cxx | 83 ++++++++++--------- libbuild2/functions-process-path.cxx | 8 +- libbuild2/functions-process.cxx | 65 ++++++++------- libbuild2/functions-project-name.cxx | 18 ++--- libbuild2/functions-regex.cxx | 36 ++++----- libbuild2/functions-string.cxx | 30 +++---- libbuild2/functions-target-triplet.cxx | 12 +-- libbuild2/install/functions.cxx | 4 +- 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 (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 ( + f.insert (".lib_poptions", false). + insert ( &lib_thunk, 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, optional> ( + // Note that this function is not pure. + // + f.insert (".lib_libs", false). + insert, optional> ( &lib_thunk, 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, optional> ( + // Note that this function is not pure. + // + f.insert (".lib_rpaths", false). + insert, optional> ( &lib_thunk, 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 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 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 args, - const function_overload& f) - { - // Call the cast thunk. - // - struct cast_data // Prefix of function_cast::data. - { - value (*const thunk) (const scope*, vector_view, const void*); - }; - - auto d (reinterpret_cast (&f.data)); - return d->thunk (base, move (args), d); - } - -#if !defined(_WIN32) - constexpr const optional* function_args<>::types; -#else - const optional* 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 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, const void*); + }; + + auto d (reinterpret_cast (&f.data)); + return d->thunk (base, move (args), d); } +#if !defined(_WIN32) + constexpr const optional* function_args<>::types; +#else + const optional* 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 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 + { + 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; + using map_type = std::map; 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 void - operator= (R (*impl) (A...)) && + operator+= (R (*impl) (A...)) const { using args = function_args; using cast = function_cast_func; - insert (move (name), - function_overload ( + insert (function_overload ( nullptr, args::min, args::max, @@ -786,13 +833,12 @@ namespace build2 template void - operator= (R (*impl) (const scope*, A...)) && + operator+= (R (*impl) (const scope*, A...)) const { using args = function_args; using cast = function_cast_func; - 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 void - operator= (const L&) && + operator+= (const L&) const { - move (*this).coerce_lambda (&L::operator()); + this->coerce_lambda (&L::operator()); } template void - coerce_lambda (R (L::*op) (A...) const) && + coerce_lambda (R (L::*op) (A...) const) const { using args = function_args; using cast = function_cast_lamb; - insert (move (name), - function_overload ( + insert (function_overload ( nullptr, args::min, args::max, @@ -836,13 +881,12 @@ namespace build2 template void - coerce_lambda (R (L::*op) (const scope*, A...) const) && + coerce_lambda (R (L::*op) (const scope*, A...) const) const { using args = function_args; using cast = function_cast_lamb; - insert (move (name), - function_overload ( + insert (function_overload ( nullptr, args::min, args::max, @@ -853,9 +897,9 @@ namespace build2 #else template 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 @@ -875,13 +919,12 @@ namespace build2 // template void - operator= (R (T::*mf) () const) && + operator+= (R (T::*mf) () const) const { using args = function_args; using cast = function_cast_memf; - insert (move (name), - function_overload ( + insert (function_overload ( nullptr, args::min, args::max, @@ -894,13 +937,12 @@ namespace build2 // template void - operator= (R T::*dm) && + operator+= (R T::*dm) const { using args = function_args; using cast = function_cast_memd; - insert (move (name), - function_overload ( + insert (function_overload ( nullptr, args::min, args::max, @@ -915,12 +957,11 @@ namespace build2 // template void - insert (function_impl* i, D d) && + insert (function_impl* i, D d) const { using args = function_args; - 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 (move (a[0]));}; + f["fail"] += []() {fail << "failed" << endf;}; + f["fail_arg"] += [](names a) {return convert (move (a[0]));}; - f["nullable"] = [](names* a) {return a == nullptr;}; - f["optional"] = [](optional a) {return !a;}; + f["nullable"] += [](names* a) {return a == nullptr;}; + f["optional"] += [](optional 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) {return a;}; - f["ambig"] = [](names a, optional) {return a;}; + f["ambig"] += [](names a, optional) {return a;}; + f["ambig"] += [](names a, optional) {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 escape) + f["quote"] += [](value* v, optional 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 v (getenv (convert (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 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 (move (start))); - }; + e += [](path pattern, optional start) + { + return path_search (pattern, start); + }; - f["path_search"] = [](names pattern, optional start) - { - return path_search (convert (move (pattern)), start); - }; + e += [](path pattern, names start) + { + return path_search (pattern, convert (move (start))); + }; + + e += [](names pattern, optional start) + { + return path_search (convert (move (pattern)), start); + }; + + e += [](names pattern, names start) + { + return path_search (convert (move (pattern)), + convert (move (start))); + }; + } - f["path_search"] = [](names pattern, names start) - { - return path_search (convert (move (pattern)), - convert (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 (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 (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 (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 (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 (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 a) + // @@ TODO: normalize(true) is not pure, redo as a separate actualize() + // function. + // + f["normalize"] += [](path p, optional a) { p.normalize (a && convert (move (*a))); return p; }; - f["normalize"] = [](dir_path p, optional a) + f["normalize"] += [](dir_path p, optional a) { p.normalize (a && convert (move (*a))); return p; }; - f["normalize"] = [](paths v, optional a) + f["normalize"] += [](paths v, optional a) { bool act (a && convert (move (*a))); @@ -242,7 +245,7 @@ namespace build2 return v; }; - f["normalize"] = [](dir_paths v, optional a) + f["normalize"] += [](dir_paths v, optional a) { bool act (a && convert (move (*a))); @@ -251,7 +254,7 @@ namespace build2 return v; }; - f[".normalize"] = [](names ns, optional a) + f[".normalize"] += [](names ns, optional a) { bool act (a && convert (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 d) + f["leaf"] += [](paths v, optional d) { for (path& p: v) p = leaf (p, d); return v; }; - f["leaf"] = [](dir_paths v, optional d) + f["leaf"] += [](dir_paths v, optional d) { for (dir_path& p: v) p = leaf (p, d); return v; }; - f[".leaf"] = [](names ns, optional d) + f[".leaf"] += [](names ns, optional 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 (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 start) + f["match"] += [](path ent, path pat, optional start) { return path_match (ent, pat, start); }; - f["match"] = [](path ent, names pat, optional start) + f["match"] += [](path ent, names pat, optional start) { return path_match (ent, convert (move (pat)), @@ -436,7 +439,7 @@ namespace build2 : optional ()); }; - f["match"] = [](names ent, path pat, optional start) + f["match"] += [](names ent, path pat, optional start) { return path_match (convert (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 start) + f[".match"] += [](names ent, names pat, optional 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 (move (ur))); }; - b[".concat"] = [](dir_path l, names ur) + b[".concat"] += [](dir_path l, names ur) { return concat_dir_path_string (move (l), convert (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, 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 and, if successful, returned, optionally // processed with , as an element of a list. // - f[".run_regex"] = [](const scope* s, names a, string p, optional 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 f) - { - return run_regex (s, - move (a), - convert (move (p)), - f ? convert (move (*f)) : nullopt_string); - }; + e += [](const scope* s, names a, string p, optional f) + { + return run_regex (s, move (a), p, f); + }; - f["run_regex"] = [](const scope* s, - process_path pp, - string p, - optional f) + e += [] (const scope* s, names a, names p, optional f) + { + return run_regex (s, + move (a), + convert (move (p)), + f ? convert (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 f) - { - return run_process_regex (s, - pp, strings (), - convert (move (p)), - (f - ? convert (move (*f)) - : nullopt_string)); - }; + e += [](const scope* s, process_path pp, string p, optional f) + { + return run_process_regex (s, pp, strings (), p, f); + }; + + e += [](const scope* s, process_path pp, names p, optional f) + { + return run_process_regex (s, + pp, strings (), + convert (move (p)), + (f + ? convert (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 ext) + f["base"] += [](project_name p, optional 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 (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 (move (ns)); return r; }; - b[".concat"] = [](names ns, project_name n) + b[".concat"] += [](names ns, project_name n) { string r (convert (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 flags) + f[".match"] += [](value s, string re, optional flags) { return match (move (s), re, move (flags)); }; - f[".match"] = [](value s, names re, optional flags) + f[".match"] += [](value s, names re, optional flags) { return match (move (s), convert (move (re)), move (flags)); }; @@ -540,12 +540,12 @@ namespace build2 // // icase - match ignoring case // - f[".find_match"] = [](names s, string re, optional flags) + f[".find_match"] += [](names s, string re, optional flags) { return find_match (move (s), re, move (flags)); }; - f[".find_match"] = [](names s, names re, optional flags) + f[".find_match"] += [](names s, names re, optional flags) { return find_match (move (s), convert (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 flags) + f[".search"] += [](value s, string re, optional flags) { return search (move (s), re, move (flags)); }; - f[".search"] = [](value s, names re, optional flags) + f[".search"] += [](value s, names re, optional flags) { return search (move (s), convert (move (re)), move (flags)); }; @@ -593,12 +593,12 @@ namespace build2 // // icase - match ignoring case // - f[".find_search"] = [](names s, string re, optional flags) + f[".find_search"] += [](names s, string re, optional flags) { return find_search (move (s), re, move (flags)); }; - f[".find_search"] = [](names s, names re, optional flags) + f[".find_search"] += [](names s, names re, optional flags) { return find_search (move (s), convert (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 flags) + f[".replace"] += [](value s, string re, string fmt, optional flags) { return replace (move (s), re, fmt, move (flags)); }; - f[".replace"] = [](value s, names re, names fmt, optional flags) + f[".replace"] += [](value s, names re, names fmt, optional flags) { return replace (move (s), convert (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 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 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 flags) + f[".split"] += [](value s, string re, string fmt, optional flags) { return split (move (s), re, fmt, move (flags)); }; - f[".split"] = [](value s, names re, names fmt, optional flags) + f[".split"] += [](value s, names re, names fmt, optional flags) { return split (move (s), convert (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 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 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 flags) + f[".apply"] += [](names s, string re, string fmt, optional flags) { return apply (move (s), re, fmt, move (flags)); }; - f[".apply"] = [](names s, names re, names fmt, optional flags) + f[".apply"] += [](names s, names re, names fmt, optional flags) { return apply (move (s), convert (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 (move (y))) == 0; }; - f["icasecmp"] = [](names x, string y) + f["icasecmp"] += [](names x, string y) { return icasecmp (convert (move (x)), y) == 0; }; - f[".icasecmp"] = [](names x, names y) + f[".icasecmp"] += [](names x, names y) { return icasecmp (convert (move (x)), convert (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 (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 (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 (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 (move (ur)); return l; }; - b[".concat"] = [](names ul, string r) + b[".concat"] += [](names ul, string r) { string l (convert (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 (move (ur)); }; - b[".concat"] = [](names ul, target_triplet r) + b[".concat"] += [](names ul, target_triplet r) { return convert (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; -- cgit v1.1