aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/os_comp.yml15
-rw-r--r--.travis.yml4
-rw-r--r--CODEOWNERS4
-rw-r--r--ci/ciimage/arch/Dockerfile4
-rwxr-xr-xci/ciimage/arch/install.sh48
-rw-r--r--ci/ciimage/eoan/Dockerfile (renamed from ciimage/Dockerfile)0
-rw-r--r--cross/ccrx.txt8
-rw-r--r--mesonbuild/compilers/c.py3
-rw-r--r--mesonbuild/compilers/mixins/arm.py2
-rw-r--r--mesonbuild/compilers/mixins/ccrx.py8
-rw-r--r--mesonbuild/coredata.py31
-rw-r--r--mesonbuild/dependencies/__init__.py55
-rw-r--r--mesonbuild/dependencies/base.py356
-rw-r--r--mesonbuild/dependencies/boost.py8
-rw-r--r--mesonbuild/dependencies/coarrays.py80
-rw-r--r--mesonbuild/dependencies/cuda.py2
-rw-r--r--mesonbuild/dependencies/dev.py138
-rw-r--r--mesonbuild/dependencies/hdf5.py2
-rw-r--r--mesonbuild/dependencies/misc.py419
-rw-r--r--mesonbuild/dependencies/mpi.py2
-rw-r--r--mesonbuild/dependencies/platform.py2
-rw-r--r--mesonbuild/dependencies/scalapack.py2
-rw-r--r--mesonbuild/dependencies/ui.py137
-rw-r--r--mesonbuild/linkers.py3
-rw-r--r--mesonbuild/modules/pkgconfig.py6
-rw-r--r--mesonbuild/modules/python.py2
-rwxr-xr-xrun_cross_test.py34
-rwxr-xr-xrun_project_tests.py26
-rwxr-xr-xrun_tests.py32
-rwxr-xr-xrun_unittests.py86
-rw-r--r--test cases/common/105 testframework options/meson.build3
-rw-r--r--test cases/failing/38 libdir must be inside prefix/meson.build4
-rw-r--r--test cases/frameworks/20 cups/meson.build1
-rw-r--r--test cases/java/3 args/meson.build4
34 files changed, 814 insertions, 717 deletions
diff --git a/.github/workflows/os_comp.yml b/.github/workflows/os_comp.yml
index 387b9c1..a4417a1 100644
--- a/.github/workflows/os_comp.yml
+++ b/.github/workflows/os_comp.yml
@@ -1,10 +1,10 @@
-name: OS Compatibility Tests
+name: OS Comp Tests
on: [push, pull_request]
jobs:
xenial:
- name: Ubuntu 16.04 (xenial)
+ name: Ubuntu 16.04
runs-on: ubuntu-16.04
steps:
- uses: actions/checkout@v1
@@ -25,3 +25,14 @@ jobs:
env:
CI: '1'
XENIAL: '1'
+
+ arch:
+ name: Arch Linux
+ runs-on: ubuntu-latest
+ container: mensinda/arch:latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Run tests
+ run: ./run_tests.py
+ env:
+ CI: '1'
diff --git a/.travis.yml b/.travis.yml
index 3c66670..ed59a85 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -33,10 +33,10 @@ matrix:
# Test cross builds separately, they do not use the global compiler
- os: linux
compiler: gcc
- env: RUN_TESTS_ARGS="--cross"
+ env: RUN_TESTS_ARGS="--cross ubuntu-armhf.txt --cross linux-mingw-w64-64bit.txt"
- os: linux
compiler: gcc
- env: RUN_TESTS_ARGS="--cross" MESON_ARGS="--unity=on"
+ env: RUN_TESTS_ARGS="--cross ubuntu-armhf.txt --cross linux-mingw-w64-64bit.txt" MESON_ARGS="--unity=on"
before_install:
- python ./skip_ci.py --base-branch-env=TRAVIS_BRANCH --is-pull-env=TRAVIS_PULL_REQUEST
diff --git a/CODEOWNERS b/CODEOWNERS
new file mode 100644
index 0000000..50c0627
--- /dev/null
+++ b/CODEOWNERS
@@ -0,0 +1,4 @@
+* @jpakkane
+/mesonbuild/modules/pkgconfig.py @xclaesse
+/mesonbuild/modules/cmake.py @mensinda
+/mesonbuild/cmake/* @mensinda
diff --git a/ci/ciimage/arch/Dockerfile b/ci/ciimage/arch/Dockerfile
new file mode 100644
index 0000000..b8a36cd
--- /dev/null
+++ b/ci/ciimage/arch/Dockerfile
@@ -0,0 +1,4 @@
+FROM archlinux:latest
+
+ADD install.sh /usr/sbin/docker-arch-install
+RUN docker-arch-install
diff --git a/ci/ciimage/arch/install.sh b/ci/ciimage/arch/install.sh
new file mode 100755
index 0000000..f7f0f6d
--- /dev/null
+++ b/ci/ciimage/arch/install.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# Inspired by https://github.com/greyltc/docker-archlinux-aur/blob/master/add-aur.sh
+
+pkgs=(
+ python python-setuptools python-wheel python-pip python-pytest-xdist python-gobject
+ ninja make git sudo fakeroot autoconf automake patch
+ libelf gcc gcc-fortran gcc-objc vala rust bison flex cython go dlang-dmd
+ mono boost qt5-base gtkmm3 gtest gmock protobuf wxgtk gobject-introspection
+ itstool gtk3 java-environment=8 gtk-doc llvm clang sdl2 graphviz
+ doxygen vulkan-validation-layers openssh mercurial gtk-sharp-2 qt5-tools
+ libwmf valgrind cmake netcdf-fortran openmpi nasm gnustep-base gettext
+ # cuda
+)
+
+aur_pkgs=(hotdoc scalapack)
+cleanup_pkgs=(go)
+
+AUR_USER=docker
+PACMAN_OPTS='--needed --noprogressbar --noconfirm'
+
+# Patch config files
+sed -i 's/#Color/Color/g' /etc/pacman.conf
+sed -i 's,#MAKEFLAGS="-j2",MAKEFLAGS="-j$(nproc)",g' /etc/makepkg.conf
+sed -i "s,PKGEXT='.pkg.tar.xz',PKGEXT='.pkg.tar',g" /etc/makepkg.conf
+
+# Install packages
+pacman -Syu $PACMAN_OPTS "${pkgs[@]}"
+
+# Setup the user
+useradd -m $AUR_USER
+echo "${AUR_USER}:" | chpasswd -e
+echo "$AUR_USER ALL = NOPASSWD: ALL" >> /etc/sudoers
+
+# Install yay
+su $AUR_USER -c 'cd; git clone https://aur.archlinux.org/yay.git'
+su $AUR_USER -c 'cd; cd yay; makepkg'
+pushd /home/$AUR_USER/yay/
+pacman -U *.pkg.tar --noprogressbar --noconfirm
+popd
+rm -rf /home/$AUR_USER/yay
+
+# Install yay deps
+su $AUR_USER -c "yay -S $PACMAN_OPTS ${aur_pkgs[*]}"
+
+# cleanup
+pacman -Rs --noconfirm "${cleanup_pkgs[@]}"
+su $AUR_USER -c "yes | yay -Scc"
diff --git a/ciimage/Dockerfile b/ci/ciimage/eoan/Dockerfile
index a98662c..a98662c 100644
--- a/ciimage/Dockerfile
+++ b/ci/ciimage/eoan/Dockerfile
diff --git a/cross/ccrx.txt b/cross/ccrx.txt
index 5474bb0..097ec06 100644
--- a/cross/ccrx.txt
+++ b/cross/ccrx.txt
@@ -10,11 +10,13 @@ strip = 'rlink'
[properties]
# The '--cpu' option with the appropriate target type should be mentioned
# to cross compile c/c++ code with ccrx,.
-c_args = ['--cpu=rx600']
-cpp_args = ['--cpu=rx600']
+c_args = ['-cpu=rx600']
+cpp_args = ['-cpu=rx600']
+c_link_args = []
+cpp_link_args = []
[host_machine]
-system = 'bare metal' # Update with your system name - bare metal/OS.
+system = 'bare metal'
cpu_family = 'rx'
cpu = 'rx600'
endian = 'little'
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index a01b1f9..87f48e6 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -390,6 +390,9 @@ class CcrxCCompiler(CcrxCompiler, CCompiler):
'none')})
return opts
+ def get_no_stdinc_args(self):
+ return []
+
def get_option_compile_args(self, options):
args = []
std = options['c_std']
diff --git a/mesonbuild/compilers/mixins/arm.py b/mesonbuild/compilers/mixins/arm.py
index d15faec..42b2142 100644
--- a/mesonbuild/compilers/mixins/arm.py
+++ b/mesonbuild/compilers/mixins/arm.py
@@ -157,7 +157,7 @@ class ArmclangCompiler:
self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage',
'b_ndebug', 'b_staticpic', 'b_colorout']
# Assembly
- self.can_compile_suffixes.update('s')
+ self.can_compile_suffixes.add('s')
def get_pic_args(self) -> T.List[str]:
# PIC support is not enabled by default for ARM,
diff --git a/mesonbuild/compilers/mixins/ccrx.py b/mesonbuild/compilers/mixins/ccrx.py
index 5e61805..b859215 100644
--- a/mesonbuild/compilers/mixins/ccrx.py
+++ b/mesonbuild/compilers/mixins/ccrx.py
@@ -52,7 +52,7 @@ class CcrxCompiler:
raise EnvironmentException('ccrx supports only cross-compilation.')
self.id = 'ccrx'
# Assembly
- self.can_compile_suffixes.update('s')
+ self.can_compile_suffixes.add('src')
default_warn_args = [] # type: T.List[str]
self.warn_args = {'0': [],
'1': default_warn_args,
@@ -83,6 +83,12 @@ class CcrxCompiler:
def get_coverage_args(self) -> T.List[str]:
return []
+ def get_no_stdinc_args(self) -> T.List[str]:
+ return []
+
+ def get_no_stdlib_link_args(self) -> T.List[str]:
+ return []
+
def get_optimization_args(self, optimization_level: str) -> T.List[str]:
return ccrx_optimization_args[optimization_level]
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 9d5e7ce..7a9fefe 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -463,7 +463,7 @@ class CoreData:
prefix = prefix[:-1]
return prefix
- def sanitize_dir_option_value(self, prefix, option, value):
+ def sanitize_dir_option_value(self, prefix: str, option: str, value: Any) -> Any:
'''
If the option is an installation directory option and the value is an
absolute path, check that it resides within prefix and return the value
@@ -471,22 +471,31 @@ class CoreData:
This way everyone can do f.ex, get_option('libdir') and be sure to get
the library directory relative to prefix.
+
+ .as_posix() keeps the posix-like file seperators Meson uses.
'''
- if option.endswith('dir') and os.path.isabs(value) and \
+ try:
+ value = PurePath(value)
+ except TypeError:
+ return value
+ if option.endswith('dir') and value.is_absolute() and \
option not in builtin_dir_noprefix_options:
# Value must be a subdir of the prefix
# commonpath will always return a path in the native format, so we
# must use pathlib.PurePath to do the same conversion before
# comparing.
- if os.path.commonpath([value, prefix]) != str(PurePath(prefix)):
- m = 'The value of the {!r} option is {!r} which must be a ' \
- 'subdir of the prefix {!r}.\nNote that if you pass a ' \
- 'relative path, it is assumed to be a subdir of prefix.'
- raise MesonException(m.format(option, value, prefix))
- # Convert path to be relative to prefix
- skip = len(prefix) + 1
- value = value[skip:]
- return value
+ msg = ('The value of the {!r} option is {!r} which must be a '
+ 'subdir of the prefix {!r}.\nNote that if you pass a '
+ 'relative path, it is assumed to be a subdir of prefix.')
+ # os.path.commonpath doesn't understand case-insensitive filesystems,
+ # but PurePath().relative_to() does.
+ try:
+ value = value.relative_to(prefix)
+ except ValueError:
+ raise MesonException(msg.format(option, value, prefix))
+ if '..' in str(value):
+ raise MesonException(msg.format(option, value, prefix))
+ return value.as_posix()
def init_builtins(self):
# Create builtin options with default values
diff --git a/mesonbuild/dependencies/__init__.py b/mesonbuild/dependencies/__init__.py
index cc8817d..d961117 100644
--- a/mesonbuild/dependencies/__init__.py
+++ b/mesonbuild/dependencies/__init__.py
@@ -18,58 +18,67 @@ from .hdf5 import HDF5Dependency
from .base import ( # noqa: F401
Dependency, DependencyException, DependencyMethods, ExternalProgram, EmptyExternalProgram, NonExistingExternalProgram,
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 .coarrays import CoarrayDependency
+ PkgConfigDependency, CMakeDependency, find_external_dependency, get_dep_identifier, packages, _packages_accept_language,
+ DependencyFactory)
+from .dev import ValgrindDependency, gmock_factory, gtest_factory, llvm_factory
+from .coarrays import coarray_factory
from .mpi import MPIDependency
from .scalapack import ScalapackDependency
-from .misc import (BlocksDependency, CursesDependency, NetCDFDependency, OpenMPDependency, Python3Dependency, ThreadDependency,
- PcapDependency, CupsDependency, LibWmfDependency, LibGCryptDependency, GpgmeDependency, ShadercDependency)
+from .misc import (
+ BlocksDependency, OpenMPDependency, cups_factory, curses_factory, gpgme_factory,
+ libgcrypt_factory, libwmf_factory, netcdf_factory, pcap_factory, python3_factory,
+ shaderc_factory, threads_factory,
+)
from .platform import AppleFrameworks
-from .ui import GLDependency, GnuStepDependency, Qt4Dependency, Qt5Dependency, SDL2Dependency, WxDependency, VulkanDependency
+from .ui import GnuStepDependency, Qt4Dependency, Qt5Dependency, WxDependency, gl_factory, sdl2_factory, vulkan_factory
+# This is a dict where the keys should be strings, and the values must be one
+# of:
+# - An ExternalDependency subclass
+# - A DependencyFactory object
+# - A callable with a signature of (Environment, MachineChoice, Dict[str, Any]) -> List[Callable[[], DependencyType]]
packages.update({
# From dev:
- 'gtest': GTestDependency,
- 'gmock': GMockDependency,
- 'llvm': LLVMDependency,
+ 'gtest': gtest_factory,
+ 'gmock': gmock_factory,
+ 'llvm': llvm_factory,
'valgrind': ValgrindDependency,
'boost': BoostDependency,
'cuda': CudaDependency,
# per-file
- 'coarray': CoarrayDependency,
+ 'coarray': coarray_factory,
'hdf5': HDF5Dependency,
'mpi': MPIDependency,
'scalapack': ScalapackDependency,
# From misc:
'blocks': BlocksDependency,
- 'curses': CursesDependency,
- 'netcdf': NetCDFDependency,
+ 'curses': curses_factory,
+ 'netcdf': netcdf_factory,
'openmp': OpenMPDependency,
- 'python3': Python3Dependency,
- 'threads': ThreadDependency,
- 'pcap': PcapDependency,
- 'cups': CupsDependency,
- 'libwmf': LibWmfDependency,
- 'libgcrypt': LibGCryptDependency,
- 'gpgme': GpgmeDependency,
- 'shaderc': ShadercDependency,
+ 'python3': python3_factory,
+ 'threads': threads_factory,
+ 'pcap': pcap_factory,
+ 'cups': cups_factory,
+ 'libwmf': libwmf_factory,
+ 'libgcrypt': libgcrypt_factory,
+ 'gpgme': gpgme_factory,
+ 'shaderc': shaderc_factory,
# From platform:
'appleframeworks': AppleFrameworks,
# From ui:
- 'gl': GLDependency,
+ 'gl': gl_factory,
'gnustep': GnuStepDependency,
'qt4': Qt4Dependency,
'qt5': Qt5Dependency,
- 'sdl2': SDL2Dependency,
+ 'sdl2': sdl2_factory,
'wxwidgets': WxDependency,
- 'vulkan': VulkanDependency,
+ 'vulkan': vulkan_factory,
})
_packages_accept_language.update({
'hdf5',
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index e9d1f89..d014415 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -37,6 +37,10 @@ from ..mesonlib import MachineChoice, MesonException, OrderedSet, PerMachine
from ..mesonlib import Popen_safe, version_compare_many, version_compare, listify, stringlistify, extract_as_list, split_args
from ..mesonlib import Version, LibType
+if T.TYPE_CHECKING:
+ from ..compilers.compilers import Compiler # noqa: F401
+ DependencyType = T.TypeVar('DependencyType', bound='Dependency')
+
# These must be defined in this file to avoid cyclical references.
packages = {}
_packages_accept_language = set()
@@ -70,38 +74,6 @@ class DependencyMethods(Enum):
class Dependency:
- @classmethod
- def _process_method_kw(cls, kwargs):
- method = kwargs.get('method', 'auto')
- if isinstance(method, DependencyMethods):
- return method
- if method not in [e.value for e in DependencyMethods]:
- raise DependencyException('method {!r} is invalid'.format(method))
- method = DependencyMethods(method)
-
- # This sets per-tool config methods which are deprecated to to the new
- # generic CONFIG_TOOL value.
- if method in [DependencyMethods.SDLCONFIG, DependencyMethods.CUPSCONFIG,
- DependencyMethods.PCAPCONFIG, DependencyMethods.LIBWMFCONFIG]:
- mlog.warning(textwrap.dedent("""\
- Configuration method {} has been deprecated in favor of
- 'config-tool'. This will be removed in a future version of
- meson.""".format(method)))
- method = DependencyMethods.CONFIG_TOOL
-
- # Set the detection method. If the method is set to auto, use any available method.
- # If method is set to a specific string, allow only that detection method.
- if method == DependencyMethods.AUTO:
- methods = cls.get_methods()
- elif method in cls.get_methods():
- methods = [method]
- else:
- raise DependencyException(
- 'Unsupported detection method: {}, allowed methods are {}'.format(
- method.value,
- mlog.format_list([x.value for x in [DependencyMethods.AUTO] + cls.get_methods()])))
-
- return methods
@classmethod
def _process_include_type_kw(cls, kwargs) -> str:
@@ -125,7 +97,7 @@ class Dependency:
# If None, self.link_args will be used
self.raw_link_args = None
self.sources = []
- self.methods = self._process_method_kw(kwargs)
+ self.methods = process_method_kw(self.get_methods(), kwargs)
self.include_type = self._process_include_type_kw(kwargs)
self.ext_deps = [] # type: T.List[Dependency]
@@ -208,19 +180,21 @@ class Dependency:
"""
raise RuntimeError('Unreachable code in partial_dependency called')
- def _add_sub_dependency(self, dep_type: T.Type['Dependency'], env: Environment,
- kwargs: T.Dict[str, T.Any], *,
- method: DependencyMethods = DependencyMethods.AUTO) -> None:
- """Add an internal dependency of of the given type.
+ def _add_sub_dependency(self, deplist: T.List['DependencyType']) -> bool:
+ """Add an internal depdency from a list of possible dependencies.
- This method is intended to simplify cases of adding a dependency on
- another dependency type (such as threads). This will by default set
- the method back to auto, but the 'method' keyword argument can be
- used to overwrite this behavior.
+ This method is intended to make it easier to add additional
+ dependencies to another dependency internally.
+
+ Returns true if the dependency was successfully added, false
+ otherwise.
"""
- kwargs = kwargs.copy()
- kwargs['method'] = method
- self.ext_deps.append(dep_type(env, kwargs))
+ for d in deplist:
+ dep = d()
+ if dep.is_found:
+ self.ext_deps.append(dep)
+ return True
+ return False
def get_variable(self, *, cmake: T.Optional[str] = None, pkgconfig: T.Optional[str] = None,
configtool: T.Optional[str] = None, internal: T.Optional[str] = None,
@@ -293,7 +267,7 @@ class HasNativeKwarg:
return MachineChoice.BUILD if kwargs.get('native', False) else MachineChoice.HOST
class ExternalDependency(Dependency, HasNativeKwarg):
- def __init__(self, type_name, environment, language, kwargs):
+ def __init__(self, type_name, environment, kwargs, language: T.Optional[str] = None):
Dependency.__init__(self, type_name, kwargs)
self.env = environment
self.name = type_name # default
@@ -309,25 +283,7 @@ class ExternalDependency(Dependency, HasNativeKwarg):
raise DependencyException('Static keyword must be boolean')
# Is this dependency to be run on the build platform?
HasNativeKwarg.__init__(self, kwargs)
- self.clib_compiler = None
- # Set the compiler that will be used by this dependency
- # This is only used for configuration checks
- compilers = self.env.coredata.compilers[self.for_machine]
- # Set the compiler for this dependency if a language is specified,
- # else try to pick something that looks usable.
- if self.language:
- if self.language not in compilers:
- m = self.name.capitalize() + ' requires a {0} compiler, but ' \
- '{0} is not in the list of project languages'
- raise DependencyException(m.format(self.language.capitalize()))
- self.clib_compiler = compilers[self.language]
- else:
- # Try to find a compiler that can find C libraries for
- # running compiler.find_library()
- for lang in clib_langs:
- self.clib_compiler = compilers.get(lang, None)
- if self.clib_compiler:
- break
+ self.clib_compiler = detect_compiler(self.name, environment, self.for_machine, self.language)
def get_compiler(self):
return self.clib_compiler
@@ -393,6 +349,25 @@ class ExternalDependency(Dependency, HasNativeKwarg):
raise DependencyException(m.format(self.name, not_found, self.version))
return
+ # Create an iterator of options
+ def search_tool(self, name, display_name, default_names):
+ # Lookup in cross or machine file.
+ potential_path = self.env.binaries[self.for_machine].lookup_entry(name)
+ if potential_path is not None:
+ mlog.debug('{} binary for {} specified from cross file, native file, '
+ 'or env var as {}'.format(display_name, self.for_machine, potential_path))
+ yield ExternalProgram.from_entry(name, potential_path)
+ # We never fallback if the user-specified option is no good, so
+ # stop returning options.
+ return
+ mlog.debug('{} binary missing from cross or native file, or env var undefined.'.format(display_name))
+ # Fallback on hard-coded defaults.
+ # TODO prefix this for the cross case instead of ignoring thing.
+ if self.env.machines.matches_build_machine(self.for_machine):
+ for potential_path in default_names:
+ mlog.debug('Trying a default {} fallback at'.format(display_name), potential_path)
+ yield ExternalProgram(potential_path, silent=True)
+
class NotFoundDependency(Dependency):
def __init__(self, environment):
@@ -415,8 +390,8 @@ class ConfigToolDependency(ExternalDependency):
tool_name = None
__strip_version = re.compile(r'^[0-9.]*')
- def __init__(self, name, environment, language, kwargs):
- super().__init__('config-tool', environment, language, kwargs)
+ def __init__(self, name, environment, kwargs, language: T.Optional[str] = None):
+ super().__init__('config-tool', environment, kwargs, language=language)
self.name = name
self.tools = listify(kwargs.get('tools', self.tools))
@@ -440,30 +415,6 @@ class ConfigToolDependency(ExternalDependency):
return m.group(0).rstrip('.')
return version
- @classmethod
- def factory(cls, name, environment, language, kwargs, tools, tool_name, finish_init=None):
- """Constructor for use in dependencies that can be found multiple ways.
-
- In addition to the standard constructor values, this constructor sets
- the tool_name and tools values of the instance.
- """
- # This deserves some explanation, because metaprogramming is hard.
- # This uses type() to create a dynamic subclass of ConfigToolDependency
- # with the tools and tool_name class attributes set, this class is then
- # instantiated and returned. The reduce function (method) is also
- # attached, since python's pickle module won't be able to do anything
- # with this dynamically generated class otherwise.
- def reduce(self):
- return (cls._unpickle, (), self.__dict__)
- sub = type('{}Dependency'.format(name.capitalize()), (cls, ),
- {'tools': tools, 'tool_name': tool_name, '__reduce__': reduce, 'finish_init': staticmethod(finish_init)})
-
- return sub(name, environment, language, kwargs)
-
- @classmethod
- def _unpickle(cls):
- return cls.__new__(cls)
-
def find_config(self, versions=None):
"""Helper method that searches for config tool binaries in PATH and
returns the one that best matches the given version requirements.
@@ -595,33 +546,14 @@ class PkgConfigDependency(ExternalDependency):
# We cache all pkg-config subprocess invocations to avoid redundant calls
pkgbin_cache = {}
- def __init__(self, name, environment, kwargs, language=None):
- super().__init__('pkgconfig', environment, language, kwargs)
+ def __init__(self, name, environment, kwargs, language: T.Optional[str] = None):
+ super().__init__('pkgconfig', environment, kwargs, language=language)
self.name = name
self.is_libtool = False
# Store a copy of the pkg-config path on the object itself so it is
# stored in the pickled coredata and recovered.
self.pkgbin = None
- # Create an iterator of options
- def search():
- # Lookup in cross or machine file.
- potential_pkgpath = environment.binaries[self.for_machine].lookup_entry('pkgconfig')
- if potential_pkgpath is not None:
- mlog.debug('Pkg-config binary for {} specified from cross file, native file, '
- 'or env var as {}'.format(self.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.
- return
- mlog.debug('Pkg-config binary missing from cross or native file, or env var undefined.')
- # Fallback on hard-coded defaults.
- # TODO prefix this for the cross case instead of ignoring thing.
- if environment.machines.matches_build_machine(self.for_machine):
- for potential_pkgpath in environment.default_pkgconfig:
- mlog.debug('Trying a default pkg-config fallback at', potential_pkgpath)
- yield ExternalProgram(potential_pkgpath, silent=True)
-
# Only search for pkg-config for each machine the first time and store
# the result in the class definition
if PkgConfigDependency.class_pkgbin[self.for_machine] is False:
@@ -631,7 +563,7 @@ class PkgConfigDependency(ExternalDependency):
else:
assert PkgConfigDependency.class_pkgbin[self.for_machine] is None
mlog.debug('Pkg-config binary for %s is not cached.' % self.for_machine)
- for potential_pkgbin in search():
+ for potential_pkgbin in self.search_tool('pkgconfig', 'Pkg-config', environment.default_pkgconfig):
mlog.debug('Trying pkg-config binary {} for machine {} at {}'
.format(potential_pkgbin.name, self.for_machine, potential_pkgbin.command))
version_if_ok = self.check_pkgconfig(potential_pkgbin)
@@ -1075,7 +1007,7 @@ class CMakeDependency(ExternalDependency):
# one module
return module
- def __init__(self, name: str, environment: Environment, kwargs, language: str = None):
+ def __init__(self, name: str, environment: Environment, kwargs, language: T.Optional[str] = None):
# Gather a list of all languages to support
self.language_list = [] # type: T.List[str]
if language is None:
@@ -1097,7 +1029,7 @@ class CMakeDependency(ExternalDependency):
# Ensure that the list is unique
self.language_list = list(set(self.language_list))
- super().__init__('cmake', environment, language, kwargs)
+ super().__init__('cmake', environment, kwargs, language=language)
self.name = name
self.is_libtool = False
# Store a copy of the CMake path on the object itself so it is
@@ -1152,6 +1084,7 @@ class CMakeDependency(ExternalDependency):
cm_args.append('-DCMAKE_PREFIX_PATH={}'.format(';'.join(pref_path)))
if not self._preliminary_find_check(name, cm_path, pref_path, environment.machines[self.for_machine]):
+ mlog.debug('Preliminary CMake check failed. Aborting.')
return
self._detect_dep(name, modules, cm_args)
@@ -1259,9 +1192,12 @@ class CMakeDependency(ExternalDependency):
if not self._cached_isdir(i):
continue
- for j in ['Find{}.cmake', '{}Config.cmake', '{}-config.cmake']:
- if os.path.isfile(os.path.join(i, j.format(name))):
- return True
+ # Check the directory case insensitve
+ content = self._cached_listdir(i)
+ candidates = ['Find{}.cmake', '{}Config.cmake', '{}-config.cmake']
+ candidates = [x.format(name).lower() for x in candidates]
+ if any([x[1] in candidates for x in content]):
+ return True
return False
# Search in <path>/(lib/<arch>|lib*|share) for cmake files
@@ -1598,7 +1534,7 @@ class DubDependency(ExternalDependency):
class_dubbin = None
def __init__(self, name, environment, kwargs):
- super().__init__('dub', environment, 'd', kwargs)
+ super().__init__('dub', environment, kwargs, language='d')
self.name = name
self.compiler = super().get_compiler()
self.module_path = None
@@ -2060,7 +1996,7 @@ class EmptyExternalProgram(ExternalProgram): # lgtm [py/missing-call-to-init]
class ExternalLibrary(ExternalDependency):
def __init__(self, name, link_args, environment, language, silent=False):
- super().__init__('library', environment, language, {})
+ super().__init__('library', environment, {}, language=language)
self.name = name
self.language = language
self.is_found = False
@@ -2102,10 +2038,10 @@ class ExternalLibrary(ExternalDependency):
class ExtraFrameworkDependency(ExternalDependency):
system_framework_paths = None
- def __init__(self, name, required, paths, env, lang, kwargs):
- super().__init__('extraframeworks', env, lang, kwargs)
+ def __init__(self, name, env, kwargs, language: T.Optional[str] = None):
+ paths = kwargs.get('paths', [])
+ super().__init__('extraframeworks', env, kwargs, language=language)
self.name = name
- self.required = required
# Full path to framework directory
self.framework_path = None
if not self.clib_compiler:
@@ -2201,6 +2137,83 @@ class ExtraFrameworkDependency(ExternalDependency):
return 'framework'
+class DependencyFactory:
+
+ """Factory to get dependencies from multiple sources.
+
+ This class provides an initializer that takes a set of names and classes
+ for various kinds of dependencies. When the initialized object is called
+ it returns a list of callables return Dependency objects to try in order.
+
+ :name: The name of the dependency. This will be passed as the name
+ parameter of the each dependency unless it is overridden on a per
+ type basis.
+ :methods: An ordered list of DependencyMethods. This is the order
+ dependencies will be returned in unless they are removed by the
+ _process_method function
+ :*_name: This will overwrite the name passed to the coresponding class.
+ For example, if the name is 'zlib', but cmake calls the dependency
+ 'Z', then using `cmake_name='Z'` will pass the name as 'Z' to cmake.
+ :*_class: A *type* or callable that creates a class, and has the
+ signature of an ExternalDependency
+ :system_class: If you pass DependencyMethods.SYSTEM in methods, you must
+ set this argument.
+ """
+
+ def __init__(self, name: str, methods: T.List[DependencyMethods], *,
+ extra_kwargs: T.Optional[T.Dict[str, T.Any]] = None,
+ pkgconfig_name: T.Optional[str] = None,
+ pkgconfig_class: 'T.Type[PkgConfigDependency]' = PkgConfigDependency,
+ cmake_name: T.Optional[str] = None,
+ cmake_class: 'T.Type[CMakeDependency]' = CMakeDependency,
+ configtool_class: 'T.Optional[T.Type[ConfigToolDependency]]' = None,
+ framework_name: T.Optional[str] = None,
+ framework_class: 'T.Type[ExtraFrameworkDependency]' = ExtraFrameworkDependency,
+ system_class: 'T.Type[ExternalDependency]' = ExternalDependency):
+
+ if DependencyMethods.CONFIG_TOOL in methods and not configtool_class:
+ raise DependencyException('A configtool must have a custom class')
+
+ self.extra_kwargs = extra_kwargs or {}
+ self.methods = methods
+ self.classes = {
+ # Just attach the correct name right now, either the generic name
+ # or the method specific name.
+ DependencyMethods.EXTRAFRAMEWORK: functools.partial(framework_class, framework_name or name),
+ DependencyMethods.PKGCONFIG: functools.partial(pkgconfig_class, pkgconfig_name or name),
+ DependencyMethods.CMAKE: functools.partial(cmake_class, cmake_name or name),
+ DependencyMethods.SYSTEM: functools.partial(system_class, name),
+ DependencyMethods.CONFIG_TOOL: None,
+ }
+ if configtool_class is not None:
+ self.classes[DependencyMethods.CONFIG_TOOL] = functools.partial(configtool_class, name)
+
+ @staticmethod
+ def _process_method(method: DependencyMethods, env: Environment, for_machine: MachineChoice) -> bool:
+ """Report whether a method is valid or not.
+
+ If the method is valid, return true, otherwise return false. This is
+ used in a list comprehension to filter methods that are not possible.
+
+ By default this only remove EXTRAFRAMEWORK dependencies for non-mac platforms.
+ """
+ # Extra frameworks are only valid for macOS and other apple products
+ if (method is DependencyMethods.EXTRAFRAMEWORK and
+ not env.machines[for_machine].is_darwin()):
+ return False
+ return True
+
+ def __call__(self, env: Environment, for_machine: MachineChoice,
+ kwargs: T.Dict[str, T.Any]) -> T.List['DependencyType']:
+ """Return a list of Dependencies with the arguments already attached."""
+ methods = process_method_kw(self.methods, kwargs)
+ nwargs = self.extra_kwargs.copy()
+ nwargs.update(kwargs)
+
+ return [functools.partial(self.classes[m], env, nwargs) for m in methods
+ if self._process_method(m, env, for_machine)]
+
+
def get_dep_identifier(name, kwargs) -> T.Tuple:
identifier = (name, )
for key, value in kwargs.items():
@@ -2252,7 +2265,7 @@ def find_external_dependency(name, env, kwargs):
type_text = PerMachine('Build-time', 'Run-time')[for_machine] + ' dependency'
# build a list of dependency methods to try
- candidates = _build_external_dependency_list(name, env, kwargs)
+ candidates = _build_external_dependency_list(name, env, for_machine, kwargs)
pkg_exc = []
pkgdep = []
@@ -2315,7 +2328,8 @@ def find_external_dependency(name, env, kwargs):
return NotFoundDependency(env)
-def _build_external_dependency_list(name, env: Environment, kwargs: T.Dict[str, T.Any]) -> list:
+def _build_external_dependency_list(name: str, env: Environment, for_machine: MachineChoice,
+ kwargs: T.Dict[str, T.Any]) -> T.List['DependencyType']:
# First check if the method is valid
if 'method' in kwargs and kwargs['method'] not in [e.value for e in DependencyMethods]:
raise DependencyException('method {!r} is invalid'.format(kwargs['method']))
@@ -2326,10 +2340,10 @@ def _build_external_dependency_list(name, env: Environment, kwargs: T.Dict[str,
# Create the list of dependency object constructors using a factory
# class method, if one exists, otherwise the list just consists of the
# constructor
- if getattr(packages[lname], '_factory', None):
- dep = packages[lname]._factory(env, kwargs)
- else:
+ if isinstance(packages[lname], type) and issubclass(packages[lname], Dependency):
dep = [functools.partial(packages[lname], env, kwargs)]
+ else:
+ dep = packages[lname](env, for_machine, kwargs)
return dep
candidates = []
@@ -2353,8 +2367,7 @@ def _build_external_dependency_list(name, env: Environment, kwargs: T.Dict[str,
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))
+ candidates.append(functools.partial(ExtraFrameworkDependency, name, env, kwargs))
return candidates
# Otherwise, just use the pkgconfig and cmake dependency detector
@@ -2363,8 +2376,7 @@ def _build_external_dependency_list(name, env: Environment, kwargs: T.Dict[str,
# On OSX, also try framework dependency detector
if mesonlib.is_osx():
- candidates.append(functools.partial(ExtraFrameworkDependency, name,
- False, None, env, None, kwargs))
+ candidates.append(functools.partial(ExtraFrameworkDependency, name, env, kwargs))
# Only use CMake as a last resort, since it might not work 100% (see #6113)
candidates.append(functools.partial(CMakeDependency, name, env, kwargs))
@@ -2405,3 +2417,85 @@ def strip_system_libdirs(environment, for_machine: MachineChoice, link_args):
"""
exclude = {'-L{}'.format(p) for p in environment.get_compiler_system_dirs(for_machine)}
return [l for l in link_args if l not in exclude]
+
+
+def process_method_kw(possible: T.List[DependencyMethods], kwargs) -> T.List[DependencyMethods]:
+ method = kwargs.get('method', 'auto')
+ if isinstance(method, DependencyMethods):
+ return method
+ if method not in [e.value for e in DependencyMethods]:
+ raise DependencyException('method {!r} is invalid'.format(method))
+ method = DependencyMethods(method)
+
+ # This sets per-tool config methods which are deprecated to to the new
+ # generic CONFIG_TOOL value.
+ if method in [DependencyMethods.SDLCONFIG, DependencyMethods.CUPSCONFIG,
+ DependencyMethods.PCAPCONFIG, DependencyMethods.LIBWMFCONFIG]:
+ mlog.warning(textwrap.dedent("""\
+ Configuration method {} has been deprecated in favor of
+ 'config-tool'. This will be removed in a future version of
+ meson.""".format(method)))
+ method = DependencyMethods.CONFIG_TOOL
+
+ # Set the detection method. If the method is set to auto, use any available method.
+ # If method is set to a specific string, allow only that detection method.
+ if method == DependencyMethods.AUTO:
+ methods = possible
+ elif method in possible:
+ methods = [method]
+ else:
+ raise DependencyException(
+ 'Unsupported detection method: {}, allowed methods are {}'.format(
+ method.value,
+ mlog.format_list([x.value for x in [DependencyMethods.AUTO] + possible])))
+
+ return methods
+
+
+if T.TYPE_CHECKING:
+ FactoryType = T.Callable[[Environment, MachineChoice, T.Dict[str, T.Any]],
+ T.List['DependencyType']]
+ FullFactoryType = T.Callable[[Environment, MachineChoice, T.Dict[str, T.Any], T.Set[DependencyMethods]],
+ T.List['DependencyType']]
+
+
+def factory_methods(methods: T.Set[DependencyMethods]) -> 'FactoryType':
+ """Decorator for handling methods for dependency factory functions.
+
+ This helps to make factory functions self documenting
+ >>> @factory_methods([DependencyMethods.PKGCONFIG, DependencyMethods.CMAKE])
+ >>> def factory(env: Environment, for_machine: MachineChoice, kwargs: T.Dict[str, T.Any], methods: T.Set[DependencyMethods]) -> T.List[DependencyType]:
+ >>> pass
+ """
+
+ def inner(func: 'FullFactoryType') -> 'FactoryType':
+
+ @functools.wraps(func)
+ def wrapped(env: Environment, for_machine: MachineChoice, kwargs: T.Dict[str, T.Any]) -> T.List['DependencyType']:
+ return func(env, for_machine, kwargs, process_method_kw(methods, kwargs))
+
+ return wrapped
+
+ return inner
+
+
+def detect_compiler(name: str, env: Environment, for_machine: MachineChoice,
+ language: T.Optional[str]) -> T.Optional['Compiler']:
+ """Given a language and environment find the compiler used."""
+ compilers = env.coredata.compilers[for_machine]
+
+ # Set the compiler for this dependency if a language is specified,
+ # else try to pick something that looks usable.
+ if language:
+ if language not in compilers:
+ m = name.capitalize() + ' requires a {0} compiler, but ' \
+ '{0} is not in the list of project languages'
+ raise DependencyException(m.format(language.capitalize()))
+ return compilers[language]
+ else:
+ for lang in clib_langs:
+ try:
+ return compilers[lang]
+ except KeyError:
+ continue
+ return None
diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py
index 340a5a9..9169039 100644
--- a/mesonbuild/dependencies/boost.py
+++ b/mesonbuild/dependencies/boost.py
@@ -22,7 +22,7 @@ from .. import mesonlib
from ..environment import detect_cpu_family
from .base import (DependencyException, ExternalDependency)
-from .misc import ThreadDependency
+from .misc import threads_factory
# On windows 3 directory layouts are supported:
# * The default layout (versioned) installed:
@@ -97,7 +97,7 @@ from .misc import ThreadDependency
class BoostDependency(ExternalDependency):
def __init__(self, environment, kwargs):
- super().__init__('boost', environment, 'cpp', kwargs)
+ super().__init__('boost', environment, kwargs, language='cpp')
self.need_static_link = ['boost_exception', 'boost_test_exec_monitor']
self.is_debug = environment.coredata.get_builtin_option('buildtype').startswith('debug')
threading = kwargs.get("threading", "multi")
@@ -105,7 +105,9 @@ class BoostDependency(ExternalDependency):
self.requested_modules = self.get_requested(kwargs)
if 'thread' in self.requested_modules:
- self._add_sub_dependency(ThreadDependency, environment, kwargs)
+ if not self._add_sub_dependency(threads_factory(environment, self.for_machine, {})):
+ self.is_found = False
+ return
self.boost_root = None
self.boost_roots = []
diff --git a/mesonbuild/dependencies/coarrays.py b/mesonbuild/dependencies/coarrays.py
index b0b6cf4..84c3412 100644
--- a/mesonbuild/dependencies/coarrays.py
+++ b/mesonbuild/dependencies/coarrays.py
@@ -12,8 +12,39 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from ..mesonlib import listify
-from .base import CMakeDependency, DependencyMethods, ExternalDependency, PkgConfigDependency
+import functools
+import typing as T
+
+from .base import CMakeDependency, DependencyMethods, ExternalDependency, PkgConfigDependency, detect_compiler, factory_methods
+
+if T.TYPE_CHECKING:
+ from . base import DependencyType
+ from ..environment import Environment, MachineChoice
+
+
+@factory_methods({DependencyMethods.PKGCONFIG, DependencyMethods.CMAKE, DependencyMethods.SYSTEM})
+def coarray_factory(env: 'Environment', for_machine: 'MachineChoice',
+ kwargs: T.Dict[str, T.Any], methods: T.List[DependencyMethods]) -> T.List['DependencyType']:
+ fcid = detect_compiler('coarray', env, for_machine, 'fortran').get_id()
+ candidates = [] # type: T.List[DependencyType]
+
+ if fcid == 'gcc':
+ # OpenCoarrays is the most commonly used method for Fortran Coarray with GCC
+ if DependencyMethods.PKGCONFIG in methods:
+ for pkg in ['caf-openmpi', 'caf']:
+ candidates.append(functools.partial(
+ PkgConfigDependency, pkg, env, kwargs, language='fortran'))
+
+ if DependencyMethods.CMAKE in methods:
+ if 'modules' not in kwargs:
+ kwargs['modules'] = 'OpenCoarrays::caf_mpi'
+ candidates.append(functools.partial(
+ CMakeDependency, 'OpenCoarrays', env, kwargs, language='fortran'))
+
+ if DependencyMethods.SYSTEM in methods:
+ candidates.append(functools.partial(CoarrayDependency, env, kwargs))
+
+ return candidates
class CoarrayDependency(ExternalDependency):
@@ -26,56 +57,27 @@ class CoarrayDependency(ExternalDependency):
low-level MPI calls.
"""
def __init__(self, environment, kwargs: dict):
- super().__init__('coarray', environment, 'fortran', kwargs)
+ super().__init__('coarray', environment, kwargs, language='fortran')
kwargs['required'] = False
kwargs['silent'] = True
- self.is_found = False
- methods = listify(self.methods)
cid = self.get_compiler().get_id()
if cid == 'gcc':
- """ OpenCoarrays is the most commonly used method for Fortran Coarray with GCC """
-
- if set([DependencyMethods.AUTO, DependencyMethods.PKGCONFIG]).intersection(methods):
- for pkg in ['caf-openmpi', 'caf']:
- pkgdep = PkgConfigDependency(pkg, environment, kwargs, language=self.language)
- if pkgdep.found():
- self.compile_args = pkgdep.get_compile_args()
- self.link_args = pkgdep.get_link_args()
- self.version = pkgdep.get_version()
- self.is_found = True
- self.pcdep = pkgdep
- return
-
- if set([DependencyMethods.AUTO, DependencyMethods.CMAKE]).intersection(methods):
- if not kwargs.get('modules'):
- kwargs['modules'] = 'OpenCoarrays::caf_mpi'
- cmakedep = CMakeDependency('OpenCoarrays', environment, kwargs, language=self.language)
- if cmakedep.found():
- self.compile_args = cmakedep.get_compile_args()
- self.link_args = cmakedep.get_link_args()
- self.version = cmakedep.get_version()
- self.is_found = True
- return
-
- if DependencyMethods.AUTO in methods:
- # fallback to single image
- self.compile_args = ['-fcoarray=single']
- self.version = 'single image (fallback)'
- self.is_found = True
- return
-
+ # Fallback to single image
+ self.compile_args = ['-fcoarray=single']
+ self.version = 'single image (fallback)'
+ self.is_found = True
elif cid == 'intel':
- """ Coarrays are built into Intel compilers, no external library needed """
+ # Coarrays are built into Intel compilers, no external library needed
self.is_found = True
self.link_args = ['-coarray=shared']
self.compile_args = self.link_args
elif cid == 'intel-cl':
- """ Coarrays are built into Intel compilers, no external library needed """
+ # Coarrays are built into Intel compilers, no external library needed
self.is_found = True
self.compile_args = ['/Qcoarray:shared']
elif cid == 'nagfor':
- """ NAG doesn't require any special arguments for Coarray """
+ # NAG doesn't require any special arguments for Coarray
self.is_found = True
@staticmethod
diff --git a/mesonbuild/dependencies/cuda.py b/mesonbuild/dependencies/cuda.py
index 7048e81..9c189be 100644
--- a/mesonbuild/dependencies/cuda.py
+++ b/mesonbuild/dependencies/cuda.py
@@ -33,7 +33,7 @@ class CudaDependency(ExternalDependency):
if language not in self.supported_languages:
raise DependencyException('Language \'{}\' is not supported by the CUDA Toolkit. Supported languages are {}.'.format(language, self.supported_languages))
- super().__init__('cuda', environment, language, kwargs)
+ super().__init__('cuda', environment, kwargs, language=language)
self.requested_modules = self.get_requested(kwargs)
if 'cudart' not in self.requested_modules:
self.requested_modules = ['cudart'] + self.requested_modules
diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py
index 49f8fea..4bec71a 100644
--- a/mesonbuild/dependencies/dev.py
+++ b/mesonbuild/dependencies/dev.py
@@ -15,21 +15,22 @@
# This file contains the detection logic for external dependencies useful for
# development purposes, such as testing, debugging, etc..
-import functools
import glob
import os
import re
+import typing as T
from .. import mesonlib, mlog
from ..mesonlib import version_compare, stringlistify, extract_as_list, MachineChoice
from ..environment import get_llvm_tool_names
from .base import (
DependencyException, DependencyMethods, ExternalDependency, PkgConfigDependency,
- strip_system_libdirs, ConfigToolDependency, CMakeDependency
+ strip_system_libdirs, ConfigToolDependency, CMakeDependency, DependencyFactory,
)
-from .misc import ThreadDependency
+from .misc import threads_factory
-import typing as T
+if T.TYPE_CHECKING:
+ from .. environment import Environment
def get_shared_library_suffix(environment, for_machine: MachineChoice):
@@ -44,13 +45,15 @@ def get_shared_library_suffix(environment, for_machine: MachineChoice):
return '.so'
-class GTestDependency(ExternalDependency):
- def __init__(self, environment, kwargs):
- super().__init__('gtest', environment, 'cpp', kwargs)
+class GTestDependencySystem(ExternalDependency):
+ def __init__(self, name: str, environment, kwargs):
+ super().__init__(name, environment, kwargs, language='cpp')
self.main = kwargs.get('main', False)
self.src_dirs = ['/usr/src/gtest/src', '/usr/src/googletest/googletest/src']
+ if not self._add_sub_dependency(threads_factory(environment, self.for_machine, {})):
+ self.is_found = False
+ return
self.detect()
- self._add_sub_dependency(ThreadDependency, environment, kwargs)
def detect(self):
gtest_detect = self.clib_compiler.find_library("gtest", self.env, [])
@@ -98,30 +101,27 @@ class GTestDependency(ExternalDependency):
def log_tried(self):
return 'system'
- @classmethod
- def _factory(cls, environment, kwargs):
- methods = cls._process_method_kw(kwargs)
- candidates = []
-
- if DependencyMethods.PKGCONFIG in methods:
- pcname = 'gtest_main' if kwargs.get('main', False) else 'gtest'
- candidates.append(functools.partial(PkgConfigDependency, pcname, environment, kwargs))
-
- if DependencyMethods.SYSTEM in methods:
- candidates.append(functools.partial(GTestDependency, environment, kwargs))
-
- return candidates
-
@staticmethod
def get_methods():
return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM]
-class GMockDependency(ExternalDependency):
- def __init__(self, environment, kwargs):
- super().__init__('gmock', environment, 'cpp', kwargs)
+class GTestDependencyPC(PkgConfigDependency):
+
+ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ assert name == 'gtest'
+ if kwargs.get('main'):
+ name = 'gtest_main'
+ super().__init__(name, environment, kwargs)
+
+
+class GMockDependencySystem(ExternalDependency):
+ def __init__(self, name: str, environment, kwargs):
+ super().__init__(name, environment, kwargs, language='cpp')
self.main = kwargs.get('main', False)
- self._add_sub_dependency(ThreadDependency, environment, kwargs)
+ if not self._add_sub_dependency(threads_factory(environment, self.for_machine, {})):
+ self.is_found = False
+ return
# If we are getting main() from GMock, we definitely
# want to avoid linking in main() from GTest
@@ -132,11 +132,10 @@ class GMockDependency(ExternalDependency):
# GMock without GTest is pretty much useless
# this also mimics the structure given in WrapDB,
# where GMock always pulls in GTest
- gtest_dep = GTestDependency(environment, gtest_kwargs)
- if not gtest_dep.is_found:
+ found = self._add_sub_dependency(gtest_factory(environment, self.for_machine, gtest_kwargs))
+ if not found:
self.is_found = False
return
- self.ext_deps.append(gtest_dep)
# GMock may be a library or just source.
# Work with both.
@@ -177,25 +176,20 @@ class GMockDependency(ExternalDependency):
def log_tried(self):
return 'system'
- @classmethod
- def _factory(cls, environment, kwargs):
- methods = cls._process_method_kw(kwargs)
- candidates = []
-
- if DependencyMethods.PKGCONFIG in methods:
- pcname = 'gmock_main' if kwargs.get('main', False) else 'gmock'
- candidates.append(functools.partial(PkgConfigDependency, pcname, environment, kwargs))
-
- if DependencyMethods.SYSTEM in methods:
- candidates.append(functools.partial(GMockDependency, environment, kwargs))
-
- return candidates
-
@staticmethod
def get_methods():
return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM]
+class GMockDependencyPC(PkgConfigDependency):
+
+ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
+ assert name == 'gmock'
+ if kwargs.get('main'):
+ name = 'gmock_main'
+ super().__init__(name, environment, kwargs)
+
+
class LLVMDependencyConfigTool(ConfigToolDependency):
"""
LLVM uses a special tool, llvm-config, which has arguments for getting
@@ -204,7 +198,7 @@ class LLVMDependencyConfigTool(ConfigToolDependency):
tool_name = 'llvm-config'
__cpp_blacklist = {'-DNDEBUG'}
- def __init__(self, environment, kwargs):
+ def __init__(self, name: str, environment, kwargs):
self.tools = get_llvm_tool_names('llvm-config')
# Fedora starting with Fedora 30 adds a suffix of the number
@@ -218,7 +212,7 @@ class LLVMDependencyConfigTool(ConfigToolDependency):
# It's necessary for LLVM <= 3.8 to use the C++ linker. For 3.9 and 4.0
# the C linker works fine if only using the C API.
- super().__init__('LLVM', environment, 'cpp', kwargs)
+ super().__init__(name, environment, kwargs, language='cpp')
self.provided_modules = []
self.required_modules = set()
self.module_details = []
@@ -241,7 +235,9 @@ class LLVMDependencyConfigTool(ConfigToolDependency):
self._set_old_link_args()
self.link_args = strip_system_libdirs(environment, self.for_machine, self.link_args)
self.link_args = self.__fix_bogus_link_args(self.link_args)
- self._add_sub_dependency(ThreadDependency, environment, kwargs)
+ if not self._add_sub_dependency(threads_factory(environment, self.for_machine, {})):
+ self.is_found = False
+ return
def __fix_bogus_link_args(self, args):
"""This function attempts to fix bogus link arguments that llvm-config
@@ -391,10 +387,10 @@ class LLVMDependencyConfigTool(ConfigToolDependency):
return ''
class LLVMDependencyCMake(CMakeDependency):
- def __init__(self, env, kwargs):
+ def __init__(self, name: str, env, kwargs):
self.llvm_modules = stringlistify(extract_as_list(kwargs, 'modules'))
self.llvm_opt_modules = stringlistify(extract_as_list(kwargs, 'optional_modules'))
- super().__init__(name='LLVM', environment=env, language='cpp', kwargs=kwargs)
+ super().__init__(name, env, kwargs, language='cpp')
if self.traceparser is None:
return
@@ -404,7 +400,9 @@ class LLVMDependencyCMake(CMakeDependency):
defs = self.traceparser.get_cmake_var('PACKAGE_DEFINITIONS')
temp = ['-I' + x for x in inc_dirs] + defs
self.compile_args += [x for x in temp if x not in self.compile_args]
- self._add_sub_dependency(ThreadDependency, env, kwargs)
+ if not self._add_sub_dependency(threads_factory(env, self.for_machine, {})):
+ self.is_found = False
+ return
def _main_cmake_file(self) -> str:
# Use a custom CMakeLists.txt for LLVM
@@ -433,26 +431,6 @@ class LLVMDependencyCMake(CMakeDependency):
return orig_name[0]
return module
-class LLVMDependency(ExternalDependency):
- def __init__(self, env, kwargs):
- super().__init__('LLVM', env, 'cpp', kwargs)
-
- @classmethod
- def _factory(cls, env, kwargs):
- methods = cls._process_method_kw(kwargs)
- candidates = []
-
- if DependencyMethods.CONFIG_TOOL in methods:
- candidates.append(functools.partial(LLVMDependencyConfigTool, env, kwargs))
-
- if DependencyMethods.CMAKE in methods:
- candidates.append(functools.partial(LLVMDependencyCMake, env, kwargs))
-
- return candidates
-
- @staticmethod
- def get_methods():
- return [DependencyMethods.CMAKE, DependencyMethods.CONFIG_TOOL]
class ValgrindDependency(PkgConfigDependency):
'''
@@ -464,3 +442,25 @@ class ValgrindDependency(PkgConfigDependency):
def get_link_args(self, **kwargs):
return []
+
+
+llvm_factory = DependencyFactory(
+ 'LLVM',
+ [DependencyMethods.CMAKE, DependencyMethods.CONFIG_TOOL],
+ cmake_class=LLVMDependencyCMake,
+ configtool_class=LLVMDependencyConfigTool,
+)
+
+gtest_factory = DependencyFactory(
+ 'gtest',
+ [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM],
+ pkgconfig_class=GTestDependencyPC,
+ system_class=GTestDependencySystem,
+)
+
+gmock_factory = DependencyFactory(
+ 'gmock',
+ [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM],
+ pkgconfig_class=GMockDependencyPC,
+ system_class=GMockDependencySystem,
+)
diff --git a/mesonbuild/dependencies/hdf5.py b/mesonbuild/dependencies/hdf5.py
index 3bb1fda..fadd109 100644
--- a/mesonbuild/dependencies/hdf5.py
+++ b/mesonbuild/dependencies/hdf5.py
@@ -27,7 +27,7 @@ class HDF5Dependency(ExternalDependency):
def __init__(self, environment, kwargs):
language = kwargs.get('language', 'c')
- super().__init__('hdf5', environment, language, kwargs)
+ super().__init__('hdf5', environment, kwargs, language=language)
kwargs['required'] = False
kwargs['silent'] = True
self.is_found = False
diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py
index 9077222..07948c9 100644
--- a/mesonbuild/dependencies/misc.py
+++ b/mesonbuild/dependencies/misc.py
@@ -18,64 +18,44 @@ from pathlib import Path
import functools
import re
import sysconfig
+import typing as T
from .. import mlog
from .. import mesonlib
from ..environment import detect_cpu_family
-from ..mesonlib import listify
from .base import (
DependencyException, DependencyMethods, ExternalDependency,
- ExtraFrameworkDependency, PkgConfigDependency,
- CMakeDependency, ConfigToolDependency,
+ PkgConfigDependency, CMakeDependency, ConfigToolDependency,
+ factory_methods, DependencyFactory,
)
+if T.TYPE_CHECKING:
+ from ..environment import Environment, MachineChoice
+ from .base import DependencyType # noqa: F401
-class NetCDFDependency(ExternalDependency):
- def __init__(self, environment, kwargs):
- language = kwargs.get('language', 'c')
- super().__init__('netcdf', environment, language, kwargs)
- kwargs['required'] = False
- kwargs['silent'] = True
- self.is_found = False
- methods = listify(self.methods)
-
- if language not in ('c', 'cpp', 'fortran'):
- raise DependencyException('Language {} is not supported with NetCDF.'.format(language))
+@factory_methods({DependencyMethods.PKGCONFIG, DependencyMethods.CMAKE})
+def netcdf_factory(env: 'Environment', for_machine: 'MachineChoice',
+ kwargs: T.Dict[str, T.Any], methods: T.List[DependencyMethods]) -> T.List['DependencyType']:
+ language = kwargs.get('language', 'c')
+ if language not in ('c', 'cpp', 'fortran'):
+ raise DependencyException('Language {} is not supported with NetCDF.'.format(language))
- if set([DependencyMethods.AUTO, DependencyMethods.PKGCONFIG]).intersection(methods):
- pkgconfig_files = ['netcdf']
+ candidates = [] # type: T.List['DependencyType']
- if language == 'fortran':
- pkgconfig_files.append('netcdf-fortran')
+ if DependencyMethods.PKGCONFIG in methods:
+ pkgconfig_files = ['netcdf']
+ if language == 'fortran':
+ pkgconfig_files.append('netcdf-fortran')
- self.compile_args = []
- self.link_args = []
- self.pcdep = []
- for pkg in pkgconfig_files:
- pkgdep = PkgConfigDependency(pkg, environment, kwargs, language=self.language)
- if pkgdep.found():
- self.compile_args.extend(pkgdep.get_compile_args())
- self.link_args.extend(pkgdep.get_link_args())
- self.version = pkgdep.get_version()
- self.is_found = True
- self.pcdep.append(pkgdep)
- if self.is_found:
- return
+ for pkg in pkgconfig_files:
+ candidates.append(functools.partial(PkgConfigDependency, pkg, env, kwargs, language=language))
- if set([DependencyMethods.AUTO, DependencyMethods.CMAKE]).intersection(methods):
- cmakedep = CMakeDependency('NetCDF', environment, kwargs, language=self.language)
- if cmakedep.found():
- self.compile_args = cmakedep.get_compile_args()
- self.link_args = cmakedep.get_link_args()
- self.version = cmakedep.get_version()
- self.is_found = True
- return
+ if DependencyMethods.CMAKE in methods:
+ candidates.append(functools.partial(CMakeDependency, 'NetCDF', env, kwargs, language=language))
- @staticmethod
- def get_methods():
- return [DependencyMethods.AUTO, DependencyMethods.PKGCONFIG, DependencyMethods.CMAKE]
+ return candidates
class OpenMPDependency(ExternalDependency):
@@ -94,7 +74,7 @@ class OpenMPDependency(ExternalDependency):
def __init__(self, environment, kwargs):
language = kwargs.get('language')
- super().__init__('openmp', environment, language, kwargs)
+ super().__init__('openmp', environment, kwargs, language=language)
self.is_found = False
if self.clib_compiler.get_id() == 'pgi':
# through at least PGI 19.4, there is no macro defined for OpenMP, but OpenMP 3.1 is supported.
@@ -124,33 +104,17 @@ class OpenMPDependency(ExternalDependency):
class ThreadDependency(ExternalDependency):
- def __init__(self, environment, kwargs):
- super().__init__('threads', environment, None, kwargs)
- self.name = 'threads'
- self.is_found = False
- methods = listify(self.methods)
- if DependencyMethods.AUTO in methods:
- self.is_found = True
- # Happens if you are using a language with threads
- # concept without C, such as plain Cuda.
- if self.clib_compiler is None:
- self.compile_args = []
- self.link_args = []
- else:
- self.compile_args = self.clib_compiler.thread_flags(environment)
- self.link_args = self.clib_compiler.thread_link_flags(environment)
- return
-
- if DependencyMethods.CMAKE in methods:
- # for unit tests and for those who simply want
- # dependency('threads', method: 'cmake')
- cmakedep = CMakeDependency('Threads', environment, kwargs)
- if cmakedep.found():
- self.compile_args = cmakedep.get_compile_args()
- self.link_args = cmakedep.get_link_args()
- self.version = cmakedep.get_version()
- self.is_found = True
- return
+ def __init__(self, name: str, environment, kwargs):
+ super().__init__(name, environment, kwargs)
+ self.is_found = True
+ # Happens if you are using a language with threads
+ # concept without C, such as plain Cuda.
+ if self.clib_compiler is None:
+ self.compile_args = []
+ self.link_args = []
+ else:
+ self.compile_args = self.clib_compiler.thread_flags(environment)
+ self.link_args = self.clib_compiler.thread_link_flags(environment)
@staticmethod
def get_methods():
@@ -159,7 +123,7 @@ class ThreadDependency(ExternalDependency):
class BlocksDependency(ExternalDependency):
def __init__(self, environment, kwargs):
- super().__init__('blocks', environment, None, kwargs)
+ super().__init__('blocks', environment, kwargs)
self.name = 'blocks'
self.is_found = False
@@ -190,12 +154,14 @@ class BlocksDependency(ExternalDependency):
self.is_found = True
-class Python3Dependency(ExternalDependency):
- def __init__(self, environment, kwargs):
- super().__init__('python3', environment, None, kwargs)
+class Python3DependencySystem(ExternalDependency):
+ def __init__(self, name, environment, kwargs):
+ super().__init__(name, environment, kwargs)
if not environment.machines.matches_build_machine(self.for_machine):
return
+ if not environment.machines[self.for_machine].is_windows():
+ return
self.name = 'python3'
self.static = kwargs.get('static', False)
@@ -203,28 +169,6 @@ class Python3Dependency(ExternalDependency):
self.version = '3'
self._find_libpy3_windows(environment)
- @classmethod
- def _factory(cls, environment, kwargs):
- methods = cls._process_method_kw(kwargs)
- candidates = []
-
- if DependencyMethods.PKGCONFIG in methods:
- candidates.append(functools.partial(PkgConfigDependency, 'python3', environment, kwargs))
-
- if DependencyMethods.SYSCONFIG in methods:
- candidates.append(functools.partial(Python3Dependency, environment, kwargs))
-
- if DependencyMethods.EXTRAFRAMEWORK in methods:
- # In OSX the Python 3 framework does not have a version
- # number in its name.
- # 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'],
- environment, kwargs.get('language', None), kwargs))
-
- return candidates
-
@staticmethod
def get_windows_python_arch():
pyplat = sysconfig.get_platform()
@@ -322,83 +266,41 @@ class Python3Dependency(ExternalDependency):
def log_tried(self):
return 'sysconfig'
-class PcapDependency(ExternalDependency):
-
- def __init__(self, environment, kwargs):
- super().__init__('pcap', environment, None, kwargs)
-
- @classmethod
- def _factory(cls, environment, kwargs):
- methods = cls._process_method_kw(kwargs)
- candidates = []
-
- if DependencyMethods.PKGCONFIG in methods:
- candidates.append(functools.partial(PkgConfigDependency, 'pcap', environment, kwargs))
+class PcapDependencyConfigTool(ConfigToolDependency):
- if DependencyMethods.CONFIG_TOOL in methods:
- candidates.append(functools.partial(ConfigToolDependency.factory,
- 'pcap', environment, None,
- kwargs, ['pcap-config'],
- 'pcap-config',
- PcapDependency.tool_finish_init))
-
- return candidates
+ tools = ['pcap-config']
+ tool_name = 'pcap-config'
@staticmethod
- def tool_finish_init(ctdep):
- ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args')
- ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args')
- ctdep.version = PcapDependency.get_pcap_lib_version(ctdep)
+ def finish_init(self) -> None:
+ self.compile_args = self.get_config_value(['--cflags'], 'compile_args')
+ self.link_args = self.get_config_value(['--libs'], 'link_args')
+ self.version = self.get_pcap_lib_version()
@staticmethod
def get_methods():
return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL]
- @staticmethod
- def get_pcap_lib_version(ctdep):
+ def get_pcap_lib_version(self):
# Since we seem to need to run a program to discover the pcap version,
# we can't do that when cross-compiling
- if not ctdep.env.machines.matches_build_machine(ctdep.for_machine):
+ if not self.env.machines.matches_build_machine(self.for_machine):
return None
- v = ctdep.clib_compiler.get_return_value('pcap_lib_version', 'string',
- '#include <pcap.h>', ctdep.env, [], [ctdep])
+ v = self.clib_compiler.get_return_value('pcap_lib_version', 'string',
+ '#include <pcap.h>', self.env, [], [self])
v = re.sub(r'libpcap version ', '', v)
v = re.sub(r' -- Apple version.*$', '', v)
return v
-class CupsDependency(ExternalDependency):
- def __init__(self, environment, kwargs):
- super().__init__('cups', environment, None, kwargs)
-
- @classmethod
- def _factory(cls, environment, kwargs):
- methods = cls._process_method_kw(kwargs)
- candidates = []
+class CupsDependencyConfigTool(ConfigToolDependency):
- if DependencyMethods.PKGCONFIG in methods:
- candidates.append(functools.partial(PkgConfigDependency, 'cups', environment, kwargs))
-
- if DependencyMethods.CONFIG_TOOL in methods:
- candidates.append(functools.partial(ConfigToolDependency.factory,
- 'cups', environment, None,
- kwargs, ['cups-config'],
- 'cups-config', CupsDependency.tool_finish_init))
-
- if DependencyMethods.EXTRAFRAMEWORK in methods:
- if mesonlib.is_osx():
- candidates.append(functools.partial(
- ExtraFrameworkDependency, 'cups', False, None, environment,
- kwargs.get('language', None), kwargs))
-
- if DependencyMethods.CMAKE in methods:
- candidates.append(functools.partial(CMakeDependency, 'Cups', environment, kwargs))
-
- return candidates
+ tools = ['cups-config']
+ tool_name = 'cups-config'
@staticmethod
- def tool_finish_init(ctdep):
+ def finish_init(ctdep):
ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args')
ctdep.link_args = ctdep.get_config_value(['--ldflags', '--libs'], 'link_args')
@@ -410,26 +312,13 @@ class CupsDependency(ExternalDependency):
return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.CMAKE]
-class LibWmfDependency(ExternalDependency):
- def __init__(self, environment, kwargs):
- super().__init__('libwmf', environment, None, kwargs)
-
- @classmethod
- def _factory(cls, environment, kwargs):
- methods = cls._process_method_kw(kwargs)
- candidates = []
-
- if DependencyMethods.PKGCONFIG in methods:
- candidates.append(functools.partial(PkgConfigDependency, 'libwmf', environment, kwargs))
-
- if DependencyMethods.CONFIG_TOOL in methods:
- candidates.append(functools.partial(ConfigToolDependency.factory,
- 'libwmf', environment, None, kwargs, ['libwmf-config'], 'libwmf-config', LibWmfDependency.tool_finish_init))
+class LibWmfDependencyConfigTool(ConfigToolDependency):
- return candidates
+ tools = ['libwmf-config']
+ tool_name = 'libwmf-config'
@staticmethod
- def tool_finish_init(ctdep):
+ def finish_init(ctdep):
ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args')
ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args')
@@ -438,28 +327,13 @@ class LibWmfDependency(ExternalDependency):
return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL]
-class LibGCryptDependency(ExternalDependency):
- def __init__(self, environment, kwargs):
- super().__init__('libgcrypt', environment, None, kwargs)
-
- @classmethod
- def _factory(cls, environment, kwargs):
- methods = cls._process_method_kw(kwargs)
- candidates = []
-
- if DependencyMethods.PKGCONFIG in methods:
- candidates.append(functools.partial(PkgConfigDependency, 'libgcrypt', environment, kwargs))
+class LibGCryptDependencyConfigTool(ConfigToolDependency):
- if DependencyMethods.CONFIG_TOOL in methods:
- candidates.append(functools.partial(ConfigToolDependency.factory,
- 'libgcrypt', environment, None, kwargs, ['libgcrypt-config'],
- 'libgcrypt-config',
- LibGCryptDependency.tool_finish_init))
-
- return candidates
+ tools = ['libgcrypt-config']
+ tool_name = 'libgcrypt-config'
@staticmethod
- def tool_finish_init(ctdep):
+ def finish_init(ctdep):
ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args')
ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args')
ctdep.version = ctdep.get_config_value(['--version'], 'version')[0]
@@ -469,28 +343,13 @@ class LibGCryptDependency(ExternalDependency):
return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL]
-class GpgmeDependency(ExternalDependency):
- def __init__(self, environment, kwargs):
- super().__init__('gpgme', environment, None, kwargs)
-
- @classmethod
- def _factory(cls, environment, kwargs):
- methods = cls._process_method_kw(kwargs)
- candidates = []
-
- if DependencyMethods.PKGCONFIG in methods:
- candidates.append(functools.partial(PkgConfigDependency, 'gpgme', environment, kwargs))
-
- if DependencyMethods.CONFIG_TOOL in methods:
- candidates.append(functools.partial(ConfigToolDependency.factory,
- 'gpgme', environment, None, kwargs, ['gpgme-config'],
- 'gpgme-config',
- GpgmeDependency.tool_finish_init))
+class GpgmeDependencyConfigTool(ConfigToolDependency):
- return candidates
+ tools = ['gpgme-config']
+ tool_name = 'gpg-config'
@staticmethod
- def tool_finish_init(ctdep):
+ def finish_init(ctdep):
ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args')
ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args')
ctdep.version = ctdep.get_config_value(['--version'], 'version')[0]
@@ -503,7 +362,7 @@ class GpgmeDependency(ExternalDependency):
class ShadercDependency(ExternalDependency):
def __init__(self, environment, kwargs):
- super().__init__('shaderc', environment, None, kwargs)
+ super().__init__('shaderc', environment, kwargs)
static_lib = 'shaderc_combined'
shared_lib = 'shaderc_shared'
@@ -528,55 +387,103 @@ class ShadercDependency(ExternalDependency):
def log_tried(self):
return 'system'
- @classmethod
- def _factory(cls, environment, kwargs):
- methods = cls._process_method_kw(kwargs)
- candidates = []
-
- if DependencyMethods.PKGCONFIG in methods:
- # ShaderC packages their shared and static libs together
- # and provides different pkg-config files for each one. We
- # smooth over this difference by handling the static
- # keyword before handing off to the pkg-config handler.
- shared_libs = ['shaderc']
- static_libs = ['shaderc_combined', 'shaderc_static']
-
- if kwargs.get('static', False):
- c = [functools.partial(PkgConfigDependency, name, environment, kwargs)
- for name in static_libs + shared_libs]
- else:
- c = [functools.partial(PkgConfigDependency, name, environment, kwargs)
- for name in shared_libs + static_libs]
- candidates.extend(c)
-
- if DependencyMethods.SYSTEM in methods:
- candidates.append(functools.partial(ShadercDependency, environment, kwargs))
-
- return candidates
-
@staticmethod
def get_methods():
return [DependencyMethods.SYSTEM, DependencyMethods.PKGCONFIG]
-class CursesDependency(ExternalDependency):
- def __init__(self, environment, kwargs):
- super().__init__('curses', environment, None, kwargs)
- self.name = 'curses'
- self.is_found = False
- methods = listify(self.methods)
-
- if set([DependencyMethods.AUTO, DependencyMethods.PKGCONFIG]).intersection(methods):
- pkgconfig_files = ['ncurses', 'ncursesw']
- for pkg in pkgconfig_files:
- pkgdep = PkgConfigDependency(pkg, environment, kwargs)
- if pkgdep.found():
- self.compile_args = pkgdep.get_compile_args()
- self.link_args = pkgdep.get_link_args()
- self.version = pkgdep.get_version()
- self.is_found = True
- self.pcdep = pkgdep
- return
- @staticmethod
- def get_methods():
- return [DependencyMethods.AUTO, DependencyMethods.PKGCONFIG]
+@factory_methods({DependencyMethods.PKGCONFIG})
+def curses_factory(env: 'Environment', for_machine: 'MachineChoice',
+ kwargs: T.Dict[str, T.Any], methods: T.List[DependencyMethods]) -> T.List['DependencyType']:
+ candidates = [] # type: T.List['DependencyType']
+
+ if DependencyMethods.PKGCONFIG in methods:
+ pkgconfig_files = ['ncurses', 'ncursesw']
+ for pkg in pkgconfig_files:
+ candidates.append(functools.partial(PkgConfigDependency, pkg, env, kwargs))
+
+ return candidates
+
+
+@factory_methods({DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM})
+def shaderc_factory(env: 'Environment', for_machine: 'MachineChoice',
+ kwargs: T.Dict[str, T.Any], methods: T.List[DependencyMethods]) -> T.List['DependencyType']:
+ """Custom DependencyFactory for ShaderC.
+
+ ShaderC's odd you get three different libraries from the same build
+ thing are just easier to represent as a separate function than
+ twisting DependencyFactory even more.
+ """
+ candidates = [] # type: T.List['DependencyType']
+
+ if DependencyMethods.PKGCONFIG in methods:
+ # ShaderC packages their shared and static libs together
+ # and provides different pkg-config files for each one. We
+ # smooth over this difference by handling the static
+ # keyword before handing off to the pkg-config handler.
+ shared_libs = ['shaderc']
+ static_libs = ['shaderc_combined', 'shaderc_static']
+
+ if kwargs.get('static', False):
+ c = [functools.partial(PkgConfigDependency, name, env, kwargs)
+ for name in static_libs + shared_libs]
+ else:
+ c = [functools.partial(PkgConfigDependency, name, env, kwargs)
+ for name in shared_libs + static_libs]
+ candidates.extend(c)
+
+ if DependencyMethods.SYSTEM in methods:
+ candidates.append(functools.partial(ShadercDependency, env, kwargs))
+
+ return candidates
+
+
+cups_factory = DependencyFactory(
+ 'cups',
+ [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK, DependencyMethods.CMAKE],
+ configtool_class=CupsDependencyConfigTool,
+ cmake_name='Cups',
+)
+
+gpgme_factory = DependencyFactory(
+ 'gpgme',
+ [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL],
+ configtool_class=GpgmeDependencyConfigTool,
+)
+
+libgcrypt_factory = DependencyFactory(
+ 'libgcrypt',
+ [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL],
+ configtool_class=LibGCryptDependencyConfigTool,
+)
+
+libwmf_factory = DependencyFactory(
+ 'libwmf',
+ [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL],
+ configtool_class=LibWmfDependencyConfigTool,
+)
+
+pcap_factory = DependencyFactory(
+ 'pcap',
+ [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL],
+ configtool_class=PcapDependencyConfigTool,
+ pkgconfig_name='libpcap',
+)
+
+python3_factory = DependencyFactory(
+ 'python3',
+ [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM, DependencyMethods.EXTRAFRAMEWORK],
+ system_class=Python3DependencySystem,
+ # There is no version number in the macOS version number
+ framework_name='Python',
+ # There is a python in /System/Library/Frameworks, but thats python 2.x,
+ # Python 3 will always be in /Library
+ extra_kwargs={'paths': ['/Library/Frameworks']},
+)
+
+threads_factory = DependencyFactory(
+ 'threads',
+ [DependencyMethods.SYSTEM, DependencyMethods.CMAKE],
+ cmake_name='Threads',
+ system_class=ThreadDependency,
+)
diff --git a/mesonbuild/dependencies/mpi.py b/mesonbuild/dependencies/mpi.py
index 0754712..59ba0cd 100644
--- a/mesonbuild/dependencies/mpi.py
+++ b/mesonbuild/dependencies/mpi.py
@@ -29,7 +29,7 @@ class MPIDependency(ExternalDependency):
def __init__(self, environment, kwargs: dict):
language = kwargs.get('language', 'c')
- super().__init__('mpi', environment, language, kwargs)
+ super().__init__('mpi', environment, kwargs, language=language)
kwargs['required'] = False
kwargs['silent'] = True
self.is_found = False
diff --git a/mesonbuild/dependencies/platform.py b/mesonbuild/dependencies/platform.py
index e913ed4..6a32e36 100644
--- a/mesonbuild/dependencies/platform.py
+++ b/mesonbuild/dependencies/platform.py
@@ -20,7 +20,7 @@ from ..mesonlib import MesonException
class AppleFrameworks(ExternalDependency):
def __init__(self, env, kwargs):
- super().__init__('appleframeworks', env, None, kwargs)
+ super().__init__('appleframeworks', env, kwargs)
modules = kwargs.get('modules', [])
if isinstance(modules, str):
modules = [modules]
diff --git a/mesonbuild/dependencies/scalapack.py b/mesonbuild/dependencies/scalapack.py
index 8a58402..83f175c 100644
--- a/mesonbuild/dependencies/scalapack.py
+++ b/mesonbuild/dependencies/scalapack.py
@@ -21,7 +21,7 @@ from .base import CMakeDependency, DependencyMethods, ExternalDependency, PkgCon
class ScalapackDependency(ExternalDependency):
def __init__(self, environment, kwargs: dict):
- super().__init__('scalapack', environment, None, kwargs)
+ super().__init__('scalapack', environment, kwargs)
kwargs['required'] = False
kwargs['silent'] = True
self.is_found = False
diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py
index da411ef..5433150 100644
--- a/mesonbuild/dependencies/ui.py
+++ b/mesonbuild/dependencies/ui.py
@@ -14,10 +14,10 @@
# This file contains the detection logic for external dependencies that
# are UI-related.
-import functools
import os
import re
import subprocess
+import typing as T
from collections import OrderedDict
from .. import mlog
@@ -28,14 +28,14 @@ from ..mesonlib import (
from ..environment import detect_cpu_family
from .base import DependencyException, DependencyMethods
-from .base import ExternalDependency, ExternalProgram, NonExistingExternalProgram
+from .base import ExternalDependency, NonExistingExternalProgram
from .base import ExtraFrameworkDependency, PkgConfigDependency
-from .base import ConfigToolDependency
+from .base import ConfigToolDependency, DependencyFactory
-class GLDependency(ExternalDependency):
- def __init__(self, environment, kwargs):
- super().__init__('gl', environment, None, kwargs)
+class GLDependencySystem(ExternalDependency):
+ def __init__(self, name: str, environment, kwargs):
+ super().__init__(name, environment, kwargs)
if self.env.machines[self.for_machine].is_darwin():
self.is_found = True
@@ -50,19 +50,6 @@ class GLDependency(ExternalDependency):
# FIXME: Detect version using self.clib_compiler
return
- @classmethod
- def _factory(cls, environment, kwargs):
- methods = cls._process_method_kw(kwargs)
- candidates = []
-
- if DependencyMethods.PKGCONFIG in methods:
- candidates.append(functools.partial(PkgConfigDependency, 'gl', environment, kwargs))
-
- if DependencyMethods.SYSTEM in methods:
- candidates.append(functools.partial(GLDependency, environment, kwargs))
-
- return candidates
-
@staticmethod
def get_methods():
if mesonlib.is_osx() or mesonlib.is_windows():
@@ -79,7 +66,7 @@ class GnuStepDependency(ConfigToolDependency):
tool_name = 'gnustep-config'
def __init__(self, environment, kwargs):
- super().__init__('gnustep', environment, 'objc', kwargs)
+ super().__init__('gnustep', environment, kwargs, language='objc')
if not self.is_found:
return
self.modules = kwargs.get('modules', [])
@@ -176,8 +163,8 @@ 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, paths, env, lang, kwargs):
- super().__init__(name, required, paths, env, lang, kwargs)
+ def __init__(self, name, env, kwargs, language: T.Optional[str] = None):
+ super().__init__(name, env, kwargs, language=language)
self.mod_name = name[2:]
def get_compile_args(self, with_private_headers=False, qt_version="0"):
@@ -191,7 +178,7 @@ class QtExtraFrameworkDependency(ExtraFrameworkDependency):
class QtBaseDependency(ExternalDependency):
def __init__(self, name, env, kwargs):
- super().__init__(name, env, 'cpp', kwargs)
+ super().__init__(name, env, kwargs, language='cpp')
self.qtname = name.capitalize()
self.qtver = name[-1]
if self.qtver == "4":
@@ -334,28 +321,24 @@ class QtBaseDependency(ExternalDependency):
if prefix:
self.bindir = os.path.join(prefix, 'bin')
- def _qmake_detect(self, mods, kwargs):
+ def search_qmake(self):
for qmake in ('qmake-' + self.name, 'qmake'):
- self.qmake = ExternalProgram.from_bin_list(
- self.env.binaries.host, qmake)
- if not self.qmake.found():
- # Even when cross-compiling, if a cross-info qmake is not
- # specified, we fallback to using the qmake in PATH because
- # that's what we used to do
- self.qmake = ExternalProgram.from_bin_list(
- self.env.binaries.build, qmake)
- if not self.qmake.found():
- self.qmake = ExternalProgram(qmake, silent=True)
- if not self.qmake.found():
+ for potential_qmake in self.search_tool(qmake, 'QMake', [qmake]):
+ yield potential_qmake
+
+ def _qmake_detect(self, mods, kwargs):
+ for qmake in self.search_qmake():
+ if not qmake.found():
continue
# Check that the qmake is for qt5
- pc, stdo = Popen_safe(self.qmake.get_command() + ['-v'])[0:2]
+ pc, stdo = Popen_safe(qmake.get_command() + ['-v'])[0:2]
if pc.returncode != 0:
continue
if not 'Qt version ' + self.qtver in stdo:
mlog.log('QMake is not for ' + self.qtname)
continue
# Found qmake for Qt5!
+ self.qmake = qmake
break
else:
# Didn't find qmake :(
@@ -377,7 +360,7 @@ class QtBaseDependency(ExternalDependency):
if self.env.machines.host.is_darwin() and not any(s in xspec for s in ['ios', 'tvos']):
mlog.debug("Building for macOS, looking for framework")
self._framework_detect(qvars, mods, kwargs)
- return qmake
+ return self.qmake.name
incdir = qvars['QT_INSTALL_HEADERS']
self.compile_args.append('-I' + incdir)
libdir = qvars['QT_INSTALL_LIBS']
@@ -418,7 +401,7 @@ class QtBaseDependency(ExternalDependency):
if not self._link_with_qtmain(is_debug, libdir):
self.is_found = False
- return qmake
+ return self.qmake.name
def _get_modules_lib_suffix(self, is_debug):
suffix = ''
@@ -443,12 +426,12 @@ class QtBaseDependency(ExternalDependency):
# ExtraFrameworkDependency doesn't support any methods
fw_kwargs = kwargs.copy()
fw_kwargs.pop('method', None)
+ fw_kwargs['paths'] = [libdir]
for m in modules:
fname = 'Qt' + m
mlog.debug('Looking for qt framework ' + fname)
- fwdep = QtExtraFrameworkDependency(fname, False, [libdir], self.env,
- self.language, fw_kwargs)
+ fwdep = QtExtraFrameworkDependency(fname, self.env, fw_kwargs, language=self.language)
self.compile_args.append('-F' + libdir)
if fwdep.found():
self.compile_args += fwdep.get_compile_args(with_private_headers=self.private_headers,
@@ -524,36 +507,13 @@ class Qt5Dependency(QtBaseDependency):
return _qt_get_private_includes(mod_inc_dir, module, self.version)
-# There are three different ways of depending on SDL2:
-# sdl2-config, pkg-config and OSX framework
-class SDL2Dependency(ExternalDependency):
- def __init__(self, environment, kwargs):
- super().__init__('sdl2', environment, None, kwargs)
-
- @classmethod
- def _factory(cls, environment, kwargs):
- methods = cls._process_method_kw(kwargs)
- candidates = []
-
- if DependencyMethods.PKGCONFIG in methods:
- candidates.append(functools.partial(PkgConfigDependency, 'sdl2', environment, kwargs))
-
- if DependencyMethods.CONFIG_TOOL in methods:
- candidates.append(functools.partial(ConfigToolDependency.factory,
- 'sdl2', environment, None,
- kwargs, ['sdl2-config'],
- 'sdl2-config', SDL2Dependency.tool_finish_init))
-
- if DependencyMethods.EXTRAFRAMEWORK in methods:
- if mesonlib.is_osx():
- candidates.append(functools.partial(ExtraFrameworkDependency,
- 'sdl2', False, None, environment,
- kwargs.get('language', None), kwargs))
- # fwdep.version = '2' # FIXME
- return candidates
+class SDL2DependencyConfigTool(ConfigToolDependency):
+
+ tools = ['sdl2-config']
+ tool_name = 'sdl2-config'
@staticmethod
- def tool_finish_init(ctdep):
+ def finish_init(ctdep):
ctdep.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args')
ctdep.link_args = ctdep.get_config_value(['--libs'], 'link_args')
@@ -571,7 +531,7 @@ class WxDependency(ConfigToolDependency):
tool_name = 'wx-config'
def __init__(self, environment, kwargs):
- super().__init__('WxWidgets', environment, None, kwargs)
+ super().__init__('WxWidgets', environment, kwargs)
if not self.is_found:
return
self.requested_modules = self.get_requested(kwargs)
@@ -590,10 +550,10 @@ class WxDependency(ConfigToolDependency):
return candidates
-class VulkanDependency(ExternalDependency):
+class VulkanDependencySystem(ExternalDependency):
- def __init__(self, environment, kwargs):
- super().__init__('vulkan', environment, None, kwargs)
+ def __init__(self, name: str, environment, kwargs, language: T.Optional[str] = None):
+ super().__init__(name, environment, kwargs, language=language)
try:
self.vulkan_sdk = os.environ['VULKAN_SDK']
@@ -646,22 +606,27 @@ class VulkanDependency(ExternalDependency):
self.link_args.append(lib)
return
- @classmethod
- def _factory(cls, environment, kwargs):
- methods = cls._process_method_kw(kwargs)
- candidates = []
-
- if DependencyMethods.PKGCONFIG in methods:
- candidates.append(functools.partial(PkgConfigDependency, 'vulkan', environment, kwargs))
-
- if DependencyMethods.SYSTEM in methods:
- candidates.append(functools.partial(VulkanDependency, environment, kwargs))
-
- return candidates
-
@staticmethod
def get_methods():
- return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM]
+ return [DependencyMethods.SYSTEM]
def log_tried(self):
return 'system'
+
+gl_factory = DependencyFactory(
+ 'gl',
+ [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM],
+ system_class=GLDependencySystem,
+)
+
+sdl2_factory = DependencyFactory(
+ 'sdl2',
+ [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK],
+ configtool_class=SDL2DependencyConfigTool,
+)
+
+vulkan_factory = DependencyFactory(
+ 'vulkan',
+ [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM],
+ system_class=VulkanDependencySystem,
+)
diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py
index 0a5c57e..9781813 100644
--- a/mesonbuild/linkers.py
+++ b/mesonbuild/linkers.py
@@ -668,7 +668,7 @@ class CcrxDynamicLinker(DynamicLinker):
def __init__(self, for_machine: mesonlib.MachineChoice,
*, version: str = 'unknown version'):
- super().__init__(['rlink.exe'], for_machine, 'rlink', '',
+ super().__init__(['rlink.exe'], for_machine, 'rlink', '', [],
version=version)
def get_accepts_rsp(self) -> bool:
@@ -769,6 +769,7 @@ class PGIStaticLinker(StaticLinker):
def get_output_args(self, target: str) -> T.List[str]:
return [target]
+
class VisualStudioLikeLinkerMixin:
_BUILDTYPE_ARGS = {
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index dc45a5b..2341bd2 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -17,6 +17,7 @@ from pathlib import PurePath
from .. import build
from .. import dependencies
+from ..dependencies.misc import ThreadDependency
from .. import mesonlib
from .. import mlog
from . import ModuleReturnValue
@@ -94,7 +95,7 @@ class DependenciesHelper:
self.add_version_reqs(name, version_req)
elif isinstance(obj, dependencies.Dependency) and not obj.found():
pass
- elif isinstance(obj, dependencies.ThreadDependency):
+ elif isinstance(obj, ThreadDependency):
pass
else:
raise mesonlib.MesonException('requires argument not a string, '
@@ -125,9 +126,6 @@ class DependenciesHelper:
if obj.found():
processed_reqs.append(obj.name)
self.add_version_reqs(obj.name, obj.version_reqs)
- elif isinstance(obj, dependencies.ThreadDependency):
- processed_libs += obj.get_compiler().thread_link_flags(obj.env)
- processed_cflags += obj.get_compiler().thread_flags(obj.env)
elif isinstance(obj, dependencies.InternalDependency):
if obj.found():
processed_libs += obj.get_link_args()
diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py
index 1e2b2ee..6644fd2 100644
--- a/mesonbuild/modules/python.py
+++ b/mesonbuild/modules/python.py
@@ -44,7 +44,7 @@ mod_kwargs -= set(['name_prefix', 'name_suffix'])
class PythonDependency(ExternalDependency):
def __init__(self, python_holder, environment, kwargs):
- super().__init__('python', environment, None, kwargs)
+ super().__init__('python', environment, kwargs)
self.name = 'python'
self.static = kwargs.get('static', False)
self.embed = kwargs.get('embed', False)
diff --git a/run_cross_test.py b/run_cross_test.py
index 8d18123..abbfdac 100755
--- a/run_cross_test.py
+++ b/run_cross_test.py
@@ -15,43 +15,25 @@
# limitations under the License.
'''Runs the basic test suite through a cross compiler.
-Not part of the main test suite because of two reasons:
-1) setup of the cross build is platform specific
-2) it can be slow (e.g. when invoking test apps via wine)
+This is now just a wrapper around run_project_tests.py with specific arguments
+'''
-Eventually migrate to something fancier.'''
-
-import sys
-import os
-from pathlib import Path
import argparse
-
-from run_project_tests import gather_tests, run_tests, StopException, setup_commands
-from run_project_tests import failing_logs
+import subprocess
+import sys
+from mesonbuild import mesonlib
def runtests(cross_file, failfast):
- commontests = [('common', gather_tests(Path('test cases', 'common')), False)]
- try:
- (passing_tests, failing_tests, skipped_tests) = \
- run_tests(commontests, 'meson-cross-test-run', failfast, ['--cross-file', cross_file])
- except StopException:
- pass
- print('\nTotal passed cross tests:', passing_tests)
- print('Total failed cross tests:', failing_tests)
- print('Total skipped cross tests:', skipped_tests)
- if failing_tests > 0 and ('CI' in os.environ):
- print('\nMesonlogs of failing tests\n')
- for log in failing_logs:
- print(log, '\n')
- return failing_tests
+ tests = ['--only', 'common']
+ cmd = mesonlib.python_command + ['run_project_tests.py', '--backend', 'ninja'] + (['--failfast'] if failfast else []) + tests + ['--cross-file', cross_file]
+ return subprocess.call(cmd)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--failfast', action='store_true')
parser.add_argument('cross_file')
options = parser.parse_args()
- setup_commands('ninja')
return runtests(options.cross_file, options.failfast)
if __name__ == '__main__':
diff --git a/run_project_tests.py b/run_project_tests.py
index acad225..2288abf 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -381,7 +381,7 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, compiler, backen
setup_env = None
# Configure in-process
if pass_prefix_to_test(testdir):
- gen_args = ['--prefix', '/usr']
+ gen_args = ['--prefix', 'x:/usr'] if mesonlib.is_windows() else ['--prefix', '/usr']
else:
gen_args = []
if pass_libdir_to_test(testdir):
@@ -547,6 +547,10 @@ def skippable(suite, test):
if not suite.endswith('frameworks'):
return True
+ # this test assumptions aren't valid for Windows paths
+ if test.endswith('38 libdir must be inside prefix'):
+ return True
+
# gtk-doc test may be skipped, pending upstream fixes for spaces in
# filenames landing in the distro used for CI
if test.endswith('10 gtk-doc'):
@@ -867,13 +871,13 @@ def check_format():
continue
check_file(root / file)
-def check_meson_commands_work():
+def check_meson_commands_work(options):
global backend, compile_commands, test_commands, install_commands
testdir = PurePath('test cases', 'common', '1 trivial').as_posix()
meson_commands = mesonlib.python_command + [get_meson_script()]
with AutoDeletedDir(tempfile.mkdtemp(prefix='b ', dir='.')) as build_dir:
print('Checking that configuring works...')
- gen_cmd = meson_commands + [testdir, build_dir] + backend_flags
+ gen_cmd = meson_commands + [testdir, build_dir] + backend_flags + options.extra_args
pc, o, e = Popen_safe(gen_cmd)
if pc.returncode != 0:
raise RuntimeError('Failed to configure {!r}:\n{}\n{}'.format(testdir, e, o))
@@ -893,11 +897,14 @@ def check_meson_commands_work():
raise RuntimeError('Failed to install {!r}:\n{}\n{}'.format(testdir, e, o))
-def detect_system_compiler():
+def detect_system_compiler(options):
global system_compiler
with AutoDeletedDir(tempfile.mkdtemp(prefix='b ', dir='.')) as build_dir:
- env = environment.Environment(None, build_dir, get_fake_options('/'))
+ fake_opts = get_fake_options('/')
+ if options.cross_file:
+ fake_opts.cross_file = [options.cross_file]
+ env = environment.Environment(None, build_dir, fake_opts)
print()
for lang in sorted(compilers.all_languages):
try:
@@ -958,16 +965,19 @@ if __name__ == '__main__':
parser.add_argument('--no-unittests', action='store_true',
help='Not used, only here to simplify run_tests.py')
parser.add_argument('--only', help='name of test(s) to run', nargs='+', choices=ALL_TESTS)
+ parser.add_argument('--cross-file', action='store', help='File describing cross compilation environment.')
options = parser.parse_args()
- setup_commands(options.backend)
+ if options.cross_file:
+ options.extra_args += ['--cross-file', options.cross_file]
- detect_system_compiler()
+ setup_commands(options.backend)
+ detect_system_compiler(options)
print_tool_versions()
script_dir = os.path.split(__file__)[0]
if script_dir != '':
os.chdir(script_dir)
check_format()
- check_meson_commands_work()
+ check_meson_commands_work(options)
try:
all_tests = detect_tests_to_run(options.only)
(passing_tests, failing_tests, skipped_tests) = run_tests(all_tests, 'meson-test-run', options.failfast, options.extra_args)
diff --git a/run_tests.py b/run_tests.py
index 4a1d271..535c792 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -312,6 +312,7 @@ def print_system_info():
print('Processor:', platform.processor())
print('System:', platform.system())
print('')
+ print(flush=True)
def main():
print_system_info()
@@ -319,7 +320,7 @@ def main():
parser.add_argument('--cov', action='store_true')
parser.add_argument('--backend', default=None, dest='backend',
choices=backendlist)
- parser.add_argument('--cross', default=False, dest='cross', action='store_true')
+ parser.add_argument('--cross', default=[], dest='cross', action='append')
parser.add_argument('--failfast', action='store_true')
parser.add_argument('--no-unittests', action='store_true', default=False)
(options, _) = parser.parse_known_args()
@@ -352,8 +353,6 @@ def main():
if 'APPVEYOR' in os.environ and os.environ['arch'] == 'x86':
os.environ.pop('platform')
# Run tests
- print(mlog.bold('Running unittests.').get_text(mlog.colorize_console))
- print(flush=True)
# Can't pass arguments to unit tests, so set the backend to use in the environment
env = os.environ.copy()
env['MESON_UNIT_TEST_BACKEND'] = backend.name
@@ -377,8 +376,11 @@ def main():
return returncode
if no_unittests:
print('Skipping all unit tests.')
+ print(flush=True)
returncode = 0
else:
+ print(mlog.bold('Running unittests.').get_text(mlog.colorize_console))
+ print(flush=True)
cmd = mesonlib.python_command + ['run_unittests.py', '-v']
if options.failfast:
cmd += ['--failfast']
@@ -389,21 +391,15 @@ def main():
returncode += subprocess.call(cmd, env=env)
else:
cross_test_args = mesonlib.python_command + ['run_cross_test.py']
- print(mlog.bold('Running armhf cross tests.').get_text(mlog.colorize_console))
- print(flush=True)
- cmd = cross_test_args + ['cross/ubuntu-armhf.txt']
- if options.failfast:
- cmd += ['--failfast']
- returncode += subprocess.call(cmd, env=env)
- if options.failfast and returncode != 0:
- return returncode
- print(mlog.bold('Running mingw-w64 64-bit cross tests.')
- .get_text(mlog.colorize_console))
- print(flush=True)
- cmd = cross_test_args + ['cross/linux-mingw-w64-64bit.txt']
- if options.failfast:
- cmd += ['--failfast']
- returncode += subprocess.call(cmd, env=env)
+ for cf in options.cross:
+ print(mlog.bold('Running {} cross tests.'.format(cf)).get_text(mlog.colorize_console))
+ print(flush=True)
+ cmd = cross_test_args + ['cross/' + cf]
+ if options.failfast:
+ cmd += ['--failfast']
+ returncode += subprocess.call(cmd, env=env)
+ if options.failfast and returncode != 0:
+ return returncode
return returncode
if __name__ == '__main__':
diff --git a/run_unittests.py b/run_unittests.py
index ff9dbd6..5388679 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -43,6 +43,7 @@ from distutils.dir_util import copy_tree
import mesonbuild.mlog
import mesonbuild.depfile
+import mesonbuild.dependencies.base
import mesonbuild.compilers
import mesonbuild.envconfig
import mesonbuild.environment
@@ -76,6 +77,13 @@ from run_tests import (
URLOPEN_TIMEOUT = 5
+@contextmanager
+def chdir(path: str):
+ curdir = os.getcwd()
+ os.chdir(path)
+ yield
+ os.chdir(curdir)
+
def get_dynamic_section_entry(fname, entry):
if is_cygwin() or is_osx():
@@ -1194,6 +1202,26 @@ class InternalTests(unittest.TestCase):
['/home/mesonuser/.local/lib/pkgconfig', '/usr/local/libdata/pkgconfig']),
['/home/mesonuser/.local/lib', '/usr/local/lib', '/usr/lib'])
+ def test_dependency_factory_order(self):
+ b = mesonbuild.dependencies.base
+ with tempfile.TemporaryDirectory() as tmpdir:
+ with chdir(tmpdir):
+ env = get_fake_env()
+
+ f = b.DependencyFactory(
+ 'test_dep',
+ methods=[b.DependencyMethods.PKGCONFIG, b.DependencyMethods.CMAKE]
+ )
+ actual = [m() for m in f(env, MachineChoice.HOST, {'required': False})]
+ self.assertListEqual([m.type_name for m in actual], ['pkgconfig', 'cmake'])
+
+ f = b.DependencyFactory(
+ 'test_dep',
+ methods=[b.DependencyMethods.CMAKE, b.DependencyMethods.PKGCONFIG]
+ )
+ actual = [m() for m in f(env, MachineChoice.HOST, {'required': False})]
+ self.assertListEqual([m.type_name for m in actual], ['cmake', 'pkgconfig'])
+
@unittest.skipIf(is_tarball(), 'Skipping because this is a tarball release')
class DataTests(unittest.TestCase):
@@ -1755,7 +1783,8 @@ class AllPlatformTests(BasePlatformTests):
https://github.com/mesonbuild/meson/issues/1345
'''
testdir = os.path.join(self.common_test_dir, '90 default options')
- prefix = '/someabs'
+ # on Windows, /someabs is *not* an absolute path
+ prefix = 'x:/someabs' if is_windows() else '/someabs'
libdir = 'libdir'
extra_args = ['--prefix=' + prefix,
# This can just be a relative path, but we want to test
@@ -1776,16 +1805,25 @@ class AllPlatformTests(BasePlatformTests):
'''
testdir = os.path.join(self.common_test_dir, '1 trivial')
# libdir being inside prefix is ok
- args = ['--prefix', '/opt', '--libdir', '/opt/lib32']
+ if is_windows():
+ args = ['--prefix', 'x:/opt', '--libdir', 'x:/opt/lib32']
+ else:
+ args = ['--prefix', '/opt', '--libdir', '/opt/lib32']
self.init(testdir, extra_args=args)
self.wipe()
# libdir not being inside prefix is not ok
- args = ['--prefix', '/usr', '--libdir', '/opt/lib32']
+ if is_windows():
+ args = ['--prefix', 'x:/usr', '--libdir', 'x:/opt/lib32']
+ else:
+ args = ['--prefix', '/usr', '--libdir', '/opt/lib32']
self.assertRaises(subprocess.CalledProcessError, self.init, testdir, extra_args=args)
self.wipe()
# libdir must be inside prefix even when set via mesonconf
self.init(testdir)
- self.assertRaises(subprocess.CalledProcessError, self.setconf, '-Dlibdir=/opt', False)
+ if is_windows():
+ self.assertRaises(subprocess.CalledProcessError, self.setconf, '-Dlibdir=x:/opt', False)
+ else:
+ self.assertRaises(subprocess.CalledProcessError, self.setconf, '-Dlibdir=/opt', False)
def test_prefix_dependent_defaults(self):
'''
@@ -5861,24 +5899,23 @@ class LinuxlikeTests(BasePlatformTests):
testdir = os.path.join(self.common_test_dir, testdir)
subdir = os.path.join(testdir, subdir_path)
curdir = os.getcwd()
- os.chdir(subdir)
- # Can't distribute broken symlinks in the source tree because it breaks
- # the creation of zipapps. Create it dynamically and run the test by
- # hand.
- src = '../../nonexistent.txt'
- os.symlink(src, 'invalid-symlink.txt')
- try:
- self.init(testdir)
- self.build()
- self.install()
- install_path = subdir_path.split(os.path.sep)[-1]
- link = os.path.join(self.installdir, 'usr', 'share', install_path, 'invalid-symlink.txt')
- self.assertTrue(os.path.islink(link), msg=link)
- self.assertEqual(src, os.readlink(link))
- self.assertFalse(os.path.isfile(link), msg=link)
- finally:
- os.remove(os.path.join(subdir, 'invalid-symlink.txt'))
- os.chdir(curdir)
+ with chdir(subdir):
+ # Can't distribute broken symlinks in the source tree because it breaks
+ # the creation of zipapps. Create it dynamically and run the test by
+ # hand.
+ src = '../../nonexistent.txt'
+ os.symlink(src, 'invalid-symlink.txt')
+ try:
+ self.init(testdir)
+ self.build()
+ self.install()
+ install_path = subdir_path.split(os.path.sep)[-1]
+ link = os.path.join(self.installdir, 'usr', 'share', install_path, 'invalid-symlink.txt')
+ self.assertTrue(os.path.islink(link), msg=link)
+ self.assertEqual(src, os.readlink(link))
+ self.assertFalse(os.path.isfile(link), msg=link)
+ finally:
+ os.remove(os.path.join(subdir, 'invalid-symlink.txt'))
def test_install_subdir_symlinks(self):
self.install_subdir_invalid_symlinks('62 install subdir', os.path.join('sub', 'sub1'))
@@ -5986,6 +6023,7 @@ c = ['{0}']
def test_ld_environment_variable_lld(self):
self._check_ld('ld.lld', 'lld', 'c', 'lld')
+ @skipIfNoExecutable('rustc')
def test_ld_environment_variable_rust(self):
self._check_ld('ld.gold', 'gold', 'rust', 'GNU ld.gold')
@@ -5998,6 +6036,7 @@ c = ['{0}']
def test_ld_environment_variable_objcpp(self):
self._check_ld('ld.gold', 'gold', 'objcpp', 'GNU ld.gold')
+ @skipIfNoExecutable('gfortran')
def test_ld_environment_variable_fortran(self):
self._check_ld('ld.gold', 'gold', 'fortran', 'GNU ld.gold')
@@ -7281,14 +7320,11 @@ def main():
import pytest # noqa: F401
# Need pytest-xdist for `-n` arg
import xdist # noqa: F401
- if sys.version_info.major <= 3 and sys.version_info.minor <= 5:
- raise ImportError('pytest with python <= 3.5 is causing issues on the CI')
pytest_args = ['-n', 'auto', './run_unittests.py']
pytest_args += convert_args(sys.argv[1:])
return subprocess.run(python_command + ['-m', 'pytest'] + pytest_args).returncode
except ImportError:
print('pytest-xdist not found, using unittest instead')
- pass
# All attempts at locating pytest failed, fall back to plain unittest.
cases = ['InternalTests', 'DataTests', 'AllPlatformTests', 'FailureTests',
'PythonTests', 'NativeFileTests', 'RewriterTests', 'CrossFileTests',
diff --git a/test cases/common/105 testframework options/meson.build b/test cases/common/105 testframework options/meson.build
index 2773730..827bae7 100644
--- a/test cases/common/105 testframework options/meson.build
+++ b/test cases/common/105 testframework options/meson.build
@@ -1,3 +1,6 @@
+# normally run only from run_tests.py or run_project_tests.py
+# else do like
+# meson build '-Dtestoption=A string with spaces' -Dother_one=true -Dcombo_opt=one -Dprefix=/usr -Dlibdir=lib -Dbackend=ninja -Dwerror=True
project('options', 'c')
assert(get_option('testoption') == 'A string with spaces', 'Incorrect value for testoption option.')
diff --git a/test cases/failing/38 libdir must be inside prefix/meson.build b/test cases/failing/38 libdir must be inside prefix/meson.build
index 66272ea..4cce7f8 100644
--- a/test cases/failing/38 libdir must be inside prefix/meson.build
+++ b/test cases/failing/38 libdir must be inside prefix/meson.build
@@ -1,2 +1,6 @@
project('libdir prefix', 'c',
default_options : ['libdir=/opt/lib'])
+
+if host_machine.system() == 'windows'
+ error('MESON_SKIP_TEST: this test does not work on Windows since /foo is not absolute')
+endif \ No newline at end of file
diff --git a/test cases/frameworks/20 cups/meson.build b/test cases/frameworks/20 cups/meson.build
index d50c4a8..3f50684 100644
--- a/test cases/frameworks/20 cups/meson.build
+++ b/test cases/frameworks/20 cups/meson.build
@@ -14,6 +14,7 @@ test('cupstest', e)
# options
dep = dependency('cups', version : '>=1.4', method : 'cups-config')
dep = dependency('cups', version : '>=1.4', method : 'config-tool')
+dep = dependency('cups', version : '>=1.4', method : 'cmake')
# check we can apply a version constraint
dependency('cups', version: '>=@0@'.format(dep.version()), method: 'pkg-config', required: false)
diff --git a/test cases/java/3 args/meson.build b/test cases/java/3 args/meson.build
index 7a73cf8..db9a35c 100644
--- a/test cases/java/3 args/meson.build
+++ b/test cases/java/3 args/meson.build
@@ -1,9 +1,9 @@
project('simplejava', 'java')
-add_project_arguments('-target', '1.6', language : 'java')
+add_project_arguments('-target', '1.8', language : 'java')
javaprog = jar('myprog', 'com/mesonbuild/Simple.java',
main_class : 'com.mesonbuild.Simple',
- java_args : ['-source', '1.6'])
+ java_args : ['-source', '1.8'])
test('mytest', javaprog)