Skip to content

Commit 8771045

Browse files
committed
dependencies/qt: Add type annotations
And fix some style and correctness issues
1 parent 73eb244 commit 8771045

File tree

2 files changed

+72
-50
lines changed

2 files changed

+72
-50
lines changed

mesonbuild/dependencies/qt.py

Lines changed: 71 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
"""Dependency finders for the Qt framework."""
1818

19+
import abc
1920
import collections
2021
import re
2122
import os
@@ -30,10 +31,13 @@
3031
from ..programs import NonExistingExternalProgram, find_external_program
3132

3233
if T.TYPE_CHECKING:
34+
from ..compilers import Compiler
35+
from ..environment import Environment
36+
from ..interpreter import Interpreter
3337
from ..programs import ExternalProgram
3438

3539

36-
def _qt_get_private_includes(mod_inc_dir, module, mod_version):
40+
def _qt_get_private_includes(mod_inc_dir: str, module: str, mod_version: str) -> T.List[str]:
3741
# usually Qt5 puts private headers in /QT_INSTALL_HEADERS/module/VERSION/module/private
3842
# except for at least QtWebkit and Enginio where the module version doesn't match Qt version
3943
# as an example with Qt 5.10.1 on linux you would get:
@@ -44,28 +48,26 @@ def _qt_get_private_includes(mod_inc_dir, module, mod_version):
4448
# on Qt4 when available private folder is directly in module folder
4549
# like /usr/include/QtCore/private/
4650
if int(mod_version.split('.')[0]) < 5:
47-
return tuple()
51+
return []
4852

4953
private_dir = os.path.join(mod_inc_dir, mod_version)
5054
# fallback, let's try to find a directory with the latest version
5155
if not os.path.exists(private_dir):
5256
dirs = [filename for filename in os.listdir(mod_inc_dir)
5357
if os.path.isdir(os.path.join(mod_inc_dir, filename))]
54-
dirs.sort(reverse=True)
5558

56-
for dirname in dirs:
59+
for dirname in sorted(dirs, reverse=True):
5760
if len(dirname.split('.')) == 3:
5861
private_dir = dirname
5962
break
60-
return (private_dir,
61-
os.path.join(private_dir, 'Qt' + module))
63+
return [private_dir, os.path.join(private_dir, 'Qt' + module)]
6264

6365
class QtExtraFrameworkDependency(ExtraFrameworkDependency):
64-
def __init__(self, name, env, kwargs, language: T.Optional[str] = None):
66+
def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None):
6567
super().__init__(name, env, kwargs, language=language)
6668
self.mod_name = name[2:]
6769

68-
def get_compile_args(self, with_private_headers=False, qt_version="0"):
70+
def get_compile_args(self, with_private_headers: bool = False, qt_version: str = "0") -> T.List[str]:
6971
if self.found():
7072
mod_inc_dir = os.path.join(self.framework_path, 'Headers')
7173
args = ['-I' + mod_inc_dir]
@@ -74,8 +76,12 @@ def get_compile_args(self, with_private_headers=False, qt_version="0"):
7476
return args
7577
return []
7678

77-
class QtBaseDependency(ExternalDependency):
78-
def __init__(self, name, env, kwargs):
79+
80+
class QtBaseDependency(ExternalDependency, metaclass=abc.ABCMeta):
81+
82+
version: T.Optional[str]
83+
84+
def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]):
7985
super().__init__(name, env, kwargs, language='cpp')
8086
self.qtname = name.capitalize()
8187
self.qtver = name[-1]
@@ -84,20 +90,20 @@ def __init__(self, name, env, kwargs):
8490
else:
8591
self.qtpkgname = self.qtname
8692
self.root = '/usr'
87-
self.bindir = None
88-
self.private_headers = kwargs.get('private_headers', False)
89-
mods = mesonlib.extract_as_list(kwargs, 'modules')
93+
self.bindir: T.Optional[str] = None
94+
self.private_headers = T.cast(bool, kwargs.get('private_headers', False))
95+
mods = mesonlib.stringlistify(mesonlib.extract_as_list(kwargs, 'modules'))
9096
self.requested_modules = mods
9197
if not mods:
9298
raise DependencyException('No ' + self.qtname + ' modules specified.')
9399
self.from_text = 'pkg-config'
94100

