diff --git a/docs/yaml/builtins/meson.yaml b/docs/yaml/builtins/meson.yaml index 3e486dac0..9d6c5ef2e 100644 --- a/docs/yaml/builtins/meson.yaml +++ b/docs/yaml/builtins/meson.yaml @@ -392,6 +392,15 @@ methods: type: exe | file | program description: The program to set as the override for `progname`. + native: + type: bool + since: 1.12.0 + default: false + description : | + If set to `true`, the override is for the build machine, and will be returned + by `find_program(..., native : true)`, otherwise for the host machine. This + is an important distinction when cross compiling. + - name: override_dependency returns: void since: 0.54.0 diff --git a/mesonbuild/build.py b/mesonbuild/build.py index f7d40c6d5..ce73bae8e 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -344,8 +344,11 @@ class Build: self.dep_manifest: T.Dict[str, DepManifest] = {} self.test_setups: T.Dict[str, TestSetup] = {} self.test_setup_default_name: str | None = None - self.find_overrides: T.Dict[str, programs.Program] = {} - self.searched_programs: T.Set[str] = set() # The list of all programs that have been searched for. + self.find_overrides: PerMachine[T.Dict[str, programs.Program]] = PerMachineDefaultable.default( + environment.is_cross_build(), {}, {}) + # The list of all programs that have been searched for. + self.searched_programs: PerMachine[T.Set[str]] = PerMachineDefaultable.default( + environment.is_cross_build(), set(), set()) # If we are doing a cross build we need two caches, if we're doing a # build == host compilation the both caches should point to the same place. diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 89ac5b119..40b546c4e 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -797,9 +797,10 @@ class Interpreter(InterpreterBase, HoldableObject): def _compiled_exe_error(self, cmd: T.Union[Program, build.Executable]) -> T.NoReturn: descr = cmd.name if isinstance(cmd, build.Executable) else cmd.description() - for name, exe in self.build.find_overrides.items(): - if cmd == exe: - raise InterpreterException(f'Program {name!r} was overridden with the compiled executable {descr!r} and therefore cannot be used during configuration') + for for_machine in MachineChoice: + for name, exe in self.build.find_overrides[for_machine].items(): + if cmd == exe: + raise InterpreterException(f'Program {name!r} was overridden with the compiled executable {descr!r} and therefore cannot be used during configuration') raise InterpreterException(f'Program {descr!r} is a compiled executable and therefore cannot be used during configuration') def run_command_impl(self, @@ -1696,31 +1697,32 @@ class Interpreter(InterpreterBase, HoldableObject): return None def program_from_overrides(self, command_names: T.List[mesonlib.FileOrString], + for_machine: MachineChoice, extra_info: T.List['mlog.TV_Loggable'] ) -> T.Optional[Program]: for name in command_names: if not isinstance(name, str): continue - if name in self.build.find_overrides: - exe = self.build.find_overrides[name] + if name in self.build.find_overrides[for_machine]: + exe = self.build.find_overrides[for_machine][name] extra_info.append(mlog.blue('(overridden)')) return exe return None - def store_name_lookups(self, command_names: T.List[mesonlib.FileOrString]) -> None: + def store_name_lookups(self, command_names: T.List[mesonlib.FileOrString], for_machine: MachineChoice) -> None: for name in command_names: if isinstance(name, str): - self.build.searched_programs.add(name) + self.build.searched_programs[for_machine].add(name) - def add_find_program_override(self, name: str, exe: Program) -> None: - if name in self.build.searched_programs: + def add_find_program_override(self, name: str, exe: Program, for_machine: MachineChoice) -> None: + if name in self.build.searched_programs[for_machine]: raise InterpreterException(f'Tried to override finding of executable "{name}" which has already been found.') - if name in self.build.find_overrides: + if name in self.build.find_overrides[for_machine]: raise InterpreterException(f'Tried to override executable "{name}" which has already been overridden.') - self.build.find_overrides[name] = exe + self.build.find_overrides[for_machine][name] = exe if name == 'pkg-config' and isinstance(exe, ExternalProgram): from ..dependencies.pkgconfig import PkgConfigInterface - PkgConfigInterface.set_program_override(exe, MachineChoice.HOST) + PkgConfigInterface.set_program_override(exe, for_machine) def notfound_program(self, args: T.List[mesonlib.FileOrString]) -> ExternalProgram: return NonExistingExternalProgram(' '.join( @@ -1754,7 +1756,7 @@ class Interpreter(InterpreterBase, HoldableObject): return progobj # Only store successful lookups - self.store_name_lookups(args) + self.store_name_lookups(args, for_machine) if not silent: mlog.log('Program', mlog.bold(progobj.name), 'found:', mlog.green('YES'), *extra_info) return progobj @@ -1768,7 +1770,7 @@ class Interpreter(InterpreterBase, HoldableObject): version_func: T.Optional[ProgramVersionFunc], extra_info: T.List[mlog.TV_Loggable] ) -> T.Optional[Program]: - progobj = self.program_from_overrides(args, extra_info) + progobj = self.program_from_overrides(args, for_machine, extra_info) if progobj: return progobj @@ -1783,7 +1785,7 @@ class Interpreter(InterpreterBase, HoldableObject): if wrap_mode != WrapMode.nofallback and self.environment.wrap_resolver: fallback = self.environment.wrap_resolver.find_program_provider(args) if fallback and wrap_mode == WrapMode.forcefallback: - return self.find_program_fallback(fallback, args, default_options, required, extra_info) + return self.find_program_fallback(fallback, args, for_machine, default_options, required, extra_info) progobj = self.program_from_file_for(for_machine, args) if progobj is None: @@ -1801,7 +1803,7 @@ class Interpreter(InterpreterBase, HoldableObject): progobj = self.notfound_program(args) mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.red('NO'), *extra_info) extra_info.clear() - progobj = self.find_program_fallback(fallback, args, default_options, required, extra_info) + progobj = self.find_program_fallback(fallback, args, for_machine, default_options, required, extra_info) return progobj @@ -1830,6 +1832,7 @@ class Interpreter(InterpreterBase, HoldableObject): return True def find_program_fallback(self, fallback: SubProject, args: T.List[mesonlib.FileOrString], + for_machine: MachineChoice, default_options: OptionDict, required: bool, extra_info: T.List[mlog.TV_Loggable] ) -> T.Optional[Program]: @@ -1843,7 +1846,7 @@ class Interpreter(InterpreterBase, HoldableObject): 'options': None, } self.do_subproject(fallback, sp_kwargs) - return self.program_from_overrides(args, extra_info) + return self.program_from_overrides(args, for_machine, extra_info) @typed_pos_args('find_program', varargs=(str, mesonlib.File), min_varargs=1) @typed_kwargs( diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index 11ac225b6..95011319b 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -326,9 +326,11 @@ class MesonMain(MesonInterpreterObject): @FeatureNew('meson.override_find_program', '0.46.0') @typed_pos_args('meson.override_find_program', str, (mesonlib.File, Program, build.Executable)) - @noKwargs + @typed_kwargs('meson.override_find_program', NATIVE_KW.evolve(since='1.12.0')) @InterpreterObject.method('override_find_program') - def override_find_program_method(self, args: T.Tuple[str, T.Union[mesonlib.File, Program, build.Executable]], kwargs: 'TYPE_kwargs') -> None: + @apply_machine_map + def override_find_program_method(self, args: T.Tuple[str, T.Union[mesonlib.File, Program, build.Executable]], + kwargs: NativeKW) -> None: name, exe = args if isinstance(exe, mesonlib.File): abspath = exe.absolute_path(self.interpreter.environment.source_dir, @@ -339,7 +341,7 @@ class MesonMain(MesonInterpreterObject): exe = build.LocalProgram(prog, self.interpreter.project_version, file=exe) elif isinstance(exe, build.Executable): exe = build.LocalProgram(exe, self.interpreter.project_version) - self.interpreter.add_find_program_override(name, exe) + self.interpreter.add_find_program_override(name, exe, kwargs['native']) @typed_kwargs( 'meson.override_dependency', diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index 38be13de2..fd689f0d7 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -92,7 +92,7 @@ class ModuleState: def find_tool(self, name: str, depname: str, varname: str, required: bool = True, wanted: T.Optional[str] = None, native: bool = True) -> Program: # Look in overrides in case it's built as subproject - progobj = self._interpreter.program_from_overrides([name], []) + progobj = self._interpreter.program_from_overrides([name], MachineChoice.HOST, []) if progobj is not None: return progobj