compilers: No need to pass exe_wrapper everywhere

Places where compiler needs it already have access to Environment object
and can use it directly.

This fixes mypy complaining that not all compilers have self.exe_wrapper
in run() method that got moved to base class.
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index c3be900..af730f8 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -568,13 +568,12 @@
         else:
             extra_paths = []
 
-        is_cross_built = not self.environment.machines.matches_build_machine(exe_for_machine)
-        if is_cross_built and self.environment.need_exe_wrapper():
-            exe_wrapper = self.environment.get_exe_wrapper()
-            if not exe_wrapper or not exe_wrapper.found():
+        if self.environment.need_exe_wrapper(exe_for_machine):
+            if not self.environment.has_exe_wrapper():
                 msg = 'An exe_wrapper is needed but was not found. Please define one ' \
                       'in cross file and check the command and/or add it to PATH.'
                 raise MesonException(msg)
+            exe_wrapper = self.environment.get_exe_wrapper()
         else:
             if exe_cmd[0].endswith('.jar'):
                 exe_cmd = ['java', '-jar'] + exe_cmd
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index 8c76a21..048649a 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -39,7 +39,6 @@
     from ..environment import Environment
     from ..linkers.linkers import DynamicLinker
     from ..mesonlib import MachineChoice
-    from ..programs import ExternalProgram
     from .compilers import CompileCheckMode
 
     CompilerMixinBase = Compiler
@@ -61,13 +60,13 @@
     language = 'c'
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         # If a child ObjC or CPP class has already set it, don't set it ourselves
         Compiler.__init__(self, ccache, exelist, version, for_machine, info,
                           is_cross=is_cross, full_version=full_version, linker=linker)
-        CLikeCompiler.__init__(self, exe_wrapper)
+        CLikeCompiler.__init__(self)
 
     def get_no_stdinc_args(self) -> T.List[str]:
         return ['-nostdinc']
@@ -137,11 +136,11 @@
 class ClangCCompiler(_ClangCStds, ClangCompiler, CCompiler):
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  defines: T.Optional[T.Dict[str, str]] = None,
                  full_version: T.Optional[str] = None):
-        CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross, info, exe_wrapper, linker=linker, full_version=full_version)
+        CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross, info, linker=linker, full_version=full_version)
         ClangCompiler.__init__(self, defines)
         default_warn_args = ['-Wall', '-Winvalid-pch']
         self.warn_args = {'0': [],
@@ -202,7 +201,7 @@
     id = 'emscripten'
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  defines: T.Optional[T.Dict[str, str]] = None,
                  full_version: T.Optional[str] = None):
@@ -211,7 +210,7 @@
         if not version_compare(version, '>=1.39.19'):
             raise MesonException('Meson requires Emscripten >= 1.39.19')
         ClangCCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                                info, exe_wrapper=exe_wrapper, linker=linker,
+                                info, linker=linker,
                                 defines=defines, full_version=full_version)
 
 
