Repository: mesonbuild/meson

Issue:
Unhelpful error for non-well-formed array option
**Describe the bug**

I ran the following command in gst-plugins-base:

```ShellSession
$ meson buidl -Dgl_winsys=['x11','wayland']
The Meson build system
Version: 0.52.1
Source dir: /home/jtojnar/Projects/gst-plugins-base
Build dir: /home/jtojnar/Projects/gst-plugins-base/buidl
Build type: native build
Traceback (most recent call last):
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/mesonmain.py", line 129, in run
    return options.run_func(options)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/msetup.py", line 245, in run
    app.generate()
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/msetup.py", line 159, in generate
    self._generate(env)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/msetup.py", line 176, in _generate
    intr = interpreter.Interpreter(b)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreter.py", line 2110, in __init__
    self.parse_project()
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 397, in parse_project
    self.evaluate_codeblock(self.ast, end=1)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 436, in evaluate_codeblock
    raise e
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 430, in evaluate_codeblock
    self.evaluate_statement(cur)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 441, in evaluate_statement
    return self.function_call(cur)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 776, in function_call
    return func(node, posargs, kwargs)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 143, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 174, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreter.py", line 2723, in func_project
    self.coredata.set_default_options(default_options, self.subproject, self.environment)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/coredata.py", line 737, in set_default_options
    self.set_options(options, subproject=subproject)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/coredata.py", line 675, in set_options
    tgt.set_value(v)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/coredata.py", line 70, in set_value
    self.value = self.validate_value(newvalue)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/coredata.py", line 183, in validate_value
    newvalue = ast.literal_eval(value)
  File "/nix/store/qcqxn0bvj9mhzzcsn02flxky3k4pxpy4-python3-3.7.5/lib/python3.7/ast.py", line 91, in literal_eval
    return _convert(node_or_string)
  File "/nix/store/qcqxn0bvj9mhzzcsn02flxky3k4pxpy4-python3-3.7.5/lib/python3.7/ast.py", line 74, in _convert
    return list(map(_convert, node.elts))
  File "/nix/store/qcqxn0bvj9mhzzcsn02flxky3k4pxpy4-python3-3.7.5/lib/python3.7/ast.py", line 90, in _convert
    return _convert_signed_num(node)
  File "/nix/store/qcqxn0bvj9mhzzcsn02flxky3k4pxpy4-python3-3.7.5/lib/python3.7/ast.py", line 63, in _convert_signed_num
    return _convert_num(node)
  File "/nix/store/qcqxn0bvj9mhzzcsn02flxky3k4pxpy4-python3-3.7.5/lib/python3.7/ast.py", line 55, in _convert_num
    raise ValueError('malformed node or string: ' + repr(node))
ValueError: malformed node or string: <_ast.Name object at 0x7fcba6584c50>
```
Immediately, it printed an unhelpful traceback.

