Skip to content

Commit

Permalink
implement @PLAINNAME0@ and @BASENAME0@
Browse files Browse the repository at this point in the history
@Plainname@ and @basename@ cannot be used in custom_target()
with multiple inputs. For those, similar macros are needed
with an index.

Fixes #13164
  • Loading branch information
stsp authored and dcbaker committed May 9, 2024
1 parent f8aefe2 commit cfd5718
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 4 deletions.
11 changes: 11 additions & 0 deletions docs/markdown/snippets/pln_bsn_support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## Support of indexed `@PLAINNAME@` and `@BASENAME@`

In `custom_target()` and `configure_file()` with multiple inputs,
it is now possible to specify index for `@PLAINNAME@` and `@BASENAME@`
macros in `output`:
```
custom_target('target_name',
output: '@[email protected]',
input: [dep1, dep2],
command: cmd)
```
4 changes: 3 additions & 1 deletion docs/yaml/functions/configure_file.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ kwargs:
type: str
description: |
The output file name. *(since 0.41.0)* may contain
`@PLAINNAME@` or `@BASENAME@` substitutions. In configuration mode,
`@PLAINNAME@` or `@BASENAME@` substitutions, as well as *(since 1.5.0)*
their indexed versions, like `@PLAINNAME0@` or `@BASENAME0@`.
In configuration mode,
the permissions of the input file (if it is specified) are copied to
the output file.
Expand Down
2 changes: 2 additions & 0 deletions docs/yaml/functions/custom_target.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ description: |
- `@OUTDIR@`: the full path to the directory where the output(s) must be written
- `@DEPFILE@`: the full path to the dependency file passed to `depfile`
- `@PLAINNAME@`: the input filename, without a path
- `@PLAINNAME0@` `@PLAINNAME1@` `...` *(since 1.5.0)*: the input filename without a path, with the specified array index in `input`
- `@BASENAME@`: the input filename, with extension removed
- `@BASENAME0@` `@BASENAME1@` `...` *(since 1.5.0)*: the input filename with extension removed, with the specified array index in `input`
- `@PRIVATE_DIR@` *(since 0.50.1)*: path to a directory where the custom target must store all its intermediate files.
- `@SOURCE_ROOT@`: the path to the root of the source tree. Depending on the backend,
this may be an absolute or a relative to current workdir path.
Expand Down
10 changes: 8 additions & 2 deletions mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -1983,17 +1983,23 @@ def func_vcs_tag(self, node: mparser.BaseNode, args: T.List['TYPE_var'], kwargs:
def func_subdir_done(self, node: mparser.BaseNode, args: TYPE_var, kwargs: TYPE_kwargs) -> T.NoReturn:
raise SubdirDoneRequest()

@staticmethod
def _validate_custom_target_outputs(has_multi_in: bool, outputs: T.Iterable[str], name: str) -> None:
def _validate_custom_target_outputs(self, has_multi_in: bool, outputs: T.Iterable[str], name: str) -> None:
"""Checks for additional invalid values in a custom_target output.
This cannot be done with typed_kwargs because it requires the number of
inputs.
"""
inregex: T.List[str] = ['@PLAINNAME[0-9]+@', '@BASENAME[0-9]+@']
from ..utils.universal import iter_regexin_iter
for out in outputs:
match = iter_regexin_iter(inregex, [out])
if has_multi_in and ('@PLAINNAME@' in out or '@BASENAME@' in out):
raise InvalidArguments(f'{name}: output cannot contain "@PLAINNAME@" or "@BASENAME@" '
'when there is more than one input (we can\'t know which to use)')
elif match:
FeatureNew.single_use(
f'{match} in output', '1.5.0',
self.subproject)

@typed_pos_args('custom_target', optargs=[str])
@typed_kwargs(
Expand Down
5 changes: 5 additions & 0 deletions mesonbuild/utils/universal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,8 @@ def get_filenames_templates_dict(inputs: T.List[str], outputs: T.List[str]) -> T
If there is more than one input file, the following keys are also created:
@INPUT0@, @INPUT1@, ... one for each input file
@PLAINNAME0@, @PLAINNAME1@, ... one for each input file
@BASENAME0@, @BASENAME1@, ... one for each input file
If there is more than one output file, the following keys are also created:
Expand All @@ -1757,6 +1759,9 @@ def get_filenames_templates_dict(inputs: T.List[str], outputs: T.List[str]) -> T
for (ii, vv) in enumerate(inputs):
# Write out @INPUT0@, @INPUT1@, ...
values[f'@INPUT{ii}@'] = vv
plain = os.path.basename(vv)
values[f'@PLAINNAME{ii}@'] = plain
values[f'@BASENAME{ii}@'] = os.path.splitext(plain)[0]
if len(inputs) == 1:
# Just one value, substitute @PLAINNAME@ and @BASENAME@
values['@PLAINNAME@'] = plain = os.path.basename(inputs[0])
Expand Down
11 changes: 10 additions & 1 deletion unittests/internaltests.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ def test_string_templates_substitution(self):
outputs = []
ret = dictfunc(inputs, outputs)
d = {'@INPUT@': inputs, '@INPUT0@': inputs[0],
'@PLAINNAME0@': 'foo.c.in', '@BASENAME0@': 'foo.c',
'@PLAINNAME@': 'foo.c.in', '@BASENAME@': 'foo.c'}
# Check dictionary
self.assertEqual(ret, d)
Expand All @@ -309,6 +310,7 @@ def test_string_templates_substitution(self):
outputs = ['out.c']
ret = dictfunc(inputs, outputs)
d = {'@INPUT@': inputs, '@INPUT0@': inputs[0],
'@PLAINNAME0@': 'foo.c.in', '@BASENAME0@': 'foo.c',
'@PLAINNAME@': 'foo.c.in', '@BASENAME@': 'foo.c',
'@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTDIR@': '.'}
# Check dictionary
Expand All @@ -330,6 +332,7 @@ def test_string_templates_substitution(self):
outputs = ['dir/out.c']
ret = dictfunc(inputs, outputs)
d = {'@INPUT@': inputs, '@INPUT0@': inputs[0],
'@PLAINNAME0@': 'foo.c.in', '@BASENAME0@': 'foo.c',
'@PLAINNAME@': 'foo.c.in', '@BASENAME@': 'foo.c',
'@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTDIR@': 'dir'}
# Check dictionary
Expand All @@ -339,7 +342,9 @@ def test_string_templates_substitution(self):
inputs = ['bar/foo.c.in', 'baz/foo.c.in']
outputs = []
ret = dictfunc(inputs, outputs)
d = {'@INPUT@': inputs, '@INPUT0@': inputs[0], '@INPUT1@': inputs[1]}
d = {'@INPUT@': inputs, '@INPUT0@': inputs[0], '@INPUT1@': inputs[1],
'@PLAINNAME0@': 'foo.c.in', '@PLAINNAME1@': 'foo.c.in',
'@BASENAME0@': 'foo.c', '@BASENAME1@': 'foo.c'}
# Check dictionary
self.assertEqual(ret, d)
# Check substitutions
Expand Down Expand Up @@ -376,6 +381,8 @@ def test_string_templates_substitution(self):
outputs = ['dir/out.c']
ret = dictfunc(inputs, outputs)
d = {'@INPUT@': inputs, '@INPUT0@': inputs[0], '@INPUT1@': inputs[1],
'@PLAINNAME0@': 'foo.c.in', '@PLAINNAME1@': 'foo.c.in',
'@BASENAME0@': 'foo.c', '@BASENAME1@': 'foo.c',
'@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTDIR@': 'dir'}
# Check dictionary
self.assertEqual(ret, d)
Expand All @@ -402,6 +409,8 @@ def test_string_templates_substitution(self):
outputs = ['dir/out.c', 'dir/out2.c']
ret = dictfunc(inputs, outputs)
d = {'@INPUT@': inputs, '@INPUT0@': inputs[0], '@INPUT1@': inputs[1],
'@PLAINNAME0@': 'foo.c.in', '@PLAINNAME1@': 'foo.c.in',
'@BASENAME0@': 'foo.c', '@BASENAME1@': 'foo.c',
'@OUTPUT@': outputs, '@OUTPUT0@': outputs[0], '@OUTPUT1@': outputs[1],
'@OUTDIR@': 'dir'}
# Check dictionary
Expand Down

0 comments on commit cfd5718

Please sign in to comment.