Repository: mesonbuild/meson

Issue:
RFC: support a dictionary as the configuration parameter of configure_file?
I like dictionaries. Any support for the following idea?
```
configure_file(output: 'config.h', configuration: {'version': '1.0'})
```

Or as a more cluttered alternative (but still better than was is available right now): give the `configuration_data()` constructor an optional dictionary argument:
```
configure_file(output: 'config.h', configuration: configuration_data({'version': '1.0'}))
```



Discussion:
Passing the configuration as a dictionary could be supported. The former syntax is nicer, though.
While at it, the same could be done for `environment()`
That sounds good. Even though I asked the question, maybe there is someone who knows the way around the source code (I have not looked at it for about 3 years) and is therefore more suited to actually implement that idea. My gut feeling is, that the changeset for this feature would only be a couple of lines?

Resolving patch:
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -2372,9 +2372,18 @@ def func_get_option(self, nodes, args, kwargs):
 
     @noKwargs
     def func_configuration_data(self, node, args, kwargs):
-        if args:
-            raise InterpreterException('configuration_data takes no arguments')
-        return ConfigurationDataHolder(self.subproject)
+        if len(args) > 1:
+            raise InterpreterException('configuration_data takes only one optional positional arguments')
+        elif len(args) == 1:
+            initial_values = args[0]
+            if not isinstance(initial_values, dict):
+                raise InterpreterException('configuration_data first argument must be a dictionary')
+        else:
+            initial_values = {}
+        cdata = ConfigurationDataHolder(self.subproject)
+        for k, v in initial_values.items():
+            cdata.set_method([k, v], {})
+        return cdata
 
     def set_options(self, default_options):
         # Set default options as if they were passed to the command line.
@@ -3552,7 +3561,12 @@ def func_configure_file(self, node, args, kwargs):
         # Perform the appropriate action
         if 'configuration' in kwargs:
             conf = kwargs['configuration']
-            if not isinstance(conf, ConfigurationDataHolder):
+            if isinstance(conf, dict):
+                cdata = ConfigurationDataHolder(self.subproject)
+                for k, v in conf.items():
+                    cdata.set_method([k, v], {})
+                conf = cdata
+            elif not isinstance(conf, ConfigurationDataHolder):
                 raise InterpreterException('Argument "configuration" is not of type configuration_data')
             mlog.log('Configuring', mlog.bold(output), 'using configuration')
             if inputfile is not None:


Test patch:
diff --git a/test cases/common/14 configure file/meson.build b/test cases/common/14 configure file/meson.build
--- a/test cases/common/14 configure file/meson.build	
+++ b/test cases/common/14 configure file/meson.build	
@@ -246,3 +246,27 @@ test('configure-file', test_file)
 
 cdata = configuration_data()
 cdata.set('invalid_value', ['array'])
+
+# Dictionaries
+
+cdata = configuration_data({
+  'A_STRING' : '"foo"',
+  'A_INT' : 42,
+  'A_DEFINED' : true,
+  'A_UNDEFINED' : false,
+})
+
+configure_file(output : 'config9a.h',
+  configuration : cdata,
+)
+
+configure_file(output : 'config9b.h',
+  configuration : {
+    'B_STRING' : '"foo"',
+    'B_INT' : 42,
+    'B_DEFINED' : true,
+    'B_UNDEFINED' : false,
+  }
+)
+
+test('test9', executable('prog9', 'prog9.c'))
diff --git a/test cases/common/14 configure file/prog9.c b/test cases/common/14 configure file/prog9.c
new file mode 100644
--- /dev/null
+++ b/test cases/common/14 configure file/prog9.c	
@@ -0,0 +1,18 @@
+#include <string.h>
+#include <config9a.h>
+#include <config9b.h>
+
+#if defined(A_UNDEFINED) || defined(B_UNDEFINED)
+#error "Should not be defined"
+#endif
+
+#if !defined(A_DEFINED) || !defined(B_DEFINED)
+#error "Should be defined"
+#endif
+
+int main(int argc, char **argv) {
+    return strcmp(A_STRING, "foo")
+        || strcmp(B_STRING, "foo")
+        || A_INT != 42
+        || B_INT != 42;
+}