Repository: mesonbuild/meson

Issue:
Passing files() in run_target() assumes working directory is the build root
I was a bit surprised when `files('foo.txt')` from a subdir returned a path like `'../common/MDP1/foo.txt'`

This made using `foo` defined as `foo = files('foo.txt')` almost useless. Is this expected? (I tried using it from the root dir.

I changed it to `foo = join_paths(meson.current_source_dir(), 'foo.txt')` and am now good but just wanted to share my experience.



Discussion:
The point of `files` is to be able to refer to a file (or files) in a source directory from anywhere else including another subdir without having to know where you are and where that file is.

What exactly are you trying to do?

Right, that is the point. But look at the result path from files(). It's
not usable from anywhere in the source tree.

I tried using it from the root directory, and the '..' messes things up.

Am I making sense?

On Wed, Oct 26, 2016, 12:20 PM Nirbheek Chauhan notifications@github.com
wrote:

> The point of files is to be able to refer to a file (or files) in a
> source directory from anywhere else including another subdir without having
> to know where you are and where that file is.
> 
> What exactly are you trying to do?
> 
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub
> https://github.com/mesonbuild/meson/issues/957#issuecomment-256450261,
> or mute the thread
> https://github.com/notifications/unsubscribe-auth/ADQ6f3CJRyuzEop2nYi_21b3FhuUWcAFks5q36gIgaJpZM4KhjtK
> .

The `'..'` is because it translates paths to the corresponding build directory.

Hmm... this is what I tried to do.

In the top level meson.build:

```
# add a top level target to generate an RPM
rpmbuild = find_program('rpmbuild.sh')
run_target('rpm',
    command: [rpmbuild, enumvalues],
)
```

`enumvalues` was defined in a subdir like this `enumvalues = files('enumvalues.txt')`.

This didn't work. I guess I was expecting either a full path or a smart relative path. But `..` from the top level source dir will never work.

Can you think of a better way?

`run_target`s are run from the build directory, so the `..` should indeed be needed to be able to refer to the `enumvalues.txt` file in the source tree. What am I missing?

Thank you @nirbheek for working this through with me.

My issue was in my `rpmbuild.sh` script, I had to `cd $MESON_BUILD_ROOT` for that relative path from `files()` to work correctly. :+1: 

I'm going to close this issue and try to remember this for the next time :)

What directory is the script run from by default?

Looks like it's run from the source dir. My script lives in the top level dir and that's what `pwd` reports.

This is the line in build.ninja.

```
build rpm: CUSTOM_COMMAND 
 COMMAND = '/usr/local/bin/python3' '/tmp/proj/ECS/MDP1/meson/meson.py' '--internal' 'commandrunner' '/tmp/proj' '/tmp/proj/build' '' '/tmp/proj/rpmbuild.sh' '../common/MDP1/EnumValues.txt'
```

Is this worth reopening as something that should be revisited?

Yes, `files()` not working as expected with run targets is a bug.


Resolving patch:
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -515,10 +515,10 @@ def generate_run_target(self, target, outfile):
                 arg_strings.append(os.path.join(self.environment.get_build_dir(), relfname))
                 deps.append(relfname)
             elif isinstance(i, mesonlib.File):
-                arg_strings.append(i.rel_to_builddir(self.build_to_src))
+                relfname = i.rel_to_builddir(self.build_to_src)
+                arg_strings.append(os.path.join(self.environment.get_build_dir(), relfname))
             else:
-                mlog.debug(str(i))
-                raise MesonException('Unreachable code in generate_run_target.')
+                raise AssertionError('Unreachable code in generate_run_target: ' + str(i))
         elem = NinjaBuildElement(self.all_outputs, target.name, 'CUSTOM_COMMAND', [])
         cmd = runnerscript + [self.environment.get_source_dir(), self.environment.get_build_dir(), target.subdir]
         texe = target.command
diff --git a/run_unittests.py b/run_unittests.py
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -73,6 +73,9 @@ def init(self, srcdir):
     def build(self):
         self.output += subprocess.check_output(self.ninja_command)
 
+    def run_target(self, target):
+        self.output += subprocess.check_output(self.ninja_command + [target])
+
     def setconf(self, arg):
         self.output += subprocess.check_output(self.mconf_command + [arg, self.builddir])
 
@@ -173,5 +176,10 @@ def test_install_introspection(self):
         self.assertEqual(intro[0]['install_filename'], '/usr/local/libtest/libstat.a')
         self.assertEqual(intro[1]['install_filename'], '/usr/local/bin/prog')
 
+    def test_run_target_files_path(self):
+        testdir = os.path.join(self.common_test_dir, '58 run target')
+        self.init(testdir)
+        self.run_target('check_exists')
+
 if __name__ == '__main__':
     unittest.main()


Test patch:
diff --git a/test cases/common/58 run target/check_exists.py b/test cases/common/58 run target/check_exists.py
new file mode 100755
--- /dev/null
+++ b/test cases/common/58 run target/check_exists.py	
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+
+import os
+import sys
+
+if not os.path.isfile(sys.argv[1]):
+    raise Exception("Couldn't find {!r}".format(sys.argv[1]))
diff --git a/test cases/common/58 run target/meson.build b/test cases/common/58 run target/meson.build
--- a/test cases/common/58 run target/meson.build	
+++ b/test cases/common/58 run target/meson.build	
@@ -35,8 +35,8 @@ python3 = find_program('python3')
 run_target('py3hi',
   command : [python3, '-c', 'print("I am Python3.")'])
 
-run_target('ct_in_arg',
-  command : ['echo', hex, files('helloprinter.c')])
+run_target('check_exists',
+  command : [find_program('check_exists.py'), files('helloprinter.c')])
 
 # What if the output of a custom_target is the command to
 # execute. Obviously this will not work as hex is not an