From 11f96380351a88059ec55f1070fdebc1b1033117 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 19 Jan 2022 12:42:25 -0800 Subject: [PATCH] build: replace kwargs in CustomTarget initializer Because we don't want to pass the Interpreter kwargs into the build layer. This turned out to be a mega commit, as there's really on elegant way to make this change in an incremental way. On the nice side, mypy made this change super easy, as nearly all of the calls to `CustomTarget` are fully type checked! It also turns out that we're not handling install_tags in custom_target correctly, since we're not converting the boolean values into Optional values! --- mesonbuild/build.py | 214 +++------- mesonbuild/interpreter/interpreter.py | 125 ++++-- mesonbuild/interpreter/kwargs.py | 6 +- mesonbuild/interpreter/type_checking.py | 3 + mesonbuild/modules/gnome.py | 383 ++++++++++-------- mesonbuild/modules/hotdoc.py | 7 +- mesonbuild/modules/i18n.py | 77 ++-- mesonbuild/modules/java.py | 18 +- mesonbuild/modules/qt.py | 59 +-- .../modules/unstable_external_project.py | 19 +- mesonbuild/modules/unstable_rust.py | 22 +- mesonbuild/modules/windows.py | 27 +- .../test.json | 2 +- 13 files changed, 497 insertions(+), 465 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index a392f9824..3fa647a61 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations from collections import OrderedDict from dataclasses import dataclass, field from functools import lru_cache @@ -24,6 +25,7 @@ import re import textwrap import typing as T + from . import environment from . import dependencies from . import mlog @@ -2328,47 +2330,78 @@ class CommandBase: return final_cmd class CustomTarget(Target, CommandBase): - known_kwargs = { - 'input', - 'output', - 'command', - 'capture', - 'feed', - 'install', - 'install_dir', - 'install_mode', - 'install_tag', - 'build_always', - 'build_always_stale', - 'depends', - 'depend_files', - 'depfile', - 'build_by_default', - 'override_options', - 'console', - 'env', - } - install_dir: T.List[T.Union[str, bool]] + typename = 'custom' - def __init__(self, name: str, subdir: str, subproject: str, kwargs: T.Mapping[str, T.Any], - absolute_paths: bool = False, backend: T.Optional['Backend'] = None): - self.typename = 'custom' + def __init__(self, + name: T.Optional[str], + subdir: str, + subproject: str, + command: T.Sequence[T.Union[ + str, BuildTarget, CustomTarget, CustomTargetIndex, GeneratedList, programs.ExternalProgram, File]], + sources: T.Sequence[T.Union[ + str, File, BuildTarget, CustomTarget, CustomTargetIndex, + ExtractedObjects, GeneratedList, programs.ExternalProgram]], + outputs: T.List[str], + *, + build_always_stale: bool = False, + build_by_default: T.Optional[bool] = None, + capture: bool = False, + console: bool = False, + depend_files: T.Optional[T.Sequence[FileOrString]] = None, + extra_depends: T.Optional[T.Sequence[T.Union[str, SourceOutputs]]] = None, + depfile: T.Optional[str] = None, + env: T.Optional[EnvironmentVariables] = None, + feed: bool = False, + install: bool = False, + install_dir: T.Optional[T.Sequence[T.Union[str, bool]]] = None, + install_mode: T.Optional[FileMode] = None, + install_tag: T.Optional[T.Sequence[T.Optional[str]]] = None, + override_options: T.Optional[T.Dict[OptionKey, str]] = None, + absolute_paths: bool = False, + backend: T.Optional['Backend'] = None, + ): # TODO expose keyword arg to make MachineChoice.HOST configurable super().__init__(name, subdir, subproject, False, MachineChoice.HOST) + self.sources = list(sources) + self.outputs = substitute_values( + outputs, get_filenames_templates_dict( + get_sources_string_names(sources, backend), + [])) + self.build_by_default = build_by_default if build_by_default is not None else install + self.build_always_stale = build_always_stale + self.capture = capture + self.console = console + self.depend_files = list(depend_files or []) self.dependencies: T.List[T.Union[CustomTarget, BuildTarget]] = [] - self.extra_depends: T.List[T.Union[CustomTarget, BuildTarget]] = [] - self.depend_files = [] # Files that this target depends on but are not on the command line. - self.depfile = None - self.process_kwargs(kwargs, backend) + # must be after depend_files and dependencies + self.command = self.flatten_command(command) + self.depfile = depfile + self.env = env or EnvironmentVariables() + self.extra_depends = list(extra_depends or []) + self.feed = feed + self.install = install + self.install_dir = list(install_dir or []) + self.install_mode = install_mode + _install_tag: T.List[T.Optional[str]] + if not install_tag: + _install_tag = [None] * len(self.outputs) + elif len(install_tag) == 1: + _install_tag = list(install_tag) * len(self.outputs) + else: + _install_tag = list(install_tag) + self.install_tag = _install_tag + self.name = name if name else self.outputs[0] + + if override_options: + for k, v in override_options.items(): + if k.lang: + self.option_overrides_compiler[k.evolve(machine=self.for_machine)] = v + else: + self.option_overrides_base[k] = v + # Whether to use absolute paths for all files on the commandline self.absolute_paths = absolute_paths - unknowns = [] - for k in kwargs: - if k not in CustomTarget.known_kwargs: - unknowns.append(k) - if unknowns: - mlog.warning('Unknown keyword arguments in target {}: {}'.format(self.name, ', '.join(unknowns))) def get_default_install_dir(self, environment) -> T.Tuple[str, str]: return None, None @@ -2405,119 +2438,6 @@ class CustomTarget(Target, CommandBase): bdeps.update(d.get_transitive_build_target_deps()) return bdeps - def process_kwargs(self, kwargs, backend): - self.process_kwargs_base(kwargs) - self.sources = extract_as_list(kwargs, 'input') - if 'output' not in kwargs: - raise InvalidArguments('Missing keyword argument "output".') - self.outputs = listify(kwargs['output']) - # This will substitute values from the input into output and return it. - inputs = get_sources_string_names(self.sources, backend) - values = get_filenames_templates_dict(inputs, []) - for i in self.outputs: - if not isinstance(i, str): - raise InvalidArguments('Output argument not a string.') - if i == '': - raise InvalidArguments('Output must not be empty.') - if i.strip() == '': - raise InvalidArguments('Output must not consist only of whitespace.') - if has_path_sep(i): - raise InvalidArguments(f'Output {i!r} must not contain a path segment.') - if '@INPUT@' in i or '@INPUT0@' in i: - m = 'Output cannot contain @INPUT@ or @INPUT0@, did you ' \ - 'mean @PLAINNAME@ or @BASENAME@?' - raise InvalidArguments(m) - # We already check this during substitution, but the error message - # will be unclear/confusing, so check it here. - if len(inputs) != 1 and ('@PLAINNAME@' in i or '@BASENAME@' in i): - m = "Output cannot contain @PLAINNAME@ or @BASENAME@ when " \ - "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.') - self.feed = kwargs.get('feed', False) - if self.feed and len(self.sources) != 1: - raise InvalidArguments('Feeding can only input from a single file.') - self.console = kwargs.get('console', False) - if not isinstance(self.console, bool): - raise InvalidArguments('"console" kwarg only accepts booleans') - if self.capture and self.console: - raise InvalidArguments("Can't both capture output and output to console") - if 'command' not in kwargs: - raise InvalidArguments('Missing keyword argument "command".') - if kwargs.get('depfile') is not None: - depfile = kwargs['depfile'] - if not isinstance(depfile, str): - raise InvalidArguments('Depfile must be a string.') - if os.path.basename(depfile) != depfile: - raise InvalidArguments('Depfile must be a plain filename without a subdirectory.') - self.depfile = depfile - self.command = self.flatten_command(kwargs['command']) - for c in self.command: - if self.capture and isinstance(c, str) and '@OUTPUT@' in c: - raise InvalidArguments('@OUTPUT@ is not allowed when capturing output.') - if self.feed and isinstance(c, str) and '@INPUT@' in c: - raise InvalidArguments('@INPUT@ is not allowed when feeding input.') - if 'install' in kwargs: - self.install = kwargs['install'] - if not isinstance(self.install, bool): - raise InvalidArguments('"install" must be boolean.') - if self.install: - if not kwargs.get('install_dir', False): - raise InvalidArguments('"install_dir" must be specified ' - 'when installing a target') - - if isinstance(kwargs['install_dir'], list): - FeatureNew.single_use('multiple install_dir for custom_target', '0.40.0', self.subproject) - # If an item in this list is False, the output corresponding to - # the list index of that item will not be installed - self.install_dir = typeslistify(kwargs['install_dir'], (str, bool)) - self.install_mode = kwargs.get('install_mode', None) - # If only one tag is provided, assume all outputs have the same tag. - # Otherwise, we must have as much tags as outputs. - install_tag: T.List[T.Union[str, bool, None]] = typeslistify(kwargs.get('install_tag', []), (str, bool, type(None))) - if not install_tag: - self.install_tag = [None] * len(self.outputs) - elif len(install_tag) == 1: - self.install_tag = install_tag * len(self.outputs) - elif install_tag and len(install_tag) != len(self.outputs): - m = f'Target {self.name!r} has {len(self.outputs)} outputs but {len(install_tag)} "install_tag"s were found.' - raise InvalidArguments(m) - else: - self.install_tag = install_tag - else: - self.install = False - self.install_dir = [] - self.install_mode = None - self.install_tag = [] - if kwargs.get('build_always') is not None and kwargs.get('build_always_stale') is not None: - raise InvalidArguments('build_always and build_always_stale are mutually exclusive. Combine build_by_default and build_always_stale.') - elif kwargs.get('build_always') is not None: - if kwargs.get('build_by_default') is not None: - self.build_by_default = kwargs['build_always'] - self.build_always_stale = kwargs['build_always'] - elif kwargs.get('build_always_stale') is not None: - self.build_always_stale = kwargs['build_always_stale'] - if not isinstance(self.build_always_stale, bool): - raise InvalidArguments('Argument build_always_stale must be a boolean.') - extra_deps, depend_files = (extract_as_list(kwargs, c, pop=False) for c in ['depends', 'depend_files']) - for ed in extra_deps: - if not isinstance(ed, (CustomTarget, BuildTarget)): - raise InvalidArguments('Can only depend on toplevel targets: custom_target or build_target ' - f'(executable or a library) got: {type(ed)}({ed})') - self.extra_depends.append(ed) - for i in depend_files: - if isinstance(i, (File, str)): - self.depend_files.append(i) - else: - mlog.debug(i) - raise InvalidArguments(f'Unknown type {type(i).__name__!r} in depend_files.') - self.env = kwargs.get('env') - def get_dependencies(self): return self.dependencies diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index bed927880..768183494 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -50,7 +50,9 @@ from .interpreterobjects import ( NullSubprojectInterpreter, ) from .type_checking import ( - COMMAND_KW, CT_BUILD_ALWAYS, CT_BUILD_ALWAYS_STALE, + COMMAND_KW, + CT_BUILD_ALWAYS, + CT_BUILD_ALWAYS_STALE, CT_BUILD_BY_DEFAULT, CT_INPUT_KW, CT_INSTALL_DIR_KW, @@ -1696,8 +1698,14 @@ external dependencies (including libraries) must go to "dependencies".''') else: vcs_cmd = [' '] # executing this cmd will fail in vcstagger.py and force to use the fallback string # vcstagger.py parameters: infile, outfile, fallback, source_dir, replace_string, regex_selector, command... - cmd_kwargs = { - 'command': self.environment.get_build_command() + \ + + self._validate_custom_target_outputs(len(kwargs['input']) > 1, kwargs['output'], "vcs_tag") + + tg = build.CustomTarget( + kwargs['output'][0], + self.subdir, + self.subproject, + self.environment.get_build_command() + \ ['--internal', 'vcstagger', '@INPUT0@', @@ -1706,12 +1714,13 @@ external dependencies (including libraries) must go to "dependencies".''') source_dir, replace_string, regex_selector] + vcs_cmd, - 'input': kwargs['input'], - 'output': kwargs['output'], - 'build_by_default': True, - 'build_always_stale':True, - } - return self._func_custom_target_impl(node, [kwargs['output']], cmd_kwargs) + self.source_strings_to_files(kwargs['input']), + kwargs['output'], + build_by_default=True, + build_always_stale=True, + ) + self.add_target(tg.name, tg) + return tg @FeatureNew('subdir_done', '0.46.0') @noPosargs @@ -1719,6 +1728,18 @@ external dependencies (including libraries) must go to "dependencies".''') def func_subdir_done(self, node, args, kwargs): raise SubdirDoneRequest() + @staticmethod + def _validate_custom_target_outputs(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. + """ + for out in outputs: + if has_multi_in and ('@PLAINNAME@' in out or '@BASENAME@' in out): + raise InvalidArguments(f'{name}: output cannot containe "@PLAINNAME@" or "@BASENAME@" ' + 'when there is more than one input (we can\'t know which to use)') + @typed_pos_args('custom_target', optargs=[str]) @typed_kwargs( 'custom_target', @@ -1747,49 +1768,87 @@ external dependencies (including libraries) must go to "dependencies".''') FeatureNew.single_use('substitutions in custom_target depfile', '0.47.0', self.subproject, location=node) # Don't mutate the kwargs - kwargs = kwargs.copy() + build_by_default = kwargs['build_by_default'] + build_always_stale = kwargs['build_always_stale'] # Remap build_always to build_by_default and build_always_stale if kwargs['build_always'] is not None and kwargs['build_always_stale'] is not None: raise InterpreterException('CustomTarget: "build_always" and "build_always_stale" are mutually exclusive') - if kwargs['build_by_default'] is None and kwargs['install']: - kwargs['build_by_default'] = True + if build_by_default is None and kwargs['install']: + build_by_default = True elif kwargs['build_always'] is not None: - if kwargs['build_by_default'] is None: - kwargs['build_by_default'] = kwargs['build_always'] - kwargs['build_always_stale'] = kwargs['build_by_default'] - - # Set this to None to satisfy process_kwargs - kwargs['build_always'] = None + if build_by_default is None: + build_by_default = kwargs['build_always'] + build_always_stale = kwargs['build_by_default'] # These are are nullaable so that we can know whether they're explicitly # set or not. If they haven't been overwritten, set them to their true # default - if kwargs['build_by_default'] is None: - kwargs['build_by_default'] = False - if kwargs['build_always_stale'] is None: - kwargs['build_always_stale'] = False + if build_by_default is None: + build_by_default = False + if build_always_stale is None: + build_always_stale = False - return self._func_custom_target_impl(node, args, kwargs) - - 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', location=node).use(self.subproject) + FeatureNew.single_use('custom_target() with no name argument', '0.60.0', self.subproject, location=node) name = '' - if 'input' in kwargs: - kwargs['input'] = self.source_strings_to_files(extract_as_list(kwargs, 'input'), strict=False) - if 'command' in kwargs and isinstance(kwargs['command'], list) and kwargs['command']: - if isinstance(kwargs['command'][0], str): - kwargs['command'][0] = self.find_program_impl([kwargs['command'][0]]) - tg = build.CustomTarget(name, self.subdir, self.subproject, kwargs, backend=self.backend) + inputs = self.source_strings_to_files(kwargs['input'], strict=False) + command = kwargs['command'] + if command and isinstance(command[0], str): + command[0] = self.find_program_impl([command[0]]) + + if len(inputs) > 1 and kwargs['feed']: + raise InvalidArguments('custom_target: "feed" keyword argument can only be used used with a single input') + if len(kwargs['output']) > 1 and kwargs['capture']: + raise InvalidArguments('custom_target: "capture" keyword argument can only be used used with a single output') + if kwargs['capture'] and kwargs['console']: + raise InvalidArguments('custom_target: "capture" and "console" keyword arguments are mutually exclusive') + for c in command: + if kwargs['capture'] and isinstance(c, str) and '@OUTPUT@' in c: + raise InvalidArguments('custom_target: "capture" keyword argument cannot be used with "@OUTPUT@"') + if kwargs['feed'] and isinstance(c, str) and '@INPUT@' in c: + raise InvalidArguments('custom_target: "feed" keyword argument cannot be used with "@INPUT@"') + if kwargs['install'] and not kwargs['install_dir']: + raise InvalidArguments('custom_target: "install_dir" keyword argument must be set when "install" is true.') + if len(kwargs['install_dir']) > 1: + FeatureNew.single_use('multiple install_dir for custom_target', '0.40.0', self.subproject, location=node) + if len(kwargs['install_tag']) not in {0, 1, len(kwargs['output'])}: + raise InvalidArguments('custom_target: install_tag argument must have 0 or 1 outputs, ' + 'or the same number of elements as the output keyword argument. ' + f'(there are {len(kwargs["install_tag"])} install_tags, ' + f'and {len(kwargs["output"])} outputs)') + + self._validate_custom_target_outputs(len(inputs) > 1, kwargs['output'], "custom_target") + + tg = build.CustomTarget( + name, + self.subdir, + self.subproject, + command, + inputs, + kwargs['output'], + build_always_stale=build_always_stale, + build_by_default=build_by_default, + capture=kwargs['capture'], + console=kwargs['console'], + depend_files=kwargs['depend_files'], + depfile=kwargs['depfile'], + extra_depends=kwargs['depends'], + env=kwargs['env'], + feed=kwargs['feed'], + install=kwargs['install'], + install_dir=kwargs['install_dir'], + install_mode=kwargs['install_mode'], + install_tag=kwargs['install_tag'], + override_options=kwargs['override_options'], + backend=self.backend) self.add_target(tg.name, tg) return tg diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 99c54c155..b97fe050b 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -167,8 +167,8 @@ class RunTarget(TypedDict): class CustomTarget(TypedDict): build_always: bool - build_always_stale: bool - build_by_default: bool + build_always_stale: T.Optional[bool] + build_by_default: T.Optional[bool] capture: bool command: T.List[T.Union[str, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, ExternalProgram, File]] @@ -183,7 +183,7 @@ class CustomTarget(TypedDict): install: bool install_dir: T.List[T.Union[str, bool]] install_mode: FileMode - install_tag: T.List[T.Union[str, bool]] + install_tag: T.List[T.Optional[str]] output: T.List[str] override_options: T.Dict[OptionKey, str] diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 52a900561..15fa61bf2 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -244,6 +244,8 @@ def _output_validator(outputs: T.List[str]) -> T.Optional[str]: return 'Output must not consist only of whitespace.' elif has_path_sep(i): return f'Output {i!r} must not contain a path segment.' + elif '@INPUT' in i: + return f'output {i!r} contains "@INPUT", which is invalid. Did you mean "@PLAINNAME@" or "@BASENAME@?' return None @@ -269,6 +271,7 @@ CT_INSTALL_TAG_KW: KwargInfo[T.List[T.Union[str, bool]]] = KwargInfo( listify=True, default=[], since='0.60.0', + convertor=lambda x: [y if isinstance(y, str) else None for y in x], ) INSTALL_KW = KwargInfo('install', bool, default=False) diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index ffdfd4211..37d4d92c7 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -148,7 +148,7 @@ if T.TYPE_CHECKING: build_by_default: T.Optional[bool] depend_files: T.List[mesonlib.File] extra_args: T.List[str] - install_dir: T.List[T.Union[str, bool]] + install_dir: T.Union[str, bool] install_header: bool internal: bool nostdinc: bool @@ -404,7 +404,7 @@ class GnomeModule(ExtensionModule): glib_version = self._get_native_glib_version(state) glib_compile_resources = state.find_program('glib-compile-resources') - cmd = [glib_compile_resources, '@INPUT@'] + cmd: T.List[T.Union[ExternalProgram, str]] = [glib_compile_resources, '@INPUT@'] source_dirs = kwargs['source_dir'] dependencies = kwargs['dependencies'] @@ -486,39 +486,47 @@ class GnomeModule(ExtensionModule): if install_header and not kwargs['export']: raise MesonException('GResource header is installed yet export is not enabled') - c_kwargs: T.Dict[str, T.Any] = { - 'build_by_default': kwargs['build_by_default'], - 'depends': depends, - 'input': input_file, - 'install': kwargs['install'], - 'install_dir': kwargs['install_dir'] or [], - 'output': output, - } + depfile: T.Optional[str] = None + target_cmd: T.List[T.Union[ExternalProgram, str]] if not mesonlib.version_compare(glib_version, gresource_dep_needed_version): # This will eventually go out of sync if dependencies are added - c_kwargs['depend_files'] = depend_files - c_kwargs['command'] = cmd + target_cmd = cmd else: depfile = f'{output}.d' - c_kwargs['depfile'] = depfile - c_kwargs['command'] = copy.copy(cmd) + ['--dependency-file', '@DEPFILE@'] - target_c = GResourceTarget(name, state.subdir, state.subproject, c_kwargs) + depend_files = [] + target_cmd = copy.copy(cmd) + ['--dependency-file', '@DEPFILE@'] + target_c = GResourceTarget( + name, + state.subdir, + state.subproject, + target_cmd, + [input_file], + [output], + build_by_default=kwargs['build_by_default'], + depfile=depfile, + depend_files=depend_files, + extra_depends=depends, + install=kwargs['install'], + install_dir=[kwargs['install_dir']] if kwargs['install_dir'] else [], + ) if gresource: # Only one target for .gresource files return ModuleReturnValue(target_c, [target_c]) - h_kwargs: T.Dict[str, T.Any] = { - 'command': cmd, - 'input': input_file, - 'output': f'{target_name}.h', - # The header doesn't actually care about the files yet it errors if missing - 'depends': depends, - 'build_by_default': kwargs['build_by_default'], - 'install_dir': kwargs['install_dir'] or [state.environment.coredata.get_option(mesonlib.OptionKey('includedir'))], - } - if install_header: - h_kwargs['install'] = install_header - target_h = GResourceHeaderTarget(f'{target_name}_h', state.subdir, state.subproject, h_kwargs) + install_dir = kwargs['install_dir'] or state.environment.coredata.get_option(mesonlib.OptionKey('includedir')) + assert isinstance(install_dir, str), 'for mypy' + target_h = GResourceHeaderTarget( + f'{target_name}_h', + state.subdir, + state.subproject, + cmd, + [input_file], + [f'{target_name}.h'], + build_by_default=kwargs['build_by_default'], + extra_depends=depends, + install=install_header, + install_dir=[install_dir], + ) rv = [target_c, target_h] return ModuleReturnValue(rv, rv) @@ -932,18 +940,19 @@ class GnomeModule(ExtensionModule): elif install_dir is False: install = False - scankwargs = { - 'input': generated_files, - 'output': girfile, - 'command': scan_command, - 'depends': depends, - 'install': install, - 'install_dir': install_dir, - 'install_tag': 'devel', - 'build_by_default': kwargs['build_by_default'], - } - - return GirTarget(girfile, state.subdir, state.subproject, scankwargs) + return GirTarget( + girfile, + state.subdir, + state.subproject, + scan_command, + generated_files, + [girfile], + build_by_default=kwargs['build_by_default'], + extra_depends=depends, + install=install, + install_dir=[install_dir], + install_tag=['devel'], + ) @staticmethod def _make_typelib_target(state: 'ModuleState', typelib_output: str, @@ -960,17 +969,18 @@ class GnomeModule(ExtensionModule): elif install_dir is False: install = False - typelib_kwargs = { - 'input': generated_files, - 'output': [typelib_output], - 'command': list(typelib_cmd), - 'install': install, - 'install_dir': install_dir, - 'install_tag': 'typelib', - 'build_by_default': kwargs['build_by_default'], - } - - return TypelibTarget(typelib_output, state.subdir, state.subproject, typelib_kwargs) + return TypelibTarget( + typelib_output, + state.subdir, + state.subproject, + typelib_cmd, + generated_files, + [typelib_output], + install=install, + install_dir=[install_dir], + install_tag=['typelib'], + build_by_default=kwargs['build_by_default'], + ) @staticmethod def _gather_typelib_includes_and_update_depends( @@ -1178,16 +1188,21 @@ class GnomeModule(ExtensionModule): srcdir = os.path.join(state.build_to_src, state.subdir) outdir = state.subdir - cmd = [state.find_program('glib-compile-schemas'), '--targetdir', outdir, srcdir] - ct_kwargs = T.cast(T.Dict[str, T.Any], kwargs.copy()) - ct_kwargs['command'] = cmd - ct_kwargs['input'] = [] - ct_kwargs['output'] = 'gschemas.compiled' + cmd: T.List[T.Union[ExternalProgram, str]] = [state.find_program('glib-compile-schemas'), '--targetdir', outdir, srcdir] if state.subdir == '': targetname = 'gsettings-compile' else: targetname = 'gsettings-compile-' + state.subdir.replace('/', '_') - target_g = build.CustomTarget(targetname, state.subdir, state.subproject, ct_kwargs) + target_g = build.CustomTarget( + targetname, + state.subdir, + state.subproject, + cmd, + [], + ['gschemas.compiled'], + build_by_default=kwargs['build_by_default'], + depend_files=kwargs['depend_files'], + ) self._devenv_prepend('GSETTINGS_SCHEMA_DIR', os.path.join(state.environment.get_build_dir(), state.subdir)) return ModuleReturnValue(target_g, [target_g]) @@ -1289,22 +1304,27 @@ class GnomeModule(ExtensionModule): potargets.append(potarget) gmo_file = project_id + '-' + l + '.gmo' - gmo_kwargs = {'command': [msgfmt, '@INPUT@', '-o', '@OUTPUT@'], - 'input': po_file, - 'output': gmo_file, - } - gmotarget = build.CustomTarget(f'help-{project_id}-{l}-gmo', l_subdir, state.subproject, gmo_kwargs) + gmotarget = build.CustomTarget( + f'help-{project_id}-{l}-gmo', + l_subdir, + state.subproject, + [msgfmt, '@INPUT@', '-o', '@OUTPUT@'], + [po_file], + [gmo_file], + ) targets.append(gmotarget) - merge_kwargs = {'command': [itstool, '-m', os.path.join(l_subdir, gmo_file), - '-o', '@OUTDIR@', '@INPUT@'], - 'input': sources_files, - 'output': sources, - 'depends': gmotarget, - 'install': True, - 'install_dir': l_install_dir, - } - mergetarget = build.CustomTarget(f'help-{project_id}-{l}', l_subdir, state.subproject, merge_kwargs) + mergetarget = build.CustomTarget( + f'help-{project_id}-{l}', + l_subdir, + state.subproject, + [itstool, '-m', os.path.join(l_subdir, gmo_file), '-o', '@OUTDIR@', '@INPUT@'], + sources_files, + sources, + extra_depends=[gmotarget], + install=True, + install_dir=[l_install_dir], + ) targets.append(mergetarget) allpotarget = build.AliasTarget(f'help-{project_id}-update-po', potargets, @@ -1377,15 +1397,16 @@ class GnomeModule(ExtensionModule): else: header_dirs.append(src_dir) - t_args = ['--internal', 'gtkdoc', - '--sourcedir=' + state.environment.get_source_dir(), - '--builddir=' + state.environment.get_build_dir(), - '--subdir=' + state.subdir, - '--headerdirs=' + '@@'.join(header_dirs), - '--mainfile=' + main_file, - '--modulename=' + modulename, - '--moduleversion=' + moduleversion, - '--mode=' + kwargs['mode']] + t_args: T.List[str] = [ + '--internal', 'gtkdoc', + '--sourcedir=' + state.environment.get_source_dir(), + '--builddir=' + state.environment.get_build_dir(), + '--subdir=' + state.subdir, + '--headerdirs=' + '@@'.join(header_dirs), + '--mainfile=' + main_file, + '--modulename=' + modulename, + '--moduleversion=' + moduleversion, + '--mode=' + kwargs['mode']] for tool in ['scan', 'scangobj', 'mkdb', 'mkhtml', 'fixxref']: program_name = 'gtkdoc-' + tool program = state.find_program(program_name) @@ -1432,12 +1453,16 @@ class GnomeModule(ExtensionModule): t_args.append(f'--installdir={"@@".join(kwargs["install_dir"])}') t_args += self._get_build_args(kwargs['c_args'], kwargs['include_directories'], kwargs['dependencies'], state, depends) - custom_kwargs = {'output': modulename + '-decl.txt', - 'command': command + t_args, - 'depends': depends, - 'build_always_stale': True, - } - custom_target = build.CustomTarget(targetname, state.subdir, state.subproject, custom_kwargs) + custom_target = build.CustomTarget( + targetname, + state.subdir, + state.subproject, + command + t_args, + [], + [f'{modulename}-decl.txt'], + build_always_stale=True, + extra_depends=depends, + ) alias_target = build.AliasTarget(targetname, [custom_target], state.subdir, state.subproject) if kwargs['check']: check_cmd = state.find_program('gtkdoc-check') @@ -1555,11 +1580,7 @@ class GnomeModule(ExtensionModule): # Added in https://gitlab.gnome.org/GNOME/glib/commit/e4d68c7b3e8b01ab1a4231bf6da21d045cb5a816 (2.55.2) # Fixed in https://gitlab.gnome.org/GNOME/glib/commit/cd1f82d8fc741a2203582c12cc21b4dacf7e1872 (2.56.2) if mesonlib.version_compare(glib_version, '>= 2.56.2'): - custom_kwargs = {'input': xml_files, - 'output': output, - 'command': cmd + ['--body', '--output', '@OUTPUT@', '@INPUT@'], - 'build_by_default': build_by_default - } + c_cmd = cmd + ['--body', '--output', '@OUTPUT@', '@INPUT@'] else: if kwargs['docbook'] is not None: docbook = kwargs['docbook'] @@ -1572,36 +1593,39 @@ class GnomeModule(ExtensionModule): else: self._print_gdbus_warning() cmd += ['--generate-c-code', '@OUTDIR@/' + namebase, '@INPUT@'] - - custom_kwargs = {'input': xml_files, - 'output': output, - 'command': cmd, - 'build_by_default': build_by_default - } - - cfile_custom_target = build.CustomTarget(output, state.subdir, state.subproject, custom_kwargs) + c_cmd = cmd + + cfile_custom_target = build.CustomTarget( + output, + state.subdir, + state.subproject, + c_cmd, + xml_files, + [output], + build_by_default=build_by_default, + ) targets.append(cfile_custom_target) output = namebase + '.h' if mesonlib.version_compare(glib_version, '>= 2.56.2'): - custom_kwargs = {'input': xml_files, - 'output': output, - 'command': cmd + ['--header', '--output', '@OUTPUT@', '@INPUT@'], - 'build_by_default': build_by_default, - 'install': install_header, - 'install_dir': install_dir - } + hfile_cmd = cmd + ['--header', '--output', '@OUTPUT@', '@INPUT@'] + depends = [] else: - custom_kwargs = {'input': xml_files, - 'output': output, - 'command': cmd, - 'build_by_default': build_by_default, - 'install': install_header, - 'install_dir': install_dir, - 'depends': cfile_custom_target - } - - hfile_custom_target = build.CustomTarget(output, state.subdir, state.subproject, custom_kwargs) + hfile_cmd = cmd + depends = [cfile_custom_target] + + hfile_custom_target = build.CustomTarget( + output, + state.subdir, + state.subproject, + hfile_cmd, + xml_files, + [output], + build_by_default=build_by_default, + extra_depends=depends, + install=install_header, + install_dir=[install_dir], + ) targets.append(hfile_custom_target) if kwargs['docbook'] is not None: @@ -1609,8 +1633,6 @@ class GnomeModule(ExtensionModule): if not isinstance(docbook, str): raise MesonException('docbook value must be a string.') - docbook_cmd = cmd + ['--output-directory', '@OUTDIR@', '--generate-docbook', docbook, '@INPUT@'] - # The docbook output is always ${docbook}-${name_of_xml_file} output = namebase + '-docbook' outputs = [] @@ -1618,20 +1640,22 @@ class GnomeModule(ExtensionModule): outputs.append('{}-{}'.format(docbook, os.path.basename(str(f)))) if mesonlib.version_compare(glib_version, '>= 2.56.2'): - custom_kwargs = {'input': xml_files, - 'output': outputs, - 'command': docbook_cmd, - 'build_by_default': build_by_default - } + docbook_cmd = cmd + ['--output-directory', '@OUTDIR@', '--generate-docbook', docbook, '@INPUT@'] + depends = [] else: - custom_kwargs = {'input': xml_files, - 'output': outputs, - 'command': cmd, - 'build_by_default': build_by_default, - 'depends': cfile_custom_target - } - - docbook_custom_target = build.CustomTarget(output, state.subdir, state.subproject, custom_kwargs) + docbook_cmd = cmd + depends = [cfile_custom_target] + + docbook_custom_target = build.CustomTarget( + output, + state.subdir, + state.subproject, + docbook_cmd, + xml_files, + outputs, + build_by_default=build_by_default, + extra_depends=depends, + ) targets.append(docbook_custom_target) return ModuleReturnValue(targets, targets) @@ -1835,18 +1859,23 @@ class GnomeModule(ExtensionModule): ) -> build.CustomTarget: real_cmd: T.List[T.Union[str, ExternalProgram]] = [state.find_program(['glib-mkenums', 'mkenums'])] real_cmd.extend(cmd) - custom_kwargs = { - 'input': sources, - 'output': [output], - 'capture': True, - 'command': real_cmd, - 'install': install, - 'install_dir': install_dir or state.environment.coredata.get_option(mesonlib.OptionKey('includedir')), - 'depends': list(depends or []), - } - return build.CustomTarget(output, state.subdir, state.subproject, custom_kwargs, - # https://github.com/mesonbuild/meson/issues/973 - absolute_paths=True) + _install_dir = install_dir or state.environment.coredata.get_option(mesonlib.OptionKey('includedir')) + assert isinstance(_install_dir, str), 'for mypy' + + return build.CustomTarget( + output, + state.subdir, + state.subproject, + real_cmd, + sources, + [output], + capture=True, + install=install, + install_dir=[_install_dir], + extra_depends=depends, + # https://github.com/mesonbuild/meson/issues/973 + absolute_paths=True, + ) @typed_pos_args('gnome.genmarshal', str) @typed_kwargs( @@ -1886,36 +1915,45 @@ class GnomeModule(ExtensionModule): cmd.append(f'--{k.replace("_", "-")}') install_header = kwargs['install_header'] - install_dir: T.List[T.Union[str, bool]] = kwargs['install_dir'] or [] - - - custom_kwargs: T.Dict[str, T.Any] = { - 'input': sources, - 'depend_files': kwargs['depend_files'], - 'install_dir': kwargs['install_dir'], - } + capture = False # https://github.com/GNOME/glib/commit/0fbc98097fac4d3e647684f344e508abae109fdf if mesonlib.version_compare(self._get_native_glib_version(state), '>= 2.51.0'): cmd += ['--output', '@OUTPUT@'] else: - custom_kwargs['capture'] = True + capture = True header_file = output + '.h' - custom_kwargs['command'] = cmd + ['--body', '@INPUT@'] + c_cmd = cmd + ['--body', '@INPUT@'] if mesonlib.version_compare(self._get_native_glib_version(state), '>= 2.53.4'): # Silence any warnings about missing prototypes - custom_kwargs['command'] += ['--include-header', header_file] - custom_kwargs['output'] = output + '.c' - body = build.CustomTarget(output + '_c', state.subdir, state.subproject, custom_kwargs) - - custom_kwargs['install'] = install_header - custom_kwargs['install_dir'] = install_dir + c_cmd += ['--include-header', header_file] + body = build.CustomTarget( + output + '_c', + state.subdir, + state.subproject, + c_cmd, + sources, + [f'{output}.c'], + capture=capture, + depend_files=kwargs['depend_files'], + ) + + h_cmd = cmd + ['--header', '@INPUT@'] if new_genmarshal: - cmd += ['--pragma-once'] - custom_kwargs['command'] = cmd + ['--header', '@INPUT@'] - custom_kwargs['output'] = header_file - header = build.CustomTarget(output + '_h', state.subdir, state.subproject, custom_kwargs) + h_cmd += ['--pragma-once'] + header = build.CustomTarget( + output + '_h', + state.subdir, + state.subproject, + h_cmd, + sources, + [header_file], + install=install_header, + install_dir=[kwargs['install_dir']] if kwargs['install_dir'] else [], + capture=capture, + depend_files=kwargs['depend_files'], + ) rv = [body, header] return ModuleReturnValue(rv, rv) @@ -2021,24 +2059,25 @@ class GnomeModule(ExtensionModule): cmd.append(gir_file) vapi_output = library + '.vapi' - custom_kwargs = { - 'command': cmd, - 'input': inputs, - 'output': vapi_output, - 'depends': vapi_depends, - } datadir = state.environment.coredata.get_option(mesonlib.OptionKey('datadir')) assert isinstance(datadir, str), 'for mypy' install_dir = kwargs['install_dir'] or os.path.join(datadir, 'vala', 'vapi') - custom_kwargs['install'] = kwargs['install'] - custom_kwargs['install_dir'] = install_dir - custom_kwargs['packages'] = packages if kwargs['install']: # We shouldn't need this locally but we install it deps_target = self._generate_deps(state, library, vapi_packages, install_dir) created_values.append(deps_target) - vapi_target = VapiTarget(vapi_output, state.subdir, state.subproject, custom_kwargs) + vapi_target = VapiTarget( + vapi_output, + state.subdir, + state.subproject, + command=cmd, + sources=inputs, + outputs=[vapi_output], + extra_depends=vapi_depends, + install=kwargs['install'], + install_dir=[install_dir], + ) # So to try our best to get this to just work we need: # - link with with the correct library diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py index 1b89d508c..69f39a638 100644 --- a/mesonbuild/modules/hotdoc.py +++ b/mesonbuild/modules/hotdoc.py @@ -344,8 +344,9 @@ class HotdocTargetBuilder: extra_assets=self._extra_assets, subprojects=self._subprojects, command=target_cmd, - depends=self._dependencies, - output=fullname, + extra_depends=self._dependencies, + outputs=[fullname], + sources=[], depfile=os.path.basename(depfile), build_by_default=self.build_by_default) @@ -379,7 +380,7 @@ class HotdocTargetHolder(CustomTargetHolder): class HotdocTarget(build.CustomTarget): def __init__(self, name, subdir, subproject, hotdoc_conf, extra_extension_paths, extra_assets, subprojects, **kwargs): - super().__init__(name, subdir, subproject, kwargs, absolute_paths=True) + super().__init__(name, subdir, subproject, **kwargs, absolute_paths=True) self.hotdoc_conf = hotdoc_conf self.extra_extension_paths = extra_extension_paths self.extra_assets = extra_assets diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index d84f66e8a..98070d75c 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -188,18 +188,18 @@ class I18nModule(ExtensionModule): if build_by_default is None: build_by_default = kwargs['install'] - real_kwargs = { - 'build_by_default': build_by_default, - 'command': command, - 'install': kwargs['install'], - 'install_dir': kwargs['install_dir'], - 'output': kwargs['output'], - 'input': kwargs['input'], - 'install_tag': kwargs['install_tag'], - } - - ct = build.CustomTarget('', state.subdir, state.subproject, - T.cast(T.Dict[str, T.Any], real_kwargs)) + ct = build.CustomTarget( + '', + state.subdir, + state.subproject, + command, + kwargs['input'], + kwargs['output'], + build_by_default=build_by_default, + install=kwargs['install'], + install_dir=kwargs['install_dir'], + install_tag=kwargs['install_tag'], + ) return ModuleReturnValue(ct, [ct]) @@ -258,18 +258,21 @@ class I18nModule(ExtensionModule): for l in languages: po_file = mesonlib.File.from_source_file(state.environment.source_dir, state.subdir, l+'.po') - gmo_kwargs = {'command': ['msgfmt', '@INPUT@', '-o', '@OUTPUT@'], - 'input': po_file, - 'output': packagename+'.mo', - 'install': install, - # We have multiple files all installed as packagename+'.mo' in different install subdirs. - # What we really wanted to do, probably, is have a rename: kwarg, but that's not available - # to custom_targets. Crude hack: set the build target's subdir manually. - # Bonus: the build tree has something usable as an uninstalled bindtextdomain() target dir. - 'install_dir': path.join(install_dir, l, 'LC_MESSAGES'), - 'install_tag': 'i18n', - } - gmotarget = build.CustomTarget(f'{packagename}-{l}.mo', path.join(state.subdir, l, 'LC_MESSAGES'), state.subproject, gmo_kwargs) + gmotarget = build.CustomTarget( + f'{packagename}-{l}.mo', + path.join(state.subdir, l, 'LC_MESSAGES'), + state.subproject, + ['msgfmt', '@INPUT@', '-o', '@OUTPUT@'], + [po_file], + [f'{packagename}.mo'], + install=install, + # We have multiple files all installed as packagename+'.mo' in different install subdirs. + # What we really wanted to do, probably, is have a rename: kwarg, but that's not available + # to custom_targets. Crude hack: set the build target's subdir manually. + # Bonus: the build tree has something usable as an uninstalled bindtextdomain() target dir. + install_dir=[path.join(install_dir, l, 'LC_MESSAGES')], + install_tag=['i18n'], + ) targets.append(gmotarget) gmotargets.append(gmotarget) @@ -331,19 +334,19 @@ class I18nModule(ExtensionModule): if build_by_default is None: build_by_default = kwargs['install'] - real_kwargs = { - 'build_by_default': build_by_default, - 'command': command, - 'depends': mo_targets, - 'install': kwargs['install'], - 'install_dir': kwargs['install_dir'], - 'output': kwargs['output'], - 'input': kwargs['input'], - 'install_tag': kwargs['install_tag'], - } - - ct = build.CustomTarget('', state.subdir, state.subproject, - T.cast(T.Dict[str, T.Any], real_kwargs)) + ct = build.CustomTarget( + '', + state.subdir, + state.subproject, + command, + kwargs['input'], + kwargs['output'], + build_by_default=build_by_default, + extra_depends=mo_targets, + install=kwargs['install'], + install_dir=kwargs['install_dir'], + install_tag=kwargs['install_tag'], + ) return ModuleReturnValue(ct, [ct]) diff --git a/mesonbuild/modules/java.py b/mesonbuild/modules/java.py index 0af5aec7d..20cf3fd67 100644 --- a/mesonbuild/modules/java.py +++ b/mesonbuild/modules/java.py @@ -51,20 +51,22 @@ class JavaModule(ExtensionModule): else: header = f'{pathlib.Path(file.fname).stem}.h' - ct_kwargs = { - 'input': file, - 'output': header, - 'command': [ + target = CustomTarget( + os.path.basename(header), + state.subdir, + state.subproject, + [ self.javac.exelist[0], '-d', '@PRIVATE_DIR@', '-h', state.subdir, '@INPUT@', - ] - } - - target = CustomTarget(os.path.basename(header), state.subdir, state.subproject, backend=state.backend, kwargs=ct_kwargs) + ], + [file], + [header], + backend=state.backend, + ) # It is only known that 1.8.0 won't pre-create the directory. 11 and 16 # do not exhibit this behavior. if version_compare(self.javac.version, '1.8.0'): diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py index cf973684b..37072b4f2 100644 --- a/mesonbuild/modules/qt.py +++ b/mesonbuild/modules/qt.py @@ -328,14 +328,16 @@ class QtBaseModule(ExtensionModule): for s in sources: qrc_deps.extend(self._parse_qrc_deps(state, s)) - rcc_kwargs: T.Dict[str, T.Any] = { # TODO: if CustomTarget had typing information we could use that here... - 'input': sources, - 'output': name + '.cpp', - 'command': self.tools['rcc'].get_command() + ['-name', name, '-o', '@OUTPUT@'] + extra_args + ['@INPUT@'] + DEPFILE_ARGS, - 'depend_files': qrc_deps, - 'depfile': f'{name}.d', - } - res_target = build.CustomTarget(name, state.subdir, state.subproject, rcc_kwargs) + res_target = build.CustomTarget( + name, + state.subdir, + state.subproject, + self.tools['rcc'].get_command() + ['-name', name, '-o', '@OUTPUT@'] + extra_args + ['@INPUT@'] + DEPFILE_ARGS, + sources, + [f'{name}.cpp'], + depend_files=qrc_deps, + depfile=f'{name}.d', + ) targets.append(res_target) else: for rcc_file in sources: @@ -345,14 +347,16 @@ class QtBaseModule(ExtensionModule): else: basename = os.path.basename(rcc_file.fname) name = f'qt{self.qt_version}-{basename.replace(".", "_")}' - rcc_kwargs = { - 'input': rcc_file, - 'output': f'{name}.cpp', - 'command': self.tools['rcc'].get_command() + ['-name', '@BASENAME@', '-o', '@OUTPUT@'] + extra_args + ['@INPUT@'] + DEPFILE_ARGS, - 'depend_files': qrc_deps, - 'depfile': f'{name}.d', - } - res_target = build.CustomTarget(name, state.subdir, state.subproject, rcc_kwargs) + res_target = build.CustomTarget( + name, + state.subdir, + state.subproject, + self.tools['rcc'].get_command() + ['-name', '@BASENAME@', '-o', '@OUTPUT@'] + extra_args + ['@INPUT@'] + DEPFILE_ARGS, + [rcc_file], + [f'{name}.cpp'], + depend_files=qrc_deps, + depfile=f'{name}.d', + ) targets.append(res_target) return targets @@ -570,16 +574,19 @@ class QtBaseModule(ExtensionModule): ts = os.path.basename(ts) else: outdir = state.subdir - cmd = [self.tools['lrelease'], '@INPUT@', '-qm', '@OUTPUT@'] - lrelease_kwargs: T.Dict[str, T.Any] = { - 'output': '@BASENAME@.qm', - 'input': ts, - 'install': kwargs['install'], - 'install_dir': install_dir or [], - 'install_tag': 'i18n', - 'build_by_default': kwargs['build_by_default'], - 'command': cmd} - lrelease_target = build.CustomTarget(f'qt{self.qt_version}-compile-{ts}', outdir, state.subproject, lrelease_kwargs) + cmd: T.List[T.Union[ExternalProgram, str]] = [self.tools['lrelease'], '@INPUT@', '-qm', '@OUTPUT@'] + lrelease_target = build.CustomTarget( + f'qt{self.qt_version}-compile-{ts}', + outdir, + state.subproject, + cmd, + [ts], + ['@BASENAME@.qm'], + install=kwargs['install'], + install_dir=install_dir, + install_tag=['i18n'], + build_by_default=kwargs['build_by_default'], + ) translations.append(lrelease_target) if qresource: return ModuleReturnValue(results.return_value[0], [results.new_objects, translations]) diff --git a/mesonbuild/modules/unstable_external_project.py b/mesonbuild/modules/unstable_external_project.py index 4e3d6db19..164af9ba6 100644 --- a/mesonbuild/modules/unstable_external_project.py +++ b/mesonbuild/modules/unstable_external_project.py @@ -227,15 +227,16 @@ class ExternalProject(NewExtensionModule): if self.verbose: cmd.append('--verbose') - target_kwargs = {'output': f'{self.name}.stamp', - 'depfile': f'{self.name}.d', - 'command': cmd + ['@OUTPUT@', '@DEPFILE@'], - 'console': True, - } - self.target = build.CustomTarget(self.name, - self.subdir.as_posix(), - self.subproject, - target_kwargs) + self.target = build.CustomTarget( + self.name, + self.subdir.as_posix(), + self.subproject, + cmd + ['@OUTPUT@', '@DEPFILE@'], + [], + [f'{self.name}.stamp'], + depfile=f'{self.name}.d', + console=True, + ) idir = build.InstallDir(self.subdir.as_posix(), Path('dist', self.rel_prefix).as_posix(), diff --git a/mesonbuild/modules/unstable_rust.py b/mesonbuild/modules/unstable_rust.py index d0d9ca576..501273f9a 100644 --- a/mesonbuild/modules/unstable_rust.py +++ b/mesonbuild/modules/unstable_rust.py @@ -212,18 +212,16 @@ class RustModule(ExtensionModule): f'rustmod-bindgen-{name}'.replace('/', '_'), state.subdir, state.subproject, - { - 'input': header, - 'output': kwargs['output'], - 'command': self._bindgen_bin.get_command() + [ - '@INPUT@', '--output', - os.path.join(state.environment.build_dir, '@OUTPUT@')] + - kwargs['args'] + ['--'] + kwargs['c_args'] + inc_strs + - ['-MD', '-MQ', '@INPUT@', '-MF', '@DEPFILE@'], - 'depfile': '@PLAINNAME@.d', - 'depends': depends, - 'depend_files': depend_files, - }, + self._bindgen_bin.get_command() + [ + '@INPUT@', '--output', + os.path.join(state.environment.build_dir, '@OUTPUT@')] + + kwargs['args'] + ['--'] + kwargs['c_args'] + inc_strs + + ['-MD', '-MQ', '@INPUT@', '-MF', '@DEPFILE@'], + [header], + [kwargs['output']], + depfile='@PLAINNAME@.d', + extra_depends=depends, + depend_files=depend_files, backend=state.backend, ) diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index 5f7301d29..eb07cedcf 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -181,26 +181,25 @@ class WindowsModule(ExtensionModule): command: T.List[T.Union[str, ExternalProgram]] = [] command.append(rescomp) command.extend(res_args) - - res_kwargs: 'RcKwargs' = { - 'output': output, - 'input': [src], - 'depfile': None, - 'depend_files': wrc_depend_files, - 'depends': wrc_depends, - 'command': [], - } - + depfile: T.Optional[str] = None # instruct binutils windres to generate a preprocessor depfile if rescomp_type == ResourceCompilerType.windres: - res_kwargs['depfile'] = f'{output}.d' + depfile = f'{output}.d' command.extend(['--preprocessor-arg=-MD', '--preprocessor-arg=-MQ@OUTPUT@', '--preprocessor-arg=-MF@DEPFILE@']) - res_kwargs['command'] = command - - res_targets.append(build.CustomTarget(name_formatted, state.subdir, state.subproject, res_kwargs)) + res_targets.append(build.CustomTarget( + name_formatted, + state.subdir, + state.subproject, + command, + [src], + [output], + depfile=depfile, + depend_files=wrc_depend_files, + extra_depends=wrc_depends, + )) return ModuleReturnValue(res_targets, [res_targets]) diff --git a/test cases/failing/41 custom target plainname many inputs/test.json b/test cases/failing/41 custom target plainname many inputs/test.json index 8c15cda5a..66a56f602 100644 --- a/test cases/failing/41 custom target plainname many inputs/test.json +++ b/test cases/failing/41 custom target plainname many inputs/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/41 custom target plainname many inputs/meson.build:5:0: ERROR: Output cannot contain @PLAINNAME@ or @BASENAME@ when there is more than one input (we can't know which to use)" + "line": "test cases/failing/41 custom target plainname many inputs/meson.build:5:0: ERROR: custom_target: output cannot containe \"@PLAINNAME@\" or \"@BASENAME@\" when there is more than one input (we can't know which to use)" } ] }