Files
meson/mesonbuild/dependencies/mpi.py
T
Dylan Baker e991c4d454 Use SPDX-License-Identifier consistently
This replaces all of the Apache blurbs at the start of each file with an
`# SPDX-License-Identifier: Apache-2.0` string. It also fixes existing
uses to be consistent in capitalization, and to be placed above any
copyright notices.

This removes nearly 3000 lines of boilerplate from the project (only
python files), which no developer cares to look at.

SPDX is in common use, particularly in the Linux kernel, and is the
recommended format for Meson's own `project(license: )` field
2023-12-13 15:19:21 -05:00

232 lines
8.3 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# Copyright 2013-2019 The Meson development team
from __future__ import annotations
import functools
import typing as T
import os
import re
from ..environment import detect_cpu_family
from .base import DependencyMethods, detect_compiler, SystemDependency
from .configtool import ConfigToolDependency
from .detect import packages
from .factory import factory_methods
from .pkgconfig import PkgConfigDependency
if T.TYPE_CHECKING:
from .factory import DependencyGenerator
from ..environment import Environment
from ..mesonlib import MachineChoice
@factory_methods({DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.SYSTEM})
def mpi_factory(env: 'Environment',
for_machine: 'MachineChoice',
kwargs: T.Dict[str, T.Any],
methods: T.List[DependencyMethods]) -> T.List['DependencyGenerator']:
language = kwargs.get('language', 'c')
if language not in {'c', 'cpp', 'fortran'}:
# OpenMPI doesn't work without any other languages
return []
candidates: T.List['DependencyGenerator'] = []
compiler = detect_compiler('mpi', env, for_machine, language)
if not compiler:
return []
compiler_is_intel = compiler.get_id() in {'intel', 'intel-cl'}
# Only OpenMPI has pkg-config, and it doesn't work with the intel compilers
if DependencyMethods.PKGCONFIG in methods and not compiler_is_intel:
pkg_name = None
if language == 'c':
pkg_name = 'ompi-c'
elif language == 'cpp':
pkg_name = 'ompi-cxx'
elif language == 'fortran':
pkg_name = 'ompi-fort'
candidates.append(functools.partial(
PkgConfigDependency, pkg_name, env, kwargs, language=language))
if DependencyMethods.CONFIG_TOOL in methods:
nwargs = kwargs.copy()
if compiler_is_intel:
if env.machines[for_machine].is_windows():
nwargs['version_arg'] = '-v'
nwargs['returncode_value'] = 3
if language == 'c':
tool_names = [os.environ.get('I_MPI_CC'), 'mpiicc']
elif language == 'cpp':
tool_names = [os.environ.get('I_MPI_CXX'), 'mpiicpc']
elif language == 'fortran':
tool_names = [os.environ.get('I_MPI_F90'), 'mpiifort']
cls: T.Type[ConfigToolDependency] = IntelMPIConfigToolDependency
else: # OpenMPI, which doesn't work with intel
#
# We try the environment variables for the tools first, but then
# fall back to the hardcoded names
if language == 'c':
tool_names = [os.environ.get('MPICC'), 'mpicc']
elif language == 'cpp':
tool_names = [os.environ.get('MPICXX'), 'mpic++', 'mpicxx', 'mpiCC']
elif language == 'fortran':
tool_names = [os.environ.get(e) for e in ['MPIFC', 'MPIF90', 'MPIF77']]
tool_names.extend(['mpifort', 'mpif90', 'mpif77'])
cls = OpenMPIConfigToolDependency
tool_names = [t for t in tool_names if t] # remove empty environment variables
assert tool_names
nwargs['tools'] = tool_names
candidates.append(functools.partial(
cls, tool_names[0], env, nwargs, language=language))
if DependencyMethods.SYSTEM in methods:
candidates.append(functools.partial(
MSMPIDependency, 'msmpi', env, kwargs, language=language))
return candidates
packages['mpi'] = mpi_factory
class _MPIConfigToolDependency(ConfigToolDependency):
def _filter_compile_args(self, args: T.List[str]) -> T.List[str]:
"""
MPI wrappers return a bunch of garbage args.
Drop -O2 and everything that is not needed.
"""
result = []
multi_args: T.Tuple[str, ...] = ('-I', )
if self.language == 'fortran':
fc = self.env.coredata.compilers[self.for_machine]['fortran']
multi_args += fc.get_module_incdir_args()
include_next = False
for f in args:
if f.startswith(('-D', '-f') + multi_args) or f == '-pthread' \
or (f.startswith('-W') and f != '-Wall' and not f.startswith('-Werror')):
result.append(f)
if f in multi_args:
# Path is a separate argument.
include_next = True
elif include_next:
include_next = False
result.append(f)
return result
def _filter_link_args(self, args: T.List[str]) -> T.List[str]:
"""
MPI wrappers return a bunch of garbage args.
Drop -O2 and everything that is not needed.
"""
result = []
include_next = False
for f in args:
if self._is_link_arg(f):
result.append(f)
if f in {'-L', '-Xlinker'}:
include_next = True
elif include_next:
include_next = False
result.append(f)
return result
def _is_link_arg(self, f: str) -> bool:
if self.clib_compiler.id == 'intel-cl':
return f == '/link' or f.startswith('/LIBPATH') or f.endswith('.lib') # always .lib whether static or dynamic
else:
return (f.startswith(('-L', '-l', '-Xlinker')) or
f == '-pthread' or
(f.startswith('-W') and f != '-Wall' and not f.startswith('-Werror')))
class IntelMPIConfigToolDependency(_MPIConfigToolDependency):
"""Wrapper around Intel's mpiicc and friends."""
version_arg = '-v' # --version is not the same as -v
def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any],
language: T.Optional[str] = None):
super().__init__(name, env, kwargs, language=language)
if not self.is_found:
return
args = self.get_config_value(['-show'], 'link and compile args')
self.compile_args = self._filter_compile_args(args)
self.link_args = self._filter_link_args(args)
def _sanitize_version(self, out: str) -> str:
v = re.search(r'(\d{4}) Update (\d)', out)
if v:
return '{}.{}'.format(v.group(1), v.group(2))
return out
class OpenMPIConfigToolDependency(_MPIConfigToolDependency):
"""Wrapper around OpenMPI mpicc and friends."""
version_arg = '--showme:version'
def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any],
language: T.Optional[str] = None):
super().__init__(name, env, kwargs, language=language)
if not self.is_found:
return
c_args = self.get_config_value(['--showme:compile'], 'compile_args')
self.compile_args = self._filter_compile_args(c_args)
l_args = self.get_config_value(['--showme:link'], 'link_args')
self.link_args = self._filter_link_args(l_args)
def _sanitize_version(self, out: str) -> str:
v = re.search(r'\d+.\d+.\d+', out)
if v:
return v.group(0)
return out
class MSMPIDependency(SystemDependency):
"""The Microsoft MPI."""
def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any],
language: T.Optional[str] = None):
super().__init__(name, env, kwargs, language=language)
# MSMPI only supports the C API
if language not in {'c', 'fortran', None}:
self.is_found = False
return
# MSMPI is only for windows, obviously
if not self.env.machines[self.for_machine].is_windows():
return
incdir = os.environ.get('MSMPI_INC')
arch = detect_cpu_family(self.env.coredata.compilers.host)
libdir = None
if arch == 'x86':
libdir = os.environ.get('MSMPI_LIB32')
post = 'x86'
elif arch == 'x86_64':
libdir = os.environ.get('MSMPI_LIB64')
post = 'x64'
if libdir is None or incdir is None:
self.is_found = False
return
self.is_found = True
self.link_args = ['-l' + os.path.join(libdir, 'msmpi')]
self.compile_args = ['-I' + incdir, '-I' + os.path.join(incdir, post)]
if self.language == 'fortran':
self.link_args.append('-l' + os.path.join(libdir, 'msmpifec'))