Merge branch 'QuLogic-mpi'

pull/2157/head
Jussi Pakkanen 7 years ago
commit a19f906258
  1. 11
      .appveyor.yml
  2. 15
      docs/markdown/Dependencies.md
  3. 1
      docs/markdown/Reference-manual.md
  4. 8
      docs/markdown/Release-notes-for-0.42.0.md
  5. 9
      mesonbuild/compilers/fortran.py
  6. 8
      mesonbuild/dependencies/__init__.py
  7. 8
      mesonbuild/dependencies/base.py
  8. 208
      mesonbuild/dependencies/misc.py
  9. 27
      test cases/frameworks/17 mpi/main.c
  10. 11
      test cases/frameworks/17 mpi/main.cpp
  11. 21
      test cases/frameworks/17 mpi/main.f90
  12. 33
      test cases/frameworks/17 mpi/meson.build

@ -76,6 +76,17 @@ install:
- ps: If($Env:compiler -eq 'msys2-mingw') {(new-object Net.WebClient).DownloadFile('https://bootstrap.pypa.io/get-pip.py', 'C:\projects\meson\get-pip.py')}
- cmd: if %compiler%==msys2-mingw ( %PYTHON% "C:\projects\meson\get-pip.py" )
- cmd: if %compiler%==cygwin ( call ci\appveyor-install.bat )
- ps: |
If($Env:compiler -like 'msvc*') {
(new-object net.webclient).DownloadFile(
"https://download.microsoft.com/download/D/B/B/DBB64BA1-7B51-43DB-8BF1-D1FB45EACF7A/msmpisdk.msi",
"C:\projects\msmpisdk.msi")
c:\windows\system32\msiexec.exe /i C:\projects\msmpisdk.msi /quiet
(new-object net.webclient).DownloadFile(
"https://download.microsoft.com/download/D/B/B/DBB64BA1-7B51-43DB-8BF1-D1FB45EACF7A/MSMpiSetup.exe",
"C:\projects\MSMpiSetup.exe")
c:\projects\MSMpiSetup.exe -unattend -full
}
# Install additional packages needed for all builds.
- cmd: "%WRAPPER% %PYTHON% -m pip install codecov"

@ -51,6 +51,21 @@ If your boost headers or libraries are in non-standard locations you can set the
GTest and GMock come as sources that must be compiled as part of your project. With Meson you don't have to care about the details, just pass `gtest` or `gmock` to `dependency` and it will do everything for you. If you want to use GMock, it is recommended to use GTest as well, as getting it to work standalone is tricky.
## MPI ##
MPI is supported for C, C++ and Fortran. Because dependencies are
language-specific, you must specify the requested language using the
`language` keyword argument, i.e.,
* `dependency('mpi', language='c')` for the C MPI headers and libraries
* `dependency('mpi', language='cpp')` for the C++ MPI headers and libraries
* `dependency('mpi', language='fortran')` for the Fortran MPI headers and libraries
Meson prefers pkg-config for MPI, but if your MPI implementation does not
provide them, it will search for the standard wrapper executables, `mpic`,
`mpicxx`, `mpic++`, `mpifort`, `mpif90`, `mpif77`. If these are not in your
path, they can be specified by setting the standard environment variables
`MPICC`, `MPICXX`, `MPIFC`, `MPIF90`, or `MPIF77`, during configuration.
## Qt5 ##
Meson has native Qt5 support. Its usage is best demonstrated with an example.

