aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2021-03-24 22:52:15 +0200
committerGitHub <noreply@github.com>2021-03-24 22:52:15 +0200
commit54767ab482eb30a843974e985d2998f5476c906c (patch)
tree34e083053ab4f12e1643b826ef12d55a8446dfdc
parentc8826e907a3e1a2428ddec73133f3c3e6ea859a2 (diff)
parent1767c57492529fc7c3823871f861db35b89f5c1f (diff)
downloadmeson-54767ab482eb30a843974e985d2998f5476c906c.zip
meson-54767ab482eb30a843974e985d2998f5476c906c.tar.gz
meson-54767ab482eb30a843974e985d2998f5476c906c.tar.bz2
Merge pull request #8568 from dcbaker/submit/qt-dependency-factory
QT: use a proper dependency factory
-rw-r--r--docs/markdown/snippets/qt_factory.md6
-rw-r--r--mesonbuild/dependencies/__init__.py9
-rw-r--r--mesonbuild/dependencies/base.py26
-rw-r--r--mesonbuild/dependencies/qt.py438
-rw-r--r--mesonbuild/dependencies/ui.py422
-rw-r--r--mesonbuild/modules/qt.py75
-rwxr-xr-xrun_mypy.py1
-rwxr-xr-xrun_project_tests.py21
-rwxr-xr-xrun_single_test.py6
-rwxr-xr-xrun_unittests.py8
-rw-r--r--test cases/frameworks/4 qt/test.json11
11 files changed, 564 insertions, 459 deletions
diff --git a/docs/markdown/snippets/qt_factory.md b/docs/markdown/snippets/qt_factory.md
new file mode 100644
index 0000000..11cc783
--- /dev/null
+++ b/docs/markdown/snippets/qt_factory.md
@@ -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.
diff --git a/mesonbuild/dependencies/__init__.py b/mesonbuild/dependencies/__init__.py
index 91d3d87..afd4adc 100644
--- a/mesonbuild/dependencies/__init__.py
+++ b/mesonbuild/dependencies/__init__.py
@@ -31,7 +31,8 @@ from .misc import (
shaderc_factory, threads_factory,
)
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.
@@ -225,9 +226,9 @@ packages.update({
# From ui:
'gl': gl_factory,
'gnustep': GnuStepDependency,
- 'qt4': Qt4Dependency,
- 'qt5': Qt5Dependency,
- 'qt6': Qt6Dependency,
+ 'qt4': qt4_factory,
+ 'qt5': qt5_factory,
+ 'qt6': qt6_factory,
'sdl2': sdl2_factory,
'wxwidgets': WxDependency,
'vulkan': vulkan_factory,
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index 27c33b6..14380d4 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -18,6 +18,7 @@ import copy
import functools
import os
import re
+import itertools
import json
import shlex
import shutil
@@ -37,9 +38,10 @@ from ..mesonlib import Popen_safe, version_compare_many, version_compare, listif
from ..mesonlib import Version, LibType, OptionKey
from ..mesondata import mesondata
from ..programs import ExternalProgram, find_external_program
+from ..interpreterbase import FeatureDeprecated
if T.TYPE_CHECKING:
- from ..compilers.compilers import CompilerType # noqa: F401
+ from ..compilers.compilers import Compiler
DependencyType = T.TypeVar('DependencyType', bound='Dependency')
# 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 = 'auto'
PKGCONFIG = 'pkg-config'
- QMAKE = 'qmake'
CMAKE = 'cmake'
# Just specify the standard link arguments, assuming the operating system provides the library.
SYSTEM = 'system'
@@ -70,6 +71,7 @@ class DependencyMethods(Enum):
CUPSCONFIG = 'cups-config'
PCAPCONFIG = 'pcap-config'
LIBWMFCONFIG = 'libwmf-config'
+ QMAKE = 'qmake'
# Misc
DUB = 'dub'
@@ -135,11 +137,21 @@ class Dependency:
return converted
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]:
if raw and self.raw_link_args is not None:
return self.raw_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:
return self.is_found
@@ -2267,10 +2279,10 @@ def process_method_kw(possible: T.Iterable[DependencyMethods], kwargs) -> T.List
# 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)))
+ FeatureDeprecated.single_use(f'Configuration method {method.value}', '0.44', 'Use "config-tool" instead.')
+ method = DependencyMethods.CONFIG_TOOL
+ if method is DependencyMethods.QMAKE:
+ FeatureDeprecated.single_use(f'Configuration method "qmake"', '0.58', 'Use "config-tool" instead.')
method = DependencyMethods.CONFIG_TOOL
# 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,
- language: T.Optional[str]) -> T.Optional['CompilerType']:
+ language: T.Optional[str]) -> T.Optional['Compiler']:
"""Given a language and environment find the compiler used."""
compilers = env.coredata.compilers[for_machine]
diff --git a/mesonbuild/dependencies/qt.py b/mesonbuild/dependencies/qt.py
new file mode 100644
index 0000000..1059871
--- /dev/null
+++ b/mesonbuild/dependencies/qt.py
@@ -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,
+)
diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py
index 40455b8..26103ce 100644
--- a/mesonbuild/dependencies/ui.py
+++ b/mesonbuild/dependencies/ui.py
@@ -15,27 +15,22 @@
# This file contains the detection logic for external dependencies that
# are UI-related.
import os
-import re
import subprocess
import typing as T
-from collections import OrderedDict
from .. import mlog
from .. import mesonlib
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 .base import DependencyException, DependencyMethods
from .base import ExternalDependency
-from .base import ExtraFrameworkDependency, PkgConfigDependency
from .base import ConfigToolDependency, DependencyFactory
-from ..programs import find_external_program, NonExistingExternalProgram
if T.TYPE_CHECKING:
from ..environment import Environment
- from ..programs import ExternalProgram
class GLDependencySystem(ExternalDependency):
@@ -140,421 +135,6 @@ class GnuStepDependency(ConfigToolDependency):
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):
tools = ['sdl2-config']
diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py
index de3a5b1..645696d 100644
--- a/mesonbuild/modules/qt.py
+++ b/mesonbuild/modules/qt.py
@@ -12,44 +12,97 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from mesonbuild.dependencies.base import find_external_dependency
import os
import shutil
+import typing as T
+
from .. import mlog
from .. import build
+from .. import mesonlib
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
from . import ModuleReturnValue, get_include_args, ExtensionModule
from ..interpreterbase import noPosargs, permittedKwargs, FeatureNew, FeatureNewKwargs
from ..interpreter import extract_required_kwarg
from ..programs import NonExistingExternalProgram
-_QT_DEPS_LUT = {
- 4: Qt4Dependency,
- 5: Qt5Dependency,
- 6: Qt6Dependency,
-}
+if T.TYPE_CHECKING:
+ from ..interpreter import Interpreter
+ from ..dependencies.qt import QtBaseDependency
+ from ..environment import Environment
+ from ..programs import ExternalProgram
class QtBaseModule(ExtensionModule):
tools_detected = False
rcc_supports_depfiles = False
- def __init__(self, interpreter, qt_version=5):
+ def __init__(self, interpreter: 'Interpreter', qt_version=5):
ExtensionModule.__init__(self, interpreter)
self.snippets.add('has_tools')
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:
return
self.tools_detected = True
mlog.log(f'Detecting Qt{self.qt_version} tools')
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():
# 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'):
self.rcc_supports_depfiles = True
else:
@@ -190,7 +243,7 @@ class QtBaseModule(ExtensionModule):
compile_args = []
for dep in unholder(dependencies):
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'):
compile_args.append(arg)
else:
diff --git a/run_mypy.py b/run_mypy.py
index 75c825e..65de414 100755
--- a/run_mypy.py
+++ b/run_mypy.py
@@ -21,6 +21,7 @@ modules = [
'mesonbuild/dependencies/boost.py',
'mesonbuild/dependencies/hdf5.py',
'mesonbuild/dependencies/mpi.py',
+ 'mesonbuild/dependencies/qt.py',
'mesonbuild/envconfig.py',
'mesonbuild/interpreterbase.py',
'mesonbuild/linkers.py',
diff --git a/run_project_tests.py b/run_project_tests.py
index eb918c1..2883e7e 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -853,7 +853,7 @@ def have_java():
return True
return False
-def skippable(suite, test):
+def skippable(suite: str, test: str) -> bool:
# Everything is optional when not running on CI
if not under_ci:
return True
@@ -901,11 +901,6 @@ def skippable(suite, test):
if test.endswith('34 gir static lib'):
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
# which don't define BOOST_ROOT
if test.endswith('1 boost'):
@@ -913,14 +908,22 @@ def skippable(suite, test):
return 'BOOST_ROOT' not in os.environ
return False
- # Qt is provided on macOS by Homebrew
- if test.endswith('4 qt') and mesonlib.is_osx():
- return False
+ # Not all OSes have all of the methods for qt (qmake and pkg-config), don't
+ # fail if that happens.
+ #
+ # 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
if test.endswith('12 bindgen'):
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
return True
diff --git a/run_single_test.py b/run_single_test.py
index 9b3ed18..c6018d5 100755
--- a/run_single_test.py
+++ b/run_single_test.py
@@ -16,7 +16,7 @@ import typing as T
from mesonbuild import environment
from mesonbuild import mlog
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
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]
failed = False
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:')
elif result.msg:
msg = mlog.red('FAIL:')
@@ -70,7 +70,7 @@ def main() -> None:
else:
msg = mlog.green('PASS:')
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)
if result.step is BuildStep.configure:
# For configure failures, instead of printing stdout,
diff --git a/run_unittests.py b/run_unittests.py
index a53a43a..5362616 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -6593,10 +6593,10 @@ class LinuxlikeTests(BasePlatformTests):
mesonlog = self.get_meson_log()
if qt4 == 0:
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:
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')
def test_generate_gir_with_address_sanitizer(self):
@@ -6627,7 +6627,7 @@ class LinuxlikeTests(BasePlatformTests):
# Confirm that the dependency was found with qmake
mesonlog = self.get_meson_log()
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):
'''
@@ -6647,7 +6647,7 @@ class LinuxlikeTests(BasePlatformTests):
# Confirm that the dependency was found with qmake
mesonlog = self.get_meson_log()
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):
files = glob(g)
diff --git a/test cases/frameworks/4 qt/test.json b/test cases/frameworks/4 qt/test.json
new file mode 100644
index 0000000..4c70060
--- /dev/null
+++ b/test cases/frameworks/4 qt/test.json
@@ -0,0 +1,11 @@
+{
+ "matrix": {
+ "options": {
+ "method": [
+ { "val": "config-tool" },
+ { "val": "qmake" },
+ { "val": "pkg-config" }
+ ]
+ }
+ }
+}