From 416a00308f5b0f228af3c93eb597eca8529fdbb0 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sun, 8 Jul 2018 20:57:09 +0530 Subject: cross: Use ExternalProgram for cross-file exe_wrapper We already have code to fetch and find binaries specified in a cross file, so use the same code for exe_wrapper. This allows us to handle the same corner-cases that were fixed for other cross binaries. --- mesonbuild/backend/backends.py | 13 ++++++++----- mesonbuild/backend/ninjabackend.py | 9 ++++----- mesonbuild/compilers/c.py | 9 ++++----- mesonbuild/dependencies/__init__.py | 2 +- mesonbuild/dependencies/base.py | 26 +++++++++++++++++++++++++- mesonbuild/environment.py | 19 +++++++++++++------ mesonbuild/interpreter.py | 4 +--- mesonbuild/mesonlib.py | 6 ++++++ mesonbuild/mtest.py | 10 ++-------- mesonbuild/scripts/meson_exe.py | 6 ++++-- 10 files changed, 68 insertions(+), 36 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 354d25a..b13aa10 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -72,6 +72,8 @@ class ExecutableSerialisation: self.cmd_args = cmd_args self.env = env self.is_cross = is_cross + if exe_wrapper is not None: + assert(isinstance(exe_wrapper, dependencies.ExternalProgram)) self.exe_runner = exe_wrapper self.workdir = workdir self.extra_paths = extra_paths @@ -85,6 +87,8 @@ class TestSerialisation: self.suite = suite self.fname = fname self.is_cross_built = is_cross_built + if exe_wrapper is not None: + assert(isinstance(exe_wrapper, dependencies.ExternalProgram)) self.exe_runner = exe_wrapper self.is_parallel = is_parallel self.cmd_args = cmd_args @@ -310,7 +314,7 @@ class Backend: self.environment.cross_info.need_cross_compiler() and \ self.environment.cross_info.need_exe_wrapper() if is_cross_built: - exe_wrapper = self.environment.cross_info.config['binaries'].get('exe_wrapper', None) + exe_wrapper = self.environment.get_exe_wrapper() else: exe_wrapper = None es = ExecutableSerialisation(basename, exe_cmd, cmd_args, env, @@ -646,10 +650,10 @@ class Backend: is_cross = is_cross and exe.is_cross if isinstance(exe, dependencies.ExternalProgram): # E.g. an external verifier or simulator program run on a generated executable. - # Can always be run. + # Can always be run without a wrapper. is_cross = False if is_cross: - exe_wrapper = self.environment.cross_info.config['binaries'].get('exe_wrapper', None) + exe_wrapper = self.environment.get_exe_wrapper() else: exe_wrapper = None if mesonlib.for_windows(is_cross, self.environment) or \ @@ -711,9 +715,8 @@ class Backend: def exe_object_to_cmd_array(self, exe): if self.environment.is_cross_build() and \ - self.environment.cross_info.need_exe_wrapper() and \ isinstance(exe, build.BuildTarget) and exe.is_cross: - if 'exe_wrapper' not in self.environment.cross_info.config['binaries']: + if self.environment.exe_wrapper is None: s = 'Can not use target %s as a generator because it is cross-built\n' s += 'and no exe wrapper is defined. You might want to set it to native instead.' s = s % exe.name diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 3ee543d..f62bc67 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -595,11 +595,10 @@ int dummy; if isinstance(texe, build.Executable): abs_exe = os.path.join(self.environment.get_build_dir(), self.get_target_filename(texe)) deps.append(self.get_target_filename(texe)) - if self.environment.is_cross_build() and \ - self.environment.cross_info.need_exe_wrapper(): - exe_wrap = self.environment.cross_info.config['binaries'].get('exe_wrapper', None) - if exe_wrap is not None: - cmd += [exe_wrap] + if self.environment.is_cross_build(): + exe_wrap = self.environment.get_exe_wrapper() + if exe_wrap: + cmd += exe_wrap.get_command() cmd.append(abs_exe) elif isinstance(texe, dependencies.ExternalProgram): cmd += texe.get_command() diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 8af7abc..fa6fd89 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -57,10 +57,9 @@ class CCompiler(Compiler): self.id = 'unknown' self.is_cross = is_cross self.can_compile_suffixes.add('h') - if isinstance(exe_wrapper, str): - self.exe_wrapper = [exe_wrapper] - else: - self.exe_wrapper = exe_wrapper + self.exe_wrapper = exe_wrapper + if self.exe_wrapper: + self.exe_wrapper = self.exe_wrapper.get_command() # Set to None until we actually need to check this self.has_fatal_warnings_link_arg = None @@ -278,7 +277,7 @@ class CCompiler(Compiler): if self.exe_wrapper is None: # Can't check if the binaries run so we have to assume they do return - cmdlist = self.exe_wrapper + [binary_name] + cmdlist = self.exe_wrapper.get_command() + [binary_name] else: cmdlist = [binary_name] mlog.debug('Running test binary command: ' + ' '.join(cmdlist)) diff --git a/mesonbuild/dependencies/__init__.py b/mesonbuild/dependencies/__init__.py index 5259c5b..00b6fa2 100644 --- a/mesonbuild/dependencies/__init__.py +++ b/mesonbuild/dependencies/__init__.py @@ -14,7 +14,7 @@ from .boost import BoostDependency from .base import ( # noqa: F401 - Dependency, DependencyException, DependencyMethods, ExternalProgram, NonExistingExternalProgram, + Dependency, DependencyException, DependencyMethods, ExternalProgram, EmptyExternalProgram, NonExistingExternalProgram, ExternalDependency, NotFoundDependency, ExternalLibrary, ExtraFrameworkDependency, InternalDependency, PkgConfigDependency, find_external_dependency, get_dep_identifier, packages, _packages_accept_language) from .dev import GMockDependency, GTestDependency, LLVMDependency, ValgrindDependency diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 63b817a..aca5d25 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -981,10 +981,14 @@ class ExternalProgram: def get_name(self): return self.name + class NonExistingExternalProgram(ExternalProgram): + "A program that will never exist" def __init__(self): - super().__init__(name = 'nonexistingprogram', silent = True) + self.name = 'nonexistingprogram' + self.command = [None] + self.path = None def __repr__(self): r = '<{} {!r} -> {!r}>' @@ -993,6 +997,26 @@ class NonExistingExternalProgram(ExternalProgram): def found(self): return False + +class EmptyExternalProgram(ExternalProgram): + ''' + A program object that returns an empty list of commands. Used for cases + such as a cross file exe_wrapper to represent that it's not required. + ''' + + def __init__(self): + self.name = None + self.command = [] + self.path = None + + def __repr__(self): + r = '<{} {!r} -> {!r}>' + return r.format(self.__class__.__name__, self.name, self.command) + + def found(self): + return True + + class ExternalLibrary(ExternalDependency): def __init__(self, name, link_args, environment, language, silent=False): super().__init__('external', environment, language, {}) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 0aa0b32..d281712 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -309,10 +309,13 @@ class Environment: # Used by the regenchecker script, which runs meson self.coredata.meson_command = mesonlib.meson_command self.first_invocation = True + self.cross_info = None + self.exe_wrapper = None if self.coredata.cross_file: self.cross_info = CrossBuildInfo(self.coredata.cross_file) - else: - self.cross_info = None + if 'exe_wrapper' in self.cross_info.config['binaries']: + from .dependencies import ExternalProgram + self.exe_wrapper = ExternalProgram.from_cross_info(self.cross_info, 'exe_wrapper') self.cmd_line_options = options.cmd_line_options.copy() # List of potential compilers. @@ -476,10 +479,7 @@ This is probably wrong, it should always point to the native compiler.''' % evar # Return value has to be a list of compiler 'choices' compilers = [compilers] is_cross = True - if self.cross_info.need_exe_wrapper(): - exe_wrap = self.cross_info.config['binaries'].get('exe_wrapper', None) - else: - exe_wrap = [] + exe_wrap = self.get_exe_wrapper() elif evar in os.environ: compilers = shlex.split(os.environ[evar]) # Ensure ccache exists and remove it if it doesn't @@ -969,6 +969,13 @@ This is probably wrong, it should always point to the native compiler.''' % evar out = out.split('\n')[index].lstrip('libraries: =').split(':') return [os.path.normpath(p) for p in out] + def get_exe_wrapper(self): + if not self.cross_info.need_exe_wrapper(): + from .dependencies import EmptyExternalProgram + return EmptyExternalProgram() + return self.exe_wrapper + + class CrossBuildInfo: def __init__(self, filename): self.config = {'properties': {}} diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 833e982..4368ffb 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1668,10 +1668,8 @@ class MesonMain(InterpreterObject): @permittedKwargs({}) def has_exe_wrapper_method(self, args, kwargs): if self.is_cross_build_method(None, None) and \ - 'binaries' in self.build.environment.cross_info.config and \ self.build.environment.cross_info.need_exe_wrapper(): - exe_wrap = self.build.environment.cross_info.config['binaries'].get('exe_wrapper', None) - if exe_wrap is None: + if self.build.environment.exe_wrapper is None: return False # We return True when exe_wrap is defined, when it's not needed, and # when we're compiling natively. The last two are semantically confusing. diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index efb8d11..4a60452 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -1057,6 +1057,12 @@ def detect_subprojects(spdir_name, current_dir='', result=None): def get_error_location_string(fname, lineno): return '{}:{}:'.format(fname, lineno) +def substring_is_in_list(substr, strlist): + for s in strlist: + if substr in s: + return True + return False + class OrderedSet(collections.MutableSet): """A set that preserves the order in which items are added, by first insertion. diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 855154f..3c4073b 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -241,6 +241,7 @@ class SingleTestRunner: return None else: return [self.test.exe_runner] + self.test.fname + return self.test.exe_runner.get_command() + self.test.fname else: return self.test.fname @@ -257,19 +258,12 @@ class SingleTestRunner: self.test.timeout = None return self._run_cmd(wrap + cmd + self.test.cmd_args + self.options.test_args) - @staticmethod - def _substring_in_list(substr, strlist): - for s in strlist: - if substr in s: - return True - return False - def _run_cmd(self, cmd): starttime = time.time() if len(self.test.extra_paths) > 0: self.env['PATH'] = os.pathsep.join(self.test.extra_paths + ['']) + self.env['PATH'] - if self._substring_in_list('wine', cmd): + if mesonlib.substring_is_in_list('wine', cmd): wine_paths = ['Z:' + p for p in self.test.extra_paths] wine_path = ';'.join(wine_paths) # Don't accidentally end with an `;` because that will add the diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py index a70acca..ee5906b 100644 --- a/mesonbuild/scripts/meson_exe.py +++ b/mesonbuild/scripts/meson_exe.py @@ -19,6 +19,8 @@ import pickle import platform import subprocess +from .. import mesonlib + options = None def buildparser(): @@ -49,7 +51,7 @@ def run_exe(exe): if exe.exe_runner is None: raise AssertionError('BUG: Trying to run cross-compiled exes with no wrapper') else: - cmd = [exe.exe_runner] + exe.fname + cmd = exe.exe_runner.get_command() + exe.fname else: cmd = exe.fname child_env = os.environ.copy() @@ -57,7 +59,7 @@ def run_exe(exe): if len(exe.extra_paths) > 0: child_env['PATH'] = (os.pathsep.join(exe.extra_paths + ['']) + child_env['PATH']) - if exe.exe_runner and 'wine' in exe.exe_runner: + if exe.exe_runner and mesonlib.substring_is_in_list('wine', exe.exe_runner.get_command()): wine_paths = ['Z:' + p for p in exe.extra_paths] wine_path = ';'.join(wine_paths) # Don't accidentally end with an `;` because that will add the -- cgit v1.1