@@ -221,11 +220,11 @@
     '''
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                           info, exe_wrapper, linker=linker, full_version=full_version)
+                           info, linker=linker, full_version=full_version)
         ArmclangCompiler.__init__(self)
         default_warn_args = ['-Wall', '-Winvalid-pch']
         self.warn_args = {'0': [],
@@ -260,11 +259,11 @@
     _INVALID_PCH_VERSION = ">=3.4.0"
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  defines: T.Optional[T.Dict[str, str]] = None,
                  full_version: T.Optional[str] = None):
-        CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross, info, exe_wrapper, linker=linker, full_version=full_version)
+        CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross, info, linker=linker, full_version=full_version)
         GnuCompiler.__init__(self, defines)
         default_warn_args = ['-Wall']
         if version_compare(self.version, self._INVALID_PCH_VERSION):
@@ -322,11 +321,11 @@
 
 class PGICCompiler(PGICompiler, CCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                           info, exe_wrapper, linker=linker, full_version=full_version)
+                           info, linker=linker, full_version=full_version)
         PGICompiler.__init__(self)
 
 
@@ -335,22 +334,22 @@
     id = 'nvidia_hpc'
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                           info, exe_wrapper, linker=linker, full_version=full_version)
+                           info, linker=linker, full_version=full_version)
         PGICompiler.__init__(self)
 
 
 class ElbrusCCompiler(ElbrusCompiler, CCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  defines: T.Optional[T.Dict[str, str]] = None,
                  full_version: T.Optional[str] = None):
         CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                           info, exe_wrapper, linker=linker, full_version=full_version)
+                           info, linker=linker, full_version=full_version)
         ElbrusCompiler.__init__(self)
 
     def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -385,11 +384,11 @@
 
 class IntelCCompiler(IntelGnuLikeCompiler, CCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                           info, exe_wrapper, linker=linker, full_version=full_version)
+                           info, linker=linker, full_version=full_version)
         IntelGnuLikeCompiler.__init__(self)
         self.lang_header = 'c-header'
         default_warn_args = ['-Wall', '-w3']
@@ -453,11 +452,10 @@
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo', target: str,
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                           info, exe_wrapper, linker=linker,
+                           info, linker=linker,
                            full_version=full_version)
         MSVCCompiler.__init__(self, target)
 
@@ -487,11 +485,10 @@
 class ClangClCCompiler(_ClangCStds, ClangClCompiler, VisualStudioLikeCCompilerMixin, CCompiler):
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo', target: str,
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CCompiler.__init__(self, [], exelist, version, for_machine, is_cross,
-                           info, exe_wrapper, linker=linker,
+                           info, linker=linker,
                            full_version=full_version)
         ClangClCompiler.__init__(self, target)
 
@@ -509,11 +506,10 @@
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo', target: str,
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CCompiler.__init__(self, [], exelist, version, for_machine, is_cross,
-                           info, exe_wrapper, linker=linker,
+                           info, linker=linker,
                            full_version=full_version)
         IntelVisualStudioLikeCompiler.__init__(self, target)
 
@@ -543,11 +539,10 @@
 class ArmCCompiler(ArmCompiler, CCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                           info, exe_wrapper, linker=linker,
+                           info, linker=linker,
                            full_version=full_version)
         ArmCompiler.__init__(self)
 
@@ -570,11 +565,10 @@
 class CcrxCCompiler(CcrxCompiler, CCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                           info, exe_wrapper, linker=linker, full_version=full_version)
+                           info, linker=linker, full_version=full_version)
         CcrxCompiler.__init__(self)
 
     # Override CCompiler.get_always_args
@@ -622,11 +616,10 @@
 class Xc16CCompiler(Xc16Compiler, CCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                           info, exe_wrapper, linker=linker, full_version=full_version)
+                           info, linker=linker, full_version=full_version)
         Xc16Compiler.__init__(self)
 
     def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -668,11 +661,10 @@
 class CompCertCCompiler(CompCertCompiler, CCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                           info, exe_wrapper, linker=linker, full_version=full_version)
+                           info, linker=linker, full_version=full_version)
         CompCertCompiler.__init__(self)
 
     def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -702,11 +694,10 @@
 class TICCompiler(TICompiler, CCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                           info, exe_wrapper, linker=linker, full_version=full_version)
+                           info, linker=linker, full_version=full_version)
         TICompiler.__init__(self)
 
     # Override CCompiler.get_always_args
@@ -743,11 +734,10 @@
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                           info, exe_wrapper, linker=linker, full_version=full_version)
+                           info, linker=linker, full_version=full_version)
         MetrowerksCompiler.__init__(self)
 
     def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
@@ -772,11 +762,10 @@
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                           info, exe_wrapper, linker=linker, full_version=full_version)
+                           info, linker=linker, full_version=full_version)
         MetrowerksCompiler.__init__(self)
 
     def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 474c87e..9e99706 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -629,14 +629,14 @@
             run_env: T.Optional[T.Dict[str, str]] = None,
             run_cwd: T.Optional[str] = None) -> RunResult:
         need_exe_wrapper = env.need_exe_wrapper(self.for_machine)
-        if need_exe_wrapper and self.exe_wrapper is None:
+        if need_exe_wrapper and not env.has_exe_wrapper():
             raise CrossNoRunException('Can not run test applications in this cross environment.')
         with self._build_wrapper(code, env, extra_args, dependencies, mode=CompileCheckMode.LINK, want_output=True) as p:
             if p.returncode != 0:
                 mlog.debug(f'Could not compile test file {p.input_name}: {p.returncode}\n')
                 return RunResult(False)
             if need_exe_wrapper:
-                cmdlist = self.exe_wrapper.get_command() + [p.output_name]
+                cmdlist = env.exe_wrapper.get_command() + [p.output_name]
             else:
                 cmdlist = [p.output_name]
             try:
diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py
index fdf632a..5e412e7 100644
--- a/mesonbuild/compilers/cpp.py
+++ b/mesonbuild/compilers/cpp.py
@@ -40,7 +40,6 @@
     from ..environment import Environment
     from ..linkers.linkers import DynamicLinker
     from ..mesonlib import MachineChoice
-    from ..programs import ExternalProgram
     CompilerMixinBase = CLikeCompiler
 else:
     CompilerMixinBase = object
@@ -67,14 +66,14 @@
     language = 'cpp'
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         # If a child ObjCPP class has already set it, don't set it ourselves
         Compiler.__init__(self, ccache, exelist, version, for_machine, info,
                           is_cross=is_cross, linker=linker,
                           full_version=full_version)
-        CLikeCompiler.__init__(self, exe_wrapper)
+        CLikeCompiler.__init__(self)
 
     @classmethod
     def get_display_language(cls) -> str:
@@ -221,12 +220,12 @@
     _CPP26_VERSION = '>=17.0.0'
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  defines: T.Optional[T.Dict[str, str]] = None,
                  full_version: T.Optional[str] = None):
         CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                             info, exe_wrapper, linker=linker, full_version=full_version)
+                             info, linker=linker, full_version=full_version)
         ClangCompiler.__init__(self, defines)
         default_warn_args = ['-Wall', '-Winvalid-pch']
         self.warn_args = {'0': [],
@@ -336,7 +335,7 @@
     id = 'emscripten'
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  defines: T.Optional[T.Dict[str, str]] = None,
                  full_version: T.Optional[str] = None):
@@ -345,7 +344,7 @@
         if not version_compare(version, '>=1.39.19'):
             raise MesonException('Meson requires Emscripten >= 1.39.19')
         ClangCPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                                  info, exe_wrapper=exe_wrapper, linker=linker,
+                                  info, linker=linker,
                                   defines=defines, full_version=full_version)
 
     def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
@@ -363,11 +362,11 @@
     '''
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                             info, exe_wrapper, linker=linker, full_version=full_version)
+                             info, linker=linker, full_version=full_version)
         ArmclangCompiler.__init__(self)
         default_warn_args = ['-Wall', '-Winvalid-pch']
         self.warn_args = {'0': [],
@@ -408,12 +407,12 @@
 
 class GnuCPPCompiler(_StdCPPLibMixin, GnuCompiler, CPPCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  defines: T.Optional[T.Dict[str, str]] = None,
                  full_version: T.Optional[str] = None):
         CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                             info, exe_wrapper, linker=linker, full_version=full_version)
+                             info, linker=linker, full_version=full_version)
         GnuCompiler.__init__(self, defines)
         default_warn_args = ['-Wall', '-Winvalid-pch']
         self.warn_args = {'0': [],
@@ -500,11 +499,11 @@
 
 class PGICPPCompiler(PGICompiler, CPPCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                             info, exe_wrapper, linker=linker, full_version=full_version)
+                             info, linker=linker, full_version=full_version)
         PGICompiler.__init__(self)
 
 
@@ -513,22 +512,22 @@
     id = 'nvidia_hpc'
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                             info, exe_wrapper, linker=linker, full_version=full_version)
+                             info, linker=linker, full_version=full_version)
         PGICompiler.__init__(self)
 
 
 class ElbrusCPPCompiler(ElbrusCompiler, CPPCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  defines: T.Optional[T.Dict[str, str]] = None,
                  full_version: T.Optional[str] = None):
         CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                             info, exe_wrapper, linker=linker, full_version=full_version)
+                             info, linker=linker, full_version=full_version)
         ElbrusCompiler.__init__(self)
 
     def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -596,11 +595,11 @@
 
 class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                             info, exe_wrapper, linker=linker, full_version=full_version)
+                             info, linker=linker, full_version=full_version)
         IntelGnuLikeCompiler.__init__(self)
         self.lang_header = 'c++-header'
         default_warn_args = ['-Wall', '-w3', '-Wpch-messages']
@@ -776,11 +775,10 @@
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo', target: str,
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                             info, exe_wrapper, linker=linker, full_version=full_version)
+                             info, linker=linker, full_version=full_version)
         MSVCCompiler.__init__(self, target)
 
         # By default, MSVC has a broken __cplusplus define that pretends to be c++98:
@@ -824,11 +822,10 @@
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo', target: str,
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CPPCompiler.__init__(self, [], exelist, version, for_machine, is_cross,
-                             info, exe_wrapper, linker=linker, full_version=full_version)
+                             info, linker=linker, full_version=full_version)
         ClangClCompiler.__init__(self, target)
 
     def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -840,11 +837,10 @@
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo', target: str,
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CPPCompiler.__init__(self, [], exelist, version, for_machine, is_cross,
-                             info, exe_wrapper, linker=linker, full_version=full_version)
+                             info, linker=linker, full_version=full_version)
         IntelVisualStudioLikeCompiler.__init__(self, target)
 
     def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -864,11 +860,11 @@
 
 class ArmCPPCompiler(ArmCompiler, CPPCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                             info, exe_wrapper, linker=linker, full_version=full_version)
+                             info, linker=linker, full_version=full_version)
         ArmCompiler.__init__(self)
 
     def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -897,11 +893,11 @@
 
 class CcrxCPPCompiler(CcrxCompiler, CPPCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                             info, exe_wrapper, linker=linker, full_version=full_version)
+                             info, linker=linker, full_version=full_version)
         CcrxCompiler.__init__(self)
 
     # Override CCompiler.get_always_args
@@ -925,11 +921,11 @@
 
 class TICPPCompiler(TICompiler, CPPCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                             info, exe_wrapper, linker=linker, full_version=full_version)
+                             info, linker=linker, full_version=full_version)
         TICompiler.__init__(self)
 
     def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -965,11 +961,10 @@
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                             info, exe_wrapper, linker=linker, full_version=full_version)
+                             info, linker=linker, full_version=full_version)
         MetrowerksCompiler.__init__(self)
 
     def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
@@ -994,11 +989,10 @@
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                             info, exe_wrapper, linker=linker, full_version=full_version)
+                             info, linker=linker, full_version=full_version)
         MetrowerksCompiler.__init__(self)
 
     def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py
index e17ba3f..391107f 100644
--- a/mesonbuild/compilers/cuda.py
+++ b/mesonbuild/compilers/cuda.py
@@ -25,7 +25,6 @@
     from ..envconfig import MachineInfo
     from ..linkers.linkers import DynamicLinker
     from ..mesonlib import MachineChoice
-    from ..programs import ExternalProgram
 
 
 cuda_optimization_args: T.Dict[str, T.List[str]] = {
@@ -184,12 +183,11 @@
     id = 'nvcc'
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
-                 is_cross: bool, exe_wrapper: T.Optional['ExternalProgram'],
+                 is_cross: bool,
                  host_compiler: Compiler, info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         super().__init__(ccache, exelist, version, for_machine, info, linker=linker, full_version=full_version, is_cross=is_cross)
-        self.exe_wrapper = exe_wrapper
         self.host_compiler = host_compiler
         self.base_options = host_compiler.base_options
         # -Wpedantic generates useless churn due to nvcc's dual compilation model producing
@@ -550,7 +548,7 @@
         flags += self.get_ccbin_args(env.coredata.options)
 
         # If cross-compiling, we can't run the sanity check, only compile it.
-        if self.is_cross and self.exe_wrapper is None:
+        if env.need_exe_wrapper(self.for_machine) and not env.has_exe_wrapper():
             # Linking cross built apps is painful. You can't really
             # tell if you should use -nostdlib or not and for example
             # on OSX the compiler binary is the same but you need
@@ -572,11 +570,11 @@
             raise EnvironmentException(f'Compiler {self.name_string()} cannot compile programs.')
 
         # Run sanity check (if possible)
-        if self.is_cross:
-            if self.exe_wrapper is None:
+        if env.need_exe_wrapper(self.for_machine):
+            if not env.has_exe_wrapper():
                 return
             else:
-                cmdlist = self.exe_wrapper.get_command() + [binary_name]
+                cmdlist = env.exe_wrapper.get_command() + [binary_name]
         else:
             cmdlist = self.exelist + ['--run', '"' + binary_name + '"']
         mlog.debug('Sanity check run command line: ', ' '.join(cmdlist))
diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py
index 8425ae2..78ce2bc 100644
--- a/mesonbuild/compilers/d.py
+++ b/mesonbuild/compilers/d.py
@@ -27,7 +27,6 @@
 if T.TYPE_CHECKING:
     from ..build import DFeatures
     from ..dependencies import Dependency
-    from ..programs import ExternalProgram
     from ..envconfig import MachineInfo
     from ..environment import Environment
     from ..linkers.linkers import DynamicLinker
@@ -432,14 +431,12 @@
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
                  info: 'MachineInfo', arch: str, *,
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None,
                  is_cross: bool = False):
         super().__init__([], exelist, version, for_machine, info, linker=linker,
                          full_version=full_version, is_cross=is_cross)
         self.arch = arch
-        self.exe_wrapper = exe_wrapper
 
     def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
         source_name = os.path.join(work_dir, 'sanity.d')
@@ -450,11 +447,11 @@
         pc.wait()
         if pc.returncode != 0:
             raise EnvironmentException('D compiler %s cannot compile programs.' % self.name_string())
-        if self.is_cross:
-            if self.exe_wrapper is None:
+        if environment.need_exe_wrapper(self.for_machine):
+            if not environment.has_exe_wrapper():
                 # Can't check if the binaries run so we have to assume they do
                 return
-            cmdlist = self.exe_wrapper.get_command() + [output_name]
+            cmdlist = environment.exe_wrapper.get_command() + [output_name]
         else:
             cmdlist = [output_name]
         if subprocess.call(cmdlist) != 0:
@@ -641,12 +638,11 @@
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
                  info: 'MachineInfo', arch: str, *,
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None,
                  is_cross: bool = False):
         DCompiler.__init__(self, exelist, version, for_machine, info, arch,
-                           exe_wrapper=exe_wrapper, linker=linker,
+                           linker=linker,
                            full_version=full_version, is_cross=is_cross)
         GnuCompiler.__init__(self, {})
         default_warn_args = ['-Wall', '-Wdeprecated']
