Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(990)

Unified Diff: third_party/WebKit/Source/platform/inspector_protocol/CodeGenerator.py

Issue 2248783003: [DevTools] Refactor CodeGenerator. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: up_to_date fix Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/platform/inspector_protocol/CodeGenerator.py
diff --git a/third_party/WebKit/Source/platform/inspector_protocol/CodeGenerator.py b/third_party/WebKit/Source/platform/inspector_protocol/CodeGenerator.py
index 128dde3bc453d09c508c4d76ae69d4210ad9c0a7..d6ab65e9535938315741066681d7adca768a12e8 100644
--- a/third_party/WebKit/Source/platform/inspector_protocol/CodeGenerator.py
+++ b/third_party/WebKit/Source/platform/inspector_protocol/CodeGenerator.py
@@ -5,6 +5,8 @@
import os.path
import sys
import optparse
+import collections
+import functools
try:
import json
except ImportError:
@@ -47,129 +49,52 @@ if os.path.isdir(deps_dir):
import jinja2
-cmdline_parser = optparse.OptionParser()
-cmdline_parser.add_option("--output_base")
-cmdline_parser.add_option("--config")
-
-try:
- arg_options, arg_values = cmdline_parser.parse_args()
- output_base = arg_options.output_base
- if not output_base:
- raise Exception("Base output directory must be specified")
- config_file = arg_options.config
- if not config_file:
- raise Exception("Config file name must be specified")
- config_dir = os.path.dirname(config_file)
-except Exception:
- # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html
- exc = sys.exc_info()[1]
- sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc)
- exit(1)
-
-
-try:
- config_json_string = open(config_file, "r").read()
- config = json.loads(config_json_string)
-
- protocol_file = config["protocol"]["path"]
- if not protocol_file:
- raise Exception("Config is missing protocol.path")
- protocol_file = os.path.join(config_dir, protocol_file)
- output_dirname = config["protocol"]["output"]
- if not output_dirname:
- raise Exception("Config is missing protocol.output")
- output_dirname = os.path.join(output_base, output_dirname)
- output_package = config["protocol"]["package"]
- if not output_package:
- raise Exception("Config is missing protocol.package")
-
- importing = False
- if "import" in config:
- importing = True
- imported_file = config["import"]["path"]
- if not imported_file:
- raise Exception("Config is missing import.path")
- imported_file = os.path.join(config_dir, imported_file)
- imported_package = config["import"]["package"]
- if not imported_package:
- raise Exception("Config is missing import.package")
-
- exporting = False
- if "export" in config:
- exporting = True
- exported_dirname = config["export"]["output"]
- if not exported_dirname:
- raise Exception("Config is missing export.output")
- exported_dirname = os.path.join(output_base, exported_dirname)
- exported_package = config["export"]["package"]
- if not exported_package:
- raise Exception("Config is missing export.package")
-
- lib = False
- if "lib" in config:
- lib = True
- lib_dirname = config["lib"]["output"]
- if not lib_dirname:
- raise Exception("Config is missing lib.output")
- lib_dirname = os.path.join(output_base, lib_dirname)
- lib_string16_include = config["lib"]["string16_impl_header_path"]
- if not lib_string16_include:
- raise Exception("Config is missing lib.string16_impl_header_path")
- lib_platform_include = config["lib"]["platform_impl_header_path"]
- if not lib_platform_include:
- raise Exception("Config is missing lib.platform_impl_header_path")
-
- string_type = config["string"]["class_name"]
- if not string_type:
- raise Exception("Config is missing string.class_name")
-
- export_macro = config["class_export"]["macro"]
- if not export_macro:
- raise Exception("Config is missing class_export.macro")
- export_macro_include = config["class_export"]["header_path"]
- if not export_macro_include:
- raise Exception("Config is missing class_export.header_path")
-
- lib_package = config["lib_package"]
- if not lib_package:
- raise Exception("Config is missing lib_package")
-except Exception:
- # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html
- exc = sys.exc_info()[1]
- sys.stderr.write("Failed to parse config file: %s\n\n" % exc)
- exit(1)
-
-
-# Make gyp / make generatos happy, otherwise make rebuilds world.
-def up_to_date():
- template_ts = max(
- os.path.getmtime(__file__),
- os.path.getmtime(os.path.join(templates_dir, "TypeBuilder_h.template")),
- os.path.getmtime(os.path.join(templates_dir, "TypeBuilder_cpp.template")),
- os.path.getmtime(os.path.join(templates_dir, "Exported_h.template")),
- os.path.getmtime(os.path.join(templates_dir, "Imported_h.template")),
- os.path.getmtime(config_file),
- os.path.getmtime(protocol_file))
- if importing:
- template_ts = max(template_ts, os.path.getmtime(imported_file))
-
- for domain in json_api["domains"]:
- name = domain["domain"]
- paths = []
- if name in generate_domains:
- paths = [os.path.join(output_dirname, name + ".h"), os.path.join(output_dirname, name + ".cpp")]
- if domain["has_exports"]:
- paths.append(os.path.join(exported_dirname, name + ".h"))
- if name in imported_domains and domain["has_exports"]:
- paths = [os.path.join(output_dirname, name + '.h')]
- for path in paths:
- if not os.path.exists(path):
- return False
- generated_ts = os.path.getmtime(path)
- if generated_ts < template_ts:
- return False
- return True
+def read_config():
+ # pylint: disable=W0703
+ def json_to_object(data, output_base, config_base):
+ def json_object_hook(object_dict):
+ items = [(k, os.path.join(config_base, v) if k == "path" else v) for (k, v) in object_dict.items()]
+ items = [(k, os.path.join(output_base, v) if k == "output" else v) for (k, v) in items]
+ keys, values = zip(*items)
+ return collections.namedtuple('X', keys)(*values)
+ return json.loads(data, object_hook=json_object_hook)
+
+ try:
+ cmdline_parser = optparse.OptionParser()
+ cmdline_parser.add_option("--output_base")
+ cmdline_parser.add_option("--config")
+ arg_options, _ = cmdline_parser.parse_args()
+ output_base = arg_options.output_base
+ if not output_base:
+ raise Exception("Base output directory must be specified")
+ config_file = arg_options.config
+ if not config_file:
+ raise Exception("Config file name must be specified")
+ config_base = os.path.dirname(config_file)
+ except Exception:
+ # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html
+ exc = sys.exc_info()[1]
+ sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc)
+ exit(1)
+
+ try:
+ config_json_file = open(config_file, "r")
+ config_json_string = config_json_file.read()
+ config_partial = json_to_object(config_json_string, output_base, config_base)
+ keys = list(config_partial._fields) # pylint: disable=E1101
+ values = [getattr(config_partial, k) for k in keys]
+ for optional in ["imported", "exported", "lib"]:
+ if optional not in keys:
+ keys.append(optional)
+ values.append(False)
+ config_json_file.close()
+ return (config_file, collections.namedtuple('X', keys)(*values))
+ except Exception:
+ # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html
+ exc = sys.exc_info()[1]
+ sys.stderr.write("Failed to parse config file: %s\n\n" % exc)
+ exit(1)
def to_title_case(name):
@@ -198,11 +123,7 @@ def initialize_jinja_env(cache_dir):
return jinja_env
-def output_file(file_name):
- return open(file_name, "w")
-
-
-def patch_full_qualified_refs():
+def patch_full_qualified_refs(protocol):
def patch_full_qualified_refs_in_domain(json, domain_name):
if isinstance(json, list):
for item in json:
@@ -220,11 +141,11 @@ def patch_full_qualified_refs():
json["$ref"] = domain_name + "." + json["$ref"]
return
- for domain in json_api["domains"]:
+ for domain in protocol.json_api["domains"]:
patch_full_qualified_refs_in_domain(domain, domain["domain"])
-def calculate_exports():
+def calculate_exports(protocol):
def calculate_exports_in_json(json_value):
has_exports = False
if isinstance(json_value, list):
@@ -236,15 +157,11 @@ def calculate_exports():
has_exports = calculate_exports_in_json(json_value[key]) or has_exports
return has_exports
- json_api["has_exports"] = False
- for domain_json in json_api["domains"]:
- domain_name = domain_json["domain"]
+ protocol.json_api["has_exports"] = False
+ for domain_json in protocol.json_api["domains"]:
domain_json["has_exports"] = calculate_exports_in_json(domain_json)
- if domain_json["has_exports"] and domain_name in generate_domains:
- if not exporting:
- sys.stderr.write("Domain %s is exported, but config is missing export entry\n\n" % domain_name)
- exit(1)
- json_api["has_exports"] = True
+ if domain_json["has_exports"] and domain_json["domain"] in protocol.generate_domains:
+ protocol.json_api["has_exports"] = True
def create_imported_type_definition(domain_name, type):
@@ -307,7 +224,7 @@ def create_any_type_definition():
}
-def create_string_type_definition(domain):
+def create_string_type_definition(string_type):
# pylint: disable=W0622
return {
"return_type": string_type,
@@ -353,14 +270,6 @@ def create_primitive_type_definition(type):
}
-type_definitions = {}
-type_definitions["number"] = create_primitive_type_definition("number")
-type_definitions["integer"] = create_primitive_type_definition("integer")
-type_definitions["boolean"] = create_primitive_type_definition("boolean")
-type_definitions["object"] = create_object_type_definition()
-type_definitions["any"] = create_any_type_definition()
-
-
def wrap_array_definition(type):
# pylint: disable=W0622
return {
@@ -378,36 +287,42 @@ def wrap_array_definition(type):
}
-def create_type_definitions():
- for domain in json_api["domains"]:
- type_definitions[domain["domain"] + ".string"] = create_string_type_definition(domain["domain"])
+def create_type_definitions(protocol, string_type):
+ protocol.type_definitions = {}
+ protocol.type_definitions["number"] = create_primitive_type_definition("number")
+ protocol.type_definitions["integer"] = create_primitive_type_definition("integer")
+ protocol.type_definitions["boolean"] = create_primitive_type_definition("boolean")
+ protocol.type_definitions["object"] = create_object_type_definition()
+ protocol.type_definitions["any"] = create_any_type_definition()
+ for domain in protocol.json_api["domains"]:
+ protocol.type_definitions[domain["domain"] + ".string"] = create_string_type_definition(string_type)
if not ("types" in domain):
continue
for type in domain["types"]:
type_name = domain["domain"] + "." + type["id"]
- if type["type"] == "object" and domain["domain"] in imported_domains:
- type_definitions[type_name] = create_imported_type_definition(domain["domain"], type)
+ if type["type"] == "object" and domain["domain"] in protocol.imported_domains:
+ protocol.type_definitions[type_name] = create_imported_type_definition(domain["domain"], type)
elif type["type"] == "object":
- type_definitions[type_name] = create_user_type_definition(domain["domain"], type)
+ protocol.type_definitions[type_name] = create_user_type_definition(domain["domain"], type)
elif type["type"] == "array":
items_type = type["items"]["type"]
- type_definitions[type_name] = wrap_array_definition(type_definitions[items_type])
+ protocol.type_definitions[type_name] = wrap_array_definition(protocol.type_definitions[items_type])
elif type["type"] == domain["domain"] + ".string":
- type_definitions[type_name] = create_string_type_definition(domain["domain"])
+ protocol.type_definitions[type_name] = create_string_type_definition(string_type)
else:
- type_definitions[type_name] = create_primitive_type_definition(type["type"])
+ protocol.type_definitions[type_name] = create_primitive_type_definition(type["type"])
-def type_definition(name):
- return type_definitions[name]
+def type_definition(protocol, name):
+ return protocol.type_definitions[name]
-def resolve_type(property):
- if "$ref" in property:
- return type_definitions[property["$ref"]]
- if property["type"] == "array":
- return wrap_array_definition(resolve_type(property["items"]))
- return type_definitions[property["type"]]
+def resolve_type(protocol, prop):
+ if "$ref" in prop:
+ return protocol.type_definitions[prop["$ref"]]
+ if prop["type"] == "array":
+ return wrap_array_definition(resolve_type(protocol, prop["items"]))
+ return protocol.type_definitions[prop["type"]]
def join_arrays(dict, keys):
@@ -425,124 +340,144 @@ def has_disable(commands):
return False
-def generate(domain_object, template, file_name):
- template_context = {
- "domain": domain_object,
- "join_arrays": join_arrays,
- "resolve_type": resolve_type,
- "type_definition": type_definition,
- "has_disable": has_disable,
- "export_macro": export_macro,
- "export_macro_include": export_macro_include,
- "output_package": output_package,
- "lib_package": lib_package
- }
- if exporting:
- template_context["exported_package"] = exported_package
- if importing:
- template_context["imported_package"] = imported_package
- out_file = output_file(file_name)
- out_file.write(template.render(template_context))
- out_file.close()
-
-
-def read_protocol_file(file_name, all_domains):
+def read_protocol_file(file_name, json_api):
input_file = open(file_name, "r")
json_string = input_file.read()
+ input_file.close()
parsed_json = json.loads(json_string)
domains = []
for domain in parsed_json["domains"]:
domains.append(domain["domain"])
- all_domains["domains"] += parsed_json["domains"]
+ json_api["domains"] += parsed_json["domains"]
return domains
-def generate_lib():
- template_context = {
- "string16_impl_h_include": lib_string16_include,
- "platform_impl_h_include": lib_platform_include,
- "lib_package": lib_package,
- "export_macro": export_macro,
- "export_macro_include": export_macro_include
- }
+class Protocol(object):
+ def __init__(self):
+ self.json_api = {}
+ self.generate_domains = []
+ self.imported_domains = []
+
+
+def main():
+ config_file, config = read_config()
- def generate_file(file_name, template_files):
- out_file = output_file(file_name)
- for template_file in template_files:
- template = jinja_env.get_template("/" + template_file)
- out_file.write(template.render(template_context))
- out_file.write("\n\n")
+ protocol = Protocol()
+ protocol.json_api = {"domains": []}
+ protocol.generate_domains = read_protocol_file(config.protocol.path, protocol.json_api)
+ protocol.imported_domains = read_protocol_file(config.imported.path, protocol.json_api) if config.imported else []
+ patch_full_qualified_refs(protocol)
+ calculate_exports(protocol)
+ create_type_definitions(protocol, config.string.class_name)
+
+ if not config.exported:
+ for domain_json in protocol.json_api["domains"]:
+ if domain_json["has_exports"] and domain_json["domain"] in protocol.generate_domains:
+ sys.stderr.write("Domain %s is exported, but config is missing export entry\n\n" % domain_json["domain"])
+ exit(1)
+
+ if not os.path.exists(config.protocol.output):
+ os.mkdir(config.protocol.output)
+ if protocol.json_api["has_exports"] and not os.path.exists(config.exported.output):
+ os.mkdir(config.exported.output)
+ jinja_env = initialize_jinja_env(config.protocol.output)
+
+ inputs = []
+ inputs.append(__file__)
+ inputs.append(config_file)
+ inputs.append(config.protocol.path)
+ if config.imported:
+ inputs.append(config.imported.path)
+ inputs.append(os.path.join(templates_dir, "TypeBuilder_h.template"))
+ inputs.append(os.path.join(templates_dir, "TypeBuilder_cpp.template"))
+ inputs.append(os.path.join(templates_dir, "Exported_h.template"))
+ inputs.append(os.path.join(templates_dir, "Imported_h.template"))
+
+ h_template = jinja_env.get_template("TypeBuilder_h.template")
+ cpp_template = jinja_env.get_template("TypeBuilder_cpp.template")
+ exported_template = jinja_env.get_template("Exported_h.template")
+ imported_template = jinja_env.get_template("Imported_h.template")
+
+ outputs = dict()
+
+ for domain in protocol.json_api["domains"]:
+ class_name = domain["domain"]
+ template_context = {
+ "config": config,
+ "domain": domain,
+ "join_arrays": join_arrays,
+ "resolve_type": functools.partial(resolve_type, protocol),
+ "type_definition": functools.partial(type_definition, protocol),
+ "has_disable": has_disable
+ }
+
+ if domain["domain"] in protocol.generate_domains:
+ outputs[os.path.join(config.protocol.output, class_name + ".h")] = h_template.render(template_context)
+ outputs[os.path.join(config.protocol.output, class_name + ".cpp")] = cpp_template.render(template_context)
+ if domain["has_exports"]:
+ outputs[os.path.join(config.exported.output, class_name + ".h")] = exported_template.render(template_context)
+ if domain["domain"] in protocol.imported_domains and domain["has_exports"]:
+ outputs[os.path.join(config.protocol.output, class_name + ".h")] = imported_template.render(template_context)
+
+ if config.lib:
+ template_context = {
+ "config": config
+ }
+
+ # Note these should be sorted in the right order.
+ # TODO(dgozman): sort them programmatically based on commented includes.
+ lib_h_templates = [
+ "Allocator_h.template",
+ "Platform_h.template",
+ "Collections_h.template",
+ "String16_h.template",
+ "ErrorSupport_h.template",
+ "Values_h.template",
+ "Object_h.template",
+ "ValueConversions_h.template",
+ "Maybe_h.template",
+ "Array_h.template",
+ "FrontendChannel_h.template",
+ "BackendCallback_h.template",
+ "DispatcherBase_h.template",
+ "Parser_h.template",
+ ]
+
+ lib_cpp_templates = [
+ "InspectorProtocol_cpp.template",
+ "String16_cpp.template",
+ "ErrorSupport_cpp.template",
+ "Values_cpp.template",
+ "Object_cpp.template",
+ "DispatcherBase_cpp.template",
+ "Parser_cpp.template",
+ ]
+
+ def generate_lib_file(file_name, template_files):
+ parts = []
+ for template_file in template_files:
+ inputs.append(os.path.join(templates_dir, template_file))
+ template = jinja_env.get_template(template_file)
+ parts.append(template.render(template_context))
+ outputs[file_name] = "\n\n".join(parts)
+
+ generate_lib_file(os.path.join(config.lib.output, "InspectorProtocol.h"), lib_h_templates)
+ generate_lib_file(os.path.join(config.lib.output, "InspectorProtocol.cpp"), lib_cpp_templates)
+
+ # Make gyp / make generatos happy, otherwise make rebuilds world.
+ inputs_ts = max(map(os.path.getmtime, inputs))
+ up_to_date = True
+ for output_file in outputs.iterkeys():
+ if not os.path.exists(output_file) or os.path.getmtime(output_file) < inputs_ts:
+ up_to_date = False
+ break
+ if up_to_date:
+ sys.exit()
+
+ for file_name, content in outputs.iteritems():
+ out_file = open(file_name, "w")
+ out_file.write(content)
out_file.close()
- # Note these should be sorted in the right order.
- # TODO(dgozman): sort them programmatically based on commented includes.
- lib_h_templates = [
- "Allocator_h.template",
- "Platform_h.template",
- "Collections_h.template",
- "String16_h.template",
-
- "ErrorSupport_h.template",
- "Values_h.template",
- "Object_h.template",
- "ValueConversions_h.template",
- "Maybe_h.template",
- "Array_h.template",
-
- "FrontendChannel_h.template",
- "BackendCallback_h.template",
- "DispatcherBase_h.template",
-
- "Parser_h.template",
- ]
-
- lib_cpp_templates = [
- "InspectorProtocol_cpp.template",
-
- "String16_cpp.template",
-
- "ErrorSupport_cpp.template",
- "Values_cpp.template",
- "Object_cpp.template",
-
- "DispatcherBase_cpp.template",
-
- "Parser_cpp.template",
- ]
-
- generate_file(os.path.join(lib_dirname, "InspectorProtocol.h"), lib_h_templates)
- generate_file(os.path.join(lib_dirname, "InspectorProtocol.cpp"), lib_cpp_templates)
-
-
-json_api = {"domains": []}
-generate_domains = read_protocol_file(protocol_file, json_api)
-imported_domains = read_protocol_file(imported_file, json_api) if importing else []
-patch_full_qualified_refs()
-calculate_exports()
-create_type_definitions()
-
-if up_to_date():
- sys.exit()
-if not os.path.exists(output_dirname):
- os.mkdir(output_dirname)
-if json_api["has_exports"] and not os.path.exists(exported_dirname):
- os.mkdir(exported_dirname)
-
-jinja_env = initialize_jinja_env(output_dirname)
-h_template = jinja_env.get_template("/TypeBuilder_h.template")
-cpp_template = jinja_env.get_template("/TypeBuilder_cpp.template")
-exported_template = jinja_env.get_template("/Exported_h.template")
-imported_template = jinja_env.get_template("/Imported_h.template")
-
-for domain in json_api["domains"]:
- class_name = domain["domain"]
- if domain["domain"] in generate_domains:
- generate(domain, h_template, os.path.join(output_dirname, class_name + ".h"))
- generate(domain, cpp_template, os.path.join(output_dirname, class_name + ".cpp"))
- if domain["has_exports"]:
- generate(domain, exported_template, os.path.join(exported_dirname, class_name + ".h"))
- if domain["domain"] in imported_domains and domain["has_exports"]:
- generate(domain, imported_template, os.path.join(output_dirname, class_name + ".h"))
-
-if lib:
- generate_lib()
+
+main()

Powered by Google App Engine
This is Rietveld 408576698