diff options
| -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, 284 insertions, 264 deletions
diff --git a/mesonbuild/compilers/asm.py b/mesonbuild/compilers/asm.py index d4af77f89..5f065e193 100644 --- a/mesonbuild/compilers/asm.py +++ b/mesonbuild/compilers/asm.py @@ -10,6 +10,7 @@ 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 @@ -39,6 +40,12 @@ 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 424b61251..a0786d55c 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -76,9 +76,8 @@ class CCompiler(CLikeCompiler, Compiler): def get_no_stdinc_args(self) -> T.List[str]: return ['-nostdinc'] - 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 _sanity_check_source_code(self) -> str: + return 'int main(void) { int class=0; return class; }\n' 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 624226d33..4c28eb2fd 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1196,8 +1196,7 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta): def name_string(self) -> str: return ' '.join(self.exelist) - @abc.abstractmethod - def sanity_check(self, work_dir: str, environment: 'Environment') -> None: + def sanity_check(self, work_dir: str, env: Environment) -> None: """Check that this compiler actually works. This should provide a simple compile/link test. Something as simple as: @@ -1205,24 +1204,103 @@ 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. - 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)) + :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)) 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 fa032ec79..8da100483 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -86,9 +86,8 @@ class CPPCompiler(CLikeCompiler, Compiler): def get_no_stdlib_link_args(self) -> T.List[str]: return ['-nostdlib++'] - 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 _sanity_check_source_code(self) -> str: + return 'class breakCCompiler;int main(void) { return 0; }\n' 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 4bbddeb20..e515c338f 100644 --- a/mesonbuild/compilers/cs.py +++ b/mesonbuild/compilers/cs.py @@ -3,11 +3,10 @@ from __future__ import annotations -import os.path, subprocess +import os.path import textwrap import typing as T -from ..mesonlib import EnvironmentException from ..linkers import RSPFileSyntax from .compilers import Compiler @@ -83,26 +82,21 @@ class CsCompiler(BasicLinkerIsCompilerMixin, Compiler): def get_pch_name(self, header_name: str) -> str: return '' - 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_source_code(self) -> str: + return textwrap.dedent(''' + public class Sanity { + static public void Main () { } - ''')) - 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()) + } + ''') + + 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]: if self.runner: - 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) + return [self.runner] + command + return command def needs_static_linker(self) -> bool: return False diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index 7e050f140..a9e6a7669 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -5,15 +5,14 @@ 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, Popen_safe, - is_windows, LibType, version_compare + EnvironmentException, is_windows, LibType, version_compare ) from .compilers import Compiler, CompileCheckMode @@ -187,6 +186,7 @@ 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,55 +499,36 @@ 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(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); + 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); + } } } + 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', source_name] + flags = ['-w', '-cudart', 'static', sourcename] # Use the -ccbin option, if available, even during sanity checking. # Otherwise, on systems where CUDA does not support the default compiler, @@ -562,33 +543,30 @@ 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(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: + 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') return - cmdlist = self.exelist + ['--run', f'"{binary_name}"'] + 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)) try: - stdo, stde = self.run_sanity_check(env, cmdlist, work_dir) - except EnvironmentException: + 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: 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 50bb4652b..2814ca0d6 100644 --- a/mesonbuild/compilers/cython.py +++ b/mesonbuild/compilers/cython.py @@ -1,13 +1,14 @@ # 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 EnvironmentException, version_compare +from ..mesonlib import version_compare from .compilers import Compiler if T.TYPE_CHECKING: @@ -49,16 +50,22 @@ 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 9f662add3..4df8f6570 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -5,7 +5,6 @@ from __future__ import annotations import os.path import re -import subprocess import typing as T from .. import mesonlib @@ -438,24 +437,11 @@ class DCompiler(Compiler): full_version=full_version, is_cross=is_cross) self.arch = arch - 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_source_code(self) -> str: + return 'void main() { }' - 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 _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] def needs_static_linker(self) -> bool: return True diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index 6f4f3d2c1..2853ee24c 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -3,6 +3,7 @@ from __future__ import annotations +import textwrap import typing as T import functools import os @@ -61,10 +62,12 @@ class FortranCompiler(CLikeCompiler, Compiler): largs = env.coredata.get_external_link_args(self.for_machine, self.language) return cargs, largs - 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 _sanity_check_source_code(self) -> str: + return textwrap.dedent(''' + PROGRAM MAIN + PRINT *, "Fortran compilation is working." + END + ''') 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 47d2ac9cd..13e48475c 100644 --- a/mesonbuild/compilers/java.py +++ b/mesonbuild/compilers/java.py @@ -6,7 +6,6 @@ from __future__ import annotations import os import os.path import shutil -import subprocess import textwrap import typing as T @@ -72,33 +71,32 @@ class JavaCompiler(BasicLinkerIsCompilerMixin, Compiler): return parameter_list - 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.') + 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]: runner = shutil.which(self.javarunner) - if runner: - cmdlist = [runner, '-cp', '.', obj] - self.run_sanity_check(environment, cmdlist, work_dir, use_exe_wrapper_for_cross=False) - else: + if runner is None: 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 a492fffed..bd139d21c 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -268,50 +268,15 @@ class CLikeCompiler(Compiler): def gen_import_library_args(self, implibname: str) -> T.List[str]: return self.linker.import_library_args(implibname) - 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) + 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) extra_flags = cargs + self.linker_to_compiler_args(largs) - - # 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) + # 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 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 d013417fc..b6deddde9 100644 --- a/mesonbuild/compilers/objc.py +++ b/mesonbuild/compilers/objc.py @@ -48,9 +48,8 @@ class ObjCCompiler(CLikeCompiler, Compiler): def get_display_language() -> str: return 'Objective-C' - 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 _sanity_check_source_code(self) -> str: + return '#import<stddef.h>\nint main(void) { return 0; }\n' def form_compileropt_key(self, basename: str) -> OptionKey: if basename == 'std': diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py index 441428b2f..e59d32708 100644 --- a/mesonbuild/compilers/objcpp.py +++ b/mesonbuild/compilers/objcpp.py @@ -50,9 +50,8 @@ class ObjCPPCompiler(CLikeCompiler, Compiler): def get_display_language() -> str: return 'Objective-C++' - 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 _sanity_check_source_code(self) -> str: + return '#import<stdio.h>\nclass MyClass;int main(void) { return 0; }\n' def get_options(self) -> MutableKeyedOptionDictType: opts = super().get_options() diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index d0f92668a..07faba840 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -107,44 +107,43 @@ class RustCompiler(Compiler): def needs_static_linker(self) -> bool: return False - 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') + def _sanity_check_compile_args(self, env: Environment, sourcename: str, binname: str) -> T.List[str]: 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 {} + } + ''') - 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.') + def sanity_check(self, work_dir: str, environment: Environment) -> None: + super().sanity_check(work_dir, environment) + source_name = self._sanity_check_filenames()[0] 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 4ad3affb5..94e565a7c 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -177,22 +177,21 @@ class SwiftCompiler(Compiler): return parameter_list - 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) + 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 if self.is_cross: - extra_flags += self.get_compile_only_args() + cmdlist.extend(self.get_compile_only_args()) else: - 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) + 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.")' 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 bbaefedb6..230a7739b 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 EnvironmentException, version_compare, LibType +from ..mesonlib import version_compare, LibType from ..options import OptionKey from .compilers import CompileCheckMode, Compiler @@ -99,18 +99,25 @@ class ValaCompiler(Compiler): return parameter_list - 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) + 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)) if self.is_cross: - extra_flags += self.get_compile_only_args() + cmdlist.extend(self.get_compile_only_args()) else: - 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) + 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) 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 72dbfdf2d..b746fa8e6 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -55,8 +55,11 @@ class MissingCompiler(_MissingCompilerBase): def get_output_args(self, outputname: str) -> T.List[str]: return [] - def sanity_check(self, work_dir: str, environment: 'Environment') -> None: - return None + 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 __getattr__(self, item: str) -> T.Any: if item.startswith('__'): |
