Reimplement several methods for Fortran compilers

The output_is_64bit, sizeof, cross_sizeof, compute_int and cross_compute_int
methods are reimplemented for Fortran compilers. Those inherited from
CLikeCompiler do not work since they assume C or C++.

Note that those tests rely on Fortran 2008 features (notably the c_sizeof
operator).

Closes #12757
pull/13831/head
Sébastien Villemot 1 month ago committed by Dylan Baker
parent db82c2d777
commit 8c5505c28a
  1. 8
      docs/markdown/snippets/fixed_sizeof_and_find_library_for_fortran.md
  2. 132
      mesonbuild/compilers/fortran.py

@ -0,0 +1,8 @@
## Fixed `sizeof` and `find_library` methods for Fortran compilers
The implementation of the `.sizeof()` method has been fixed for Fortran
compilers (it was previously broken since it would try to compile a C code
snippet). Note that this functionality requires Fortran 2008 support.
Incidentally this also fixes the `.find_library()` method for Fortran compilers
when the `prefer_static` built-in option is set to true.

@ -4,9 +4,11 @@
from __future__ import annotations
import typing as T
import functools
import os
from .. import options
from .. import mesonlib
from .compilers import (
clike_debug_args,
Compiler,
@ -121,6 +123,136 @@ class FortranCompiler(CLikeCompiler, Compiler):
'none'),
)
def _compile_int(self, expression: str, prefix: str, env: 'Environment',
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
dependencies: T.Optional[T.List['Dependency']]) -> bool:
# Use a trick for emulating a static assert
# Taken from https://github.com/j3-fortran/fortran_proposals/issues/70
t = f'''program test
{prefix}
real(merge(kind(1.),-1,({expression}))), parameter :: fail = 1.
end program test'''
return self.compiles(t, env, extra_args=extra_args,
dependencies=dependencies)[0]
def cross_compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
guess: T.Optional[int], prefix: str, env: 'Environment',
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> int:
# This only difference between this implementation and that of CLikeCompiler
# is a change in logical conjunction operator (.and. instead of &&)
# Try user's guess first
if isinstance(guess, int):
if self._compile_int(f'{expression} == {guess}', prefix, env, extra_args, dependencies):
return guess
# If no bounds are given, compute them in the limit of int32
maxint = 0x7fffffff
minint = -0x80000000
if not isinstance(low, int) or not isinstance(high, int):
if self._compile_int(f'{expression} >= 0', prefix, env, extra_args, dependencies):
low = cur = 0
while self._compile_int(f'{expression} > {cur}', prefix, env, extra_args, dependencies):
low = cur + 1
if low > maxint:
raise mesonlib.EnvironmentException('Cross-compile check overflowed')
cur = min(cur * 2 + 1, maxint)
high = cur
else:
high = cur = -1
while self._compile_int(f'{expression} < {cur}', prefix, env, extra_args, dependencies):
high = cur - 1
if high < minint:
raise mesonlib.EnvironmentException('Cross-compile check overflowed')
cur = max(cur * 2, minint)
low = cur
else:
# Sanity check limits given by user
if high < low:
raise mesonlib.EnvironmentException('high limit smaller than low limit')
condition = f'{expression} <= {high} .and. {expression} >= {low}'
if not self._compile_int(condition, prefix, env, extra_args, dependencies):
raise mesonlib.EnvironmentException('Value out of given range')
# Binary search
while low != high:
cur = low + int((high - low) / 2)
if self._compile_int(f'{expression} <= {cur}', prefix, env, extra_args, dependencies):
high = cur
else:
low = cur + 1
return low
def compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
guess: T.Optional[int], prefix: str, env: 'Environment', *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
if self.is_cross:
return self.cross_compute_int(expression, low, high, guess, prefix, env, extra_args, dependencies)
t = f'''program test
{prefix}
print '(i0)', {expression}
end program test
'''
res = self.run(t, env, extra_args=extra_args,
dependencies=dependencies)
if not res.compiled:
return -1
if res.returncode != 0:
raise mesonlib.EnvironmentException('Could not run compute_int test binary.')
return int(res.stdout)
def cross_sizeof(self, typename: str, prefix: str, env: 'Environment', *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
t = f'''program test
use iso_c_binding
{prefix}
{typename} :: something
end program test
'''
if not self.compiles(t, env, extra_args=extra_args,
dependencies=dependencies)[0]:
return -1
return self.cross_compute_int('c_sizeof(x)', None, None, None, prefix + '\nuse iso_c_binding\n' + typename + ' :: x', env, extra_args, dependencies)
def sizeof(self, typename: str, prefix: str, env: 'Environment', *,
extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[int, bool]:
if extra_args is None:
extra_args = []
if self.is_cross:
r = self.cross_sizeof(typename, prefix, env, extra_args=extra_args,
dependencies=dependencies)
return r, False
t = f'''program test
use iso_c_binding
{prefix}
{typename} :: x
print '(i0)', c_sizeof(x)
end program test
'''
res = self.cached_run(t, env, extra_args=extra_args,
dependencies=dependencies)
if not res.compiled:
return -1, False
if res.returncode != 0:
raise mesonlib.EnvironmentException('Could not run sizeof test binary.')
return int(res.stdout), res.cached
@functools.lru_cache()
def output_is_64bit(self, env: 'Environment') -> bool:
'''
returns true if the output produced is 64-bit, false if 32-bit
'''
return self.sizeof('type(c_ptr)', '', env)[0] == 8
class GnuFortranCompiler(GnuCompiler, FortranCompiler):

Loading…
Cancel
Save