aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Reference-manual.md16
-rw-r--r--docs/markdown/snippets/custom_target_name.md4
-rw-r--r--mesonbuild/backend/ninjabackend.py3
-rw-r--r--mesonbuild/build.py2
-rw-r--r--mesonbuild/interpreter/interpreter.py13
-rw-r--r--test cases/unit/99 custom target name/file.txt.in0
-rw-r--r--test cases/unit/99 custom target name/meson.build14
-rw-r--r--test cases/unit/99 custom target name/subdir/meson.build9
-rw-r--r--unittests/allplatformstests.py8
9 files changed, 63 insertions, 6 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index b3dbf65..a91fa3e 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -326,8 +326,20 @@ false otherwise.
```
Create a custom top level build target. The only positional argument
-is the name of this target and the keyword arguments are the
-following.
+is the name of this target and cannot contain path separators (`/` or `\`).
+The name of custom target might not be used by every backends, for instance with
+the Ninja backend, `subdir/meson.build` containing the example below,
+`ninja -C builddir foo` or `ninja -C builddir subdir/foo` won't work,
+it is instead `ninja -C builddir subdir/file.txt`. Howerver, `meson compile subdir/foo`
+is accepted.
+```meson
+custom_target('foo', output: 'file.txt', ...)
+```
+
+*Since 0.60.0* the name argument is optional and defaults to the basename of the first
+output (`file.txt` in the example above).
+
+These are all the supported keyword arguments:
- `build_by_default` *(since 0.38.0)*: causes, when set to true, to
have this target be built by default. This means it will be built when
diff --git a/docs/markdown/snippets/custom_target_name.md b/docs/markdown/snippets/custom_target_name.md
new file mode 100644
index 0000000..5ad136d
--- /dev/null
+++ b/docs/markdown/snippets/custom_target_name.md
@@ -0,0 +1,4 @@
+## Optional `custom_target()` name
+
+The name argument is now optional and defaults to the basename of the first
+output. \ No newline at end of file
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 5dbbf8b..80a1c56 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -1012,8 +1012,9 @@ class NinjaBackend(backends.Backend):
elem.add_item('DEPFILE', rel_dfile)
if target.console:
elem.add_item('pool', 'console')
+ full_name = Path(target.subdir, target.name).as_posix()
elem.add_item('COMMAND', cmd)
- elem.add_item('description', f'Generating {target.name} with a custom command{cmd_type}')
+ elem.add_item('description', f'Generating {full_name} with a custom command{cmd_type}')
self.add_build(elem)
self.processed_targets.add(target.get_id())
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 24eff8c..e22f1b1 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -2360,6 +2360,8 @@ class CustomTarget(Target, CommandBase):
"there is more than one input (we can't know which to use)"
raise InvalidArguments(m)
self.outputs = substitute_values(self.outputs, values)
+ if not self.name:
+ self.name = self.outputs[0]
self.capture = kwargs.get('capture', False)
if self.capture and len(self.outputs) != 1:
raise InvalidArguments('Capturing can only output to a single file.')
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index c452251..8bd68b8 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -1633,8 +1633,8 @@ external dependencies (including libraries) must go to "dependencies".''')
'build_always', 'capture', 'depends', 'depend_files', 'depfile',
'build_by_default', 'build_always_stale', 'console', 'env',
'feed', 'install_tag'})
- @typed_pos_args('custom_target', str)
- def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> build.CustomTarget:
+ @typed_pos_args('custom_target', optargs=[str])
+ def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[T.Optional[str]], kwargs: 'TYPE_kwargs') -> build.CustomTarget:
if 'depfile' in kwargs and ('@BASENAME@' in kwargs['depfile'] or '@PLAINNAME@' in kwargs['depfile']):
FeatureNew.single_use('substitutions in custom_target depfile', '0.47.0', self.subproject)
return self._func_custom_target_impl(node, args, kwargs)
@@ -1642,6 +1642,13 @@ external dependencies (including libraries) must go to "dependencies".''')
def _func_custom_target_impl(self, node, args, kwargs):
'Implementation-only, without FeatureNew checks, for internal use'
name = args[0]
+ if name is None:
+ # name will default to first output, but we cannot do that yet because
+ # they could need substitutions (e.g. @BASENAME@) first. CustomTarget()
+ # will take care of setting a proper default but name must be an empty
+ # string in the meantime.
+ FeatureNew('custom_target() with no name argument', '0.60.0').use(self.subproject)
+ name = ''
kwargs['install_mode'] = self._get_kwarg_install_mode(kwargs)
if 'input' in kwargs:
try:
@@ -1654,7 +1661,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
if isinstance(kwargs['command'][0], str):
kwargs['command'][0] = self.func_find_program(node, kwargs['command'][0], {})
tg = build.CustomTarget(name, self.subdir, self.subproject, kwargs, backend=self.backend)
- self.add_target(name, tg)
+ self.add_target(tg.name, tg)
return tg
@FeatureNewKwargs('run_target', '0.57.0', ['env'])
diff --git a/test cases/unit/99 custom target name/file.txt.in b/test cases/unit/99 custom target name/file.txt.in
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test cases/unit/99 custom target name/file.txt.in
diff --git a/test cases/unit/99 custom target name/meson.build b/test cases/unit/99 custom target name/meson.build
new file mode 100644
index 0000000..8d148a8
--- /dev/null
+++ b/test cases/unit/99 custom target name/meson.build
@@ -0,0 +1,14 @@
+project('custom target name', 'c')
+
+python = import('python').find_installation()
+
+# Name argument is optional and should default to 'file.txt'
+custom_target(
+ input: 'file.txt.in',
+ output: '@BASENAME@',
+ command: [python, '--version'],
+ build_by_default: true,
+ capture: true,
+)
+
+subdir('subdir')
diff --git a/test cases/unit/99 custom target name/subdir/meson.build b/test cases/unit/99 custom target name/subdir/meson.build
new file mode 100644
index 0000000..785a7b3
--- /dev/null
+++ b/test cases/unit/99 custom target name/subdir/meson.build
@@ -0,0 +1,9 @@
+# Name argument is optional and should default to 'file.txt', but should be
+# displayed as 'subdir/file.txt' by ninja.
+custom_target(
+ input: '../file.txt.in',
+ output: ['@BASENAME@'],
+ command: [python, '--version'],
+ build_by_default: true,
+ capture: true,
+)
diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py
index e6ddab3..b8cdfb6 100644
--- a/unittests/allplatformstests.py
+++ b/unittests/allplatformstests.py
@@ -4061,3 +4061,11 @@ class AllPlatformTests(BasePlatformTests):
with self.assertRaises(subprocess.CalledProcessError) as cm:
self.build()
self.assertIn('error: use of a blacklisted/placeholder name `foo`', cm.exception.stdout)
+
+ def test_custom_target_name(self):
+ testdir = os.path.join(self.unit_test_dir, '99 custom target name')
+ self.init(testdir)
+ out = self.build()
+ if self.backend is Backend.ninja:
+ self.assertIn('Generating file.txt with a custom command', out)
+ self.assertIn('Generating subdir/file.txt with a custom command', out)