From 08928821ec00bb7e74cbf791e1f42eefd134379e Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Sat, 5 Mar 2022 21:10:06 -0500 Subject: [PATCH] i18n module: detect gettext tools at configure time Use this instead of shutil.which to detect whether they will be available, and pass the ExternalProgram object to CustomTarget invocations, or else make use of the new functionality to specify the correct program path in wrapper scripts. Drop duplicate reporting for itstool missing. Since we use find_program in required mode, its absence is already fatal, and already has a really good error description. --- mesonbuild/modules/i18n.py | 40 +++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index 210dd93c8..1eeb9adcf 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -11,9 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations from os import path -import shutil import typing as T from . import ExtensionModule, ModuleReturnValue @@ -131,15 +131,18 @@ class I18nModule(ExtensionModule): 'gettext': self.gettext, 'itstool_join': self.itstool_join, }) + self.tools: T.Dict[str, T.Optional[ExternalProgram]] = { + 'itstool': None, + 'msgfmt': None, + 'msginit': None, + 'msgmerge': None, + 'xgettext': None, + } @staticmethod def nogettext_warning() -> None: mlog.warning('Gettext not found, all translation targets will be ignored.', once=True) - @staticmethod - def noitstool_error() -> T.NoReturn: - raise mesonlib.MesonException('Did not find itstool. Please install it to continue.') - @staticmethod def _get_data_dirs(state: 'ModuleState', dirs: T.Iterable[str]) -> T.List[str]: """Returns source directories of relative paths""" @@ -162,7 +165,9 @@ class I18nModule(ExtensionModule): KwargInfo('type', str, default='xml', validator=in_set_validator({'xml', 'desktop'})), ) def merge_file(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'MergeFile') -> ModuleReturnValue: - if not shutil.which('xgettext'): + if self.tools['msgfmt'] is None: + self.tools['msgfmt'] = state.find_program('msgfmt', required=False, for_machine=mesonlib.MachineChoice.BUILD) + if not self.tools['msgfmt'].found(): self.nogettext_warning() return ModuleReturnValue(None, []) podir = path.join(state.build_to_src, state.subdir, kwargs['po_dir']) @@ -175,6 +180,7 @@ class I18nModule(ExtensionModule): command.extend(state.environment.get_build_command()) command.extend([ '--internal', 'msgfmthelper', + '--msgfmt=' + self.tools['msgfmt'].get_path(), ]) if datadirs: command.append(datadirs) @@ -218,9 +224,13 @@ class I18nModule(ExtensionModule): ), ) def gettext(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'Gettext') -> ModuleReturnValue: - if not shutil.which('xgettext'): - self.nogettext_warning() - return ModuleReturnValue(None, []) + for tool in ['msgfmt', 'msginit', 'msgmerge', 'xgettext']: + if self.tools[tool] is None: + self.tools[tool] = state.find_program(tool, required=False, for_machine=mesonlib.MachineChoice.BUILD) + # still not found? + if not self.tools[tool].found(): + self.nogettext_warning() + return ModuleReturnValue(None, []) packagename = args[0] pkg_arg = f'--pkgname={packagename}' @@ -246,6 +256,7 @@ class I18nModule(ExtensionModule): potargs.append(datadirs) if extra_arg: potargs.append(extra_arg) + potargs.append('--xgettext=' + self.tools['xgettext'].get_path()) pottarget = build.RunTarget(packagename + '-pot', potargs, [], state.subdir, state.subproject) targets.append(pottarget) @@ -261,7 +272,7 @@ class I18nModule(ExtensionModule): f'{packagename}-{l}.mo', path.join(state.subdir, l, 'LC_MESSAGES'), state.subproject, - ['msgfmt', '@INPUT@', '-o', '@OUTPUT@'], + [self.tools['msgfmt'], '@INPUT@', '-o', '@OUTPUT@'], [po_file], [f'{packagename}.mo'], install=install, @@ -285,6 +296,8 @@ class I18nModule(ExtensionModule): updatepoargs.append(datadirs) if extra_arg: updatepoargs.append(extra_arg) + for tool in ['msginit', 'msgmerge']: + updatepoargs.append(f'--{tool}=' + self.tools[tool].get_path()) updatepotarget = build.RunTarget(packagename + '-update-po', updatepoargs, [], state.subdir, state.subproject) targets.append(updatepotarget) @@ -305,8 +318,8 @@ class I18nModule(ExtensionModule): KwargInfo('mo_targets', ContainerTypeInfo(list, build.CustomTarget), required=True), ) def itstool_join(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs: 'ItsJoinFile') -> ModuleReturnValue: - if not shutil.which('itstool'): - self.noitstool_error() + if self.tools['itstool'] is None: + self.tools['itstool'] = state.find_program('itstool', for_machine=mesonlib.MachineChoice.BUILD) mo_targets = kwargs['mo_targets'] its_files = kwargs.get('its_files', []) @@ -320,7 +333,8 @@ class I18nModule(ExtensionModule): command.extend([ '--internal', 'itstool', 'join', '-i', '@INPUT@', - '-o', '@OUTPUT@' + '-o', '@OUTPUT@', + '--itstool=' + self.tools['itstool'].get_path(), ]) if its_files: for fname in its_files: