Bug 1393147 - Update the new langpack-webext manifest scheme. r=kmag draft
authorZibi Braniecki <zbraniecki@mozilla.com>
Mon, 28 Aug 2017 10:42:31 -0700
changeset 655532 f4ca6f61d31db8eb442b586c7e511dff4b616eae
parent 655315 5860fc2b329f62e10cc1e6c7a97fb75b05bba167
child 728861 1059fbeaa7f05c12bddd9b2c739a323636f65fe4
push id76903
push userbmo:gandalf@aviary.pl
push dateWed, 30 Aug 2017 03:59:48 +0000
reviewerskmag
bugs1393147
milestone57.0a1
Bug 1393147 - Update the new langpack-webext manifest scheme. r=kmag MozReview-Commit-ID: 80g61YvsiSk
python/mozbuild/mozbuild/action/langpack_manifest.py
--- a/python/mozbuild/mozbuild/action/langpack_manifest.py
+++ b/python/mozbuild/mozbuild/action/langpack_manifest.py
@@ -13,18 +13,16 @@ from __future__ import absolute_import
 import argparse
 import sys
 import os
 import json
 import io
 from mozpack.chrome.manifest import (
     Manifest,
     ManifestLocale,
-    ManifestOverride,
-    ManifestResource,
     parse_manifest,
 )
 from mozbuild.preprocessor import Preprocessor
 
 
 def write_file(path, content):
     with io.open(path, 'w', encoding='utf-8') as out:
         out.write(content + '\n')
@@ -76,20 +74,94 @@ def convert_contributors(str):
     str = str.replace('<em:contributor>', '')
     tokens = str.split('</em:contributor>')
     tokens = map(lambda t: t.strip(), tokens)
     tokens = filter(lambda t: t != '', tokens)
     return ', '.join(tokens)
 
 
 ###
+# Build the manifest author string based on the author string
+# and optionally adding the list of contributors, if provided.
+#
+# Args:
+#    author (str)       - a string with the name of the author
+#    contributors (str) - RDF based list of contributors from a chrome manifest
+#
+# Returns:
+#    (str) - a string to be placed in the author field of the manifest.json
+#
+# Example:
+#    s = build_author_string(
+#    'Aviary.pl',
+#    '
+#        <em:contributor>Marek Wawoczny</em:contributor>
+#        <em:contributor>Marek Stepien</em:contributor>
+#    ')
+#    s == 'Aviary.pl (contributors: Marek Wawoczny, Marek Stepien)'
+###
+def build_author_string(author, contributors):
+    contrib = convert_contributors(contributors)
+    if len(contrib) == 0:
+        return author
+    return '{0} (contributors: {1})'.format(author, contrib)
+
+
+##
+# Converts the list of chrome manifest entry flags to the list of platforms
+# for the langpack manifest.
+#
+# The list of result platforms is taken from AppConstants.platform.
+#
+# Args:
+#    flags (FlagList) - a list of Chrome Manifest entry flags
+#
+# Returns:
+#    (list) - a list of platform the entry applies to
+#
+# Example:
+#    str(flags) == "os==MacOS os==Windows"
+#    platforms = convert_entry_flags_to_platform_codes(flags)
+#    platforms == ['mac', 'win']
+#
+# The method supports only `os` flag name and equality operator.
+# It will throw if tried with other flags or operators.
+###
+def convert_entry_flags_to_platform_codes(flags):
+    if not flags:
+        return None
+
+    ret = []
+    for key in flags:
+        if key != 'os':
+            raise Exception('Unknown flag name')
+
+        for value in flags[key].values:
+            if value[0] != '==':
+                raise Exception('Inequality flag cannot be converted')
+
+            if value[1] == 'Android':
+                ret.append('android')
+            elif value[1] == 'LikeUnix':
+                ret.append('linux')
+            elif value[1] == 'Darwin':
+                ret.append('macosx')
+            elif value[1] == 'WINNT':
+                ret.append('win')
+            else:
+                raise Exception('Unknown flag value {0}'.format(value[1]))
+
+    return ret
+
+
+###
 # Recursively parse a chrome manifest file appending new entries
 # to the result list
 #
-# The function can handle three entry types: 'locale', 'override' and 'resource'
+# The function can handle two entry types: 'locale' and 'manifest'
 #
 # Args:
 #    path           (str)  - a path to a chrome manifest
 #    base_path      (str)  - a path to the base directory all chrome registry
 #                            entries will be relative to
 #    chrome_entries (list) - a list to which entries will be appended to
 #
 # Example:
@@ -97,22 +169,24 @@ def convert_contributors(str):
 #    chrome_entries = {}
 #    parse_manifest('./chrome.manifest', './', chrome_entries)
 #
 #    chrome_entries == [
 #        {
 #            'type': 'locale',
 #            'alias': 'devtools',
 #            'locale': 'pl',
+#            'platforms': null,
 #            'path': 'chrome/pl/locale/pl/devtools/'
 #        },
 #        {
 #            'type': 'locale',
 #            'alias': 'autoconfig',
 #            'locale': 'pl',
