Merge pull request #8568 from dcbaker/submit/qt-dependency-factory

QT: use a proper dependency factory
pull/8576/head
Jussi Pakkanen 4 years ago committed by GitHub
commit 54767ab482
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      docs/markdown/snippets/qt_factory.md
  2. 9
      mesonbuild/dependencies/__init__.py
  3. 26
      mesonbuild/dependencies/base.py
  4. 438
      mesonbuild/dependencies/qt.py
  5. 422
      mesonbuild/dependencies/ui.py
  6. 75
      mesonbuild/modules/qt.py
  7. 1
      run_mypy.py
  8. 21
      run_project_tests.py
  9. 6
      run_single_test.py
  10. 8
      run_unittests.py
  11. 11
      test cases/frameworks/4 qt/test.json

@ -0,0 +1,6 @@
## Qt Dependency uses a Factory
This separates the Pkg-config and QMake based discovery methods into two
distinct classes in the backend. This allows using
`dependency.get_variable()` and `dependency.get_pkg_config_variable()`, as
well as being a cleaner implementation.

@ -31,7 +31,8 @@ from .misc import (
shaderc_factory, threads_factory, shaderc_factory, threads_factory,
) )
from .platform import AppleFrameworks from .platform import AppleFrameworks
from .ui import GnuStepDependency, Qt4Dependency, Qt5Dependency, Qt6Dependency, WxDependency, gl_factory, sdl2_factory, vulkan_factory from .qt import qt4_factory, qt5_factory, qt6_factory
from .ui import GnuStepDependency, WxDependency, gl_factory, sdl2_factory, vulkan_factory
"""Dependency representations and discovery logic. """Dependency representations and discovery logic.
@ -225,9 +226,9 @@ packages.update({
# From ui: # From ui:
'gl': gl_factory, 'gl': gl_factory,
'gnustep': GnuStepDependency, 'gnustep': GnuStepDependency,
'qt4': Qt4Dependency, 'qt4': qt4_factory,
'qt5': Qt5Dependency, 'qt5': qt5_factory,
'qt6': Qt6Dependency, 'qt6': qt6_factory,
'sdl2': sdl2_factory, 'sdl2': sdl2_factory,
'wxwidgets': WxDependency, 'wxwidgets': WxDependency,
'vulkan': vulkan_factory, 'vulkan': vulkan_factory,

@ -18,6 +18,7 @@ import copy
import functools import functools
import os import os
import re import re
import itertools
import json import json
import shlex import shlex
import shutil import shutil
@ -37,9 +38,10 @@ from ..mesonlib import Popen_safe, version_compare_many, version_compare, listif
from ..mesonlib import Version, LibType, OptionKey from ..mesonlib import Version, LibType, OptionKey
from ..mesondata import mesondata from ..mesondata import mesondata
from ..programs import ExternalProgram, find_external_program from ..programs import ExternalProgram, find_external_program
from ..interpreterbase import FeatureDeprecated
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from ..compilers.compilers import CompilerType # noqa: F401 from ..compilers.compilers import Compiler
DependencyType = T.TypeVar('DependencyType', bound='Dependency') DependencyType = T.TypeVar('DependencyType', bound='Dependency')
# These must be defined in this file to avoid cyclical references. # These must be defined in this file to avoid cyclical references.
@ -55,7 +57,6 @@ class DependencyMethods(Enum):
# Auto means to use whatever dependency checking mechanisms in whatever order meson thinks is best. # Auto means to use whatever dependency checking mechanisms in whatever order meson thinks is best.
AUTO = 'auto' AUTO = 'auto'
PKGCONFIG = 'pkg-config' PKGCONFIG = 'pkg-config'
QMAKE = 'qmake'
CMAKE = 'cmake' CMAKE = 'cmake'
# Just specify the standard link arguments, assuming the operating system provides the library. # Just specify the standard link arguments, assuming the operating system provides the library.
SYSTEM = 'system' SYSTEM = 'system'
@ -70,6 +71,7 @@ class DependencyMethods(Enum):
CUPSCONFIG = 'cups-config' CUPSCONFIG = 'cups-config'
PCAPCONFIG = 'pcap-config' PCAPCONFIG = 'pcap-config'
LIBWMFCONFIG = 'libwmf-config' LIBWMFCONFIG = 'libwmf-config'
QMAKE = 'qmake'
# Misc # Misc
DUB = 'dub' DUB = 'dub'
@ -135,11 +137,21 @@ class Dependency:
return converted return converted
return self.compile_args return self.compile_args
def get_all_compile_args(self) -> T.List[str]:
"""Get the compile arguments from this dependency and it's sub dependencies."""
return list(itertools.chain(self.get_compile_args(),
*[d.get_all_compile_args() for d in self.ext_deps]))
def get_link_args(self, raw: bool = False) -> T.List[str]: def get_link_args(self, raw: bool = False) -> T.List[str]:
if raw and self.raw_link_args is not None: if raw and self.raw_link_args is not None:
return self.raw_link_args return self.raw_link_args
return self.link_args return self.link_args
def get_all_link_args(self) -> T.List[str]:
"""Get the link arguments from this dependency and it's sub dependencies."""
return list(itertools.chain(self.get_link_args(),
*[d.get_all_link_args() for d in self.ext_deps]))
def found(self) -> bool: def found(self) -> bool:
return self.is_found return self.is_found
@ -2267,10 +2279,10 @@ def process_method_kw(possible: T.Iterable[DependencyMethods], kwargs) -> T.List
# generic CONFIG_TOOL value. # generic CONFIG_TOOL value.
if method in [DependencyMethods.SDLCONFIG, DependencyMethods.CUPSCONFIG, if method in [DependencyMethods.SDLCONFIG, DependencyMethods.CUPSCONFIG,
DependencyMethods.PCAPCONFIG, DependencyMethods.LIBWMFCONFIG]: DependencyMethods.PCAPCONFIG, DependencyMethods.LIBWMFCONFIG]:
mlog.warning(textwrap.dedent("""\ FeatureDeprecated.single_use(f'Configuration method {method.value}', '0.44', 'Use "config-tool" instead.')
Configuration method {} has been deprecated in favor of method = DependencyMethods.CONFIG_TOOL
'config-tool'. This will be removed in a future version of if method is DependencyMethods.QMAKE:
meson.""".format(method))) FeatureDeprecated.single_use(f'Configuration method "qmake"', '0.58', 'Use "config-tool" instead.')
method = DependencyMethods.CONFIG_TOOL method = DependencyMethods.CONFIG_TOOL
# Set the detection method. If the method is set to auto, use any available method. # Set the detection method. If the method is set to auto, use any available method.
@ -2313,7 +2325,7 @@ def factory_methods(methods: T.Set[DependencyMethods]) -> T.Callable[['FactoryTy
def detect_compiler(name: str, env: Environment, for_machine: MachineChoice, def detect_compiler(name: str, env: Environment, for_machine: MachineChoice,
language: T.Optional[str]) -> T.Optional['CompilerType']: language: T.Optional[str]) -> T.Optional['Compiler']:
"""Given a language and environment find the compiler used.""" """Given a language and environment find the compiler used."""
compilers = env.coredata.compilers[for_machine] compilers = env.coredata.compilers[for_machine]

@ -0,0 +1,438 @@
# Copyright 2013-2017 The Meson development team
# Copyright © 2021 Intel Corporation
# SPDX-license-identifier: Apache-2.0
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Dependency finders for the Qt framework."""
import abc
import re
import os
import typing as T
from . import (
ExtraFrameworkDependency, DependencyException, DependencyMethods,
PkgConfigDependency,
)
from .base import ConfigToolDependency, DependencyFactory
from .. import mlog
from .. import mesonlib
if T.TYPE_CHECKING:
from ..compilers import Compiler
from ..envconfig import MachineInfo
from ..environment import Environment
def _qt_get_private_includes(mod_inc_dir: str, module: str, mod_version: str) -> T.List[str]:
# usually Qt5 puts private headers in /QT_INSTALL_HEADERS/module/VERSION/module/private
# except for at least QtWebkit and Enginio where the module version doesn't match Qt version
# as an example with Qt 5.10.1 on linux you would get:
# /usr/include/qt5/QtCore/5.10.1/QtCore/private/
# /usr/include/qt5/QtWidgets/5.10.1/QtWidgets/private/
# /usr/include/qt5/QtWebKit/5.212.0/QtWebKit/private/
# on Qt4 when available private folder is directly in module folder
# like /usr/include/QtCore/private/
if int(mod_version.split('.')[0]) < 5:
return []
private_dir = os.path.join(mod_inc_dir, mod_version)
# fallback, let's try to find a directory with the latest version
if not os.path.exists(private_dir):
dirs = [filename for filename in os.listdir(mod_inc_dir)
if os.path.isdir(os.path.join(mod_inc_dir, filename))]
for dirname in sorted(dirs, reverse=True):
if len(dirname.split('.')) == 3:
private_dir = dirname
break
return [private_dir, os.path.join(private_dir, 'Qt' + module)]
def get_qmake_host_bins(qvars: T.Dict[str, str]) -> str:
# Prefer QT_HOST_BINS (qt5, correct for cross and native compiling)
# but fall back to QT_INSTALL_BINS (qt4)
if 'QT_HOST_BINS' in qvars:
return qvars['QT_HOST_BINS']
return qvars['QT_INSTALL_BINS']
def _get_modules_lib_suffix(version: str, info: 'MachineInfo', is_debug: bool) -> str:
"""Get the module suffix based on platform and debug type."""
suffix = ''
if info.is_windows():
if is_debug:
suffix += 'd'
if version.startswith('4'):
suffix += '4'
if info.is_darwin():
if is_debug:
suffix += '_debug'
if mesonlib.version_compare(version, '>= 5.14.0'):
if info.is_android():
if info.cpu_family == 'x86':
suffix += '_x86'
elif info.cpu_family == 'x86_64':
suffix += '_x86_64'
elif info.cpu_family == 'arm':
suffix += '_armeabi-v7a'
elif info.cpu_family == 'aarch64':
suffix += '_arm64-v8a'
else:
mlog.warning(f'Android target arch "{info.cpu_family}"" for Qt5 is unknown, '
'module detection may not work')
return suffix
class QtExtraFrameworkDependency(ExtraFrameworkDependency):
def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any], 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: bool = False, qt_version: str = "0") -> T.List[str]:
if self.found():
mod_inc_dir = os.path.join(self.framework_path, 'Headers')
args = ['-I' + mod_inc_dir]
if with_private_headers:
args += ['-I' + dirname for dirname in _qt_get_private_includes(mod_inc_dir, self.mod_name, qt_version)]
return args
return []
class _QtBase:
"""Mixin class for shared componenets between PkgConfig and Qmake."""
link_args: T.List[str]
clib_compiler: 'Compiler'
env: 'Environment'
def __init__(self, name: str, kwargs: T.Dict[str, T.Any]):
self.qtname = name.capitalize()
self.qtver = name[-1]
if self.qtver == "4":
self.qtpkgname = 'Qt'
else:
self.qtpkgname = self.qtname
self.private_headers = T.cast(bool, kwargs.get('private_headers', False))
self.requested_modules = mesonlib.stringlistify(mesonlib.extract_as_list(kwargs, 'modules'))
if not self.requested_modules:
raise DependencyException('No ' + self.qtname + ' modules specified.')
self.qtmain = T.cast(bool, kwargs.get('main', False))
if not isinstance(self.qtmain, bool):
raise DependencyException('"main" argument must be a boolean')
def _link_with_qtmain(self, is_debug: bool, libdir: T.Union[str, T.List[str]]) -> bool:
libdir = mesonlib.listify(libdir) # TODO: shouldn't be necessary
base_name = 'qtmaind' if is_debug else 'qtmain'
qtmain = self.clib_compiler.find_library(base_name, self.env, libdir)
if qtmain:
self.link_args.append(qtmain[0])
return True
return False
def get_exe_args(self, compiler: 'Compiler') -> T.List[str]:
# Originally this was -fPIE but nowadays the default
# for upstream and distros seems to be -reduce-relocations
# which requires -fPIC. This may cause a performance
# penalty when using self-built Qt or on platforms
# where -fPIC is not required. If this is an issue
# for you, patches are welcome.
return compiler.get_pic_args()
def log_details(self) -> str:
return f'modules: {", ".join(sorted(self.requested_modules))}'
class QtPkgConfigDependency(_QtBase, PkgConfigDependency, metaclass=abc.ABCMeta):
"""Specialization of the PkgConfigDependency for Qt."""
def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
_QtBase.__init__(self, name, kwargs)
# Always use QtCore as the "main" dependency, since it has the extra
# pkg-config variables that a user would expect to get. If "Core" is
# not a requested module, delete the compile and link arguments to
# avoid linking with something they didn't ask for
PkgConfigDependency.__init__(self, self.qtpkgname + 'Core', env, kwargs)
if 'Core' not in self.requested_modules:
self.compile_args = []
self.link_args = []
for m in self.requested_modules:
mod = PkgConfigDependency(self.qtpkgname + m, self.env, kwargs, language=self.language)
if not mod.found():
self.is_found = False
return
if self.private_headers:
qt_inc_dir = mod.get_pkgconfig_variable('includedir', {})
mod_private_dir = os.path.join(qt_inc_dir, 'Qt' + m)
if not os.path.isdir(mod_private_dir):
# At least some versions of homebrew don't seem to set this
# up correctly. /usr/local/opt/qt/include/Qt + m_name is a
# symlink to /usr/local/opt/qt/include, but the pkg-config
# file points to /usr/local/Cellar/qt/x.y.z/Headers/, and
# the Qt + m_name there is not a symlink, it's a file
mod_private_dir = qt_inc_dir
mod_private_inc = _qt_get_private_includes(mod_private_dir, m, mod.version)
for directory in mod_private_inc:
mod.compile_args.append('-I' + directory)
self._add_sub_dependency([lambda: mod])
if self.env.machines[self.for_machine].is_windows() and self.qtmain:
# Check if we link with debug binaries
debug_lib_name = self.qtpkgname + 'Core' + _get_modules_lib_suffix(self.version, self.env.machines[self.for_machine], True)
is_debug = False
for arg in self.get_link_args():
if arg == f'-l{debug_lib_name}' or arg.endswith(f'{debug_lib_name}.lib') or arg.endswith(f'{debug_lib_name}.a'):
is_debug = True
break
libdir = self.get_pkgconfig_variable('libdir', {})
if not self._link_with_qtmain(is_debug, libdir):
self.is_found = False
return
self.bindir = self.get_pkgconfig_host_bins(self)
if not self.bindir:
# If exec_prefix is not defined, the pkg-config file is broken
prefix = self.get_pkgconfig_variable('exec_prefix', {})
if prefix:
self.bindir = os.path.join(prefix, 'bin')
@staticmethod
@abc.abstractmethod
def get_pkgconfig_host_bins(core: PkgConfigDependency) -> T.Optional[str]:
pass
@abc.abstractmethod
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
pass
def log_info(self) -> str:
return 'pkg-config'
class QmakeQtDependency(_QtBase, ConfigToolDependency, metaclass=abc.ABCMeta):
"""Find Qt using Qmake as a config-tool."""
tool_name = 'qmake'
version_arg = '-v'
def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
_QtBase.__init__(self, name, kwargs)
self.tools = [f'qmake-{self.qtname}', 'qmake']
# Add additional constraits that the Qt version is met, but preserve
# any version requrements the user has set as well. For exmaple, if Qt5
# is requested, add "">= 5, < 6", but if the user has ">= 5.6", don't
# lose that.
kwargs = kwargs.copy()
_vers = mesonlib.listify(kwargs.get('version', []))
_vers.extend([f'>= {self.qtver}', f'< {int(self.qtver) + 1}'])
kwargs['version'] = _vers
ConfigToolDependency.__init__(self, name, env, kwargs)
if not self.found():
return
# Query library path, header path, and binary path
stdo = self.get_config_value(['-query'], 'args')
qvars: T.Dict[str, str] = {}
for line in stdo:
line = line.strip()
if line == '':
continue
k, v = line.split(':', 1)
qvars[k] = v
# Qt on macOS uses a framework, but Qt for iOS/tvOS does not
xspec = qvars.get('QMAKE_XSPEC', '')
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, self.requested_modules, kwargs)
# Sometimes Qt is built not as a framework (for instance, when using conan pkg manager)
# skip and fall back to normal procedure then
if self.is_found:
return
else:
mlog.debug("Building for macOS, couldn't find framework, falling back to library search")
incdir = qvars['QT_INSTALL_HEADERS']
self.compile_args.append('-I' + incdir)
libdir = qvars['QT_INSTALL_LIBS']
# Used by qt.compilers_detect()
self.bindir = get_qmake_host_bins(qvars)
# Use the buildtype by default, but look at the b_vscrt option if the
# compiler supports it.
is_debug = self.env.coredata.get_option(mesonlib.OptionKey('buildtype')) == 'debug'
if mesonlib.OptionKey('b_vscrt') in self.env.coredata.options:
if self.env.coredata.options[mesonlib.OptionKey('b_vscrt')].value in {'mdd', 'mtd'}:
is_debug = True
modules_lib_suffix = _get_modules_lib_suffix(self.version, self.env.machines[self.for_machine], is_debug)
for module in self.requested_modules:
mincdir = os.path.join(incdir, 'Qt' + module)
self.compile_args.append('-I' + mincdir)
if module == 'QuickTest':
define_base = 'QMLTEST'
elif module == 'Test':
define_base = 'TESTLIB'
else:
define_base = module.upper()
self.compile_args.append(f'-DQT_{define_base}_LIB')
if self.private_headers:
priv_inc = self.get_private_includes(mincdir, module)
for directory in priv_inc:
self.compile_args.append('-I' + directory)
libfiles = self.clib_compiler.find_library(
self.qtpkgname + module + modules_lib_suffix, self.env,
mesonlib.listify(libdir)) # TODO: shouldn't be necissary
if libfiles:
libfile = libfiles[0]
else:
mlog.log("Could not find:", module,
self.qtpkgname + module + modules_lib_suffix,
'in', libdir)
self.is_found = False
break
self.link_args.append(libfile)
if self.env.machines[self.for_machine].is_windows() and self.qtmain:
if not self._link_with_qtmain(is_debug, libdir):
self.is_found = False
def _sanitize_version(self, version: str) -> str:
m = re.search(rf'({self.qtver}(\.\d+)+)', version)
if m:
return m.group(0).rstrip('.')
return version
@abc.abstractmethod
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
pass
def _framework_detect(self, qvars: T.Dict[str, str], modules: T.List[str], kwargs: T.Dict[str, T.Any]) -> None:
libdir = qvars['QT_INSTALL_LIBS']
# 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, self.env, fw_kwargs, language=self.language)
if fwdep.found():
self.compile_args.append('-F' + libdir)
self.compile_args += fwdep.get_compile_args(with_private_headers=self.private_headers,
qt_version=self.version)
self.link_args += fwdep.get_link_args()
else:
self.is_found = False
break
else:
self.is_found = True
# Used by self.compilers_detect()
self.bindir = get_qmake_host_bins(qvars)
def log_info(self) -> str:
return 'qmake'
class Qt4ConfigToolDependency(QmakeQtDependency):
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
return []
class Qt5ConfigToolDependency(QmakeQtDependency):
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
return _qt_get_private_includes(mod_inc_dir, module, self.version)
class Qt6ConfigToolDependency(QmakeQtDependency):
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
return _qt_get_private_includes(mod_inc_dir, module, self.version)
class Qt4PkgConfigDependency(QtPkgConfigDependency):
@staticmethod
def get_pkgconfig_host_bins(core: PkgConfigDependency) -> T.Optional[str]:
# Only return one bins dir, because the tools are generally all in one
# directory for Qt4, in Qt5, they must all be in one directory. Return
# the first one found among the bin variables, in case one tool is not
# configured to be built.
applications = ['moc', 'uic', 'rcc', 'lupdate', 'lrelease']
for application in applications:
try:
return os.path.dirname(core.get_pkgconfig_variable('%s_location' % application, {}))
except mesonlib.MesonException:
pass
return None
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
return []
class Qt5PkgConfigDependency(QtPkgConfigDependency):
@staticmethod
def get_pkgconfig_host_bins(core: PkgConfigDependency) -> str:
return core.get_pkgconfig_variable('host_bins', {})
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
return _qt_get_private_includes(mod_inc_dir, module, self.version)
class Qt6PkgConfigDependency(QtPkgConfigDependency):
@staticmethod
def get_pkgconfig_host_bins(core: PkgConfigDependency) -> str:
return core.get_pkgconfig_variable('host_bins', {})
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
return _qt_get_private_includes(mod_inc_dir, module, self.version)
qt4_factory = DependencyFactory(
'qt4',
[DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL],
pkgconfig_class=Qt4PkgConfigDependency,
configtool_class=Qt4ConfigToolDependency,
)
qt5_factory = DependencyFactory(
'qt5',
[DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL],
pkgconfig_class=Qt5PkgConfigDependency,
configtool_class=Qt5ConfigToolDependency,
)
qt6_factory = DependencyFactory(
'qt6',
[DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL],
pkgconfig_class=Qt6PkgConfigDependency,
configtool_class=Qt6ConfigToolDependency,
)

@ -15,27 +15,22 @@
# This file contains the detection logic for external dependencies that # This file contains the detection logic for external dependencies that
# are UI-related. # are UI-related.
import os import os
import re
import subprocess import subprocess
import typing as T import typing as T
from collections import OrderedDict
from .. import mlog from .. import mlog
from .. import mesonlib from .. import mesonlib
from ..mesonlib import ( from ..mesonlib import (
MesonException, Popen_safe, extract_as_list, version_compare_many Popen_safe, extract_as_list, version_compare_many
) )
from ..environment import detect_cpu_family from ..environment import detect_cpu_family
from .base import DependencyException, DependencyMethods from .base import DependencyException, DependencyMethods
from .base import ExternalDependency from .base import ExternalDependency
from .base import ExtraFrameworkDependency, PkgConfigDependency
from .base import ConfigToolDependency, DependencyFactory from .base import ConfigToolDependency, DependencyFactory
from ..programs import find_external_program, NonExistingExternalProgram
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from ..environment import Environment from ..environment import Environment
from ..programs import ExternalProgram
class GLDependencySystem(ExternalDependency): class GLDependencySystem(ExternalDependency):
@ -140,421 +135,6 @@ class GnuStepDependency(ConfigToolDependency):
return version return version
def _qt_get_private_includes(mod_inc_dir, module, mod_version):
# usually Qt5 puts private headers in /QT_INSTALL_HEADERS/module/VERSION/module/private
# except for at least QtWebkit and Enginio where the module version doesn't match Qt version
# as an example with Qt 5.10.1 on linux you would get:
# /usr/include/qt5/QtCore/5.10.1/QtCore/private/
# /usr/include/qt5/QtWidgets/5.10.1/QtWidgets/private/
# /usr/include/qt5/QtWebKit/5.212.0/QtWebKit/private/
# on Qt4 when available private folder is directly in module folder
# like /usr/include/QtCore/private/
if int(mod_version.split('.')[0]) < 5:
return tuple()
private_dir = os.path.join(mod_inc_dir, mod_version)
# fallback, let's try to find a directory with the latest version
if not os.path.exists(private_dir):
dirs = [filename for filename in os.listdir(mod_inc_dir)
if os.path.isdir(os.path.join(mod_inc_dir, filename))]
dirs.sort(reverse=True)
for dirname in dirs:
if len(dirname.split('.')) == 3:
private_dir = dirname
break
return (private_dir,
os.path.join(private_dir, 'Qt' + module))
class QtExtraFrameworkDependency(ExtraFrameworkDependency):
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"):
if self.found():
mod_inc_dir = os.path.join(self.framework_path, 'Headers')
args = ['-I' + mod_inc_dir]
if with_private_headers:
args += ['-I' + dirname for dirname in _qt_get_private_includes(mod_inc_dir, self.mod_name, qt_version)]
return args
return []
class QtBaseDependency(ExternalDependency):
def __init__(self, name, env, kwargs):
super().__init__(name, env, kwargs, language='cpp')
self.qtname = name.capitalize()
self.qtver = name[-1]
if self.qtver == "4":
self.qtpkgname = 'Qt'
else:
self.qtpkgname = self.qtname
self.root = '/usr'
self.bindir = None
self.private_headers = kwargs.get('private_headers', False)
mods = extract_as_list(kwargs, 'modules')
self.requested_modules = mods
if not mods:
raise DependencyException('No ' + self.qtname + ' modules specified.')
self.from_text = 'pkg-config'
self.qtmain = kwargs.get('main', False)
if not isinstance(self.qtmain, bool):
raise DependencyException('"main" argument must be a boolean')
# Keep track of the detection methods used, for logging purposes.
methods = []
# Prefer pkg-config, then fallback to `qmake -query`
if DependencyMethods.PKGCONFIG in self.methods:
mlog.debug('Trying to find qt with pkg-config')
self._pkgconfig_detect(mods, kwargs)
methods.append('pkgconfig')
if not self.is_found and DependencyMethods.QMAKE in self.methods:
mlog.debug('Trying to find qt with qmake')
self.from_text = self._qmake_detect(mods, kwargs)
methods.append('qmake-' + self.name)
methods.append('qmake')
if not self.is_found:
# Reset compile args and link args
self.compile_args = []
self.link_args = []
self.from_text = mlog.format_list(methods)
self.version = None
def compilers_detect(self, interp_obj):
"Detect Qt (4 or 5) moc, uic, rcc in the specified bindir or in PATH"
# It is important that this list does not change order as the order of
# the returned ExternalPrograms will change as well
bins = ['moc', 'uic', 'rcc', 'lrelease']
found = {b: NonExistingExternalProgram(name=f'{b}-{self.name}')
for b in bins}
wanted = f'== {self.version}'
def gen_bins():
for b in bins:
if self.bindir:
yield os.path.join(self.bindir, b), b
# prefer the <tool>-qt<version> of the tool to the plain one, as we
# don't know what the unsuffixed one points to without calling it.
yield f'{b}-{self.name}', b
yield b, b
for b, name in gen_bins():
if found[name].found():
continue
if name == 'lrelease':
arg = ['-version']
elif mesonlib.version_compare(self.version, '>= 5'):
arg = ['--version']
else:
arg = ['-v']
# Ensure that the version of qt and each tool are the same
def get_version(p):
_, out, err = mesonlib.Popen_safe(p.get_command() + arg)
if b.startswith('lrelease') or not self.version.startswith('4'):
care = out
else:
care = err
return care.split(' ')[-1].replace(')', '').strip()
p = interp_obj.find_program_impl([b], required=False,
version_func=get_version,
wanted=wanted).held_object
if p.found():
found[name] = p
return tuple([found[b] for b in bins])
def _pkgconfig_detect(self, mods, kwargs):
# We set the value of required to False so that we can try the
# qmake-based fallback if pkg-config fails.
kwargs['required'] = False
modules = OrderedDict()
for module in mods:
modules[module] = PkgConfigDependency(self.qtpkgname + module, self.env,
kwargs, language=self.language)
for m_name, m in modules.items():
if not m.found():
self.is_found = False
return
self.compile_args += m.get_compile_args()
if self.private_headers:
qt_inc_dir = m.get_pkgconfig_variable('includedir', dict())
mod_private_dir = os.path.join(qt_inc_dir, 'Qt' + m_name)
if not os.path.isdir(mod_private_dir):
# At least some versions of homebrew don't seem to set this
# up correctly. /usr/local/opt/qt/include/Qt + m_name is a
# symlink to /usr/local/opt/qt/include, but the pkg-config
# file points to /usr/local/Cellar/qt/x.y.z/Headers/, and
# the Qt + m_name there is not a symlink, it's a file
mod_private_dir = qt_inc_dir
mod_private_inc = _qt_get_private_includes(mod_private_dir, m_name, m.version)
for directory in mod_private_inc:
self.compile_args.append('-I' + directory)
self.link_args += m.get_link_args()
if 'Core' in modules:
core = modules['Core']
else:
corekwargs = {'required': 'false', 'silent': 'true'}
core = PkgConfigDependency(self.qtpkgname + 'Core', self.env, corekwargs,
language=self.language)
modules['Core'] = core
if self.env.machines[self.for_machine].is_windows() and self.qtmain:
# Check if we link with debug binaries
debug_lib_name = self.qtpkgname + 'Core' + self._get_modules_lib_suffix(True)
is_debug = False
for arg in core.get_link_args():
if arg == '-l%s' % debug_lib_name or arg.endswith('%s.lib' % debug_lib_name) or arg.endswith('%s.a' % debug_lib_name):
is_debug = True
break
libdir = core.get_pkgconfig_variable('libdir', {})
if not self._link_with_qtmain(is_debug, libdir):
self.is_found = False
return
self.is_found = True
self.version = m.version
self.pcdep = list(modules.values())
# Try to detect moc, uic, rcc
# Used by self.compilers_detect()
self.bindir = self.get_pkgconfig_host_bins(core)
if not self.bindir:
# If exec_prefix is not defined, the pkg-config file is broken
prefix = core.get_pkgconfig_variable('exec_prefix', {})
if prefix:
self.bindir = os.path.join(prefix, 'bin')
def search_qmake(self) -> T.Generator['ExternalProgram', None, None]:
for qmake in ('qmake-' + self.name, 'qmake'):
yield from find_external_program(self.env, self.for_machine, qmake, 'QMake', [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(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 :(
self.is_found = False
return
self.version = re.search(self.qtver + r'(\.\d+)+', stdo).group(0)
# Query library path, header path, and binary path
mlog.log("Found qmake:", mlog.bold(self.qmake.get_path()), '(%s)' % self.version)
stdo = Popen_safe(self.qmake.get_command() + ['-query'])[1]
qvars = {}
for line in stdo.split('\n'):
line = line.strip()
if line == '':
continue
(k, v) = tuple(line.split(':', 1))
qvars[k] = v
# Qt on macOS uses a framework, but Qt for iOS/tvOS does not
xspec = qvars.get('QMAKE_XSPEC', '')
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)
# Sometimes Qt is built not as a framework (for instance, when using conan pkg manager)
# skip and fall back to normal procedure then
if self.is_found:
return self.qmake.name
else:
mlog.debug("Building for macOS, couldn't find framework, falling back to library search")
incdir = qvars['QT_INSTALL_HEADERS']
self.compile_args.append('-I' + incdir)
libdir = qvars['QT_INSTALL_LIBS']
# Used by self.compilers_detect()
self.bindir = self.get_qmake_host_bins(qvars)
self.is_found = True
# Use the buildtype by default, but look at the b_vscrt option if the
# compiler supports it.
is_debug = self.env.coredata.get_option(mesonlib.OptionKey('buildtype')) == 'debug'
if mesonlib.OptionKey('b_vscrt') in self.env.coredata.options:
if self.env.coredata.options[mesonlib.OptionKey('b_vscrt')].value in {'mdd', 'mtd'}:
is_debug = True
modules_lib_suffix = self._get_modules_lib_suffix(is_debug)
for module in mods:
mincdir = os.path.join(incdir, 'Qt' + module)
self.compile_args.append('-I' + mincdir)
if module == 'QuickTest':
define_base = 'QMLTEST'
elif module == 'Test':
define_base = 'TESTLIB'
else:
define_base = module.upper()
self.compile_args.append('-DQT_%s_LIB' % define_base)
if self.private_headers:
priv_inc = self.get_private_includes(mincdir, module)
for directory in priv_inc:
self.compile_args.append('-I' + directory)
libfile = self.clib_compiler.find_library(self.qtpkgname + module + modules_lib_suffix,
self.env,
libdir)
if libfile:
libfile = libfile[0]
else:
mlog.log("Could not find:", module,
self.qtpkgname + module + modules_lib_suffix,
'in', libdir)
self.is_found = False
break
self.link_args.append(libfile)
if self.env.machines[self.for_machine].is_windows() and self.qtmain:
if not self._link_with_qtmain(is_debug, libdir):
self.is_found = False
return self.qmake.name
def _get_modules_lib_suffix(self, is_debug):
suffix = ''
if self.env.machines[self.for_machine].is_windows():
if is_debug:
suffix += 'd'
if self.qtver == '4':
suffix += '4'
if self.env.machines[self.for_machine].is_darwin():
if is_debug:
suffix += '_debug'
if mesonlib.version_compare(self.version, '>= 5.14.0'):
if self.env.machines[self.for_machine].is_android():
cpu_family = self.env.machines[self.for_machine].cpu_family
if cpu_family == 'x86':
suffix += '_x86'
elif cpu_family == 'x86_64':
suffix += '_x86_64'
elif cpu_family == 'arm':
suffix += '_armeabi-v7a'
elif cpu_family == 'aarch64':
suffix += '_arm64-v8a'
else:
mlog.warning('Android target arch {!r} for Qt5 is unknown, '
'module detection may not work'.format(cpu_family))
return suffix
def _link_with_qtmain(self, is_debug, libdir):
base_name = 'qtmaind' if is_debug else 'qtmain'
qtmain = self.clib_compiler.find_library(base_name, self.env, libdir)
if qtmain:
self.link_args.append(qtmain[0])
return True
return False
def _framework_detect(self, qvars, modules, kwargs):
libdir = qvars['QT_INSTALL_LIBS']
# 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, self.env, fw_kwargs, language=self.language)
if fwdep.found():
self.compile_args.append('-F' + libdir)
self.compile_args += fwdep.get_compile_args(with_private_headers=self.private_headers,
qt_version=self.version)
self.link_args += fwdep.get_link_args()
else:
break
else:
self.is_found = True
# Used by self.compilers_detect()
self.bindir = self.get_qmake_host_bins(qvars)
def get_qmake_host_bins(self, qvars):
# Prefer QT_HOST_BINS (qt5, correct for cross and native compiling)
# but fall back to QT_INSTALL_BINS (qt4)
if 'QT_HOST_BINS' in qvars:
return qvars['QT_HOST_BINS']
else:
return qvars['QT_INSTALL_BINS']
@staticmethod
def get_methods():
return [DependencyMethods.PKGCONFIG, DependencyMethods.QMAKE]
def get_exe_args(self, compiler):
# Originally this was -fPIE but nowadays the default
# for upstream and distros seems to be -reduce-relocations
# which requires -fPIC. This may cause a performance
# penalty when using self-built Qt or on platforms
# where -fPIC is not required. If this is an issue
# for you, patches are welcome.
return compiler.get_pic_args()
def get_private_includes(self, mod_inc_dir, module):
return tuple()
def log_details(self):
module_str = ', '.join(self.requested_modules)
return 'modules: ' + module_str
def log_info(self):
return f'{self.from_text}'
def log_tried(self):
return self.from_text
class Qt4Dependency(QtBaseDependency):
def __init__(self, env, kwargs):
QtBaseDependency.__init__(self, 'qt4', env, kwargs)
def get_pkgconfig_host_bins(self, core):
# Only return one bins dir, because the tools are generally all in one
# directory for Qt4, in Qt5, they must all be in one directory. Return
# the first one found among the bin variables, in case one tool is not
# configured to be built.
applications = ['moc', 'uic', 'rcc', 'lupdate', 'lrelease']
for application in applications:
try:
return os.path.dirname(core.get_pkgconfig_variable('%s_location' % application, {}))
except MesonException:
pass
class Qt5Dependency(QtBaseDependency):
def __init__(self, env, kwargs):
QtBaseDependency.__init__(self, 'qt5', env, kwargs)
def get_pkgconfig_host_bins(self, core):
return core.get_pkgconfig_variable('host_bins', {})
def get_private_includes(self, mod_inc_dir, module):
return _qt_get_private_includes(mod_inc_dir, module, self.version)
class Qt6Dependency(QtBaseDependency):
def __init__(self, env, kwargs):
QtBaseDependency.__init__(self, 'qt6', env, kwargs)
def get_pkgconfig_host_bins(self, core):
return core.get_pkgconfig_variable('host_bins', {})
def get_private_includes(self, mod_inc_dir, module):
return _qt_get_private_includes(mod_inc_dir, module, self.version)
class SDL2DependencyConfigTool(ConfigToolDependency): class SDL2DependencyConfigTool(ConfigToolDependency):
tools = ['sdl2-config'] tools = ['sdl2-config']

@ -12,44 +12,97 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from mesonbuild.dependencies.base import find_external_dependency
import os import os
import shutil import shutil
import typing as T
from .. import mlog from .. import mlog
from .. import build from .. import build
from .. import mesonlib
from ..mesonlib import MesonException, extract_as_list, File, unholder, version_compare from ..mesonlib import MesonException, extract_as_list, File, unholder, version_compare
from ..dependencies import Dependency, Qt4Dependency, Qt5Dependency, Qt6Dependency from ..dependencies import Dependency
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from . import ModuleReturnValue, get_include_args, ExtensionModule from . import ModuleReturnValue, get_include_args, ExtensionModule
from ..interpreterbase import noPosargs, permittedKwargs, FeatureNew, FeatureNewKwargs from ..interpreterbase import noPosargs, permittedKwargs, FeatureNew, FeatureNewKwargs
from ..interpreter import extract_required_kwarg from ..interpreter import extract_required_kwarg
from ..programs import NonExistingExternalProgram from ..programs import NonExistingExternalProgram
_QT_DEPS_LUT = { if T.TYPE_CHECKING:
4: Qt4Dependency, from ..interpreter import Interpreter
5: Qt5Dependency, from ..dependencies.qt import QtBaseDependency
6: Qt6Dependency, from ..environment import Environment
} from ..programs import ExternalProgram
class QtBaseModule(ExtensionModule): class QtBaseModule(ExtensionModule):
tools_detected = False tools_detected = False
rcc_supports_depfiles = False rcc_supports_depfiles = False
def __init__(self, interpreter, qt_version=5): def __init__(self, interpreter: 'Interpreter', qt_version=5):
ExtensionModule.__init__(self, interpreter) ExtensionModule.__init__(self, interpreter)
self.snippets.add('has_tools') self.snippets.add('has_tools')
self.qt_version = qt_version self.qt_version = qt_version
self.moc: 'ExternalProgram' = NonExistingExternalProgram('moc')
self.uic: 'ExternalProgram' = NonExistingExternalProgram('uic')
self.rcc: 'ExternalProgram' = NonExistingExternalProgram('rcc')
self.lrelease: 'ExternalProgram' = NonExistingExternalProgram('lrelease')
def compilers_detect(self, qt_dep: 'QtBaseDependency') -> None:
"""Detect Qt (4 or 5) moc, uic, rcc in the specified bindir or in PATH"""
# It is important that this list does not change order as the order of
# the returned ExternalPrograms will change as well
bins = ['moc', 'uic', 'rcc', 'lrelease']
found = {b: NonExistingExternalProgram(name=f'{b}-qt{qt_dep.qtver}')
for b in bins}
wanted = f'== {qt_dep.version}'
def gen_bins() -> T.Generator[T.Tuple[str, str], None, None]:
for b in bins:
if qt_dep.bindir:
yield os.path.join(qt_dep.bindir, b), b
# prefer the <tool>-qt<version> of the tool to the plain one, as we
# don't know what the unsuffixed one points to without calling it.
yield f'{b}-qt{qt_dep.qtver}', b
yield b, b
for b, name in gen_bins():
if found[name].found():
continue
if name == 'lrelease':
arg = ['-version']
elif mesonlib.version_compare(qt_dep.version, '>= 5'):
arg = ['--version']
else:
arg = ['-v']
# Ensure that the version of qt and each tool are the same
def get_version(p: 'ExternalProgram') -> str:
_, out, err = mesonlib.Popen_safe(p.get_command() + arg)
if b.startswith('lrelease') or not qt_dep.version.startswith('4'):
care = out
else:
care = err
return care.split(' ')[-1].replace(')', '').strip()
p = self.interpreter.find_program_impl(
[b], required=False,
version_func=get_version,
wanted=wanted).held_object
if p.found():
setattr(self, name, p)
def _detect_tools(self, env, method, required=True): def _detect_tools(self, env: 'Environment', method, required=True):
if self.tools_detected: if self.tools_detected:
return return
self.tools_detected = True self.tools_detected = True
mlog.log(f'Detecting Qt{self.qt_version} tools') mlog.log(f'Detecting Qt{self.qt_version} tools')
kwargs = {'required': required, 'modules': 'Core', 'method': method} kwargs = {'required': required, 'modules': 'Core', 'method': method}
qt = _QT_DEPS_LUT[self.qt_version](env, kwargs) qt = find_external_dependency(f'qt{self.qt_version}', env, kwargs)
if qt.found(): if qt.found():
# Get all tools and then make sure that they are the right version # Get all tools and then make sure that they are the right version
self.moc, self.uic, self.rcc, self.lrelease = qt.compilers_detect(self.interpreter) self.compilers_detect(qt)
if version_compare(qt.version, '>=5.14.0'): if version_compare(qt.version, '>=5.14.0'):
self.rcc_supports_depfiles = True self.rcc_supports_depfiles = True
else: else:
@ -190,7 +243,7 @@ class QtBaseModule(ExtensionModule):
compile_args = [] compile_args = []
for dep in unholder(dependencies): for dep in unholder(dependencies):
if isinstance(dep, Dependency): if isinstance(dep, Dependency):
for arg in dep.get_compile_args(): for arg in dep.get_all_compile_args():
if arg.startswith('-I') or arg.startswith('-D'): if arg.startswith('-I') or arg.startswith('-D'):
compile_args.append(arg) compile_args.append(arg)
else: else:

@ -21,6 +21,7 @@ modules = [
'mesonbuild/dependencies/boost.py', 'mesonbuild/dependencies/boost.py',
'mesonbuild/dependencies/hdf5.py', 'mesonbuild/dependencies/hdf5.py',
'mesonbuild/dependencies/mpi.py', 'mesonbuild/dependencies/mpi.py',
'mesonbuild/dependencies/qt.py',
'mesonbuild/envconfig.py', 'mesonbuild/envconfig.py',
'mesonbuild/interpreterbase.py', 'mesonbuild/interpreterbase.py',
'mesonbuild/linkers.py', 'mesonbuild/linkers.py',

@ -853,7 +853,7 @@ def have_java():
return True return True
return False return False
def skippable(suite, test): def skippable(suite: str, test: str) -> bool:
# Everything is optional when not running on CI # Everything is optional when not running on CI
if not under_ci: if not under_ci:
return True return True
@ -901,11 +901,6 @@ def skippable(suite, test):
if test.endswith('34 gir static lib'): if test.endswith('34 gir static lib'):
return True return True
# No frameworks test should be skipped on linux CI, as we expect all
# prerequisites to be installed
if mesonlib.is_linux():
return False
# Boost test should only be skipped for windows CI build matrix entries # Boost test should only be skipped for windows CI build matrix entries
# which don't define BOOST_ROOT # which don't define BOOST_ROOT
if test.endswith('1 boost'): if test.endswith('1 boost'):
@ -913,14 +908,22 @@ def skippable(suite, test):
return 'BOOST_ROOT' not in os.environ return 'BOOST_ROOT' not in os.environ
return False return False
# Qt is provided on macOS by Homebrew # Not all OSes have all of the methods for qt (qmake and pkg-config), don't
if test.endswith('4 qt') and mesonlib.is_osx(): # fail if that happens.
return False #
# On macOS we should have all of the requirements at all times.
if test.endswith('4 qt'):
return not mesonlib.is_osx()
# Bindgen isn't available in all distros # Bindgen isn't available in all distros
if test.endswith('12 bindgen'): if test.endswith('12 bindgen'):
return False return False
# No frameworks test should be skipped on linux CI, as we expect all
# prerequisites to be installed
if mesonlib.is_linux():
return False
# Other framework tests are allowed to be skipped on other platforms # Other framework tests are allowed to be skipped on other platforms
return True return True

@ -16,7 +16,7 @@ import typing as T
from mesonbuild import environment from mesonbuild import environment
from mesonbuild import mlog from mesonbuild import mlog
from mesonbuild import mesonlib from mesonbuild import mesonlib
from run_project_tests import TestDef, load_test_json, run_test, BuildStep from run_project_tests import TestDef, load_test_json, run_test, BuildStep, skippable
from run_tests import get_backend_commands, guess_backend, get_fake_options from run_tests import get_backend_commands, guess_backend, get_fake_options
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
@ -62,7 +62,7 @@ def main() -> None:
results = [run_test(t, t.args, comp, backend, backend_args, commands, False, True) for t in tests] results = [run_test(t, t.args, comp, backend, backend_args, commands, False, True) for t in tests]
failed = False failed = False
for test, result in zip(tests, results): for test, result in zip(tests, results):
if result is None: if (result is None) or (('MESON_SKIP_TEST' in result.stdo) and (skippable(str(args.case.parent), test.path.as_posix()))):
msg = mlog.yellow('SKIP:') msg = mlog.yellow('SKIP:')
elif result.msg: elif result.msg:
msg = mlog.red('FAIL:') msg = mlog.red('FAIL:')
@ -70,7 +70,7 @@ def main() -> None:
else: else:
msg = mlog.green('PASS:') msg = mlog.green('PASS:')
mlog.log(msg, test.display_name()) mlog.log(msg, test.display_name())
if result.msg: if result.msg and 'MESON_SKIP_TEST' not in result.stdo:
mlog.log('reason:', result.msg) mlog.log('reason:', result.msg)
if result.step is BuildStep.configure: if result.step is BuildStep.configure:
# For configure failures, instead of printing stdout, # For configure failures, instead of printing stdout,

@ -6593,10 +6593,10 @@ class LinuxlikeTests(BasePlatformTests):
mesonlog = self.get_meson_log() mesonlog = self.get_meson_log()
if qt4 == 0: if qt4 == 0:
self.assertRegex('\n'.join(mesonlog), self.assertRegex('\n'.join(mesonlog),
r'Run-time dependency qt4 \(modules: Core\) found: YES 4.* \(pkg-config\)\n') r'Run-time dependency qt4 \(modules: Core\) found: YES 4.* \(pkg-config\)')
if qt5 == 0: if qt5 == 0:
self.assertRegex('\n'.join(mesonlog), self.assertRegex('\n'.join(mesonlog),
r'Run-time dependency qt5 \(modules: Core\) found: YES 5.* \(pkg-config\)\n') r'Run-time dependency qt5 \(modules: Core\) found: YES 5.* \(pkg-config\)')
@skip_if_not_base_option('b_sanitize') @skip_if_not_base_option('b_sanitize')
def test_generate_gir_with_address_sanitizer(self): def test_generate_gir_with_address_sanitizer(self):
@ -6627,7 +6627,7 @@ class LinuxlikeTests(BasePlatformTests):
# Confirm that the dependency was found with qmake # Confirm that the dependency was found with qmake
mesonlog = self.get_meson_log() mesonlog = self.get_meson_log()
self.assertRegex('\n'.join(mesonlog), self.assertRegex('\n'.join(mesonlog),
r'Run-time dependency qt5 \(modules: Core\) found: YES .* \((qmake|qmake-qt5)\)\n') r'Run-time dependency qt5 \(modules: Core\) found: YES .* \(qmake\)\n')
def test_qt6dependency_qmake_detection(self): def test_qt6dependency_qmake_detection(self):
''' '''
@ -6647,7 +6647,7 @@ class LinuxlikeTests(BasePlatformTests):
# Confirm that the dependency was found with qmake # Confirm that the dependency was found with qmake
mesonlog = self.get_meson_log() mesonlog = self.get_meson_log()
self.assertRegex('\n'.join(mesonlog), self.assertRegex('\n'.join(mesonlog),
r'Run-time dependency qt6 \(modules: Core\) found: YES .* \((qmake|qmake-qt6)\)\n') r'Run-time dependency qt6 \(modules: Core\) found: YES .* \(qmake\)\n')
def glob_sofiles_without_privdir(self, g): def glob_sofiles_without_privdir(self, g):
files = glob(g) files = glob(g)

@ -0,0 +1,11 @@
{
"matrix": {
"options": {
"method": [
{ "val": "config-tool" },
{ "val": "qmake" },
{ "val": "pkg-config" }
]
}
}
}
Loading…
Cancel
Save