From 7d1b74ea99a936e0b0f71b97932759444f1e3499 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 18 Nov 2025 08:56:22 -0800 Subject: [PATCH 1/5] programs: Move AnyProgram type from dlang module to programs module --- mesonbuild/modules/dlang.py | 10 ++++------ mesonbuild/programs.py | 5 +++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/mesonbuild/modules/dlang.py b/mesonbuild/modules/dlang.py index 35ce86be81f1..b60ad0481621 100644 --- a/mesonbuild/modules/dlang.py +++ b/mesonbuild/modules/dlang.py @@ -22,20 +22,18 @@ from typing_extensions import Literal, TypeAlias from . import ModuleState - from ..build import OverrideExecutable from ..interpreter.interpreter import Interpreter from ..interpreterbase.baseobjects import TYPE_kwargs - from ..programs import ExternalProgram, OverrideProgram + from ..programs import AnyProgram - _AnyProgram: TypeAlias = T.Union[OverrideExecutable, ExternalProgram, OverrideProgram] _JSONTypes: TypeAlias = T.Union[str, int, bool, None, T.List['_JSONTypes'], T.Dict[str, '_JSONTypes']] class DlangModule(ExtensionModule): - class_dubbin: T.Union[_AnyProgram, Literal[False], None] = None + class_dubbin: T.Union[AnyProgram, Literal[False], None] = None init_dub = False - dubbin: T.Union[_AnyProgram, Literal[False], None] + dubbin: T.Union[AnyProgram, Literal[False], None] INFO = ModuleInfo('dlang', '0.48.0') @@ -122,7 +120,7 @@ def _call_dubbin(self, args: T.List[str], env: T.Optional[T.Mapping[str, str]] = p, out = Popen_safe(self.dubbin.get_command() + args, env=env)[0:2] return p.returncode, out.strip() - def check_dub(self, state: ModuleState) -> T.Union[_AnyProgram, Literal[False]]: + def check_dub(self, state: ModuleState) -> T.Union[AnyProgram, Literal[False]]: dubbin = state.find_program('dub', silent=True) if dubbin.found(): try: diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py index a1cfc06e98a2..2bd38709e398 100644 --- a/mesonbuild/programs.py +++ b/mesonbuild/programs.py @@ -19,9 +19,14 @@ from .mesonlib import MachineChoice, OrderedSet if T.TYPE_CHECKING: + from typing_extensions import TypeAlias + + from .build import OverrideExecutable from .environment import Environment from .interpreter import Interpreter + AnyProgram: TypeAlias = T.Union[OverrideExecutable, 'ExternalProgram', 'OverrideProgram'] + class ExternalProgram(mesonlib.HoldableObject): From 78ca20478d241355b7d58480945affe385f4568d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 18 Nov 2025 09:09:23 -0800 Subject: [PATCH 2/5] programs: Add a couple more useful aliases These are useful for the many cases of creating lists of commands that are mixtures of strings and programs --- mesonbuild/programs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py index 2bd38709e398..ecb6f19ce667 100644 --- a/mesonbuild/programs.py +++ b/mesonbuild/programs.py @@ -26,6 +26,8 @@ from .interpreter import Interpreter AnyProgram: TypeAlias = T.Union[OverrideExecutable, 'ExternalProgram', 'OverrideProgram'] + CommandListEntry: TypeAlias = T.Union[str, AnyProgram] + CommandList: TypeAlias = T.List[CommandListEntry] class ExternalProgram(mesonlib.HoldableObject): From dd807c649a90c1f1e545bb884d27860b7784d0a2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 18 Nov 2025 09:28:52 -0800 Subject: [PATCH 3/5] Make use the program TypeAliases This doesn't cover every possible use, but it does cover a lot of the obvious places where the exact alias is in use. There are also a number of places that *should* use this alias that don't. --- mesonbuild/build.py | 4 +-- mesonbuild/interpreter/interpreter.py | 18 ++++++------- mesonbuild/modules/__init__.py | 6 ++--- mesonbuild/modules/codegen.py | 13 ++++----- mesonbuild/modules/gnome.py | 39 +++++++++++++-------------- mesonbuild/modules/rust.py | 4 +-- 6 files changed, 39 insertions(+), 45 deletions(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index a43709342dd4..b9d0abee0761 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -363,7 +363,7 @@ def __init__(self, environment: Environment): self.stdlibs = PerMachine({}, {}) self.test_setups: T.Dict[str, TestSetup] = {} self.test_setup_default_name = None - self.find_overrides: T.Dict[str, T.Union['OverrideExecutable', programs.ExternalProgram, programs.OverrideProgram]] = {} + self.find_overrides: T.Dict[str, programs.AnyProgram] = {} self.searched_programs: T.Set[str] = set() # The list of all programs that have been searched for. # If we are doing a cross build we need two caches, if we're doing a @@ -2850,7 +2850,7 @@ def __init__(self, environment: Environment, command: T.Sequence[T.Union[ str, BuildTargetTypes, GeneratedList, - programs.ExternalProgram, File]], + programs.AnyProgram, File]], sources: T.Sequence[T.Union[ str, File, BuildTargetTypes, ExtractedObjects, GeneratedList, programs.ExternalProgram]], diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index f3382d20e37d..6bc36569a6ec 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -119,7 +119,7 @@ from ..backend.backends import Backend from ..interpreterbase.baseobjects import InterpreterObject, TYPE_var, TYPE_kwargs from ..options import OptionDict - from ..programs import OverrideProgram + from ..programs import AnyProgram from .type_checking import SourcesVarargsType # Input source types passed to Targets @@ -131,7 +131,7 @@ BuildTargetSource = T.Union[mesonlib.FileOrString, build.GeneratedTypes, build.StructuredSources] - ProgramVersionFunc = T.Callable[[T.Union[ExternalProgram, build.Executable, OverrideProgram]], str] + ProgramVersionFunc = T.Callable[[AnyProgram], str] TestClass = T.TypeVar('TestClass', bound=Test) @@ -1585,7 +1585,7 @@ def program_from_system(self, args: T.List[mesonlib.FileOrString], search_dirs: def program_from_overrides(self, command_names: T.List[mesonlib.FileOrString], extra_info: T.List['mlog.TV_Loggable'] - ) -> T.Optional[T.Union[ExternalProgram, OverrideProgram, build.OverrideExecutable]]: + ) -> T.Optional[AnyProgram]: for name in command_names: if not isinstance(name, str): continue @@ -1600,7 +1600,7 @@ def store_name_lookups(self, command_names: T.List[mesonlib.FileOrString]) -> No if isinstance(name, str): self.build.searched_programs.add(name) - def add_find_program_override(self, name: str, exe: T.Union[build.OverrideExecutable, ExternalProgram, 'OverrideProgram']) -> None: + def add_find_program_override(self, name: str, exe: AnyProgram) -> None: if name in self.build.searched_programs: raise InterpreterException(f'Tried to override finding of executable "{name}" which has already been found.') if name in self.build.find_overrides: @@ -1625,7 +1625,7 @@ def find_program_impl(self, args: T.List[mesonlib.FileOrString], search_dirs: T.Optional[T.List[str]] = None, version_arg: T.Optional[str] = '', version_func: T.Optional[ProgramVersionFunc] = None - ) -> T.Union['ExternalProgram', 'build.OverrideExecutable', 'OverrideProgram']: + ) -> AnyProgram: args = mesonlib.listify(args) extra_info: T.List[mlog.TV_Loggable] = [] @@ -1657,7 +1657,7 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi version_arg: T.Optional[str], version_func: T.Optional[ProgramVersionFunc], extra_info: T.List[mlog.TV_Loggable] - ) -> T.Optional[T.Union[ExternalProgram, build.Executable, OverrideProgram]]: + ) -> T.Optional[AnyProgram]: progobj = self.program_from_overrides(args, extra_info) if progobj: return progobj @@ -1693,7 +1693,7 @@ def program_lookup(self, args: T.List[mesonlib.FileOrString], for_machine: Machi return progobj - def check_program_version(self, progobj: T.Union[ExternalProgram, build.Executable, OverrideProgram], + def check_program_version(self, progobj: AnyProgram, wanted: T.Union[str, T.List[str]], version_func: T.Optional[ProgramVersionFunc], extra_info: T.List[mlog.TV_Loggable]) -> bool: @@ -1720,7 +1720,7 @@ def check_program_version(self, progobj: T.Union[ExternalProgram, build.Executab def find_program_fallback(self, fallback: str, args: T.List[mesonlib.FileOrString], default_options: OptionDict, required: bool, extra_info: T.List[mlog.TV_Loggable] - ) -> T.Optional[T.Union[ExternalProgram, build.Executable, OverrideProgram]]: + ) -> AnyProgram: mlog.log('Fallback to subproject', mlog.bold(fallback), 'which provides program', mlog.bold(' '.join(args))) sp_kwargs: kwtypes.DoSubproject = { @@ -1747,7 +1747,7 @@ def find_program_fallback(self, fallback: str, args: T.List[mesonlib.FileOrStrin @disablerIfNotFound def func_find_program(self, node: mparser.BaseNode, args: T.Tuple[T.List[mesonlib.FileOrString]], kwargs: 'kwtypes.FindProgram', - ) -> T.Union['build.Executable', ExternalProgram, 'OverrideProgram']: + ) -> AnyProgram: disabled, required, feature = extract_required_kwarg(kwargs, self.subproject) if disabled: mlog.log('Program', mlog.bold(' '.join(args[0])), 'skipped: feature', mlog.bold(feature), 'disabled') diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index 3ff9368d907f..6c1f59f096a3 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -18,7 +18,7 @@ from ..interpreter import Interpreter from ..interpreter.interpreter import ProgramVersionFunc from ..interpreterbase import TYPE_var, TYPE_kwargs - from ..programs import OverrideProgram + from ..programs import AnyProgram from ..dependencies import Dependency from ..options import ElementaryOptionValues @@ -75,14 +75,14 @@ def find_program(self, prog: T.Union[mesonlib.FileOrString, T.List[mesonlib.File required: bool = True, version_func: T.Optional[ProgramVersionFunc] = None, wanted: T.Union[str, T.List[str]] = '', silent: bool = False, - for_machine: MachineChoice = MachineChoice.HOST) -> T.Union[ExternalProgram, build.OverrideExecutable, OverrideProgram]: + for_machine: MachineChoice = MachineChoice.HOST) -> AnyProgram: if not isinstance(prog, list): prog = [prog] return self._interpreter.find_program_impl(prog, required=required, version_func=version_func, wanted=wanted, silent=silent, for_machine=for_machine) def find_tool(self, name: str, depname: str, varname: str, required: bool = True, - wanted: T.Optional[str] = None) -> T.Union[build.OverrideExecutable, ExternalProgram, 'OverrideProgram']: + wanted: T.Optional[str] = None) -> AnyProgram: # Look in overrides in case it's built as subproject progobj = self._interpreter.program_from_overrides([name], []) if progobj is not None: diff --git a/mesonbuild/modules/codegen.py b/mesonbuild/modules/codegen.py index c1daa91c59c3..0b9b6a1d9d35 100644 --- a/mesonbuild/modules/codegen.py +++ b/mesonbuild/modules/codegen.py @@ -21,18 +21,16 @@ from .. import mlog if T.TYPE_CHECKING: - from typing_extensions import Literal, TypeAlias, TypedDict + from typing_extensions import Literal, TypedDict from . import ModuleState from .._typing import ImmutableListProtocol - from ..build import Executable from ..interpreter import Interpreter from ..interpreter.kwargs import ExtractRequired from ..interpreterbase import TYPE_var, TYPE_kwargs from ..mesonlib import MachineChoice - from ..programs import OverrideProgram + from ..programs import AnyProgram, CommandList - Program: TypeAlias = T.Union[Executable, ExternalProgram, OverrideProgram] LexImpls = Literal['lex', 'flex', 'reflex', 'win_flex'] YaccImpls = Literal['yacc', 'byacc', 'bison', 'win_bison'] @@ -87,12 +85,11 @@ def inner(check: T.List[str]) -> T.Optional[str]: class _CodeGenerator(HoldableObject): name: str - program: Program + program: AnyProgram arguments: ImmutableListProtocol[str] = dataclasses.field(default_factory=list) - def command(self) -> T.List[T.Union[Program, str]]: - return (T.cast('T.List[T.Union[Program, str]]', [self.program]) + - T.cast('T.List[T.Union[Program, str]]', self.arguments)) + def command(self) -> CommandList: + return T.cast('CommandList', [self.program]) + T.cast('CommandList', self.arguments) def found(self) -> bool: return self.program.found() diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index d6f56b9fa65d..9e444db8b813 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -37,7 +37,7 @@ from ..scripts.gettext import read_linguas if T.TYPE_CHECKING: - from typing_extensions import Literal, TypeAlias, TypedDict + from typing_extensions import Literal, TypedDict from . import ModuleState from ..build import BuildTarget @@ -45,7 +45,7 @@ from ..interpreter import Interpreter from ..interpreterbase import TYPE_var, TYPE_kwargs from ..mesonlib import FileOrString - from ..programs import ExternalProgram + from ..programs import AnyProgram, CommandList, CommandListEntry, ExternalProgram class PostInstall(TypedDict): glib_compile_schemas: bool @@ -198,8 +198,6 @@ class MkEnums(_MkEnumsCommon): vtail: T.Optional[str] depends: T.List[T.Union[BuildTarget, CustomTarget, CustomTargetIndex]] - ToolType: TypeAlias = T.Union[Executable, ExternalProgram, OverrideProgram] - # Differs from the CustomTarget version in that it straight defaults to True _BUILD_BY_DEFAULT: KwargInfo[bool] = KwargInfo( @@ -256,8 +254,8 @@ class GnomeModule(ExtensionModule): def __init__(self, interpreter: 'Interpreter') -> None: super().__init__(interpreter) self.gir_dep: T.Optional[Dependency] = None - self.giscanner: T.Optional[ToolType] = None - self.gicompiler: T.Optional[ToolType] = None + self.giscanner: T.Optional[AnyProgram] = None + self.gicompiler: T.Optional[AnyProgram] = None self.install_glib_compile_schemas = False self.install_gio_querymodules: T.List[str] = [] self.install_gtk_update_icon_cache = False @@ -309,7 +307,7 @@ def _print_gdbus_warning() -> None: once=True, fatal=False) @staticmethod - def _find_tool(state: 'ModuleState', tool: str) -> 'ToolType': + def _find_tool(state: 'ModuleState', tool: str) -> 'AnyProgram': tool_map = { 'gio-querymodules': 'gio-2.0', 'glib-compile-schemas': 'gio-2.0', @@ -398,7 +396,7 @@ def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, 'FileOrStri glib_version = self._get_native_glib_version(state) glib_compile_resources = self._find_tool(state, 'glib-compile-resources') - cmd: T.List[T.Union['ToolType', str]] = [glib_compile_resources, '@INPUT@'] + cmd: T.List[T.Union['AnyProgram', str]] = [glib_compile_resources, '@INPUT@'] source_dirs = kwargs['source_dir'] dependencies = kwargs['dependencies'] @@ -496,7 +494,7 @@ def compile_resources(self, state: 'ModuleState', args: T.Tuple[str, 'FileOrStri raise MesonException('GResource header is installed yet export is not enabled') depfile: T.Optional[str] = None - target_cmd: T.List[T.Union['ToolType', str]] + target_cmd: T.List[T.Union['AnyProgram', str]] if not mesonlib.version_compare(glib_version, gresource_dep_needed_version): # This will eventually go out of sync if dependencies are added target_cmd = cmd @@ -789,8 +787,7 @@ def postconf_hook(self, b: build.Build) -> None: if self.devenv is not None: b.devenv.append(self.devenv) - def _get_gir_dep(self, state: 'ModuleState') -> T.Tuple[Dependency, T.Union[Executable, 'ExternalProgram', 'OverrideProgram'], - T.Union[Executable, 'ExternalProgram', 'OverrideProgram']]: + def _get_gir_dep(self, state: 'ModuleState') -> T.Tuple[Dependency, AnyProgram, AnyProgram]: if not self.gir_dep: self.gir_dep = state.dependency('gobject-introspection-1.0') self.giscanner = self._find_tool(state, 'g-ir-scanner') @@ -971,7 +968,7 @@ def _make_gir_target( self, state: 'ModuleState', girfile: str, - scan_command: T.Sequence[T.Union['FileOrString', Executable, ExternalProgram, OverrideProgram]], + scan_command: T.Sequence[T.Union['FileOrString', Executable, AnyProgram]], generated_files: T.Sequence[T.Union[str, mesonlib.File, build.GeneratedTypes]], depends: T.Sequence[T.Union['FileOrString', build.BuildTarget, 'build.GeneratedTypes', build.StructuredSources]], env_flags: T.Sequence[str], @@ -1194,7 +1191,8 @@ def generate_gir(self, state: 'ModuleState', args: T.Tuple[T.List[T.Union[Execut gir_inc_dirs: T.List[str] = [] - scan_command: T.List[T.Union[str, Executable, 'ExternalProgram', 'OverrideProgram']] = [giscanner] + # Executables are passed as indexes other than 0 in this List + scan_command: T.List[T.Union[CommandListEntry, Executable]] = [giscanner] scan_command += ['--quiet'] scan_command += ['--no-libtool'] scan_command += ['--namespace=' + ns, '--nsversion=' + nsversion] @@ -1267,7 +1265,7 @@ def compile_schemas(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs srcdir = os.path.join(state.build_to_src, state.subdir) outdir = state.subdir - cmd: T.List[T.Union['ToolType', str]] = [self._find_tool(state, 'glib-compile-schemas'), '--targetdir', outdir, srcdir] + cmd: T.List[T.Union['AnyProgram', str]] = [self._find_tool(state, 'glib-compile-schemas'), '--targetdir', outdir, srcdir] if state.subdir == '': targetname = 'gsettings-compile' else: @@ -1347,7 +1345,7 @@ def yelp(self, state: 'ModuleState', args: T.Tuple[str, T.List[str]], kwargs: 'Y pot_file = os.path.join('@SOURCE_ROOT@', state.subdir, 'C', project_id + '.pot') pot_sources = [os.path.join('@SOURCE_ROOT@', state.subdir, 'C', s) for s in sources] - pot_args: T.List[T.Union[ExternalProgram, Executable, OverrideProgram, str]] = [itstool, '-o', pot_file] + pot_args: CommandList = [itstool, '-o', pot_file] pot_args.extend(pot_sources) pottarget = build.RunTarget(f'help-{project_id}-pot', pot_args, [], os.path.join(state.subdir, 'C'), state.subproject, @@ -1379,7 +1377,7 @@ def yelp(self, state: 'ModuleState', args: T.Tuple[str, T.List[str]], kwargs: 'Y targets.append(l_data) po_file = l + '.po' - po_args: T.List[T.Union[ExternalProgram, Executable, OverrideProgram, str]] = [ + po_args: CommandList = [ msgmerge, '-q', '-o', os.path.join('@SOURCE_ROOT@', l_subdir, po_file), os.path.join('@SOURCE_ROOT@', l_subdir, po_file), pot_file] @@ -1644,7 +1642,7 @@ def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional[T.Un kwargs: 'GdbusCodegen') -> ModuleReturnValue: namebase = args[0] xml_files: T.List[T.Union['FileOrString', build.GeneratedTypes]] = [args[1]] if args[1] else [] - cmd: T.List[T.Union['ToolType', str]] = [self._find_tool(state, 'gdbus-codegen')] + cmd: T.List[T.Union['AnyProgram', str]] = [self._find_tool(state, 'gdbus-codegen')] cmd.extend(kwargs['extra_args']) # Autocleanup supported? @@ -2049,7 +2047,7 @@ def _make_mkenum_impl( install_dir: T.Optional[T.Sequence[T.Union[str, bool]]] = None, depends: T.Optional[T.Sequence[build.BuildTargetTypes]] = None ) -> build.CustomTarget: - real_cmd: T.List[T.Union[str, 'ToolType']] = [self._find_tool(state, 'glib-mkenums')] + real_cmd: T.List[T.Union[str, 'AnyProgram']] = [self._find_tool(state, 'glib-mkenums')] real_cmd.extend(cmd) _install_dir = install_dir or state.environment.coredata.optstore.get_value_for(OptionKey('includedir')) assert isinstance(_install_dir, str), 'for mypy' @@ -2095,7 +2093,7 @@ def genmarshal(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'GenMarsh new_genmarshal = mesonlib.version_compare(self._get_native_glib_version(state), '>= 2.53.3') - cmd: T.List[T.Union['ToolType', str]] = [self._find_tool(state, 'glib-genmarshal'), '--quiet'] + cmd: T.List[T.Union['AnyProgram', str]] = [self._find_tool(state, 'glib-genmarshal'), '--quiet'] if kwargs['prefix']: cmd.extend(['--prefix', kwargs['prefix']]) if kwargs['extra_args']: @@ -2242,8 +2240,7 @@ def generate_vapi(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'Gener build_dir = os.path.join(state.environment.get_build_dir(), state.subdir) source_dir = os.path.join(state.environment.get_source_dir(), state.subdir) pkg_cmd, vapi_depends, vapi_packages, vapi_includes, packages = self._extract_vapi_packages(state, kwargs['packages']) - cmd: T.List[T.Union[ExternalProgram, Executable, OverrideProgram, str]] - cmd = [state.find_program('vapigen'), '--quiet', f'--library={library}', f'--directory={build_dir}'] + cmd: CommandList = [state.find_program('vapigen'), '--quiet', f'--library={library}', f'--directory={build_dir}'] cmd.extend([f'--vapidir={d}' for d in kwargs['vapi_dirs']]) cmd.extend([f'--metadatadir={d}' for d in kwargs['metadata_dirs']]) cmd.extend([f'--girdir={d}' for d in kwargs['gir_dirs']]) diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index a22c4317e2bf..f96efbbbe75d 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -33,7 +33,7 @@ from ..interpreter import kwargs as _kwargs from ..interpreter.interpreter import SourceInputs, SourceOutputs from ..interpreter.interpreterobjects import Test - from ..programs import OverrideProgram + from ..programs import AnyProgram from ..interpreter.type_checking import SourcesVarargsType from typing_extensions import TypedDict, Literal @@ -91,7 +91,7 @@ class RustModule(ExtensionModule): def __init__(self, interpreter: Interpreter) -> None: super().__init__(interpreter) - self._bindgen_bin: T.Optional[T.Union[ExternalProgram, Executable, OverrideProgram]] = None + self._bindgen_bin: T.Optional[AnyProgram] = None if 'rust' in interpreter.compilers.host: rustc = T.cast('RustCompiler', interpreter.compilers.host['rust']) self._bindgen_rust_target = 'nightly' if rustc.is_nightly else rustc.version From 2b9d8d4a8dbcffea1a22a503605c4232f55113c4 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 18 Nov 2025 08:54:35 -0800 Subject: [PATCH 4/5] modules/windows: Clean up finding windres There's some typing issues here that are shadowed by not using the shared helpers. As soon as we switch over to those we run into clear type problems. Let's fix that. --- mesonbuild/modules/windows.py | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index 3250c072ca9a..4df975c5e3af 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -23,6 +23,7 @@ from ..compilers import Compiler from ..interpreter import Interpreter from ..interpreter.interpreter import SourceOutputs + from ..programs import AnyProgram, CommandList from typing_extensions import TypedDict @@ -62,7 +63,7 @@ def detect_compiler(self, compilers: T.Dict[str, 'Compiler']) -> 'Compiler': return compilers[l] raise MesonException('Resource compilation requires a C or C++ compiler.') - def _find_resource_compiler(self, state: 'ModuleState') -> T.Tuple[ExternalProgram, ResourceCompilerType]: + def _find_resource_compiler(self, state: 'ModuleState') -> T.Tuple[AnyProgram, ResourceCompilerType]: # FIXME: Does not handle `native: true` executables, see # See https://github.com/mesonbuild/meson/issues/1531 # Take a parameter instead of the hardcoded definition below @@ -71,24 +72,17 @@ def _find_resource_compiler(self, state: 'ModuleState') -> T.Tuple[ExternalProgr if self._rescomp: return self._rescomp - # Will try cross / native file and then env var - rescomp = ExternalProgram.from_bin_list(state.environment, for_machine, 'windres') - - if not rescomp or not rescomp.found(): - def search_programs(names: T.List[str]) -> T.Optional[ExternalProgram]: - for name in names: - program = ExternalProgram(name, silent=True) - if program.found(): - return program - return None + comp = self.detect_compiler(state.environment.coredata.compilers[for_machine]) + rescomp_names: T.List[mesonlib.FileOrString] + if comp.id in {'msvc', 'clang-cl', 'intel-cl'} or (comp.linker and comp.linker.id in {'link', 'lld-link'}): + rescomp_names = ['rc', 'llvm-rc'] + else: + rescomp_names = ['windres', 'llvm-windres'] - comp = self.detect_compiler(state.environment.coredata.compilers[for_machine]) - if comp.id in {'msvc', 'clang-cl', 'intel-cl'} or (comp.linker and comp.linker.id in {'link', 'lld-link'}): - rescomp = search_programs(['rc', 'llvm-rc']) - else: - rescomp = search_programs(['windres', 'llvm-windres']) + rescomp = state.find_program( + rescomp_names, silent=True, required=False, for_machine=for_machine) - if not rescomp: + if not rescomp.found(): raise MesonException('Could not find Windows resource compiler') for (arg, match, rc_type) in [ @@ -208,8 +202,7 @@ def get_names() -> T.Iterable[T.Tuple[str, str, T.Union[str, mesonlib.File, buil name = name.replace('/', '_').replace('\\', '_').replace(':', '_') name_formatted = name_formatted.replace('/', '_').replace('\\', '_').replace(':', '_') output = f'{name}_@BASENAME@.{suffix}' - command: T.List[T.Union[str, ExternalProgram]] = [] - command.append(rescomp) + command: CommandList = [rescomp] command.extend(res_args) depfile: T.Optional[str] = None extra_depends = wrc_depends.copy() From 4532db0e2954ba8a7f963d62ab9b8e012d985e60 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 18 Nov 2025 09:36:09 -0800 Subject: [PATCH 5/5] modules/qt: Make use of program type aliases --- mesonbuild/modules/_qt.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mesonbuild/modules/_qt.py b/mesonbuild/modules/_qt.py index b75169e4b2b0..e0ed3edee92d 100644 --- a/mesonbuild/modules/_qt.py +++ b/mesonbuild/modules/_qt.py @@ -28,7 +28,7 @@ from ..interpreter import Interpreter from ..interpreter import kwargs from ..mesonlib import FileOrString - from ..programs import ExternalProgram + from ..programs import AnyProgram, CommandList from typing_extensions import Literal QtDependencyType = T.Union[QtPkgConfigDependency, QmakeQtDependency] @@ -208,8 +208,8 @@ def __init__(self, interpreter: Interpreter, qt_version: int = 5): ExtensionModule.__init__(self, interpreter) self.qt_version = qt_version # It is important that this list does not change order as the order of - # the returned ExternalPrograms will change as well - self.tools: T.Dict[str, T.Union[ExternalProgram, build.Executable]] = { + # the returned program will change as well + self.tools: T.Dict[str, AnyProgram] = { tool: NonExistingExternalProgram(tool) for tool in self._set_of_qt_tools } self.methods.update({ @@ -251,7 +251,7 @@ def gen_bins() -> T.Generator[T.Tuple[str, str], None, None]: arg = ['-v'] # Ensure that the version of qt and each tool are the same - def get_version(p: T.Union[ExternalProgram, build.Executable]) -> str: + def get_version(p: AnyProgram) -> str: _, out, err = Popen_safe(p.get_command() + arg) if name == 'lrelease' or not qt_dep.version.startswith('4'): care = out @@ -727,7 +727,7 @@ def compile_translations(self, state: ModuleState, args: T.Tuple, kwargs: Compil ts = os.path.basename(ts) else: outdir = state.subdir - cmd: T.List[T.Union[ExternalProgram, build.Executable, str]] = [self.tools['lrelease'], '@INPUT@', '-qm', '@OUTPUT@'] + cmd: CommandList = [self.tools['lrelease'], '@INPUT@', '-qm', '@OUTPUT@'] lrelease_target = build.CustomTarget( f'qt{self.qt_version}-compile-{ts}', outdir,