| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2017 The Chromium Authors. All rights reserved. | 2 # Copyright 2017 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Generates a C++ source file which defines a string constant containing the | 6 """Generates C++ source and header files defining function to create an |
| 7 contents of a catalog manifest file. Useful for baking catalogs into binaries | 7 in-memory representation of a static catalog manifest at runtime.""" |
| 8 which don't want to hit disk before initializing the catalog.""" | 8 |
| 9 | 9 |
| 10 import argparse | 10 import argparse |
| 11 import imp |
| 11 import json | 12 import json |
| 12 import os.path | 13 import os.path |
| 13 import sys | 14 import sys |
| 14 | 15 |
| 15 | 16 |
| 16 # Token used to delimit raw strings in the generated source file. It's illegal | 17 _H_FILE_TEMPLATE = "catalog.h.tmpl" |
| 17 # for this token to appear within the contents of the input manifest itself. | 18 _CC_FILE_TEMPLATE = "catalog.cc.tmpl" |
| 18 _RAW_STRING_DELIMITER = "#CATALOG_JSON#" | 19 |
| 20 |
| 21 # Disable lint check for finding modules: |
| 22 # pylint: disable=F0401 |
| 23 |
| 24 def _GetDirAbove(dirname): |
| 25 """Returns the directory "above" this file containing |dirname| (which must |
| 26 also be "above" this file).""" |
| 27 path = os.path.abspath(__file__) |
| 28 while True: |
| 29 path, tail = os.path.split(path) |
| 30 assert tail |
| 31 if tail == dirname: |
| 32 return path |
| 33 |
| 34 |
| 35 try: |
| 36 imp.find_module("jinja2") |
| 37 except ImportError: |
| 38 sys.path.append(os.path.join(_GetDirAbove("services"), "third_party")) |
| 39 import jinja2 |
| 40 |
| 41 |
| 42 def ApplyTemplate(path_to_template, output_path, global_vars, **kwargs): |
| 43 def make_ascii(maybe_unicode): |
| 44 if type(maybe_unicode) is str: |
| 45 return maybe_unicode |
| 46 assert type(maybe_unicode) is unicode |
| 47 return maybe_unicode.encode("ascii", "ignore") |
| 48 |
| 49 with open(output_path, "w") as output_file: |
| 50 jinja_env = jinja2.Environment( |
| 51 loader=jinja2.FileSystemLoader(os.path.dirname(__file__)), |
| 52 keep_trailing_newline=True, **kwargs) |
| 53 jinja_env.globals.update(global_vars) |
| 54 jinja_env.filters.update({ |
| 55 "is_dict": lambda x : type(x) is dict, |
| 56 "is_list": lambda x : type(x) is list, |
| 57 "is_number": lambda x : type(x) is int or type(x) is float, |
| 58 "is_bool": lambda x: type(x) is bool, |
| 59 "is_string": lambda x: type(x) is str, |
| 60 "is_unicode": lambda x: type(x) is unicode, |
| 61 "make_ascii": make_ascii, |
| 62 }) |
| 63 output_file.write(jinja_env.get_template(path_to_template).render()) |
| 19 | 64 |
| 20 | 65 |
| 21 def main(): | 66 def main(): |
| 22 parser = argparse.ArgumentParser( | 67 parser = argparse.ArgumentParser( |
| 23 description="Generates a C++ constant containing a catalog manifest.") | 68 description="Generates a C++ constant containing a catalog manifest.") |
| 24 parser.add_argument("--input") | 69 parser.add_argument("--input") |
| 25 parser.add_argument("--output") | 70 parser.add_argument("--output-filename-base") |
| 26 parser.add_argument("--symbol-name") | 71 parser.add_argument("--output-function-name") |
| 27 parser.add_argument("--pretty", action="store_true") | 72 parser.add_argument("--module-path") |
| 28 args, _ = parser.parse_known_args() | 73 args, _ = parser.parse_known_args() |
| 29 | 74 |
| 30 if args.input is None or args.output is None or args.symbol_name is None: | 75 if args.input is None: |
| 31 raise Exception("--input, --output, and --symbol-name are required") | 76 raise Exception("--input is required") |
| 77 if args.output_filename_base is None: |
| 78 raise Exception("--output-filename-base is required") |
| 79 if args.output_function_name is None: |
| 80 raise Exception("--output-function-name is required") |
| 81 if args.module_path is None: |
| 82 raise Exception("--module-path is required") |
| 32 | 83 |
| 33 with open(args.input, 'r') as input_file: | 84 with open(args.input, "r") as input_file: |
| 34 manifest_contents = input_file.read() | 85 catalog = json.load(input_file) |
| 35 | 86 |
| 36 if manifest_contents.find(_RAW_STRING_DELIMITER) >= 0: | 87 qualified_function_name = args.output_function_name.split("::") |
| 37 raise Exception( | 88 namespaces = qualified_function_name[0:-1] |
| 38 "Unexpected '%s' found in input manifest." % _RAW_STRING_DELIMITER) | 89 function_name = qualified_function_name[-1] |
| 39 | 90 |
| 40 qualified_symbol_name = args.symbol_name.split("::") | 91 def raise_error(error, value): |
| 41 namespace = qualified_symbol_name[0:-1] | 92 raise Exception(error) |
| 42 symbol_name = qualified_symbol_name[-1] | |
| 43 | 93 |
| 44 with open(args.output, 'w') as output_file: | 94 global_vars = { |
| 45 output_file.write( | 95 "catalog": catalog, |
| 46 "// This is a generated file produced by\n" | 96 "function_name": function_name, |
| 47 "// src/services/catalog/public/tools/sourcify_manifest.py.\n\n") | 97 "namespaces": namespaces, |
| 48 for name in namespace: | 98 "path": args.module_path, |
| 49 output_file.write("namespace %s {\n" % name) | 99 "raise": raise_error, |
| 50 output_file.write("\nextern const char %s[];" % symbol_name) | 100 } |
| 51 output_file.write("\nconst char %s[] = R\"%s(%s)%s\";\n\n" % | 101 |
| 52 (symbol_name, _RAW_STRING_DELIMITER, manifest_contents, | 102 input_h_filename = _H_FILE_TEMPLATE |
| 53 _RAW_STRING_DELIMITER)) | 103 output_h_filename = "%s.h" % args.output_filename_base |
| 54 for name in reversed(namespace): | 104 ApplyTemplate(input_h_filename, output_h_filename, global_vars) |
| 55 output_file.write("} // %s\n" % name) | 105 |
| 106 input_cc_filename = _CC_FILE_TEMPLATE |
| 107 output_cc_filename = "%s.cc" % args.output_filename_base |
| 108 ApplyTemplate(input_cc_filename, output_cc_filename, global_vars) |
| 56 | 109 |
| 57 return 0 | 110 return 0 |
| 58 | 111 |
| 59 if __name__ == "__main__": | 112 if __name__ == "__main__": |
| 60 sys.exit(main()) | 113 sys.exit(main()) |
| OLD | NEW |