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)" } ] }