95-
self.qtmain = kwargs.get('main', False)
101+
self.qtmain = T.cast(bool, kwargs.get('main', False))
96102
if not isinstance(self.qtmain, bool):
97103
raise DependencyException('"main" argument must be a boolean')
98104

99105
# Keep track of the detection methods used, for logging purposes.
100-
methods = []
106+
methods: T.List[str] = []
101107
# Prefer pkg-config, then fallback to `qmake -query`
102108
if DependencyMethods.PKGCONFIG in self.methods:
103109
mlog.debug('Trying to find qt with pkg-config')
@@ -115,16 +121,20 @@ def __init__(self, name, env, kwargs):
115121
self.from_text = mlog.format_list(methods)
116122
self.version = None
117123

118-
def compilers_detect(self, interp_obj):
119-
"Detect Qt (4 or 5) moc, uic, rcc in the specified bindir or in PATH"
124+
@abc.abstractmethod
125+
def get_pkgconfig_host_bins(self, core: PkgConfigDependency) -> T.Optional[str]:
126+
pass
127+
128+
def compilers_detect(self, interp_obj: 'Interpreter') -> T.Tuple['ExternalProgram', 'ExternalProgram', 'ExternalProgram', 'ExternalProgram']:
129+
"""Detect Qt (4 or 5) moc, uic, rcc in the specified bindir or in PATH"""
120130
# It is important that this list does not change order as the order of
121131
# the returned ExternalPrograms will change as well
122132
bins = ['moc', 'uic', 'rcc', 'lrelease']
123133
found = {b: NonExistingExternalProgram(name=f'{b}-{self.name}')
124134
for b in bins}
125135
wanted = f'== {self.version}'
126136

127-
def gen_bins():
137+
def gen_bins() -> T.Generator[T.Tuple[str, str], None, None]:
128138
for b in bins:
129139
if self.bindir:
130140
yield os.path.join(self.bindir, b), b
@@ -145,7 +155,7 @@ def gen_bins():
145155
arg = ['-v']
146156

147157
# Ensure that the version of qt and each tool are the same
148-
def get_version(p):
158+
def get_version(p: 'ExternalProgram') -> str:
149159
_, out, err = mesonlib.Popen_safe(p.get_command() + arg)
150160
if b.startswith('lrelease') or not self.version.startswith('4'):
151161
care = out
@@ -159,13 +169,18 @@ def get_version(p):
159169
if p.found():
160170
found[name] = p
161171

162-
return tuple([found[b] for b in bins])
172+
# Since we're converting from a list (no size constraints) to a tuple
173+
# (size constrained), we have to cast. We can impsect the code to see
174+
# that obviously this is correct since `len(bins) == 4`, but static
175+
# type checkers can't
176+
return T.cast(T.Tuple['ExternalProgram', 'ExternalProgram', 'ExternalProgram', 'ExternalProgram'],
177+
tuple([found[b] for b in bins]))
163178

164-
def _pkgconfig_detect(self, mods, kwargs):
179+
def _pkgconfig_detect(self, mods: T.List[str], kwargs: T.Dict[str, T.Any]) -> None:
165180
# We set the value of required to False so that we can try the
166181
# qmake-based fallback if pkg-config fails.
167182
kwargs['required'] = False
168-
modules = collections.OrderedDict()
183+
modules: T.MutableMapping[str, PkgConfigDependency] = collections.OrderedDict()
169184
for module in mods:
170185
modules[module] = PkgConfigDependency(self.qtpkgname + module, self.env,
171186
kwargs, language=self.language)
@@ -226,7 +241,7 @@ def search_qmake(self) -> T.Generator['ExternalProgram', None, None]:
226241
for qmake in ('qmake-' + self.name, 'qmake'):
227242
yield from find_external_program(self.env, self.for_machine, qmake, 'QMake', [qmake])
228243

