From 921c2d503a63f4769fdf1c7e6eb31be2706f9bea Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 15 Oct 2020 14:20:44 +0200 Subject: Add ability to specify fallback value for NULL substitutions with in.null --- doc/manual.cli | 13 +++++++++++++ libbuild2/bash/rule.cxx | 5 +++-- libbuild2/bash/rule.hxx | 3 ++- libbuild2/in/init.cxx | 5 +++++ libbuild2/in/rule.cxx | 45 +++++++++++++++++++++++++++++++++++---------- libbuild2/in/rule.hxx | 13 +++++++++---- libbuild2/version/rule.cxx | 6 ++++-- libbuild2/version/rule.hxx | 3 ++- 8 files changed, 73 insertions(+), 20 deletions(-) diff --git a/doc/manual.cli b/doc/manual.cli index 59084d6..0cd5a5e 100644 --- a/doc/manual.cli +++ b/doc/manual.cli @@ -8156,6 +8156,19 @@ target-specific variables. Typed variable values are converted to string using the corresponding \c{builtin.string()} function overload before substitution. +Using an undefined variable in a substitution is an error. Using a \c{null} +value in a substitution is also an error unless the fallback value is +specified with the \c{in.null} variable. For example: + +\ +# buildfile + +h{config}: in{config} +{ + in.null = '' # Substitute null values with empty string. +} +\ + A number of other build system modules, for example, \l{#module-version \c{version}} and \l{#module-bash \c{bash}}, are based on the \c{in} module and provide extended functionality. The \c{in} preprocessing rule matches any diff --git a/libbuild2/bash/rule.cxx b/libbuild2/bash/rule.cxx index 148da95..9fc89d7 100644 --- a/libbuild2/bash/rule.cxx +++ b/libbuild2/bash/rule.cxx @@ -195,11 +195,12 @@ namespace build2 action a, const target& t, const string& n, - bool strict) const + bool strict, + const optional& null) const { return n.compare (0, 6, "import") == 0 && (n[6] == ' ' || n[6] == '\t') ? substitute_import (l, a, t, trim (string (n, 7))) - : rule::substitute (l, a, t, n, strict); + : rule::substitute (l, a, t, n, strict, null); } string in_rule:: diff --git a/libbuild2/bash/rule.hxx b/libbuild2/bash/rule.hxx index 0774b5f..81c4604 100644 --- a/libbuild2/bash/rule.hxx +++ b/libbuild2/bash/rule.hxx @@ -51,7 +51,8 @@ namespace build2 action a, const target&, const string&, - bool) const override; + bool, + const optional&) const override; string substitute_import (const location&, diff --git a/libbuild2/in/init.cxx b/libbuild2/in/init.cxx index 6cd2f96..18071f8 100644 --- a/libbuild2/in/init.cxx +++ b/libbuild2/in/init.cxx @@ -59,6 +59,11 @@ namespace build2 // unknown substitutions as is. // vp.insert ("in.substitution"); + + // Fallback value to use for NULL value substitutions. If unspecified, + // NULL substitutions are an error. + // + vp.insert ("in.null"); } // Register target types. diff --git a/libbuild2/in/rule.cxx b/libbuild2/in/rule.cxx index 16747ab..b64541c 100644 --- a/libbuild2/in/rule.cxx +++ b/libbuild2/in/rule.cxx @@ -116,6 +116,14 @@ namespace build2 fail << "invalid substitution mode '" << *s << "'"; } + // NULL substitutions. + // + optional null; + if (const string* s = cast_null (t["in.null"])) + null = *s; + else + null = null_; + // Determine if anything needs to be updated. // timestamp mt (t.load_mtime ()); @@ -220,7 +228,7 @@ namespace build2 // can be overriden with custom substitution semantics. // optional v ( - substitute (location (ip, ln), a, t, n, strict)); + substitute (location (ip, ln), a, t, n, strict, null)); assert (v); // Rule semantics change without version increment? @@ -360,7 +368,8 @@ namespace build2 // pointing to the opening symbol and e -- to the closing. // string name (s, b + 1, e - b -1); - if (optional val = substitute (l, a, t, name, strict)) + if (optional val = + substitute (l, a, t, name, strict, null)) { // Save in depdb. // @@ -429,11 +438,26 @@ namespace build2 } string rule:: - lookup (const location& l, action, const target& t, const string& n) const + lookup (const location& loc, + action, + const target& t, + const string& n, + const optional& null) const { - if (auto x = t[n]) + auto l (t[n]); + + if (l.defined ()) { - value v (*x); + value v (*l); + + if (v.null) + { + if (null) + return *null; + else + fail (loc) << "null value in variable '" << n << "'" << + info << "use in.null to specify null value substiution string"; + } // For typed values call string() for conversion. // @@ -445,16 +469,16 @@ namespace build2 : t.ctx.functions.call (&t.base_scope (), "string", vector_view (&v, 1), - l)); + loc)); } catch (const invalid_argument& e) { - fail (l) << e << + fail (loc) << e << info << "while substituting '" << n << "'" << endf; } } else - fail (l) << "undefined variable '" << n << "'" << endf; + fail (loc) << "undefined variable '" << n << "'" << endf; } optional rule:: @@ -462,7 +486,8 @@ namespace build2 action a, const target& t, const string& n, - bool strict) const + bool strict, + const optional& null) const { // In the lax mode scan the fragment to make sure it is a variable name // (that is, it can be expanded in a buildfile as just $; see @@ -486,7 +511,7 @@ namespace build2 } } - return lookup (l, a, t, n); + return lookup (l, a, t, n, null); } } } diff --git a/libbuild2/in/rule.hxx b/libbuild2/in/rule.hxx index cf67060..2fa1305 100644 --- a/libbuild2/in/rule.hxx +++ b/libbuild2/in/rule.hxx @@ -33,11 +33,13 @@ namespace build2 rule (string rule_id, string program, char symbol = '$', - bool strict = true) + bool strict = true, + optional null = nullopt) : rule_id_ (move (rule_id)), program_ (move (program)), symbol_ (symbol), - strict_ (strict) {} + strict_ (strict), + null_ (move (null)) {} virtual bool match (action, target&, const string&) const override; @@ -65,7 +67,8 @@ namespace build2 lookup (const location&, action, const target&, - const string& name) const; + const string& name, + const optional& null) const; // Perform variable substitution. Return nullopt if it should be // ignored. @@ -75,13 +78,15 @@ namespace build2 action, const target&, const string& name, - bool strict) const; + bool strict, + const optional& null) const; protected: const string rule_id_; const string program_; char symbol_; bool strict_; + optional null_; }; } } diff --git a/libbuild2/version/rule.cxx b/libbuild2/version/rule.cxx index 6fd97ae..919dfcf 100644 --- a/libbuild2/version/rule.cxx +++ b/libbuild2/version/rule.cxx @@ -88,7 +88,8 @@ namespace build2 lookup (const location& l, action a, const target& t, - const string& n) const + const string& n, + const optional& null) const { // Note that this code will be executed during up-to-date check for each // substitution so let's try not to do anything overly sub-optimal here. @@ -108,7 +109,8 @@ namespace build2 return rule::lookup (l, // Standard lookup. a, t, - p == string::npos ? n : string (n, p + 1)); + p == string::npos ? n : string (n, p + 1), + null); } string pn (n, 0, p); diff --git a/libbuild2/version/rule.hxx b/libbuild2/version/rule.hxx index f9e7655..ddc5e11 100644 --- a/libbuild2/version/rule.hxx +++ b/libbuild2/version/rule.hxx @@ -29,7 +29,8 @@ namespace build2 lookup (const location&, action, const target&, - const string&) const override; + const string&, + const optional&) const override; }; // Pre-process manifest before installation to patch in the version. -- cgit v1.1