Repository: mesonbuild/meson

Issue:
alias_target that depends on a run_target is quite broken
While testing #9174, I tried to create the target I alluded to, there.

In lib/libalpm/po/meson.build I added:

```
lib_mo = i18n.gettext(...) # this used to not be saved

alias_target('regenerate-all-pot', lib_mo[1])
```

In my build.ninja, I ended up with:

```
build regenerate-all-pot: phony  | lib/libalpm/po/libalpm-pot
```

This is plainly wrong!

It's reproducible with a simple meson.build

```
$ cat meson.build
project('foo', 'c')

subdir('in_subdir')
$ cat in_subdir/meson.build
r = run_target('run_a_target', command: ['echo', 'ran a target'])
alias_target('aliased_run_a_target', r)
```

However, it is not reproducible in released meson at all, because instead I get:

```
in_subdir/meson.build:2:0: ERROR: Depends items must be build targets.
```

This seems like a regression from commit 982973f303e3d0431d9b79f89cdf2c8f1ee67bc0 which removed that error message and made it accept any build.Target -- which I then assumed should work while testing my PR...
/cc @dcbaker 

I would actually sort of kind of like this to work properly as I'd like an alias_target that lets me run the i18n.gettext() pseudo-targets for multiple gettext domains in a single ninja invocation. Currently we have a 40-line shell script which spends most of its time trying to find the builddir.


Discussion:
I have a branch I'm adding type annotations to those, and i think I've fixed it there. I'll double check later (on my phone)
I remember trying this a while back and it being broken. Thanks for reporting it.
We cannot make a target that depends on a run_target(), because at the ninja level a target depends on the output file of another target, but run_target() has no output file.

The workaround you can do is replacing your run_target() by a custom_target() that generates a dummy file and make it always_stale.
Clearly we can and do right now, so I don't at all understand where you're getting this belief from.

It's a "phony" target, but of course it exists!

```
build libalpm-pot: phony meson-libalpm-pot

build meson-libalpm-pot: CUSTOM_COMMAND 
```

This is generated by the basic builtin functionality of meson. The objective here is to get back some handle for that ninja target, which is *already* called `meson-libalpm-pot` but aliased as `libalpm-pot`, to add even more aliases.

> The workaround you can do is replacing your run_target() by a custom_target() that generates a dummy file and make it always_stale.

I can't replace my anything, the run_target is internally generated by import('i18n'). The only reason this ticket doesn't *already* work, is because the name yielded is broken due to containing the subdir path.

Resolving patch:
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -358,7 +358,10 @@ def get_target_filename_for_linking(self, target: T.Union[build.Target, build.Cu
 
     @lru_cache(maxsize=None)
     def get_target_dir(self, target: T.Union[build.Target, build.CustomTargetIndex]) -> str:
-        if self.environment.coredata.get_option(OptionKey('layout')) == 'mirror':
+        if isinstance(target, build.RunTarget):
+            # this produces no output, only a dummy top-level name
+            dirname = ''
+        elif self.environment.coredata.get_option(OptionKey('layout')) == 'mirror':
             dirname = target.get_subdir()
         else:
             dirname = 'meson-out'
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -2528,6 +2528,10 @@ class AliasTarget(RunTarget):
     def __init__(self, name, dependencies, subdir, subproject):
         super().__init__(name, [], dependencies, subdir, subproject)
 
+    def __repr__(self):
+        repr_str = "<{0} {1}>"
+        return repr_str.format(self.__class__.__name__, self.get_id())
+
 class Jar(BuildTarget):
     known_kwargs = known_jar_kwargs
 
diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py
--- a/unittests/allplatformstests.py
+++ b/unittests/allplatformstests.py
@@ -3076,6 +3076,8 @@ def test_alias_target(self):
         self.run_target('build-all')
         self.assertPathExists(os.path.join(self.builddir, 'prog' + exe_suffix))
         self.assertPathExists(os.path.join(self.builddir, 'hello.txt'))
+        out = self.run_target('aliased-run')
+        self.assertIn('a run target was here', out)
 
     def test_configure(self):
         testdir = os.path.join(self.common_test_dir, '2 cpp')


Test patch:
diff --git a/test cases/unit/65 alias target/meson.build b/test cases/unit/65 alias target/meson.build
--- a/test cases/unit/65 alias target/meson.build	
+++ b/test cases/unit/65 alias target/meson.build	
@@ -13,3 +13,5 @@ custom_target = custom_target('custom-target',
 )
 
 alias_target('build-all', [exe_target, custom_target])
+
+subdir('subdir')
diff --git a/test cases/unit/65 alias target/subdir/meson.build b/test cases/unit/65 alias target/subdir/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/unit/65 alias target/subdir/meson.build	
@@ -0,0 +1,6 @@
+r = run_target('run-target',
+  command: [python3, '-c', 'print("a run target was here")']
+)
+
+alias_target('aliased-run', r)
+