diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2021-06-16 20:11:46 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-16 20:11:46 +0300 |
commit | 6fb2f86379c224e99652748eea94a03321b9bd11 (patch) | |
tree | a96fb2b8f6468d793b64b172abc67b8224e0edc3 /mesonbuild/modules/qt.py | |
parent | 537adce5d803ff4ae373d87671190a4a2682ff54 (diff) | |
parent | 0f5e55a749f0fed6330b216a82de941de3ccf9d6 (diff) | |
download | meson-6fb2f86379c224e99652748eea94a03321b9bd11.zip meson-6fb2f86379c224e99652748eea94a03321b9bd11.tar.gz meson-6fb2f86379c224e99652748eea94a03321b9bd11.tar.bz2 |
Merge pull request #8822 from dcbaker/submit/annotate-and-check-qt-module
Rewrite the Qt module for type safety!
Diffstat (limited to 'mesonbuild/modules/qt.py')
-rw-r--r-- | mesonbuild/modules/qt.py | 436 |
1 files changed, 311 insertions, 125 deletions
diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py index aecfe50..7d752db 100644 --- a/mesonbuild/modules/qt.py +++ b/mesonbuild/modules/qt.py @@ -1,4 +1,5 @@ # Copyright 2015 The Meson development team +# Copyright © 2021 Intel Corporation # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,34 +13,95 @@ # See the License for the specific language governing permissions and # limitations under the License. -from mesonbuild.dependencies import find_external_dependency import os import shutil import typing as T +import xml.etree.ElementTree as ET -from .. import mlog +from . import ModuleReturnValue, ExtensionModule from .. import build from .. import mesonlib -from ..mesonlib import MesonException, extract_as_list, File, unholder, version_compare -from ..dependencies import Dependency -import xml.etree.ElementTree as ET -from . import ModuleReturnValue, ExtensionModule -from ..interpreterbase import noPosargs, permittedKwargs, FeatureNew, FeatureNewKwargs +from .. import mlog +from ..dependencies import find_external_dependency from ..interpreter import extract_required_kwarg +from ..interpreter.interpreterobjects import DependencyHolder, ExternalLibraryHolder, IncludeDirsHolder, FeatureOptionHolder, GeneratedListHolder +from ..interpreterbase import ContainerTypeInfo, FeatureDeprecated, KwargInfo, noPosargs, FeatureNew, typed_kwargs +from ..mesonlib import MesonException, File from ..programs import NonExistingExternalProgram if T.TYPE_CHECKING: + from . import ModuleState + from ..dependencies.qt import QtPkgConfigDependency, QmakeQtDependency from ..interpreter import Interpreter - from ..dependencies.qt import QtBaseDependency - from ..environment import Environment + from ..interpreter import kwargs from ..programs import ExternalProgram + QtDependencyType = T.Union[QtPkgConfigDependency, QmakeQtDependency] + + from typing_extensions import TypedDict + + class ResourceCompilerKwArgs(TypedDict): + + """Keyword arguments for the Resource Compiler method.""" + + name: T.Optional[str] + sources: T.List[mesonlib.FileOrString] + extra_args: T.List[str] + method: str + + class UICompilerKwArgs(TypedDict): + + """Keyword arguments for the Ui Compiler method.""" + + sources: T.List[mesonlib.FileOrString] + extra_args: T.List[str] + method: str + + class MocCompilerKwArgs(TypedDict): + + """Keyword arguments for the Moc Compiler method.""" + + sources: T.List[mesonlib.FileOrString] + headers: T.List[mesonlib.FileOrString] + extra_args: T.List[str] + method: str + include_directories: T.List[IncludeDirsHolder] + dependencies: T.List[T.Union[DependencyHolder, ExternalLibraryHolder]] + + class PreprocessKwArgs(TypedDict): + + sources: T.List[mesonlib.FileOrString] + moc_sources: T.List[mesonlib.FileOrString] + moc_headers: T.List[mesonlib.FileOrString] + qresources: T.List[mesonlib.FileOrString] + ui_files: T.List[mesonlib.FileOrString] + moc_extra_arguments: T.List[str] + rcc_extra_arguments: T.List[str] + uic_extra_arguments: T.List[str] + include_directories: T.List[IncludeDirsHolder] + dependencies: T.List[T.Union[DependencyHolder, ExternalLibraryHolder]] + method: str + + class HasToolKwArgs(kwargs.ExtractRequired): + + method: str + + class CompileTranslationsKwArgs(TypedDict): + + build_by_default: bool + install: bool + install_dir: T.Optional[str] + method: str + qresource: T.Optional[str] + rcc_extra_arguments: T.List[str] + ts_files: T.List[str] + class QtBaseModule(ExtensionModule): - tools_detected = False - rcc_supports_depfiles = False + _tools_detected = False + _rcc_supports_depfiles = False - def __init__(self, interpreter: 'Interpreter', qt_version=5): + def __init__(self, interpreter: 'Interpreter', qt_version: int = 5): ExtensionModule.__init__(self, interpreter) self.qt_version = qt_version self.moc: 'ExternalProgram' = NonExistingExternalProgram('moc') @@ -50,9 +112,12 @@ class QtBaseModule(ExtensionModule): 'has_tools': self.has_tools, 'preprocess': self.preprocess, 'compile_translations': self.compile_translations, + 'compile_resources': self.compile_resources, + 'compile_ui': self.compile_ui, + 'compile_moc': self.compile_moc, }) - def compilers_detect(self, state, qt_dep: 'QtBaseDependency') -> None: + def compilers_detect(self, state: 'ModuleState', qt_dep: 'QtDependencyType') -> 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 @@ -96,18 +161,19 @@ class QtBaseModule(ExtensionModule): if p.found(): setattr(self, name, p) - def _detect_tools(self, state, method, required=True): - if self.tools_detected: + def _detect_tools(self, state: 'ModuleState', method: str, required: bool = True) -> None: + if self._tools_detected: return - self.tools_detected = True + self._tools_detected = True mlog.log(f'Detecting Qt{self.qt_version} tools') kwargs = {'required': required, 'modules': 'Core', 'method': method} - qt = find_external_dependency(f'qt{self.qt_version}', state.environment, kwargs) + # Just pick one to make mypy happy + qt = T.cast('QtPkgConfigDependency', find_external_dependency(f'qt{self.qt_version}', state.environment, kwargs)) if qt.found(): # Get all tools and then make sure that they are the right version self.compilers_detect(state, qt) - if version_compare(qt.version, '>=5.14.0'): - self.rcc_supports_depfiles = True + if mesonlib.version_compare(qt.version, '>=5.14.0'): + self._rcc_supports_depfiles = True else: mlog.warning('rcc dependencies will not work properly until you move to Qt >= 5.14:', mlog.bold('https://bugreports.qt.io/browse/QTBUG-45460'), fatal=False) @@ -118,21 +184,24 @@ class QtBaseModule(ExtensionModule): self.rcc = NonExistingExternalProgram(name='rcc' + suffix) self.lrelease = NonExistingExternalProgram(name='lrelease' + suffix) - def qrc_nodes(self, state, rcc_file): - if type(rcc_file) is str: + @staticmethod + def _qrc_nodes(state: 'ModuleState', rcc_file: 'mesonlib.FileOrString') -> T.Tuple[str, T.List[str]]: + abspath: str + if isinstance(rcc_file, str): abspath = os.path.join(state.environment.source_dir, state.subdir, rcc_file) rcc_dirname = os.path.dirname(abspath) - elif type(rcc_file) is File: + else: abspath = rcc_file.absolute_path(state.environment.source_dir, state.environment.build_dir) rcc_dirname = os.path.dirname(abspath) + # FIXME: what error are we actually tring to check here? try: tree = ET.parse(abspath) root = tree.getroot() - result = [] + result: T.List[str] = [] for child in root[0]: if child.tag != 'file': - mlog.warning("malformed rcc file: ", os.path.join(state.subdir, rcc_file)) + mlog.warning("malformed rcc file: ", os.path.join(state.subdir, str(rcc_file))) break else: result.append(child.text) @@ -141,9 +210,9 @@ class QtBaseModule(ExtensionModule): except Exception: raise MesonException(f'Unable to parse resource file {abspath}') - def parse_qrc_deps(self, state, rcc_file): - rcc_dirname, nodes = self.qrc_nodes(state, rcc_file) - result = [] + def _parse_qrc_deps(self, state: 'ModuleState', rcc_file: 'mesonlib.FileOrString') -> T.List[File]: + rcc_dirname, nodes = self._qrc_nodes(state, rcc_file) + result: T.List[File] = [] for resource_path in nodes: # We need to guess if the pointed resource is: # a) in build directory -> implies a generated file @@ -170,11 +239,18 @@ class QtBaseModule(ExtensionModule): result.append(File(is_built=False, subdir=state.subdir, fname=path_from_rcc)) return result - @noPosargs - @permittedKwargs({'method', 'required'}) @FeatureNew('qt.has_tools', '0.54.0') - def has_tools(self, state, args, kwargs): + @noPosargs + @typed_kwargs( + 'qt.has_tools', + KwargInfo('required', (bool, FeatureOptionHolder), default=False), + KwargInfo('method', str, default='auto'), + ) + def has_tools(self, state: 'ModuleState', args: T.Tuple, kwargs: 'HasToolKwArgs') -> bool: method = kwargs.get('method', 'auto') + # We have to cast here because TypedDicts are invariant, even though + # ExtractRequiredKwArgs is a subset of HasToolKwArgs, type checkers + # will insist this is wrong disabled, required, feature = extract_required_kwarg(kwargs, state.subproject, default=False) if disabled: mlog.log('qt.has_tools skipped: feature', mlog.bold(feature), 'disabled') @@ -187,117 +263,227 @@ class QtBaseModule(ExtensionModule): return False return True - @FeatureNewKwargs('qt.preprocess', '0.49.0', ['uic_extra_arguments']) - @FeatureNewKwargs('qt.preprocess', '0.44.0', ['moc_extra_arguments']) - @FeatureNewKwargs('qt.preprocess', '0.49.0', ['rcc_extra_arguments']) - @permittedKwargs({'moc_headers', 'moc_sources', 'uic_extra_arguments', 'moc_extra_arguments', 'rcc_extra_arguments', 'include_directories', 'dependencies', 'ui_files', 'qresources', 'method'}) - def preprocess(self, state, args, kwargs): - rcc_files, ui_files, moc_headers, moc_sources, uic_extra_arguments, moc_extra_arguments, rcc_extra_arguments, sources, include_directories, dependencies \ - = [extract_as_list(kwargs, c, pop=True) for c in ['qresources', 'ui_files', 'moc_headers', 'moc_sources', 'uic_extra_arguments', 'moc_extra_arguments', 'rcc_extra_arguments', 'sources', 'include_directories', 'dependencies']] - sources += args[1:] - method = kwargs.get('method', 'auto') - self._detect_tools(state, method) - err_msg = "{0} sources specified and couldn't find {1}, " \ - "please check your qt{2} installation" - if (moc_headers or moc_sources) and not self.moc.found(): - raise MesonException(err_msg.format('MOC', f'moc-qt{self.qt_version}', self.qt_version)) - if rcc_files: - if not self.rcc.found(): - raise MesonException(err_msg.format('RCC', f'rcc-qt{self.qt_version}', self.qt_version)) + @FeatureNew('qt.compile_resources', '0.59.0') + @noPosargs + @typed_kwargs( + 'qt.compile_resources', + KwargInfo('name', str), + KwargInfo('sources', ContainerTypeInfo(list, (File, str), allow_empty=False), listify=True, required=True), + KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]), + KwargInfo('method', str, default='auto') + ) + def compile_resources(self, state: 'ModuleState', args: T.Tuple, kwargs: 'ResourceCompilerKwArgs') -> ModuleReturnValue: + """Compile Qt resources files. + + Uses CustomTargets to generate .cpp files from .qrc files. + """ + self._detect_tools(state, kwargs['method']) + if not self.rcc.found(): + err_msg = ("{0} sources specified and couldn't find {1}, " + "please check your qt{2} installation") + raise MesonException(err_msg.format('RCC', f'rcc-qt{self.qt_version}', self.qt_version)) + + # List of generated CustomTargets + targets: T.List[build.CustomTarget] = [] + + # depfile arguments + DEPFILE_ARGS: T.List[str] = ['--depfile', '@DEPFILE@'] if self._rcc_supports_depfiles else [] + + name = kwargs['name'] + sources = kwargs['sources'] + extra_args = kwargs['extra_args'] + + # If a name was set generate a single .cpp file from all of the qrc + # files, otherwise generate one .cpp file per qrc file. + if name: + qrc_deps: T.List[File] = [] + for s in sources: + qrc_deps.extend(self._parse_qrc_deps(state, s)) + + rcc_kwargs: T.Dict[str, T.Any] = { # TODO: if CustomTarget had typing information we could use that here... + 'input': sources, + 'output': name + '.cpp', + 'command': self.rcc.get_command() + ['-name', name, '-o', '@OUTPUT@'] + extra_args + ['@INPUT@'] + DEPFILE_ARGS, + 'depend_files': qrc_deps, + 'depfile': f'{name}.d', + } + res_target = build.CustomTarget(name, state.subdir, state.subproject, rcc_kwargs) + targets.append(res_target) + else: + for rcc_file in sources: + qrc_deps = self._parse_qrc_deps(state, rcc_file) + if isinstance(rcc_file, str): + basename = os.path.basename(rcc_file) + else: + basename = os.path.basename(rcc_file.fname) + name = f'qt{self.qt_version}-{basename.replace(".", "_")}' + rcc_kwargs = { + 'input': rcc_file, + 'output': f'{name}.cpp', + 'command': self.rcc.get_command() + ['-name', '@BASENAME@', '-o', '@OUTPUT@'] + extra_args + ['@INPUT@'] + DEPFILE_ARGS, + 'depend_files': qrc_deps, + 'depfile': f'{name}.d', + } + res_target = build.CustomTarget(name, state.subdir, state.subproject, rcc_kwargs) + targets.append(res_target) + + return ModuleReturnValue(targets, [targets]) + + @FeatureNew('qt.compile_ui', '0.59.0') + @noPosargs + @typed_kwargs( + 'qt.compile_ui', + KwargInfo('sources', ContainerTypeInfo(list, (File, str), allow_empty=False), listify=True, required=True), + KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]), + KwargInfo('method', str, default='auto') + ) + def compile_ui(self, state: 'ModuleState', args: T.Tuple, kwargs: 'ResourceCompilerKwArgs') -> ModuleReturnValue: + """Compile UI resources into cpp headers.""" + self._detect_tools(state, kwargs['method']) + if not self.uic.found(): + err_msg = ("{0} sources specified and couldn't find {1}, " + "please check your qt{2} installation") + raise MesonException(err_msg.format('UIC', f'uic-qt{self.qt_version}', self.qt_version)) + + # TODO: This generator isn't added to the generator list in the Interpreter + gen = build.Generator( + self.uic, + kwargs['extra_args'] + ['-o', '@OUTPUT@', '@INPUT@'], + ['ui_@BASENAME@.h'], + name=f'Qt{self.qt_version} ui') + out = GeneratedListHolder(gen.process_files(kwargs['sources'], state)) + return ModuleReturnValue(out, [out]) + + @FeatureNew('qt.compile_moc', '0.59.0') + @noPosargs + @typed_kwargs( + 'qt.compile_moc', + KwargInfo('sources', ContainerTypeInfo(list, (File, str)), listify=True, default=[]), + KwargInfo('headers', ContainerTypeInfo(list, (File, str)), listify=True, default=[]), + KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]), + KwargInfo('method', str, default='auto'), + KwargInfo('include_directories', ContainerTypeInfo(list, IncludeDirsHolder), listify=True, default=[]), + KwargInfo('dependencies', ContainerTypeInfo(list, (DependencyHolder, ExternalLibraryHolder)), listify=True, default=[]), + ) + def compile_moc(self, state: 'ModuleState', args: T.Tuple, kwargs: 'MocCompilerKwArgs') -> ModuleReturnValue: + self._detect_tools(state, kwargs['method']) + if not self.moc.found(): + err_msg = ("{0} sources specified and couldn't find {1}, " + "please check your qt{2} installation") + raise MesonException(err_msg.format('MOC', f'uic-qt{self.qt_version}', self.qt_version)) + + if not (kwargs['headers'] or kwargs['sources']): + raise build.InvalidArguments('At least one of the "headers" or "sources" keyword arguments must be provied and not empty') + + inc = state.get_include_args(include_dirs=kwargs['include_directories']) + compile_args: T.List[str] = [] + for dep in kwargs['dependencies']: + compile_args.extend([a for a in dep.held_object.get_all_compile_args() if a.startswith(('-I', '-D'))]) + + output: T.List[build.GeneratedList] = [] + + arguments = kwargs['extra_args'] + inc + compile_args + ['@INPUT@', '-o', '@OUTPUT@'] + if kwargs['headers']: + moc_gen = build.Generator( + self.moc, arguments, ['moc_@BASENAME@.cpp'], + name=f'Qt{self.qt_version} moc header') + output.append(moc_gen.process_files(kwargs['headers'], state)) + if kwargs['sources']: + moc_gen = build.Generator( + self.moc, arguments, ['@BASENAME@.moc'], + name=f'Qt{self.qt_version} moc source') + output.append(moc_gen.process_files(kwargs['sources'], state)) + + return ModuleReturnValue(output, [output]) + + # We can't use typed_pos_args here, the signature is ambiguious + @typed_kwargs( + 'qt.preprocess', + KwargInfo('sources', ContainerTypeInfo(list, (File, str)), listify=True, default=[], deprecated='0.59.0'), + KwargInfo('qresources', ContainerTypeInfo(list, (File, str)), listify=True, default=[]), + KwargInfo('ui_files', ContainerTypeInfo(list, (File, str)), listify=True, default=[]), + KwargInfo('moc_sources', ContainerTypeInfo(list, (File, str)), listify=True, default=[]), + KwargInfo('moc_headers', ContainerTypeInfo(list, (File, str)), listify=True, default=[]), + KwargInfo('moc_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.44.0'), + KwargInfo('rcc_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.49.0'), + KwargInfo('uic_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.49.0'), + KwargInfo('method', str, default='auto'), + KwargInfo('include_directories', ContainerTypeInfo(list, IncludeDirsHolder), listify=True, default=[]), + KwargInfo('dependencies', ContainerTypeInfo(list, (DependencyHolder, ExternalLibraryHolder)), listify=True, default=[]), + ) + def preprocess(self, state: 'ModuleState', args: T.List[T.Union[str, File]], kwargs: 'PreprocessKwArgs') -> ModuleReturnValue: + _sources = args[1:] + if _sources: + FeatureDeprecated.single_use('qt.preprocess positional sources', '0.59', state.subproject) + sources = _sources + kwargs['sources'] + for s in sources: + if not isinstance(s, (str, File)): + raise build.InvalidArguments('Variadic arguments to qt.preprocess must be Strings or Files') + method = kwargs['method'] + + if kwargs['qresources']: # custom output name set? -> one output file, multiple otherwise + rcc_kwargs: 'ResourceCompilerKwArgs' = {'name': '', 'sources': kwargs['qresources'], 'extra_args': kwargs['rcc_extra_arguments'], 'method': method} if args: - qrc_deps = [] - for i in rcc_files: - qrc_deps += self.parse_qrc_deps(state, i) - name = args[0] - rcc_kwargs = {'input': rcc_files, - 'output': name + '.cpp', - 'command': [self.rcc, '-name', name, '-o', '@OUTPUT@', rcc_extra_arguments, '@INPUT@'], - 'depend_files': qrc_deps} - res_target = build.CustomTarget(name, state.subdir, state.subproject, rcc_kwargs) - sources.append(res_target) - else: - for rcc_file in rcc_files: - qrc_deps = self.parse_qrc_deps(state, rcc_file) - if type(rcc_file) is str: - basename = os.path.basename(rcc_file) - elif type(rcc_file) is File: - basename = os.path.basename(rcc_file.fname) - name = 'qt' + str(self.qt_version) + '-' + basename.replace('.', '_') - rcc_kwargs = {'input': rcc_file, - 'output': name + '.cpp', - 'command': [self.rcc, '-name', '@BASENAME@', '-o', '@OUTPUT@', rcc_extra_arguments, '@INPUT@'], - 'depend_files': qrc_deps} - if self.rcc_supports_depfiles: - rcc_kwargs['depfile'] = name + '.d' - rcc_kwargs['command'] += ['--depfile', '@DEPFILE@'] - res_target = build.CustomTarget(name, state.subdir, state.subproject, rcc_kwargs) - sources.append(res_target) - if ui_files: - if not self.uic.found(): - raise MesonException(err_msg.format('UIC', f'uic-qt{self.qt_version}', self.qt_version)) - arguments = uic_extra_arguments + ['-o', '@OUTPUT@', '@INPUT@'] - ui_kwargs = {'output': 'ui_@BASENAME@.h', - 'arguments': arguments} - ui_gen = build.Generator([self.uic], ui_kwargs) - ui_output = ui_gen.process_files(f'Qt{self.qt_version} ui', ui_files, state) - sources.append(ui_output) - inc = state.get_include_args(include_dirs=include_directories) - compile_args = [] - for dep in unholder(dependencies): - if isinstance(dep, Dependency): - for arg in dep.get_all_compile_args(): - if arg.startswith('-I') or arg.startswith('-D'): - compile_args.append(arg) - else: - raise MesonException('Argument is of an unacceptable type {!r}.\nMust be ' - 'either an external dependency (returned by find_library() or ' - 'dependency()) or an internal dependency (returned by ' - 'declare_dependency()).'.format(type(dep).__name__)) - if moc_headers: - arguments = moc_extra_arguments + inc + compile_args + ['@INPUT@', '-o', '@OUTPUT@'] - moc_kwargs = {'output': 'moc_@BASENAME@.cpp', - 'arguments': arguments} - moc_gen = build.Generator([self.moc], moc_kwargs) - moc_output = moc_gen.process_files(f'Qt{self.qt_version} moc header', moc_headers, state) - sources.append(moc_output) - if moc_sources: - arguments = moc_extra_arguments + inc + compile_args + ['@INPUT@', '-o', '@OUTPUT@'] - moc_kwargs = {'output': '@BASENAME@.moc', - 'arguments': arguments} - moc_gen = build.Generator([self.moc], moc_kwargs) - moc_output = moc_gen.process_files(f'Qt{self.qt_version} moc source', moc_sources, state) - sources.append(moc_output) - return ModuleReturnValue(sources, sources) + if not isinstance(args[0], str): + raise build.InvalidArguments('First argument to qt.preprocess must be a string') + rcc_kwargs['name'] = args[0] + sources.extend(self.compile_resources(state, tuple(), rcc_kwargs).return_value) + + if kwargs['ui_files']: + ui_kwargs: 'UICompilerKwArgs' = {'sources': kwargs['ui_files'], 'extra_args': kwargs['uic_extra_arguments'], 'method': method} + sources.extend(self.compile_ui(state, tuple(), ui_kwargs).return_value) + + if kwargs['moc_headers'] or kwargs['moc_sources']: + moc_kwargs: 'MocCompilerKwArgs' = { + 'extra_args': kwargs['moc_extra_arguments'], + 'sources': kwargs['moc_sources'], + 'headers': kwargs['moc_headers'], + 'include_directories': kwargs['include_directories'], + 'dependencies': kwargs['dependencies'], + 'method': method, + } + sources.extend(self.compile_moc(state, tuple(), moc_kwargs).return_value) + + return ModuleReturnValue(sources, [sources]) @FeatureNew('qt.compile_translations', '0.44.0') - @FeatureNewKwargs('qt.compile_translations', '0.56.0', ['qresource']) - @FeatureNewKwargs('qt.compile_translations', '0.56.0', ['rcc_extra_arguments']) - @permittedKwargs({'ts_files', 'qresource', 'rcc_extra_arguments', 'install', 'install_dir', 'build_by_default', 'method'}) - def compile_translations(self, state, args, kwargs): - ts_files, install_dir = [extract_as_list(kwargs, c, pop=True) for c in ['ts_files', 'install_dir']] - qresource = kwargs.get('qresource') + @noPosargs + @typed_kwargs( + 'qt.compile_translations', + KwargInfo('build_by_default', bool, default=False), + KwargInfo('install', bool, default=False), + KwargInfo('install_dir', str), + KwargInfo('method', str, default='auto'), + KwargInfo('qresource', str, since='0.56.0'), + KwargInfo('rcc_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.56.0'), + KwargInfo('ts_files', ContainerTypeInfo(list, (str, File)), listify=True, default=[]), + ) + def compile_translations(self, state: 'ModuleState', args: T.Tuple, kwargs: 'CompileTranslationsKwArgs') -> ModuleReturnValue: + ts_files = kwargs['ts_files'] + install_dir = kwargs['install_dir'] + qresource = kwargs['qresource'] if qresource: if ts_files: raise MesonException('qt.compile_translations: Cannot specify both ts_files and qresource') if os.path.dirname(qresource) != '': raise MesonException('qt.compile_translations: qresource file name must not contain a subdirectory.') - qresource = File.from_built_file(state.subdir, qresource) - infile_abs = os.path.join(state.environment.source_dir, qresource.relative_name()) - outfile_abs = os.path.join(state.environment.build_dir, qresource.relative_name()) + qresource_file = File.from_built_file(state.subdir, qresource) + infile_abs = os.path.join(state.environment.source_dir, qresource_file.relative_name()) + outfile_abs = os.path.join(state.environment.build_dir, qresource_file.relative_name()) os.makedirs(os.path.dirname(outfile_abs), exist_ok=True) shutil.copy2(infile_abs, outfile_abs) self.interpreter.add_build_def_file(infile_abs) - rcc_file, nodes = self.qrc_nodes(state, qresource) + _, nodes = self._qrc_nodes(state, qresource_file) for c in nodes: if c.endswith('.qm'): - ts_files.append(c.rstrip('.qm')+'.ts') + ts_files.append(c.rstrip('.qm') + '.ts') else: raise MesonException(f'qt.compile_translations: qresource can only contain qm files, found {c}') - results = self.preprocess(state, [], {'qresources': qresource, 'rcc_extra_arguments': kwargs.get('rcc_extra_arguments', [])}) - self._detect_tools(state, kwargs.get('method', 'auto')) - translations = [] + results = self.preprocess(state, [], {'qresources': qresource, 'rcc_extra_arguments': kwargs['rcc_extra_arguments']}) + self._detect_tools(state, kwargs['method']) + translations: T.List[build.CustomTarget] = [] for ts in ts_files: if not self.lrelease.found(): raise MesonException('qt.compile_translations: ' + @@ -320,4 +506,4 @@ class QtBaseModule(ExtensionModule): if qresource: return ModuleReturnValue(results.return_value[0], [results.new_objects, translations]) else: - return ModuleReturnValue(translations, translations) + return ModuleReturnValue(translations, [translations]) |