summaryrefslogtreecommitdiffstats
path: root/chromium/components/policy/tools/template_writers/template_formatter.py
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/policy/tools/template_writers/template_formatter.py')
-rwxr-xr-xchromium/components/policy/tools/template_writers/template_formatter.py274
1 files changed, 274 insertions, 0 deletions
diff --git a/chromium/components/policy/tools/template_writers/template_formatter.py b/chromium/components/policy/tools/template_writers/template_formatter.py
new file mode 100755
index 00000000000..9af88164b66
--- /dev/null
+++ b/chromium/components/policy/tools/template_writers/template_formatter.py
@@ -0,0 +1,274 @@
+#!/usr/bin/env python3
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+'''Takes translated policy_template.json files as input, applies template
+writers and emits various template and doc files (admx, html, json etc.).
+'''
+
+import argparse
+import codecs
+import collections
+import json
+import os
+import re
+import sys
+
+sys.path.insert(
+ 0,
+ os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir,
+ os.pardir, 'third_party', 'six', 'src'))
+
+import six
+
+import writer_configuration
+import policy_template_generator
+
+from writers import adm_writer, adml_writer, admx_writer, \
+ chromeos_admx_writer, chromeos_adml_writer, \
+ google_admx_writer, google_adml_writer, \
+ android_policy_writer, reg_writer, doc_writer, \
+ doc_atomic_groups_writer , json_writer, plist_writer, \
+ plist_strings_writer, ios_app_config_writer, jamf_writer
+
+
+def MacLanguageMap(lang):
+ '''Handles slightly different path naming convention for Macs:
+ - 'en-US' -> 'en'
+ - '-' -> '_'
+
+ Args:
+ lang: Language, e.g. 'en-US'.
+ '''
+ return 'en' if lang == 'en-US' else lang.replace('-', '_')
+
+
+'''Template writer descriptors.
+
+Members:
+ type: Writer type, e.g. 'admx'
+ is_per_language: Whether one file per language should be emitted.
+ encoding: Encoding of the output file.
+ language_map: Optional language mapping for file paths.
+ force_windows_line_ending: Forces output file to use Windows line ending.
+'''
+WriterDesc = collections.namedtuple('WriterDesc', [
+ 'type', 'is_per_language', 'encoding', 'language_map',
+ 'force_windows_line_ending'
+])
+
+_WRITER_DESCS = [
+ WriterDesc('adm', True, 'utf-16', None, True),
+ WriterDesc('adml', True, 'utf-16', None, True),
+ WriterDesc('admx', False, 'utf-16', None, True),
+ WriterDesc('google_adml', True, 'utf-8', None, True),
+ WriterDesc('google_admx', False, 'utf-8', None, True),
+ WriterDesc('chromeos_adml', True, 'utf-8', None, True),
+ WriterDesc('chromeos_admx', False, 'utf-8', None, True),
+ WriterDesc('android_policy', False, 'utf-8', None, False),
+ WriterDesc('reg', False, 'utf-16', None, False),
+ WriterDesc('doc', True, 'utf-8', None, False),
+ WriterDesc('doc_atomic_groups', True, 'utf-8', None, False),
+ WriterDesc('json', False, 'utf-8', None, False),
+ WriterDesc('plist', False, 'utf-8', None, False),
+ WriterDesc('plist_strings', True, 'utf-8', MacLanguageMap, False),
+ WriterDesc('jamf', False, 'utf-8', None, False),
+ WriterDesc('ios_app_config', False, 'utf-8', None, False),
+]
+
+# Template writers that are not per-language use policy_templates.json from
+# this language.
+_DEFAULT_LANGUAGE = 'en-US'
+
+
+def GetWriter(writer_type, config):
+ '''Returns the template writer for the given writer type.
+
+ Args:
+ writer_type: Writer type, e.g. 'admx'.
+ config: Writer configuration, see writer_configuration.py.
+ '''
+ return eval(writer_type + '_writer.GetWriter(config)')
+
+
+def _GetWriterConfiguration(grit_defines):
+ '''Returns the writer configuration based on grit defines.
+
+ Args:
+ grit_defines: Array of grit defines, see grit_rule.gni.
+ '''
+ # Build a dictionary from grit defines, which can be plain DEFs or KEY=VALUEs.
+ grit_defines_dict = {}
+ for define in grit_defines:
+ parts = define.split('=', 1)
+ grit_defines_dict[parts[0]] = parts[1] if len(parts) > 1 else 1
+ return writer_configuration.GetConfigurationForBuild(grit_defines_dict)
+
+
+def _ParseVersionFile(version_path):
+ '''Parse version file, return the version if it exists.
+
+ Args:
+ version_path: The path of Chrome VERSION file containing the major version
+ number.
+ '''
+ version = {}
+ with open(version_path) as fp:
+ for line in fp:
+ key, _, value = line.partition('=')
+ if key.strip() == 'MAJOR':
+ version['major'] = value.strip()
+ elif key.strip() == 'MINOR':
+ version['minor'] = value.strip()
+ elif key.strip() == 'BUILD':
+ version['build'] = value.strip()
+ elif key.strip() == 'PATCH':
+ version['patch'] = value.strip()
+
+ version_found = len(version) == 4
+ return version if version_found else None
+
+
+def _JsonToUtf8Encoding(data, ignore_dicts=False):
+ if six.PY2 and isinstance(data, unicode):
+ return data.encode('utf-8')
+ elif isinstance(data, list):
+ return [_JsonToUtf8Encoding(item, False) for item in data]
+ elif isinstance(data, dict):
+ return {
+ _JsonToUtf8Encoding(key): _JsonToUtf8Encoding(value)
+ for key, value in data.items()
+ }
+ return data
+
+
+def main():
+ '''Main policy template conversion script.
+ Usage: template_formatter
+ --translations <translations_path>
+ --languages <language_list>
+ [--adm <adm_path>]
+ ...
+ [--android_policy <android_policy_path>]
+ -D <grit_define>
+ -E <grit_env_variable>
+ -t <grit_target_platform>
+
+ Args:
+ translations: Absolute path of the translated policy_template.json
+ files. Must contain a ${lang} placeholder for the language.
+ languages: Comma-separated list of languages. Trailing commas are fine, e.g.
+ 'en,de,'
+ adm, adml, google_adml, doc, plist_string: Absolute path of the
+ corresponding file types. Must contain a ${lang} placeholder.
+ admx, google_admx, android_policy, reg, json, plist: Absolute path of the
+ corresponding file types. Must NOT contain a ${lang} placeholder. There
+ is only one output file, not one per language.
+ D: List of grit defines, used to assemble writer configuration.
+ E, t: Grit environment variables and target platform. Unused, but
+ grit_rule.gni adds them, so ArgumentParser must handle them.
+ '''
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--translations', dest='translations')
+ parser.add_argument('--languages', dest='languages')
+ parser.add_argument('--version_path', dest='version_path')
+ parser.add_argument('--adm', action='append', dest='adm')
+ parser.add_argument('--adml', action='append', dest='adml')
+ parser.add_argument('--admx', action='append', dest='admx')
+ parser.add_argument('--chromeos_adml', action='append', dest='chromeos_adml')
+ parser.add_argument('--chromeos_admx', action='append', dest='chromeos_admx')
+ parser.add_argument('--google_adml', action='append', dest='google_adml')
+ parser.add_argument('--google_admx', action='append', dest='google_admx')
+ parser.add_argument('--reg', action='append', dest='reg')
+ parser.add_argument('--doc', action='append', dest='doc')
+ parser.add_argument(
+ '--doc_atomic_groups', action='append', dest='doc_atomic_groups')
+ parser.add_argument('--local',
+ action='store_true',
+ help='If set, the documentation will be built so '
+ 'that links work locally in the generated path.')
+ parser.add_argument('--json', action='append', dest='json')
+ parser.add_argument('--plist', action='append', dest='plist')
+ parser.add_argument('--plist_strings', action='append', dest='plist_strings')
+ parser.add_argument('--jamf', action='append', dest='jamf')
+ parser.add_argument(
+ '--android_policy', action='append', dest='android_policy')
+ parser.add_argument(
+ '--ios_app_config', action='append', dest='ios_app_config')
+ parser.add_argument('-D', action='append', dest='grit_defines')
+ parser.add_argument('-E', action='append', dest='grit_build_env')
+ parser.add_argument('-t', action='append', dest='grit_target')
+ args = parser.parse_args()
+
+ _LANG_PLACEHOLDER = "${lang}"
+ assert _LANG_PLACEHOLDER in args.translations
+
+ languages = list(filter(bool, args.languages.split(',')))
+ assert _DEFAULT_LANGUAGE in languages
+
+ config = _GetWriterConfiguration(args.grit_defines)
+
+ version = _ParseVersionFile(args.version_path)
+ if version != None:
+ config['major_version'] = int(version['major'])
+ config['version'] = '.'.join([
+ version['major'], version['minor'], version['build'], version['patch']
+ ])
+ config['local'] = args.local
+
+ # For each language, load policy data once and run all writers on it.
+ for lang in languages:
+ # Load the policy data.
+ policy_templates_json_path = args.translations.replace(
+ _LANG_PLACEHOLDER, lang)
+ # Loads the localized policy json file which must be a valid json file
+ # encoded in utf-8.
+ with codecs.open(policy_templates_json_path, 'r', 'utf-8') as policy_file:
+ policy_data = json.loads(
+ policy_file.read(), object_hook=_JsonToUtf8Encoding)
+
+ # Preprocess the policy data.
+ policy_generator = policy_template_generator.PolicyTemplateGenerator(
+ config, policy_data)
+
+ for writer_desc in _WRITER_DESCS:
+ # For writer types that are not per language (e.g. admx), only do it once.
+ if (not writer_desc.is_per_language and lang != _DEFAULT_LANGUAGE):
+ continue
+
+ # Was the current writer type passed as argument, e.g. --admx <path>?
+ # Note that all paths are arrays and we loop over all of them.
+ output_paths = getattr(args, writer_desc.type, '')
+ if (not output_paths):
+ continue
+ for output_path in output_paths:
+ # Substitute language placeholder in output file.
+ if (writer_desc.is_per_language):
+ assert _LANG_PLACEHOLDER in output_path
+ mapped_lang = writer_desc.language_map(
+ lang) if writer_desc.language_map else lang
+ output_path = output_path.replace(_LANG_PLACEHOLDER, mapped_lang)
+ else:
+ assert _LANG_PLACEHOLDER not in output_path
+
+ # Run the template writer on th policy data.
+ writer = GetWriter(writer_desc.type, config)
+ output_data = policy_generator.GetTemplateText(writer)
+ # Make sure the file uses Windows line endings if needed. This is
+ # important here because codecs.open() opens files in binary more and
+ # will not do line ending conversion.
+ if writer_desc.force_windows_line_ending:
+ output_data = re.sub(r'([^\r])\n', r'\1\r\n', output_data)
+
+ # Make output directory if it doesn't exist yet.
+ output_dir = os.path.split(output_path)[0]
+ if not os.path.exists(output_dir):
+ os.makedirs(output_dir)
+
+ # Write output file.
+ with codecs.open(output_path, 'w', writer_desc.encoding) as output_file:
+ output_file.write(output_data)
+
+
+if '__main__' == __name__:
+ sys.exit(main())