229-
def _qmake_detect(self, mods, kwargs):
244+
def _qmake_detect(self, mods: T.List[str], kwargs: T.Dict[str, T.Any]) -> T.Optional[str]:
230245
for qmake in self.search_qmake():
231246
if not qmake.found():
232247
continue
@@ -243,7 +258,7 @@ def _qmake_detect(self, mods, kwargs):
243258
else:
244259
# Didn't find qmake :(
245260
self.is_found = False
246-
return
261+
return None
247262
self.version = re.search(self.qtver + r'(\.\d+)+', stdo).group(0)
248263
# Query library path, header path, and binary path
249264
mlog.log("Found qmake:", mlog.bold(self.qmake.get_path()), '(%s)' % self.version)
@@ -297,11 +312,11 @@ def _qmake_detect(self, mods, kwargs):
297312
priv_inc = self.get_private_includes(mincdir, module)
298313
for directory in priv_inc:
299314
self.compile_args.append('-I' + directory)
300-
libfile = self.clib_compiler.find_library(self.qtpkgname + module + modules_lib_suffix,
301-
self.env,
302-
libdir)
303-
if libfile:
304-
libfile = libfile[0]
315+
libfiles = self.clib_compiler.find_library(
316+
self.qtpkgname + module + modules_lib_suffix, self.env,
317+
mesonlib.listify(libdir)) # TODO: shouldn't be necissary
318+
if libfiles:
319+
libfile = libfiles[0]
305320
else:
306321
mlog.log("Could not find:", module,
307322
self.qtpkgname + module + modules_lib_suffix,
@@ -316,7 +331,7 @@ def _qmake_detect(self, mods, kwargs):
316331

317332
return self.qmake.name
318333

319-
def _get_modules_lib_suffix(self, is_debug):
334+
def _get_modules_lib_suffix(self, is_debug: bool) -> str:
320335
suffix = ''
321336
if self.env.machines[self.for_machine].is_windows():
322337
if is_debug:
@@ -342,15 +357,16 @@ def _get_modules_lib_suffix(self, is_debug):
342357
'module detection may not work'.format(cpu_family))
343358
return suffix
344359

345-
def _link_with_qtmain(self, is_debug, libdir):
360+
def _link_with_qtmain(self, is_debug: bool, libdir: T.Union[str, T.List[str]]) -> bool:
361+
libdir = mesonlib.listify(libdir) # TODO: shouldn't be necessary
346362
base_name = 'qtmaind' if is_debug else 'qtmain'
347363
qtmain = self.clib_compiler.find_library(base_name, self.env, libdir)
348364
if qtmain:
349365
self.link_args.append(qtmain[0])
350366
return True
351367
return False
352368

353-
def _framework_detect(self, qvars, modules, kwargs):
369+
def _framework_detect(self, qvars: T.Dict[str, str], modules: T.List[str], kwargs: T.Dict[str, T.Any]) -> None:
354370
libdir = qvars['QT_INSTALL_LIBS']
355371

356372
# ExtraFrameworkDependency doesn't support any methods
@@ -374,19 +390,20 @@ def _framework_detect(self, qvars, modules, kwargs):
374390
# Used by self.compilers_detect()
375391
self.bindir = self.get_qmake_host_bins(qvars)
376392

377-
def get_qmake_host_bins(self, qvars):
393+
@staticmethod
394+
def get_qmake_host_bins(qvars: T.Dict[str, str]) -> str:
378395
# Prefer QT_HOST_BINS (qt5, correct for cross and native compiling)
379396
# but fall back to QT_INSTALL_BINS (qt4)
380397
if 'QT_HOST_BINS' in qvars:
381398
return qvars['QT_HOST_BINS']
382-
else:
383-
return qvars['QT_INSTALL_BINS']
399+
return qvars['QT_INSTALL_BINS']
384400

