Repository: mesonbuild/meson

Issue:
Feature Request: generator.process() to allow 'extra_args' argument
Hi Jussi,

I have a generator for cuda's `nvcc` command and it's working OK. The issue is - sometimes an app needs to add custom args to `nvcc`.

Would something like this be possible?

```
cuda_gen = generator(nvcc,
    output: '@BASENAME@.o',
    arguments: ['-c', '@INPUT@', '-o', '@OUTPUT@'],
)
```

```
generated = cuda_gen.process('f1.cu', extra_args=['-Darg1', '-Darg2', '-foo-bar-arg3'])
```

Being about to pass extra args to the nvcc command would be ideal. The alternative is to create multiple generators.... not that huge of a deal, but breaks the DRY principle.

Thanks!



Discussion:

Resolving patch:
diff --git a/build.py b/build.py
--- a/build.py
+++ b/build.py
@@ -575,7 +575,7 @@ def get_arglist(self):
         return self.arglist
 
 class GeneratedList():
-    def __init__(self, generator):
+    def __init__(self, generator, extra_args=[]):
         if hasattr(generator, 'held_object'):
             generator = generator.held_object
         self.generator = generator
@@ -583,6 +583,7 @@ def __init__(self, generator):
         self.outfilelist = []
         self.outmap = {}
         self.extra_depends = []
+        self.extra_args = extra_args
 
     def add_file(self, newfile):
         self.infilelist.append(newfile)
@@ -602,6 +603,9 @@ def get_outputs_for(self, filename):
     def get_generator(self):
         return self.generator
 
+    def get_extra_args(self):
+        return self.extra_args
+
 class Executable(BuildTarget):
     def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
         super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs)
diff --git a/interpreter.py b/interpreter.py
--- a/interpreter.py
+++ b/interpreter.py
@@ -290,18 +290,17 @@ def __init__(self, interpreter, args, kwargs):
         self.methods.update({'process' : self.process_method})
 
     def process_method(self, args, kwargs):
-        if len(kwargs) > 0:
-            raise InvalidArguments('Process does not take keyword arguments.')
         check_stringlist(args)
-        gl = GeneratedListHolder(self)
+        extras = mesonlib.stringlistify(kwargs.get('extra_args', []))
+        gl = GeneratedListHolder(self, extras)
         [gl.add_file(os.path.join(self.interpreter.subdir, a)) for a in args]
         return gl
 
 class GeneratedListHolder(InterpreterObject):
-    def __init__(self, arg1):
+    def __init__(self, arg1, extra_args=[]):
         super().__init__()
         if isinstance(arg1, GeneratorHolder):
-            self.held_object = build.GeneratedList(arg1.held_object)
+            self.held_object = build.GeneratedList(arg1.held_object, extra_args)
         else:
             self.held_object = arg1
 
diff --git a/ninjabackend.py b/ninjabackend.py
--- a/ninjabackend.py
+++ b/ninjabackend.py
@@ -1141,7 +1141,13 @@ def generate_custom_generator_rules(self, target, outfile):
                 relout = self.get_target_private_dir(target)
                 args = [x.replace("@SOURCE_DIR@", self.build_to_src).replace("@BUILD_DIR@", relout)
                         for x in args]
-                cmdlist = exe_arr + args
+                final_args = []
+                for a in args:
+                    if a == '@EXTRA_ARGS@':
+                        final_args += genlist.get_extra_args()
+                    else:
+                        final_args.append(a)
+                cmdlist = exe_arr + final_args
                 elem = NinjaBuildElement(outfiles, 'CUSTOM_COMMAND', infilename)
                 if len(extra_dependencies) > 0:
                     elem.add_dep(extra_dependencies)


Test patch:
diff --git a/test cases/common/98 gen extra/meson.build b/test cases/common/98 gen extra/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/common/98 gen extra/meson.build	
@@ -0,0 +1,13 @@
+project('extra args in gen', 'c')
+
+prog = find_program('srcgen.py')
+
+gen = generator(prog,
+  output : '@BASENAME@.c',
+  arguments : ['--input=@INPUT@', '--output=@OUTPUT@', '@EXTRA_ARGS@'])
+
+g1 = gen.process('name.dat')
+g2 = gen.process('name.dat', extra_args: '--upper')
+
+test('basic', executable('basic', 'plain.c', g1))
+test('upper', executable('upper', 'upper.c', g2))
diff --git a/test cases/common/98 gen extra/name.dat b/test cases/common/98 gen extra/name.dat
new file mode 100644
--- /dev/null
+++ b/test cases/common/98 gen extra/name.dat	
@@ -0,0 +1 @@
+bob_mcbob
diff --git a/test cases/common/98 gen extra/plain.c b/test cases/common/98 gen extra/plain.c
new file mode 100644
--- /dev/null
+++ b/test cases/common/98 gen extra/plain.c	
@@ -0,0 +1,5 @@
+int bob_mcbob();
+
+int main(int argc, char **argv) {
+    return bob_mcbob();
+}
diff --git a/test cases/common/98 gen extra/srcgen.py b/test cases/common/98 gen extra/srcgen.py
new file mode 100755
--- /dev/null
+++ b/test cases/common/98 gen extra/srcgen.py	
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+
+import sys
+import os
+import argparse
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--input', dest='input',
+                    help='the input file')
+parser.add_argument('--output', dest='output',
+                    help='the output file')
+parser.add_argument('--upper', dest='upper', action='store_true', default=False,
+                    help='Convert to upper case.')
+
+c_templ = '''int %s() {
+    return 0;
+}
+'''
+
+options = parser.parse_args(sys.argv[1:])
+
+funcname = open(options.input).readline().strip()
+if options.upper:
+    funcname = funcname.upper()
+
+open(options.output, 'w').write(c_templ % funcname)
diff --git a/test cases/common/98 gen extra/upper.c b/test cases/common/98 gen extra/upper.c
new file mode 100644
--- /dev/null
+++ b/test cases/common/98 gen extra/upper.c	
@@ -0,0 +1,5 @@
+int BOB_MCBOB();
+
+int main(int argc, char **argv) {
+    return BOB_MCBOB();
+}