diff options
| author | Dylan Baker <dylan@pnwbakers.com> | 2025-10-06 10:21:07 -0700 |
|---|---|---|
| committer | Dylan Baker <dylan@pnwbakers.com> | 2025-10-06 10:22:41 -0700 |
| commit | a6af0ad50373324ec9fe569e8315b03439491865 (patch) | |
| tree | ed221189af614ce578190dfb51193e1efcb72b17 /mesonbuild | |
| parent | 6c5a88632f1d3319124d03eab596d57416ef16c6 (diff) | |
| download | meson-a6af0ad50373324ec9fe569e8315b03439491865.tar.gz | |
Revert "compilers: refactor sanity checking code"
This reverts commit 806289a5d27958a084bc6cba41b7cf9ccee4ecf4.
Diffstat (limited to 'mesonbuild')
| -rw-r--r-- | mesonbuild/compilers/asm.py | 7 | ||||
| -rw-r--r-- | mesonbuild/compilers/c.py | 5 | ||||
| -rw-r--r-- | mesonbuild/compilers/compilers.py | 100 | ||||
| -rw-r--r-- | mesonbuild/compilers/cpp.py | 5 | ||||
| -rw-r--r-- | mesonbuild/compilers/cs.py | 34 | ||||
| -rw-r--r-- | mesonbuild/compilers/cuda.py | 114 | ||||
| -rw-r--r-- | mesonbuild/compilers/cython.py | 23 | ||||
| -rw-r--r-- | mesonbuild/compilers/d.py | 22 | ||||
| -rw-r--r-- | mesonbuild/compilers/fortran.py | 11 | ||||
| -rw-r--r-- | mesonbuild/compilers/java.py | 40 | ||||
| -rw-r--r-- | mesonbuild/compilers/mixins/clike.py | 51 | ||||
| -rw-r--r-- | mesonbuild/compilers/objc.py | 5 | ||||
| -rw-r--r-- | mesonbuild/compilers/objcpp.py | 5 | ||||
| -rw-r--r-- | mesonbuild/compilers/rust.py | 63 | ||||
| -rw-r--r-- | mesonbuild/compilers/swift.py | 27 | ||||
| -rw-r--r-- | mesonbuild/compilers/vala.py | 29 | ||||
| -rw-r--r-- | mesonbuild/dependencies/base.py | 7 |
17 files changed, 264 insertions, 284 deletions
diff --git a/mesonbuild/compilers/asm.py b/mesonbuild/compilers/asm.py index 5f065e193..d4af77f89 100644 --- a/mesonbuild/compilers/asm.py +++ b/mesonbuild/compilers/asm.py @@ -10,7 +10,6 @@ from .mixins.metrowerks import MetrowerksCompiler, mwasmarm_instruction_set_args from .mixins.ti import TICompiler if T.TYPE_CHECKING: - from ..environment import Environment from ..linkers.linkers import DynamicLinker from ..mesonlib import MachineChoice from ..envconfig import MachineInfo @@ -40,12 +39,6 @@ class ASMCompiler(Compiler): raise EnvironmentException(f'ASM Compiler {self.id} does not support building for {info.cpu_family} CPU family.') super().__init__(ccache, exelist, version, for_machine, info, linker, full_version, is_cross) - def _sanity_check_compile_args(self, env: Environment, sourcename: str, binname: str) -> T.List[str]: - return [] - - def _sanity_check_source_code(self) -> str: - return '' - class NasmCompiler(ASMCompiler): language = 'nasm' diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index a0786d55c..424b61251 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -76,8 +76,9 @@ class CCompiler(CLikeCompiler, Compiler): def get_no_stdinc_args(self) -> T.List[str]: return ['-nostdinc'] - def _sanity_check_source_code(self) -> str: - return 'int main(void) { int class=0; return class; }\n' + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: + code = 'int main(void) { int class=0; return class; }\n' + return self._sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code) def has_header_symbol(self, hname: str, symbol: str, prefix: str, env: 'Environment', *, diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 4c28eb2fd..624226d33 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1196,7 +1196,8 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta): def name_string(self) -> str: return ' '.join(self.exelist) - def sanity_check(self, work_dir: str, env: Environment) -> None: + @abc.abstractmethod + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: """Check that this compiler actually works. This should provide a simple compile/link test. Something as simple as: @@ -1204,103 +1205,24 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta): main(): return 0 ``` is good enough here. - - :param work_dir: A directory to put temporary artifacts - :param env: The :class:`environment.Environment` instance to use with - this check - :raises mesonlib.EnvironmentException: If building the binary fails - :raises mesonlib.EnvironmentException: If running the binary is attempted and fails """ - sourcename, binname = self._sanity_check_filenames() - cmdlist = self._sanity_check_compile_args(env, sourcename, binname) - - with open(os.path.join(work_dir, sourcename), 'w', encoding='utf-8') as f: - f.write(self._sanity_check_source_code()) - - pc, stdo, stde = mesonlib.Popen_safe(cmdlist, cwd=work_dir) - mlog.debug('Sanity check compiler command line:', mesonlib.join_args(cmdlist)) - mlog.debug('Sanity check compile stdout:') - mlog.debug(stdo) - mlog.debug('-----\nSanity check compile stderr:') - mlog.debug(stde) - mlog.debug('-----') - if pc.returncode != 0: - raise mesonlib.EnvironmentException(f'Compiler {self.name_string()} cannot compile programs.') - - self._run_sanity_check(env, [os.path.join(work_dir, binname)], work_dir) - - def _sanity_check_filenames(self) -> T.Tuple[str, str]: - """Generate the name of the source and binary file for the sanity check. - - The returned names should be just the names of the files with - extensions, but no paths. - - :return: A tuple of (sourcename, binaryname) - """ - default_ext = lang_suffixes[self.language][0] - template = f'sanity_check_for_{self.language}' - sourcename = f'{template}.{default_ext}' - binaryname = f'{template}{"_cross" if self.is_cross else ""}.exe' - return sourcename, binaryname - - @abc.abstractmethod - def _sanity_check_compile_args(self, env: Environment, sourcename: str, binname: str) -> T.List[str]: - """Get arguments to run compiler for sanity check. - :param env: The :class:`environment.Environment` instance to use - :param sourcename: the name of the source file to generate - :param binname: the name of the binary file to generate - :return: a list of strings to pass to :func:`subprocess.run` or equivalent - """ - - @abc.abstractmethod - def _sanity_check_source_code(self) -> str: - """Get the source code to run for a sanity check - - :return: A string to be written into a file and ran. - """ - - def _sanity_check_run_with_exe_wrapper(self, env: Environment, command: T.List[str]) -> T.List[str]: - """Wrap the binary to run in the test with the exe_wrapper if necessary - - Languages that do no want to use an exe_wrapper (or always want to use - some kind of wrapper) should override this method - - :param env: the :class:`environment.Environment` instance to use - :param command: The string list of commands to run - :return: The list of commands wrapped by the exe_wrapper if it is needed, otherwise the original commands - """ - if self.is_cross and env.has_exe_wrapper(): - assert env.exe_wrapper is not None, 'for mypy' - return env.exe_wrapper.get_command() + command - return command - - def _run_sanity_check(self, env: Environment, cmdlist: T.List[str], work_dir: str) -> None: - """Run a sanity test binary - - :param env: the :class:`environment.Environment` instance to use - :param cmdlist: A list of strings to pass to :func:`subprocess.run` or equivalent to run the test - :param work_dir: A directory to place temporary artifacts - :raises mesonlib.EnvironmentException: If the binary cannot be run or if it returns a non-zero exit code - """ - # Can't check binaries, so we have to assume they work - if self.is_cross and not env.has_exe_wrapper(): - mlog.debug('Cannot run cross check') - return - - cmdlist = self._sanity_check_run_with_exe_wrapper(env, cmdlist) - mlog.debug('Sanity check built target output for', self.for_machine, self.language, 'compiler') - mlog.debug(' -- Running test binary command: ', mesonlib.join_args(cmdlist)) + 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) - mlog.debug(' -- stdout:\n', stdo) - mlog.debug(' -- stderr:\n', stde) - mlog.debug(' -- returncode:', pe.returncode) 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 8da100483..fa032ec79 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -86,8 +86,9 @@ class CPPCompiler(CLikeCompiler, Compiler): def get_no_stdlib_link_args(self) -> T.List[str]: return ['-nostdlib++'] - def _sanity_check_source_code(self) -> str: - return 'class breakCCompiler;int main(void) { return 0; }\n' + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: + code = 'class breakCCompiler;int main(void) { return 0; }\n' + return self._sanity_check_impl(work_dir, environment, 'sanitycheckcpp.cc', code) def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]: # -fpermissive allows non-conforming code to compile which is necessary diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py index e515c338f..4bbddeb20 100644 --- a/mesonbuild/compilers/cs.py +++ b/mesonbuild/compilers/cs.py @@ -3,10 +3,11 @@ from __future__ import annotations -import os.path +import os.path, subprocess import textwrap import typing as T +from ..mesonlib import EnvironmentException from ..linkers import RSPFileSyntax from .compilers import Compiler @@ -82,21 +83,26 @@ class CsCompiler(BasicLinkerIsCompilerMixin, Compiler): def get_pch_name(self, header_name: str) -> str: return '' - def _sanity_check_source_code(self) -> str: - return textwrap.dedent(''' - public class Sanity { - static public void Main () { + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: + src = 'sanity.cs' + obj = 'sanity.exe' + source_name = os.path.join(work_dir, src) + with open(source_name, 'w', encoding='utf-8') as ofile: + ofile.write(textwrap.dedent(''' + public class Sanity { + static public void Main () { + } } - } - ''') - - def _sanity_check_compile_args(self, env: Environment, sourcename: str, binname: str) -> T.List[str]: - return self.exelist + self.get_always_args() + [sourcename] + self.get_output_args(binname) - - def _sanity_check_run_with_exe_wrapper(self, env: Environment, command: T.List[str]) -> T.List[str]: + ''')) + pc = subprocess.Popen(self.exelist + self.get_always_args() + [src], cwd=work_dir) + pc.wait() + if pc.returncode != 0: + raise EnvironmentException('C# compiler %s cannot compile programs.' % self.name_string()) if self.runner: - return [self.runner] + command - return command + cmdlist = [self.runner, obj] + else: + cmdlist = [os.path.join(work_dir, obj)] + 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 a9e6a7669..7e050f140 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -5,14 +5,15 @@ from __future__ import annotations import enum +import os.path import string import typing as T from .. import options from .. import mlog -from .. import mesonlib from ..mesonlib import ( - EnvironmentException, is_windows, LibType, version_compare + EnvironmentException, Popen_safe, + is_windows, LibType, version_compare ) from .compilers import Compiler, CompileCheckMode @@ -186,7 +187,6 @@ class CudaCompiler(Compiler): host_compiler: Compiler, info: 'MachineInfo', linker: T.Optional['DynamicLinker'] = None, full_version: T.Optional[str] = None): - self.detected_cc = '' super().__init__(ccache, exelist, version, for_machine, info, linker=linker, full_version=full_version, is_cross=is_cross) self.host_compiler = host_compiler self.base_options = host_compiler.base_options @@ -499,36 +499,55 @@ class CudaCompiler(Compiler): def thread_link_flags(self, environment: 'Environment') -> T.List[str]: return self._to_host_flags(self.host_compiler.thread_link_flags(environment), Phase.LINKER) - def _sanity_check_source_code(self) -> str: - return r''' - #include <cuda_runtime.h> - #include <stdio.h> - - __global__ void kernel (void) {} - - int main(void){ - struct cudaDeviceProp prop; - int count, i; - cudaError_t ret = cudaGetDeviceCount(&count); - if(ret != cudaSuccess){ - fprintf(stderr, "%d\n", (int)ret); - }else{ - for(i=0;i<count;i++){ - if(cudaGetDeviceProperties(&prop, i) == cudaSuccess){ - fprintf(stdout, "%d.%d\n", prop.major, prop.minor); - } + def sanity_check(self, work_dir: str, env: 'Environment') -> None: + mlog.debug('Sanity testing ' + self.get_display_language() + ' compiler:', ' '.join(self.exelist)) + mlog.debug('Is cross compiler: %s.' % str(self.is_cross)) + + sname = 'sanitycheckcuda.cu' + code = r''' + #include <cuda_runtime.h> + #include <stdio.h> + + __global__ void kernel (void) {} + + int main(void){ + struct cudaDeviceProp prop; + int count, i; + cudaError_t ret = cudaGetDeviceCount(&count); + if(ret != cudaSuccess){ + fprintf(stderr, "%d\n", (int)ret); + }else{ + for(i=0;i<count;i++){ + if(cudaGetDeviceProperties(&prop, i) == cudaSuccess){ + fprintf(stdout, "%d.%d\n", prop.major, prop.minor); } } - fflush(stderr); - fflush(stdout); - return 0; } - ''' + fflush(stderr); + fflush(stdout); + return 0; + } + ''' + binname = sname.rsplit('.', 1)[0] + binname += '_cross' if self.is_cross else '' + source_name = os.path.join(work_dir, sname) + binary_name = os.path.join(work_dir, binname + '.exe') + with open(source_name, 'w', encoding='utf-8') as ofile: + ofile.write(code) + + # The Sanity Test for CUDA language will serve as both a sanity test + # and a native-build GPU architecture detection test, useful later. + # + # For this second purpose, NVCC has very handy flags, --run and + # --run-args, that allow one to run an application with the + # environment set up properly. Of course, this only works for native + # builds; For cross builds we must still use the exe_wrapper (if any). + self.detected_cc = '' + flags = [] - def _sanity_check_compile_args(self, env: Environment, sourcename: str, binname: str) -> T.List[str]: # Disable warnings, compile with statically-linked runtime for minimum # reliance on the system. - flags = ['-w', '-cudart', 'static', sourcename] + flags += ['-w', '-cudart', 'static', source_name] # Use the -ccbin option, if available, even during sanity checking. # Otherwise, on systems where CUDA does not support the default compiler, @@ -543,30 +562,33 @@ class CudaCompiler(Compiler): # a ton of compiler flags to differentiate between # arm and x86_64. So just compile. flags += self.get_compile_only_args() - flags += self.get_output_args(binname) - - return self.exelist + flags - - def _run_sanity_check(self, env: Environment, cmdlist: T.List[str], work_dir: str) -> None: - # Can't check binaries, so we have to assume they work - if self.is_cross and not env.has_exe_wrapper(): - mlog.debug('Cannot run cross check') + flags += self.get_output_args(binary_name) + + # Compile sanity check + cmdlist = self.exelist + flags + mlog.debug('Sanity check compiler command line: ', ' '.join(cmdlist)) + pc, stdo, stde = Popen_safe(cmdlist, cwd=work_dir) + mlog.debug('Sanity check compile stdout: ') + mlog.debug(stdo) + mlog.debug('-----\nSanity check compile stderr:') + mlog.debug(stde) + mlog.debug('-----') + if pc.returncode != 0: + raise EnvironmentException(f'Compiler {self.name_string()} cannot compile programs.') + + # Run sanity check (if possible) + if self.is_cross: return - cmdlist = self._sanity_check_run_with_exe_wrapper(env, cmdlist) - mlog.debug('Sanity check built target output for', self.for_machine, self.language, 'compiler') - mlog.debug(' -- Running test binary command: ', mesonlib.join_args(cmdlist)) + cmdlist = self.exelist + ['--run', f'"{binary_name}"'] try: - pe, stdo, stde = mesonlib.Popen_safe_logged(cmdlist, 'Sanity check', cwd=work_dir) - mlog.debug(' -- stdout:\n', stdo) - mlog.debug(' -- stderr:\n', stde) - mlog.debug(' -- returncode:', pe.returncode) - except Exception as e: - raise EnvironmentException(f'Could not invoke sanity check executable: {e!s}.') - - if pe.returncode != 0: + 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. + # As mentioned above, it is not only a sanity test but also a GPU + # architecture detection test. if stde == '': self.detected_cc = stdo diff --git a/mesonbuild/compilers/cython.py b/mesonbuild/compilers/cython.py index 2814ca0d6..50bb4652b 100644 --- a/mesonbuild/compilers/cython.py +++ b/mesonbuild/compilers/cython.py @@ -1,14 +1,13 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright © 2021-2025 Intel Corporation +from __future__ import annotations """Abstraction for Cython language compilers.""" -from __future__ import annotations -import os import typing as T from .. import options -from ..mesonlib import version_compare +from ..mesonlib import EnvironmentException, version_compare from .compilers import Compiler if T.TYPE_CHECKING: @@ -50,22 +49,16 @@ class CythonCompiler(Compiler): def get_depfile_suffix(self) -> str: return 'dep' + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: + code = 'print("hello world")' + with self.cached_compile(code, environment.coredata) as p: + if p.returncode != 0: + raise EnvironmentException(f'Cython compiler {self.id!r} cannot compile programs') + def get_pic_args(self) -> T.List[str]: # We can lie here, it's fine return [] - def _sanity_check_source_code(self) -> str: - return 'print("Hello world")' - - def _sanity_check_compile_args(self, env: Environment, sourcename: str, binname: str) -> T.List[str]: - return self.exelist + self.get_always_args() + self.get_output_args(binname) + [sourcename] - - def _run_sanity_check(self, env: Environment, cmdlist: T.List[str], work_dir: str) -> None: - # Cython will do a Cython -> C -> Exe, so the output file will actually have - # the name of the C compiler. - # TODO: find a way to not make this so hacky - return super()._run_sanity_check(env, [os.path.join(work_dir, 'sanity_check_for_c.exe')], work_dir) - def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], build_dir: str) -> T.List[str]: new: T.List[str] = [] diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 4df8f6570..9f662add3 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -5,6 +5,7 @@ from __future__ import annotations import os.path import re +import subprocess import typing as T from .. import mesonlib @@ -437,11 +438,24 @@ class DCompiler(Compiler): full_version=full_version, is_cross=is_cross) self.arch = arch - def _sanity_check_source_code(self) -> str: - return 'void main() { }' + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: + source_name = os.path.join(work_dir, 'sanity.d') + output_name = os.path.join(work_dir, 'dtest') + with open(source_name, 'w', encoding='utf-8') as ofile: + ofile.write('''void main() { }''') - def _sanity_check_compile_args(self, env: Environment, sourcename: str, binname: str) -> T.List[str]: - return self.exelist + self.get_output_args(binname) + self._get_target_arch_args() + [sourcename] + compile_cmdlist = self.exelist + self.get_output_args(output_name) + self._get_target_arch_args() + [source_name] + + # If cross-compiling, we can't run the sanity check, only compile it. + if self.is_cross and not environment.has_exe_wrapper(): + compile_cmdlist += self.get_compile_only_args() + + pc = subprocess.Popen(compile_cmdlist, cwd=work_dir) + pc.wait() + if pc.returncode != 0: + raise EnvironmentException('D compiler %s cannot compile programs.' % 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/fortran.py b/mesonbuild/compilers/fortran.py index 2853ee24c..6f4f3d2c1 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -3,7 +3,6 @@ from __future__ import annotations -import textwrap import typing as T import functools import os @@ -62,12 +61,10 @@ class FortranCompiler(CLikeCompiler, Compiler): largs = env.coredata.get_external_link_args(self.for_machine, self.language) return cargs, largs - def _sanity_check_source_code(self) -> str: - return textwrap.dedent(''' - PROGRAM MAIN - PRINT *, "Fortran compilation is working." - END - ''') + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: + source_name = 'sanitycheckf.f' + code = ' PROGRAM MAIN\n PRINT *, "Fortran compilation is working."\n END\n' + return self._sanity_check_impl(work_dir, environment, source_name, code) def get_optimization_args(self, optimization_level: str) -> T.List[str]: return gnu_optimization_args[optimization_level] diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py index 13e48475c..47d2ac9cd 100644 --- a/mesonbuild/compilers/java.py +++ b/mesonbuild/compilers/java.py @@ -6,6 +6,7 @@ from __future__ import annotations import os import os.path import shutil +import subprocess import textwrap import typing as T @@ -71,32 +72,33 @@ class JavaCompiler(BasicLinkerIsCompilerMixin, Compiler): return parameter_list - def _sanity_check_filenames(self) -> T.Tuple[str, str]: - sup = super()._sanity_check_filenames() - return sup[0], 'SanityCheck' - - def _sanity_check_compile_args(self, env: Environment, sourcename: str, binname: str) -> T.List[str]: - return self.exelist + self.get_always_args() + [sourcename] - - def _sanity_check_run_with_exe_wrapper(self, env: Environment, command: T.List[str]) -> T.List[str]: + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: + src = 'SanityCheck.java' + obj = 'SanityCheck' + source_name = os.path.join(work_dir, src) + with open(source_name, 'w', encoding='utf-8') as ofile: + ofile.write(textwrap.dedent( + '''class SanityCheck { + public static void main(String[] args) { + int i; + } + } + ''')) + pc = subprocess.Popen(self.exelist + [src], cwd=work_dir) + pc.wait() + if pc.returncode != 0: + raise EnvironmentException(f'Java compiler {self.name_string()} cannot compile programs.') runner = shutil.which(self.javarunner) - if runner is None: + if runner: + cmdlist = [runner, '-cp', '.', obj] + 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 " \ "requirement doesn't make sense, please open a bug at " \ "https://github.com/mesonbuild/meson/issues/new and tell us " \ "all about it." raise EnvironmentException(m) - return [runner, '-cp', '.', os.path.basename(command[0])] - - def _sanity_check_source_code(self) -> str: - return textwrap.dedent( - '''class SanityCheck { - public static void main(String[] args) { - int i; - } - } - ''') def needs_static_linker(self) -> bool: return False diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index bd139d21c..a492fffed 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -268,15 +268,50 @@ class CLikeCompiler(Compiler): def gen_import_library_args(self, implibname: str) -> T.List[str]: return self.linker.import_library_args(implibname) - def _sanity_check_compile_args(self, env: Environment, sourcename: str, binname: str) -> T.List[str]: - # Cross-compiling is hard. For example, you might need -nostdlib, or to pass --target, etc. - mode = CompileCheckMode.COMPILE if self.is_cross and not env.has_exe_wrapper() else CompileCheckMode.LINK - cargs, largs = self._get_basic_compiler_args(env, mode) + def _sanity_check_impl(self, work_dir: str, environment: 'Environment', + sname: str, code: str) -> None: + mlog.debug('Sanity testing ' + self.get_display_language() + ' compiler:', mesonlib.join_args(self.exelist)) + mlog.debug(f'Is cross compiler: {self.is_cross!s}.') + + source_name = os.path.join(work_dir, sname) + binname = sname.rsplit('.', 1)[0] + mode = CompileCheckMode.LINK + if self.is_cross: + binname += '_cross' + if 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 + # a ton of compiler flags to differentiate between + # arm and x86_64. So just compile. + mode = CompileCheckMode.COMPILE + cargs, largs = self._get_basic_compiler_args(environment, mode) extra_flags = cargs + self.linker_to_compiler_args(largs) - # It is important that extra_flags is last as it may contain `/link` - # directives, MSVC-compatible compilers will pass all arguments after - # that to the linker - return self.exelist + [sourcename] + self.get_output_args(binname) + extra_flags + + # Is a valid executable output for all toolchains and platforms + binname += '.exe' + # Write binary check source + binary_name = os.path.join(work_dir, binname) + with open(source_name, 'w', encoding='utf-8') as ofile: + ofile.write(code) + # Compile sanity check + # NOTE: extra_flags must be added at the end. On MSVC, it might contain a '/link' argument + # after which all further arguments will be passed directly to the linker + cmdlist = self.exelist + [sname] + self.get_output_args(binname) + extra_flags + pc, stdo, stde = mesonlib.Popen_safe(cmdlist, cwd=work_dir) + mlog.debug('Sanity check compiler command line:', mesonlib.join_args(cmdlist)) + mlog.debug('Sanity check compile stdout:') + mlog.debug(stdo) + mlog.debug('-----\nSanity check compile stderr:') + mlog.debug(stde) + mlog.debug('-----') + if pc.returncode != 0: + raise mesonlib.EnvironmentException(f'Compiler {self.name_string()} cannot compile programs.') + 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' + return self._sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code) def check_header(self, hname: str, prefix: str, env: 'Environment', *, extra_args: T.Union[None, T.List[str], T.Callable[['CompileCheckMode'], T.List[str]]] = None, diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py index b6deddde9..d013417fc 100644 --- a/mesonbuild/compilers/objc.py +++ b/mesonbuild/compilers/objc.py @@ -48,8 +48,9 @@ class ObjCCompiler(CLikeCompiler, Compiler): def get_display_language() -> str: return 'Objective-C' - def _sanity_check_source_code(self) -> str: - return '#import<stddef.h>\nint main(void) { return 0; }\n' + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: + code = '#import<stddef.h>\nint main(void) { return 0; }\n' + return self._sanity_check_impl(work_dir, environment, 'sanitycheckobjc.m', code) def form_compileropt_key(self, basename: str) -> OptionKey: if basename == 'std': diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py index e59d32708..441428b2f 100644 --- a/mesonbuild/compilers/objcpp.py +++ b/mesonbuild/compilers/objcpp.py @@ -50,8 +50,9 @@ class ObjCPPCompiler(CLikeCompiler, Compiler): def get_display_language() -> str: return 'Objective-C++' - def _sanity_check_source_code(self) -> str: - return '#import<stdio.h>\nclass MyClass;int main(void) { return 0; }\n' + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: + code = '#import<stdio.h>\nclass MyClass;int main(void) { return 0; }\n' + return self._sanity_check_impl(work_dir, environment, 'sanitycheckobjcpp.mm', code) def get_options(self) -> MutableKeyedOptionDictType: opts = super().get_options() diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index 07faba840..d0f92668a 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -107,43 +107,44 @@ class RustCompiler(Compiler): def needs_static_linker(self) -> bool: return False - def _sanity_check_compile_args(self, env: Environment, sourcename: str, binname: str) -> T.List[str]: + def sanity_check(self, work_dir: str, environment: Environment) -> None: + source_name = os.path.join(work_dir, 'sanity.rs') + output_name = os.path.join(work_dir, 'rusttest.exe') cmdlist = self.exelist.copy() - assert self.linker is not None, 'for mypy' - if self.info.kernel == 'none' and 'ld.' in self.linker.id: - cmdlist.extend(['-C', 'link-arg=-nostartfiles']) - cmdlist.extend(self.get_output_args(binname)) - cmdlist.append(sourcename) - return cmdlist - - def _sanity_check_source_code(self) -> str: - if self.info.kernel != 'none': - return textwrap.dedent( - '''fn main() { - } - ''') - return textwrap.dedent( - '''#![no_std] - #![no_main] - #[no_mangle] - pub fn _start() { - } - #[panic_handler] - fn panic(_info: &core::panic::PanicInfo) -> ! { - loop {} - } - ''') - def sanity_check(self, work_dir: str, environment: Environment) -> None: - super().sanity_check(work_dir, environment) - source_name = self._sanity_check_filenames()[0] + with open(source_name, 'w', encoding='utf-8') as ofile: + # If machine kernel is not `none`, try to compile a dummy program. + # If 'none', this is likely a `no-std`(i.e. bare metal) project. + if self.info.kernel != 'none': + ofile.write(textwrap.dedent( + '''fn main() { + } + ''')) + else: + # If rustc linker is gcc, add `-nostartfiles` + if 'ld.' in self.linker.id: + cmdlist.extend(['-C', 'link-arg=-nostartfiles']) + ofile.write(textwrap.dedent( + '''#![no_std] + #![no_main] + #[no_mangle] + pub fn _start() { + } + #[panic_handler] + fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} + } + ''')) + + cmdlist.extend(['-o', output_name, source_name]) + 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.') self._native_static_libs(work_dir, source_name) + 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 - if self.native_static_libs: - return - cmdlist = self.exelist + ['--crate-type', 'staticlib', '--print', 'native-static-libs', source_name] p, stdo, stde = Popen_safe_logged(cmdlist, cwd=work_dir) if p.returncode != 0: diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index 94e565a7c..4ad3affb5 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -177,21 +177,22 @@ class SwiftCompiler(Compiler): return parameter_list - def _sanity_check_compile_args(self, env: Environment, sourcename: str, binname: str) -> T.List[str]: - cmdlist = self.exelist.copy() - # TODO: I can't test this, but it doesn't seem right + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: + src = 'swifttest.swift' + source_name = os.path.join(work_dir, src) + output_name = os.path.join(work_dir, 'swifttest') + extra_flags: T.List[str] = [] + extra_flags += environment.coredata.get_external_args(self.for_machine, self.language) if self.is_cross: - cmdlist.extend(self.get_compile_only_args()) + extra_flags += self.get_compile_only_args() else: - cmdlist.extend(env.coredata.get_external_link_args(self.for_machine, self.language)) - cmdlist.extend(self.get_std_exe_link_args()) - cmdlist.extend(self.get_output_args(binname)) - cmdlist.append(sourcename) - - return cmdlist - - def _sanity_check_source_code(self) -> str: - return 'print("Swift compilation is working.")' + extra_flags += environment.coredata.get_external_link_args(self.for_machine, self.language) + with open(source_name, 'w', encoding='utf-8') as ofile: + ofile.write('''print("Swift compilation is working.") +''') + pc = subprocess.Popen(self.exelist + extra_flags + ['-emit-executable', '-o', output_name, src], cwd=work_dir) + pc.wait() + 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/compilers/vala.py b/mesonbuild/compilers/vala.py index 230a7739b..bbaefedb6 100644 --- a/mesonbuild/compilers/vala.py +++ b/mesonbuild/compilers/vala.py @@ -8,7 +8,7 @@ import typing as T from .. import mlog from .. import mesonlib -from ..mesonlib import version_compare, LibType +from ..mesonlib import EnvironmentException, version_compare, LibType from ..options import OptionKey from .compilers import CompileCheckMode, Compiler @@ -99,25 +99,18 @@ class ValaCompiler(Compiler): return parameter_list - def _sanity_check_source_code(self) -> str: - return 'public static int main() { return 0; }' - - def _sanity_check_compile_args(self, env: Environment, sourcename: str, binname: str) -> T.List[str]: - cmdlist = self.exelist.copy() - cmdlist.extend(env.coredata.get_external_args(self.for_machine, self.language)) + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: + code = 'class MesonSanityCheck : Object { }' + extra_flags: T.List[str] = [] + extra_flags += environment.coredata.get_external_args(self.for_machine, self.language) if self.is_cross: - cmdlist.extend(self.get_compile_only_args()) + extra_flags += self.get_compile_only_args() else: - cmdlist.extend(env.coredata.get_external_link_args(self.for_machine, self.language)) - cmdlist.extend(self.get_output_args(binname)) - cmdlist.append(sourcename) - return cmdlist - - def _run_sanity_check(self, env: Environment, cmdlist: T.List[str], work_dir: str) -> None: - # Vala will do a Vala -> C -> Exe, so the output file will actually have - # the name of the C compiler. - # TODO: find a way to not make this so hacky - return super()._run_sanity_check(env, [os.path.join(work_dir, 'sanity_check_for_c.exe')], work_dir) + extra_flags += environment.coredata.get_external_link_args(self.for_machine, self.language) + with self.cached_compile(code, environment.coredata, extra_args=extra_flags, mode=CompileCheckMode.COMPILE) as p: + if p.returncode != 0: + msg = f'Vala compiler {self.name_string()!r} cannot compile programs' + raise EnvironmentException(msg) def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str], libtype: LibType = LibType.PREFER_SHARED, lib_prefix_warning: bool = True, ignore_system_dirs: bool = False) -> T.Optional[T.List[str]]: diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index b746fa8e6..72dbfdf2d 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -55,11 +55,8 @@ class MissingCompiler(_MissingCompilerBase): def get_output_args(self, outputname: str) -> T.List[str]: return [] - def _sanity_check_compile_args(self, env: Environment, sourcename: str, binname: str) -> T.List[str]: - return [] - - def _sanity_check_source_code(self) -> str: - return '' + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: + return None def __getattr__(self, item: str) -> T.Any: if item.startswith('__'): |
