diff --git a/docs/markdown/Commands.md b/docs/markdown/Commands.md index efc316ce7..3fcdedd03 100644 --- a/docs/markdown/Commands.md +++ b/docs/markdown/Commands.md @@ -58,14 +58,17 @@ Builds a default or a specified target of a configured Meson project. *(since 0.55.0)* -`TARGET` has the following syntax `[PATH/]NAME[:TYPE]`, where: +`TARGET` has the following syntax `[PATH/]NAME.SUFFIX[:TYPE]`, where: - `NAME`: name of the target from `meson.build` (e.g. `foo` from `executable('foo', ...)`). +- `SUFFIX`: name of the suffix of the target from `meson.build` (e.g. `exe` from `executable('foo', suffix: 'exe', ...)`). - `PATH`: path to the target relative to the root `meson.build` file. Note: relative path for a target specified in the root `meson.build` is `./`. - `TYPE`: type of the target. Can be one of the following: 'executable', 'static_library', 'shared_library', 'shared_module', 'custom', 'alias', 'run', 'jar'. -`PATH` and/or `TYPE` can be omitted if the resulting `TARGET` can be +`PATH`, `SUFFIX`, and `TYPE` can all be omitted if the resulting `TARGET` can be used to uniquely identify the target in `meson.build`. +Note that `SUFFIX` did not exist prior to 1.3.0. + #### Backend specific arguments *(since 0.55.0)* diff --git a/docs/markdown/snippets/meson_compile_suffixes.md b/docs/markdown/snippets/meson_compile_suffixes.md new file mode 100644 index 000000000..481e8adf4 --- /dev/null +++ b/docs/markdown/snippets/meson_compile_suffixes.md @@ -0,0 +1,7 @@ +## Meson compile command now accepts suffixes for TARGET + +The syntax for specifying a target for meson compile is now +`[PATH_TO_TARGET/]TARGET_NAME.TARGET_SUFFIX[:TARGET_TYPE]` where +`TARGET_SUFFIX` is the suffix argument given in the build target +within meson.build. It is optional and `TARGET_NAME` remains +sufficient if it uniquely resolves to one single target. diff --git a/mesonbuild/mcompile.py b/mesonbuild/mcompile.py index 19875c22a..6721beb0b 100644 --- a/mesonbuild/mcompile.py +++ b/mesonbuild/mcompile.py @@ -61,9 +61,11 @@ def parse_introspect_data(builddir: Path) -> T.Dict[str, T.List[dict]]: class ParsedTargetName: full_name = '' + base_name = '' name = '' type = '' path = '' + suffix = '' def __init__(self, target: str): self.full_name = target @@ -80,6 +82,13 @@ class ParsedTargetName: else: self.name = split[0] + split = self.name.rsplit('.', 1) + if len(split) > 1: + self.base_name = split[0] + self.suffix = split[1] + else: + self.base_name = split[0] + @staticmethod def _is_valid_type(type: str) -> bool: # Amend docs in Commands.md when editing this list @@ -96,19 +105,32 @@ class ParsedTargetName: return type in allowed_types def get_target_from_intro_data(target: ParsedTargetName, builddir: Path, introspect_data: T.Dict[str, T.Any]) -> T.Dict[str, T.Any]: - if target.name not in introspect_data: + if target.name not in introspect_data and target.base_name not in introspect_data: raise MesonException(f'Can\'t invoke target `{target.full_name}`: target not found') intro_targets = introspect_data[target.name] + # if target.name doesn't find anything, try just the base name + if not intro_targets: + intro_targets = introspect_data[target.base_name] found_targets: T.List[T.Dict[str, T.Any]] = [] resolved_bdir = builddir.resolve() - if not target.type and not target.path: + if not target.type and not target.path and not target.suffix: found_targets = intro_targets else: for intro_target in intro_targets: + # Parse out the name from the id if needed + intro_target_name = intro_target['name'] + split = intro_target['id'].rsplit('@', 1) + if len(split) > 1: + split = split[0].split('@@', 1) + if len(split) > 1: + intro_target_name = split[1] + else: + intro_target_name = split[0] if ((target.type and target.type != intro_target['type'].replace(' ', '_')) or + (target.name != intro_target_name) or (target.path and intro_target['filename'] != 'no_name' and Path(target.path) != Path(intro_target['filename'][0]).relative_to(resolved_bdir).parent)): continue @@ -119,12 +141,20 @@ def get_target_from_intro_data(target: ParsedTargetName, builddir: Path, introsp elif len(found_targets) > 1: suggestions: T.List[str] = [] for i in found_targets: - p = Path(i['filename'][0]).relative_to(resolved_bdir).parent / i['name'] + i_name = i['name'] + split = i['id'].rsplit('@', 1) + if len(split) > 1: + split = split[0].split('@@', 1) + if len(split) > 1: + i_name = split[1] + else: + i_name = split[0] + p = Path(i['filename'][0]).relative_to(resolved_bdir).parent / i_name t = i['type'].replace(' ', '_') suggestions.append(f'- ./{p}:{t}') suggestions_str = '\n'.join(suggestions) raise MesonException(f'Can\'t invoke target `{target.full_name}`: ambiguous name.' - f'Add target type and/or path:\n{suggestions_str}') + f' Add target type and/or path:\n{suggestions_str}') return found_targets[0] @@ -278,7 +308,7 @@ def add_arguments(parser: 'argparse.ArgumentParser') -> None: metavar='TARGET', nargs='*', default=None, - help='Targets to build. Target has the following format: [PATH_TO_TARGET/]TARGET_NAME[:TARGET_TYPE].') + help='Targets to build. Target has the following format: [PATH_TO_TARGET/]TARGET_NAME.TARGET_SUFFIX[:TARGET_TYPE].') parser.add_argument( '--clean', action='store_true', diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 030b216d7..f5048837f 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -2049,6 +2049,15 @@ class AllPlatformTests(BasePlatformTests): self.assertPathExists(exe2) self.assertNotEqual(exe1, exe2) + # Wipe and run the compile command against the target names + self.init(testdir, extra_args=['--wipe']) + self._run([*self.meson_command, 'compile', '-C', self.builddir, './foo']) + self._run([*self.meson_command, 'compile', '-C', self.builddir, './foo.bin']) + self.assertPathExists(exe1) + self.assertPathExists(exe2) + self.assertNotEqual(exe1, exe2) + + def opt_has(self, name, value): res = self.introspect('--buildoptions') found = False