Skip to content

Commit

Permalink
i18n: use real build/install targets for gmo files
Browse files Browse the repository at this point in the history
Don't just create a .PHONY target which runs a script that magically
generates files ninja doesn't know about. It results in untracked files,
and `meson install` has to run additional commands instead of copying
over files, and then cannot track them to uninstall them later.

I'm not even really sure why it was originally done via a proxy script,
most likely bad legacy design. This is after all one of the oldest
modules...

One side effect of this is that meson doesn't know how to rename
build.CustomTarget files on install (only data files are supported?),
and every file needs to be installed as "domainname.mo" so it must be
named that in-tree too. To prevent clashes, every locale gets its own
locale-specific subdirectory.

Once we are doing that anyway, we can output them to the actual
structure required by the gettext family of functions, and
bindtextdomain() can therefore point to this location if desired. This
might be useful for running localized programs from the build tree.
  • Loading branch information
eli-schwartz committed Aug 9, 2021
1 parent 27b3195 commit 487d45c
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 51 deletions.
42 changes: 23 additions & 19 deletions mesonbuild/modules/i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from os import path
from .. import coredata, mesonlib, build, mlog
from ..mesonlib import MesonException
from ..scripts.gettext import read_linguas
from . import ModuleReturnValue
from . import ExtensionModule
from ..interpreterbase import permittedKwargs, FeatureNew, FeatureNewKwargs
Expand Down Expand Up @@ -141,6 +142,7 @@ def gettext(self, state, args, kwargs):
languages = mesonlib.stringlistify(kwargs.get('languages', []))
datadirs = self._get_data_dirs(state, mesonlib.stringlistify(kwargs.get('data_dirs', [])))
extra_args = mesonlib.stringlistify(kwargs.get('args', []))
targets = []

preset = kwargs.pop('preset', None)
if preset:
Expand All @@ -161,11 +163,27 @@ def gettext(self, state, args, kwargs):
if extra_args:
potargs.append(extra_args)
pottarget = build.RunTarget(packagename + '-pot', potargs, [], state.subdir, state.subproject)
targets.append(pottarget)

gmoargs = state.environment.get_build_command() + ['--internal', 'gettext', 'gen_gmo']
if lang_arg:
gmoargs.append(lang_arg)
gmotarget = build.RunTarget(packagename + '-gmo', gmoargs, [], state.subdir, state.subproject)
install = kwargs.get('install', True)
install_dir = kwargs.get('install_dir', state.environment.coredata.get_option(mesonlib.OptionKey('localedir')))
if not languages:
languages = read_linguas(path.join(state.environment.source_dir, state.subdir))
for l in languages:
po_file = mesonlib.File.from_source_file(state.environment.source_dir,
state.subdir, l+'.po')
gmo_kwargs = {'command': ['msgfmt', '@INPUT@', '-o', '@OUTPUT@'],
'input': po_file,
'output': packagename+'.mo',
'install': install,
# We have multiple files all installed as packagename+'.mo' in different install subdirs.
# What we really wanted to do, probably, is have a rename: kwarg, but that's not available
# to custom_targets. Crude hack: set the build target's subdir manually.
# Bonus: the build tree has something usable as an uninstalled bindtextdomain() target dir.
'install_dir': path.join(install_dir, l, 'LC_MESSAGES'),
}
gmotarget = build.CustomTarget(l+'.mo', path.join(state.subdir, l, 'LC_MESSAGES'), state.subproject, gmo_kwargs)
targets.append(gmotarget)

updatepoargs = state.environment.get_build_command() + ['--internal', 'gettext', 'update_po', pkg_arg]
if lang_arg:
Expand All @@ -175,21 +193,7 @@ def gettext(self, state, args, kwargs):
if extra_args:
updatepoargs.append(extra_args)
updatepotarget = build.RunTarget(packagename + '-update-po', updatepoargs, [], state.subdir, state.subproject)

targets = [pottarget, gmotarget, updatepotarget]

install = kwargs.get('install', True)
if install:
install_dir = kwargs.get('install_dir', state.environment.coredata.get_option(mesonlib.OptionKey('localedir')))
script = state.environment.get_build_command()
args = ['--internal', 'gettext', 'install',
'--subdir=' + state.subdir,
'--localedir=' + install_dir,
pkg_arg]
if lang_arg:
args.append(lang_arg)
iscript = state.backend.get_executable_serialisation(script + args)
targets.append(iscript)
targets.append(updatepotarget)

return ModuleReturnValue(None, targets)

Expand Down
32 changes: 0 additions & 32 deletions mesonbuild/scripts/gettext.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@
# limitations under the License.

import os
import shutil
import argparse
import subprocess
from . import destdir_join
import typing as T

parser = argparse.ArgumentParser()
Expand Down Expand Up @@ -61,12 +59,6 @@ def run_potgen(src_sub: str, pkgname: str, datadirs: str, args: T.List[str]) ->
'-D', os.environ['MESON_SOURCE_ROOT'], '-k_', '-o', ofile] + args,
env=child_env)

def gen_gmo(src_sub: str, bld_sub: str, langs: T.List[str]) -> int:
for l in langs:
subprocess.check_call(['msgfmt', os.path.join(src_sub, l + '.po'),
'-o', os.path.join(bld_sub, l + '.gmo')])
return 0

def update_po(src_sub: str, pkgname: str, langs: T.List[str]) -> int:
potfile = os.path.join(src_sub, pkgname + '.pot')
for l in langs:
Expand All @@ -77,19 +69,6 @@ def update_po(src_sub: str, pkgname: str, langs: T.List[str]) -> int:
subprocess.check_call(['msginit', '--input', potfile, '--output-file', pofile, '--locale', l, '--no-translator'])
return 0

def do_install(src_sub: str, bld_sub: str, dest: str, pkgname: str, langs: T.List[str]) -> int:
for l in langs:
srcfile = os.path.join(bld_sub, l + '.gmo')
outfile = os.path.join(dest, l, 'LC_MESSAGES',
pkgname + '.mo')
tempfile = outfile + '.tmp'
os.makedirs(os.path.dirname(outfile), exist_ok=True)
shutil.copy2(srcfile, tempfile)
os.replace(tempfile, outfile)
if not os.getenv('MESON_INSTALL_QUIET', False):
print(f'Installing {srcfile} to {outfile}')
return 0

def run(args: T.List[str]) -> int:
options = parser.parse_args(args)
subcmd = options.command
Expand All @@ -99,27 +78,16 @@ def run(args: T.List[str]) -> int:
if options.subdir:
subdir = options.subdir
src_sub = os.path.join(os.environ['MESON_SOURCE_ROOT'], subdir)
bld_sub = os.path.join(os.environ['MESON_BUILD_ROOT'], subdir)

if not langs:
langs = read_linguas(src_sub)

if subcmd == 'pot':
return run_potgen(src_sub, options.pkgname, options.datadirs, extra_args)
elif subcmd == 'gen_gmo':
return gen_gmo(src_sub, bld_sub, langs)
elif subcmd == 'update_po':
if run_potgen(src_sub, options.pkgname, options.datadirs, extra_args) != 0:
return 1
return update_po(src_sub, options.pkgname, langs)
elif subcmd == 'install':
destdir = os.environ.get('DESTDIR', '')
dest = destdir_join(destdir, os.path.join(os.environ['MESON_INSTALL_PREFIX'],
options.localedir))
if gen_gmo(src_sub, bld_sub, langs) != 0:
return 1
do_install(src_sub, bld_sub, dest, options.pkgname, langs)
else:
print('Unknown subcommand.')
return 1
return 0

0 comments on commit 487d45c

Please sign in to comment.