Repository: mesonbuild/meson

Issue:
Dependency for generators
I was having a look at the port of protobuf-c to meson and one issue they build an executable that is passed as a command line param to the generator in this format:

```
gen_protobuf_c = generator(protoc,
    output    : ['@BASENAME@.pb-c.c', '@BASENAME@.pb-c.h'],
    arguments : ['--proto_path=@CURRENT_SOURCE_DIR@', '--plugin=protoc-gen-c=@0@'.format(protoc_gen_c.full_path()), '--cpp_out=@BUILD_DIR@',  @INPUT@'])
```
When I try to use the generator it does not find the protoc_gen_c yet, I think we need a way to tell the generator that the executable has to be ready before calling it.


Discussion:
Isn't there a 'dependencies' keyword for generator?
As far as I can see there isn't: https://mesonbuild.com/Reference-manual.html#generator
The reason is that you're using `.full_path()`, which you're forced to because `generator()` only takes a single argument as the input. If you were using a `custom_target()` you could put that in the list of inputs (which will add a dependency on it) and then use it as `@INPUT0@`, etc in the arguments.
The problem I see by using custom_target is that I do not have a process method, so the inputs are basically hardcoded
We could probably add a `depends` kwarg (or something better-named) which you can refer to in arguments with `@DEPEND@` similar to `@INPUT@`.
sounds good to me
@nirbheek I tried to have a look at the patch but I have not much knowledge of the internals of meson. Any chance you could make a patch for this? would be awesome to have protobuf-c with meson
Hi, protobuf-c maintainer here. I'd still like to switch protobuf-c from autotools/cmake to meson at some point, but if I recall correctly we were stuck on this issue. Any progress?

Thanks!

Resolving patch:
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -1803,6 +1803,7 @@ def generate_genlist_for_target(self, genlist, target):
                 cmd = cmdlist
 
             elem = NinjaBuildElement(self.all_outputs, outfiles, rulename, infilename)
+            elem.add_dep([self.get_target_filename(x) for x in generator.depends])
             if generator.depfile is not None:
                 elem.add_item('DEPFILE', depfile)
             if len(extra_dependencies) > 0:
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -1280,6 +1280,7 @@ def __init__(self, args, kwargs):
         self.exe = exe
         self.depfile = None
         self.capture = False
+        self.depends = []
         self.process_kwargs(kwargs)
 
     def __repr__(self):
@@ -1328,6 +1329,12 @@ def process_kwargs(self, kwargs):
             if not isinstance(capture, bool):
                 raise InvalidArguments('Capture must be boolean.')
             self.capture = capture
+        if 'depends' in kwargs:
+            depends = listify(kwargs['depends'], unholder=True)
+            for d in depends:
+                if not isinstance(d, BuildTarget):
+                    raise InvalidArguments('Depends entries must be build targets.')
+                self.depends.append(d)
 
     def get_base_outnames(self, inname):
         plainname = os.path.basename(inname)
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -1984,7 +1984,12 @@ def get_cross_property_method(self, args, kwargs):
                                            },
                     'executable': build.known_exe_kwargs,
                     'find_program': {'required', 'native'},
-                    'generator': {'arguments', 'output', 'depfile', 'capture', 'preserve_path_from'},
+                    'generator': {'arguments',
+                                  'output',
+                                  'depends',
+                                  'depfile',
+                                  'capture',
+                                  'preserve_path_from'},
                     'include_directories': {'is_system'},
                     'install_data': {'install_dir', 'install_mode', 'rename', 'sources'},
                     'install_headers': {'install_dir', 'install_mode', 'subdir'},


