blob: b29e6648fa2d13e02bb1f9a234fcb1d53cfa47ae [file]
# SPDX-License-Identifier: Apache-2.0
# Copyright 2015 The Meson development team
# Copyright © 2021-2025 Intel Corporation
from __future__ import annotations
import os
import shutil
import typing as T
import xml.etree.ElementTree as ET
import re
from . import ModuleReturnValue, ExtensionModule
from .. import build
from .. import mlog
from ..dependencies import DependencyMethods, find_external_dependency, Dependency, ExternalLibrary, InternalDependency
from ..mesonlib import MesonException, File, FileMode, version_compare, Popen_safe
from ..interpreter import extract_required_kwarg
from ..interpreter.type_checking import DEPENDENCY_METHOD_KW, INSTALL_DIR_KW, INSTALL_KW, REQUIRED_KW, NoneType
from ..interpreterbase import ContainerTypeInfo, FeatureDeprecated, KwargInfo, noPosargs, FeatureNew, typed_kwargs, typed_pos_args
from ..programs import NonExistingExternalProgram
if T.TYPE_CHECKING:
from . import ModuleState
from ..build import TargetSources
from ..dependencies.qt import QtPkgConfigDependency, QmakeQtDependency
from ..dependencies.base import DependencyObjectKWs
from ..interpreter import Interpreter
from ..interpreter import kwargs
from ..interpreter.interpreter import CustomTargetSources
from ..mesonlib import FileOrString
from ..programs import CommandList, Program
from typing_extensions import Literal
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[str | build.TargetSources]
extra_args: T.List[str]
method: DependencyMethods
class UICompilerKwArgs(TypedDict):
"""Keyword arguments for the Ui Compiler method."""
sources: T.List[str | build.TargetSources]
extra_args: T.List[str]
method: DependencyMethods
preserve_paths: bool
class MocCompilerKwArgs(TypedDict):
"""Keyword arguments for the Moc Compiler method."""
sources: T.List[str | build.TargetSources]
headers: T.List[str | build.TargetSources]
extra_args: T.List[str]
method: DependencyMethods
include_directories: T.List[T.Union[str, build.IncludeDirs]]
dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
preserve_paths: bool
output_json: bool
class PreprocessKwArgs(TypedDict):
sources: T.List[FileOrString]
moc_sources: T.List[str | build.TargetSources]
moc_headers: T.List[str | build.TargetSources]
qresources: T.List[FileOrString]
ui_files: T.List[str | build.TargetSources]
moc_extra_arguments: T.List[str]
rcc_extra_arguments: T.List[str]
uic_extra_arguments: T.List[str]
moc_output_json: bool
include_directories: T.List[T.Union[str, build.IncludeDirs]]
dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
method: DependencyMethods
preserve_paths: bool
class HasToolKwArgs(kwargs.ExtractRequired):
method: DependencyMethods
tools: T.List[Literal['moc', 'uic', 'rcc', 'lrelease', 'qmlcachegen', 'qmltyperegistrar']]
version: T.List[str]
class CompileTranslationsKwArgs(TypedDict):
build_by_default: bool
install: bool
install_dir: T.Optional[str]
method: DependencyMethods
qresource: T.Optional[str]
rcc_extra_arguments: T.List[str]
ts_files: T.List[str | build.TargetSources]
class GenQrcKwArgs(TypedDict):
sources: T.List[File]
aliases: T.List[str]
prefix: str
output: str
class GenQmldirKwArgs(TypedDict):
module_name: str
module_version: str
module_prefix: str
qml_sources: T.List[str | build.TargetSources]
qml_singletons: T.List[str | build.TargetSources]
qml_internals: T.List[str | build.TargetSources]
designer_supported: bool
imports: T.List[str]
optional_imports: T.List[str]
default_imports: T.List[str]
depends_imports: T.List[str]
typeinfo: str
output: str
class GenQmlCachegenKwArgs(TypedDict):
target_name: str
qml_sources: T.List[str | build.TargetSources]
qml_qrc: File
extra_args: T.List[str]
module_prefix: str
method: DependencyMethods
class GenQmlTypeRegistrarKwArgs(TypedDict):
target_name: str
import_name: str
major_version: str
minor_version: str
namespace: str
typeinfo: str
generate_qmltype: bool
collected_json: T.Optional[T.Union[File, build.CustomTarget]]
extra_args: T.List[str]
method: DependencyMethods
install: bool
install_dir: T.Optional[str]
class MocJsonCollectKwArgs(TypedDict):
target_name: str
moc_json: T.Sequence[build.GeneratedList]
method: DependencyMethods
class QmlModuleKwArgs(TypedDict):
version: str
qml_sources: T.List[str | build.TargetSources]
qml_singletons: T.List[str | build.TargetSources]
qml_internals: T.List[str | build.TargetSources]
resources_prefix: str
moc_headers: T.List[str | build.TargetSources]
include_directories: T.List[T.Union[str, build.IncludeDirs]]
imports: T.List[str]
optional_imports: T.List[str]
default_imports: T.List[str]
depends_imports: T.List[str]
designer_supported: bool
namespace: str
typeinfo: str
moc_extra_arguments: T.List[str]
rcc_extra_arguments: T.List[str]
qmlcachegen_extra_arguments: T.List[str]
qmltyperegistrar_extra_arguments: T.List[str]
generate_qmldir: bool
generate_qmltype: bool
cachegen: bool
dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
method: DependencyMethods
preserve_paths: bool
install_dir: str
install: bool
def _list_in_set_validator(choices: T.Set[str]) -> T.Callable[[T.List[str]], T.Optional[str]]:
"""Check that the choice given was one of the given set."""
def inner(checklist: T.List[str]) -> T.Optional[str]:
invalid = set(checklist).difference(choices)
if invalid:
return f"invalid selections {', '.join(sorted(invalid))}, valid elements are {', '.join(sorted(choices))}."
return None
return inner
#While Qt recomment module name to a be dot separated alphanum, it can technically be
#any well-formed ECMAScript Identifier Name.
#As best effort here we just check for illegal characters
#see https://doc.qt.io/qt-6/qtqml-modules-identifiedmodules.html
_MODULE_NAME_PUNCT = r'- {}<>()[\].:;~%?&,+^=|!\/*"\''
_MODULE_NAME_RE = f'[^{_MODULE_NAME_PUNCT}0-9][^{_MODULE_NAME_PUNCT}]*(\\.[^{_MODULE_NAME_PUNCT}0-9][^{_MODULE_NAME_PUNCT}]*)*'
class QtBaseModule(ExtensionModule):
_tools_detected = False
_rcc_supports_depfiles = False
_moc_supports_depfiles = False
_set_of_qt_tools = {'moc', 'uic', 'rcc', 'lrelease', 'qmlcachegen', 'qmltyperegistrar'}
_moc_supports_json = False
_support_qml_module = False
def __init__(self, interpreter: Interpreter, qt_version: int = 5):
ExtensionModule.__init__(self, interpreter)
self.qt_version = qt_version
# It is important that this list does not change order as the order of
# the returned ExternalPrograms will change as well
self.tools: T.Dict[str, Program] = {
tool: NonExistingExternalProgram(tool) for tool in self._set_of_qt_tools
}
self.methods.update({
'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,
'qml_module': self.qml_module,
})
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"""
wanted = f'== {qt_dep.version}'
def gen_bins() -> T.Generator[T.Tuple[str, str], None, None]:
for b in self.tools:
if qt_dep.bindir:
yield os.path.join(qt_dep.bindir, b), b
if qt_dep.libexecdir:
yield os.path.join(qt_dep.libexecdir, b), b
# prefer the (official) <tool><version> or (unofficial) <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_dep.qtver}', b
yield f'{b}-qt{qt_dep.qtver}', b
yield b, b
for b, name in gen_bins():
if self.tools[name].found():
continue
if name == 'lrelease':
arg = ['-version']
elif 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: Program) -> str:
_, out, err = Popen_safe(p.get_command() + arg)
if name == 'lrelease' or not qt_dep.version.startswith('4'):
care = out
else:
care = err
return care.rsplit(' ', maxsplit=1)[-1].replace(')', '').strip()
p = state.find_program(b, required=False,
version_func=get_version,
wanted=wanted)
if p.found():
self.tools[name] = p
def _detect_tools(self, state: ModuleState, method: DependencyMethods, required: bool = True, version: T.Optional[T.List[str]] = None) -> None:
if self._tools_detected:
return
self._tools_detected = True
mlog.log(f'Detecting Qt{self.qt_version} tools')
native = state.machine_map.host
version = version or []
kwargs: DependencyObjectKWs = {'required': required, 'modules': ['Core'], 'method': method, 'native': native, 'version': version}
# 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, '>=6.2.0'):
#5.1x supports qmlcachegen and other tools to some extend, but arguments/build process marginally differs
self._support_qml_module = True
if version_compare(qt.version, '>=5.15.0'):
self._moc_supports_depfiles = True
self._moc_supports_json = True
else:
mlog.warning('moc dependencies will not work properly until you move to Qt >= 5.15', fatal=False)
if 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)
else:
suffix = f'-qt{self.qt_version}'
self.tools['moc'] = NonExistingExternalProgram(name='moc' + suffix)
self.tools['uic'] = NonExistingExternalProgram(name='uic' + suffix)
self.tools['rcc'] = NonExistingExternalProgram(name='rcc' + suffix)
self.tools['lrelease'] = NonExistingExternalProgram(name='lrelease' + suffix)
@staticmethod
def _qrc_nodes(state: ModuleState, rcc_file: 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)
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 trying to check here? (probably parse errors?)
try:
tree = ET.parse(abspath)
root = tree.getroot()
result: T.List[str] = []
for child in root[0]:
if child.tag != 'file':
mlog.warning("malformed rcc file: ", os.path.join(state.subdir, str(rcc_file)))
break
elif child.text is None:
raise MesonException(f'<file> element without a path in {os.path.join(state.subdir, str(rcc_file))}')
else:
result.append(child.text)
return rcc_dirname, result
except MesonException:
raise
except Exception:
raise MesonException(f'Unable to parse resource file {abspath}')
def _parse_qrc_deps(self, state: ModuleState, rcc_file_: FileOrString) -> T.List[File]:
result: T.List[File] = []
inputs: T.Sequence['FileOrString'] = [rcc_file_]
for rcc_file in inputs:
rcc_dirname, nodes = self._qrc_nodes(state, rcc_file)
for resource_path in nodes:
# We need to guess if the pointed resource is:
# a) in build directory -> implies a generated file
# b) in source directory
# c) somewhere else external dependency file to bundle
#
# Also from qrc documentation: relative path are always from qrc file
# So relative path must always be computed from qrc file !
if os.path.isabs(resource_path):
# a)
if resource_path.startswith(os.path.abspath(state.environment.build_dir)):
resource_relpath = os.path.relpath(resource_path, state.environment.build_dir)
result.append(File(is_built=True, subdir='', fname=resource_relpath))
# either b) or c)
else:
result.append(File(is_built=False, subdir=state.subdir, fname=resource_path))
else:
path_from_rcc = os.path.normpath(os.path.join(rcc_dirname, resource_path))
# a)
if path_from_rcc.startswith(state.environment.build_dir):
result.append(File(is_built=True, subdir=state.subdir, fname=resource_path))
# b)
else:
result.append(File(is_built=False, subdir=state.subdir, fname=path_from_rcc))
return result
@FeatureNew('qt.has_tools', '0.54.0')
@noPosargs
@typed_kwargs(
'qt.has_tools',
DEPENDENCY_METHOD_KW,
REQUIRED_KW.evolve(default=False),
KwargInfo('tools', ContainerTypeInfo(list, str), listify=True,
default=['moc', 'uic', 'rcc', 'lrelease'],
validator=_list_in_set_validator(_set_of_qt_tools),
since='1.6.0'),
KwargInfo('version', ContainerTypeInfo(list, str), listify=True, default=[], since='1.11'),
)
def has_tools(self, state: ModuleState, args: T.Tuple, kwargs: HasToolKwArgs) -> bool:
method = kwargs['method']
# 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')
return False
self._detect_tools(state, method, required=False, version=kwargs['version'])
for tool in kwargs['tools']:
assert tool in self._set_of_qt_tools, f'tools must be in {self._set_of_qt_tools}'
if not self.tools[tool].found():
if required:
raise MesonException('Qt tools not found')
return False
return True
@FeatureNew('qt.compile_resources', '0.59.0')
@noPosargs
@typed_kwargs(
'qt.compile_resources',
DEPENDENCY_METHOD_KW,
KwargInfo('name', (str, NoneType)),
KwargInfo(
'sources',
ContainerTypeInfo(list, (File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList), allow_empty=False),
listify=True,
required=True,
),
KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]),
)
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.
"""
if any(isinstance(s, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) for s in kwargs['sources']):
FeatureNew.single_use('qt.compile_resources: custom_target or generator for "sources" keyword argument',
'0.60.0', state.subproject, location=state.current_node)
out = self._compile_resources_impl(state, kwargs)
return ModuleReturnValue(out, [out])
def _compile_resources_impl(self, state: 'ModuleState', kwargs: 'ResourceCompilerKwArgs') -> T.List[build.CustomTarget]:
# Avoid the FeatureNew when dispatching from preprocess
self._detect_tools(state, kwargs['method'])
if not self.tools['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: T.List[File] = []
for s in kwargs['sources']:
if isinstance(s, str):
sources.append(File.from_source_file(state.environment.source_dir, state.subdir, s))
elif isinstance(s, File):
sources.append(s)
else:
# TODO: this could be fixed with dyndeps
raise MesonException('Resource xml files generated at build-time cannot be used with '
'qt.compile_resources() because we need to scan the xml for '
'dependencies.\nUse configure_file() instead to generate it at '
'configure-time.')
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.
cmd: CommandList
if name:
qrc_deps: T.List[File] = []
for s in sources:
qrc_deps.extend(self._parse_qrc_deps(state, s))
cmd = [self.tools['rcc'], '-name', name, '-o', '@OUTPUT@', *extra_args, '@INPUT@', *DEPFILE_ARGS]
res_target = build.CustomTarget(
name,
state.subdir,
state.environment,
cmd,
sources,
[f'{name}.cpp'],
state.current_build_project,
depend_files=qrc_deps,
depfile=f'{name}.d',
description='Compiling Qt resources {}',
)
targets.append(res_target)
else:
for rcc_file in sources:
qrc_deps = self._parse_qrc_deps(state, rcc_file)
basename = os.path.basename(rcc_file.fname)
name = f'qt{self.qt_version}-{basename.replace(".", "_")}'
cmd = [self.tools['rcc'], '-name', '@BASENAME@', '-o', '@OUTPUT@', *extra_args, '@INPUT@', *DEPFILE_ARGS]
res_target = build.CustomTarget(
name,
state.subdir,
state.environment,
cmd,
[rcc_file],
[f'{name}.cpp'],
state.current_build_project,
depend_files=qrc_deps,
depfile=f'{name}.d',
description='Compiling Qt resources {}',
)
targets.append(res_target)
return targets
@FeatureNew('qt.compile_ui', '0.59.0')
@noPosargs
@typed_kwargs(
'qt.compile_ui',
DEPENDENCY_METHOD_KW,
KwargInfo(
'sources',
ContainerTypeInfo(list, (File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList), allow_empty=False),
listify=True,
required=True,
),
KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]),
KwargInfo('preserve_paths', bool, default=False, since='1.4.0'),
)
def compile_ui(self, state: ModuleState, args: T.Tuple, kwargs: UICompilerKwArgs) -> ModuleReturnValue:
"""Compile UI resources into cpp headers."""
if any(isinstance(s, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) for s in kwargs['sources']):
FeatureNew.single_use('qt.compile_ui: custom_target or generator for "sources" keyword argument',
'0.60.0', state.subproject, location=state.current_node)
out = self._compile_ui_impl(state, kwargs)
return ModuleReturnValue(out, [out])
def _compile_ui_impl(self, state: ModuleState, kwargs: UICompilerKwArgs) -> build.GeneratedList:
# Avoid the FeatureNew when dispatching from preprocess
self._detect_tools(state, kwargs['method'])
if not self.tools['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))
preserve_path_from = os.path.join(state.source_root, state.subdir) if kwargs['preserve_paths'] else None
gen = build.Generator(
state.environment,
self.tools['uic'],
kwargs['extra_args'] + ['-o', '@OUTPUT@', '@INPUT@'],
['ui_@BASENAME@.h'],
name=f'Qt{self.qt_version} ui')
sources = self._source_to_files(state, kwargs['sources'])
return gen.process_files(sources, state.subdir, preserve_path_from)
@FeatureNew('qt.compile_moc', '0.59.0')
@noPosargs
@typed_kwargs(
'qt.compile_moc',
DEPENDENCY_METHOD_KW,
KwargInfo(
'sources',
ContainerTypeInfo(list, (File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)),
listify=True,
default=[],
),
KwargInfo(
'headers',
ContainerTypeInfo(list, (File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)),
listify=True,
default=[]
),
KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]),
KwargInfo('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]),
KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]),
KwargInfo('preserve_paths', bool, default=False, since='1.4.0'),
KwargInfo('output_json', bool, default=False, since='1.7.0'),
)
def compile_moc(self, state: ModuleState, args: T.Tuple, kwargs: MocCompilerKwArgs) -> ModuleReturnValue:
if any(isinstance(s, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) for s in kwargs['headers']):
FeatureNew.single_use('qt.compile_moc: custom_target or generator for "headers" keyword argument',
'0.60.0', state.subproject, location=state.current_node)
if any(isinstance(s, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) for s in kwargs['sources']):
FeatureNew.single_use('qt.compile_moc: custom_target or generator for "sources" keyword argument',
'0.60.0', state.subproject, location=state.current_node)
out = self._compile_moc_impl(state, kwargs)
return ModuleReturnValue(out, [out])
def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T.List[build.GeneratedList]:
# Avoid the FeatureNew when dispatching from preprocess
self._detect_tools(state, kwargs['method'])
if not self.tools['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 provided and not empty')
inc = state.get_include_args(include_dirs=kwargs['include_directories'])
compile_args: T.List[str] = []
depends: T.List[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]] = []
for dep in kwargs['dependencies']:
compile_args.extend(a for a in dep.get_all_compile_args() if a.startswith(('-I', '-F', '-D')))
if isinstance(dep, InternalDependency):
for incl in dep.include_directories:
compile_args.extend(f'-I{i}' for i in incl.abs_string_list(self.interpreter.source_root, self.interpreter.environment.build_dir))
for src in dep.sources:
if isinstance(src, (build.CustomTarget, build.BuildTarget, build.CustomTargetIndex)):
depends.append(src)
output: T.List[build.GeneratedList] = []
do_output_json: bool = kwargs['output_json']
if do_output_json and not self._moc_supports_json:
raise MesonException(f'moc-qt{self.qt_version} doesn\'t support "output_json" option')
# depfile arguments (defaults to <output-name>.d)
DEPFILE_ARGS: T.List[str] = ['--output-dep-file'] if self._moc_supports_depfiles else []
JSON_ARGS: T.List[str] = ['--output-json'] if do_output_json else []
arguments = kwargs['extra_args'] + DEPFILE_ARGS + JSON_ARGS + inc + compile_args + ['@INPUT@', '-o', '@OUTPUT0@']
preserve_path_from = os.path.join(state.source_root, state.subdir) if kwargs['preserve_paths'] else None
if kwargs['headers']:
header_gen_output: T.List[str] = ['moc_@BASENAME@.cpp']
if do_output_json:
header_gen_output.append('moc_@BASENAME@.cpp.json')
moc_gen = build.Generator(
state.environment,
self.tools['moc'], arguments, header_gen_output,
depends=depends,
depfile='moc_@BASENAME@.cpp.d',
name=f'Qt{self.qt_version} moc header')
headers = self._source_to_files(state, kwargs['headers'])
output.append(moc_gen.process_files(headers, state.subdir, preserve_path_from))
if kwargs['sources']:
source_gen_output: T.List[str] = ['@BASENAME@.moc']
if do_output_json:
source_gen_output.append('@BASENAME@.moc.json')
moc_gen = build.Generator(
state.environment,
self.tools['moc'], arguments, source_gen_output,
depfile='@BASENAME@.moc.d',
name=f'Qt{self.qt_version} moc source')
sources = self._source_to_files(state, kwargs['sources'])
output.append(moc_gen.process_files(sources, state.subdir, preserve_path_from))
return output
# We can't use typed_pos_args here, the signature is ambiguous
@typed_kwargs(
'qt.preprocess',
DEPENDENCY_METHOD_KW,
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, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)), listify=True, default=[],
since_values={ContainerTypeInfo(list, (build.CustomTargetIndex, build.GeneratedList)): '1.12'}),
KwargInfo('moc_sources', ContainerTypeInfo(list, (File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)), listify=True, default=[],
since_values={ContainerTypeInfo(list, (build.CustomTargetIndex, build.GeneratedList)): '1.12'}),
KwargInfo('moc_headers', ContainerTypeInfo(list, (File, str, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)), listify=True, default=[],
since_values={ContainerTypeInfo(list, (build.CustomTargetIndex, build.GeneratedList)): '1.12'}),
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('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]),
KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]),
KwargInfo('preserve_paths', bool, default=False, since='1.4.0'),
KwargInfo('moc_output_json', bool, default=False, since='1.7.0'),
)
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, location=state.current_node)
# List is invariant, os we have to cast...
sources: T.List[str | build.TargetSources] = [*_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': T.cast('T.List[str | TargetSources]', kwargs['qresources']),
'extra_args': kwargs['rcc_extra_arguments'], 'method': method}
if args:
name = args[0]
if not isinstance(name, str):
raise build.InvalidArguments('First argument to qt.preprocess must be a string')
rcc_kwargs['name'] = name
sources.extend(self._compile_resources_impl(state, rcc_kwargs))
if kwargs['ui_files']:
ui_kwargs: UICompilerKwArgs = {
'sources': kwargs['ui_files'],
'extra_args': kwargs['uic_extra_arguments'],
'method': method,
'preserve_paths': kwargs['preserve_paths'],
}
sources.append(self._compile_ui_impl(state, ui_kwargs))
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,
'preserve_paths': kwargs['preserve_paths'],
'output_json': kwargs['moc_output_json']
}
sources.extend(self._compile_moc_impl(state, moc_kwargs))
return ModuleReturnValue(sources, [sources])
@FeatureNew('qt.compile_translations', '0.44.0')
@noPosargs
@typed_kwargs(
'qt.compile_translations',
KwargInfo('build_by_default', bool, default=False),
DEPENDENCY_METHOD_KW,
INSTALL_KW,
INSTALL_DIR_KW,
KwargInfo('qresource', (str, NoneType), 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, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)), listify=True, default=[]),
)
def compile_translations(self, state: ModuleState, args: T.Tuple, kwargs: CompileTranslationsKwArgs) -> ModuleReturnValue:
ts_files = kwargs['ts_files']
if any(isinstance(s, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) for s in ts_files):
FeatureNew.single_use('qt.compile_translations: custom_target or generator for "ts_files" keyword argument',
'0.60.0', state.subproject, location=state.current_node)
if kwargs['install'] and not kwargs['install_dir']:
raise MesonException('qt.compile_translations: "install_dir" keyword argument must be set when "install" is true.')
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 = 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)
_, nodes = self._qrc_nodes(state, qresource_file)
for c in nodes:
if c.endswith('.qm'):
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_file, '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.tools['lrelease'].found():
raise MesonException('qt.compile_translations: ' +
self.tools['lrelease'].name + ' not found')
ts_file: CustomTargetSources
if qresource:
# In this case we know that ts_files is always a List[str], as
# it's generated above and no ts_files are passed in. However,
# mypy can't figure that out so we use assert to assure it that
# what we're doing is safe
assert isinstance(ts, str), 'for mypy'
outdir = os.path.dirname(os.path.normpath(os.path.join(state.subdir, ts)))
ts_file = File.from_source_file(state.environment.source_dir, outdir, os.path.basename(ts))
elif isinstance(ts, str):
outdir = state.subdir
ts_file = File.from_source_file(state.environment.source_dir, state.subdir, ts)
else:
outdir = state.subdir
ts_file = ts
cmd: CommandList = [self.tools['lrelease'], '@INPUT@', '-qm', '@OUTPUT@']
lrelease_target = build.CustomTarget(
f'qt{self.qt_version}-compile-{ts}',
outdir,
state.environment,
cmd,
[ts_file],
['@BASENAME@.qm'],
state.current_build_project,
install=kwargs['install'],
install_dir=[kwargs['install_dir']],
install_tag=['i18n'],
build_by_default=kwargs['build_by_default'],
description='Compiling Qt translations {}',
)
translations.append(lrelease_target)
if qresource:
return ModuleReturnValue(results.return_value[0], [results.new_objects, translations])
else:
return ModuleReturnValue(translations, [translations])
def _source_to_files(self, state: ModuleState, sources: T.List[str | build.TargetSources]) -> T.List[File]:
content_files = []
for s in sources:
if isinstance(s, (build.CustomTarget, build.CustomTargetIndex)):
for o in s.get_outputs():
content_files.append(File.from_built_file(state.backend.get_target_dir(s), o))
elif isinstance(s, File):
content_files.append(s)
elif isinstance(s, build.GeneratedList):
for gen_src in s.get_outputs():
content_files.append(File.from_built_file(state.subdir, gen_src))
else:
content_files.append(File.from_source_file(
state.environment.get_source_dir(),
state.subdir,
s
))
return content_files
def _gen_qrc(self, state: ModuleState, kwargs: GenQrcKwArgs) -> File:
fileout = File.from_built_file(state.subdir, kwargs['output'])
fileout_abs = os.path.join(state.environment.build_dir, fileout.relative_name())
if not os.path.isdir(state.environment.build_dir):
os.mkdir(state.environment.build_dir)
rcc = ET.Element('RCC')
qresource = ET.SubElement(rcc, 'qresource', prefix='/' + kwargs['prefix'])
assert (len(kwargs['sources']) == len(kwargs['aliases']))
for source, alias in zip(kwargs['sources'], kwargs['aliases']):
filenode = ET.SubElement(qresource, 'file', alias=alias)
filenode.text = source.absolute_path(
state.environment.get_source_dir(),
state.environment.get_build_dir()
)
tree = ET.ElementTree(rcc)
tree.write(fileout_abs)
return fileout
def _gen_qmldir(self, state: ModuleState, kwargs: GenQmldirKwArgs) -> File:
module_name: str = kwargs['module_name']
module_version: str = kwargs['module_version']
module_prefix: str = kwargs['module_prefix']
designer_supported: bool = kwargs['designer_supported']
typeinfo_file: str = kwargs['typeinfo']
#Foo.Bar/1.0 foo.bar/auto foo.bar
import_re = re.compile(r'^(' + _MODULE_NAME_RE + r')(/((\d+(\.\d+)?)|auto))?$')
fileout = File.from_built_file(state.subdir, kwargs['output'])
fileout_abs = os.path.join(state.environment.build_dir, fileout.relative_name())
if not os.path.isdir(state.environment.build_dir):
os.mkdir(state.environment.build_dir)
with open(fileout_abs, 'w', encoding='utf-8') as fd:
def __gen_import(import_type: str, importlist: T.List[str]) -> None:
for import_string in importlist:
match = import_re.match(import_string)
if not match:
raise MesonException(f'invalid syntax for qml import {import_string}')
module: str = match.group(1)
version: str = match.group(4) or ''
fd.write(f'{import_type} {module} {version}\n')
def __gen_declaration(qualifier: str, version: str, importlist: T.List[str | build.TargetSources]) -> None:
importpathlist = self._source_to_files(state, importlist)
for s in importpathlist:
basename: str = os.path.basename(s.fname)
classname: str = basename.rsplit('.', maxsplit=1)[0]
if not basename.endswith(('.qml', '.js', '.mjs')):
raise MesonException(f'unexpected file type declared in qml sources {s}')
if not classname or '.' in classname or classname[0].islower():
raise MesonException(f'{basename} is not a valid QML file name')
if version:
fd.write(f'{qualifier}{classname} {version} {basename}\n')
else:
fd.write(f'{qualifier}{classname} {basename}\n')
fd.write(f'module {module_name}\n')
fd.write(f'prefer :/{module_prefix}/\n')
__gen_import('import', kwargs['imports'])
__gen_import('optional import', kwargs['optional_imports'])
__gen_import('default import', kwargs['default_imports'])
__gen_import('depends', kwargs['depends_imports'])
__gen_declaration('', module_version, kwargs['qml_sources'])
__gen_declaration('singleton ', module_version, kwargs['qml_singletons'])
__gen_declaration('internal ', '', kwargs['qml_internals'])
if typeinfo_file:
fd.write(f'typeinfo {typeinfo_file}\n')
if designer_supported:
fd.write('designersupported\n')
return fileout
def _moc_json_collect(self, state: ModuleState, kwargs: MocJsonCollectKwArgs) -> build.CustomTarget:
self._detect_tools(state, kwargs['method'])
if not self.tools['moc'].found():
raise MesonException('qt.qml_module: ' +
self.tools['moc'].name + ' not found')
target_name: str = kwargs['target_name']
moc_json: T.Sequence[build.GeneratedList] = kwargs['moc_json']
#there may be a better way :-/
input_args: T.List[str] = []
input_counter = 0
for g in moc_json:
for fname in g.get_outputs():
if fname.endswith('.json'):
input_args.append(f'@INPUT{input_counter}@')
input_counter += 1
cmd: CommandList = [self.tools['moc'], '--collect-json', '-o', '@OUTPUT@', *input_args]
return build.CustomTarget(
f'moc_collect_json_{target_name}',
state.subdir,
state.environment,
cmd,
moc_json,
[f'{target_name}_json_collect.json'],
state.current_build_project,
description=f'Collecting json type information for {target_name}',
)
def _gen_qml_cachegen(self, state: ModuleState, kwargs: GenQmlCachegenKwArgs) -> T.List[T.Union[build.CustomTarget, build.GeneratedList]]:
self._detect_tools(state, kwargs['method'])
if not self.tools['qmlcachegen'].found():
raise MesonException('qt.qml_module: ' +
self.tools['qmlcachegen'].name + ' not found')
target_name: str = kwargs['target_name']
command_args = ['-o', '@OUTPUT@'] + kwargs['extra_args']
for qrc in self._source_to_files(state, [kwargs['qml_qrc']]):
command_args.extend(['--resource', qrc.absolute_path(
state.environment.get_source_dir(),
state.environment.get_build_dir()
)])
command_args.append('@INPUT@')
cache_gen = build.Generator(
state.environment,
self.tools['qmlcachegen'],
command_args,
[f'{target_name}_@BASENAME@.cpp'],
name=f'Qml cache generation for {target_name}')
output: T.List[T.Union[build.CustomTarget, build.GeneratedList]] = []
qml_sources = self._source_to_files(state, kwargs['qml_sources'])
output.append(cache_gen.process_files(qml_sources, state.subdir))
cachegen_inputs: T.List[str] = []
qml_sources_paths = self._source_to_files(state, kwargs['qml_sources'])
for s in qml_sources_paths:
source_basename = os.path.basename(s.fname)
ressource_path = os.path.join('/', kwargs['module_prefix'], source_basename)
cachegen_inputs.append(ressource_path)
cmd: CommandList = \
[self.tools['qmlcachegen'], '-o', '@OUTPUT@', '--resource-name', f'qmlcache_{target_name}',
*kwargs['extra_args'], '--resource=@INPUT@', *cachegen_inputs]
cacheloader_target = build.CustomTarget(
f'cacheloader_{target_name}',
state.subdir,
state.environment,
cmd,
[kwargs['qml_qrc']],
#output name format matters here
[f'{target_name}_qmlcache_loader.cpp'],
state.current_build_project,
description=f'Qml cache loader for {target_name}',
)
output.append(cacheloader_target)
return output
def _qml_type_registrar(self, state: ModuleState, kwargs: GenQmlTypeRegistrarKwArgs) -> build.CustomTarget:
self._detect_tools(state, kwargs['method'])
if not self.tools['qmltyperegistrar'].found():
raise MesonException('qt.qml_module: ' +
self.tools['qmltyperegistrar'].name + ' not found')
import_name: str = kwargs['import_name']
major_version: str = kwargs['major_version']
minor_version: str = kwargs['minor_version']
namespace: str = kwargs['namespace']
typeinfo: str = kwargs['typeinfo']
target_name: str = kwargs['target_name']
collected_json: T.Optional[T.Union[File, build.CustomTarget]] = kwargs['collected_json']
inputs: T.Sequence[T.Union[File, build.CustomTarget]] = [collected_json] if collected_json else []
outputs: T.List[str] = [f'{target_name}_qmltyperegistrations.cpp']
install_dir: T.List[T.Union[str, Literal[False]]] = [False]
install_tag: T.List[T.Union[str, None]] = [None]
cmd: CommandList = [
self.tools['qmltyperegistrar'],
'--import-name', import_name,
'--major-version', major_version,
'--minor-version', minor_version,
'-o', '@OUTPUT0@',
*kwargs['extra_args'],
]
if namespace:
cmd.extend(['--namespace', namespace])
if kwargs['generate_qmltype']:
cmd.extend(['--generate-qmltypes', '@OUTPUT1@'])
if typeinfo == '':
outputs.append(f'{target_name}.qmltypes')
else:
outputs.append(f'{typeinfo}')
install_dir.append(kwargs['install_dir'])
install_tag.append('devel')
if collected_json:
cmd.append('@INPUT@')
return build.CustomTarget(
f'typeregistrar_{target_name}',
state.subdir,
state.environment,
cmd,
inputs,
outputs,
state.current_build_project,
install=kwargs['install'],
install_dir=install_dir,
install_tag=install_tag,
description=f'Qml type registration for {target_name}',
)
@FeatureNew('qt.qml_module', '1.7')
@typed_pos_args('qt.qml_module', str)
@typed_kwargs(
'qt.qml_module',
KwargInfo('version', str, default='254.254'),
#qml sources
KwargInfo('qml_sources', ContainerTypeInfo(list, (File, str, build.CustomTarget)), listify=True, default=[]),
KwargInfo('qml_singletons', ContainerTypeInfo(list, (File, str, build.CustomTarget)), listify=True, default=[]),
KwargInfo('qml_internals', ContainerTypeInfo(list, (File, str, build.CustomTarget)), listify=True, default=[]),
KwargInfo('resources_prefix', str, default='qt/qml'),
#qmldir generation
KwargInfo('imports', ContainerTypeInfo(list, (str)), default=[]),
KwargInfo('optional_imports', ContainerTypeInfo(list, (str)), default=[]),
KwargInfo('default_imports', ContainerTypeInfo(list, (str)), default=[]),
#match DEPENDENCIES argument from CMake, but dependencies keyword is already taken
KwargInfo('depends_imports', ContainerTypeInfo(list, (str)), default=[]),
KwargInfo('designer_supported', bool, default=False),
#for type registration, same arguments as moc
#moc_sources is voluntary ommited as typeregistrar needs to import a header
KwargInfo('moc_headers', ContainerTypeInfo(list, (File, str, build.CustomTarget)), listify=True, default=[]),
KwargInfo('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]),
KwargInfo('namespace', str, default=''),
KwargInfo('typeinfo', str, default=''),
KwargInfo('moc_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[]),
KwargInfo('rcc_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[]),
KwargInfo('qmlcachegen_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[]),
KwargInfo('qmltyperegistrar_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[]),
KwargInfo('generate_qmldir', bool, default=True),
KwargInfo('generate_qmltype', bool, default=True),
KwargInfo('cachegen', bool, default=True),
KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]),
INSTALL_DIR_KW,
INSTALL_KW,
DEPENDENCY_METHOD_KW,
KwargInfo('preserve_paths', bool, default=False),
)
def qml_module(self, state: ModuleState, args: T.Tuple[str], kwargs: QmlModuleKwArgs) -> ModuleReturnValue:
self._detect_tools(state, kwargs['method'])
if not self._support_qml_module:
raise MesonException('qt.qml_module is not suppported for this version of Qt')
#Major.Minor(.Patch)
version_re = re.compile(r'^(\d+)\.(\d+)(\.(\d+))?$')
module_name_re = re.compile(_MODULE_NAME_RE)
output: T.List[T.Union[build.CustomTarget, build.GeneratedList]] = []
module_name: str = args[0]
if not module_name_re.fullmatch(module_name):
raise MesonException(f'qml module URI should be in the form Foo.Bar.xxx, got {module_name}')
module_version: str = kwargs['version']
module_version_match = version_re.match(module_version)
if not module_version_match:
raise MesonException(f'qml module version should be in the form Major.Minor, got {module_version}')
module_version_major: str = module_version_match.group(1)
module_version_minor: str = module_version_match.group(2)
#qt ignores .patch version
module_version_short = f'{module_version_major}.{module_version_minor}'
module_prefix_list: T.List[str] = module_name.split('.')
module_prefix: str = os.path.join(*module_prefix_list)
module_prefix_full: str = os.path.join(*(kwargs['resources_prefix'].split('/') + module_prefix_list))
#same format as the one derived from qmltyperegistrar
target_name = re.sub(r'[^A-Za-z0-9]', '_', module_name)
qrc_resouces: T.List[str | build.TargetSources] = []
all_qml: T.List[str | build.TargetSources] = kwargs['qml_sources'] + kwargs['qml_singletons'] + kwargs['qml_internals']
all_qml_files: T.List[File] = self._source_to_files(state, all_qml)
all_qml_basename: T.List[str] = [os.path.basename(p.fname) for p in all_qml_files]
install_dir: str = kwargs['install_dir'] or 'qml'
module_install_dir: str = os.path.join(install_dir, module_prefix)
if len(all_qml) != 0:
qml_qrc_kwargs: GenQrcKwArgs = {
'output': f'{target_name}_qml.qrc',
'sources': all_qml_files,
'aliases': all_qml_basename,
'prefix': module_prefix_full,
}
qml_qrc = self._gen_qrc(state, qml_qrc_kwargs)
if not kwargs['cachegen']:
qrc_resouces.append(qml_qrc)
else:
cachegen_kwargs: GenQmlCachegenKwArgs = {
'target_name': target_name,
'qml_qrc': qml_qrc,
'qml_sources': all_qml,
'module_prefix': module_prefix_full,
'extra_args': kwargs['qmlcachegen_extra_arguments'],
'method': kwargs['method'],
}
output.extend(self._gen_qml_cachegen(state, cachegen_kwargs))
#copy QML files for Qt tools
if kwargs['install']:
self.interpreter.install_data_impl(all_qml_files, module_install_dir,
FileMode(), all_qml_basename, 'devel')
collected_json: T.Optional[T.Union[File, build.CustomTarget]] = None
if kwargs['moc_headers']:
compile_moc_kwargs: MocCompilerKwArgs = {
'sources': [],
'headers': kwargs['moc_headers'],
'extra_args': kwargs['moc_extra_arguments'],
'method': kwargs['method'],
'include_directories': kwargs['include_directories'],
'dependencies': kwargs['dependencies'],
'preserve_paths': kwargs['preserve_paths'],
'output_json': True,
}
moc_output = self._compile_moc_impl(state, compile_moc_kwargs)
output.extend(moc_output)
moc_collect_json_kwargs: MocJsonCollectKwArgs = {
'target_name': target_name,
'moc_json': moc_output,
'method': kwargs['method'],
}
collected_json = self._moc_json_collect(state, moc_collect_json_kwargs)
output.append(collected_json)
typeinfo_file: str = ''
#cmake NO_GENERATE_QMLTYPE disable the whole type registration, not just the .qmltype generation
if kwargs['generate_qmltype']:
qmltyperegistrar_kwargs: GenQmlTypeRegistrarKwArgs = {
'target_name': target_name,
'import_name': module_name,
'major_version': module_version_major,
'minor_version': module_version_minor,
'collected_json': collected_json,
'namespace': kwargs['namespace'],
'generate_qmltype': True,
'extra_args': kwargs['qmltyperegistrar_extra_arguments'],
'typeinfo': kwargs['typeinfo'],
'method': kwargs['method'],
'install': kwargs['install'],
'install_dir': module_install_dir,
}
type_registrar_output = self._qml_type_registrar(state, qmltyperegistrar_kwargs)
output.append(type_registrar_output)
if len(type_registrar_output.get_outputs()) == 2:
typeinfo_file = type_registrar_output.get_outputs()[1]
if kwargs['generate_qmldir']:
qmldir_kwargs: GenQmldirKwArgs = {
'output': f'{target_name}_qmldir',
'module_name': module_name,
'module_version': module_version_short,
'qml_sources': kwargs['qml_sources'],
'qml_singletons': kwargs['qml_singletons'],
'qml_internals': kwargs['qml_internals'],
'imports': kwargs['imports'],
'optional_imports': kwargs['optional_imports'],
'default_imports': kwargs['default_imports'],
'depends_imports': kwargs['depends_imports'],
'designer_supported': kwargs['designer_supported'],
'typeinfo': typeinfo_file,
'module_prefix': module_prefix_full,
}
qmldir_file: File = self._gen_qmldir(state, qmldir_kwargs)
qmldir_qrc_kwargs: GenQrcKwArgs = {
'output': f'{target_name}_qmldir.qrc',
'sources': self._source_to_files(state, [qmldir_file]),
'aliases': ['qmldir'],
'prefix': module_prefix_full,
}
qrc_resouces.append(self._gen_qrc(state, qmldir_qrc_kwargs))
if kwargs['install']:
self.interpreter.install_data_impl([qmldir_file], module_install_dir,
FileMode(), ['qmldir'], 'devel')
if qrc_resouces:
compile_resource_kwargs: ResourceCompilerKwArgs = {
'name': target_name,
'sources': qrc_resouces,
'extra_args': kwargs['rcc_extra_arguments'],
'method': kwargs['method'],
}
output.extend(self._compile_resources_impl(state, compile_resource_kwargs))
return ModuleReturnValue(output, [output])