diff options
Diffstat (limited to 'chromium/components/policy/tools/template_writers/template_formatter.py')
-rwxr-xr-x | chromium/components/policy/tools/template_writers/template_formatter.py | 274 |
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()) |