@ -193,6 +193,7 @@ Finds an external dependency with the given name with `pkg-config` if possible a
- `fallback` specifies a subproject fallback to use in case the dependency is not found in the system. The value is an array `['subproj_name', 'subproj_dep']` where the first value is the name of the subproject and the second is the variable name in that subproject that contains the value of [`declare_dependency`](#declare_dependency).
- `default_options` *(added 0.37.0)* an array of option values that override those set in the project's `default_options` invocation (like `default_options` in [`project()`](#project), they only have effect when Meson is run for the first time, and command line arguments override any default options in build files)
- `method` defines the way the dependency is detected, the default is `auto` but can be overridden to be e.g. `qmake` for Qt development, and different dependencies support different values for this (though `auto` will work on all of them)
- `language` *(added 0.42.0)* defines what language-specific dependency to find if it's available for multiple languages.
The returned object also has methods that are documented in the [object methods section](#dependency-object) below.

@ -104,3 +104,11 @@ By default Meson adds the current source and build directories to the
header search path. On some rare occasions this is not desired. Setting
the `implicit_include_directories` keyword argument to `false` these
directories are not used.
## Support for MPI dependency
MPI is now supported as a dependency. Because dependencies are
language-specific, you must specify the requested language with the `language`
keyword, i.e., `dependency('mpi', language='c')` will request the C MPI headers
and libraries. See [the MPI dependency](Dependencies.md#mpi) for more
information.

@ -120,6 +120,9 @@ end program prog
def get_include_args(self, path, is_system):
return ['-I' + path]
def get_module_incdir_args(self):
return ('-I', )
def get_module_outdir_args(self, path):
return ['-J' + path]
@ -209,6 +212,9 @@ class SunFortranCompiler(FortranCompiler):
def get_warn_args(self, level):
return []
def get_module_incdir_args(self):
return ('-M', )
def get_module_outdir_args(self, path):
return ['-moddir=' + path]
@ -251,6 +257,9 @@ class PGIFortranCompiler(FortranCompiler):
super().__init__(exelist, version, is_cross, exe_wrapper=None)
self.id = 'pgi'
def get_module_incdir_args(self):
return ('-module', )
def get_module_outdir_args(self, path):
return ['-module', path]

@ -15,9 +15,9 @@
from .base import ( # noqa: F401
Dependency, DependencyException, DependencyMethods, ExternalProgram,
ExternalDependency, ExternalLibrary, ExtraFrameworkDependency, InternalDependency,
PkgConfigDependency, find_external_dependency, get_dep_identifier, packages)
PkgConfigDependency, find_external_dependency, get_dep_identifier, packages, _packages_accept_language)
from .dev import GMockDependency, GTestDependency, LLVMDependency, ValgrindDependency
from .misc import BoostDependency, Python3Dependency, ThreadDependency
from .misc import (BoostDependency, MPIDependency, Python3Dependency, ThreadDependency)
from .platform import AppleFrameworks
from .ui import GLDependency, GnuStepDependency, Qt4Dependency, Qt5Dependency, SDL2Dependency, WxDependency, VulkanDependency
@ -31,6 +31,7 @@ packages.update({
# From misc:
'boost': BoostDependency,
'mpi': MPIDependency,
'python3': Python3Dependency,
'threads': ThreadDependency,
@ -46,3 +47,6 @@ packages.update({
'wxwidgets': WxDependency,
'vulkan': VulkanDependency,
})
_packages_accept_language.update({
'mpi',
})

@ -26,8 +26,9 @@ from .. import mesonlib
from ..mesonlib import MesonException, Popen_safe, flatten, version_compare_many
# This must be defined in this file to avoid cyclical references.
# These must be defined in this file to avoid cyclical references.
packages = {}
_packages_accept_language = set()
class DependencyException(MesonException):
@ -610,10 +611,15 @@ def find_external_dependency(name, env, kwargs):
raise DependencyException('Keyword "method" must be a string.')
lname = name.lower()
if lname in packages:
if lname not in _packages_accept_language and 'language' in kwargs:
raise DependencyException('%s dependency does not accept "language" keyword argument' % (lname, ))
dep = packages[lname](env, kwargs)
if required and not dep.found():
raise DependencyException('Dependency "%s" not found' % name)
return dep
if 'language' in kwargs:
# Remove check when PkgConfigDependency supports language.
raise DependencyException('%s dependency does not accept "language" keyword argument' % (lname, ))
pkg_exc = None
pkgdep = None
try:

@ -16,6 +16,8 @@
import glob
import os
import re
import shlex
import stat
import sysconfig
@ -24,7 +26,7 @@ from .. import mesonlib
from ..environment import detect_cpu_family
from .base import DependencyException, DependencyMethods
from .base import ExternalDependency, ExtraFrameworkDependency, PkgConfigDependency
from .base import ExternalDependency, ExternalProgram, ExtraFrameworkDependency, PkgConfigDependency
class BoostDependency(ExternalDependency):
@ -277,6 +279,210 @@ class BoostDependency(ExternalDependency):
return 'thread' in self.requested_modules
class MPIDependency(ExternalDependency):
def __init__(self, environment, kwargs):
language = kwargs.get('language', 'c')
super().__init__('mpi', environment, language, kwargs)
required = kwargs.pop('required', True)
kwargs['required'] = False
kwargs['silent'] = True
self.is_found = False
# NOTE: Only OpenMPI supplies a pkg-config file at the moment.
if language == 'c':
env_vars = ['MPICC']
pkgconfig_files = ['ompi-c']
default_wrappers = ['mpicc']
elif language == 'cpp':
env_vars = ['MPICXX']
pkgconfig_files = ['ompi-cxx']
default_wrappers = ['mpic++', 'mpicxx', 'mpiCC']
elif language == 'fortran':
env_vars = ['MPIFC', 'MPIF90', 'MPIF77']
pkgconfig_files = ['ompi-fort']
default_wrappers = ['mpifort', 'mpif90', 'mpif77']
else:
raise DependencyException('Language {} is not supported with MPI.'.format(language))
for pkg in pkgconfig_files:
try:
pkgdep = PkgConfigDependency(pkg, environment, kwargs)
if pkgdep.found():
self.compile_args = pkgdep.get_compile_args()
self.link_args = pkgdep.get_link_args()
self.version = pkgdep.get_version()
self.is_found = True
break
except Exception:
pass
if not self.is_found:
# Prefer environment.
for var in env_vars:
if var in os.environ:
wrappers = [os.environ[var]]
break
else:
# Or search for default wrappers.
wrappers = default_wrappers
for prog in wrappers:
result = self._try_openmpi_wrapper(prog)
if result is not None:
self.is_found = True
self.version = result[0]
self.compile_args = self._filter_compile_args(result[1])
self.link_args = self._filter_link_args(result[2])
break
result = self._try_other_wrapper(prog)
if result is not None:
self.is_found = True
self.version = result[0]
self.compile_args = self._filter_compile_args(result[1])
self.link_args = self._filter_link_args(result[2])
break
if not self.is_found and mesonlib.is_windows():
result = self._try_msmpi()
if result is not None:
self.is_found = True
self.version, self.compile_args, self.link_args = result
if self.is_found:
mlog.log('Dependency', mlog.bold(self.name), 'for', self.language, 'found:', mlog.green('YES'), self.version)
else:
mlog.log('Dependency', mlog.bold(self.name), 'for', self.language, 'found:', mlog.red('NO'))
if required:
raise DependencyException('MPI dependency {!r} not found'.format(self.name))
def _filter_compile_args(self, args):
"""
MPI wrappers return a bunch of garbage args.
Drop -O2 and everything that is not needed.
"""
result = []
multi_args = ('-I', )
if self.language == 'fortran':
fc = self.env.coredata.compilers['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):
"""
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 f.startswith(('-L', '-l', '-Xlinker')) or f == '-pthread' \
or (f.startswith('-W') and f != '-Wall' and not f.startswith('-Werror')):
result.append(f)
if f in ('-L', '-Xlinker'):
include_next = True
elif include_next:
include_next = False
result.append(f)
return result
def _try_openmpi_wrapper(self, prog):
prog = ExternalProgram(prog, silent=True)
if prog.found():
cmd = prog.get_command() + ['--showme:compile']
p, o, e = mesonlib.Popen_safe(cmd)
p.wait()
if p.returncode != 0:
mlog.debug('Command', mlog.bold(cmd), 'failed to run:')
mlog.debug(mlog.bold('Standard output\n'), o)
mlog.debug(mlog.bold('Standard error\n'), e)
return
cargs = shlex.split(o)
cmd = prog.get_command() + ['--showme:link']
p, o, e = mesonlib.Popen_safe(cmd)
p.wait()
if p.returncode != 0:
mlog.debug('Command', mlog.bold(cmd), 'failed to run:')
mlog.debug(mlog.bold('Standard output\n'), o)
mlog.debug(mlog.bold('Standard error\n'), e)
return
libs = shlex.split(o)
cmd = prog.get_command() + ['--showme:version']
p, o, e = mesonlib.Popen_safe(cmd)
p.wait()
if p.returncode != 0:
mlog.debug('Command', mlog.bold(cmd), 'failed to run:')
mlog.debug(mlog.bold('Standard output\n'), o)
mlog.debug(mlog.bold('Standard error\n'), e)
return
version = re.search('\d+.\d+.\d+', o)
if version:
version = version.group(0)
else:
version = 'none'
return version, cargs, libs
def _try_other_wrapper(self, prog):
prog = ExternalProgram(prog, silent=True)
if prog.found():
cmd = prog.get_command() + ['-show']
p, o, e = mesonlib.Popen_safe(cmd)
p.wait()
if p.returncode != 0:
mlog.debug('Command', mlog.bold(cmd), 'failed to run:')
mlog.debug(mlog.bold('Standard output\n'), o)
mlog.debug(mlog.bold('Standard error\n'), e)
return
args = shlex.split(o)
version = 'none'
return version, args, args
def _try_msmpi(self):
if self.language == 'cpp':
# MS-MPI does not support the C++ version of MPI, only the standard C API.
return
if 'MSMPI_INC' not in os.environ:
return
incdir = os.environ['MSMPI_INC']
arch = detect_cpu_family(self.env.coredata.compilers)
if arch == 'x86':
if 'MSMPI_LIB32' not in os.environ:
return
libdir = os.environ['MSMPI_LIB32']
post = 'x86'
elif arch == 'x86_64':
if 'MSMPI_LIB64' not in os.environ:
return
libdir = os.environ['MSMPI_LIB64']
post = 'x64'
else:
return
if self.language == 'fortran':
return ('none',
['-I' + incdir, '-I' + os.path.join(incdir, post)],
[os.path.join(libdir, 'msmpi.lib'), os.path.join(libdir, 'msmpifec.lib')])
else:
return ('none',
['-I' + incdir, '-I' + os.path.join(incdir, post)],
[os.path.join(libdir, 'msmpi.lib')])
class ThreadDependency(ExternalDependency):
def __init__(self, environment, kwargs):
super().__init__('threads', environment, None, {})

@ -0,0 +1,27 @@
#include <stdio.h>
#include <mpi.h>
int main(int argc, char **argv)
{
int ier, flag;
ier = MPI_Init(&argc, &argv);
if (ier) {
printf("Unable to initialize MPI: %d\n", ier);
return 1;
}
ier = MPI_Initialized(&flag);
if (ier) {
printf("Unable to check MPI initialization state: %d\n", ier);
return 1;
}
if (!flag) {
printf("MPI did not initialize!\n");
return 1;
}
ier = MPI_Finalize();
if (ier) {
printf("Unable to finalize MPI: %d\n", ier);
return 1;
}
return 0;
}

@ -0,0 +1,11 @@
#include <mpi.h>
int main(int argc, char **argv)
{
MPI::Init(argc, argv);
if (!MPI::Is_initialized()) {
printf("MPI did not initialize!\n");
return 1;
}
MPI::Finalize();
}

@ -0,0 +1,21 @@
program mpitest
implicit none
include 'mpif.h'
logical :: flag
integer :: ier
call MPI_Init(ier)
if (ier /= 0) then
print *, 'Unable to initialize MPI: ', ier
stop 1
endif
call MPI_Initialized(flag, ier)
if (ier /= 0) then
print *, 'Unable to check MPI initialization state: ', ier
stop 1
endif
call MPI_Finalize(ier)
if (ier /= 0) then
print *, 'Unable to finalize MPI: ', ier
stop 1
endif
end program mpitest

@ -0,0 +1,33 @@
project('mpi', 'c', 'cpp')
cc = meson.get_compiler('c')
if build_machine.system() == 'windows' and cc.get_id() != 'msvc'
error('MESON_SKIP_TEST: MPI not available on Windows without MSVC.')
endif
mpic = dependency('mpi', language : 'c')
exec = executable('exec',
'main.c',
dependencies : [mpic])
test('MPI C', exec)
if build_machine.system() != 'windows'
# C++ MPI not supported by MS-MPI used on AppVeyor.
mpicpp = dependency('mpi', language : 'cpp')
execpp = executable('execpp',
'main.cpp',
dependencies : [mpicpp])
test('MPI C++', execpp)
endif
if add_languages('fortran', required : false)
mpifort = dependency('mpi', language : 'fortran')
exef = executable('exef',
'main.f90',
dependencies : [mpifort])
test('MPI Fortran', exef)
endif
Loading…
Cancel
Save