385401
@staticmethod
386-
def get_methods():
402+
def get_methods() -> T.List[DependencyMethods]:
387403
return [DependencyMethods.PKGCONFIG, DependencyMethods.QMAKE]
388404

389-
def get_exe_args(self, compiler):
405+
@staticmethod
406+
def get_exe_args(compiler: 'Compiler') -> T.List[str]:
390407
# Originally this was -fPIE but nowadays the default
391408
# for upstream and distros seems to be -reduce-relocations
392409
# which requires -fPIC. This may cause a performance
@@ -395,25 +412,26 @@ def get_exe_args(self, compiler):
395412
# for you, patches are welcome.
396413
return compiler.get_pic_args()
397414

398-
def get_private_includes(self, mod_inc_dir, module):
399-
return tuple()
415+
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
416+
return []
400417

401-
def log_details(self):
418+
def log_details(self) -> str:
402419
module_str = ', '.join(self.requested_modules)
403420
return 'modules: ' + module_str
404421

405-
def log_info(self):
422+
def log_info(self) -> str:
406423
return f'{self.from_text}'
407424

408-
def log_tried(self):
425+
def log_tried(self) -> str:
409426
return self.from_text
410427

411428

412429
class Qt4Dependency(QtBaseDependency):
413-
def __init__(self, env, kwargs):
430+
def __init__(self, env: 'Environment', kwargs: T.Dict[str, T.Any]):
414431
QtBaseDependency.__init__(self, 'qt4', env, kwargs)
415432

416-
def get_pkgconfig_host_bins(self, core):
433+
@staticmethod
434+
def get_pkgconfig_host_bins(core: PkgConfigDependency) -> T.Optional[str]:
417435
# Only return one bins dir, because the tools are generally all in one
418436
# directory for Qt4, in Qt5, they must all be in one directory. Return
419437
# the first one found among the bin variables, in case one tool is not
@@ -424,25 +442,28 @@ def get_pkgconfig_host_bins(self, core):
424442
return os.path.dirname(core.get_pkgconfig_variable('%s_location' % application, {}))
425443
except mesonlib.MesonException:
426444
pass
445+
return None
427446

428447

429448
class Qt5Dependency(QtBaseDependency):
430-
def __init__(self, env, kwargs):
449+
def __init__(self, env: 'Environment', kwargs: T.Dict[str, T.Any]):
431450
QtBaseDependency.__init__(self, 'qt5', env, kwargs)
432451

433-
def get_pkgconfig_host_bins(self, core):
452+
@staticmethod
453+
def get_pkgconfig_host_bins(core: PkgConfigDependency) -> str:
434454
return core.get_pkgconfig_variable('host_bins', {})
435455

436-
def get_private_includes(self, mod_inc_dir, module):
456+
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
437457
return _qt_get_private_includes(mod_inc_dir, module, self.version)
438458

439459

440460
class Qt6Dependency(QtBaseDependency):
441-
def __init__(self, env, kwargs):
461+
def __init__(self, env: 'Environment', kwargs: T.Dict[str, T.Any]):
442462
QtBaseDependency.__init__(self, 'qt6', env, kwargs)
443463

444-
def get_pkgconfig_host_bins(self, core):
464+
@staticmethod
465+
def get_pkgconfig_host_bins(core: PkgConfigDependency) -> str:
445466
return core.get_pkgconfig_variable('host_bins', {})
446467

447-
def get_private_includes(self, mod_inc_dir, module):
468+
def get_private_includes(self, mod_inc_dir: str, module: str) -> T.List[str]:
448469
return _qt_get_private_includes(mod_inc_dir, module, self.version)

run_mypy.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
'mesonbuild/dependencies/boost.py',
2222
'mesonbuild/dependencies/hdf5.py',
2323
'mesonbuild/dependencies/mpi.py',
24+
'mesonbuild/dependencies/qt.py',
2425
'mesonbuild/envconfig.py',
2526
'mesonbuild/interpreterbase.py',
2627
'mesonbuild/linkers.py',

0 commit comments

Comments
 (0)