diff options
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) |