Repository: mesonbuild/meson

Issue:
protobuf generator does not work in sub-directory
When attempting to use the protobuf generator under a subdirectory, the build complains about missing generated files.

If we use the following setup under say <project_root>/mysubdir/meson.build

```meson
protoc = find_program('protoc')
dep = dependency('protobuf')

gen = generator(protoc, \
  output    : ['@BASENAME@.pb.cc', '@BASENAME@.pb.h'],
  arguments : ['--proto_path=@SOURCE_DIR@', '--cpp_out=@BUILD_DIR@', '@INPUT@'])

generated = gen.process('defs.proto')
e = executable('prog', 'main.cpp', generated, dependencies : dep)
test('prototest', e)
```

Under a project hierarchy like:
<project_root>/meson.build
<project_root>/mysubdir/meson.build
<project_root>/mysubdir/defs.proto

Then:
INPUT=../mysubdir/defs.proto
SOURCE_DIR=../
BUILD_DIR=mysubdir/prog@exe

protoc requires the input files to exist relative to the path given in --proto_path. It uses this relative path to generate files in path given in -cpp-out.

In this case the relative path computed by protoc is mysubdir/ therefore files are generated in "mysubdir/prog@exe/mysubdir/"

Meanwhile, the build target expects generated files to be in mysubdir/prog@exe, so the build fails.


Discussion:

Resolving patch:
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -149,6 +149,10 @@ def get_target_dir(self, target):
             dirname = 'meson-out'
         return dirname
 
+    def get_target_source_dir(self, target):
+        dirname = os.path.join(self.build_to_src, self.get_target_dir(target))
+        return dirname
+
     def get_target_private_dir(self, target):
         dirname = os.path.join(self.get_target_dir(target), target.get_basename() + target.type_suffix())
         return dirname
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -1650,6 +1650,7 @@ def generate_genlist_for_target(self, genlist, target, outfile):
         outfilelist = genlist.get_outputs()
         base_args = generator.get_arglist()
         extra_dependencies = [os.path.join(self.build_to_src, i) for i in genlist.extra_depends]
+        source_target_dir = self.get_target_source_dir(target)
         for i in range(len(infilelist)):
             if len(generator.outputs) == 1:
                 sole_output = os.path.join(self.get_target_private_dir(target), outfilelist[i])
@@ -1676,6 +1677,7 @@ def generate_genlist_for_target(self, genlist, 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]
+            args = [x.replace("@CURRENT_SOURCE_DIR@", source_target_dir) for x in args]
             args = [x.replace("@SOURCE_ROOT@", self.build_to_src).replace("@BUILD_ROOT@", '.')
                     for x in args]
             cmdlist = exe_arr + self.replace_extra_args(args, genlist)
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -120,6 +120,7 @@ def generate_custom_generator_commands(self, target, parent_node):
         custom_target_include_dirs = []
         custom_target_output_files = []
         target_private_dir = self.relpath(self.get_target_private_dir(target), self.get_target_dir(target))
+        source_target_dir = self.get_target_source_dir(target)
         down = self.target_to_build_root(target)
         for genlist in target.get_generated_sources():
             if isinstance(genlist, build.CustomTarget):
@@ -154,6 +155,7 @@ def generate_custom_generator_commands(self, target, parent_node):
                     args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir())
                              .replace("@BUILD_DIR@", target_private_dir)
                             for x in args]
+                    args = [x.replace("@CURRENT_SOURCE_DIR@", source_target_dir) for x in args]
                     args = [x.replace("@SOURCE_ROOT@", self.environment.get_source_dir())
                              .replace("@BUILD_ROOT@", self.environment.get_build_dir())
                             for x in args]


Test patch:
diff --git a/test cases/frameworks/5 protocol buffers/asubdir/defs.proto b/test cases/frameworks/5 protocol buffers/asubdir/defs.proto
new file mode 100644
--- /dev/null
+++ b/test cases/frameworks/5 protocol buffers/asubdir/defs.proto	
@@ -0,0 +1,3 @@
+message Dummy {
+  required string text = 1;
+}
diff --git a/test cases/frameworks/5 protocol buffers/asubdir/main.cpp b/test cases/frameworks/5 protocol buffers/asubdir/main.cpp
new file mode 100644
--- /dev/null
+++ b/test cases/frameworks/5 protocol buffers/asubdir/main.cpp	
@@ -0,0 +1,9 @@
+#include "defs.pb.h"
+
+int main(int argc, char **argv) {
+    GOOGLE_PROTOBUF_VERIFY_VERSION;
+    Dummy *d = new Dummy;
+    delete d;
+    google::protobuf::ShutdownProtobufLibrary();
+    return 0;
+}
diff --git a/test cases/frameworks/5 protocol buffers/asubdir/meson.build b/test cases/frameworks/5 protocol buffers/asubdir/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/frameworks/5 protocol buffers/asubdir/meson.build	
@@ -0,0 +1,8 @@
+subdirgen = generator(protoc, \
+  output    : ['@BASENAME@.pb.cc', '@BASENAME@.pb.h'],
+  arguments : ['--proto_path=@CURRENT_SOURCE_DIR@', '--cpp_out=@BUILD_DIR@', '@INPUT@'])
+
+generated = subdirgen.process('defs.proto')
+e = executable('subdir-prog', 'main.cpp', generated,
+ dependencies : dep)
+test('subdir-prototest', e)
diff --git a/test cases/frameworks/5 protocol buffers/meson.build b/test cases/frameworks/5 protocol buffers/meson.build
--- a/test cases/frameworks/5 protocol buffers/meson.build	
+++ b/test cases/frameworks/5 protocol buffers/meson.build	
@@ -16,3 +16,5 @@ generated = gen.process('defs.proto')
 e = executable('prog', 'main.cpp', generated,
  dependencies : dep)
 test('prototest', e)
+
+subdir('asubdir')