qt module: add qresource support to compile_translations

A common pattern in Qt5 applications is to embed translations in the
executable using the qresource system. In this case, the list of
translation files is already available in the .qrc file and there's no
good reason to duplicate this info in meson.build.

Let compile_translations optionally take a qrc input, in which case it
will go straight to generating the relevant translations and
rcc-generated .cpp, and directly return the thing users actually care
about -- the .cpp for linking.
pull/7651/head
Eli Schwartz 5 years ago committed by Jussi Pakkanen
parent fc13c90de3
commit 1de1cc22e2
  1. 17
      docs/markdown/Qt5-module.md
  2. 19
      docs/markdown/snippets/qt_compile_translations_from_qrc.md
  3. 60
      mesonbuild/modules/qt.py

@ -22,6 +22,12 @@ This method generates the necessary targets to build translation files with lrel
- `install` when true, this target is installed during the install step (optional).
- `install_dir` directory to install to (optional).
- `build_by_default` when set to true, to have this target be built by default, that is, when invoking `meson compile`; the default value is false (optional).
- `qresource` rcc source file to extract ts_files from; cannot be used with ts_files kwarg. Available since v0.56.0.
- `rcc_extra_arguments`, any additional arguments to `rcc` (optional), when used with `qresource. Available since v0.56.0.
Returns either: a list of custom targets for the compiled translations, or, if
using a `qresource` file, a single custom target containing the processed
source file, which should be passed to a main build target.
## has_tools
@ -71,3 +77,14 @@ executable('myprog', 'main.cpp', 'myclass.cpp', moc_files,
include_directories: inc,
dependencies : qt5_dep)
```
Sometimes, translations are embedded inside the binary using qresource files.
In this case the ts files do not need to be explicitly listed. For example:
```meson
qt5 = import('qt5')
qt5_dep = dependency('qt5', modules: ['Core', 'Gui'])
lang_cpp = qt5.compile_translations(qresource: 'lang.qrc')
executable('myprog', 'main.cpp', lang_cpp,
dependencies: qt5_dep)
```

@ -0,0 +1,19 @@
## Qt5 compile_translations now supports qresource preprocessing
When using qtmod.preprocess() in combination with qtmod.compile_translations()
to embed translations using rcc, it is no longer required to do this:
```meson
ts_files = ['list', 'of', 'files']
qtmod.compile_translations(ts_files)
# lang.qrc also contains the duplicated list of files
lang_cpp = qtmod.preprocess(qresources: 'lang.qrc')
```
Instead, use:
```meson
lang_cpp = qtmod.compile_translations(qresource: 'lang.qrc')
```
which will automatically detect and generate the needed compile_translations
targets.

@ -13,6 +13,7 @@
# limitations under the License.
import os
import shutil
from .. import mlog
from .. import build
from ..mesonlib import MesonException, extract_as_list, File, unholder, version_compare
@ -59,7 +60,7 @@ class QtBaseModule(ExtensionModule):
self.rcc = NonExistingExternalProgram(name='rcc' + suffix)
self.lrelease = NonExistingExternalProgram(name='lrelease' + suffix)
def parse_qrc(self, state, rcc_file):
def qrc_nodes(self, state, rcc_file):
if type(rcc_file) is str:
abspath = os.path.join(state.environment.source_dir, state.subdir, rcc_file)
rcc_dirname = os.path.dirname(abspath)
@ -76,7 +77,16 @@ class QtBaseModule(ExtensionModule):
mlog.warning("malformed rcc file: ", os.path.join(state.subdir, rcc_file))
break
else:
resource_path = child.text
result.append(child.text)
return rcc_dirname, result
except Exception:
return []
def parse_qrc_deps(self, state, rcc_file):
rcc_dirname, nodes = self.qrc_nodes(state, rcc_file)
result = []
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
@ -100,9 +110,7 @@ class QtBaseModule(ExtensionModule):
# b)
else:
result.append(File(is_built=False, subdir=state.subdir, fname=path_from_rcc))
return result
except Exception:
return []
return result
@noPosargs
@permittedKwargs({'method', 'required'})
@ -142,7 +150,7 @@ class QtBaseModule(ExtensionModule):
if args:
qrc_deps = []
for i in rcc_files:
qrc_deps += self.parse_qrc(state, i)
qrc_deps += self.parse_qrc_deps(state, i)
name = args[0]
rcc_kwargs = {'input': rcc_files,
'output': name + '.cpp',
@ -152,7 +160,7 @@ class QtBaseModule(ExtensionModule):
sources.append(res_target)
else:
for rcc_file in rcc_files:
qrc_deps = self.parse_qrc(state, rcc_file)
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:
@ -205,15 +213,42 @@ class QtBaseModule(ExtensionModule):
return ModuleReturnValue(sources, sources)
@FeatureNew('qt.compile_translations', '0.44.0')
@permittedKwargs({'ts_files', 'install', 'install_dir', 'build_by_default', 'method'})
@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']]
ts_files, install_dir = [extract_as_list(kwargs, c, pop=True) for c in ['ts_files', 'install_dir']]
qresource = kwargs.get('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())
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)
for c in nodes:
if c.endswith('.qm'):
ts_files.append(c.rstrip('.qm')+'.ts')
else:
raise MesonException('qt.compile_translations: qresource can only contain ts files, found {}'.format(c))
results = self.preprocess(state, [], {'qresources': qresource, 'rcc_extra_arguments': kwargs.get('rcc_extra_arguments', [])})
self._detect_tools(state.environment, kwargs.get('method', 'auto'))
translations = []
for ts in ts_files:
if not self.lrelease.found():
raise MesonException('qt.compile_translations: ' +
self.lrelease.name + ' not found')
if qresource:
outdir = os.path.dirname(os.path.normpath(os.path.join(state.subdir, ts)))
ts = os.path.basename(ts)
else:
outdir = state.subdir
cmd = [self.lrelease, '@INPUT@', '-qm', '@OUTPUT@']
lrelease_kwargs = {'output': '@BASENAME@.qm',
'input': ts,
@ -222,6 +257,9 @@ class QtBaseModule(ExtensionModule):
'command': cmd}
if install_dir is not None:
lrelease_kwargs['install_dir'] = install_dir
lrelease_target = build.CustomTarget('qt{}-compile-{}'.format(self.qt_version, ts), state.subdir, state.subproject, lrelease_kwargs)
lrelease_target = build.CustomTarget('qt{}-compile-{}'.format(self.qt_version, ts), outdir, state.subproject, lrelease_kwargs)
translations.append(lrelease_target)
return ModuleReturnValue(translations, translations)
if qresource:
return ModuleReturnValue(results.return_value[0], [results.new_objects, translations])
else:
return ModuleReturnValue(translations, translations)

Loading…
Cancel
Save