diff options
-rw-r--r-- | docs/markdown/Rust.md | 2 | ||||
-rw-r--r-- | mesonbuild/compilers/compilers.py | 17 | ||||
-rw-r--r-- | mesonbuild/compilers/cpp.py | 12 | ||||
-rw-r--r-- | mesonbuild/compilers/cs.py | 5 | ||||
-rw-r--r-- | mesonbuild/compilers/cuda.py | 23 | ||||
-rw-r--r-- | mesonbuild/compilers/d.py | 10 | ||||
-rw-r--r-- | mesonbuild/compilers/java.py | 5 | ||||
-rw-r--r-- | mesonbuild/compilers/mixins/clike.py | 16 | ||||
-rw-r--r-- | mesonbuild/compilers/rust.py | 14 | ||||
-rw-r--r-- | mesonbuild/compilers/swift.py | 10 | ||||
-rw-r--r-- | mesonbuild/interpreter/interpreter.py | 27 | ||||
-rw-r--r-- | mesonbuild/options.py | 32 | ||||
-rw-r--r-- | unittests/allplatformstests.py | 3 |
13 files changed, 80 insertions, 96 deletions
diff --git a/docs/markdown/Rust.md b/docs/markdown/Rust.md index d30fe68..08580cd 100644 --- a/docs/markdown/Rust.md +++ b/docs/markdown/Rust.md @@ -83,7 +83,7 @@ Meson will generate a `rust-project.json` file in the root of the build directory if there are any rust targets in the project. Most IDEs will need to be configured to use the file as it's not in the source root (Meson does not write files into the source directory). [See the upstream -docs](https://rust-analyzer.github.io/manual.html#non-cargo-based-projects) for +docs](https://rust-analyzer.github.io/book/non_cargo_based_projects.html) for more information on how to configure that. ## Linking with standard libraries diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 3c1d58b..ad252a1 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1200,6 +1200,23 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta): is good enough here. """ + def run_sanity_check(self, environment: Environment, cmdlist: T.List[str], work_dir: str, use_exe_wrapper_for_cross: bool = True) -> T.Tuple[str, str]: + # Run sanity check + if self.is_cross and use_exe_wrapper_for_cross: + if not environment.has_exe_wrapper(): + # Can't check if the binaries run so we have to assume they do + return ('', '') + cmdlist = environment.exe_wrapper.get_command() + cmdlist + mlog.debug('Running test binary command: ', mesonlib.join_args(cmdlist)) + try: + pe, stdo, stde = Popen_safe_logged(cmdlist, 'Sanity check', cwd=work_dir) + except Exception as e: + raise mesonlib.EnvironmentException(f'Could not invoke sanity check executable: {e!s}.') + + if pe.returncode != 0: + raise mesonlib.EnvironmentException(f'Executables created by {self.language} compiler {self.name_string()} are not runnable.') + return stdo, stde + def split_shlib_to_parts(self, fname: str) -> T.Tuple[T.Optional[str], str]: return None, fname diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 01b9bb9..f7dc150 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -311,6 +311,9 @@ class ClangCPPCompiler(_StdCPPLibMixin, ClangCPPStds, ClangCompiler, CPPCompiler return libs return [] + def is_libcpp_enable_assertions_deprecated(self) -> bool: + return version_compare(self.version, ">=18") + def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]: if disable: return ['-DNDEBUG'] @@ -323,7 +326,7 @@ class ClangCPPCompiler(_StdCPPLibMixin, ClangCPPStds, ClangCompiler, CPPCompiler if self.language_stdlib_provider(env) == 'stdc++': return ['-D_GLIBCXX_ASSERTIONS=1'] else: - if version_compare(self.version, '>=18'): + if self.is_libcpp_enable_assertions_deprecated(): return ['-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST'] elif version_compare(self.version, '>=15'): return ['-D_LIBCPP_ENABLE_ASSERTIONS=1'] @@ -343,7 +346,12 @@ class ArmLtdClangCPPCompiler(ClangCPPCompiler): class AppleClangCPPCompiler(AppleCompilerMixin, AppleCPPStdsMixin, ClangCPPCompiler): - pass + def is_libcpp_enable_assertions_deprecated(self) -> bool: + # Upstream libc++ deprecated _LIBCPP_ENABLE_ASSERTIONS + # in favor of _LIBCPP_HARDENING_MODE from version 18 onwards, + # but Apple Clang 17's libc++ has back-ported that change. + # See: https://github.com/mesonbuild/meson/issues/14440 + return version_compare(self.version, ">=17") class EmscriptenCPPCompiler(EmscriptenMixin, ClangCPPCompiler): diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py index 38bb338..4bbddeb 100644 --- a/mesonbuild/compilers/cs.py +++ b/mesonbuild/compilers/cs.py @@ -102,10 +102,7 @@ class CsCompiler(BasicLinkerIsCompilerMixin, Compiler): cmdlist = [self.runner, obj] else: cmdlist = [os.path.join(work_dir, obj)] - pe = subprocess.Popen(cmdlist, cwd=work_dir) - pe.wait() - if pe.returncode != 0: - raise EnvironmentException('Executables created by Mono compiler %s are not runnable.' % self.name_string()) + self.run_sanity_check(environment, cmdlist, work_dir, use_exe_wrapper_for_cross=False) def needs_static_linker(self) -> bool: return False diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 6cc6f96..ab00cf1 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -577,21 +577,12 @@ class CudaCompiler(Compiler): # Run sanity check (if possible) if self.is_cross: - if not env.has_exe_wrapper(): - return - else: - cmdlist = env.exe_wrapper.get_command() + [binary_name] - else: - cmdlist = self.exelist + ['--run', '"' + binary_name + '"'] - mlog.debug('Sanity check run command line: ', ' '.join(cmdlist)) - pe, stdo, stde = Popen_safe(cmdlist, cwd=work_dir) - mlog.debug('Sanity check run stdout: ') - mlog.debug(stdo) - mlog.debug('-----\nSanity check run stderr:') - mlog.debug(stde) - mlog.debug('-----') - pe.wait() - if pe.returncode != 0: + return + + cmdlist = self.exelist + ['--run', f'"{binary_name}"'] + try: + stdo, stde = self.run_sanity_check(env, cmdlist, work_dir) + except EnvironmentException: raise EnvironmentException(f'Executables created by {self.language} compiler {self.name_string()} are not runnable.') # Interpret the result of the sanity test. @@ -599,8 +590,6 @@ class CudaCompiler(Compiler): # architecture detection test. if stde == '': self.detected_cc = stdo - else: - mlog.debug('cudaGetDeviceCount() returned ' + stde) def has_header_symbol(self, hname: str, symbol: str, prefix: str, env: 'Environment', *, diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 8ee6ebf..51f2436 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -456,15 +456,7 @@ class DCompiler(Compiler): if pc.returncode != 0: raise EnvironmentException('D compiler %s cannot compile programs.' % self.name_string()) - if self.is_cross: - if not environment.has_exe_wrapper(): - # Can't check if the binaries run so we have to assume they do - return - cmdlist = environment.exe_wrapper.get_command() + [output_name] - else: - cmdlist = [output_name] - if subprocess.call(cmdlist) != 0: - raise EnvironmentException('Executables created by D compiler %s are not runnable.' % self.name_string()) + stdo, stde = self.run_sanity_check(environment, [output_name], work_dir) def needs_static_linker(self) -> bool: return True diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py index 540e2aa..47d2ac9 100644 --- a/mesonbuild/compilers/java.py +++ b/mesonbuild/compilers/java.py @@ -91,10 +91,7 @@ class JavaCompiler(BasicLinkerIsCompilerMixin, Compiler): runner = shutil.which(self.javarunner) if runner: cmdlist = [runner, '-cp', '.', obj] - pe = subprocess.Popen(cmdlist, cwd=work_dir) - pe.wait() - if pe.returncode != 0: - raise EnvironmentException(f'Executables created by Java compiler {self.name_string()} are not runnable.') + self.run_sanity_check(environment, cmdlist, work_dir, use_exe_wrapper_for_cross=False) else: m = "Java Virtual Machine wasn't found, but it's needed by Meson. " \ "Please install a JRE.\nIf you have specific needs where this " \ diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index a42ee96..e45c485 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -307,21 +307,7 @@ class CLikeCompiler(Compiler): mlog.debug('-----') if pc.returncode != 0: raise mesonlib.EnvironmentException(f'Compiler {self.name_string()} cannot compile programs.') - # Run sanity check - if self.is_cross: - if not environment.has_exe_wrapper(): - # Can't check if the binaries run so we have to assume they do - return - cmdlist = environment.exe_wrapper.get_command() + [binary_name] - else: - cmdlist = [binary_name] - mlog.debug('Running test binary command: ', mesonlib.join_args(cmdlist)) - try: - pe, _, _ = Popen_safe_logged(cmdlist, 'Sanity check', cwd=work_dir) - except Exception as e: - raise mesonlib.EnvironmentException(f'Could not invoke sanity test executable: {e!s}.') - if pe.returncode != 0: - raise mesonlib.EnvironmentException(f'Executables created by {self.language} compiler {self.name_string()} are not runnable.') + self.run_sanity_check(environment, [binary_name], work_dir) def sanity_check(self, work_dir: str, environment: 'Environment') -> None: code = 'int main(void) { int class=0; return class; }\n' diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index d0d2e69..cc9dc21 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -5,7 +5,7 @@ from __future__ import annotations import functools -import subprocess, os.path +import os.path import textwrap import re import typing as T @@ -141,17 +141,7 @@ class RustCompiler(Compiler): if pc.returncode != 0: raise EnvironmentException(f'Rust compiler {self.name_string()} cannot compile programs.') self._native_static_libs(work_dir, source_name) - if self.is_cross: - if not environment.has_exe_wrapper(): - # Can't check if the binaries run so we have to assume they do - return - cmdlist = environment.exe_wrapper.get_command() + [output_name] - else: - cmdlist = [output_name] - pe = subprocess.Popen(cmdlist, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - pe.wait() - if pe.returncode != 0: - raise EnvironmentException(f'Executables created by Rust compiler {self.name_string()} are not runnable.') + self.run_sanity_check(environment, [output_name], work_dir) def _native_static_libs(self, work_dir: str, source_name: str) -> None: # Get libraries needed to link with a Rust staticlib diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index 8410fbb..528d76f 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -8,7 +8,7 @@ import subprocess, os.path import typing as T from .. import mlog, options -from ..mesonlib import EnvironmentException, MesonException, version_compare +from ..mesonlib import MesonException, version_compare from .compilers import Compiler, clike_debug_args @@ -170,13 +170,7 @@ class SwiftCompiler(Compiler): ''') pc = subprocess.Popen(self.exelist + extra_flags + ['-emit-executable', '-o', output_name, src], cwd=work_dir) pc.wait() - if pc.returncode != 0: - raise EnvironmentException('Swift compiler %s cannot compile programs.' % self.name_string()) - if self.is_cross: - # Can't check if the binaries run so we have to assume they do - return - if subprocess.call(output_name) != 0: - raise EnvironmentException('Executables created by Swift compiler %s are not runnable.' % self.name_string()) + self.run_sanity_check(environment, [output_name], work_dir) def get_debug_args(self, is_debug: bool) -> T.List[str]: return clike_debug_args[is_debug] diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index bf41bfb..abdc889 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1201,6 +1201,20 @@ class Interpreter(InterpreterBase, HoldableObject): self.coredata.initialized_subprojects.add(self.subproject) if not self.is_subproject(): + # We have to activate VS before adding languages and before calling + # self.set_backend() otherwise it wouldn't be able to detect which + # vs backend version we need. But after setting default_options in case + # the project sets vs backend by default. + backend = self.coredata.optstore.get_value_for(OptionKey('backend')) + assert backend is None or isinstance(backend, str), 'for mypy' + vsenv = self.coredata.optstore.get_value_for(OptionKey('vsenv')) + assert isinstance(vsenv, bool), 'for mypy' + force_vsenv = vsenv or backend.startswith('vs') + mesonlib.setup_vsenv(force_vsenv) + self.set_backend() + + if not self.is_subproject(): + self.coredata.optstore.validate_cmd_line_options(self.user_defined_options.cmd_line_options) self.build.project_name = proj_name self.active_projectname = proj_name @@ -1270,22 +1284,9 @@ class Interpreter(InterpreterBase, HoldableObject): mlog.log('Project name:', mlog.bold(proj_name)) mlog.log('Project version:', mlog.bold(self.project_version)) - if not self.is_subproject(): - # We have to activate VS before adding languages and before calling - # self.set_backend() otherwise it wouldn't be able to detect which - # vs backend version we need. But after setting default_options in case - # the project sets vs backend by default. - backend = self.coredata.optstore.get_value_for(OptionKey('backend')) - assert backend is None or isinstance(backend, str), 'for mypy' - vsenv = self.coredata.optstore.get_value_for(OptionKey('vsenv')) - assert isinstance(vsenv, bool), 'for mypy' - force_vsenv = vsenv or backend.startswith('vs') - mesonlib.setup_vsenv(force_vsenv) - self.add_languages(proj_langs, True, MachineChoice.HOST) self.add_languages(proj_langs, False, MachineChoice.BUILD) - self.set_backend() if not self.is_subproject(): self.check_stdlibs() diff --git a/mesonbuild/options.py b/mesonbuild/options.py index 62413b1..3b7d8b2 100644 --- a/mesonbuild/options.py +++ b/mesonbuild/options.py @@ -1243,7 +1243,7 @@ class OptionStore: def first_handle_prefix(self, project_default_options: T.Union[T.List[str], OptionStringLikeDict], - cmd_line_options: T.Union[T.List[str], OptionStringLikeDict], + cmd_line_options: OptionStringLikeDict, machine_file_options: T.Mapping[OptionKey, ElementaryOptionValues]) \ -> T.Tuple[T.Union[T.List[str], OptionStringLikeDict], T.Union[T.List[str], OptionStringLikeDict], @@ -1282,7 +1282,7 @@ class OptionStore: def initialize_from_top_level_project_call(self, project_default_options_in: T.Union[T.List[str], OptionStringLikeDict], - cmd_line_options_in: T.Union[T.List[str], OptionStringLikeDict], + cmd_line_options_in: OptionStringLikeDict, machine_file_options_in: T.Mapping[OptionKey, ElementaryOptionValues]) -> None: first_invocation = True (project_default_options, cmd_line_options, machine_file_options) = self.first_handle_prefix(project_default_options_in, @@ -1370,16 +1370,28 @@ class OptionStore: if proj_key in self.options: self.set_option(proj_key, valstr, True) else: - # Fail on unknown options that we can know must - # exist at this point in time. Subproject and compiler - # options are resolved later. - # - # Some base options (sanitizers etc) might get added later. - # Permitting them all is not strictly correct. - if key.subproject is None and not self.is_compiler_option(key) and not self.is_base_option(key): - raise MesonException(f'Unknown options: "{keystr}"') self.pending_options[key] = valstr + def validate_cmd_line_options(self, cmd_line_options: OptionStringLikeDict) -> None: + unknown_options = [] + for keystr, valstr in cmd_line_options.items(): + if isinstance(keystr, str): + key = OptionKey.from_string(keystr) + else: + key = keystr + # Fail on unknown options that we can know must exist at this point in time. + # Subproject and compiler options are resolved later. + # + # Some base options (sanitizers etc) might get added later. + # Permitting them all is not strictly correct. + if key.subproject is None and not self.is_compiler_option(key) and not self.is_base_option(key) and \ + key in self.pending_options: + unknown_options.append(f'"{key}"') + + if unknown_options: + keys = ', '.join(unknown_options) + raise MesonException(f'Unknown options: {keys}') + def hacky_mchackface_back_to_list(self, optdict: T.Dict[str, str]) -> T.List[str]: if isinstance(optdict, dict): return [f'{k}={v}' for k, v in optdict.items()] diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index ea220a0..0618b20 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -529,7 +529,8 @@ class AllPlatformTests(BasePlatformTests): if self.backend is not Backend.ninja: raise SkipTest(f'{self.backend.name!r} backend can\'t install files') testdir = os.path.join(self.common_test_dir, '8 install') - self.init(testdir) + # sneak in a test that covers backend options... + self.init(testdir, extra_args=['-Dbackend_max_links=4']) intro = self.introspect('--targets') if intro[0]['type'] == 'executable': intro = intro[::-1] |