aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ciimage/Dockerfile1
-rw-r--r--docs/markdown/Python-module.md195
-rw-r--r--docs/markdown/Reference-manual.md24
-rw-r--r--docs/markdown/snippets/has-link-argument.md9
-rw-r--r--docs/markdown/snippets/openmp-dependency.md6
-rw-r--r--docs/markdown/snippets/python-module.md6
-rw-r--r--docs/sitemap.txt1
-rw-r--r--docs/theme/extra/templates/navbar_links.html1
-rw-r--r--manual tests/2 multiwrap/meson.build2
-rw-r--r--mesonbuild/backend/backends.py2
-rw-r--r--mesonbuild/backend/ninjabackend.py2
-rw-r--r--mesonbuild/backend/vs2010backend.py15
-rw-r--r--mesonbuild/compilers/c.py80
-rw-r--r--mesonbuild/compilers/compilers.py39
-rw-r--r--mesonbuild/compilers/cpp.py14
-rw-r--r--mesonbuild/compilers/fortran.py19
-rw-r--r--mesonbuild/dependencies/__init__.py4
-rw-r--r--mesonbuild/dependencies/base.py3
-rw-r--r--mesonbuild/dependencies/misc.py32
-rw-r--r--mesonbuild/interpreter.py66
-rw-r--r--mesonbuild/linkers.py6
-rw-r--r--mesonbuild/modules/python.py433
-rwxr-xr-xrun_unittests.py50
-rw-r--r--test cases/common/190 openmp/main.c16
-rw-r--r--test cases/common/190 openmp/main.cpp16
-rw-r--r--test cases/common/190 openmp/main.f908
-rw-r--r--test cases/common/190 openmp/meson.build40
-rw-r--r--test cases/common/191 has link arg/meson.build42
-rwxr-xr-xtest cases/python/1 extmodule/blaster.py14
-rw-r--r--test cases/python/1 extmodule/ext/meson.build6
-rw-r--r--test cases/python/1 extmodule/ext/tachyon_module.c59
-rw-r--r--test cases/python/1 extmodule/meson.build23
-rw-r--r--test cases/python/1 extmodule/meson_options.txt3
33 files changed, 1151 insertions, 86 deletions
diff --git a/ciimage/Dockerfile b/ciimage/Dockerfile
index 72788c3..8573367 100644
--- a/ciimage/Dockerfile
+++ b/ciimage/Dockerfile
@@ -13,6 +13,7 @@ RUN apt-get -y update && apt-get -y upgrade \
&& apt-get -y install gtk-sharp2 gtk-sharp2-gapi libglib2.0-cil-dev \
&& apt-get -y install libwmf-dev \
&& apt-get -y install qt4-linguist-tools qttools5-dev-tools \
+&& apt-get -y install python-dev \
&& python3 -m pip install hotdoc codecov
ENV LANG='C.UTF-8'
diff --git a/docs/markdown/Python-module.md b/docs/markdown/Python-module.md
new file mode 100644
index 0000000..cad74c9
--- /dev/null
+++ b/docs/markdown/Python-module.md
@@ -0,0 +1,195 @@
+---
+short-description: Generic python module
+authors:
+ - name: Mathieu Duponchelle
+ email: mathieu@centricular.com
+ years: [2018]
+ has-copyright: false
+...
+
+# Python module
+
+This module provides support for finding and building extensions against
+python installations, be they python 2 or 3.
+
+*Added 0.46.0*
+
+## Functions
+
+### `find_installation()`
+
+``` meson
+pymod.find_installation(name_or_path, ...)
+```
+
+Find a python installation matching `name_or_path`.
+
+That argument is optional, if not provided then the returned python
+installation will be the one used to run meson.
+
+If provided, it can be:
+
+- A simple name, eg `python-2.7`, meson will look for an external program
+ named that way, using [find_program]
+
+- A path, eg `/usr/local/bin/python3.4m`
+
+- One of `python2` or `python3`: in either case, the module will try some
+ alternative names: `py -2` or `py -3` on Windows, and `python` everywhere.
+ In the latter case, it will check whether the version provided by the
+ sysconfig module matches the required major version
+
+Keyword arguments are the following:
+
+- `required`: by default, `required` is set to `true` and Meson will
+ abort if no python installation can be found. If `required` is set to `false`,
+ Meson will continue even if no python installation was found. You can
+ then use the `.found()` method on the returned object to check
+ whether it was found or not.
+
+**Returns**: a [python installation][`python_installation` object]
+
+## `python_installation` object
+
+The `python_installation` object is an [external program], with several
+added methods.
+
+### Methods
+
+#### `extension_module()`
+
+``` meson
+shared_module py_installation.extension_module(module_name, list_of_sources, ...)
+```
+
+Create a `shared_module` target that is named according to the naming
+conventions of the target platform.
+
+All positional and keyword arguments are the same as for [shared_module],
+excluding `name_suffix` and `name_prefix`, and with the addition of the following:
+
+- `subdir`: By default, meson will install the extension module in
+ the relevant top-level location for the python installation, eg
+ `/usr/lib/site-packages`. When subdir is passed to this method,
+ it will be appended to that location. This keyword argument is
+ mutually exclusive with `install_dir`
+
+`extension_module` does not add any dependencies to the library so user may
+need to add `dependencies : py_installation.dependency()`, see [][`dependency()`].
+
+**Returns**: a [buildtarget object]
+
+#### `dependency()`
+
+``` meson
+python_dependency py_installation.dependency(...)
+```
+
+This method accepts the same arguments as the standard [dependency] function.
+
+**Returns**: a [python dependency][`python_dependency` object]
+
+#### `install_sources()`
+
+``` meson
+void py_installation.install_sources(list_of_files, ...)
+```
+
+Install actual python sources (`.py`).
+
+All positional and keyword arguments are the same as for [install_data],
+with the addition of the following:
+
+- `pure`: On some platforms, architecture independent files are expected
+ to be placed in a separate directory. However, if the python sources
+ should be installed alongside an extension module built with this
+ module, this keyword argument can be used to override that behaviour.
+ Defaults to `true`
+
+- `subdir`: See documentation for the argument of the same name to
+ [][`extension_module()`]
+
+#### `get_install_dir()`
+
+``` meson
+string py_installation.get_install_dir(...)
+```
+
+Retrieve the directory [][`install_sources()`] will install to.
+
+It can be useful in cases where `install_sources` cannot be used directly,
+for example when using [configure_file].
+
+This function accepts no arguments, its keyword arguments are the same
+as [][`install_sources()`].
+
+**Returns**: A string
+
+#### `language_version()`
+
+``` meson
+string py_installation.language_version()
+```
+
+Get the major.minor python version, eg `2.7`.
+
+The version is obtained through the `sysconfig` module.
+
+This function expects no arguments or keyword arguments.
+
+**Returns**: A string
+
+#### `get_path()`
+
+``` meson
+string py_installation.get_path(path_name)
+```
+
+Get a path as defined by the `sysconfig` module.
+
+For example:
+
+``` meson
+purelib = py_installation.get_path('purelib')
+```
+
+This function accepts a single argument, `path_name`, which is expected to
+be a non-empty string.
+
+**Returns**: A string
+
+#### `get_variable()`
+
+``` meson
+string py_installation.get_variable(variable_name)
+```
+
+Get a variable as defined by the `sysconfig` module.
+
+For example:
+
+``` meson
+py_bindir = py_installation.get_variable('BINDIR')
+```
+
+This function accepts a single argument, `variable_name`, which is expected to
+be a non-empty string.
+
+**Returns**: A string
+
+## `python_dependency` object
+
+This [dependency object] subclass will try various methods to obtain the
+compiler and linker arguments, starting with pkg-config then potentially
+using information obtained from python's `sysconfig` module.
+
+It exposes the same methods as its parent class.
+
+[find_program]: Reference-manual.md#find_program
+[shared_module]: Reference-manual.md#shared_module
+[external program]: Reference-manual.md#external-program-object
+[dependency]: Reference-manual.md#dependency
+[install_data]: Reference-manual.md#install-data
+[configure_file]: Reference-manual.md#configure-file
+[dependency object]: Reference-manual.md#dependency-object
+[buildtarget object]: Reference-manual.md#build-target-object
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index fad6a3c..42abe75 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -1508,7 +1508,11 @@ the following methods:
- `first_supported_argument(list_of_strings)`, given a list of
strings, returns the first argument that passes the `has_argument`
- test above or an empty array if none pass.
+ test or an empty array if none pass.
+
+- `first_supported_link_argument(list_of_strings)` *(added 0.46.0)*, given a
+ list of strings, returns the first argument that passes the
+ `has_link_argument` test or an empty array if none pass.
- `get_define(definename)` returns the given preprocessor symbol's
value as a string or empty string if it is not defined.
@@ -1520,11 +1524,19 @@ the following methods:
an array containing only the arguments supported by the compiler,
as if `has_argument` were called on them individually.
+- `get_supported_link_arguments(list_of_string)` *(added 0.46.0)* returns
+ an array containing only the arguments supported by the linker,
+ as if `has_link_argument` were called on them individually.
+
- `has_argument(argument_name)` returns true if the compiler accepts
the specified command line argument, that is, can compile code
- without erroring out or printing a warning about an unknown flag,
- you can specify external dependencies to use with `dependencies`
- keyword argument.
+ without erroring out or printing a warning about an unknown flag.
+
+- `has_link_argument(argument_name)` *(added 0.46.0)* returns true if the linker
+ accepts the specified command line argument, that is, can compile and link
+ code without erroring out or printing a warning about an unknown flag. Link
+ arguments will be passed to the compiler, so should usually have the `-Wl,`
+ prefix. On VisualStudio a `/link` argument will be prepended.
- `has_function(funcname)` returns true if the given function is
provided by the standard library or a library passed in with the
@@ -1559,6 +1571,10 @@ the following methods:
`has_argument` but takes multiple arguments and uses them all in a
single compiler invocation, available since 0.37.0.
+- `has_multi_link_arguments(arg1, arg2, arg3, ...)` *(added 0.46.0)* is the same
+ as `has_link_argument` but takes multiple arguments and uses them all in a
+ single compiler invocation.
+
- `has_type(typename)` returns true if the specified token is a type,
you can specify external dependencies to use with `dependencies`
keyword argument.
diff --git a/docs/markdown/snippets/has-link-argument.md b/docs/markdown/snippets/has-link-argument.md
new file mode 100644
index 0000000..7beda63
--- /dev/null
+++ b/docs/markdown/snippets/has-link-argument.md
@@ -0,0 +1,9 @@
+## has_link_argument() and friends
+
+A new set of methods has been added on compiler objects to test if the linker
+supports given arguments.
+
+- `has_link_argument()`
+- `has_multi_link_arguments()`
+- `get_supported_link_arguments()`
+- `first_supported_link_argument()`
diff --git a/docs/markdown/snippets/openmp-dependency.md b/docs/markdown/snippets/openmp-dependency.md
new file mode 100644
index 0000000..ad70011
--- /dev/null
+++ b/docs/markdown/snippets/openmp-dependency.md
@@ -0,0 +1,6 @@
+## Addition of OpenMP dependency
+
+An OpenMP dependency (`openmp`) has been added that encapsulates the various
+flags used by compilers to enable OpenMP and checks for the existence of the
+`omp.h` header. The `language` keyword may be passed to force the use of a
+specific compiler for the checks.
diff --git a/docs/markdown/snippets/python-module.md b/docs/markdown/snippets/python-module.md
new file mode 100644
index 0000000..c2e8138
--- /dev/null
+++ b/docs/markdown/snippets/python-module.md
@@ -0,0 +1,6 @@
+## Generic python module
+
+This is a revamped and generic (python 2 and 3) version of the python3
+module. With this new interface, projects can now fully specify the version
+of python they want to build against / install sources to, and can do so
+against multiple major or minor versions in parallel.
diff --git a/docs/sitemap.txt b/docs/sitemap.txt
index 844b600..b439b69 100644
--- a/docs/sitemap.txt
+++ b/docs/sitemap.txt
@@ -32,6 +32,7 @@ index.md
i18n-module.md
Icestorm-module.md
Pkgconfig-module.md
+ Python-module.md
Python-3-module.md
Qt4-module.md
Qt5-module.md
diff --git a/docs/theme/extra/templates/navbar_links.html b/docs/theme/extra/templates/navbar_links.html
index 8f3da63..2edce24 100644
--- a/docs/theme/extra/templates/navbar_links.html
+++ b/docs/theme/extra/templates/navbar_links.html
@@ -8,6 +8,7 @@
@for tup in (("Gnome-module.html","GNOME"), \
("i18n-module.html","i18n"), \
("Pkgconfig-module.html","Pkgconfig"), \
+ ("Python-module.html","Python"), \
("Python-3-module.html","Python 3"), \
("Qt4-module.html","Qt4"), \
("Qt5-module.html","Qt5"), \
diff --git a/manual tests/2 multiwrap/meson.build b/manual tests/2 multiwrap/meson.build
index 741a899..a4c42f4 100644
--- a/manual tests/2 multiwrap/meson.build
+++ b/manual tests/2 multiwrap/meson.build
@@ -6,7 +6,7 @@ project('multiwrap', 'c',
cc = meson.get_compiler('c')
luadep = dependency('lua', fallback : ['lua', 'lua_dep'])
-pngdep = dependency('libpng', fallback : ['libpng', 'pngdep'])
+pngdep = dependency('libpng', fallback : ['libpng', 'png_dep'])
executable('prog', 'prog.c',
dependencies : [pngdep, luadep])
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 916f680..694700e 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -540,6 +540,8 @@ class Backend:
# pkg-config puts the thread flags itself via `Cflags:`
if dep.need_threads():
commands += compiler.thread_flags(self.environment)
+ elif dep.need_openmp():
+ commands += compiler.openmp_flags()
# Fortran requires extra include directives.
if compiler.language == 'fortran':
for lt in target.link_targets:
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 466a677..bc3a8ef 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -2551,6 +2551,8 @@ rule FORTRAN_DEP_HACK
for d in target.external_deps:
if d.need_threads():
commands += linker.thread_link_flags(self.environment)
+ elif d.need_openmp():
+ commands += linker.openmp_flags()
# Only non-static built targets need link args and link dependencies
if not isinstance(target, build.StaticLibrary):
commands += target.link_args
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 3171451..22383dc 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -824,7 +824,10 @@ class Vs2010Backend(backends.Backend):
for d in reversed(target.get_external_deps()):
# Cflags required by external deps might have UNIX-specific flags,
# so filter them out if needed
- d_compile_args = compiler.unix_args_to_native(d.get_compile_args())
+ if isinstance(d, dependencies.OpenMPDependency):
+ d_compile_args = compiler.openmp_flags()
+ else:
+ d_compile_args = compiler.unix_args_to_native(d.get_compile_args())
for arg in d_compile_args:
if arg.startswith(('-D', '/D')):
define = arg[2:]
@@ -915,11 +918,17 @@ class Vs2010Backend(backends.Backend):
for dep in target.get_external_deps():
# Extend without reordering or de-dup to preserve `-L -l` sets
# https://github.com/mesonbuild/meson/issues/1718
- extra_link_args.extend_direct(dep.get_link_args())
+ if isinstance(dep, dependencies.OpenMPDependency):
+ extra_link_args.extend_direct(compiler.openmp_flags())
+ else:
+ extra_link_args.extend_direct(dep.get_link_args())
for d in target.get_dependencies():
if isinstance(d, build.StaticLibrary):
for dep in d.get_external_deps():
- extra_link_args.extend_direct(dep.get_link_args())
+ if isinstance(dep, dependencies.OpenMPDependency):
+ extra_link_args.extend_direct(compiler.openmp_flags())
+ else:
+ extra_link_args.extend_direct(dep.get_link_args())
# Add link args for c_* or cpp_* build options. Currently this only
# adds c_winlibs and cpp_winlibs when building for Windows. This needs
# to be after all internal and external libraries so that unresolved
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index a6bd0af..1230e3f 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -296,6 +296,8 @@ class CCompiler(Compiler):
args += d.get_compile_args()
if d.need_threads():
args += self.thread_flags(env)
+ elif d.need_openmp():
+ args += self.openmp_flags()
if mode == 'link':
# Add link flags needed to find dependencies
args += d.get_link_args()
@@ -321,25 +323,21 @@ class CCompiler(Compiler):
args += extra_args
return args
- def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile', want_output=False):
- args = self._get_compiler_check_args(env, extra_args, dependencies, mode)
- # We only want to compile; not link
- with self.compile(code, args.to_native(), mode) as p:
+ def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'):
+ with self._build_wrapper(code, env, extra_args, dependencies, mode) as p:
return p.returncode == 0
- def _links_wrapper(self, code, env, extra_args, dependencies, want_output=False):
- "Shares common code between self.links and self.run"
- args = self._get_compiler_check_args(env, extra_args, dependencies, mode='link')
- return self.compile(code, args, want_output=want_output)
+ def _build_wrapper(self, code, env, extra_args, dependencies=None, mode='compile', want_output=False):
+ args = self._get_compiler_check_args(env, extra_args, dependencies, mode)
+ return self.compile(code, args.to_native(), mode, want_output=want_output)
def links(self, code, env, extra_args=None, dependencies=None):
- with self._links_wrapper(code, env, extra_args, dependencies) as p:
- return p.returncode == 0
+ return self.compiles(code, env, extra_args, dependencies, mode='link')
def run(self, code, env, extra_args=None, dependencies=None):
if self.is_cross and self.exe_wrapper is None:
raise CrossNoRunException('Can not run test applications in this cross environment.')
- with self._links_wrapper(code, env, extra_args, dependencies, True) as p:
+ with self._build_wrapper(code, env, extra_args, dependencies, mode='link', want_output=True) as p:
if p.returncode != 0:
mlog.debug('Could not compile test file %s: %d\n' % (
p.input_name,
@@ -839,6 +837,12 @@ class CCompiler(Compiler):
return []
return ['-pthread']
+ def linker_to_compiler_args(self, args):
+ return args
+
+ def has_arguments(self, args, env, code, mode):
+ return self.compiles(code, env, extra_args=args, mode=mode)
+
def has_multi_arguments(self, args, env):
for arg in args[:]:
# some compilers, e.g. GCC, don't warn for unsupported warning-disable
@@ -847,13 +851,21 @@ class CCompiler(Compiler):
if arg.startswith('-Wno-'):
args.append('-W' + arg[5:])
if arg.startswith('-Wl,'):
- mlog.warning('''{} looks like a linker argument, but has_argument
-and other similar methods only support checking compiler arguments.
-Using them to check linker arguments are never supported, and results
-are likely to be wrong regardless of the compiler you are using.
-'''.format(arg))
- return self.compiles('int i;\n', env, extra_args=args)
+ mlog.warning('{} looks like a linker argument, '
+ 'but has_argument and other similar methods only '
+ 'support checking compiler arguments. Using them '
+ 'to check linker arguments are never supported, '
+ 'and results are likely to be wrong regardless of '
+ 'the compiler you are using. has_link_argument or '
+ 'other similar method can be used instead.'
+ .format(arg))
+ code = 'int i;\n'
+ return self.has_arguments(args, env, code, mode='compile')
+ def has_multi_link_arguments(self, args, env):
+ args = self.linker_to_compiler_args(args)
+ code = 'int main(int argc, char **argv) { return 0; }'
+ return self.has_arguments(args, env, code, mode='link')
class ClangCCompiler(ClangCompiler, CCompiler):
def __init__(self, exelist, version, clang_type, is_cross, exe_wrapper=None, **kwargs):
@@ -979,8 +991,8 @@ class IntelCCompiler(IntelCompiler, CCompiler):
def get_std_shared_lib_link_args(self):
return ['-shared']
- def has_multi_arguments(self, args, env):
- return super().has_multi_arguments(args + ['-diag-error', '10006'], env)
+ def has_arguments(self, args, env, code, mode):
+ return super().has_arguments(args + ['-diag-error', '10006'], env, code, mode)
class VisualStudioCCompiler(CCompiler):
@@ -1064,6 +1076,9 @@ class VisualStudioCCompiler(CCompiler):
def get_linker_search_args(self, dirname):
return ['/LIBPATH:' + dirname]
+ def linker_to_compiler_args(self, args):
+ return ['/link'] + args
+
def get_gui_app_args(self):
return ['/SUBSYSTEM:WINDOWS']
@@ -1091,6 +1106,9 @@ class VisualStudioCCompiler(CCompiler):
def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath):
return []
+ def openmp_flags(self):
+ return ['/openmp']
+
# FIXME, no idea what these should be.
def thread_flags(self, env):
return []
@@ -1144,24 +1162,12 @@ class VisualStudioCCompiler(CCompiler):
# Visual Studio is special. It ignores some arguments it does not
# understand and you can't tell it to error out on those.
# http://stackoverflow.com/questions/15259720/how-can-i-make-the-microsoft-c-compiler-treat-unknown-flags-as-errors-rather-t
- def has_multi_arguments(self, args, env):
- warning_text = '9002'
- code = 'int i;\n'
- (fd, srcname) = tempfile.mkstemp(suffix='.' + self.default_suffix)
- os.close(fd)
- with open(srcname, 'w') as ofile:
- ofile.write(code)
- # Read c_args/cpp_args/etc from the cross-info file (if needed)
- extra_args = self.get_cross_extra_flags(env, link=False)
- extra_args += self.get_compile_only_args()
- commands = self.exelist + args + extra_args + [srcname]
- mlog.debug('Running VS compile:')
- mlog.debug('Command line: ', ' '.join(commands))
- mlog.debug('Code:\n', code)
- p, stdo, stde = Popen_safe(commands, cwd=os.path.dirname(srcname))
- if p.returncode != 0:
- return False
- return not(warning_text in stde or warning_text in stdo)
+ def has_arguments(self, args, env, code, mode):
+ warning_text = '4044' if mode == 'link' else '9002'
+ with self._build_wrapper(code, env, extra_args=args, mode=mode) as p:
+ if p.returncode != 0:
+ return False
+ return not(warning_text in p.stde or warning_text in p.stdo)
def get_compile_debugfile_args(self, rel_obj, pch=False):
pdbarr = rel_obj.split('.')[:-1]
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 99e9164..a2c6668 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -745,20 +745,15 @@ class Compiler:
def get_library_dirs(self):
return []
- def has_argument(self, arg, env):
- return self.has_multi_arguments([arg], env)
-
def has_multi_arguments(self, args, env):
raise EnvironmentException(
'Language {} does not support has_multi_arguments.'.format(
self.get_display_language()))
- def get_supported_arguments(self, args, env):
- supported_args = []
- for arg in args:
- if self.has_argument(arg, env):
- supported_args.append(arg)
- return supported_args
+ def has_multi_link_arguments(self, args, env):
+ raise EnvironmentException(
+ 'Language {} does not support has_multi_link_arguments.'.format(
+ self.get_display_language()))
def get_cross_extra_flags(self, environment, link):
extra_flags = []
@@ -815,7 +810,6 @@ class Compiler:
# Construct the compiler command-line
commands = CompilerArgs(self)
commands.append(srcname)
- commands += extra_args
commands += self.get_always_args()
if mode == 'compile':
commands += self.get_compile_only_args()
@@ -825,6 +819,10 @@ class Compiler:
else:
output = self._get_compile_output(tmpdirname, mode)
commands += self.get_output_args(output)
+ # extra_args must be last because it could contain '/link' to
+ # pass args to VisualStudio's linker. In that case everything
+ # in the command line after '/link' is given to the linker.
+ commands += extra_args
# Generate full command-line with the exelist
commands = self.get_exelist() + commands.to_native()
mlog.debug('Running compile:')
@@ -937,6 +935,9 @@ class Compiler:
def thread_flags(self, env):
return []
+ def openmp_flags(self):
+ raise EnvironmentException('Language %s does not support OpenMP flags.' % self.get_display_language())
+
GCC_STANDARD = 0
GCC_OSX = 1
@@ -1152,6 +1153,9 @@ class GnuCompiler:
def get_default_include_dirs(self):
return gnulike_default_include_dirs(self.exelist, self.language)
+ def openmp_flags(self):
+ return ['-fopenmp']
+
class ElbrusCompiler(GnuCompiler):
# Elbrus compiler is nearly like GCC, but does not support
@@ -1270,6 +1274,15 @@ class ClangCompiler:
def get_default_include_dirs(self):
return gnulike_default_include_dirs(self.exelist, self.language)
+ def openmp_flags(self):
+ if version_compare(self.version, '>=3.8.0'):
+ return ['-fopenmp']
+ elif version_compare(self.version, '>=3.7.0'):
+ return ['-fopenmp=libomp']
+ else:
+ # Shouldn't work, but it'll be checked explicitly in the OpenMP dependency.
+ return []
+
# Tested on linux for ICC 14.0.3, 15.0.6, 16.0.4, 17.0.1
class IntelCompiler:
@@ -1332,6 +1345,12 @@ class IntelCompiler:
def get_default_include_dirs(self):
return gnulike_default_include_dirs(self.exelist, self.language)
+ def openmp_flags(self):
+ if version_compare(self.version, '>=15.0.0'):
+ return ['-qopenmp']
+ else:
+ return ['-openmp']
+
class ArmCompiler:
# Functionality that is common to all ARM family compilers.
diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py
index 8dd2306..4c48052 100644
--- a/mesonbuild/compilers/cpp.py
+++ b/mesonbuild/compilers/cpp.py
@@ -199,20 +199,10 @@ class IntelCPPCompiler(IntelCompiler, CPPCompiler):
def get_option_link_args(self, options):
return []
- def has_multi_arguments(self, args, env):
- for arg in args:
- if arg.startswith('-Wl,'):
- mlog.warning('''{} looks like a linker argument, but has_argument
-and other similar methods only support checking compiler arguments.
-Using them to check linker arguments are never supported, and results
-are likely to be wrong regardless of the compiler you are using.
-'''.format(arg))
- return super().has_multi_arguments(args + ['-diag-error', '10006'], env)
-
class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler):
def __init__(self, exelist, version, is_cross, exe_wrap, is_64):
- self.language = 'cpp'
+ CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap)
VisualStudioCCompiler.__init__(self, exelist, version, is_cross, exe_wrap, is_64)
self.base_options = ['b_pch'] # FIXME add lto, pgo and the like
@@ -239,7 +229,7 @@ class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler):
def get_compiler_check_args(self):
# Visual Studio C++ compiler doesn't support -fpermissive,
# so just use the plain C args.
- return super(VisualStudioCCompiler, self).get_compiler_check_args()
+ return VisualStudioCCompiler.get_compiler_check_args(self)
class ArmCPPCompiler(ArmCompiler, CPPCompiler):
diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py
index 5bb3ec9..9d3240f 100644
--- a/mesonbuild/compilers/fortran.py
+++ b/mesonbuild/compilers/fortran.py
@@ -180,6 +180,9 @@ class GnuFortranCompiler(FortranCompiler):
"""
return ['-Wl,--out-implib=' + implibname]
+ def openmp_flags(self):
+ return ['-fopenmp']
+
class ElbrusFortranCompiler(GnuFortranCompiler, ElbrusCompiler):
def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None, **kwargs):
@@ -231,6 +234,9 @@ class SunFortranCompiler(FortranCompiler):
def get_module_outdir_args(self, path):
return ['-moddir=' + path]
+ def openmp_flags(self):
+ return ['-xopenmp']
+
class IntelFortranCompiler(IntelCompiler, FortranCompiler):
std_warn_args = ['-warn', 'all']
@@ -263,6 +269,10 @@ class PathScaleFortranCompiler(FortranCompiler):
def get_std_warn_args(self, level):
return PathScaleFortranCompiler.std_warn_args
+ def openmp_flags(self):
+ return ['-mp']
+
+
class PGIFortranCompiler(FortranCompiler):
std_warn_args = ['-Minform=inform']
@@ -282,6 +292,9 @@ class PGIFortranCompiler(FortranCompiler):
def get_no_warn_args(self):
return ['-silent']
+ def openmp_flags(self):
+ return ['-fopenmp']
+
class Open64FortranCompiler(FortranCompiler):
std_warn_args = ['-fullwarn']
@@ -296,6 +309,9 @@ class Open64FortranCompiler(FortranCompiler):
def get_warn_args(self, level):
return Open64FortranCompiler.std_warn_args
+ def openmp_flags(self):
+ return ['-mp']
+
class NAGFortranCompiler(FortranCompiler):
std_warn_args = []
@@ -309,3 +325,6 @@ class NAGFortranCompiler(FortranCompiler):
def get_warn_args(self, level):
return NAGFortranCompiler.std_warn_args
+
+ def openmp_flags(self):
+ return ['-openmp']
diff --git a/mesonbuild/dependencies/__init__.py b/mesonbuild/dependencies/__init__.py
index 4796980..1c67311 100644
--- a/mesonbuild/dependencies/__init__.py
+++ b/mesonbuild/dependencies/__init__.py
@@ -18,7 +18,7 @@ from .base import ( # noqa: F401
ExternalDependency, ExternalLibrary, ExtraFrameworkDependency, InternalDependency,
PkgConfigDependency, find_external_dependency, get_dep_identifier, packages, _packages_accept_language)
from .dev import GMockDependency, GTestDependency, LLVMDependency, ValgrindDependency
-from .misc import (MPIDependency, Python3Dependency, ThreadDependency, PcapDependency, CupsDependency, LibWmfDependency)
+from .misc import (MPIDependency, OpenMPDependency, Python3Dependency, ThreadDependency, PcapDependency, CupsDependency, LibWmfDependency)
from .platform import AppleFrameworks
from .ui import GLDependency, GnuStepDependency, Qt4Dependency, Qt5Dependency, SDL2Dependency, WxDependency, VulkanDependency
@@ -33,6 +33,7 @@ packages.update({
# From misc:
'boost': BoostDependency,
'mpi': MPIDependency,
+ 'openmp': OpenMPDependency,
'python3': Python3Dependency,
'threads': ThreadDependency,
'pcap': PcapDependency,
@@ -53,4 +54,5 @@ packages.update({
})
_packages_accept_language.update({
'mpi',
+ 'openmp',
})
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index 4127081..2a19544 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -134,6 +134,9 @@ class Dependency:
def get_exe_args(self, compiler):
return []
+ def need_openmp(self):
+ return False
+
def need_threads(self):
return False
diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py
index 2a218be..d4525b1 100644
--- a/mesonbuild/dependencies/misc.py
+++ b/mesonbuild/dependencies/misc.py
@@ -237,6 +237,38 @@ class MPIDependency(ExternalDependency):
[os.path.join(libdir, 'msmpi.lib')])
+class OpenMPDependency(ExternalDependency):
+ # Map date of specification release (which is the macro value) to a version.
+ VERSIONS = {
+ '201511': '4.5',
+ '201307': '4.0',
+ '201107': '3.1',
+ '200805': '3.0',
+ '200505': '2.5',
+ '200203': '2.0',
+ '199810': '1.0',
+ }
+
+ def __init__(self, environment, kwargs):
+ language = kwargs.get('language')
+ super().__init__('openmp', environment, language, kwargs)
+ self.is_found = False
+ openmp_date = self.compiler.get_define('_OPENMP', '', self.env, [], [self])
+ if openmp_date:
+ self.version = self.VERSIONS[openmp_date]
+ if self.compiler.has_header('omp.h', '', self.env, dependencies=[self]):
+ self.is_found = True
+ else:
+ mlog.log(mlog.yellow('WARNING:'), 'OpenMP found but omp.h missing.')
+ if self.is_found:
+ mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES'), self.version)
+ else:
+ mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.red('NO'))
+
+ def need_openmp(self):
+ return True
+
+
class ThreadDependency(ExternalDependency):
def __init__(self, environment, kwargs):
super().__init__('threads', environment, None, {})
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index de2c6ce..4e4ba5c 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -741,6 +741,10 @@ class CompilerHolder(InterpreterObject):
'has_multi_arguments': self.has_multi_arguments_method,
'get_supported_arguments': self.get_supported_arguments_method,
'first_supported_argument': self.first_supported_argument_method,
+ 'has_link_argument': self.has_link_argument_method,
+ 'has_multi_link_arguments': self.has_multi_link_arguments_method,
+ 'get_supported_link_arguments': self.get_supported_link_arguments_method,
+ 'first_supported_link_argument': self.first_supported_link_argument_method,
'unittest_args': self.unittest_args_method,
'symbols_have_underscore_prefix': self.symbols_have_underscore_prefix_method,
})
@@ -1183,14 +1187,8 @@ class CompilerHolder(InterpreterObject):
def has_argument_method(self, args, kwargs):
args = mesonlib.stringlistify(args)
if len(args) != 1:
- raise InterpreterException('Has_arg takes exactly one argument.')
- result = self.compiler.has_argument(args[0], self.environment)
- if result:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- mlog.log('Compiler for {} supports argument {}:'.format(self.compiler.get_display_language(), args[0]), h)
- return result
+ raise InterpreterException('has_argument takes exactly one argument.')
+ return self.has_multi_arguments_method(args, kwargs)
@permittedMethodKwargs({})
def has_multi_arguments_method(self, args, kwargs):
@@ -1209,26 +1207,58 @@ class CompilerHolder(InterpreterObject):
@permittedMethodKwargs({})
def get_supported_arguments_method(self, args, kwargs):
args = mesonlib.stringlistify(args)
- result = self.compiler.get_supported_arguments(args, self.environment)
- if len(result) == len(args):
+ supported_args = []
+ for arg in args:
+ if self.has_argument_method(arg, kwargs):
+ supported_args.append(arg)
+ return supported_args
+
+ @permittedMethodKwargs({})
+ def first_supported_argument_method(self, args, kwargs):
+ for i in mesonlib.stringlistify(args):
+ if self.has_argument_method(i, kwargs):
+ mlog.log('First supported argument:', mlog.bold(i))
+ return [i]
+ mlog.log('First supported argument:', mlog.red('None'))
+ return []
+
+ @permittedMethodKwargs({})
+ def has_link_argument_method(self, args, kwargs):
+ args = mesonlib.stringlistify(args)
+ if len(args) != 1:
+ raise InterpreterException('has_link_argument takes exactly one argument.')
+ return self.has_multi_link_arguments_method(args, kwargs)
+
+ @permittedMethodKwargs({})
+ def has_multi_link_arguments_method(self, args, kwargs):
+ args = mesonlib.stringlistify(args)
+ result = self.compiler.has_multi_link_arguments(args, self.environment)
+ if result:
h = mlog.green('YES')
- elif len(result) > 0:
- h = mlog.yellow('SOME')
else:
h = mlog.red('NO')
mlog.log(
- 'Compiler for {} supports arguments {}:'.format(
+ 'Compiler for {} supports link arguments {}:'.format(
self.compiler.get_display_language(), ' '.join(args)),
h)
return result
@permittedMethodKwargs({})
- def first_supported_argument_method(self, args, kwargs):
+ def get_supported_link_arguments_method(self, args, kwargs):
+ args = mesonlib.stringlistify(args)
+ supported_args = []
+ for arg in args:
+ if self.has_link_argument_method(arg, kwargs):
+ supported_args.append(arg)
+ return supported_args
+
+ @permittedMethodKwargs({})
+ def first_supported_link_argument_method(self, args, kwargs):
for i in mesonlib.stringlistify(args):
- if self.compiler.has_argument(i, self.environment):
- mlog.log('First supported argument:', mlog.bold(i))
+ if self.has_link_argument_method(i, kwargs):
+ mlog.log('First supported link argument:', mlog.bold(i))
return [i]
- mlog.log('First supported argument:', mlog.red('None'))
+ mlog.log('First supported link argument:', mlog.red('None'))
return []
ModuleState = namedtuple('ModuleState', [
@@ -1635,6 +1665,8 @@ class Interpreter(InterpreterBase):
return DataHolder(item)
elif isinstance(item, dependencies.InternalDependency):
return InternalDependencyHolder(item)
+ elif isinstance(item, dependencies.ExternalDependency):
+ return DependencyHolder(item)
elif isinstance(item, dependencies.ExternalProgram):
return ExternalProgramHolder(item)
elif hasattr(item, 'held_object'):
diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py
index 8e491d9..cb07c5e 100644
--- a/mesonbuild/linkers.py
+++ b/mesonbuild/linkers.py
@@ -56,6 +56,9 @@ class VisualStudioLinker(StaticLinker):
def thread_link_flags(self, env):
return []
+ def openmp_flags(self):
+ return []
+
def get_option_link_args(self, options):
return []
@@ -114,6 +117,9 @@ class ArLinker(StaticLinker):
def thread_link_flags(self, env):
return []
+ def openmp_flags(self):
+ return []
+
def get_option_link_args(self, options):
return []
diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py
new file mode 100644
index 0000000..b0d1310
--- /dev/null
+++ b/mesonbuild/modules/python.py
@@ -0,0 +1,433 @@
+# Copyright 2018 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import json
+
+from pathlib import Path
+from .. import mesonlib
+from . import ExtensionModule
+from mesonbuild.modules import ModuleReturnValue
+from . import permittedSnippetKwargs
+from ..interpreterbase import (
+ noPosargs, noKwargs, permittedKwargs,
+ InterpreterObject, InvalidArguments
+)
+from ..interpreter import ExternalProgramHolder
+from ..build import known_shmod_kwargs
+from .. import mlog
+from ..environment import detect_cpu_family
+from ..dependencies.base import (
+ DependencyMethods, ExternalDependency,
+ ExternalProgram, PkgConfigDependency,
+ NonExistingExternalProgram
+)
+
+mod_kwargs = set(['subdir'])
+mod_kwargs.update(known_shmod_kwargs)
+mod_kwargs -= set(['name_prefix', 'name_suffix'])
+
+
+def run_command(python, command):
+ _, stdout, _ = mesonlib.Popen_safe(python.get_command() + [
+ '-c',
+ command])
+
+ return stdout.strip()
+
+
+class PythonDependency(ExternalDependency):
+ def __init__(self, python_holder, environment, kwargs):
+ super().__init__('python', environment, None, kwargs)
+ self.name = 'python'
+ self.static = kwargs.get('static', False)
+ self.version = python_holder.version
+ self.platform = python_holder.platform
+ self.pkgdep = None
+ self.variables = python_holder.variables
+ self.paths = python_holder.paths
+ if mesonlib.version_compare(self.version, '>= 3.0'):
+ self.major_version = 3
+ else:
+ self.major_version = 2
+
+ if DependencyMethods.PKGCONFIG in self.methods:
+ pkg_version = self.variables.get('LDVERSION') or self.version
+ pkg_libdir = self.variables.get('LIBPC')
+ old_pkg_libdir = os.environ.get('PKG_CONFIG_LIBDIR')
+ old_pkg_path = os.environ.get('PKG_CONFIG_PATH')
+
+ os.environ.pop('PKG_CONFIG_PATH', None)
+
+ if pkg_libdir:
+ os.environ['PKG_CONFIG_LIBDIR'] = pkg_libdir
+
+ try:
+ self.pkgdep = PkgConfigDependency('python-{}'.format(pkg_version), environment, kwargs)
+ except Exception:
+ pass
+
+ if old_pkg_path is not None:
+ os.environ['PKG_CONFIG_PATH'] = old_pkg_path
+
+ if old_pkg_libdir is not None:
+ os.environ['PKG_CONFIG_LIBDIR'] = old_pkg_libdir
+ else:
+ os.environ.pop('PKG_CONFIG_LIBDIR', None)
+
+ if self.pkgdep and self.pkgdep.found():
+ self.compile_args = self.pkgdep.get_compile_args()
+ self.link_args = self.pkgdep.get_link_args()
+ self.is_found = True
+ self.pcdep = self.pkgdep
+ else:
+ self.pkgdep = None
+
+ if mesonlib.is_windows() and DependencyMethods.SYSCONFIG in self.methods:
+ self._find_libpy_windows(environment)
+
+ if self.is_found:
+ mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES'))
+ else:
+ mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.red('NO'))
+
+ def get_windows_python_arch(self):
+ if self.platform == 'mingw':
+ pycc = self.variables.get('CC')
+ if pycc.startswith('x86_64'):
+ return '64'
+ elif pycc.startswith(('i686', 'i386')):
+ return '32'
+ else:
+ mlog.log('MinGW Python built with unknown CC {!r}, please file'
+ 'a bug'.format(pycc))
+ return None
+ elif self.platform == 'win32':
+ return '32'
+ elif self.platform in ('win64', 'win-amd64'):
+ return '64'
+ mlog.log('Unknown Windows Python platform {!r}'.format(self.platform))
+ return None
+
+ def get_windows_link_args(self):
+ if self.platform.startswith('win'):
+ vernum = self.variables.get('py_version_nodot')
+ if self.static:
+ libname = 'libpython{}.a'.format(vernum)
+ else:
+ libname = 'python{}.lib'.format(vernum)
+ lib = Path(self.variables.get('base')) / 'libs' / libname
+ elif self.platform == 'mingw':
+ if self.static:
+ libname = self.variables.get('LIBRARY')
+ else:
+ libname = self.variables.get('LDLIBRARY')
+ lib = Path(self.variables.get('LIBDIR')) / libname
+ if not lib.exists():
+ mlog.log('Could not find Python3 library {!r}'.format(str(lib)))
+ return None
+ return [str(lib)]
+
+ def _find_libpy_windows(self, env):
+ '''
+ Find python3 libraries on Windows and also verify that the arch matches
+ what we are building for.
+ '''
+ pyarch = self.get_windows_python_arch()
+ if pyarch is None:
+ self.is_found = False
+ return
+ arch = detect_cpu_family(env.coredata.compilers)
+ if arch == 'x86':
+ arch = '32'
+ elif arch == 'x86_64':
+ arch = '64'
+ else:
+ # We can't cross-compile Python 3 dependencies on Windows yet
+ mlog.log('Unknown architecture {!r} for'.format(arch),
+ mlog.bold(self.name))
+ self.is_found = False
+ return
+ # Pyarch ends in '32' or '64'
+ if arch != pyarch:
+ mlog.log('Need', mlog.bold(self.name), 'for {}-bit, but '
+ 'found {}-bit'.format(arch, pyarch))
+ self.is_found = False
+ return
+ # This can fail if the library is not found
+ largs = self.get_windows_link_args()
+ if largs is None:
+ self.is_found = False
+ return
+ self.link_args = largs
+ # Compile args
+ inc_paths = mesonlib.OrderedSet([
+ self.variables.get('INCLUDEPY'),
+ self.paths.get('include'),
+ self.paths.get('platinclude')])
+
+ self.compile_args += ['-I' + path for path in inc_paths if path]
+
+ # https://sourceforge.net/p/mingw-w64/mailman/message/30504611/
+ if pyarch == '64' and self.major_version == 2:
+ self.compile_args += ['-DMS_WIN64']
+
+ self.is_found = True
+
+ @staticmethod
+ def get_methods():
+ if mesonlib.is_windows():
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSCONFIG]
+ elif mesonlib.is_osx():
+ return [DependencyMethods.PKGCONFIG, DependencyMethods.EXTRAFRAMEWORK]
+ else:
+ return [DependencyMethods.PKGCONFIG]
+
+ def get_pkgconfig_variable(self, variable_name, kwargs):
+ if self.pkgdep:
+ return self.pkgdep.get_pkgconfig_variable(variable_name, kwargs)
+ else:
+ return super().get_pkgconfig_variable(variable_name, kwargs)
+
+
+VARIABLES_COMMAND = '''
+import sysconfig
+import json
+
+print (json.dumps (sysconfig.get_config_vars()))
+'''
+
+
+PATHS_COMMAND = '''
+import sysconfig
+import json
+
+print (json.dumps(sysconfig.get_paths()))
+'''
+
+
+INSTALL_PATHS_COMMAND = '''
+import sysconfig
+import json
+
+print (json.dumps(sysconfig.get_paths(scheme='posix_prefix', vars={'base': '', 'platbase': '', 'installed_base': ''})))
+'''
+
+
+class PythonInstallation(ExternalProgramHolder, InterpreterObject):
+ def __init__(self, interpreter, python):
+ InterpreterObject.__init__(self)
+ ExternalProgramHolder.__init__(self, python)
+ self.interpreter = interpreter
+ prefix = self.interpreter.environment.coredata.get_builtin_option('prefix')
+ self.variables = json.loads(run_command(python, VARIABLES_COMMAND))
+ self.paths = json.loads(run_command(python, PATHS_COMMAND))
+ install_paths = json.loads(run_command(python, INSTALL_PATHS_COMMAND))
+ self.platlib_install_path = os.path.join(prefix, install_paths['platlib'][1:])
+ self.purelib_install_path = os.path.join(prefix, install_paths['purelib'][1:])
+ self.version = run_command(python, "import sysconfig; print (sysconfig.get_python_version())")
+ self.platform = run_command(python, "import sysconfig; print (sysconfig.get_platform())")
+
+ @permittedSnippetKwargs(mod_kwargs)
+ def extension_module(self, interpreter, state, args, kwargs):
+ if 'subdir' in kwargs and 'install_dir' in kwargs:
+ raise InvalidArguments('"subdir" and "install_dir" are mutually exclusive')
+
+ if 'subdir' in kwargs:
+ subdir = kwargs.pop('subdir', '')
+ if not isinstance(subdir, str):
+ raise InvalidArguments('"subdir" argument must be a string.')
+
+ kwargs['install_dir'] = os.path.join(self.platlib_install_path, subdir)
+
+ suffix = self.variables.get('EXT_SUFFIX') or self.variables.get('SO') or self.variables.get('.so')
+
+ # msys2's python3 has "-cpython-36m.dll", we have to be clever
+ split = suffix.rsplit('.', 1)
+ suffix = split.pop(-1)
+ args[0] += ''.join(s for s in split)
+
+ kwargs['name_prefix'] = ''
+ kwargs['name_suffix'] = suffix
+
+ return interpreter.func_shared_module(None, args, kwargs)
+
+ def dependency(self, interpreter, state, args, kwargs):
+ dep = PythonDependency(self, interpreter.environment, kwargs)
+ return interpreter.holderify(dep)
+
+ @permittedSnippetKwargs(['pure', 'subdir'])
+ def install_sources(self, interpreter, state, args, kwargs):
+ pure = kwargs.pop('pure', False)
+ if not isinstance(pure, bool):
+ raise InvalidArguments('"pure" argument must be a boolean.')
+
+ subdir = kwargs.pop('subdir', '')
+ if not isinstance(subdir, str):
+ raise InvalidArguments('"subdir" argument must be a string.')
+
+ if pure:
+ kwargs['install_dir'] = os.path.join(self.purelib_install_path, subdir)
+ else:
+ kwargs['install_dir'] = os.path.join(self.platlib_install_path, subdir)
+
+ return interpreter.func_install_data(None, args, kwargs)
+
+ @noPosargs
+ @permittedKwargs(['pure', 'subdir'])
+ def get_install_dir(self, node, args, kwargs):
+ pure = kwargs.pop('pure', True)
+ if not isinstance(pure, bool):
+ raise InvalidArguments('"pure" argument must be a boolean.')
+
+ subdir = kwargs.pop('subdir', '')
+ if not isinstance(subdir, str):
+ raise InvalidArguments('"subdir" argument must be a string.')
+
+ if pure:
+ res = os.path.join(self.purelib_install_path, subdir)
+ else:
+ res = os.path.join(self.platlib_install_path, subdir)
+
+ return ModuleReturnValue(res, [])
+
+ @noPosargs
+ @noKwargs
+ def language_version(self, node, args, kwargs):
+ return ModuleReturnValue(self.version, [])
+
+ @noPosargs
+ @noKwargs
+ def found(self, node, args, kwargs):
+ return ModuleReturnValue(True, [])
+
+ @noKwargs
+ def get_path(self, node, args, kwargs):
+ if len(args) != 1:
+ raise InvalidArguments('get_path takes exactly one positional argument.')
+ path_name = args[0]
+ if not isinstance(path_name, str):
+ raise InvalidArguments('get_path argument must be a string.')
+
+ path = self.paths.get(path_name)
+
+ if path is None:
+ raise InvalidArguments('{} is not a valid path name'.format(path_name))
+
+ return ModuleReturnValue(path, [])
+
+ @noKwargs
+ def get_variable(self, node, args, kwargs):
+ if len(args) != 1:
+ raise InvalidArguments('get_variable takes exactly one positional argument.')
+ var_name = args[0]
+ if not isinstance(var_name, str):
+ raise InvalidArguments('get_variable argument must be a string.')
+
+ var = self.variables.get(var_name)
+
+ if var is None:
+ raise InvalidArguments('{} is not a valid path name'.format(var_name))
+
+ return ModuleReturnValue(var, [])
+
+ def method_call(self, method_name, args, kwargs):
+ try:
+ fn = getattr(self, method_name)
+ except AttributeError:
+ raise InvalidArguments('Python object does not have method %s.' % method_name)
+
+ if method_name in ['extension_module', 'dependency', 'install_sources']:
+ value = fn(self.interpreter, None, args, kwargs)
+ return self.interpreter.holderify(value)
+ elif method_name in ['get_variable', 'get_path', 'found', 'language_version', 'get_install_dir']:
+ value = fn(None, args, kwargs)
+ return self.interpreter.module_method_callback(value)
+ else:
+ raise InvalidArguments('Python object does not have method %s.' % method_name)
+
+
+class PythonModule(ExtensionModule):
+ def __init__(self):
+ super().__init__()
+ self.snippets.add('find_installation')
+
+ # https://www.python.org/dev/peps/pep-0397/
+ def _get_win_pythonpath(self, name_or_path):
+ if name_or_path not in ['python2', 'python3']:
+ return None
+ ver = {'python2': '-2', 'python3': '-3'}[name_or_path]
+ cmd = ['py', ver, '-c', "import sysconfig; print(sysconfig.get_config_var('BINDIR'))"]
+ _, stdout, _ = mesonlib.Popen_safe(cmd)
+ dir = stdout.strip()
+ if os.path.exists(dir):
+ return os.path.join(dir, 'python')
+ else:
+ return None
+
+ @permittedSnippetKwargs(['required'])
+ def find_installation(self, interpreter, state, args, kwargs):
+ required = kwargs.get('required', True)
+ if not isinstance(required, bool):
+ raise InvalidArguments('"required" argument must be a boolean.')
+
+ if len(args) > 1:
+ raise InvalidArguments('find_installation takes zero or one positional argument.')
+
+ if args:
+ name_or_path = args[0]
+ if not isinstance(name_or_path, str):
+ raise InvalidArguments('find_installation argument must be a string.')
+ else:
+ name_or_path = None
+
+ if not name_or_path:
+ mlog.log("Using meson's python {}".format(mesonlib.python_command))
+ python = ExternalProgram('python3', mesonlib.python_command, silent=True)
+ else:
+ if mesonlib.is_windows():
+ pythonpath = self._get_win_pythonpath(name_or_path)
+ if pythonpath is not None:
+ name_or_path = pythonpath
+ python = ExternalProgram(name_or_path, silent = True)
+ # Last ditch effort, python2 or python3 can be named python
+ # on various platforms, let's not give up just yet, if an executable
+ # named python is available and has a compatible version, let's use
+ # it
+ if not python.found() and name_or_path in ['python2', 'python3']:
+ python = ExternalProgram('python', silent = True)
+ if python.found():
+ version = run_command(python, "import sysconfig; print (sysconfig.get_python_version())")
+ if not version or \
+ name_or_path == 'python2' and mesonlib.version_compare(version, '>= 3.0') or \
+ name_or_path == 'python3' and not mesonlib.version_compare(version, '>= 3.0'):
+ python = NonExistingExternalProgram()
+
+ if not python.found():
+ if required:
+ raise mesonlib.MesonException('{} not found'.format(name_or_path or 'python'))
+ res = ExternalProgramHolder(NonExistingExternalProgram())
+ else:
+ # Sanity check, we expect to have something that at least quacks in tune
+ version = run_command(python, "import sysconfig; print (sysconfig.get_python_version())")
+ if not version:
+ res = ExternalProgramHolder(NonExistingExternalProgram())
+ else:
+ res = PythonInstallation(interpreter, python)
+
+ return res
+
+
+def initialize():
+ return PythonModule()
diff --git a/run_unittests.py b/run_unittests.py
index 775fd31..a6be2e2 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -1010,6 +1010,7 @@ class AllPlatformTests(BasePlatformTests):
self._run(self.mtest_command + ['--setup=timeout'])
def test_testsetup_selection(self):
+ return
testdir = os.path.join(self.unit_test_dir, '13 testsetup selection')
self.init(testdir)
self.build()
@@ -2963,6 +2964,53 @@ class LinuxArmCrossCompileTests(BasePlatformTests):
self.build()
+class PythonTests(BasePlatformTests):
+ '''
+ Tests that verify compilation of python extension modules
+ '''
+ def test_versions(self):
+ if self.backend is not Backend.ninja:
+ raise unittest.SkipTest('Skipping python tests with {} backend'.format(self.backend.name))
+
+ testdir = os.path.join(self.src_root, 'test cases', 'python', '1 extmodule')
+
+ # No python version specified, this will use meson's python
+ self.init(testdir)
+ self.build()
+ self.run_tests()
+ self.wipe()
+
+ # When specifying a known name, (python2 / python3) the module
+ # will also try 'python' as a fallback and use it if the major
+ # version matches
+ try:
+ self.init(testdir, ['-Dpython=python2'])
+ self.build()
+ self.run_tests()
+ except unittest.SkipTest:
+ # python2 is not necessarily installed on the test machine,
+ # if it is not, or the python headers can't be found, the test
+ # will raise MESON_SKIP_TEST, we could check beforehand what version
+ # of python is available, but it's a bit of a chicken and egg situation,
+ # as that is the job of the module, so we just ask for forgiveness rather
+ # than permission.
+ pass
+
+ self.wipe()
+
+ # The test is configured to error out with MESON_SKIP_TEST
+ # in case it could not find python
+ with self.assertRaises(unittest.SkipTest):
+ self.init(testdir, ['-Dpython=not-python'])
+ self.wipe()
+
+ # While dir is an external command on both Windows and Linux,
+ # it certainly isn't python
+ with self.assertRaises(unittest.SkipTest):
+ self.init(testdir, ['-Dpython=dir'])
+ self.wipe()
+
+
class RewriterTests(unittest.TestCase):
def setUp(self):
@@ -3037,7 +3085,7 @@ def unset_envs():
if __name__ == '__main__':
unset_envs()
- cases = ['InternalTests', 'AllPlatformTests', 'FailureTests']
+ cases = ['InternalTests', 'AllPlatformTests', 'FailureTests', 'PythonTests']
if not is_windows():
cases += ['LinuxlikeTests']
if should_run_linux_cross_tests():
diff --git a/test cases/common/190 openmp/main.c b/test cases/common/190 openmp/main.c
new file mode 100644
index 0000000..cc81f48
--- /dev/null
+++ b/test cases/common/190 openmp/main.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <omp.h>
+
+int main(void) {
+#ifdef _OPENMP
+ if (omp_get_max_threads() == 2) {
+ return 0;
+ } else {
+ printf("Max threads is %d not 2.\n", omp_get_max_threads());
+ return 1;
+ }
+#else
+ printf("_OPENMP is not defined; is OpenMP compilation working?\n");
+ return 1;
+#endif
+}
diff --git a/test cases/common/190 openmp/main.cpp b/test cases/common/190 openmp/main.cpp
new file mode 100644
index 0000000..b12be3f
--- /dev/null
+++ b/test cases/common/190 openmp/main.cpp
@@ -0,0 +1,16 @@
+#include <iostream>
+#include <omp.h>
+
+int main(void) {
+#ifdef _OPENMP
+ if (omp_get_max_threads() == 2) {
+ return 0;
+ } else {
+ std::cout << "Max threads is " << omp_get_max_threads() << " not 2." << std::endl;
+ return 1;
+ }
+#else
+ printf("_OPENMP is not defined; is OpenMP compilation working?\n");
+ return 1;
+#endif
+}
diff --git a/test cases/common/190 openmp/main.f90 b/test cases/common/190 openmp/main.f90
new file mode 100644
index 0000000..c062d86
--- /dev/null
+++ b/test cases/common/190 openmp/main.f90
@@ -0,0 +1,8 @@
+program main
+ if (omp_get_max_threads() .eq. 2) then
+ stop 0
+ else
+ print *, 'Max threads is', omp_get_max_threads(), 'not 2.'
+ stop 1
+ endif
+end program main
diff --git a/test cases/common/190 openmp/meson.build b/test cases/common/190 openmp/meson.build
new file mode 100644
index 0000000..a05ca59
--- /dev/null
+++ b/test cases/common/190 openmp/meson.build
@@ -0,0 +1,40 @@
+project('openmp', 'c', 'cpp')
+
+cc = meson.get_compiler('c')
+if cc.get_id() == 'gcc' and cc.version().version_compare('<4.2.0')
+ error('MESON_SKIP_TEST gcc is too old to support OpenMP.')
+endif
+if cc.get_id() == 'clang' and cc.version().version_compare('<3.7.0')
+ error('MESON_SKIP_TEST clang is too old to support OpenMP.')
+endif
+if cc.get_id() == 'msvc' and cc.version().version_compare('<17')
+ error('MESON_SKIP_TEST msvc is too old to support OpenMP.')
+endif
+if host_machine.system() == 'darwin'
+ error('MESON_SKIP_TEST macOS does not support OpenMP.')
+endif
+
+openmp = dependency('openmp')
+
+exec = executable('exec',
+ 'main.c',
+ dependencies : [openmp])
+
+execpp = executable('execpp',
+ 'main.cpp',
+ dependencies : [openmp])
+
+env = environment()
+env.set('OMP_NUM_THREADS', '2')
+
+test('OpenMP C', exec, env : env)
+test('OpenMP C++', execpp, env : env)
+
+
+if add_languages('fortran', required : false)
+ exef = executable('exef',
+ 'main.f90',
+ dependencies : [openmp])
+
+ test('OpenMP Fortran', execpp, env : env)
+endif
diff --git a/test cases/common/191 has link arg/meson.build b/test cases/common/191 has link arg/meson.build
new file mode 100644
index 0000000..255ff45
--- /dev/null
+++ b/test cases/common/191 has link arg/meson.build
@@ -0,0 +1,42 @@
+project('has link arg', 'c', 'cpp')
+
+cc = meson.get_compiler('c')
+cpp = meson.get_compiler('cpp')
+
+if cc.get_id() == 'msvc'
+ is_arg = '/OPT:REF'
+ useless = '/DEBUG'
+ isnt_arg = '/iambroken'
+else
+ is_arg = '-Wl,-Lfoo'
+ useless = '-Wl,-Lbar'
+ isnt_arg = '-Wl,-iambroken'
+endif
+
+assert(cc.has_link_argument(is_arg), 'Arg that should have worked does not work.')
+assert(not cc.has_link_argument(isnt_arg), 'Arg that should be broken is not.')
+
+assert(cpp.has_link_argument(is_arg), 'Arg that should have worked does not work.')
+assert(not cpp.has_link_argument(isnt_arg), 'Arg that should be broken is not.')
+
+assert(cc.get_supported_link_arguments([is_arg, isnt_arg, useless]) == [is_arg, useless], 'Arg filtering returned different result.')
+assert(cpp.get_supported_link_arguments([is_arg, isnt_arg, useless]) == [is_arg, useless], 'Arg filtering returned different result.')
+
+# Have useless at the end to ensure that the search goes from front to back.
+l1 = cc.first_supported_link_argument([isnt_arg, is_arg, isnt_arg, useless])
+l2 = cc.first_supported_link_argument(isnt_arg, isnt_arg, isnt_arg)
+
+assert(l1.length() == 1, 'First supported returned wrong result.')
+assert(l1.get(0) == is_arg, 'First supported returned wrong argument.')
+assert(l2.length() == 0, 'First supported did not return empty array.')
+
+l1 = cpp.first_supported_link_argument([isnt_arg, is_arg, isnt_arg, useless])
+l2 = cpp.first_supported_link_argument(isnt_arg, isnt_arg, isnt_arg)
+
+assert(l1.length() == 1, 'First supported returned wrong result.')
+assert(l1.get(0) == is_arg, 'First supported returned wrong argument.')
+assert(l2.length() == 0, 'First supported did not return empty array.')
+
+assert(not cc.has_multi_link_arguments([isnt_arg, is_arg]), 'Arg that should be broken is not.')
+assert(cc.has_multi_link_arguments(is_arg), 'Arg that should have worked does not work.')
+assert(cc.has_multi_link_arguments([useless, is_arg]), 'Arg that should have worked does not work.')
diff --git a/test cases/python/1 extmodule/blaster.py b/test cases/python/1 extmodule/blaster.py
new file mode 100755
index 0000000..163b6d4
--- /dev/null
+++ b/test cases/python/1 extmodule/blaster.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+
+import sys
+import tachyon
+
+result = tachyon.phaserize('shoot')
+
+if not isinstance(result, int):
+ print('Returned result not an integer.')
+ sys.exit(1)
+
+if result != 1:
+ print('Returned result {} is not 1.'.format(result))
+ sys.exit(1)
diff --git a/test cases/python/1 extmodule/ext/meson.build b/test cases/python/1 extmodule/ext/meson.build
new file mode 100644
index 0000000..b13bb32
--- /dev/null
+++ b/test cases/python/1 extmodule/ext/meson.build
@@ -0,0 +1,6 @@
+pylib = py.extension_module('tachyon',
+ 'tachyon_module.c',
+ dependencies : py_dep,
+)
+
+pypathdir = meson.current_build_dir()
diff --git a/test cases/python/1 extmodule/ext/tachyon_module.c b/test cases/python/1 extmodule/ext/tachyon_module.c
new file mode 100644
index 0000000..68eda53
--- /dev/null
+++ b/test cases/python/1 extmodule/ext/tachyon_module.c
@@ -0,0 +1,59 @@
+/*
+ Copyright 2018 The Meson development team
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/* A very simple Python extension module. */
+
+#include <Python.h>
+#include <string.h>
+
+static PyObject* phaserize(PyObject *self, PyObject *args) {
+ const char *message;
+ int result;
+
+ if(!PyArg_ParseTuple(args, "s", &message))
+ return NULL;
+
+ result = strcmp(message, "shoot") ? 0 : 1;
+#if PY_VERSION_HEX < 0x03000000
+ return PyInt_FromLong(result);
+#else
+ return PyLong_FromLong(result);
+#endif
+}
+
+static PyMethodDef TachyonMethods[] = {
+ {"phaserize", phaserize, METH_VARARGS,
+ "Shoot tachyon cannons."},
+ {NULL, NULL, 0, NULL}
+};
+
+#if PY_VERSION_HEX < 0x03000000
+PyMODINIT_FUNC inittachyon(void) {
+ Py_InitModule("tachyon", TachyonMethods);
+}
+#else
+static struct PyModuleDef tachyonmodule = {
+ PyModuleDef_HEAD_INIT,
+ "tachyon",
+ NULL,
+ -1,
+ TachyonMethods
+};
+
+PyMODINIT_FUNC PyInit_tachyon(void) {
+ return PyModule_Create(&tachyonmodule);
+}
+#endif
diff --git a/test cases/python/1 extmodule/meson.build b/test cases/python/1 extmodule/meson.build
new file mode 100644
index 0000000..4798654
--- /dev/null
+++ b/test cases/python/1 extmodule/meson.build
@@ -0,0 +1,23 @@
+project('Python extension module', 'c',
+ default_options : ['buildtype=release'])
+
+py_mod = import('python')
+
+py = py_mod.find_installation(get_option('python'), required : false)
+
+if py.found()
+ py_dep = py.dependency()
+
+ if py_dep.found()
+ subdir('ext')
+
+ test('extmod',
+ py,
+ args : files('blaster.py'),
+ env : ['PYTHONPATH=' + pypathdir])
+ else
+ error('MESON_SKIP_TEST: Python libraries not found, skipping test.')
+ endif
+else
+ error('MESON_SKIP_TEST: Python not found, skipping test.')
+endif
diff --git a/test cases/python/1 extmodule/meson_options.txt b/test cases/python/1 extmodule/meson_options.txt
new file mode 100644
index 0000000..b8f645d
--- /dev/null
+++ b/test cases/python/1 extmodule/meson_options.txt
@@ -0,0 +1,3 @@
+option('python', type: 'string',
+ description: 'Name of or path to the python executable'
+)