Make custom_target() name argument optional

pull/9328/head
Xavier Claessens 3 years ago committed by Xavier Claessens
parent e58b3adc70
commit d2fa6d5080
  1. 16
      docs/markdown/Reference-manual.md
  2. 4
      docs/markdown/snippets/custom_target_name.md
  3. 3
      mesonbuild/backend/ninjabackend.py
  4. 2
      mesonbuild/build.py
  5. 13
      mesonbuild/interpreter/interpreter.py
  6. 0
      test cases/unit/99 custom target name/file.txt.in
  7. 14
      test cases/unit/99 custom target name/meson.build
  8. 9
      test cases/unit/99 custom target name/subdir/meson.build
  9. 8
      unittests/allplatformstests.py

@ -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

@ -0,0 +1,4 @@
## Optional `custom_target()` name
The name argument is now optional and defaults to the basename of the first
output.

@ -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())

@ -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.')

@ -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'])

@ -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')

@ -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,
)

@ -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)

Loading…
Cancel
Save