**To Reproduce**
Checkout a meson project with an array option (e.g. https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/blob/3b8769e67351f079184b24b7b04b327fd3f16aad/meson_options.txt#L14-16) and try to configure it with an array, not quoting the items.

**Expected behavior**
A clear error message like the following should be displayed:

> **error**: unable to parse option `-Dgl_winsys`: expected a list of values. If you used quotes in the array, make sure to also quote the whole thing to avoid shell from eating them. See https://mesonbuild.com/Build-options.html#using-build-options for more information

**system parameters**
* NixOS
* Python 3.7.5
* Meson 0.52.1

Unhelpful error for non-well-formed array option
**Describe the bug**

I ran the following command in gst-plugins-base:

```ShellSession
$ meson buidl -Dgl_winsys=['x11','wayland']
The Meson build system
Version: 0.52.1
Source dir: /home/jtojnar/Projects/gst-plugins-base
Build dir: /home/jtojnar/Projects/gst-plugins-base/buidl
Build type: native build
Traceback (most recent call last):
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/mesonmain.py", line 129, in run
    return options.run_func(options)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/msetup.py", line 245, in run
    app.generate()
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/msetup.py", line 159, in generate
    self._generate(env)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/msetup.py", line 176, in _generate
    intr = interpreter.Interpreter(b)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreter.py", line 2110, in __init__
    self.parse_project()
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 397, in parse_project
    self.evaluate_codeblock(self.ast, end=1)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 436, in evaluate_codeblock
    raise e
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 430, in evaluate_codeblock
    self.evaluate_statement(cur)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 441, in evaluate_statement
    return self.function_call(cur)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 776, in function_call
    return func(node, posargs, kwargs)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 143, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 174, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/interpreter.py", line 2723, in func_project
    self.coredata.set_default_options(default_options, self.subproject, self.environment)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/coredata.py", line 737, in set_default_options
    self.set_options(options, subproject=subproject)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/coredata.py", line 675, in set_options
    tgt.set_value(v)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/coredata.py", line 70, in set_value
    self.value = self.validate_value(newvalue)
  File "/nix/store/lxgl0ximm5vca0jy7zrbybydp7vp5brq-meson-0.52.1/lib/python3.7/site-packages/mesonbuild/coredata.py", line 183, in validate_value
    newvalue = ast.literal_eval(value)
  File "/nix/store/qcqxn0bvj9mhzzcsn02flxky3k4pxpy4-python3-3.7.5/lib/python3.7/ast.py", line 91, in literal_eval
    return _convert(node_or_string)
  File "/nix/store/qcqxn0bvj9mhzzcsn02flxky3k4pxpy4-python3-3.7.5/lib/python3.7/ast.py", line 74, in _convert
    return list(map(_convert, node.elts))
  File "/nix/store/qcqxn0bvj9mhzzcsn02flxky3k4pxpy4-python3-3.7.5/lib/python3.7/ast.py", line 90, in _convert
    return _convert_signed_num(node)
  File "/nix/store/qcqxn0bvj9mhzzcsn02flxky3k4pxpy4-python3-3.7.5/lib/python3.7/ast.py", line 63, in _convert_signed_num
    return _convert_num(node)
  File "/nix/store/qcqxn0bvj9mhzzcsn02flxky3k4pxpy4-python3-3.7.5/lib/python3.7/ast.py", line 55, in _convert_num
    raise ValueError('malformed node or string: ' + repr(node))
ValueError: malformed node or string: <_ast.Name object at 0x7fcba6584c50>
```
Immediately, it printed an unhelpful traceback.

**To Reproduce**
Checkout a meson project with an array option (e.g. https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/blob/3b8769e67351f079184b24b7b04b327fd3f16aad/meson_options.txt#L14-16) and try to configure it with an array, not quoting the items.

**Expected behavior**
A clear error message like the following should be displayed:

> **error**: unable to parse option `-Dgl_winsys`: expected a list of values. If you used quotes in the array, make sure to also quote the whole thing to avoid shell from eating them. See https://mesonbuild.com/Build-options.html#using-build-options for more information

**system parameters**
* NixOS
* Python 3.7.5
* Meson 0.52.1



Discussion:
With Bash, Python 3.7.5 & Meson 0.52.1, I could reproduce the issue with minimal example below.

```sh
meson build -Darr=['b','c']
```


meson.build
```meson
project('opts')

message(get_option('arr'))
```

meson_options.txt
```ini
option('arr', type : 'array', choices : ['a', 'b', 'c'], value : ['c'])
```


With `-Darr=['b', 'c']` what your app receives in argv is `-Darr=[b,c]` because bash unqote it before execv(). With `-Darr="['b', 'c']"` bash removes the double-quote and keeps the single quotes.

As side node, you could also use simpler syntax: `-Dgl_winsys=x11,wayland`.

But here the problem is Meson should not backtrace on bad syntax. It's a matter of wrapping literal_eval() into a try/execpt block and raise MesonException() instead.
With Bash, Python 3.7.5 & Meson 0.52.1, I could reproduce the issue with minimal example below.

```sh
meson build -Darr=['b','c']
```


meson.build
```meson
project('opts')

message(get_option('arr'))
```

meson_options.txt
```ini
option('arr', type : 'array', choices : ['a', 'b', 'c'], value : ['c'])
```


With `-Darr=['b', 'c']` what your app receives in argv is `-Darr=[b,c]` because bash unqote it before execv(). With `-Darr="['b', 'c']"` bash removes the double-quote and keeps the single quotes.

As side node, you could also use simpler syntax: `-Dgl_winsys=x11,wayland`.

But here the problem is Meson should not backtrace on bad syntax. It's a matter of wrapping literal_eval() into a try/execpt block and raise MesonException() instead.

Resolving patch:
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -170,7 +170,7 @@ def __init__(self, description, value, split_args=False, user_input=False, allow
         self.allow_dups = allow_dups
         self.value = self.validate_value(value, user_input=user_input)
 
-    def validate_value(self, value, user_input=True) -> List[str]:
+    def validate_value(self, value, user_input: bool = True) -> List[str]:
         # User input is for options defined on the command line (via -D
         # options). Users can put their input in as a comma separated
         # string, but for defining options in meson_options.txt the format
@@ -180,7 +180,10 @@ def validate_value(self, value, user_input=True) -> List[str]:
 
         if isinstance(value, str):
             if value.startswith('['):
-                newvalue = ast.literal_eval(value)
+                try:
+                    newvalue = ast.literal_eval(value)
+                except ValueError:
+                    raise MesonException('malformed option {}'.format(value))
             elif value == '':
                 newvalue = []
             else:
@@ -191,7 +194,7 @@ def validate_value(self, value, user_input=True) -> List[str]:
         elif isinstance(value, list):
             newvalue = value
         else:
-            raise MesonException('"{0}" should be a string array, but it is not'.format(str(newvalue)))
+            raise MesonException('"{}" should be a string array, but it is not'.format(newvalue))
 
         if not self.allow_dups and len(set(newvalue)) != len(newvalue):
             msg = 'Duplicated values in array option is deprecated. ' \


Test patch: