aboutsummaryrefslogtreecommitdiff
path: root/libbuild2
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2022-03-23 09:10:50 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2022-03-23 09:10:50 +0200
commite6b40289f227428901e246149c25ffef18dd4491 (patch)
treee4951bb9e5ff40f63ad6f6bee0dd4a94f7dab755 /libbuild2
parentc1c80c10a8d469b2659015429c7695f3dbd51ef2 (diff)
Make project configuration variables non-nullable by default
A project configuration variable with the NULL default value is naturally assumed nullable, for example: config [string] config.libhello.fallback_name ?= [null] Otherwise, to make a project configuration nullable we use the `null` variable attribute, for example: config [string, null] config.libhello.fallback_name ?= "World"
Diffstat (limited to 'libbuild2')
-rw-r--r--libbuild2/parser.cxx50
1 files changed, 46 insertions, 4 deletions
diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx
index b76bb18..1e8757a 100644
--- a/libbuild2/parser.cxx
+++ b/libbuild2/parser.cxx
@@ -2862,18 +2862,23 @@ namespace build2
next_with_attributes (t, tt);
// Get variable attributes, if any, and deal with the special config.*
- // attributes. Since currently they can only appear in the config
- // directive, we handle them in an ad hoc manner.
+ // attributes as well as null. Since currently they can only appear in the
+ // config directive, we handle them in an ad hoc manner.
//
attributes_push (t, tt);
attributes& as (attributes_top ());
+ bool nullable (false);
optional<string> report;
string report_var;
for (auto i (as.begin ()); i != as.end (); )
{
- if (i->name == "config.report")
+ if (i->name == "null")
+ {
+ nullable = true;
+ }
+ else if (i->name == "config.report")
{
try
{
@@ -2927,7 +2932,7 @@ namespace build2
if (report && *report != "false" && !config)
{
- if (!as.empty ())
+ if (!as.empty () || nullable)
fail (as.loc) << "unexpected attributes for report-only variable";
attributes_pop ();
@@ -3029,6 +3034,9 @@ namespace build2
peeked ().value != "false")
fail (loc) << var << " variable default value must be literal false";
+ if (nullable)
+ fail (loc) << var << " variable must not be nullable";
+
sflags |= config::save_false_omitted;
}
@@ -3047,14 +3055,48 @@ namespace build2
// all.
//
if (l.defined ())
+ {
+ // Peek at the attributes to detect whether the value is NULL.
+ //
+ if (!dev && !nullable)
+ {
+ // Essentially a prefix of parse_variable_value().
+ //
+ mode (lexer_mode::value, '@');
+ next_with_attributes (t, tt);
+ attributes_push (t, tt, true);
+ for (const attribute& a: attributes_pop ())
+ {
+ if (a.name == "null")
+ {
+ nullable = true;
+ break;
+ }
+ }
+ }
+
skip_line (t, tt);
+ }
else
{
value lhs, rhs (parse_variable_value (t, tt, !dev /* mode */));
apply_value_attributes (&var, lhs, move (rhs), type::assign);
+
+ if (!nullable)
+ nullable = lhs.null;
+
l = config::lookup_config (new_val, *root_, var, move (lhs), sflags);
}
}
+
+ // If the variable is not nullable, verify the value is not NULL.
+ //
+ // Note that undefined is not the same as NULL (if it is undefined, we
+ // should either see the default value or if there is no default value,
+ // then the user is expected to handle the undefined case).
+ //
+ if (!nullable && l.defined () && l->null)
+ fail (loc) << "null value in non-nullable variable " << var;
}
// We will be printing the report at either level 2 (-v) or 3 (-V)