Repository: mesonbuild/meson

Issue:
`meson_options.txt` cannot be used to specify negative integer values
*Actual:*
When trying to specify a negative number in `meson_options.txt`, I get the error `Arguments may only be string, int, bool, or array of those.`.

*Expected:*
No error is raised, and negative values are accepted.

*Rationale:*
These options are ideal for setting defaults for build configuration via `#mesondefine`. It's fairly common for integer options to treat zero and negative values as having special meanings. And even if not, sometimes you'd like to have negative values as options.

*Workaround:*
- Add a second option for the special meaning (ugh!)
- Pass the option as a string, and use `#define` instead of `#mesondefine` (ugh!)

*Details:*
The error is raised here:
https://github.com/mesonbuild/meson/blob/6e865fc08d0d13b3846d00e80c9f3ea090645fb8/mesonbuild/optinterpreter.py#L170

The culprit looks like it may be here: https://github.com/mesonbuild/meson/blob/6e865fc08d0d13b3846d00e80c9f3ea090645fb8/mesonbuild/mparser.py#L107


Discussion:

Resolving patch:
diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py
--- a/mesonbuild/optinterpreter.py
+++ b/mesonbuild/optinterpreter.py
@@ -166,6 +166,16 @@ def reduce_single(self, arg):
             return arg.value
         elif isinstance(arg, mparser.ArrayNode):
             return [self.reduce_single(curarg) for curarg in arg.args.arguments]
+        elif isinstance(arg, mparser.UMinusNode):
+            res = self.reduce_single(arg.value)
+            if not isinstance(res, (int, float)):
+                raise OptionException('Token after "-" is not a number')
+            return -res
+        elif isinstance(arg, mparser.NotNode):
+            res = self.reduce_single(arg.value)
+            if not isinstance(res, bool):
+                raise OptionException('Token after "not" is not a a boolean')
+            return not res
         else:
             raise OptionException('Arguments may only be string, int, bool, or array of those.')
 


Test patch:
diff --git a/test cases/common/43 options/meson.build b/test cases/common/43 options/meson.build
--- a/test cases/common/43 options/meson.build	
+++ b/test cases/common/43 options/meson.build	
@@ -30,4 +30,8 @@ if get_option('integer_opt') != 3
   error('Incorrect value in integer option.')
 endif
 
+if get_option('neg_int_opt') != -3
+  error('Incorrect value in negative integer option.')
+endif
+
 assert(get_option('wrap_mode') == 'default', 'Wrap mode option is broken.')
diff --git a/test cases/common/43 options/meson_options.txt b/test cases/common/43 options/meson_options.txt
--- a/test cases/common/43 options/meson_options.txt	
+++ b/test cases/common/43 options/meson_options.txt	
@@ -1,6 +1,7 @@
 option('testoption', type : 'string', value : 'optval', description : 'An option to do something')
-option('other_one', type : 'boolean', value : false)
+option('other_one', type : 'boolean', value : not (not (not (not false))))
 option('combo_opt', type : 'combo', choices : ['one', 'two', 'combo'], value : 'combo')
 option('array_opt', type : 'array', choices : ['one', 'two', 'three'], value : ['one', 'two'])
 option('free_array_opt', type : 'array')
-option('integer_opt', type : 'integer', min : 0, max : 5, value : 3)
+option('integer_opt', type : 'integer', min : 0, max : -(-5), value : 3)
+option('neg_int_opt', type : 'integer', min : -5, max : 5, value : -3)