michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: ''' michael@0: Replace localized parts of a packaged directory with data from a langpack michael@0: directory. michael@0: ''' michael@0: michael@0: import os michael@0: import mozpack.path michael@0: from mozpack.packager.formats import ( michael@0: FlatFormatter, michael@0: JarFormatter, michael@0: OmniJarFormatter, michael@0: ) michael@0: from mozpack.packager import SimplePackager michael@0: from mozpack.files import ManifestFile michael@0: from mozpack.copier import ( michael@0: FileCopier, michael@0: Jarrer, michael@0: ) michael@0: from mozpack.chrome.manifest import ( michael@0: ManifestLocale, michael@0: ManifestEntryWithRelPath, michael@0: is_manifest, michael@0: ManifestChrome, michael@0: Manifest, michael@0: ) michael@0: from mozpack.errors import errors michael@0: from mozpack.packager.unpack import UnpackFinder michael@0: from createprecomplete import generate_precomplete michael@0: michael@0: michael@0: class LocaleManifestFinder(object): michael@0: def __init__(self, finder): michael@0: # Read all manifest entries michael@0: manifests = dict((p, m) for p, m in finder.find('**/*.manifest') michael@0: if is_manifest(p)) michael@0: assert all(isinstance(m, ManifestFile) michael@0: for m in manifests.itervalues()) michael@0: self.entries = [e for m in manifests.itervalues() michael@0: for e in m if e.localized] michael@0: # Find unique locales used in these manifest entries. michael@0: self.locales = list(set(e.id for e in self.entries michael@0: if isinstance(e, ManifestLocale))) michael@0: # Find all paths whose manifest are included by no other manifest. michael@0: includes = set(mozpack.path.join(e.base, e.relpath) michael@0: for m in manifests.itervalues() michael@0: for e in m if isinstance(e, Manifest)) michael@0: self.bases = [mozpack.path.dirname(p) michael@0: for p in set(manifests.keys()) - includes] michael@0: michael@0: michael@0: def _repack(app_finder, l10n_finder, copier, formatter, non_chrome=set()): michael@0: app = LocaleManifestFinder(app_finder) michael@0: l10n = LocaleManifestFinder(l10n_finder) michael@0: michael@0: # The code further below assumes there's only one locale replaced with michael@0: # another one. michael@0: if len(app.locales) > 1 or len(l10n.locales) > 1: michael@0: errors.fatal("Multiple locales aren't supported") michael@0: locale = app.locales[0] michael@0: l10n_locale = l10n.locales[0] michael@0: michael@0: # For each base directory, store what path a locale chrome package name michael@0: # corresponds to. michael@0: # e.g., for the following entry under app/chrome: michael@0: # locale foo en-US path/to/files michael@0: # keep track that the locale path for foo in app is michael@0: # app/chrome/path/to/files. michael@0: l10n_paths = {} michael@0: for e in l10n.entries: michael@0: if isinstance(e, ManifestChrome): michael@0: base = mozpack.path.basedir(e.path, app.bases) michael@0: l10n_paths.setdefault(base, {}) michael@0: l10n_paths[base][e.name] = e.path michael@0: michael@0: # For chrome and non chrome files or directories, store what langpack path michael@0: # corresponds to a package path. michael@0: paths = dict((e.path, michael@0: l10n_paths[mozpack.path.basedir(e.path, app.bases)][e.name]) michael@0: for e in app.entries michael@0: if isinstance(e, ManifestEntryWithRelPath)) michael@0: michael@0: for pattern in non_chrome: michael@0: for base in app.bases: michael@0: path = mozpack.path.join(base, pattern) michael@0: left = set(p for p, f in app_finder.find(path)) michael@0: right = set(p for p, f in l10n_finder.find(path)) michael@0: for p in right: michael@0: paths[p] = p michael@0: for p in left - right: michael@0: paths[p] = None michael@0: michael@0: # Create a new package, with non localized bits coming from the original michael@0: # package, and localized bits coming from the langpack. michael@0: packager = SimplePackager(formatter) michael@0: for p, f in app_finder: michael@0: if is_manifest(p): michael@0: # Remove localized manifest entries. michael@0: for e in [e for e in f if e.localized]: michael@0: f.remove(e) michael@0: # If the path is one that needs a locale replacement, use the michael@0: # corresponding file from the langpack. michael@0: path = None michael@0: if p in paths: michael@0: path = paths[p] michael@0: if not path: michael@0: continue michael@0: else: michael@0: base = mozpack.path.basedir(p, paths.keys()) michael@0: if base: michael@0: subpath = mozpack.path.relpath(p, base) michael@0: path = mozpack.path.normpath(mozpack.path.join(paths[base], michael@0: subpath)) michael@0: if path: michael@0: files = [f for p, f in l10n_finder.find(path)] michael@0: if not len(files): michael@0: if base not in non_chrome: michael@0: errors.error("Missing file: %s" % michael@0: os.path.join(l10n_finder.base, path)) michael@0: else: michael@0: packager.add(path, files[0]) michael@0: else: michael@0: packager.add(p, f) michael@0: michael@0: # Add localized manifest entries from the langpack. michael@0: l10n_manifests = [] michael@0: for base in set(e.base for e in l10n.entries): michael@0: m = ManifestFile(base, [e for e in l10n.entries if e.base == base]) michael@0: path = mozpack.path.join(base, 'chrome.%s.manifest' % l10n_locale) michael@0: l10n_manifests.append((path, m)) michael@0: bases = packager.get_bases() michael@0: for path, m in l10n_manifests: michael@0: base = mozpack.path.basedir(path, bases) michael@0: packager.add(path, m) michael@0: # Add a "manifest $path" entry in the top manifest under that base. michael@0: m = ManifestFile(base) michael@0: m.add(Manifest(base, mozpack.path.relpath(path, base))) michael@0: packager.add(mozpack.path.join(base, 'chrome.manifest'), m) michael@0: michael@0: packager.close() michael@0: michael@0: # Add any remaining non chrome files. michael@0: for pattern in non_chrome: michael@0: for base in bases: michael@0: for p, f in l10n_finder.find(mozpack.path.join(base, pattern)): michael@0: if not formatter.contains(p): michael@0: formatter.add(p, f) michael@0: michael@0: # Transplant jar preloading information. michael@0: for path, log in app_finder.jarlogs.iteritems(): michael@0: assert isinstance(copier[path], Jarrer) michael@0: copier[path].preload([l.replace(locale, l10n_locale) for l in log]) michael@0: michael@0: michael@0: def repack(source, l10n, non_resources=[], non_chrome=set()): michael@0: app_finder = UnpackFinder(source) michael@0: l10n_finder = UnpackFinder(l10n) michael@0: copier = FileCopier() michael@0: if app_finder.kind == 'flat': michael@0: formatter = FlatFormatter(copier) michael@0: elif app_finder.kind == 'jar': michael@0: formatter = JarFormatter(copier, optimize=app_finder.optimizedjars) michael@0: elif app_finder.kind == 'omni': michael@0: formatter = OmniJarFormatter(copier, app_finder.omnijar, michael@0: optimize=app_finder.optimizedjars, michael@0: non_resources=non_resources) michael@0: michael@0: with errors.accumulate(): michael@0: _repack(app_finder, l10n_finder, copier, formatter, non_chrome) michael@0: copier.copy(source, skip_if_older=False) michael@0: generate_precomplete(source)