Repository: mesonbuild/meson

Issue:
Build fails when passing -Dc_args due to extra quotes
I encountered this regression when moving of 0.45 to 0.46 today.  If I call meson with ```-Dc_args="-g -O0" ```

I get the following error at build:
```
cc -Iorc/orc@@orc-0.4@sha -Iorc -I../orc -I. -I../ -fdiagnostics-color=always -pipe -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -std=gnu99 -O2 -g -DHAVE_CONFIG_H '-g -O0' -fPIC -DORC_ENABLE_UNSTABLE_API -D_GNU_SOURCE  -MD -MQ 'orc/orc@@orc-0.4@sha/orcdebug.c.o' -MF 'orc/orc@@orc-0.4@sha/orcdebug.c.o.d' -o 'orc/orc@@orc-0.4@sha/orcdebug.c.o' -c ../orc/orcdebug.c
cc: error: unrecognized debug output level ' -O0'
```
As you can see, meson added quotes around '-g -O0'. Removing the qotes fixes it.


Discussion:
If I do `meson builddir -Dc_args="-g -O0"` and then `meson configure builddir` I see:

```
Compiler arguments:
  c_args ['-g -O0', '-g -O0']
```

So it's not only the extra quoting, also it seems duplicated for no reason.

Oh, and with 0.45 there is no quoting and build works, but `meson configure` shows nothing in c_args:
```
Compiler arguments:
  c_args []
```
Oh, and doc says the proper syntax is:
```
$ meson configure -Darray_opt=foo,bar
$ meson configure "-Doption=['a,b', 'c,d']"
```

But that doesn't work neither.
Can you bisect the exact commit that broke this?
I'm pretty sure this actually never worked. It's new in 0.46.0 that -Dc_args is not ignored: https://github.com/mesonbuild/meson/pull/3203

Still, that PR has 2 flaws:
- `-Dc_args=foo -Dc_args=bar` result in `c_args=['foo', 'bar']` because it appends instead of replacing when passing that cmd line option more than once, unlike all other options.
- `-Dc_args="foo bar"` result in `c_args=['foo bar']` because it doesn't split on space, unlike `meson configure -Dc_args="foo bar"`.
We should do an `shlex.split()` on `-D${lang}_args` command-line options. We already do this for `CFLAGS` and other env variables.

Resolving patch:
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -2386,24 +2386,21 @@ def add_languages(self, args, required):
             mlog.log('Native %s compiler: ' % comp.get_display_language(), mlog.bold(' '.join(comp.get_exelist())), version_string, sep='')
 
             # If <language>_args/_link_args settings are given on the
-            # command line, use them.
+            # command line, use them, otherwise take them from env.
+            (preproc_args, compile_args, link_args) = environment.get_args_from_envvars(comp)
             for optspec in self.build.environment.cmd_line_options.projectoptions:
                 (optname, optvalue) = optspec.split('=', maxsplit=1)
-                if optname.endswith('_link_args'):
-                    lang = optname[:-10]
-                    self.coredata.external_link_args.setdefault(lang, []).append(optvalue)
+                if optname == lang + '_link_args':
+                    link_args = shlex.split(optvalue)
                 elif optname.endswith('_args'):
-                    lang = optname[:-5]
-                    self.coredata.external_args.setdefault(lang, []).append(optvalue)
-            # Otherwise, look for definitions from environment
-            # variables such as CFLAGS.
-            (preproc_args, compile_args, link_args) = environment.get_args_from_envvars(comp)
-            if not comp.get_language() in self.coredata.external_preprocess_args:
-                self.coredata.external_preprocess_args[comp.get_language()] = preproc_args
-            if not comp.get_language() in self.coredata.external_args:
-                self.coredata.external_args[comp.get_language()] = compile_args
-            if not comp.get_language() in self.coredata.external_link_args:
-                self.coredata.external_link_args[comp.get_language()] = link_args
+                    compile_args = shlex.split(optvalue)
+            if lang not in self.coredata.external_preprocess_args:
+                self.coredata.external_preprocess_args[lang] = preproc_args
+            if lang not in self.coredata.external_args:
+                self.coredata.external_args[lang] = compile_args
+            if lang not in self.coredata.external_link_args:
+                self.coredata.external_link_args[lang] = link_args
+
             self.build.add_compiler(comp)
             if need_cross_compiler:
                 mlog.log('Cross %s compiler: ' % cross_comp.get_display_language(), mlog.bold(' '.join(cross_comp.get_exelist())), ' (%s %s)' % (cross_comp.id, cross_comp.version), sep='')
diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py
--- a/mesonbuild/mconf.py
+++ b/mesonbuild/mconf.py
@@ -15,6 +15,7 @@
 import os
 import sys
 import argparse
+import shlex
 from . import (coredata, mesonlib, build)
 
 def buildparser():
@@ -119,14 +120,14 @@ def set_options(self, options):
                     raise ConfException('Unknown language %s in linkargs.' % lang)
                 # TODO, currently split on spaces, make it so that user
                 # can pass in an array string.
-                newvalue = v.split()
+                newvalue = shlex.split(v)
                 self.coredata.external_link_args[lang] = newvalue
             elif k.endswith('_args'):
                 lang = k[:-5]
                 if lang not in self.coredata.external_args:
                     raise ConfException('Unknown language %s in compile args' % lang)
                 # TODO same fix as above
-                newvalue = v.split()
+                newvalue = shlex.split(v)
                 self.coredata.external_args[lang] = newvalue
             else:
                 raise ConfException('Unknown option %s.' % k)


Test patch:
diff --git a/test cases/common/181 initial c_args/meson.build b/test cases/common/181 initial c_args/meson.build
--- a/test cases/common/181 initial c_args/meson.build	
+++ b/test cases/common/181 initial c_args/meson.build	
@@ -1,7 +1,7 @@
 project('options', 'c')
 
 # Test passing c_args and c_link_args options from the command line.
-assert(get_option('c_args') == ['-march=native', '-funroll-loops'],
+assert(get_option('c_args') == ['-funroll-loops'],
        'Incorrect value for c_args option.')
 assert(get_option('c_link_args') == ['-random_linker_option'],
        'Incorrect value for c_link_args option.')