modules/qt: Add a `compile_resources` method

This is a separate method for just handling qrc resources.
pull/8822/head
Dylan Baker 4 years ago
parent 4575ed3d31
commit d27948b1dc
  1. 112
      mesonbuild/modules/qt.py
  2. 6
      test cases/frameworks/4 qt/meson.build

@ -24,16 +24,28 @@ from ..mesonlib import MesonException, extract_as_list, File, unholder, version_
from ..dependencies import Dependency from ..dependencies import Dependency
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from . import ModuleReturnValue, ExtensionModule from . import ModuleReturnValue, ExtensionModule
from ..interpreterbase import FeatureDeprecated, FeatureDeprecatedKwargs, noPosargs, permittedKwargs, FeatureNew, FeatureNewKwargs from ..interpreterbase import ContainerTypeInfo, FeatureDeprecated, FeatureDeprecatedKwargs, KwargInfo, noPosargs, permittedKwargs, FeatureNew, FeatureNewKwargs, typed_kwargs
from ..interpreter import extract_required_kwarg from ..interpreter import extract_required_kwarg
from ..programs import NonExistingExternalProgram from ..programs import NonExistingExternalProgram
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from ..interpreter import Interpreter from . import ModuleState
from ..dependencies.qt import QtBaseDependency from ..dependencies.qt import QtBaseDependency
from ..environment import Environment from ..environment import Environment
from ..interpreter import Interpreter
from ..programs import ExternalProgram from ..programs import ExternalProgram
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 QtBaseModule(ExtensionModule): class QtBaseModule(ExtensionModule):
tools_detected = False tools_detected = False
@ -50,6 +62,7 @@ class QtBaseModule(ExtensionModule):
'has_tools': self.has_tools, 'has_tools': self.has_tools,
'preprocess': self.preprocess, 'preprocess': self.preprocess,
'compile_translations': self.compile_translations, 'compile_translations': self.compile_translations,
'compile_resources': self.compile_resources,
}) })
def compilers_detect(self, state, qt_dep: 'QtBaseDependency') -> None: def compilers_detect(self, state, qt_dep: 'QtBaseDependency') -> None:
@ -96,7 +109,7 @@ class QtBaseModule(ExtensionModule):
if p.found(): if p.found():
setattr(self, name, p) setattr(self, name, p)
def _detect_tools(self, state, method, required=True): def _detect_tools(self, state: 'ModuleState', method: str, required: bool = True) -> None:
if self.tools_detected: if self.tools_detected:
return return
self.tools_detected = True self.tools_detected = True
@ -118,21 +131,24 @@ class QtBaseModule(ExtensionModule):
self.rcc = NonExistingExternalProgram(name='rcc' + suffix) self.rcc = NonExistingExternalProgram(name='rcc' + suffix)
self.lrelease = NonExistingExternalProgram(name='lrelease' + suffix) self.lrelease = NonExistingExternalProgram(name='lrelease' + suffix)
def qrc_nodes(self, state, rcc_file): @staticmethod
if type(rcc_file) is str: 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) abspath = os.path.join(state.environment.source_dir, state.subdir, rcc_file)
rcc_dirname = os.path.dirname(abspath) 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) abspath = rcc_file.absolute_path(state.environment.source_dir, state.environment.build_dir)
rcc_dirname = os.path.dirname(abspath) rcc_dirname = os.path.dirname(abspath)
# FIXME: what error are we actually tring to check here?
try: try:
tree = ET.parse(abspath) tree = ET.parse(abspath)
root = tree.getroot() root = tree.getroot()
result = [] result: T.List[str] = []
for child in root[0]: for child in root[0]:
if child.tag != 'file': 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 break
else: else:
result.append(child.text) result.append(child.text)
@ -141,9 +157,9 @@ class QtBaseModule(ExtensionModule):
except Exception: except Exception:
raise MesonException(f'Unable to parse resource file {abspath}') raise MesonException(f'Unable to parse resource file {abspath}')
def parse_qrc_deps(self, state, rcc_file): def _parse_qrc_deps(self, state: 'ModuleState', rcc_file: 'mesonlib.FileOrString') -> T.List[File]:
rcc_dirname, nodes = self.qrc_nodes(state, rcc_file) rcc_dirname, nodes = self._qrc_nodes(state, rcc_file)
result = [] result: T.List[File] = []
for resource_path in nodes: for resource_path in nodes:
# We need to guess if the pointed resource is: # We need to guess if the pointed resource is:
# a) in build directory -> implies a generated file # a) in build directory -> implies a generated file
@ -187,6 +203,73 @@ class QtBaseModule(ExtensionModule):
return False return False
return True return True
@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),
KwargInfo('method', str, default='auto')
)
def compile_resources(self, state: 'ModuleState', args: T.Tuple, kwargs: 'ResourceCompilerKwArgs'):
"""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'] or []
# 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, '-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, '-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])
@FeatureNewKwargs('qt.preprocess', '0.49.0', ['uic_extra_arguments']) @FeatureNewKwargs('qt.preprocess', '0.49.0', ['uic_extra_arguments'])
@FeatureNewKwargs('qt.preprocess', '0.44.0', ['moc_extra_arguments']) @FeatureNewKwargs('qt.preprocess', '0.44.0', ['moc_extra_arguments'])
@FeatureNewKwargs('qt.preprocess', '0.49.0', ['rcc_extra_arguments']) @FeatureNewKwargs('qt.preprocess', '0.49.0', ['rcc_extra_arguments'])
@ -209,10 +292,11 @@ class QtBaseModule(ExtensionModule):
if not self.rcc.found(): if not self.rcc.found():
raise MesonException(err_msg.format('RCC', f'rcc-qt{self.qt_version}', self.qt_version)) raise MesonException(err_msg.format('RCC', f'rcc-qt{self.qt_version}', self.qt_version))
# custom output name set? -> one output file, multiple otherwise # custom output name set? -> one output file, multiple otherwise
rcc_kwargs: 'ResourceCompilerKwArgs' = {'sources': rcc_files, 'extra_args': rcc_extra_arguments, 'method': method}
if args: if args:
qrc_deps = [] qrc_deps = []
for i in rcc_files: for i in rcc_files:
qrc_deps += self.parse_qrc_deps(state, i) qrc_deps += self._parse_qrc_deps(state, i)
name = args[0] name = args[0]
rcc_kwargs = {'input': rcc_files, rcc_kwargs = {'input': rcc_files,
'output': name + '.cpp', 'output': name + '.cpp',
@ -222,7 +306,7 @@ class QtBaseModule(ExtensionModule):
sources.append(res_target) sources.append(res_target)
else: else:
for rcc_file in rcc_files: for rcc_file in rcc_files:
qrc_deps = self.parse_qrc_deps(state, rcc_file) qrc_deps = self._parse_qrc_deps(state, rcc_file)
if type(rcc_file) is str: if type(rcc_file) is str:
basename = os.path.basename(rcc_file) basename = os.path.basename(rcc_file)
elif type(rcc_file) is File: elif type(rcc_file) is File:
@ -293,7 +377,7 @@ class QtBaseModule(ExtensionModule):
shutil.copy2(infile_abs, outfile_abs) shutil.copy2(infile_abs, outfile_abs)
self.interpreter.add_build_def_file(infile_abs) self.interpreter.add_build_def_file(infile_abs)
rcc_file, nodes = self.qrc_nodes(state, qresource) rcc_file, nodes = self._qrc_nodes(state, qresource)
for c in nodes: for c in nodes:
if c.endswith('.qm'): if c.endswith('.qm'):
ts_files.append(c.rstrip('.qm')+'.ts') ts_files.append(c.rstrip('.qm')+'.ts')

@ -66,7 +66,11 @@ foreach qt : ['qt4', 'qt5', 'qt6']
endif endif
# Test that setting a unique name with a positional argument works # Test that setting a unique name with a positional argument works
qtmodule.preprocess(qt + 'teststuff', qresources : files(['stuff.qrc', 'stuff2.qrc']), method : get_option('method')) qtmodule.compile_resources(
name : qt + 'teststuff',
sources : files(['stuff.qrc', 'stuff2.qrc']),
method : get_option('method')
)
# Test that passing extra arguments to rcc works # Test that passing extra arguments to rcc works
# qt4-rcc and qt5-rcc take different arguments, for example qt4: ['-compress', '3']; qt5: '--compress=3' # qt4-rcc and qt5-rcc take different arguments, for example qt4: ['-compress', '3']; qt5: '--compress=3'

Loading…
Cancel
Save