Test patch:
diff --git a/test cases/common/27 pipeline/depends/copyrunner.py b/test cases/common/27 pipeline/depends/copyrunner.py
new file mode 100755
--- /dev/null
+++ b/test cases/common/27 pipeline/depends/copyrunner.py	
@@ -0,0 +1,7 @@
+#!/usr/bin/env python3
+
+import sys, subprocess
+
+prog, infile, outfile = sys.argv[1:]
+
+subprocess.check_call([prog, infile, outfile])
diff --git a/test cases/common/27 pipeline/depends/filecopier.c b/test cases/common/27 pipeline/depends/filecopier.c
new file mode 100644
--- /dev/null
+++ b/test cases/common/27 pipeline/depends/filecopier.c	
@@ -0,0 +1,22 @@
+#include<stdio.h>
+#include<assert.h>
+
+#define BUFSIZE 1024
+
+int main(int argc, char **argv) {
+    char buffer[BUFSIZE];
+    size_t num_read;
+    size_t num_written;
+    FILE *fin = fopen(argv[1], "rb");
+    FILE *fout;
+    assert(fin);
+    num_read = fread(buffer, 1, BUFSIZE, fin);
+    assert(num_read > 0);
+    fclose(fin);
+    fout = fopen(argv[2], "wb");
+    assert(fout);
+    num_written = fwrite(buffer, 1, num_read, fout);
+    assert(num_written == num_read);
+    fclose(fout);
+    return 0;
+}
diff --git a/test cases/common/27 pipeline/depends/libsrc.c.in b/test cases/common/27 pipeline/depends/libsrc.c.in
new file mode 100644
--- /dev/null
+++ b/test cases/common/27 pipeline/depends/libsrc.c.in	
@@ -0,0 +1,3 @@
+int func() {
+    return 42;
+}
diff --git a/test cases/common/27 pipeline/depends/meson.build b/test cases/common/27 pipeline/depends/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/common/27 pipeline/depends/meson.build	
@@ -0,0 +1,11 @@
+runner = find_program('copyrunner.py')
+
+copier = executable('copier', 'filecopier.c', native: true)
+
+cg = generator(runner,
+    output: ['@BASENAME@.c'],
+    arguments: [copier.full_path(), '@INPUT@', '@OUTPUT@'],
+    depends: copier)
+
+test('generatordep',
+    executable('gd', 'prog.c', cg.process('libsrc.c.in')))
diff --git a/test cases/common/27 pipeline/depends/prog.c b/test cases/common/27 pipeline/depends/prog.c
new file mode 100644
--- /dev/null
+++ b/test cases/common/27 pipeline/depends/prog.c	
@@ -0,0 +1,5 @@
+int func();
+
+int main(int argc, char **argv) {
+    return func() != 42;
+}
diff --git a/test cases/common/27 pipeline/meson.build b/test cases/common/27 pipeline/meson.build
--- a/test cases/common/27 pipeline/meson.build	
+++ b/test cases/common/27 pipeline/meson.build	
@@ -15,3 +15,9 @@ generated = gen.process(['input_src.dat'])
 e2 = executable('prog', 'prog.c', generated)
 
 test('pipelined', e2)
+
+# This is in a subdirectory to make sure
+# we write proper subdir paths to output.
+subdir('src')
+
+subdir('depends')
diff --git a/test cases/common/28 pipeline/src/input_src.dat b/test cases/common/27 pipeline/src/input_src.dat
similarity index 100%
rename from test cases/common/28 pipeline/src/input_src.dat
rename to test cases/common/27 pipeline/src/input_src.dat
diff --git a/test cases/common/28 pipeline/src/meson.build b/test cases/common/27 pipeline/src/meson.build
similarity index 100%
rename from test cases/common/28 pipeline/src/meson.build
rename to test cases/common/27 pipeline/src/meson.build
diff --git a/test cases/common/28 pipeline/src/prog.c b/test cases/common/27 pipeline/src/prog.c
similarity index 100%
rename from test cases/common/28 pipeline/src/prog.c
rename to test cases/common/27 pipeline/src/prog.c
diff --git a/test cases/common/28 pipeline/src/srcgen.c b/test cases/common/27 pipeline/src/srcgen.c
similarity index 100%
rename from test cases/common/28 pipeline/src/srcgen.c
rename to test cases/common/27 pipeline/src/srcgen.c
diff --git a/test cases/common/28 pipeline/meson.build b/test cases/common/28 pipeline/meson.build
deleted file mode 100644
--- a/test cases/common/28 pipeline/meson.build	
+++ /dev/null
@@ -1,5 +0,0 @@
-project('pipeline test', 'c')
-
-# This is in a subdirectory to make sure
-# we write proper subdir paths to output.
-subdir('src')