aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ciimage/Dockerfile1
-rw-r--r--docs/markdown/Dependencies.md19
-rw-r--r--docs/markdown/howtox.md17
-rw-r--r--docs/markdown/snippets/cmake_module_path.md9
-rw-r--r--docs/markdown/snippets/hdf5.md3
-rw-r--r--mesonbuild/ast/interpreter.py10
-rw-r--r--mesonbuild/backend/ninjabackend.py5
-rw-r--r--mesonbuild/backend/vs2010backend.py5
-rw-r--r--mesonbuild/compilers/c.py89
-rw-r--r--mesonbuild/compilers/fortran.py4
-rw-r--r--mesonbuild/compilers/vala.py6
-rw-r--r--mesonbuild/coredata.py2
-rw-r--r--mesonbuild/dependencies/__init__.py4
-rw-r--r--mesonbuild/dependencies/base.py173
-rw-r--r--mesonbuild/dependencies/misc.py51
-rw-r--r--mesonbuild/dependencies/platform.py18
-rw-r--r--mesonbuild/dependencies/ui.py19
-rw-r--r--mesonbuild/environment.py4
-rw-r--r--mesonbuild/interpreter.py13
-rw-r--r--mesonbuild/mesonlib.py23
-rw-r--r--mesonbuild/modules/gnome.py12
-rw-r--r--mesonbuild/modules/pkgconfig.py32
-rw-r--r--mesonbuild/modules/python.py7
-rwxr-xr-xrun_project_tests.py4
-rwxr-xr-xrun_tests.py14
-rwxr-xr-xrun_unittests.py165
-rw-r--r--test cases/common/14 configure file/meson.build6
-rw-r--r--test cases/frameworks/25 hdf5/main.c30
-rw-r--r--test cases/frameworks/25 hdf5/main.cpp29
-rw-r--r--test cases/frameworks/25 hdf5/main.f9017
-rw-r--r--test cases/frameworks/25 hdf5/meson.build43
-rw-r--r--test cases/linuxlike/13 cmake dependency/cmake/FindSomethingLikeZLIB.cmake9
-rw-r--r--test cases/linuxlike/13 cmake dependency/meson.build6
-rw-r--r--test cases/osx/2 library versions/meson.build32
-rw-r--r--test cases/osx/2 library versions/require_pkgconfig.py9
-rw-r--r--test cases/osx/5 extra frameworks/installed_files.txt2
-rw-r--r--test cases/osx/5 extra frameworks/meson.build13
-rw-r--r--test cases/osx/5 extra frameworks/prog.c3
-rw-r--r--test cases/osx/5 extra frameworks/stat.c1
39 files changed, 743 insertions, 166 deletions
diff --git a/ciimage/Dockerfile b/ciimage/Dockerfile
index 520ce0f..980ed53 100644
--- a/ciimage/Dockerfile
+++ b/ciimage/Dockerfile
@@ -20,6 +20,7 @@ RUN sed -i '/^#\sdeb-src /s/^#//' "/etc/apt/sources.list" \
&& apt-get -y install --no-install-recommends wine-stable \
&& apt-get -y install llvm-dev libclang-dev \
&& apt-get -y install libgcrypt11-dev \
+&& apt-get -y install libhdf5-dev \
&& dub fetch urld && dub build urld --compiler=gdc \
&& dub fetch dubtestproject \
&& dub build dubtestproject:test1 --compiler=ldc2 \
diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md
index e3fedc4..259f09e 100644
--- a/docs/markdown/Dependencies.md
+++ b/docs/markdown/Dependencies.md
@@ -148,6 +148,14 @@ it automatically.
cmake_dep = dependency('ZLIB', method : 'cmake', modules : ['ZLIB::ZLIB'])
```
+It is also possible to reuse existing `Find<name>.cmake` files with the
+`cmake_module_path` property. Using this property is equivalent to setting the
+`CMAKE_MODULE_PATH` variable in CMake. The path(s) given to `cmake_module_path`
+should all be relative to the project source directory. Absolute paths
+should only be used if the CMake files are not stored in the project itself.
+
+Additional CMake parameters can be specified with the `cmake_args` property.
+
### Some notes on Dub
Please understand that meson is only able to find dependencies that
@@ -269,6 +277,17 @@ e = executable('testprog', 'test.cc', dependencies : gtest_dep)
test('gtest test', e)
```
+## HDF5
+HDF5 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('hdf5', language: 'c')` for the C HDF5 headers and libraries
+ * `dependency('hdf5', language: 'cpp')` for the C++ HDF5 headers and libraries
+ * `dependency('hdf5', language: 'fortran')` for the Fortran HDF5 headers and libraries
+
+Meson uses pkg-config to find HDF5. The standard low-level HDF5 function and the `HL` high-level HDF5 functions are linked for each language.
+
+
## libwmf
*(added 0.44.0)*
diff --git a/docs/markdown/howtox.md b/docs/markdown/howtox.md
index 3d8515f..8ae4fde 100644
--- a/docs/markdown/howtox.md
+++ b/docs/markdown/howtox.md
@@ -203,3 +203,20 @@ executable(..., dependencies : m_dep)
```meson
executable(..., install : true, install_dir : get_option('libexecdir'))
```
+
+## Use existing `Find<name>.cmake` files
+
+Meson can use the CMake `find_package()` ecosystem if CMake is installed.
+To find a dependency with custom `Find<name>.cmake`, set the `cmake_module_path`
+property to the path in your project where the CMake scripts are stored.
+
+Example for a `FindCmakeOnlyDep.cmake` in a `cmake` subdirectory:
+
+```meson
+cm_dep = dependency('CmakeOnlyDep', cmake_module_path : 'cmake')
+```
+
+The `cmake_module_path` property is only needed for custom CMake scripts. System
+wide CMake scripts are found automatically.
+
+More information can be found [here](Dependencies.md#cmake)
diff --git a/docs/markdown/snippets/cmake_module_path.md b/docs/markdown/snippets/cmake_module_path.md
new file mode 100644
index 0000000..7beb453
--- /dev/null
+++ b/docs/markdown/snippets/cmake_module_path.md
@@ -0,0 +1,9 @@
+## Added `cmake_module_path` and `cmake_args` to dependency
+
+The CMake dependency backend can now make use of existing `Find<name>.cmake`
+files by setting the `CMAKE_MODULE_PATH` with the new `dependency()` property
+`cmake_module_path`. The paths given to `cmake_module_path` should be relative
+to the project source directory.
+
+Furthermore the property `cmake_args` was added to give CMake additional
+parameters.
diff --git a/docs/markdown/snippets/hdf5.md b/docs/markdown/snippets/hdf5.md
new file mode 100644
index 0000000..8ebb4c0
--- /dev/null
+++ b/docs/markdown/snippets/hdf5.md
@@ -0,0 +1,3 @@
+## HDF5
+
+HDF5 support is added via pkg-config.
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py
index 2071432..68c017a 100644
--- a/mesonbuild/ast/interpreter.py
+++ b/mesonbuild/ast/interpreter.py
@@ -89,6 +89,16 @@ class AstInterpreter(interpreterbase.InterpreterBase):
'set_variable': self.func_do_nothing,
'get_variable': self.func_do_nothing,
'is_variable': self.func_do_nothing,
+ 'disabler': self.func_do_nothing,
+ 'gettext': self.func_do_nothing,
+ 'jar': self.func_do_nothing,
+ 'warning': self.func_do_nothing,
+ 'shared_module': self.func_do_nothing,
+ 'option': self.func_do_nothing,
+ 'both_libraries': self.func_do_nothing,
+ 'add_test_setup': self.func_do_nothing,
+ 'find_library': self.func_do_nothing,
+ 'subdir_done': self.func_do_nothing,
})
def func_do_nothing(self, node, args, kwargs):
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 493fc0d..9b215b2 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -2347,15 +2347,14 @@ rule FORTRAN_DEP_HACK%s
target_args = self.build_target_link_arguments(linker, target.link_whole_targets)
return linker.get_link_whole_for(target_args) if len(target_args) else []
- @staticmethod
@lru_cache(maxsize=None)
- def guess_library_absolute_path(linker, libname, search_dirs, patterns):
+ def guess_library_absolute_path(self, linker, libname, search_dirs, patterns):
for d in search_dirs:
for p in patterns:
trial = CCompiler._get_trials_from_pattern(p, d, libname)
if not trial:
continue
- trial = CCompiler._get_file_from_list(trial)
+ trial = CCompiler._get_file_from_list(self.environment, trial)
if not trial:
continue
# Return the first result
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 783ae64..621aa1a 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -1153,7 +1153,7 @@ class Vs2010Backend(backends.Backend):
ET.SubElement(meson_file_group, 'None', Include=os.path.join(proj_to_src_dir, build_filename))
extra_files = target.extra_files
- if len(headers) + len(gen_hdrs) + len(extra_files) > 0:
+ if len(headers) + len(gen_hdrs) + len(extra_files) + len(pch_sources) > 0:
inc_hdrs = ET.SubElement(root, 'ItemGroup')
for h in headers:
relpath = os.path.join(down, h.rel_to_builddir(self.build_to_src))
@@ -1163,6 +1163,9 @@ class Vs2010Backend(backends.Backend):
for h in target.extra_files:
relpath = os.path.join(down, h.rel_to_builddir(self.build_to_src))
ET.SubElement(inc_hdrs, 'CLInclude', Include=relpath)
+ for lang in pch_sources:
+ h = pch_sources[lang][0]
+ ET.SubElement(inc_hdrs, 'CLInclude', Include=os.path.join(proj_to_src_dir, h))
if len(sources) + len(gen_src) + len(pch_sources) > 0:
inc_src = ET.SubElement(root, 'ItemGroup')
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index c0cd0bc..a591183 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -27,6 +27,7 @@ from . import compilers
from ..mesonlib import (
EnvironmentException, MesonException, version_compare, Popen_safe, listify,
for_windows, for_darwin, for_cygwin, for_haiku, for_openbsd,
+ darwin_get_object_archs
)
from .c_function_attributes import C_FUNC_ATTRIBUTES
@@ -59,6 +60,7 @@ class CCompiler(Compiler):
library_dirs_cache = {}
program_dirs_cache = {}
find_library_cache = {}
+ find_framework_cache = {}
internal_libs = gnu_compiler_internal_libs
@staticmethod
@@ -979,10 +981,28 @@ class CCompiler(Compiler):
return [f.as_posix()]
@staticmethod
- def _get_file_from_list(files: List[str]) -> str:
+ def _get_file_from_list(env, files: List[str]) -> str:
+ '''
+ We just check whether the library exists. We can't do a link check
+ because the library might have unresolved symbols that require other
+ libraries. On macOS we check if the library matches our target
+ architecture.
+ '''
+ # If not building on macOS for Darwin, do a simple file check
+ if not env.machines.host.is_darwin() or not env.machines.build.is_darwin():
+ for f in files:
+ if os.path.isfile(f):
+ return f
+ # Run `lipo` and check if the library supports the arch we want
for f in files:
- if os.path.isfile(f):
+ if not os.path.isfile(f):
+ continue
+ archs = darwin_get_object_archs(f)
+ if archs and env.machines.host.cpu_family in archs:
return f
+ else:
+ mlog.debug('Rejected {}, supports {} but need {}'
+ .format(f, archs, env.machines.host.cpu_family))
return None
@functools.lru_cache()
@@ -1023,10 +1043,7 @@ class CCompiler(Compiler):
trial = self._get_trials_from_pattern(p, d, libname)
if not trial:
continue
- # We just check whether the library exists. We can't do a link
- # check because the library might have unresolved symbols that
- # require other libraries.
- trial = self._get_file_from_list(trial)
+ trial = self._get_file_from_list(env, trial)
if not trial:
continue
return [trial]
@@ -1052,6 +1069,66 @@ class CCompiler(Compiler):
code = 'int main(int argc, char **argv) { return 0; }'
return self.find_library_impl(libname, env, extra_dirs, code, libtype)
+ def find_framework_paths(self, env):
+ '''
+ These are usually /Library/Frameworks and /System/Library/Frameworks,
+ unless you select a particular macOS SDK with the -isysroot flag.
+ You can also add to this by setting -F in CFLAGS.
+ '''
+ if self.id != 'clang':
+ raise MesonException('Cannot find framework path with non-clang compiler')
+ # Construct the compiler command-line
+ commands = self.get_exelist() + ['-v', '-E', '-']
+ commands += self.get_always_args()
+ # Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS from the env
+ commands += env.coredata.get_external_args(self.language)
+ mlog.debug('Finding framework path by running: ', ' '.join(commands), '\n')
+ os_env = os.environ.copy()
+ os_env['LC_ALL'] = 'C'
+ _, _, stde = Popen_safe(commands, env=os_env, stdin=subprocess.PIPE)
+ paths = []
+ for line in stde.split('\n'):
+ if '(framework directory)' not in line:
+ continue
+ # line is of the form:
+ # ` /path/to/framework (framework directory)`
+ paths.append(line[:-21].strip())
+ return paths
+
+ def find_framework_real(self, name, env, extra_dirs, allow_system):
+ code = 'int main(int argc, char **argv) { return 0; }'
+ link_args = []
+ for d in extra_dirs:
+ link_args += ['-F' + d]
+ # We can pass -Z to disable searching in the system frameworks, but
+ # then we must also pass -L/usr/lib to pick up libSystem.dylib
+ extra_args = [] if allow_system else ['-Z', '-L/usr/lib']
+ link_args += ['-framework', name]
+ if self.links(code, env, extra_args=(extra_args + link_args)):
+ return link_args
+
+ def find_framework_impl(self, name, env, extra_dirs, allow_system):
+ if isinstance(extra_dirs, str):
+ extra_dirs = [extra_dirs]
+ key = (tuple(self.exelist), name, tuple(extra_dirs), allow_system)
+ if key in self.find_framework_cache:
+ value = self.find_framework_cache[key]
+ else:
+ value = self.find_framework_real(name, env, extra_dirs, allow_system)
+ self.find_framework_cache[key] = value
+ if value is None:
+ return None
+ return value[:]
+
+ def find_framework(self, name, env, extra_dirs, allow_system=True):
+ '''
+ Finds the framework with the specified name, and returns link args for
+ the same or returns None when the framework is not found.
+ '''
+ if self.id != 'clang':
+ raise MesonException('Cannot find frameworks with non-clang compiler')
+ return self.find_framework_impl(name, env, extra_dirs, allow_system)
+
def thread_flags(self, env):
if for_haiku(self.is_cross, env) or for_darwin(self.is_cross, env):
return []
diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py
index a8e8e25..eea1660 100644
--- a/mesonbuild/compilers/fortran.py
+++ b/mesonbuild/compilers/fortran.py
@@ -267,8 +267,8 @@ class FortranCompiler(Compiler):
return CCompiler._get_trials_from_pattern(pattern, directory, libname)
@staticmethod
- def _get_file_from_list(files) -> List[str]:
- return CCompiler._get_file_from_list(files)
+ def _get_file_from_list(env, files: List[str]) -> str:
+ return CCompiler._get_file_from_list(env, files)
class GnuFortranCompiler(GnuCompiler, FortranCompiler):
def __init__(self, exelist, version, compiler_type, is_cross, exe_wrapper=None, defines=None, **kwargs):
diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py
index e64d57f..5303298 100644
--- a/mesonbuild/compilers/vala.py
+++ b/mesonbuild/compilers/vala.py
@@ -49,6 +49,12 @@ class ValaCompiler(Compiler):
def get_pic_args(self):
return []
+ def get_pie_args(self):
+ return []
+
+ def get_pie_link_args(self):
+ return []
+
def get_always_args(self):
return ['-C']
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 9850722..b2b9e91 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -234,7 +234,7 @@ def load_configs(filenames):
raise MesonException('Cannot find specified native file: ' + f)
- config = configparser.SafeConfigParser()
+ config = configparser.ConfigParser()
config.read(gen())
return config
diff --git a/mesonbuild/dependencies/__init__.py b/mesonbuild/dependencies/__init__.py
index afe2a3b..f5034db 100644
--- a/mesonbuild/dependencies/__init__.py
+++ b/mesonbuild/dependencies/__init__.py
@@ -18,7 +18,7 @@ from .base import ( # noqa: F401
ExternalDependency, NotFoundDependency, ExternalLibrary, ExtraFrameworkDependency, InternalDependency,
PkgConfigDependency, CMakeDependency, find_external_dependency, get_dep_identifier, packages, _packages_accept_language)
from .dev import GMockDependency, GTestDependency, LLVMDependency, ValgrindDependency
-from .misc import (MPIDependency, OpenMPDependency, Python3Dependency, ThreadDependency, PcapDependency, CupsDependency, LibWmfDependency, LibGCryptDependency)
+from .misc import (HDF5Dependency, MPIDependency, OpenMPDependency, Python3Dependency, ThreadDependency, PcapDependency, CupsDependency, LibWmfDependency, LibGCryptDependency)
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,
+ 'hdf5': HDF5Dependency,
'openmp': OpenMPDependency,
'python3': Python3Dependency,
'threads': ThreadDependency,
@@ -54,6 +55,7 @@ packages.update({
'vulkan': VulkanDependency,
})
_packages_accept_language.update({
+ 'hdf5',
'mpi',
'openmp',
})
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index 4b54005..9da0d7c 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -19,7 +19,6 @@ import copy
import functools
import os
import re
-import stat
import json
import shlex
import shutil
@@ -27,15 +26,17 @@ import textwrap
import platform
import itertools
import ctypes
+from typing import List
from enum import Enum
-from pathlib import PurePath
+from pathlib import Path, PurePath
from .. import mlog
from .. import mesonlib
from ..compilers import clib_langs
-from ..environment import BinaryTable
+from ..environment import BinaryTable, Environment
from ..mesonlib import MachineChoice, MesonException, OrderedSet, PerMachine
from ..mesonlib import Popen_safe, version_compare_many, version_compare, listify
+from ..mesonlib import Version
# These must be defined in this file to avoid cyclical references.
packages = {}
@@ -514,7 +515,8 @@ class PkgConfigDependency(ExternalDependency):
# Lookup in cross or machine file.
potential_pkgpath = environment.binaries[for_machine].lookup_entry('pkgconfig')
if potential_pkgpath is not None:
- mlog.debug('Pkg-config binary for %s specified from cross file, native file, or env var as %s.', for_machine, potential_pkgpath)
+ mlog.debug('Pkg-config binary for {} specified from cross file, native file, '
+ 'or env var as {}'.format(for_machine, potential_pkgpath))
yield ExternalProgram.from_entry('pkgconfig', potential_pkgpath)
# We never fallback if the user-specified option is no good, so
# stop returning options.
@@ -530,16 +532,15 @@ class PkgConfigDependency(ExternalDependency):
# Only search for pkg-config for each machine the first time and store
# the result in the class definition
if PkgConfigDependency.class_pkgbin[for_machine] is False:
- mlog.debug('Pkg-config binary for %s is cached missing.' % for_machine)
+ mlog.debug('Pkg-config binary for %s is cached as not found.' % for_machine)
elif PkgConfigDependency.class_pkgbin[for_machine] is not None:
mlog.debug('Pkg-config binary for %s is cached.' % for_machine)
else:
assert PkgConfigDependency.class_pkgbin[for_machine] is None
mlog.debug('Pkg-config binary for %s is not cached.' % for_machine)
for potential_pkgbin in search():
- mlog.debug(
- 'Trying pkg-config binary %s for machine %s at %s.',
- potential_pkgbin.name, for_machine, potential_pkgbin.command)
+ mlog.debug('Trying pkg-config binary {} for machine {} at {}'
+ .format(potential_pkgbin.name, for_machine, potential_pkgbin.command))
version_if_ok = self.check_pkgconfig(potential_pkgbin)
if not version_if_ok:
continue
@@ -558,11 +559,12 @@ class PkgConfigDependency(ExternalDependency):
self.pkgbin = PkgConfigDependency.class_pkgbin[for_machine]
if self.pkgbin is False:
self.pkgbin = None
- msg = 'No pkg-config binary for machine %s not found. Giving up.' % for_machine
+ msg = 'Pkg-config binary for machine %s not found. Giving up.' % for_machine
if self.required:
raise DependencyException(msg)
else:
mlog.debug(msg)
+ return
mlog.debug('Determining dependency {!r} with pkg-config executable '
'{!r}'.format(name, self.pkgbin.get_path()))
@@ -792,10 +794,10 @@ class PkgConfigDependency(ExternalDependency):
if 'define_variable' in kwargs:
definition = kwargs.get('define_variable', [])
if not isinstance(definition, list):
- raise MesonException('define_variable takes a list')
+ raise DependencyException('define_variable takes a list')
if len(definition) != 2 or not all(isinstance(i, str) for i in definition):
- raise MesonException('define_variable must be made up of 2 strings for VARIABLENAME and VARIABLEVALUE')
+ raise DependencyException('define_variable must be made up of 2 strings for VARIABLENAME and VARIABLEVALUE')
options = ['--define-variable=' + '='.join(definition)] + options
@@ -827,8 +829,7 @@ class PkgConfigDependency(ExternalDependency):
def check_pkgconfig(self, pkgbin):
if not pkgbin.found():
- mlog.log('Did not find anything at {!r}'
- ''.format(' '.join(pkgbin.get_command())))
+ mlog.log('Did not find pkg-config by name {!r}'.format(pkgbin.name))
return None
try:
p, out = Popen_safe(pkgbin.get_command() + ['--version'])[0:2]
@@ -925,7 +926,7 @@ class CMakeDependency(ExternalDependency):
def _gen_exception(self, msg):
return DependencyException('Dependency {} not found: {}'.format(self.name, msg))
- def __init__(self, name, environment, kwargs, language=None):
+ def __init__(self, name: str, environment: Environment, kwargs, language=None):
super().__init__('cmake', environment, language, kwargs)
self.name = name
self.is_libtool = False
@@ -972,16 +973,15 @@ class CMakeDependency(ExternalDependency):
# Only search for CMake the first time and store the result in the class
# definition
if CMakeDependency.class_cmakebin[for_machine] is False:
- mlog.debug('CMake binary for %s is cached missing.' % for_machine)
+ mlog.debug('CMake binary for %s is cached as not found' % for_machine)
elif CMakeDependency.class_cmakebin[for_machine] is not None:
mlog.debug('CMake binary for %s is cached.' % for_machine)
else:
assert CMakeDependency.class_cmakebin[for_machine] is None
- mlog.debug('CMake binary for %s is not cached.', for_machine)
+ mlog.debug('CMake binary for %s is not cached' % for_machine)
for potential_cmakebin in search():
- mlog.debug(
- 'Trying CMake binary %s for machine %s at %s.',
- potential_cmakebin.name, for_machine, potential_cmakebin.command)
+ mlog.debug('Trying CMake binary {} for machine {} at {}'
+ .format(potential_cmakebin.name, for_machine, potential_cmakebin.command))
version_if_ok = self.check_cmake(potential_cmakebin)
if not version_if_ok:
continue
@@ -1007,18 +1007,28 @@ class CMakeDependency(ExternalDependency):
if self.required:
raise DependencyException(msg)
mlog.debug(msg)
+ return
modules = kwargs.get('modules', [])
+ cm_path = kwargs.get('cmake_module_path', [])
+ cm_args = kwargs.get('cmake_args', [])
if not isinstance(modules, list):
modules = [modules]
- self._detect_dep(name, modules)
+ if not isinstance(cm_path, list):
+ cm_path = [cm_path]
+ if not isinstance(cm_args, list):
+ cm_args = [cm_args]
+ cm_path = [x if os.path.isabs(x) else os.path.join(environment.get_source_dir(), x) for x in cm_path]
+ if cm_path:
+ cm_args += ['-DCMAKE_MODULE_PATH={}'.format(';'.join(cm_path))]
+ self._detect_dep(name, modules, cm_args)
def __repr__(self):
s = '<{0} {1}: {2} {3}>'
return s.format(self.__class__.__name__, self.name, self.is_found,
self.version_reqs)
- def _detect_dep(self, name, modules):
+ def _detect_dep(self, name: str, modules: List[str], args: List[str]):
# Detect a dependency with CMake using the '--find-package' mode
# and the trace output (stderr)
#
@@ -1034,7 +1044,7 @@ class CMakeDependency(ExternalDependency):
mlog.debug('Try CMake generator: {}'.format(i if len(i) > 0 else 'auto'))
# Prepare options
- cmake_opts = ['--trace-expand', '-DNAME={}'.format(name), '.']
+ cmake_opts = ['--trace-expand', '-DNAME={}'.format(name)] + args + ['.']
if len(i) > 0:
cmake_opts = ['-G', i] + cmake_opts
@@ -1485,8 +1495,7 @@ set(CMAKE_SIZEOF_VOID_P "{}")
def check_cmake(self, cmakebin):
if not cmakebin.found():
- mlog.log('Did not find CMake {!r}'
- ''.format(' '.join(cmakebin.get_command())))
+ mlog.log('Did not find CMake {!r}'.format(cmakebin.name))
return None
try:
p, out = Popen_safe(cmakebin.get_command() + ['--version'])[0:2]
@@ -1984,40 +1993,91 @@ class ExternalLibrary(ExternalDependency):
class ExtraFrameworkDependency(ExternalDependency):
- def __init__(self, name, required, path, env, lang, kwargs):
+ system_framework_paths = None
+
+ def __init__(self, name, required, paths, env, lang, kwargs):
super().__init__('extraframeworks', env, lang, kwargs)
self.name = name
self.required = required
- self.detect(name, path)
- if self.found():
- self.compile_args = ['-I' + os.path.join(self.path, self.name, 'Headers')]
- self.link_args = ['-F' + self.path, '-framework', self.name.split('.')[0]]
-
- def detect(self, name, path):
- # should use the compiler to look for frameworks, rather than peering at
- # the filesystem, so we can also find them when cross-compiling
- if self.want_cross:
+ # Full path to framework directory
+ self.framework_path = None
+ if not self.clib_compiler:
+ raise DependencyException('No C-like compilers are available')
+ if self.system_framework_paths is None:
+ self.system_framework_paths = self.clib_compiler.find_framework_paths(self.env)
+ self.detect(name, paths)
+
+ def detect(self, name, paths):
+ if not paths:
+ paths = self.system_framework_paths
+ for p in paths:
+ mlog.debug('Looking for framework {} in {}'.format(name, p))
+ # We need to know the exact framework path because it's used by the
+ # Qt5 dependency class, and for setting the include path. We also
+ # want to avoid searching in an invalid framework path which wastes
+ # time and can cause a false positive.
+ framework_path = self._get_framework_path(p, name)
+ if framework_path is None:
+ continue
+ # We want to prefer the specified paths (in order) over the system
+ # paths since these are "extra" frameworks.
+ # For example, Python2's framework is in /System/Library/Frameworks and
+ # Python3's framework is in /Library/Frameworks, but both are called
+ # Python.framework. We need to know for sure that the framework was
+ # found in the path we expect.
+ allow_system = p in self.system_framework_paths
+ args = self.clib_compiler.find_framework(name, self.env, [p], allow_system)
+ if args is None:
+ continue
+ self.link_args = args
+ self.framework_path = framework_path.as_posix()
+ self.compile_args = ['-F' + self.framework_path]
+ # We need to also add -I includes to the framework because all
+ # cross-platform projects such as OpenGL, Python, Qt, GStreamer,
+ # etc do not use "framework includes":
+ # https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Tasks/IncludingFrameworks.html
+ incdir = self._get_framework_include_path(framework_path)
+ if incdir:
+ self.compile_args += ['-I' + incdir]
+ self.is_found = True
return
+ def _get_framework_path(self, path, name):
+ p = Path(path)
lname = name.lower()
- if path is None:
- paths = ['/System/Library/Frameworks', '/Library/Frameworks']
- else:
- paths = [path]
- for p in paths:
- for d in os.listdir(p):
- fullpath = os.path.join(p, d)
- if lname != d.rsplit('.', 1)[0].lower():
- continue
- if not stat.S_ISDIR(os.stat(fullpath).st_mode):
- continue
- self.path = p
- self.name = d
- self.is_found = True
- return
+ for d in p.glob('*.framework/'):
+ if lname == d.name.rsplit('.', 1)[0].lower():
+ return d
+ return None
+
+ def _get_framework_latest_version(self, path):
+ versions = []
+ for each in path.glob('Versions/*'):
+ # macOS filesystems are usually case-insensitive
+ if each.name.lower() == 'current':
+ continue
+ versions.append(Version(each.name))
+ return 'Versions/{}/Headers'.format(sorted(versions)[-1]._s)
+
+ def _get_framework_include_path(self, path):
+ # According to the spec, 'Headers' must always be a symlink to the
+ # Headers directory inside the currently-selected version of the
+ # framework, but sometimes frameworks are broken. Look in 'Versions'
+ # for the currently-selected version or pick the latest one.
+ trials = ('Headers', 'Versions/Current/Headers',
+ self._get_framework_latest_version(path))
+ for each in trials:
+ trial = path / each
+ if trial.is_dir():
+ return trial.as_posix()
+ return None
+
+ @staticmethod
+ def get_methods():
+ return [DependencyMethods.EXTRAFRAMEWORK]
def log_info(self):
- return os.path.join(self.path, self.name)
+ return self.framework_path
def log_tried(self):
return 'framework'
@@ -2044,6 +2104,7 @@ display_name_map = {
'dub': 'DUB',
'gmock': 'GMock',
'gtest': 'GTest',
+ 'hdf5': 'HDF5',
'llvm': 'LLVM',
'mpi': 'MPI',
'openmp': 'OpenMP',
@@ -2086,7 +2147,7 @@ def find_external_dependency(name, env, kwargs):
d = c()
d._check_version()
pkgdep.append(d)
- except Exception as e:
+ except DependencyException as e:
pkg_exc.append(e)
mlog.debug(str(e))
else:
@@ -2128,7 +2189,7 @@ def find_external_dependency(name, env, kwargs):
# if an exception occurred with the first detection method, re-raise it
# (on the grounds that it came from the preferred dependency detection
# method)
- if pkg_exc[0]:
+ if pkg_exc and pkg_exc[0]:
raise pkg_exc[0]
# we have a list of failed ExternalDependency objects, so we can report
@@ -2173,6 +2234,14 @@ def _build_external_dependency_list(name, env, kwargs):
candidates.append(functools.partial(CMakeDependency, name, env, kwargs))
return candidates
+ # If it's explicitly requested, use the Extraframework detection method (only)
+ if 'extraframework' == kwargs.get('method', ''):
+ # On OSX, also try framework dependency detector
+ if mesonlib.is_osx():
+ candidates.append(functools.partial(ExtraFrameworkDependency, name,
+ False, None, env, None, kwargs))
+ return candidates
+
# Otherwise, just use the pkgconfig and cmake dependency detector
if 'auto' == kwargs.get('method', 'auto'):
candidates.append(functools.partial(PkgConfigDependency, name, env, kwargs))
diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py
index e6f52a5..208f063 100644
--- a/mesonbuild/dependencies/misc.py
+++ b/mesonbuild/dependencies/misc.py
@@ -14,14 +14,13 @@
# This file contains the detection logic for miscellaneous external dependencies.
+from pathlib import Path
import functools
import os
import re
import shlex
import sysconfig
-from pathlib import Path
-
from .. import mlog
from .. import mesonlib
from ..environment import detect_cpu_family
@@ -33,6 +32,52 @@ from .base import (
)
+class HDF5Dependency(ExternalDependency):
+
+ def __init__(self, environment, kwargs):
+ language = kwargs.get('language', 'c')
+ super().__init__('hdf5', environment, language, kwargs)
+ kwargs['required'] = False
+ kwargs['silent'] = True
+ self.is_found = False
+
+ pkgconfig_files = ['hdf5']
+
+ if language not in ('c', 'cpp', 'fortran'):
+ raise DependencyException('Language {} is not supported with HDF5.'.format(language))
+
+ for pkg in pkgconfig_files:
+ try:
+ pkgdep = PkgConfigDependency(pkg, environment, kwargs, language=self.language)
+ if pkgdep.found():
+ self.compile_args = pkgdep.get_compile_args()
+ # derive needed libraries by language
+ pd_link_args = pkgdep.get_link_args()
+ link_args = []
+ for larg in pd_link_args:
+ lpath = Path(larg)
+ if lpath.is_file():
+ if language == 'cpp':
+ link_args.append(str(lpath.parent / (lpath.stem + '_hl_cpp' + lpath.suffix)))
+ link_args.append(str(lpath.parent / (lpath.stem + '_cpp' + lpath.suffix)))
+ elif language == 'fortran':
+ link_args.append(str(lpath.parent / (lpath.stem + 'hl_fortran' + lpath.suffix)))
+ link_args.append(str(lpath.parent / (lpath.stem + '_fortran' + lpath.suffix)))
+
+ # HDF5 C libs are required by other HDF5 languages
+ link_args.append(str(lpath.parent / (lpath.stem + '_hl' + lpath.suffix)))
+ link_args.append(larg)
+ else:
+ link_args.append(larg)
+
+ self.link_args = link_args
+ self.version = pkgdep.get_version()
+ self.is_found = True
+ self.pcdep = pkgdep
+ break
+ except Exception:
+ pass
+
class MPIDependency(ExternalDependency):
def __init__(self, environment, kwargs):
@@ -307,7 +352,7 @@ class Python3Dependency(ExternalDependency):
# There is a python in /System/Library/Frameworks, but that's
# python 2, Python 3 will always be in /Library
candidates.append(functools.partial(
- ExtraFrameworkDependency, 'python', False, '/Library/Frameworks',
+ ExtraFrameworkDependency, 'Python', False, ['/Library/Frameworks'],
environment, kwargs.get('language', None), kwargs))
return candidates
diff --git a/mesonbuild/dependencies/platform.py b/mesonbuild/dependencies/platform.py
index c78ebed..7e9f9d8 100644
--- a/mesonbuild/dependencies/platform.py
+++ b/mesonbuild/dependencies/platform.py
@@ -15,8 +15,6 @@
# This file contains the detection logic for external dependencies that are
# platform-specific (generally speaking).
-from .. import mesonlib
-
from .base import ExternalDependency, DependencyException
@@ -29,11 +27,19 @@ class AppleFrameworks(ExternalDependency):
if not modules:
raise DependencyException("AppleFrameworks dependency requires at least one module.")
self.frameworks = modules
- # FIXME: Use self.clib_compiler to check if the frameworks are available
+ if not self.clib_compiler:
+ raise DependencyException('No C-like compilers are available, cannot find the framework')
+ self.is_found = True
for f in self.frameworks:
- self.link_args += ['-framework', f]
-
- self.is_found = mesonlib.for_darwin(self.want_cross, self.env)
+ args = self.clib_compiler.find_framework(f, env, [])
+ if args is not None:
+ # No compile args are needed for system frameworks
+ self.link_args = args
+ else:
+ self.is_found = False
+
+ def log_info(self):
+ return ', '.join(self.frameworks)
def log_tried(self):
return 'framework'
diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py
index e23124c..ce1ca68 100644
--- a/mesonbuild/dependencies/ui.py
+++ b/mesonbuild/dependencies/ui.py
@@ -91,9 +91,9 @@ class GnuStepDependency(ConfigToolDependency):
'link_args'))
def find_config(self, versions=None):
- tool = self.tools[0]
+ tool = [self.tools[0]]
try:
- p, out = Popen_safe([tool, '--help'])[:2]
+ p, out = Popen_safe(tool + ['--help'])[:2]
except (FileNotFoundError, PermissionError):
return (None, None)
if p.returncode != 0:
@@ -177,13 +177,13 @@ def _qt_get_private_includes(mod_inc_dir, module, mod_version):
os.path.join(private_dir, 'Qt' + module))
class QtExtraFrameworkDependency(ExtraFrameworkDependency):
- def __init__(self, name, required, path, env, lang, kwargs):
- super().__init__(name, required, path, env, lang, kwargs)
+ def __init__(self, name, required, paths, env, lang, kwargs):
+ super().__init__(name, required, paths, env, lang, kwargs)
self.mod_name = name[2:]
def get_compile_args(self, with_private_headers=False, qt_version="0"):
if self.found():
- mod_inc_dir = os.path.join(self.path, self.name, 'Headers')
+ mod_inc_dir = os.path.join(self.framework_path, 'Headers')
args = ['-I' + mod_inc_dir]
if with_private_headers:
args += ['-I' + dirname for dirname in _qt_get_private_includes(mod_inc_dir, self.mod_name, qt_version)]
@@ -216,9 +216,11 @@ class QtBaseDependency(ExternalDependency):
methods = []
# Prefer pkg-config, then fallback to `qmake -query`
if DependencyMethods.PKGCONFIG in self.methods:
+ mlog.debug('Trying to find qt with pkg-config')
self._pkgconfig_detect(mods, kwargs)
methods.append('pkgconfig')
if not self.is_found and DependencyMethods.QMAKE in self.methods:
+ mlog.debug('Trying to find qt with qmake')
self.from_text = self._qmake_detect(mods, kwargs)
methods.append('qmake-' + self.name)
methods.append('qmake')
@@ -371,7 +373,9 @@ class QtBaseDependency(ExternalDependency):
continue
(k, v) = tuple(line.split(':', 1))
qvars[k] = v
- if mesonlib.is_osx():
+ # Qt on macOS uses a framework, but Qt for iOS does not
+ if self.env.machines.host.is_darwin() and 'ios' not in qvars['QMAKE_XSPEC']:
+ mlog.debug("Building for macOS, looking for framework")
self._framework_detect(qvars, mods, kwargs)
return qmake
incdir = qvars['QT_INSTALL_HEADERS']
@@ -442,7 +446,8 @@ class QtBaseDependency(ExternalDependency):
for m in modules:
fname = 'Qt' + m
- fwdep = QtExtraFrameworkDependency(fname, False, libdir, self.env,
+ mlog.debug('Looking for qt framework ' + fname)
+ fwdep = QtExtraFrameworkDependency(fname, False, [libdir], self.env,
self.language, fw_kwargs)
self.compile_args.append('-F' + libdir)
if fwdep.found():
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index cedbc7e..f9defcd 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -1337,6 +1337,9 @@ class MachineInfo:
return NotImplemented
return not self.__eq__(other)
+ def __repr__(self):
+ return '<MachineInfo: {} {} ({})>'.format(self.system, self.cpu_family, self.cpu)
+
@staticmethod
def detect(compilers = None):
"""Detect the machine we're running on
@@ -1520,6 +1523,7 @@ class BinaryTable:
'windres': 'WINDRES',
'cmake': 'CMAKE',
+ 'qmake': 'QMAKE',
'pkgconfig': 'PKG_CONFIG',
}
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index c940f40..fb4c468 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -276,7 +276,8 @@ class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder):
if len(args) == 1 and isinstance(args[0], list) and len(args[0]) == 2:
mlog.deprecation('Passing a list as the single argument to '
'configuration_data.set is deprecated. This will '
- 'become a hard error in the future.')
+ 'become a hard error in the future.',
+ location=self.current_node)
args = args[0]
if len(args) != 2:
@@ -289,7 +290,7 @@ class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder):
msg = 'Setting a configuration data value to {!r} is invalid, ' \
'and will fail at configure_file(). If you are using it ' \
'just to store some values, please use a dict instead.'
- mlog.deprecation(msg.format(val))
+ mlog.deprecation(msg.format(val), location=self.current_node)
desc = kwargs.get('description', None)
if not isinstance(name, str):
raise InterpreterException("First argument to set must be a string.")
@@ -1928,6 +1929,7 @@ permitted_kwargs = {'add_global_arguments': {'language', 'native'},
'main',
'method',
'modules',
+ 'cmake_module_path',
'optional_modules',
'native',
'not_found_message',
@@ -1935,6 +1937,7 @@ permitted_kwargs = {'add_global_arguments': {'language', 'native'},
'static',
'version',
'private_headers',
+ 'cmake_args',
},
'declare_dependency': {'include_directories',
'link_with',
@@ -2902,10 +2905,10 @@ external dependencies (including libraries) must go to "dependencies".''')
elif name == 'openmp':
FeatureNew('OpenMP Dependency', '0.46.0').use(self.subproject)
+ @FeatureNewKwargs('dependency', '0.50.0', ['not_found_message', 'cmake_module_path', 'cmake_args'])
@FeatureNewKwargs('dependency', '0.49.0', ['disabler'])
@FeatureNewKwargs('dependency', '0.40.0', ['method'])
@FeatureNewKwargs('dependency', '0.38.0', ['default_options'])
- @FeatureNewKwargs('dependency', '0.50.0', ['not_found_message'])
@disablerIfNotFound
@permittedKwargs(permitted_kwargs['dependency'])
def func_dependency(self, node, args, kwargs):
@@ -3594,6 +3597,10 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
# for backwards compatibility. That was the behaviour before
# 0.45.0 so preserve it.
idir = kwargs.get('install_dir', '')
+ if idir is False:
+ idir = ''
+ mlog.deprecation('Please use the new `install:` kwarg instead of passing '
+ '`false` to `install_dir:`', location=node)
if not isinstance(idir, str):
raise InterpreterException('"install_dir" must be a string')
install = kwargs.get('install', idir != '')
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index 8454d79..540fcdc 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -461,6 +461,26 @@ def exe_exists(arglist):
pass
return False
+lru_cache(maxsize=None)
+def darwin_get_object_archs(objpath):
+ '''
+ For a specific object (executable, static library, dylib, etc), run `lipo`
+ to fetch the list of archs supported by it. Supports both thin objects and
+ 'fat' objects.
+ '''
+ _, stdo, stderr = Popen_safe(['lipo', '-info', objpath])
+ if not stdo:
+ mlog.debug('lipo {}: {}'.format(objpath, stderr))
+ return None
+ stdo = stdo.rsplit(': ', 1)[1]
+ # Convert from lipo-style archs to meson-style CPUs
+ stdo = stdo.replace('i386', 'x86')
+ stdo = stdo.replace('arm64', 'aarch64')
+ # Add generic name for armv7 and armv7s
+ if 'armv7' in stdo:
+ stdo += ' arm'
+ return stdo.split()
+
def detect_vcs(source_dir):
vcs_systems = [
dict(name = 'git', cmd = 'git', repo_dir = '.git', get_rev = 'git describe --dirty=+', rev_regex = '(.*)', dep = '.git/logs/HEAD'),
@@ -496,6 +516,9 @@ class Version:
def __str__(self):
return '%s (V=%s)' % (self._s, str(self._v))
+ def __repr__(self):
+ return '<Version: {}>'.format(self._s)
+
def __lt__(self, other):
return self.__cmp__(other) == -1
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index 2ab575c..871cd48 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -607,9 +607,15 @@ class GnomeModule(ExtensionModule):
if 'b_sanitize' in compiler.base_options:
sanitize = state.environment.coredata.base_options['b_sanitize'].value
cflags += compilers.sanitizer_compile_args(sanitize)
- if 'address' in sanitize.split(','):
- internal_ldflags += ['-lasan'] # This must be first in ldflags
- # FIXME: Linking directly to libasan is not recommended but g-ir-scanner
+ sanitize = sanitize.split(',')
+ # These must be first in ldflags
+ if 'address' in sanitize:
+ internal_ldflags += ['-lasan']
+ if 'thread' in sanitize:
+ internal_ldflags += ['-ltsan']
+ if 'undefined' in sanitize:
+ internal_ldflags += ['-lubsan']
+ # FIXME: Linking directly to lib*san is not recommended but g-ir-scanner
# does not understand -f LDFLAGS. https://bugzilla.gnome.org/show_bug.cgi?id=783892
# ldflags += compilers.sanitizer_link_args(sanitize)
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index 0e232a0..8ce28ba 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -23,6 +23,8 @@ from . import ModuleReturnValue
from . import ExtensionModule
from ..interpreterbase import permittedKwargs, FeatureNew, FeatureNewKwargs
+already_warned_objs = set()
+
class DependenciesHelper:
def __init__(self, name):
self.name = name
@@ -51,16 +53,21 @@ class DependenciesHelper:
self.priv_reqs += self._process_reqs(reqs)
def _check_generated_pc_deprecation(self, obj):
- if hasattr(obj, 'generated_pc_warn'):
- mlog.deprecation('Library', mlog.bold(obj.name), 'was passed to the '
- '"libraries" keyword argument of a previous call '
- 'to generate() method instead of first positional '
- 'argument.', 'Adding', mlog.bold(obj.generated_pc),
- 'to "Requires" field, but this is a deprecated '
- 'behaviour that will change in a future version '
- 'of Meson. Please report the issue if this '
- 'warning cannot be avoided in your case.',
- location=obj.generated_pc_warn)
+ if not hasattr(obj, 'generated_pc_warn'):
+ return
+ name = obj.generated_pc_warn[0]
+ if (name, obj.name) in already_warned_objs:
+ return
+ mlog.deprecation('Library', mlog.bold(obj.name), 'was passed to the '
+ '"libraries" keyword argument of a previous call '
+ 'to generate() method instead of first positional '
+ 'argument.', 'Adding', mlog.bold(obj.generated_pc),
+ 'to "Requires" field, but this is a deprecated '
+ 'behaviour that will change in a future version '
+ 'of Meson. Please report the issue if this '
+ 'warning cannot be avoided in your case.',
+ location=obj.generated_pc_warn[1])
+ already_warned_objs.add((name, obj.name))
def _process_reqs(self, reqs):
'''Returns string names of requirements'''
@@ -442,8 +449,9 @@ class PkgConfigModule(ExtensionModule):
for lib in deps.pub_libs:
if not isinstance(lib, str) and not hasattr(lib, 'generated_pc'):
lib.generated_pc = filebase
- lib.generated_pc_warn = types.SimpleNamespace(subdir=state.subdir,
- lineno=state.current_lineno)
+ location = types.SimpleNamespace(subdir=state.subdir,
+ lineno=state.current_lineno)
+ lib.generated_pc_warn = [name, location]
return ModuleReturnValue(res, [res])
def initialize(*args, **kwargs):
diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py
index 1d41165..049c457 100644
--- a/mesonbuild/modules/python.py
+++ b/mesonbuild/modules/python.py
@@ -60,6 +60,7 @@ class PythonDependency(ExternalDependency):
self.pkgdep = None
self.variables = python_holder.variables
self.paths = python_holder.paths
+ self.link_libpython = python_holder.link_libpython
if mesonlib.version_compare(self.version, '>= 3.0'):
self.major_version = 3
else:
@@ -149,11 +150,11 @@ class PythonDependency(ExternalDependency):
libdirs = []
largs = self.clib_compiler.find_library(libname, environment, libdirs)
-
- self.is_found = largs is not None
- if self.is_found:
+ if largs is not None:
self.link_args = largs
+ self.is_found = largs is not None or not self.link_libpython
+
inc_paths = mesonlib.OrderedSet([
self.variables.get('INCLUDEPY'),
self.paths.get('include'),
diff --git a/run_project_tests.py b/run_project_tests.py
index 4c6ca3b..d10f3a2 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -496,6 +496,10 @@ def skippable(suite, test):
return 'BOOST_ROOT' not in os.environ
return False
+ # Qt is provided on macOS by Homebrew
+ if test.endswith('4 qt') and mesonlib.is_osx():
+ return False
+
# Other framework tests are allowed to be skipped on other platforms
return True
diff --git a/run_tests.py b/run_tests.py
index 25a2d7f..20cb4e2 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -66,7 +66,7 @@ class FakeCompilerOptions:
def __init__(self):
self.value = []
-def get_fake_options(prefix):
+def get_fake_options(prefix=''):
import argparse
opts = argparse.Namespace()
opts.cross_file = None
@@ -76,11 +76,12 @@ def get_fake_options(prefix):
opts.native_file = []
return opts
-def get_fake_env(sdir, bdir, prefix, opts = None):
+def get_fake_env(sdir='', bdir=None, prefix='', opts=None):
if opts is None:
opts = get_fake_options(prefix)
env = Environment(sdir, bdir, opts)
env.coredata.compiler_options['c_args'] = FakeCompilerOptions()
+ env.machines.host.cpu_family = 'x86_64' # Used on macOS inside find_library
return env
@@ -213,6 +214,14 @@ def run_mtest_inprocess(commandlist):
sys.stderr = old_stderr
return returncode, mystdout.getvalue(), mystderr.getvalue()
+def clear_meson_configure_class_caches():
+ mesonbuild.compilers.CCompiler.library_dirs_cache = {}
+ mesonbuild.compilers.CCompiler.program_dirs_cache = {}
+ mesonbuild.compilers.CCompiler.find_library_cache = {}
+ mesonbuild.compilers.CCompiler.find_framework_cache = {}
+ mesonbuild.dependencies.PkgConfigDependency.pkgbin_cache = {}
+ mesonbuild.dependencies.PkgConfigDependency.class_pkgbin = mesonlib.PerMachine(None, None, None)
+
def run_configure_inprocess(commandlist):
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
@@ -223,6 +232,7 @@ def run_configure_inprocess(commandlist):
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
+ clear_meson_configure_class_caches()
return returncode, mystdout.getvalue(), mystderr.getvalue()
def run_configure_external(full_command):
diff --git a/run_unittests.py b/run_unittests.py
index 8c4093b..9525b01 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -42,10 +42,11 @@ import mesonbuild.mesonlib
import mesonbuild.coredata
import mesonbuild.modules.gnome
from mesonbuild.interpreter import Interpreter, ObjectHolder
+from mesonbuild.ast import AstInterpreter
from mesonbuild.mesonlib import (
is_windows, is_osx, is_cygwin, is_dragonflybsd, is_openbsd, is_haiku,
windows_proof_rmtree, python_command, version_compare,
- BuildDirLock, Version
+ BuildDirLock, Version, PerMachine
)
from mesonbuild.environment import detect_ninja
from mesonbuild.mesonlib import MesonException, EnvironmentException
@@ -163,7 +164,7 @@ def skip_if_not_language(lang):
@functools.wraps(func)
def wrapped(*args, **kwargs):
try:
- env = get_fake_env('', '', '')
+ env = get_fake_env()
f = getattr(env, 'detect_{}_compiler'.format(lang))
if lang in ['cs', 'vala', 'java', 'swift']:
f()
@@ -203,7 +204,7 @@ def skip_if_not_base_option(feature):
def actual(f):
@functools.wraps(f)
def wrapped(*args, **kwargs):
- env = get_fake_env('', '', '')
+ env = get_fake_env()
cc = env.detect_c_compiler(False)
if feature not in cc.base_options:
raise unittest.SkipTest(
@@ -230,6 +231,32 @@ def temp_filename():
except OSError:
pass
+@contextmanager
+def no_pkgconfig():
+ '''
+ A context manager that overrides shutil.which and ExternalProgram to force
+ them to return None for pkg-config to simulate it not existing.
+ '''
+ old_which = shutil.which
+ old_search = ExternalProgram._search
+
+ def new_search(self, name, search_dir):
+ if name == 'pkg-config':
+ return [None]
+ return old_search(self, name, search_dir)
+
+ def new_which(cmd, *kwargs):
+ if cmd == 'pkg-config':
+ return None
+ return old_which(cmd, *kwargs)
+
+ shutil.which = new_which
+ ExternalProgram._search = new_search
+ try:
+ yield
+ finally:
+ shutil.which = old_which
+ ExternalProgram._search = old_search
class PatchModule:
'''
@@ -581,9 +608,9 @@ class InternalTests(unittest.TestCase):
config.write(configfile)
configfile.flush()
configfile.close()
- opts = get_fake_options('')
+ opts = get_fake_options()
opts.cross_file = configfilename
- env = get_fake_env('', '', '', opts)
+ env = get_fake_env(opts=opts)
detected_value = env.need_exe_wrapper()
os.unlink(configfilename)
@@ -596,9 +623,9 @@ class InternalTests(unittest.TestCase):
configfilename = configfile.name
config.write(configfile)
configfile.close()
- opts = get_fake_options('')
+ opts = get_fake_options()
opts.cross_file = configfilename
- env = get_fake_env('', '', '', opts)
+ env = get_fake_env(opts=opts)
forced_value = env.need_exe_wrapper()
os.unlink(configfilename)
@@ -718,7 +745,7 @@ class InternalTests(unittest.TestCase):
'windows-mingw': {'shared': ('lib{}.dll.a', 'lib{}.lib', 'lib{}.dll',
'{}.dll.a', '{}.lib', '{}.dll'),
'static': msvc_static}}
- env = get_fake_env('', '', '')
+ env = get_fake_env()
cc = env.detect_c_compiler(False)
if is_osx():
self._test_all_naming(cc, env, patterns, 'darwin')
@@ -755,9 +782,20 @@ class InternalTests(unittest.TestCase):
https://github.com/mesonbuild/meson/issues/3951
'''
+ def create_static_lib(name):
+ if not is_osx():
+ name.open('w').close()
+ return
+ src = name.with_suffix('.c')
+ out = name.with_suffix('.o')
+ with src.open('w') as f:
+ f.write('int meson_foobar (void) { return 0; }')
+ subprocess.check_call(['clang', '-c', str(src), '-o', str(out)])
+ subprocess.check_call(['ar', 'csr', str(name), str(out)])
+
with tempfile.TemporaryDirectory() as tmpdir:
pkgbin = ExternalProgram('pkg-config', command=['pkg-config'], silent=True)
- env = get_fake_env('', '', '')
+ env = get_fake_env()
compiler = env.detect_c_compiler(False)
env.coredata.compilers = {'c': compiler}
env.coredata.compiler_options['c_link_args'] = FakeCompilerOptions()
@@ -766,16 +804,16 @@ class InternalTests(unittest.TestCase):
p1.mkdir()
p2.mkdir()
# libfoo.a is in one prefix
- (p1 / 'libfoo.a').open('w').close()
+ create_static_lib(p1 / 'libfoo.a')
# libbar.a is in both prefixes
- (p1 / 'libbar.a').open('w').close()
- (p2 / 'libbar.a').open('w').close()
+ create_static_lib(p1 / 'libbar.a')
+ create_static_lib(p2 / 'libbar.a')
# Ensure that we never statically link to these
- (p1 / 'libpthread.a').open('w').close()
- (p1 / 'libm.a').open('w').close()
- (p1 / 'libc.a').open('w').close()
- (p1 / 'libdl.a').open('w').close()
- (p1 / 'librt.a').open('w').close()
+ create_static_lib(p1 / 'libpthread.a')
+ create_static_lib(p1 / 'libm.a')
+ create_static_lib(p1 / 'libc.a')
+ create_static_lib(p1 / 'libdl.a')
+ create_static_lib(p1 / 'librt.a')
def fake_call_pkgbin(self, args, env=None):
if '--libs' not in args:
@@ -789,30 +827,31 @@ class InternalTests(unittest.TestCase):
old_call = PkgConfigDependency._call_pkgbin
old_check = PkgConfigDependency.check_pkgconfig
- old_pkgbin = PkgConfigDependency.class_pkgbin
PkgConfigDependency._call_pkgbin = fake_call_pkgbin
PkgConfigDependency.check_pkgconfig = lambda x, _: pkgbin
# Test begins
- kwargs = {'required': True, 'silent': True}
- foo_dep = PkgConfigDependency('foo', env, kwargs)
- self.assertEqual(foo_dep.get_link_args(),
- [(p1 / 'libfoo.a').as_posix(), (p2 / 'libbar.a').as_posix()])
- bar_dep = PkgConfigDependency('bar', env, kwargs)
- self.assertEqual(bar_dep.get_link_args(), [(p2 / 'libbar.a').as_posix()])
- internal_dep = PkgConfigDependency('internal', env, kwargs)
- if compiler.get_argument_syntax() == 'msvc':
- self.assertEqual(internal_dep.get_link_args(), [])
- else:
- link_args = internal_dep.get_link_args()
- for link_arg in link_args:
- for lib in ('pthread', 'm', 'c', 'dl', 'rt'):
- self.assertNotIn('lib{}.a'.format(lib), link_arg, msg=link_args)
- # Test ends
- PkgConfigDependency._call_pkgbin = old_call
- PkgConfigDependency.check_pkgconfig = old_check
- # Reset dependency class to ensure that in-process configure doesn't mess up
- PkgConfigDependency.pkgbin_cache = {}
- PkgConfigDependency.class_pkgbin = old_pkgbin
+ try:
+ kwargs = {'required': True, 'silent': True}
+ foo_dep = PkgConfigDependency('foo', env, kwargs)
+ self.assertEqual(foo_dep.get_link_args(),
+ [(p1 / 'libfoo.a').as_posix(), (p2 / 'libbar.a').as_posix()])
+ bar_dep = PkgConfigDependency('bar', env, kwargs)
+ self.assertEqual(bar_dep.get_link_args(), [(p2 / 'libbar.a').as_posix()])
+ internal_dep = PkgConfigDependency('internal', env, kwargs)
+ if compiler.get_argument_syntax() == 'msvc':
+ self.assertEqual(internal_dep.get_link_args(), [])
+ else:
+ link_args = internal_dep.get_link_args()
+ for link_arg in link_args:
+ for lib in ('pthread', 'm', 'c', 'dl', 'rt'):
+ self.assertNotIn('lib{}.a'.format(lib), link_arg, msg=link_args)
+ finally:
+ # Test ends
+ PkgConfigDependency._call_pkgbin = old_call
+ PkgConfigDependency.check_pkgconfig = old_check
+ # Reset dependency class to ensure that in-process configure doesn't mess up
+ PkgConfigDependency.pkgbin_cache = {}
+ PkgConfigDependency.class_pkgbin = PerMachine(None, None, None)
def test_version_compare(self):
comparefunc = mesonbuild.mesonlib.version_compare_many
@@ -962,7 +1001,7 @@ class DataTests(unittest.TestCase):
with open('docs/markdown/Builtin-options.md') as f:
md = f.read()
self.assertIsNotNone(md)
- env = get_fake_env('', '', '')
+ env = get_fake_env()
# FIXME: Support other compilers
cc = env.detect_c_compiler(False)
cpp = env.detect_cpp_compiler(False)
@@ -1008,13 +1047,23 @@ class DataTests(unittest.TestCase):
Ensure that syntax highlighting files were updated for new functions in
the global namespace in build files.
'''
- env = get_fake_env('', '', '')
+ env = get_fake_env()
interp = Interpreter(FakeBuild(env), mock=True)
with open('data/syntax-highlighting/vim/syntax/meson.vim') as f:
res = re.search(r'syn keyword mesonBuiltin(\s+\\\s\w+)+', f.read(), re.MULTILINE)
defined = set([a.strip() for a in res.group().split('\\')][1:])
self.assertEqual(defined, set(chain(interp.funcs.keys(), interp.builtin.keys())))
+ def test_all_functions_defined_in_ast_interpreter(self):
+ '''
+ Ensure that the all functions defined in the Interpreter are also defined
+ in the AstInterpreter (and vice versa).
+ '''
+ env = get_fake_env()
+ interp = Interpreter(FakeBuild(env), mock=True)
+ astint = AstInterpreter('.', '')
+ self.assertEqual(set(interp.funcs.keys()), set(astint.funcs.keys()))
+
class BasePlatformTests(unittest.TestCase):
def setUp(self):
@@ -2201,7 +2250,7 @@ int main(int argc, char **argv) {
self.assertPathExists(os.path.join(testdir, i))
def detect_prebuild_env(self):
- env = get_fake_env('', self.builddir, self.prefix)
+ env = get_fake_env()
cc = env.detect_c_compiler(False)
stlinker = env.detect_static_linker(cc)
if mesonbuild.mesonlib.is_windows():
@@ -3302,7 +3351,7 @@ recommended as it is not supported on some platforms''')
# Check buildsystem_files
bs_files = ['meson.build', 'sharedlib/meson.build', 'staticlib/meson.build']
bs_files = [os.path.join(testdir, x) for x in bs_files]
- self.assertPathListEqual(res['buildsystem_files'], bs_files)
+ self.assertPathListEqual(list(sorted(res['buildsystem_files'])), list(sorted(bs_files)))
# Check dependencies
dependencies_to_find = ['threads']
@@ -3412,7 +3461,7 @@ class FailureTests(BasePlatformTests):
and slows down testing.
'''
dnf = "[Dd]ependency.*not found(:.*)?"
- nopkg = '[Pp]kg-config not found'
+ nopkg = '[Pp]kg-config.*not found'
def setUp(self):
super().setUp()
@@ -3493,16 +3542,29 @@ class FailureTests(BasePlatformTests):
self.assertMesonRaises("dependency('appleframeworks')",
"requires at least one module")
+ def test_extraframework_dependency_method(self):
+ code = "dependency('python', method : 'extraframework')"
+ if not is_osx():
+ self.assertMesonRaises(code, self.dnf)
+ else:
+ # Python2 framework is always available on macOS
+ self.assertMesonOutputs(code, '[Dd]ependency.*python.*found.*YES')
+
def test_sdl2_notfound_dependency(self):
# Want to test failure, so skip if available
if shutil.which('sdl2-config'):
raise unittest.SkipTest('sdl2-config found')
self.assertMesonRaises("dependency('sdl2', method : 'sdlconfig')", self.dnf)
if shutil.which('pkg-config'):
- errmsg = self.dnf
- else:
- errmsg = self.nopkg
- self.assertMesonRaises("dependency('sdl2', method : 'pkg-config')", errmsg)
+ self.assertMesonRaises("dependency('sdl2', method : 'pkg-config')", self.dnf)
+ with no_pkgconfig():
+ # Look for pkg-config, cache it, then
+ # Use cached pkg-config without erroring out, then
+ # Use cached pkg-config to error out
+ code = "dependency('foobarrr', method : 'pkg-config', required : false)\n" \
+ "dependency('foobarrr2', method : 'pkg-config', required : false)\n" \
+ "dependency('sdl2', method : 'pkg-config')"
+ self.assertMesonRaises(code, self.nopkg)
def test_gnustep_notfound_dependency(self):
# Want to test failure, so skip if available
@@ -3563,7 +3625,7 @@ class FailureTests(BasePlatformTests):
'''
Test that when we can't detect objc or objcpp, we fail gracefully.
'''
- env = get_fake_env('', self.builddir, self.prefix)
+ env = get_fake_env()
try:
env.detect_objc_compiler(False)
env.detect_objcpp_compiler(False)
@@ -3830,6 +3892,7 @@ class DarwinTests(BasePlatformTests):
self.assertIsNotNone(m, msg=out)
return m.groups()
+ @skipIfNoPkgconfig
def test_library_versioning(self):
'''
Ensure that compatibility_version and current_version are set correctly
@@ -5180,7 +5243,7 @@ class NativeFileTests(BasePlatformTests):
"""Helper for generating tests for overriding compilers for langaugages
with more than one implementation, such as C, C++, ObjC, ObjC++, and D.
"""
- env = get_fake_env('', '', '')
+ env = get_fake_env()
getter = getattr(env, 'detect_{}_compiler'.format(lang))
if lang not in ['cs']:
getter = functools.partial(getter, False)
@@ -5341,7 +5404,7 @@ class NativeFileTests(BasePlatformTests):
Builds a wrapper around the compiler to override the version.
"""
wrapper = self.helper_create_binary_wrapper(binary, version=version_str)
- env = get_fake_env('', '', '')
+ env = get_fake_env()
getter = getattr(env, 'detect_{}_compiler'.format(lang))
if lang in ['rust']:
getter = functools.partial(getter, False)
@@ -5370,7 +5433,7 @@ class NativeFileTests(BasePlatformTests):
def test_swift_compiler(self):
wrapper = self.helper_create_binary_wrapper(
'swiftc', version='Swift 1.2345', outfile='stderr')
- env = get_fake_env('', '', '')
+ env = get_fake_env()
env.binaries.host.binaries['swift'] = wrapper
compiler = env.detect_swift_compiler()
self.assertEqual(compiler.version, '1.2345')
diff --git a/test cases/common/14 configure file/meson.build b/test cases/common/14 configure file/meson.build
index 982ae2a..50393e9 100644
--- a/test cases/common/14 configure file/meson.build
+++ b/test cases/common/14 configure file/meson.build
@@ -141,6 +141,12 @@ cfile = configure_file(input : 'config.h.in',
install_dir : '',
configuration : conf)
+# test install_dir : false (deprecated)
+cfile = configure_file(input : 'config.h.in',
+ output : 'do_not_get_installed_please.h',
+ install_dir : false,
+ configuration : conf)
+
# test intsall_dir with install: false
cfile = configure_file(input : 'config.h.in',
output : 'do_not_get_installed_in_install_dir.h',
diff --git a/test cases/frameworks/25 hdf5/main.c b/test cases/frameworks/25 hdf5/main.c
new file mode 100644
index 0000000..4c46310
--- /dev/null
+++ b/test cases/frameworks/25 hdf5/main.c
@@ -0,0 +1,30 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "hdf5.h"
+
+int main(void)
+{
+herr_t ier;
+unsigned maj, min, rel;
+
+ier = H5open();
+if (ier) {
+ fprintf(stderr,"Unable to initialize HDF5: %d\n", ier);
+ return EXIT_FAILURE;
+}
+
+ier = H5get_libversion(&maj, &min, &rel);
+if (ier) {
+ fprintf(stderr,"HDF5 did not initialize!\n");
+ return EXIT_FAILURE;
+}
+printf("C HDF5 version %d.%d.%d\n", maj, min, rel);
+
+ier = H5close();
+if (ier) {
+ fprintf(stderr,"Unable to close HDF5: %d\n", ier);
+ return EXIT_FAILURE;
+}
+return EXIT_SUCCESS;
+}
diff --git a/test cases/frameworks/25 hdf5/main.cpp b/test cases/frameworks/25 hdf5/main.cpp
new file mode 100644
index 0000000..477e76b
--- /dev/null
+++ b/test cases/frameworks/25 hdf5/main.cpp
@@ -0,0 +1,29 @@
+#include <iostream>
+#include "hdf5.h"
+
+
+int main(void)
+{
+herr_t ier;
+unsigned maj, min, rel;
+
+ier = H5open();
+if (ier) {
+ std::cerr << "Unable to initialize HDF5: " << ier << std::endl;
+ return EXIT_FAILURE;
+}
+
+ier = H5get_libversion(&maj, &min, &rel);
+if (ier) {
+ std::cerr << "HDF5 did not initialize!" << std::endl;
+ return EXIT_FAILURE;
+}
+std::cout << "C++ HDF5 version " << maj << "." << min << "." << rel << std::endl;
+
+ier = H5close();
+if (ier) {
+ std::cerr << "Unable to close HDF5: " << ier << std::endl;
+ return EXIT_FAILURE;
+}
+return EXIT_SUCCESS;
+}
diff --git a/test cases/frameworks/25 hdf5/main.f90 b/test cases/frameworks/25 hdf5/main.f90
new file mode 100644
index 0000000..b21abf1
--- /dev/null
+++ b/test cases/frameworks/25 hdf5/main.f90
@@ -0,0 +1,17 @@
+use hdf5
+
+implicit none
+
+integer :: ier, major, minor, rel
+
+call h5open_f(ier)
+if (ier /= 0) error stop 'Unable to initialize HDF5'
+
+call h5get_libversion_f(major, minor, rel, ier)
+if (ier /= 0) error stop 'Unable to check HDF5 version'
+print '(A,I1,A1,I0.2,A1,I1)','Fortran HDF5 version ',major,'.',minor,'.',rel
+
+call h5close_f(ier)
+if (ier /= 0) error stop 'Unable to close HDF5 library'
+
+end program
diff --git a/test cases/frameworks/25 hdf5/meson.build b/test cases/frameworks/25 hdf5/meson.build
new file mode 100644
index 0000000..9033354
--- /dev/null
+++ b/test cases/frameworks/25 hdf5/meson.build
@@ -0,0 +1,43 @@
+project('hdf5_test', 'c', 'cpp')
+
+if build_machine.system() == 'darwin'
+ error('MESON_SKIP_TEST: HDF5 CI image not setup for OSX.')
+endif
+
+if build_machine.system() == 'cygwin'
+ error('MESON_SKIP_TEST: HDF5 CI image not setup for Cygwin.')
+endif
+
+
+# --- C tests
+h5c = dependency('hdf5', language : 'c', required : false)
+if not h5c.found()
+ error('MESON_SKIP_TEST: HDF5 C library not found, skipping HDF5 framework tests.')
+endif
+exec = executable('exec', 'main.c', dependencies : h5c)
+
+test('HDF5 C', exec)
+
+# --- C++ tests
+h5cpp = dependency('hdf5', language : 'cpp', required : false)
+if h5cpp.found()
+ execpp = executable('execpp', 'main.cpp', dependencies : h5cpp)
+ test('HDF5 C++', execpp)
+endif
+
+# --- Fortran tests
+if build_machine.system() != 'windows'
+ add_languages('fortran')
+
+ h5f = dependency('hdf5', language : 'fortran', required : false)
+ if h5f.found()
+ exef = executable('exef', 'main.f90', dependencies : h5f)
+
+ test('HDF5 Fortran', exef)
+ endif
+endif
+
+# Check we can apply a version constraint
+if h5c.version() != 'unknown'
+ dependency('hdf5', version: '>=@0@'.format(h5c.version()))
+endif
diff --git a/test cases/linuxlike/13 cmake dependency/cmake/FindSomethingLikeZLIB.cmake b/test cases/linuxlike/13 cmake dependency/cmake/FindSomethingLikeZLIB.cmake
new file mode 100644
index 0000000..a2f8456
--- /dev/null
+++ b/test cases/linuxlike/13 cmake dependency/cmake/FindSomethingLikeZLIB.cmake
@@ -0,0 +1,9 @@
+find_package(ZLIB)
+
+if(ZLIB_FOUND OR ZLIB_Found)
+ set(SomethingLikeZLIB_FOUND ON)
+ set(SomethingLikeZLIB_LIBRARIES ${ZLIB_LIBRARY})
+ set(SomethingLikeZLIB_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR})
+else()
+ set(SomethingLikeZLIB_FOUND OFF)
+endif()
diff --git a/test cases/linuxlike/13 cmake dependency/meson.build b/test cases/linuxlike/13 cmake dependency/meson.build
index 72773b2..a18cd84 100644
--- a/test cases/linuxlike/13 cmake dependency/meson.build
+++ b/test cases/linuxlike/13 cmake dependency/meson.build
@@ -36,6 +36,12 @@ depf2 = dependency('ZLIB', required : false, method : 'cmake', modules : 'dfggh:
assert(depf2.found() == false, 'Invalid CMake targets should fail')
+# Try to find a dependency with a custom CMake module
+
+depm1 = dependency('SomethingLikeZLIB', required : true, method : 'cmake', cmake_module_path : 'cmake')
+depm2 = dependency('SomethingLikeZLIB', required : true, method : 'cmake', cmake_module_path : ['cmake'])
+depm3 = dependency('SomethingLikeZLIB', required : true, cmake_module_path : 'cmake')
+
# Try to compile a test that takes a dep and an include_directories
cc = meson.get_compiler('c')
diff --git a/test cases/osx/2 library versions/meson.build b/test cases/osx/2 library versions/meson.build
index 26f945a..0d21a3a 100644
--- a/test cases/osx/2 library versions/meson.build
+++ b/test cases/osx/2 library versions/meson.build
@@ -1,15 +1,27 @@
project('library versions', 'c')
-zlib_dep = dependency('zlib')
-
-some = shared_library('some', 'lib.c',
- # duplicate the rpath again, in order
- # to test Meson's RPATH deduplication
- build_rpath : zlib_dep.get_pkgconfig_variable('libdir'),
- dependencies : zlib_dep,
- version : '1.2.3',
- soversion : '7',
- install : true)
+if run_command(find_program('require_pkgconfig.py'), check: true).stdout().strip() == 'yes'
+ required = true
+else
+ required = false
+endif
+
+zlib_dep = dependency('zlib', required: required)
+if zlib_dep.found()
+ some = shared_library('some', 'lib.c',
+ # duplicate the rpath again, in order
+ # to test Meson's RPATH deduplication
+ build_rpath : zlib_dep.get_pkgconfig_variable('libdir'),
+ dependencies : zlib_dep,
+ version : '1.2.3',
+ soversion : '7',
+ install : true)
+else
+ some = shared_library('some', 'lib.c',
+ version : '1.2.3',
+ soversion : '7',
+ install : true)
+endif
noversion = shared_library('noversion', 'lib.c',
install : true)
diff --git a/test cases/osx/2 library versions/require_pkgconfig.py b/test cases/osx/2 library versions/require_pkgconfig.py
new file mode 100644
index 0000000..3d228aa
--- /dev/null
+++ b/test cases/osx/2 library versions/require_pkgconfig.py
@@ -0,0 +1,9 @@
+#!/usr/bin/env python3
+
+import os
+import shutil
+
+if 'CI' in os.environ or shutil.which('pkg-config'):
+ print('yes')
+else:
+ print('no')
diff --git a/test cases/osx/5 extra frameworks/installed_files.txt b/test cases/osx/5 extra frameworks/installed_files.txt
new file mode 100644
index 0000000..2c6bd93
--- /dev/null
+++ b/test cases/osx/5 extra frameworks/installed_files.txt
@@ -0,0 +1,2 @@
+usr/bin/prog
+usr/lib/libstat.a
diff --git a/test cases/osx/5 extra frameworks/meson.build b/test cases/osx/5 extra frameworks/meson.build
new file mode 100644
index 0000000..cb4847e
--- /dev/null
+++ b/test cases/osx/5 extra frameworks/meson.build
@@ -0,0 +1,13 @@
+project('xcode extra framework test', 'c')
+
+dep_libs = dependency('OpenGL', method : 'extraframework')
+assert(dep_libs.type_name() == 'extraframeworks', 'type_name is ' + dep_libs.type_name())
+
+dep_main = dependency('Foundation')
+assert(dep_main.type_name() == 'extraframeworks', 'type_name is ' + dep_main.type_name())
+
+dep_py = dependency('python', method : 'extraframework')
+assert(dep_main.type_name() == 'extraframeworks', 'type_name is ' + dep_main.type_name())
+
+stlib = static_library('stat', 'stat.c', install : true, dependencies: dep_libs)
+exe = executable('prog', 'prog.c', install : true, dependencies: dep_main)
diff --git a/test cases/osx/5 extra frameworks/prog.c b/test cases/osx/5 extra frameworks/prog.c
new file mode 100644
index 0000000..11b7fad
--- /dev/null
+++ b/test cases/osx/5 extra frameworks/prog.c
@@ -0,0 +1,3 @@
+int main(int argc, char **argv) {
+ return 0;
+}
diff --git a/test cases/osx/5 extra frameworks/stat.c b/test cases/osx/5 extra frameworks/stat.c
new file mode 100644
index 0000000..fa76a65
--- /dev/null
+++ b/test cases/osx/5 extra frameworks/stat.c
@@ -0,0 +1 @@
+int func() { return 933; }