@@ -725,12 +721,11 @@
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
                  info: 'MachineInfo', arch: str, *,
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None,
                  is_cross: bool = False, version_output: T.Optional[str] = None):
         DCompiler.__init__(self, exelist, version, for_machine, info, arch,
-                           exe_wrapper=exe_wrapper, linker=linker,
+                           linker=linker,
                            full_version=full_version, is_cross=is_cross)
         DmdLikeCompilerMixin.__init__(self, dmd_frontend_version=find_ldc_dmd_frontend_version(version_output))
         self.base_options = {OptionKey(o) for o in ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']}
@@ -789,12 +784,11 @@
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
                  info: 'MachineInfo', arch: str, *,
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None,
                  is_cross: bool = False):
         DCompiler.__init__(self, exelist, version, for_machine, info, arch,
-                           exe_wrapper=exe_wrapper, linker=linker,
+                           linker=linker,
                            full_version=full_version, is_cross=is_cross)
         DmdLikeCompilerMixin.__init__(self, version)
         self.base_options = {OptionKey(o) for o in ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']}
diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py
index f047242..aed0339 100644
--- a/mesonbuild/compilers/detect.py
+++ b/mesonbuild/compilers/detect.py
@@ -28,7 +28,6 @@
     from .rust import RustCompiler
     from ..linkers.linkers import StaticLinker, DynamicLinker
     from ..environment import Environment
-    from ..programs import ExternalProgram
 
 
 # Default compilers and linkers
@@ -116,7 +115,7 @@
 # Helpers
 # =======
 
-def _get_compilers(env: 'Environment', lang: str, for_machine: MachineChoice) -> T.Tuple[T.List[T.List[str]], T.List[str], T.Optional['ExternalProgram']]:
+def _get_compilers(env: 'Environment', lang: str, for_machine: MachineChoice) -> T.Tuple[T.List[T.List[str]], T.List[str]]:
     '''
     The list of compilers is detected in the exact same way for
     C, C++, ObjC, ObjC++, Fortran, CS so consolidate it here.
@@ -132,12 +131,7 @@
         compilers = [[x] for x in defaults[lang]]
         ccache = BinaryTable.detect_compiler_cache()
 
-    if env.machines.matches_build_machine(for_machine):
-        exe_wrap: T.Optional[ExternalProgram] = None
-    else:
-        exe_wrap = env.get_exe_wrapper()
-
-    return compilers, ccache, exe_wrap
+    return compilers, ccache
 
 def _handle_exceptions(
         exceptions: T.Mapping[str, T.Union[Exception, str]],
@@ -269,7 +263,7 @@
     from . import c, cpp
     from ..linkers import linkers
     popen_exceptions: T.Dict[str, T.Union[Exception, str]] = {}
-    compilers, ccache, exe_wrap = _get_compilers(env, lang, for_machine)
+    compilers, ccache = _get_compilers(env, lang, for_machine)
     if override_compiler is not None:
         compilers = [override_compiler]
     is_cross = env.is_cross_build(for_machine)
@@ -361,7 +355,7 @@
 
             return cls(
                 ccache, compiler, version, for_machine, is_cross,
-                info, exe_wrap, defines=defines, full_version=full_version,
+                info, defines=defines, full_version=full_version,
                 linker=linker)
 
         if 'Emscripten' in out:
@@ -380,7 +374,7 @@
                 [], version=search_version(o))
             return cls(
                 ccache, compiler, version, for_machine, is_cross, info,
-                exe_wrap, linker=linker, full_version=full_version)
+                linker=linker, full_version=full_version)
 
         if 'Arm C/C++/Fortran Compiler' in out:
             arm_ver_match = re.search(r'version (\d+)\.(\d+)\.?(\d+)? \(build number (\d+)\)', out)
@@ -393,7 +387,7 @@
             linker = guess_nix_linker(env, compiler, cls, version, for_machine)
             return cls(
                 ccache, compiler, version, for_machine, is_cross, info,
-                exe_wrap, linker=linker)
+                linker=linker)
         if 'armclang' in out:
             # The compiler version is not present in the first line of output,
             # instead it is present in second line, startswith 'Component:'.
@@ -413,7 +407,7 @@
             env.coredata.add_lang_args(cls.language, cls, for_machine, env)
             return cls(
                 ccache, compiler, version, for_machine, is_cross, info,
-                exe_wrap, full_version=full_version, linker=linker)
+                full_version=full_version, linker=linker)
         if 'CL.EXE COMPATIBILITY' in out:
             # if this is clang-cl masquerading as cl, detect it as cl, not
             # clang
@@ -432,7 +426,7 @@
             linker = guess_win_linker(env, ['lld-link'], cls, version, for_machine)
             return cls(
                 compiler, version, for_machine, is_cross, info, target,
-                exe_wrap, linker=linker)
+                linker=linker)
 
         # must be detected here before clang because TI compilers contain 'clang' in their output and so that they can be detected as 'clang'
         ti_compilers = {
@@ -449,7 +443,7 @@
                 linker = lnk(compiler, for_machine, version=version)
                 return cls(
                     ccache, compiler, version, for_machine, is_cross, info,
-                    exe_wrap, full_version=full_version, linker=linker)
+                    full_version=full_version, linker=linker)
 
         if 'clang' in out or 'Clang' in out:
             linker = None
@@ -476,7 +470,7 @@
 
             return cls(
                 ccache, compiler, version, for_machine, is_cross, info,
-                exe_wrap, defines=defines, full_version=full_version, linker=linker)
+                defines=defines, full_version=full_version, linker=linker)
 
         if 'Intel(R) C++ Intel(R)' in err:
             version = search_version(err)
@@ -486,7 +480,7 @@
             linker = linkers.XilinkDynamicLinker(for_machine, [], version=version)
             return cls(
                 compiler, version, for_machine, is_cross, info, target,
-                exe_wrap, linker=linker)
+                linker=linker)
         if 'Intel(R) oneAPI DPC++/C++ Compiler for applications' in err:
             version = search_version(err)
             target = 'x86' if 'IA-32' in err else 'x86_64'
@@ -495,7 +489,7 @@
             linker = linkers.XilinkDynamicLinker(for_machine, [], version=version)
             return cls(
                 compiler, version, for_machine, is_cross, info, target,
-                exe_wrap, linker=linker)
+                linker=linker)
         if 'Microsoft' in out or 'Microsoft' in err:
             # Latest versions of Visual Studio print version
             # number to stderr but earlier ones print version
@@ -521,47 +515,47 @@
                 ccache = []
             return cls(
                 ccache, compiler, version, for_machine, is_cross, info, target,
-                exe_wrap, full_version=cl_signature, linker=linker)
+                full_version=cl_signature, linker=linker)
         if 'PGI Compilers' in out:
             cls = c.PGICCompiler if lang == 'c' else cpp.PGICPPCompiler
             env.coredata.add_lang_args(cls.language, cls, for_machine, env)
             linker = linkers.PGIDynamicLinker(compiler, for_machine, cls.LINKER_PREFIX, [], version=version)
             return cls(
                 ccache, compiler, version, for_machine, is_cross,
-                info, exe_wrap, linker=linker)
+                info, linker=linker)
         if 'NVIDIA Compilers and Tools' in out:
             cls = c.NvidiaHPC_CCompiler if lang == 'c' else cpp.NvidiaHPC_CPPCompiler
             env.coredata.add_lang_args(cls.language, cls, for_machine, env)
             linker = linkers.NvidiaHPC_DynamicLinker(compiler, for_machine, cls.LINKER_PREFIX, [], version=version)
             return cls(
                 ccache, compiler, version, for_machine, is_cross,
-                info, exe_wrap, linker=linker)
+                info, linker=linker)
         if '(ICC)' in out:
             cls = c.IntelCCompiler if lang == 'c' else cpp.IntelCPPCompiler
             l = guess_nix_linker(env, compiler, cls, version, for_machine)
             return cls(
                 ccache, compiler, version, for_machine, is_cross, info,
-                exe_wrap, full_version=full_version, linker=l)
+                full_version=full_version, linker=l)
         if 'Intel(R) oneAPI' in out:
             cls = c.IntelLLVMCCompiler if lang == 'c' else cpp.IntelLLVMCPPCompiler
             l = guess_nix_linker(env, compiler, cls, version, for_machine)
             return cls(
                 ccache, compiler, version, for_machine, is_cross, info,
-                exe_wrap, full_version=full_version, linker=l)
+                full_version=full_version, linker=l)
         if 'ARM' in out and not ('Metrowerks' in out or 'Freescale' in out):
             cls = c.ArmCCompiler if lang == 'c' else cpp.ArmCPPCompiler
             env.coredata.add_lang_args(cls.language, cls, for_machine, env)
             linker = linkers.ArmDynamicLinker(for_machine, version=version)
             return cls(
                 ccache, compiler, version, for_machine, is_cross,
-                info, exe_wrap, full_version=full_version, linker=linker)
+                info, full_version=full_version, linker=linker)
         if 'RX Family' in out:
             cls = c.CcrxCCompiler if lang == 'c' else cpp.CcrxCPPCompiler
             env.coredata.add_lang_args(cls.language, cls, for_machine, env)
             linker = linkers.CcrxDynamicLinker(for_machine, version=version)
             return cls(
                 ccache, compiler, version, for_machine, is_cross, info,
-                exe_wrap, full_version=full_version, linker=linker)
+                full_version=full_version, linker=linker)
 
         if 'Microchip Technology' in out:
             cls = c.Xc16CCompiler
@@ -569,7 +563,7 @@
             linker = linkers.Xc16DynamicLinker(for_machine, version=version)
             return cls(
                 ccache, compiler, version, for_machine, is_cross, info,
-                exe_wrap, full_version=full_version, linker=linker)
+                full_version=full_version, linker=linker)
 
         if 'CompCert' in out:
             cls = c.CompCertCCompiler
@@ -577,7 +571,7 @@
             linker = linkers.CompCertDynamicLinker(for_machine, version=version)
             return cls(
                 ccache, compiler, version, for_machine, is_cross, info,
-                exe_wrap, full_version=full_version, linker=linker)
+                full_version=full_version, linker=linker)
 
         if 'Metrowerks C/C++' in out or 'Freescale C/C++' in out:
             if 'ARM' in out:
@@ -607,7 +601,7 @@
 
             return cls(
                 ccache, compiler, compiler_version, for_machine, is_cross, info,
-                exe_wrap, full_version=full_version, linker=linker)
+                full_version=full_version, linker=linker)
 
     _handle_exceptions(popen_exceptions, compilers)
     raise EnvironmentException(f'Unknown compiler {compilers}')
@@ -623,7 +617,7 @@
     from ..linkers.linkers import CudaLinker
     popen_exceptions = {}
     is_cross = env.is_cross_build(for_machine)
-    compilers, ccache, exe_wrap = _get_compilers(env, 'cuda', for_machine)
+    compilers, ccache = _get_compilers(env, 'cuda', for_machine)
     info = env.machines[for_machine]
     for compiler in compilers:
         arg = '--version'
@@ -652,14 +646,14 @@
         cls = CudaCompiler
         env.coredata.add_lang_args(cls.language, cls, for_machine, env)
         linker = CudaLinker(compiler, for_machine, CudaCompiler.LINKER_PREFIX, [], version=CudaLinker.parse_version())
-        return cls(ccache, compiler, version, for_machine, is_cross, exe_wrap, host_compiler=cpp_compiler, info=info, linker=linker)
+        return cls(ccache, compiler, version, for_machine, is_cross, host_compiler=cpp_compiler, info=info, linker=linker)
     raise EnvironmentException(f'Could not find suitable CUDA compiler: "{"; ".join([" ".join(c) for c in compilers])}"')
 
 def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
     from . import fortran
     from ..linkers import linkers
     popen_exceptions: T.Dict[str, T.Union[Exception, str]] = {}
-    compilers, ccache, exe_wrap = _get_compilers(env, 'fortran', for_machine)
+    compilers, ccache = _get_compilers(env, 'fortran', for_machine)
     is_cross = env.is_cross_build(for_machine)
     info = env.machines[for_machine]
     cls: T.Type[FortranCompiler]
@@ -691,14 +685,14 @@
                     linker = guess_nix_linker(env, compiler, cls, version, for_machine)
                     return cls(
                         compiler, version, for_machine, is_cross, info,
-                        exe_wrap, defines, full_version=full_version, linker=linker)
+                        defines, full_version=full_version, linker=linker)
                 else:
                     version = _get_gnu_version_from_defines(defines)
                     cls = fortran.GnuFortranCompiler
                     linker = guess_nix_linker(env, compiler, cls, version, for_machine)
                     return cls(
                         compiler, version, for_machine, is_cross, info,
-                        exe_wrap, defines, full_version=full_version, linker=linker)
+                        defines, full_version=full_version, linker=linker)
 
             if 'Arm C/C++/Fortran Compiler' in out:
                 cls = fortran.ArmLtdFlangFortranCompiler
@@ -708,13 +702,13 @@
                 linker = guess_nix_linker(env, compiler, cls, version, for_machine)
                 return cls(
                     compiler, version, for_machine, is_cross, info,
-                    exe_wrap, linker=linker)
+                    linker=linker)
             if 'G95' in out:
                 cls = fortran.G95FortranCompiler
                 linker = guess_nix_linker(env, compiler, cls, version, for_machine)
                 return cls(
                     compiler, version, for_machine, is_cross, info,
-                    exe_wrap, full_version=full_version, linker=linker)
+                    full_version=full_version, linker=linker)
 
             if 'Sun Fortran' in err:
                 version = search_version(err)
@@ -722,7 +716,7 @@
                 linker = guess_nix_linker(env, compiler, cls, version, for_machine)
                 return cls(
                     compiler, version, for_machine, is_cross, info,
-                    exe_wrap, full_version=full_version, linker=linker)
+                    full_version=full_version, linker=linker)
 
             if 'Intel(R) Fortran Compiler for applications' in err:
                 version = search_version(err)
@@ -732,7 +726,7 @@
                 linker = linkers.XilinkDynamicLinker(for_machine, [], version=version)
                 return cls(
                     compiler, version, for_machine, is_cross, info,
-                    target, exe_wrap, linker=linker)
+                    target, linker=linker)
 
             if 'Intel(R) Visual Fortran' in err or 'Intel(R) Fortran' in err:
                 version = search_version(err)
@@ -742,26 +736,26 @@
                 linker = linkers.XilinkDynamicLinker(for_machine, [], version=version)
                 return cls(
                     compiler, version, for_machine, is_cross, info,
-                    target, exe_wrap, linker=linker)
+                    target, linker=linker)
 
             if 'ifort (IFORT)' in out:
                 cls = fortran.IntelFortranCompiler
                 linker = guess_nix_linker(env, compiler, cls, version, for_machine)
                 return cls(
                     compiler, version, for_machine, is_cross, info,
-                    exe_wrap, full_version=full_version, linker=linker)
+                    full_version=full_version, linker=linker)
 
             if 'ifx (IFORT)' in out or 'ifx (IFX)' in out:
                 cls = fortran.IntelLLVMFortranCompiler
                 linker = guess_nix_linker(env, compiler, cls, version, for_machine)
                 return cls(
                     compiler, version, for_machine, is_cross, info,
-                    exe_wrap, full_version=full_version, linker=linker)
+                    full_version=full_version, linker=linker)
 
             if 'PathScale EKOPath(tm)' in err:
                 return fortran.PathScaleFortranCompiler(
                     compiler, version, for_machine, is_cross, info,
-                    exe_wrap, full_version=full_version)
+                    full_version=full_version)
 
             if 'PGI Compilers' in out:
                 cls = fortran.PGIFortranCompiler
@@ -769,7 +763,7 @@
                 linker = linkers.PGIDynamicLinker(compiler, for_machine,
                                                   cls.LINKER_PREFIX, [], version=version)
                 return cls(
-                    compiler, version, for_machine, is_cross, info, exe_wrap,
+                    compiler, version, for_machine, is_cross, info,
                     full_version=full_version, linker=linker)
 
             if 'NVIDIA Compilers and Tools' in out:
@@ -778,7 +772,7 @@
                 linker = linkers.PGIDynamicLinker(compiler, for_machine,
                                                   cls.LINKER_PREFIX, [], version=version)
                 return cls(
-                    compiler, version, for_machine, is_cross, info, exe_wrap,
+                    compiler, version, for_machine, is_cross, info,
                     full_version=full_version, linker=linker)
 
             if 'flang' in out or 'clang' in out:
@@ -800,7 +794,7 @@
                                               version, for_machine)
                 return cls(
                     compiler, version, for_machine, is_cross, info,
-                    exe_wrap, full_version=full_version, linker=linker)
+                    full_version=full_version, linker=linker)
 
             if 'Open64 Compiler Suite' in err:
                 cls = fortran.Open64FortranCompiler
@@ -808,7 +802,7 @@
                                           compiler, cls, version, for_machine)
                 return cls(
                     compiler, version, for_machine, is_cross, info,
-                    exe_wrap, full_version=full_version, linker=linker)
+                    full_version=full_version, linker=linker)
 
             if 'NAG Fortran' in err:
                 full_version = err.split('\n', 1)[0]
@@ -820,7 +814,7 @@
                     version=version)
                 return cls(
                     compiler, version, for_machine, is_cross, info,
-                    exe_wrap, full_version=full_version, linker=linker)
+                    full_version=full_version, linker=linker)
 
     _handle_exceptions(popen_exceptions, compilers)
     raise EnvironmentException('Unreachable code (exception to make mypy happy)')
@@ -834,7 +828,7 @@
 def _detect_objc_or_objcpp_compiler(env: 'Environment', lang: str, for_machine: MachineChoice) -> 'Compiler':
     from . import objc, objcpp
     popen_exceptions: T.Dict[str, T.Union[Exception, str]] = {}
-    compilers, ccache, exe_wrap = _get_compilers(env, lang, for_machine)
+    compilers, ccache = _get_compilers(env, lang, for_machine)
     is_cross = env.is_cross_build(for_machine)
     info = env.machines[for_machine]
     comp: T.Union[T.Type[objc.ObjCCompiler], T.Type[objcpp.ObjCPPCompiler]]
@@ -857,7 +851,7 @@
             linker = guess_nix_linker(env, compiler, comp, version, for_machine)
             return comp(
                 ccache, compiler, version, for_machine, is_cross, info,
-                exe_wrap, defines, linker=linker)
+                defines, linker=linker)
         if 'clang' in out:
             linker = None
             defines = _get_clang_compiler_defines(compiler)
@@ -879,7 +873,7 @@
                 linker = guess_nix_linker(env, compiler, comp, version, for_machine)
             return comp(
                 ccache, compiler, version, for_machine,
-                is_cross, info, exe_wrap, linker=linker, defines=defines)
+                is_cross, info, linker=linker, defines=defines)
     _handle_exceptions(popen_exceptions, compilers)
     raise EnvironmentException('Unreachable code (exception to make mypy happy)')
 
@@ -908,7 +902,7 @@
 
 def detect_cs_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
     from . import cs
-    compilers, ccache, exe_wrap = _get_compilers(env, 'cs', for_machine)
+    compilers, ccache = _get_compilers(env, 'cs', for_machine)
     popen_exceptions = {}
     info = env.machines[for_machine]
     for comp in compilers:
@@ -935,7 +929,7 @@
 def detect_cython_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
     """Search for a cython compiler."""
     from .cython import CythonCompiler
-    compilers, _, _ = _get_compilers(env, 'cython', MachineChoice.BUILD)
+    compilers, _ = _get_compilers(env, 'cython', MachineChoice.BUILD)
     is_cross = env.is_cross_build(for_machine)
     info = env.machines[for_machine]
 
@@ -985,7 +979,7 @@
     from . import rust
     from ..linkers import linkers
     popen_exceptions: T.Dict[str, Exception] = {}
-    compilers, _, exe_wrap = _get_compilers(env, 'rust', for_machine)
+    compilers, _ = _get_compilers(env, 'rust', for_machine)
     is_cross = env.is_cross_build(for_machine)
     info = env.machines[for_machine]
 
@@ -1092,7 +1086,7 @@
 
             env.coredata.add_lang_args(cls.language, cls, for_machine, env)
             return cls(
-                compiler, version, for_machine, is_cross, info, exe_wrap,
+                compiler, version, for_machine, is_cross, info,
                 linker=linker)
 
     _handle_exceptions(popen_exceptions, compilers)
@@ -1117,7 +1111,7 @@
 
     popen_exceptions = {}
     is_cross = env.is_cross_build(for_machine)
-    compilers, ccache, exe_wrap = _get_compilers(env, 'd', for_machine)
+    compilers, ccache = _get_compilers(env, 'd', for_machine)
     cls: T.Type[d.DCompiler]
     for exelist in compilers:
         # Search for a D compiler.
@@ -1172,8 +1166,7 @@
             linker = guess_nix_linker(env, exelist, cls, version, for_machine)
             return cls(
                 exelist, version, for_machine, info, arch,
-                exe_wrapper=exe_wrap, is_cross=is_cross,
-                full_version=full_version, linker=linker)
+                is_cross=is_cross, full_version=full_version, linker=linker)
         elif 'The D Language Foundation' in out or 'Digital Mars' in out:
             cls = d.DmdDCompiler
             # DMD seems to require a file
@@ -1237,7 +1230,7 @@
 
 def detect_nasm_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
     from .asm import NasmCompiler, YasmCompiler, MetrowerksAsmCompilerARM, MetrowerksAsmCompilerEmbeddedPowerPC
-    compilers, _, _ = _get_compilers(env, 'nasm', for_machine)
+    compilers, _ = _get_compilers(env, 'nasm', for_machine)
     is_cross = env.is_cross_build(for_machine)
 
     # We need a C compiler to properly detect the machine info and linker
diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py
index d68c547..3a73650 100644
--- a/mesonbuild/compilers/fortran.py
+++ b/mesonbuild/compilers/fortran.py
@@ -31,7 +31,6 @@
     from ..environment import Environment
     from ..linkers.linkers import DynamicLinker
     from ..mesonlib import MachineChoice
-    from ..programs import ExternalProgram
 
 
 class FortranCompiler(CLikeCompiler, Compiler):
@@ -39,12 +38,12 @@
     language = 'fortran'
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         Compiler.__init__(self, [], exelist, version, for_machine, info,
                           is_cross=is_cross, full_version=full_version, linker=linker)
-        CLikeCompiler.__init__(self, exe_wrapper)
+        CLikeCompiler.__init__(self)
 
     def has_function(self, funcname: str, prefix: str, env: 'Environment', *,
                      extra_args: T.Optional[T.List[str]] = None,
@@ -128,12 +127,12 @@
 class GnuFortranCompiler(GnuCompiler, FortranCompiler):
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  defines: T.Optional[T.Dict[str, str]] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         FortranCompiler.__init__(self, exelist, version, for_machine,
-                                 is_cross, info, exe_wrapper, linker=linker,
+                                 is_cross, info, linker=linker,
                                  full_version=full_version)
         GnuCompiler.__init__(self, defines)
         default_warn_args = ['-Wall']
@@ -197,12 +196,12 @@
 
 class ElbrusFortranCompiler(ElbrusCompiler, FortranCompiler):
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  defines: T.Optional[T.Dict[str, str]] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         FortranCompiler.__init__(self, exelist, version, for_machine, is_cross,
-                                 info, exe_wrapper, linker=linker, full_version=full_version)
+                                 info, linker=linker, full_version=full_version)
         ElbrusCompiler.__init__(self)
 
     def get_options(self) -> 'MutableKeyedOptionDictType':
@@ -222,11 +221,11 @@
     id = 'g95'
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         FortranCompiler.__init__(self, exelist, version, for_machine,
-                                 is_cross, info, exe_wrapper, linker=linker,
+                                 is_cross, info, linker=linker,
                                  full_version=full_version)
         default_warn_args = ['-Wall']
         self.warn_args = {'0': [],
@@ -269,11 +268,11 @@
     id = 'intel'
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         FortranCompiler.__init__(self, exelist, version, for_machine,
-                                 is_cross, info, exe_wrapper, linker=linker,
+                                 is_cross, info, linker=linker,
                                  full_version=full_version)
         # FIXME: Add support for OS X and Windows in detect_fortran_compiler so
         # we are sent the type of compiler
@@ -323,11 +322,10 @@
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo', target: str,
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         FortranCompiler.__init__(self, exelist, version, for_machine,
-                                 is_cross, info, exe_wrapper, linker=linker,
+                                 is_cross, info, linker=linker,
                                  full_version=full_version)
         IntelVisualStudioLikeCompiler.__init__(self, target)
 
@@ -366,11 +364,11 @@
     id = 'pathscale'
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         FortranCompiler.__init__(self, exelist, version, for_machine,
-                                 is_cross, info, exe_wrapper, linker=linker,
+                                 is_cross, info, linker=linker,
                                  full_version=full_version)
         default_warn_args = ['-fullwarn']
         self.warn_args = {'0': [],
@@ -386,11 +384,11 @@
 class PGIFortranCompiler(PGICompiler, FortranCompiler):
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         FortranCompiler.__init__(self, exelist, version, for_machine,
-                                 is_cross, info, exe_wrapper, linker=linker,
+                                 is_cross, info, linker=linker,
                                  full_version=full_version)
         PGICompiler.__init__(self)
 
@@ -412,11 +410,11 @@
     id = 'nvidia_hpc'
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         FortranCompiler.__init__(self, exelist, version, for_machine,
-                                 is_cross, info, exe_wrapper, linker=linker,
+                                 is_cross, info, linker=linker,
                                  full_version=full_version)
         PGICompiler.__init__(self)
 
@@ -433,11 +431,11 @@
     id = 'flang'
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         FortranCompiler.__init__(self, exelist, version, for_machine,
-                                 is_cross, info, exe_wrapper, linker=linker,
+                                 is_cross, info, linker=linker,
                                  full_version=full_version)
         ClangCompiler.__init__(self, {})
         default_warn_args = ['-Minform=inform']
@@ -467,11 +465,11 @@
     id = 'open64'
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         FortranCompiler.__init__(self, exelist, version, for_machine,
-                                 is_cross, info, exe_wrapper, linker=linker,
+                                 is_cross, info, linker=linker,
                                  full_version=full_version)
         default_warn_args = ['-fullwarn']
         self.warn_args = {'0': [],
@@ -489,11 +487,11 @@
     id = 'nagfor'
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
-                 info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+                 info: 'MachineInfo',
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         FortranCompiler.__init__(self, exelist, version, for_machine,
-                                 is_cross, info, exe_wrapper, linker=linker,
+                                 is_cross, info, linker=linker,
                                  full_version=full_version)
         # Warnings are on by default; -w disables (by category):
         self.warn_args = {
diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py
index eb3a4f1..09bd438 100644
--- a/mesonbuild/compilers/mixins/clike.py
+++ b/mesonbuild/compilers/mixins/clike.py
@@ -36,7 +36,6 @@
     from ..._typing import ImmutableListProtocol
     from ...environment import Environment
     from ...compilers.compilers import Compiler
-    from ...programs import ExternalProgram
 else:
     # This is a bit clever, for mypy we pretend that these mixins descend from
     # Compiler, so we get all of the methods and attributes defined for us, but
@@ -133,15 +132,9 @@
     find_framework_cache: T.Dict[T.Tuple[T.Tuple[str, ...], str, T.Tuple[str, ...], bool], T.Optional[T.List[str]]] = {}
     internal_libs = arglist.UNIXY_COMPILER_INTERNAL_LIBS
 
-    def __init__(self, exe_wrapper: T.Optional['ExternalProgram'] = None):
+    def __init__(self) -> None:
         # If a child ObjC or CPP class has already set it, don't set it ourselves
         self.can_compile_suffixes.add('h')
-        # If the exe wrapper was not found, pretend it wasn't set so that the
-        # sanity check is skipped and compiler checks use fallbacks.
-        if not exe_wrapper or not exe_wrapper.found() or not exe_wrapper.get_command():
-            self.exe_wrapper = None
-        else:
-            self.exe_wrapper = exe_wrapper
         # Lazy initialized in get_preprocessor()
         self.preprocessor: T.Optional[Compiler] = None
 
@@ -285,7 +278,7 @@
         mode = CompileCheckMode.LINK
         if self.is_cross:
             binname += '_cross'
-            if self.exe_wrapper is None:
+            if environment.need_exe_wrapper(self.for_machine) and not environment.has_exe_wrapper():
                 # Linking cross built C/C++ apps is painful. You can't really
                 # tell if you should use -nostdlib or not and for example
                 # on OSX the compiler binary is the same but you need
@@ -315,11 +308,11 @@
         if pc.returncode != 0:
             raise mesonlib.EnvironmentException(f'Compiler {self.name_string()} cannot compile programs.')
         # Run sanity check
-        if self.is_cross:
-            if self.exe_wrapper is None:
+        if environment.need_exe_wrapper(self.for_machine):
+            if not environment.has_exe_wrapper():
                 # Can't check if the binaries run so we have to assume they do
                 return
-            cmdlist = self.exe_wrapper.get_command() + [binary_name]
+            cmdlist = environment.exe_wrapper.get_command() + [binary_name]
         else:
             cmdlist = [binary_name]
         mlog.debug('Running test binary command: ', mesonlib.join_args(cmdlist))
diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py
index 23008e6..f805bd6 100644
--- a/mesonbuild/compilers/objc.py
+++ b/mesonbuild/compilers/objc.py
@@ -14,7 +14,6 @@
 from .mixins.clang import ClangCompiler
 
 if T.TYPE_CHECKING:
-    from ..programs import ExternalProgram
     from ..envconfig import MachineInfo
     from ..environment import Environment
     from ..linkers.linkers import DynamicLinker
@@ -27,13 +26,12 @@
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrap: T.Optional['ExternalProgram'],
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         Compiler.__init__(self, ccache, exelist, version, for_machine, info,
                           is_cross=is_cross, full_version=full_version,
                           linker=linker)
-        CLikeCompiler.__init__(self, exe_wrap)
+        CLikeCompiler.__init__(self)
 
     @staticmethod
     def get_display_language() -> str:
@@ -47,12 +45,11 @@
 class GnuObjCCompiler(GnuCompiler, ObjCCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  defines: T.Optional[T.Dict[str, str]] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         ObjCCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                              info, exe_wrapper, linker=linker, full_version=full_version)
+                              info, linker=linker, full_version=full_version)
         GnuCompiler.__init__(self, defines)
         default_warn_args = ['-Wall', '-Winvalid-pch']
         self.warn_args = {'0': [],
@@ -67,12 +64,11 @@
 class ClangObjCCompiler(ClangCompiler, ObjCCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  defines: T.Optional[T.Dict[str, str]] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         ObjCCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                              info, exe_wrapper, linker=linker, full_version=full_version)
+                              info, linker=linker, full_version=full_version)
         ClangCompiler.__init__(self, defines)
         default_warn_args = ['-Wall', '-Winvalid-pch']
         self.warn_args = {'0': [],
diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py
index a5664da..baf4fb5 100644
--- a/mesonbuild/compilers/objcpp.py
+++ b/mesonbuild/compilers/objcpp.py
@@ -14,7 +14,6 @@
 from .mixins.clang import ClangCompiler
 
 if T.TYPE_CHECKING:
-    from ..programs import ExternalProgram
     from ..envconfig import MachineInfo
     from ..environment import Environment
     from ..linkers.linkers import DynamicLinker
@@ -26,13 +25,12 @@
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrap: T.Optional['ExternalProgram'],
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         Compiler.__init__(self, ccache, exelist, version, for_machine, info,
                           is_cross=is_cross, full_version=full_version,
                           linker=linker)
-        CLikeCompiler.__init__(self, exe_wrap)
+        CLikeCompiler.__init__(self)
 
     @staticmethod
     def get_display_language() -> str:
@@ -46,12 +44,11 @@
 class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler):
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  defines: T.Optional[T.Dict[str, str]] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         ObjCPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                                info, exe_wrapper, linker=linker, full_version=full_version)
+                                info, linker=linker, full_version=full_version)
         GnuCompiler.__init__(self, defines)
         default_warn_args = ['-Wall', '-Winvalid-pch']
         self.warn_args = {'0': [],
@@ -67,12 +64,11 @@
 
     def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  defines: T.Optional[T.Dict[str, str]] = None,
                  linker: T.Optional['DynamicLinker'] = None,
                  full_version: T.Optional[str] = None):
         ObjCPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
-                                info, exe_wrapper, linker=linker, full_version=full_version)
+                                info, linker=linker, full_version=full_version)
         ClangCompiler.__init__(self, defines)
         default_warn_args = ['-Wall', '-Winvalid-pch']
         self.warn_args = {'0': [],
diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py
index 89b913a..f557e89 100644
--- a/mesonbuild/compilers/rust.py
+++ b/mesonbuild/compilers/rust.py
@@ -19,7 +19,6 @@
     from ..environment import Environment  # noqa: F401
     from ..linkers.linkers import DynamicLinker
     from ..mesonlib import MachineChoice
-    from ..programs import ExternalProgram
     from ..dependencies import Dependency
 
 
@@ -59,13 +58,11 @@
 
     def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
                  is_cross: bool, info: 'MachineInfo',
-                 exe_wrapper: T.Optional['ExternalProgram'] = None,
                  full_version: T.Optional[str] = None,
                  linker: T.Optional['DynamicLinker'] = None):
         super().__init__([], exelist, version, for_machine, info,
                          is_cross=is_cross, full_version=full_version,
                          linker=linker)
-        self.exe_wrapper = exe_wrapper
         self.base_options.update({OptionKey(o) for o in ['b_colorout', 'b_ndebug']})
         if 'link' in self.linker.id:
             self.base_options.add(OptionKey('b_vscrt'))
@@ -87,11 +84,11 @@
         pc, stdo, stde = Popen_safe_logged(cmdlist, cwd=work_dir)
         if pc.returncode != 0:
             raise EnvironmentException(f'Rust compiler {self.name_string()} cannot compile programs.')
-        if self.is_cross:
-            if self.exe_wrapper is None:
+        if environment.need_exe_wrapper(self.for_machine):
+            if not environment.has_exe_wrapper():
                 # Can't check if the binaries run so we have to assume they do
                 return
-            cmdlist = self.exe_wrapper.get_command() + [output_name]
+            cmdlist = environment.exe_wrapper.get_command() + [output_name]
         else:
             cmdlist = [output_name]
         pe = subprocess.Popen(cmdlist, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index af69f64..86bbbb1 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -957,3 +957,6 @@
         if not self.need_exe_wrapper():
             return None
         return self.exe_wrapper
+
+    def has_exe_wrapper(self) -> bool:
+        return self.exe_wrapper and self.exe_wrapper.found()