Repository: mesonbuild/meson

Issue:
Object returned by executable() does not expand to full path when used as argument in test()
http://mesonbuild.com/Reference-manual.html#build-target-object says
> full_path() returns a full path pointing to the result target file. NOTE: In most cases using the object itself will do the same job as this and will also allow Meson to setup inter-target dependencies correctly. Please file a bug if that doesn't work for you.

... so I'm filling this.

```meson
        exe = executable('systemd-tmpfiles',
                         'src/tmpfiles/tmpfiles.c',
                         include_directories : includes,
                         link_with : [libshared],
                         dependencies : [libacl],
                         install_rpath : rootlibexecdir,
                         install : true,
                         install_dir : rootbindir)
        public_programs += [exe]

        test('test-systemd-tmpfiles',
             test_systemd_tmpfiles_py,
             args : exe)
```
this expands to
```
--- command ---
/home/zbyszek/src/systemd-work/src/test/test-systemd-tmpfiles.py systemd-tmpfiles
```
i.e. it runs the system-installed binary instead of the one I want to test.

Using `exe.full_path()` works fine, but it will break dependency calculation when #1704 is resolved.


Discussion:
The reason this happens is that we pass a relative path to the target. So if they are in a different directory then it expands to `foo/bar.exe` and correspondingly to `bar.exe` if they are in the same dir. Due to path expansion (and the fact that most people don't have `.` in their path) the latter will resolve to the system binary.

We try to avoid absolute paths when possible, since they have the tendency to break reproducible builds because paths get captured in the weirdest of ways. Maybe we should detect this case and then do `./bar.exe`.
In this case `exe` is passed as an argument to the test and the absolute path would only be embedded in the meson test serialization data, so we should do it automatically. In fact, this was supposed to be fixed already, see: https://github.com/mesonbuild/meson/issues/1094

Resolving patch:
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -725,7 +725,7 @@ def write_test_serialisation(self, tests, datafile):
                 elif isinstance(a, str):
                     cmd_args.append(a)
                 elif isinstance(a, build.Target):
-                    cmd_args.append(self.get_target_filename(a))
+                    cmd_args.append(self.construct_target_rel_path(a, t.workdir))
                 else:
                     raise MesonException('Bad object in test command.')
             ts = TestSerialisation(t.get_name(), t.project_name, t.suite, cmd, is_cross,
@@ -734,6 +734,13 @@ def write_test_serialisation(self, tests, datafile):
             arr.append(ts)
         pickle.dump(arr, datafile)
 
+    def construct_target_rel_path(self, a, workdir):
+        if workdir is None:
+            return self.get_target_filename(a)
+        assert(os.path.isabs(workdir))
+        abs_path = self.get_target_filename_abs(a)
+        return os.path.relpath(abs_path, workdir)
+
     def generate_depmf_install(self, d):
         if self.build.dep_manifest_name is None:
             return


Test patch:
diff --git a/test cases/common/97 test workdir/meson.build b/test cases/common/97 test workdir/meson.build
--- a/test cases/common/97 test workdir/meson.build	
+++ b/test cases/common/97 test workdir/meson.build	
@@ -4,3 +4,5 @@ exe = executable('opener', 'opener.c')
 
 test('basic', exe, workdir : meson.source_root())
 test('shouldfail', exe, should_fail : true)
+
+subdir('subdir')
diff --git a/test cases/common/97 test workdir/subdir/checker.py b/test cases/common/97 test workdir/subdir/checker.py
new file mode 100755
--- /dev/null
+++ b/test cases/common/97 test workdir/subdir/checker.py	
@@ -0,0 +1,5 @@
+#!/usr/bin/env python3
+
+import sys
+
+data = open(sys.argv[1], 'rb').read()
diff --git a/test cases/common/97 test workdir/subdir/meson.build b/test cases/common/97 test workdir/subdir/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/common/97 test workdir/subdir/meson.build	
@@ -0,0 +1,4 @@
+exe2 = executable('dummy', '../opener.c')
+test('subdir', find_program('checker.py'),
+  workdir : meson.source_root(),
+  args: [exe2])