python/mozbuild/mozpack/packager/l10n.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/python/mozbuild/mozpack/packager/l10n.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,174 @@
     1.4 +# This Source Code Form is subject to the terms of the Mozilla Public
     1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.7 +
     1.8 +'''
     1.9 +Replace localized parts of a packaged directory with data from a langpack
    1.10 +directory.
    1.11 +'''
    1.12 +
    1.13 +import os
    1.14 +import mozpack.path
    1.15 +from mozpack.packager.formats import (
    1.16 +    FlatFormatter,
    1.17 +    JarFormatter,
    1.18 +    OmniJarFormatter,
    1.19 +)
    1.20 +from mozpack.packager import SimplePackager
    1.21 +from mozpack.files import ManifestFile
    1.22 +from mozpack.copier import (
    1.23 +    FileCopier,
    1.24 +    Jarrer,
    1.25 +)
    1.26 +from mozpack.chrome.manifest import (
    1.27 +    ManifestLocale,
    1.28 +    ManifestEntryWithRelPath,
    1.29 +    is_manifest,
    1.30 +    ManifestChrome,
    1.31 +    Manifest,
    1.32 +)
    1.33 +from mozpack.errors import errors
    1.34 +from mozpack.packager.unpack import UnpackFinder
    1.35 +from createprecomplete import generate_precomplete
    1.36 +
    1.37 +
    1.38 +class LocaleManifestFinder(object):
    1.39 +    def __init__(self, finder):
    1.40 +        # Read all manifest entries
    1.41 +        manifests = dict((p, m) for p, m in finder.find('**/*.manifest')
    1.42 +                         if is_manifest(p))
    1.43 +        assert all(isinstance(m, ManifestFile)
    1.44 +                   for m in manifests.itervalues())
    1.45 +        self.entries = [e for m in manifests.itervalues()
    1.46 +                        for e in m if e.localized]
    1.47 +        # Find unique locales used in these manifest entries.
    1.48 +        self.locales = list(set(e.id for e in self.entries
    1.49 +                                if isinstance(e, ManifestLocale)))
    1.50 +        # Find all paths whose manifest are included by no other manifest.
    1.51 +        includes = set(mozpack.path.join(e.base, e.relpath)
    1.52 +                       for m in manifests.itervalues()
    1.53 +                       for e in m if isinstance(e, Manifest))
    1.54 +        self.bases = [mozpack.path.dirname(p)
    1.55 +                      for p in set(manifests.keys()) - includes]
    1.56 +
    1.57 +
    1.58 +def _repack(app_finder, l10n_finder, copier, formatter, non_chrome=set()):
    1.59 +    app = LocaleManifestFinder(app_finder)
    1.60 +    l10n = LocaleManifestFinder(l10n_finder)
    1.61 +
    1.62 +    # The code further below assumes there's only one locale replaced with
    1.63 +    # another one.
    1.64 +    if len(app.locales) > 1 or len(l10n.locales) > 1:
    1.65 +        errors.fatal("Multiple locales aren't supported")
    1.66 +    locale = app.locales[0]
    1.67 +    l10n_locale = l10n.locales[0]
    1.68 +
    1.69 +    # For each base directory, store what path a locale chrome package name
    1.70 +    # corresponds to.
    1.71 +    # e.g., for the following entry under app/chrome:
    1.72 +    #     locale foo en-US path/to/files
    1.73 +    # keep track that the locale path for foo in app is
    1.74 +    # app/chrome/path/to/files.
    1.75 +    l10n_paths = {}
    1.76 +    for e in l10n.entries:
    1.77 +        if isinstance(e, ManifestChrome):
    1.78 +            base = mozpack.path.basedir(e.path, app.bases)
    1.79 +            l10n_paths.setdefault(base, {})
    1.80 +            l10n_paths[base][e.name] = e.path
    1.81 +
    1.82 +    # For chrome and non chrome files or directories, store what langpack path
    1.83 +    # corresponds to a package path.
    1.84 +    paths = dict((e.path,
    1.85 +                  l10n_paths[mozpack.path.basedir(e.path, app.bases)][e.name])
    1.86 +                 for e in app.entries
    1.87 +                 if isinstance(e, ManifestEntryWithRelPath))
    1.88 +
    1.89 +    for pattern in non_chrome:
    1.90 +        for base in app.bases:
    1.91 +            path = mozpack.path.join(base, pattern)
    1.92 +            left = set(p for p, f in app_finder.find(path))
    1.93 +            right = set(p for p, f in l10n_finder.find(path))
    1.94 +            for p in right:
    1.95 +                paths[p] = p
    1.96 +            for p in left - right:
    1.97 +                paths[p] = None
    1.98 +
    1.99 +    # Create a new package, with non localized bits coming from the original
   1.100 +    # package, and localized bits coming from the langpack.
   1.101 +    packager = SimplePackager(formatter)
   1.102 +    for p, f in app_finder:
   1.103 +        if is_manifest(p):
   1.104 +            # Remove localized manifest entries.
   1.105 +            for e in [e for e in f if e.localized]:
   1.106 +                f.remove(e)
   1.107 +        # If the path is one that needs a locale replacement, use the
   1.108 +        # corresponding file from the langpack.
   1.109 +        path = None
   1.110 +        if p in paths:
   1.111 +            path = paths[p]
   1.112 +            if not path:
   1.113 +                continue
   1.114 +        else:
   1.115 +            base = mozpack.path.basedir(p, paths.keys())
   1.116 +            if base:
   1.117 +                subpath = mozpack.path.relpath(p, base)
   1.118 +                path = mozpack.path.normpath(mozpack.path.join(paths[base],
   1.119 +                                                               subpath))
   1.120 +        if path:
   1.121 +            files = [f for p, f in l10n_finder.find(path)]
   1.122 +            if not len(files):
   1.123 +                if base not in non_chrome:
   1.124 +                    errors.error("Missing file: %s" %
   1.125 +                                 os.path.join(l10n_finder.base, path))
   1.126 +            else:
   1.127 +                packager.add(path, files[0])
   1.128 +        else:
   1.129 +            packager.add(p, f)
   1.130 +
   1.131 +    # Add localized manifest entries from the langpack.
   1.132 +    l10n_manifests = []
   1.133 +    for base in set(e.base for e in l10n.entries):
   1.134 +        m = ManifestFile(base, [e for e in l10n.entries if e.base == base])
   1.135 +        path = mozpack.path.join(base, 'chrome.%s.manifest' % l10n_locale)
   1.136 +        l10n_manifests.append((path, m))
   1.137 +    bases = packager.get_bases()
   1.138 +    for path, m in l10n_manifests:
   1.139 +        base = mozpack.path.basedir(path, bases)
   1.140 +        packager.add(path, m)
   1.141 +        # Add a "manifest $path" entry in the top manifest under that base.
   1.142 +        m = ManifestFile(base)
   1.143 +        m.add(Manifest(base, mozpack.path.relpath(path, base)))
   1.144 +        packager.add(mozpack.path.join(base, 'chrome.manifest'), m)
   1.145 +
   1.146 +    packager.close()
   1.147 +
   1.148 +    # Add any remaining non chrome files.
   1.149 +    for pattern in non_chrome:
   1.150 +        for base in bases:
   1.151 +            for p, f in l10n_finder.find(mozpack.path.join(base, pattern)):
   1.152 +                if not formatter.contains(p):
   1.153 +                    formatter.add(p, f)
   1.154 +
   1.155 +    # Transplant jar preloading information.
   1.156 +    for path, log in app_finder.jarlogs.iteritems():
   1.157 +        assert isinstance(copier[path], Jarrer)
   1.158 +        copier[path].preload([l.replace(locale, l10n_locale) for l in log])
   1.159 +
   1.160 +
   1.161 +def repack(source, l10n, non_resources=[], non_chrome=set()):
   1.162 +    app_finder = UnpackFinder(source)
   1.163 +    l10n_finder = UnpackFinder(l10n)
   1.164 +    copier = FileCopier()
   1.165 +    if app_finder.kind == 'flat':
   1.166 +        formatter = FlatFormatter(copier)
   1.167 +    elif app_finder.kind == 'jar':
   1.168 +        formatter = JarFormatter(copier, optimize=app_finder.optimizedjars)
   1.169 +    elif app_finder.kind == 'omni':
   1.170 +        formatter = OmniJarFormatter(copier, app_finder.omnijar,
   1.171 +                                     optimize=app_finder.optimizedjars,
   1.172 +                                     non_resources=non_resources)
   1.173 +
   1.174 +    with errors.accumulate():
   1.175 +        _repack(app_finder, l10n_finder, copier, formatter, non_chrome)
   1.176 +    copier.copy(source, skip_if_older=False)
   1.177 +    generate_precomplete(source)

mercurial