+#            'platforms': ['win', 'mac'],
 #            'path': 'chrome/pl/locale/pl/autoconfig/'
 #        },
 #    ]
 ###
 def parse_chrome_manifest(path, base_path, chrome_entries):
     for entry in parse_manifest(None, path):
         if isinstance(entry, Manifest):
             parse_chrome_manifest(
@@ -120,38 +194,27 @@ def parse_chrome_manifest(path, base_pat
                 base_path,
                 chrome_entries
             )
         elif isinstance(entry, ManifestLocale):
             chrome_entries.append({
                 'type': 'locale',
                 'alias': entry.name,
                 'locale': entry.id,
+                'platforms': convert_entry_flags_to_platform_codes(entry.flags),
                 'path': os.path.join(
                     os.path.relpath(
                         os.path.dirname(path),
                         base_path
                     ),
                     entry.relpath
                 )
             })
-        elif isinstance(entry, ManifestOverride):
-            chrome_entries.append({
-                'type': 'override',
-                'real-path': entry.overloaded,
-                'overlay-path': entry.overload
-            })
-        elif isinstance(entry, ManifestResource):
-            chrome_entries.append({
-                'type': 'resource',
-                'alias': entry.name,
-                'path': entry.target
-            })
         else:
-            raise Exception('Unknown type %s' % entry[0])
+            raise Exception('Unknown type {0}'.format(entry.name))
 
 
 ###
 # Generates a new web manifest dict with values specific for a language pack.
 #
 # Args:
 #    locstr         (str)  - A string with a comma separated list of locales
 #                            for which resources are embedded in the
@@ -163,81 +226,99 @@ def parse_chrome_manifest(path, base_pat
 #
 # Returns:
 #    (dict) - a web manifest
 #
 # Example:
 #    manifest = create_webmanifest(
 #      ['pl'],
 #      '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}',
-#      '55.0a1',
+#      '57.0',
 #      {'MOZ_LANG_TITLE': 'Polski'},
 #      chrome_entries
 #    )
 #    manifest == {
-#        'languages': ['pl'],
+#        'languages': {
+#            'pl': {
+#                'version': '201709121481',
+#                'resources': None,
+#                'chrome_resources': {
+#                    'alert': 'chrome/pl/locale/pl/alert/',
+#                    'branding': 'browser/chrome/pl/locale/global/',
+#                    'global-platform': {
+#                      'macosx': 'chrome/pl/locale/pl/global-platform/mac/',
+#                      'win': 'chrome/pl/locale/pl/global-platform/win/',
+#                      'linux': 'chrome/pl/locale/pl/global-platform/unix/',
+#                      'android': 'chrome/pl/locale/pl/global-platform/unix/',
+#                    },
+#                    'forms': 'browser/chrome/pl/locale/forms/',
+#                    ...
+#                }
+#            }
+#        },
 #        'applications': {
 #            'gecko':  {
-#                'strict_min_version': '55.0a1',
-#                'id': '',
+#                'strict_min_version': '57.0',
+#                'strict_max_version': '57.0.*',
+#                'id': 'langpack-pl@mozilla.org',
 #            }
 #        },
-#        'version': '55.0a1',
+#        'version': '57.0',
 #        'name': 'Polski Language Pack',
 #        ...
 #    }
 ###
 def create_webmanifest(locstr, appver, defines, chrome_entries):
     locales = map(lambda loc: loc.strip(), locstr.split(','))
     main_locale = locales[0]
 
-    contributors = convert_contributors(defines['MOZ_LANGPACK_CONTRIBUTORS'])
+    author = build_author_string(
+        defines['MOZ_LANGPACK_CREATOR'],
+        defines['MOZ_LANGPACK_CONTRIBUTORS']
+    )
 
     manifest = {
-        'langpack-id': main_locale,
+        'langpack_id': main_locale,
         'manifest_version': 2,
         'applications': {
             'gecko': {
-                'id': "langpack-" + main_locale + "@mozilla.org",
-                'strict_min_version': appver
+                'id': 'langpack-{0}@firefox.mozilla.org'.format(main_locale),
+                'strict_min_version': appver,
+                'strict_max_version': '{0}.*'.format(appver)
             }
         },
-        'name': defines['MOZ_LANG_TITLE'] + ' Language Pack',
-        'description': 'Language pack for Firefox for ' + main_locale,
+        'name': '{0} Language Pack'.format(defines['MOZ_LANG_TITLE']),
+        'description': 'Language pack for Firefox for {0}'.format(main_locale),
         'version': appver,
-        'languages': locales,
-        'author': '%s (contributors: %s)' % (defines['MOZ_LANGPACK_CREATOR'], contributors),
-        'chrome_entries': [
-        ]
+        'languages': {},
+        'author': author
     }
 
+    cr = {}
     for entry in chrome_entries:
-        line = ''
         if entry['type'] == 'locale':
-            line = '%s %s %s %s' % (
-                entry['type'],
-                entry['alias'],
-                entry['locale'],
-                entry['path']
-            )
-        elif entry['type'] == 'override':
-            line = '%s %s %s' % (
-                entry['type'],
-                entry['real-path'],
-                entry['overlay-path']
-            )
-        elif entry['type'] == 'resource':
-            line = '%s %s %s' % (
-                entry['type'],
-                entry['alias'],
-                entry['path']
-            )
+            platforms = entry['platforms']
+            if platforms:
+                if entry['alias'] not in cr:
+                    cr[entry['alias']] = {}
+                for platform in platforms:
+                    cr[entry['alias']][platform] = entry['path']
+            else:
+                assert entry['alias'] not in cr
+                cr[entry['alias']] = entry['path']
         else:
-            raise Exception('Unknown type %s' % entry['type'])
-        manifest['chrome_entries'].append(line)
+            raise Exception('Unknown type {0}'.format(entry['type']))
+
+    for loc in locales:
+        manifest['languages'][loc] = {
+            'version': appver,
+            'resources': None,
+            'chrome_resources': cr
+        }
+
     return json.dumps(manifest, indent=2, ensure_ascii=False, encoding='utf8')
 
 
 def main(args):
     parser = argparse.ArgumentParser()
     parser.add_argument('--locales',
                         help='List of language codes provided by the langpack')
     parser.add_argument('--appver',