Refactor Qt Dependency into proper split classes with factories
Currently the Qt Dependencies still use the old "combined" method for dependencies with multiple ways to be found. This is problematic as it means that `get_variable()` and friends don't work, as the dependency can't implement any of those methods. The correct solution is to make use of multiple Dependency instances, and a factory to tie them together. This does that. To handle QMake, I've leveraged the existing config-tool mechanism, which allows us to save a good deal of code, and use well tested code instead of rolling more of our own code. The one thing this doesn't do, but we probably should, is expose the macOS ExtraFrameworks directly, instead of forcing them to be found through QMake. That is a problem for another series, and someone who cares more about macOS than I do.
This commit is contained in:
parent
54c55f77a9
commit
c211fea513
|
@ -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,7 @@ from .misc import (
|
||||||
shaderc_factory, threads_factory,
|
shaderc_factory, threads_factory,
|
||||||
)
|
)
|
||||||
from .platform import AppleFrameworks
|
from .platform import AppleFrameworks
|
||||||
from .qt import Qt4Dependency, Qt5Dependency, Qt6Dependency
|
from .qt import qt4_factory, qt5_factory, qt6_factory
|
||||||
from .ui import GnuStepDependency, WxDependency, gl_factory, sdl2_factory, vulkan_factory
|
from .ui import GnuStepDependency, WxDependency, gl_factory, sdl2_factory, vulkan_factory
|
||||||
|
|
||||||
"""Dependency representations and discovery logic.
|
"""Dependency representations and discovery logic.
|
||||||
|
@ -226,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
|
||||||
|
@ -136,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
|
||||||
|
|
||||||
|
|
|
@ -17,25 +17,22 @@
|
||||||
"""Dependency finders for the Qt framework."""
|
"""Dependency finders for the Qt framework."""
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
import collections
|
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import typing as T
|
import typing as T
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
ExtraFrameworkDependency, ExternalDependency, DependencyException, DependencyMethods,
|
ExtraFrameworkDependency, DependencyException, DependencyMethods,
|
||||||
PkgConfigDependency
|
PkgConfigDependency,
|
||||||
)
|
)
|
||||||
from .base import ConfigToolDependency
|
from .base import ConfigToolDependency, DependencyFactory
|
||||||
from .. import mlog
|
from .. import mlog
|
||||||
from .. import mesonlib
|
from .. import mesonlib
|
||||||
from ..programs import find_external_program
|
|
||||||
|
|
||||||
if T.TYPE_CHECKING:
|
if T.TYPE_CHECKING:
|
||||||
from ..compilers import Compiler
|
from ..compilers import Compiler
|
||||||
from ..envconfig import MachineInfo
|
from ..envconfig import MachineInfo
|
||||||
from ..environment import Environment
|
from ..environment import Environment
|
||||||
from ..programs import ExternalProgram
|
|
||||||
|
|
||||||
|
|
||||||
def _qt_get_private_includes(mod_inc_dir: str, module: str, mod_version: str) -> T.List[str]:
|
def _qt_get_private_includes(mod_inc_dir: str, module: str, mod_version: str) -> T.List[str]:
|
||||||
|
@ -114,70 +111,78 @@ class QtExtraFrameworkDependency(ExtraFrameworkDependency):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
class QtBaseDependency(ExternalDependency, metaclass=abc.ABCMeta):
|
class _QtBase:
|
||||||
|
|
||||||
version: T.Optional[str]
|
"""Mixin class for shared componenets between PkgConfig and Qmake."""
|
||||||
|
|
||||||
def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
|
link_args: T.List[str]
|
||||||
super().__init__(name, env, kwargs, language='cpp')
|
clib_compiler: 'Compiler'
|
||||||
|
env: 'Environment'
|
||||||
|
|
||||||
|
def __init__(self, name: str, kwargs: T.Dict[str, T.Any]):
|
||||||
self.qtname = name.capitalize()
|
self.qtname = name.capitalize()
|
||||||
self.qtver = name[-1]
|
self.qtver = name[-1]
|
||||||
if self.qtver == "4":
|
if self.qtver == "4":
|
||||||
self.qtpkgname = 'Qt'
|
self.qtpkgname = 'Qt'
|
||||||
else:
|
else:
|
||||||
self.qtpkgname = self.qtname
|
self.qtpkgname = self.qtname
|
||||||
self.root = '/usr'
|
|
||||||
self.bindir: T.Optional[str] = None
|
|
||||||
self.private_headers = T.cast(bool, kwargs.get('private_headers', False))
|
self.private_headers = T.cast(bool, kwargs.get('private_headers', False))
|
||||||
mods = mesonlib.stringlistify(mesonlib.extract_as_list(kwargs, 'modules'))
|
|
||||||
self.requested_modules = mods
|
self.requested_modules = mesonlib.stringlistify(mesonlib.extract_as_list(kwargs, 'modules'))
|
||||||
if not mods:
|
if not self.requested_modules:
|
||||||
raise DependencyException('No ' + self.qtname + ' modules specified.')
|
raise DependencyException('No ' + self.qtname + ' modules specified.')
|
||||||
self.from_text = 'pkg-config'
|
|
||||||
|
|
||||||
self.qtmain = T.cast(bool, kwargs.get('main', False))
|
self.qtmain = T.cast(bool, kwargs.get('main', False))
|
||||||
if not isinstance(self.qtmain, bool):
|
if not isinstance(self.qtmain, bool):
|
||||||
raise DependencyException('"main" argument must be a boolean')
|
raise DependencyException('"main" argument must be a boolean')
|
||||||
|
|
||||||
# Keep track of the detection methods used, for logging purposes.
|
def _link_with_qtmain(self, is_debug: bool, libdir: T.Union[str, T.List[str]]) -> bool:
|
||||||
methods: T.List[str] = []
|
libdir = mesonlib.listify(libdir) # TODO: shouldn't be necessary
|
||||||
# Prefer pkg-config, then fallback to `qmake -query`
|
base_name = 'qtmaind' if is_debug else 'qtmain'
|
||||||
if DependencyMethods.PKGCONFIG in self.methods:
|
qtmain = self.clib_compiler.find_library(base_name, self.env, libdir)
|
||||||
mlog.debug('Trying to find qt with pkg-config')
|
if qtmain:
|
||||||
self._pkgconfig_detect(mods, kwargs)
|
self.link_args.append(qtmain[0])
|
||||||
methods.append('pkgconfig')
|
return True
|
||||||
if not self.is_found and DependencyMethods.QMAKE in self.methods:
|
return False
|
||||||
mlog.debug('Trying to find qt with qmake')
|
|
||||||
self.from_text = self._qmake_detect(mods, kwargs)
|
def get_exe_args(self, compiler: 'Compiler') -> T.List[str]:
|
||||||
methods.append('qmake-' + self.name)
|
# Originally this was -fPIE but nowadays the default
|
||||||
methods.append('qmake')
|
# for upstream and distros seems to be -reduce-relocations
|
||||||
if not self.is_found:
|
# which requires -fPIC. This may cause a performance
|
||||||
# Reset compile args and link args
|
# 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.compile_args = []
|
||||||
self.link_args = []
|
self.link_args = []
|
||||||
self.from_text = mlog.format_list(methods)
|
|
||||||
self.version = None
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
for m in self.requested_modules:
|
||||||
def get_pkgconfig_host_bins(self, core: PkgConfigDependency) -> T.Optional[str]:
|
mod = PkgConfigDependency(self.qtpkgname + m, self.env, kwargs, language=self.language)
|
||||||
pass
|
if not mod.found():
|
||||||
|
|
||||||
def _pkgconfig_detect(self, mods: T.List[str], kwargs: T.Dict[str, T.Any]) -> None:
|
|
||||||
# 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: T.MutableMapping[str, PkgConfigDependency] = collections.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
|
self.is_found = False
|
||||||
return
|
return
|
||||||
self.compile_args += m.get_compile_args()
|
|
||||||
if self.private_headers:
|
if self.private_headers:
|
||||||
qt_inc_dir = m.get_pkgconfig_variable('includedir', dict())
|
qt_inc_dir = mod.get_pkgconfig_variable('includedir', {})
|
||||||
mod_private_dir = os.path.join(qt_inc_dir, 'Qt' + m_name)
|
mod_private_dir = os.path.join(qt_inc_dir, 'Qt' + m)
|
||||||
if not os.path.isdir(mod_private_dir):
|
if not os.path.isdir(mod_private_dir):
|
||||||
# At least some versions of homebrew don't seem to set this
|
# At least some versions of homebrew don't seem to set this
|
||||||
# up correctly. /usr/local/opt/qt/include/Qt + m_name is a
|
# up correctly. /usr/local/opt/qt/include/Qt + m_name is a
|
||||||
|
@ -185,94 +190,93 @@ class QtBaseDependency(ExternalDependency, metaclass=abc.ABCMeta):
|
||||||
# file points to /usr/local/Cellar/qt/x.y.z/Headers/, and
|
# 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
|
# the Qt + m_name there is not a symlink, it's a file
|
||||||
mod_private_dir = qt_inc_dir
|
mod_private_dir = qt_inc_dir
|
||||||
mod_private_inc = _qt_get_private_includes(mod_private_dir, m_name, m.version)
|
mod_private_inc = _qt_get_private_includes(mod_private_dir, m, mod.version)
|
||||||
for directory in mod_private_inc:
|
for directory in mod_private_inc:
|
||||||
self.compile_args.append('-I' + directory)
|
mod.compile_args.append('-I' + directory)
|
||||||
self.link_args += m.get_link_args()
|
self._add_sub_dependency([lambda: mod])
|
||||||
|
|
||||||
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:
|
if self.env.machines[self.for_machine].is_windows() and self.qtmain:
|
||||||
# Check if we link with debug binaries
|
# 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)
|
debug_lib_name = self.qtpkgname + 'Core' + _get_modules_lib_suffix(self.version, self.env.machines[self.for_machine], True)
|
||||||
is_debug = False
|
is_debug = False
|
||||||
for arg in core.get_link_args():
|
for arg in self.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):
|
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
|
is_debug = True
|
||||||
break
|
break
|
||||||
libdir = core.get_pkgconfig_variable('libdir', {})
|
libdir = self.get_pkgconfig_variable('libdir', {})
|
||||||
if not self._link_with_qtmain(is_debug, libdir):
|
if not self._link_with_qtmain(is_debug, libdir):
|
||||||
self.is_found = False
|
self.is_found = False
|
||||||
return
|
return
|
||||||
|
|
||||||
self.is_found = True
|
self.bindir = self.get_pkgconfig_host_bins(self)
|
||||||
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 not self.bindir:
|
||||||
# If exec_prefix is not defined, the pkg-config file is broken
|
# If exec_prefix is not defined, the pkg-config file is broken
|
||||||
prefix = core.get_pkgconfig_variable('exec_prefix', {})
|
prefix = self.get_pkgconfig_variable('exec_prefix', {})
|
||||||
if prefix:
|
if prefix:
|
||||||
self.bindir = os.path.join(prefix, 'bin')
|
self.bindir = os.path.join(prefix, 'bin')
|
||||||
|
|
||||||
def search_qmake(self) -> T.Generator['ExternalProgram', None, None]:
|
@staticmethod
|
||||||
for qmake in ('qmake-' + self.name, 'qmake'):
|
@abc.abstractmethod
|
||||||
yield from find_external_program(self.env, self.for_machine, qmake, 'QMake', [qmake])
|
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
|
||||||
|
|
||||||
def _qmake_detect(self, mods: T.List[str], kwargs: T.Dict[str, T.Any]) -> T.Optional[str]:
|
|
||||||
for qmake in self.search_qmake():
|
|
||||||
if not qmake.found():
|
|
||||||
continue
|
|
||||||
# Check that the qmake is for qt5
|
|
||||||
pc, stdo = mesonlib.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 None
|
|
||||||
self.version = re.search(self.qtver + r'(\.\d+)+', stdo).group(0)
|
|
||||||
# Query library path, header path, and binary path
|
# Query library path, header path, and binary path
|
||||||
mlog.log("Found qmake:", mlog.bold(self.qmake.get_path()), '(%s)' % self.version)
|
stdo = self.get_config_value(['-query'], 'args')
|
||||||
stdo = mesonlib.Popen_safe(self.qmake.get_command() + ['-query'])[1]
|
qvars: T.Dict[str, str] = {}
|
||||||
qvars = {}
|
for line in stdo:
|
||||||
for line in stdo.split('\n'):
|
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if line == '':
|
if line == '':
|
||||||
continue
|
continue
|
||||||
(k, v) = tuple(line.split(':', 1))
|
k, v = line.split(':', 1)
|
||||||
qvars[k] = v
|
qvars[k] = v
|
||||||
# Qt on macOS uses a framework, but Qt for iOS/tvOS does not
|
# Qt on macOS uses a framework, but Qt for iOS/tvOS does not
|
||||||
xspec = qvars.get('QMAKE_XSPEC', '')
|
xspec = qvars.get('QMAKE_XSPEC', '')
|
||||||
if self.env.machines.host.is_darwin() and not any(s in xspec for s in ['ios', 'tvos']):
|
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")
|
mlog.debug("Building for macOS, looking for framework")
|
||||||
self._framework_detect(qvars, mods, kwargs)
|
self._framework_detect(qvars, self.requested_modules, kwargs)
|
||||||
# Sometimes Qt is built not as a framework (for instance, when using conan pkg manager)
|
# Sometimes Qt is built not as a framework (for instance, when using conan pkg manager)
|
||||||
# skip and fall back to normal procedure then
|
# skip and fall back to normal procedure then
|
||||||
if self.is_found:
|
if self.is_found:
|
||||||
return self.qmake.name
|
return
|
||||||
else:
|
else:
|
||||||
mlog.debug("Building for macOS, couldn't find framework, falling back to library search")
|
mlog.debug("Building for macOS, couldn't find framework, falling back to library search")
|
||||||
incdir = qvars['QT_INSTALL_HEADERS']
|
incdir = qvars['QT_INSTALL_HEADERS']
|
||||||
self.compile_args.append('-I' + incdir)
|
self.compile_args.append('-I' + incdir)
|
||||||
libdir = qvars['QT_INSTALL_LIBS']
|
libdir = qvars['QT_INSTALL_LIBS']
|
||||||
# Used by self.compilers_detect()
|
# Used by qt.compilers_detect()
|
||||||
self.bindir = get_qmake_host_bins(qvars)
|
self.bindir = get_qmake_host_bins(qvars)
|
||||||
self.is_found = True
|
|
||||||
|
|
||||||
# Use the buildtype by default, but look at the b_vscrt option if the
|
# Use the buildtype by default, but look at the b_vscrt option if the
|
||||||
# compiler supports it.
|
# compiler supports it.
|
||||||
|
@ -282,7 +286,7 @@ class QtBaseDependency(ExternalDependency, metaclass=abc.ABCMeta):
|
||||||
is_debug = True
|
is_debug = True
|
||||||
modules_lib_suffix = _get_modules_lib_suffix(self.version, self.env.machines[self.for_machine], is_debug)
|
modules_lib_suffix = _get_modules_lib_suffix(self.version, self.env.machines[self.for_machine], is_debug)
|
||||||
|
|
||||||
for module in mods:
|
for module in self.requested_modules:
|
||||||
mincdir = os.path.join(incdir, 'Qt' + module)
|
mincdir = os.path.join(incdir, 'Qt' + module)
|
||||||
self.compile_args.append('-I' + mincdir)
|
self.compile_args.append('-I' + mincdir)
|
||||||
|
|
||||||
|
@ -292,7 +296,7 @@ class QtBaseDependency(ExternalDependency, metaclass=abc.ABCMeta):
|
||||||
define_base = 'TESTLIB'
|
define_base = 'TESTLIB'
|
||||||
else:
|
else:
|
||||||
define_base = module.upper()
|
define_base = module.upper()
|
||||||
self.compile_args.append('-DQT_%s_LIB' % define_base)
|
self.compile_args.append(f'-DQT_{define_base}_LIB')
|
||||||
|
|
||||||
if self.private_headers:
|
if self.private_headers:
|
||||||
priv_inc = self.get_private_includes(mincdir, module)
|
priv_inc = self.get_private_includes(mincdir, module)
|
||||||
|
@ -315,16 +319,15 @@ class QtBaseDependency(ExternalDependency, metaclass=abc.ABCMeta):
|
||||||
if not self._link_with_qtmain(is_debug, libdir):
|
if not self._link_with_qtmain(is_debug, libdir):
|
||||||
self.is_found = False
|
self.is_found = False
|
||||||
|
|
||||||
return self.qmake.name
|
def _sanitize_version(self, version: str) -> str:
|
||||||
|
m = re.search(rf'({self.qtver}(\.\d+)+)', version)
|
||||||
|
if m:
|
||||||
|
return m.group(0).rstrip('.')
|
||||||
|
return version
|
||||||
|
|
||||||
def _link_with_qtmain(self, is_debug: bool, libdir: T.Union[str, T.List[str]]) -> bool:
|
@abc.abstractmethod
|
||||||
libdir = mesonlib.listify(libdir) # TODO: shouldn't be necessary
|
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
|
||||||
base_name = 'qtmaind' if is_debug else 'qtmain'
|
pass
|
||||||
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: T.Dict[str, str], modules: T.List[str], kwargs: T.Dict[str, T.Any]) -> None:
|
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']
|
libdir = qvars['QT_INSTALL_LIBS']
|
||||||
|
@ -344,43 +347,36 @@ class QtBaseDependency(ExternalDependency, metaclass=abc.ABCMeta):
|
||||||
qt_version=self.version)
|
qt_version=self.version)
|
||||||
self.link_args += fwdep.get_link_args()
|
self.link_args += fwdep.get_link_args()
|
||||||
else:
|
else:
|
||||||
|
self.is_found = False
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
self.is_found = True
|
self.is_found = True
|
||||||
# Used by self.compilers_detect()
|
# Used by self.compilers_detect()
|
||||||
self.bindir = get_qmake_host_bins(qvars)
|
self.bindir = get_qmake_host_bins(qvars)
|
||||||
|
|
||||||
@staticmethod
|
def log_info(self) -> str:
|
||||||
def get_methods() -> T.List[DependencyMethods]:
|
return 'qmake'
|
||||||
return [DependencyMethods.PKGCONFIG, DependencyMethods.QMAKE]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_exe_args(compiler: 'Compiler') -> T.List[str]:
|
class Qt4ConfigToolDependency(QmakeQtDependency):
|
||||||
# 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: str, module: str) -> T.List[str]:
|
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def log_details(self) -> str:
|
|
||||||
module_str = ', '.join(self.requested_modules)
|
|
||||||
return 'modules: ' + module_str
|
|
||||||
|
|
||||||
def log_info(self) -> str:
|
class Qt5ConfigToolDependency(QmakeQtDependency):
|
||||||
return f'{self.from_text}'
|
|
||||||
|
|
||||||
def log_tried(self) -> str:
|
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
|
||||||
return self.from_text
|
return _qt_get_private_includes(mod_inc_dir, module, self.version)
|
||||||
|
|
||||||
|
|
||||||
class Qt4Dependency(QtBaseDependency):
|
class Qt6ConfigToolDependency(QmakeQtDependency):
|
||||||
def __init__(self, env: 'Environment', kwargs: T.Dict[str, T.Any]):
|
|
||||||
QtBaseDependency.__init__(self, 'qt4', env, kwargs)
|
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
|
@staticmethod
|
||||||
def get_pkgconfig_host_bins(core: PkgConfigDependency) -> T.Optional[str]:
|
def get_pkgconfig_host_bins(core: PkgConfigDependency) -> T.Optional[str]:
|
||||||
|
@ -396,10 +392,11 @@ class Qt4Dependency(QtBaseDependency):
|
||||||
pass
|
pass
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
|
||||||
|
return []
|
||||||
|
|
||||||
class Qt5Dependency(QtBaseDependency):
|
|
||||||
def __init__(self, env: 'Environment', kwargs: T.Dict[str, T.Any]):
|
class Qt5PkgConfigDependency(QtPkgConfigDependency):
|
||||||
QtBaseDependency.__init__(self, 'qt5', env, kwargs)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_pkgconfig_host_bins(core: PkgConfigDependency) -> str:
|
def get_pkgconfig_host_bins(core: PkgConfigDependency) -> str:
|
||||||
|
@ -409,9 +406,7 @@ class Qt5Dependency(QtBaseDependency):
|
||||||
return _qt_get_private_includes(mod_inc_dir, module, self.version)
|
return _qt_get_private_includes(mod_inc_dir, module, self.version)
|
||||||
|
|
||||||
|
|
||||||
class Qt6Dependency(QtBaseDependency):
|
class Qt6PkgConfigDependency(QtPkgConfigDependency):
|
||||||
def __init__(self, env: 'Environment', kwargs: T.Dict[str, T.Any]):
|
|
||||||
QtBaseDependency.__init__(self, 'qt6', env, kwargs)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_pkgconfig_host_bins(core: PkgConfigDependency) -> str:
|
def get_pkgconfig_host_bins(core: PkgConfigDependency) -> str:
|
||||||
|
@ -419,3 +414,25 @@ class Qt6Dependency(QtBaseDependency):
|
||||||
|
|
||||||
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
|
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)
|
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,
|
||||||
|
)
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
# 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
|
import typing as T
|
||||||
|
@ -20,7 +21,7 @@ from .. import mlog
|
||||||
from .. import build
|
from .. import build
|
||||||
from .. import mesonlib
|
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
|
||||||
|
@ -28,16 +29,11 @@ from ..interpreter import extract_required_kwarg
|
||||||
from ..programs import NonExistingExternalProgram
|
from ..programs import NonExistingExternalProgram
|
||||||
|
|
||||||
if T.TYPE_CHECKING:
|
if T.TYPE_CHECKING:
|
||||||
from .. import Interpreter
|
from ..interpreter import Interpreter
|
||||||
from ..dependencies.qt import QtBaseDependency
|
from ..dependencies.qt import QtBaseDependency
|
||||||
|
from ..environment import Environment
|
||||||
from ..programs import ExternalProgram
|
from ..programs import ExternalProgram
|
||||||
|
|
||||||
_QT_DEPS_LUT = {
|
|
||||||
4: Qt4Dependency,
|
|
||||||
5: Qt5Dependency,
|
|
||||||
6: Qt6Dependency,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class QtBaseModule(ExtensionModule):
|
class QtBaseModule(ExtensionModule):
|
||||||
tools_detected = False
|
tools_detected = False
|
||||||
|
@ -57,7 +53,7 @@ class QtBaseModule(ExtensionModule):
|
||||||
# It is important that this list does not change order as the order of
|
# It is important that this list does not change order as the order of
|
||||||
# the returned ExternalPrograms will change as well
|
# the returned ExternalPrograms will change as well
|
||||||
bins = ['moc', 'uic', 'rcc', 'lrelease']
|
bins = ['moc', 'uic', 'rcc', 'lrelease']
|
||||||
found = {b: NonExistingExternalProgram(name=f'{b}-{qt_dep.name}')
|
found = {b: NonExistingExternalProgram(name=f'{b}-qt{qt_dep.qtver}')
|
||||||
for b in bins}
|
for b in bins}
|
||||||
wanted = f'== {qt_dep.version}'
|
wanted = f'== {qt_dep.version}'
|
||||||
|
|
||||||
|
@ -67,7 +63,7 @@ class QtBaseModule(ExtensionModule):
|
||||||
yield os.path.join(qt_dep.bindir, b), b
|
yield os.path.join(qt_dep.bindir, b), b
|
||||||
# prefer the <tool>-qt<version> of the tool to the plain one, as we
|
# 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.
|
# don't know what the unsuffixed one points to without calling it.
|
||||||
yield f'{b}-{qt_dep.name}', b
|
yield f'{b}-qt{qt_dep.qtver}', b
|
||||||
yield b, b
|
yield b, b
|
||||||
|
|
||||||
for b, name in gen_bins():
|
for b, name in gen_bins():
|
||||||
|
@ -97,13 +93,13 @@ class QtBaseModule(ExtensionModule):
|
||||||
if p.found():
|
if p.found():
|
||||||
setattr(self, name, p)
|
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.compilers_detect(qt)
|
self.compilers_detect(qt)
|
||||||
|
@ -247,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:
|
||||||
|
|
|
@ -6604,10 +6604,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):
|
||||||
|
@ -6638,7 +6638,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):
|
||||||
'''
|
'''
|
||||||
|
@ -6658,7 +6658,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…
Reference in New Issue