OLD | NEW |
---|---|
1 # Copyright 2016 The Chromium Authors. All rights reserved. | 1 # Copyright 2016 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import os.path | 5 import os.path |
6 import sys | 6 import sys |
7 import optparse | 7 import optparse |
8 try: | 8 try: |
9 import json | 9 import json |
10 except ImportError: | 10 except ImportError: |
(...skipping 25 matching lines...) Expand all Loading... | |
36 | 36 |
37 if os.path.isdir(deps_dir): | 37 if os.path.isdir(deps_dir): |
38 sys.path.insert(1, os.path.join(deps_dir, "jinja2")) | 38 sys.path.insert(1, os.path.join(deps_dir, "jinja2")) |
39 sys.path.insert(1, os.path.join(deps_dir, "markupsafe")) | 39 sys.path.insert(1, os.path.join(deps_dir, "markupsafe")) |
40 | 40 |
41 import jinja2 | 41 import jinja2 |
42 | 42 |
43 cmdline_parser = optparse.OptionParser() | 43 cmdline_parser = optparse.OptionParser() |
44 cmdline_parser.add_option("--protocol") | 44 cmdline_parser.add_option("--protocol") |
45 cmdline_parser.add_option("--include") | 45 cmdline_parser.add_option("--include") |
46 cmdline_parser.add_option("--include_package") | |
46 cmdline_parser.add_option("--string_type") | 47 cmdline_parser.add_option("--string_type") |
47 cmdline_parser.add_option("--export_macro") | 48 cmdline_parser.add_option("--export_macro") |
48 cmdline_parser.add_option("--output_dir") | 49 cmdline_parser.add_option("--output_dir") |
49 cmdline_parser.add_option("--output_package") | 50 cmdline_parser.add_option("--output_package") |
51 cmdline_parser.add_option("--exported_dir") | |
52 cmdline_parser.add_option("--exported_package") | |
50 | 53 |
51 try: | 54 try: |
52 arg_options, arg_values = cmdline_parser.parse_args() | 55 arg_options, arg_values = cmdline_parser.parse_args() |
53 protocol_file = arg_options.protocol | 56 protocol_file = arg_options.protocol |
54 if not protocol_file: | 57 if not protocol_file: |
55 raise Exception("Protocol directory must be specified") | 58 raise Exception("Protocol directory must be specified") |
56 include_file = arg_options.include | 59 include_file = arg_options.include |
60 include_package = arg_options.include_package | |
61 if include_file and not include_package: | |
62 raise Exception("Include package must be specified when using include fi le") | |
63 if include_package and not include_file: | |
64 raise Exception("Include file must be specified when using include packa ge") | |
57 output_dirname = arg_options.output_dir | 65 output_dirname = arg_options.output_dir |
58 if not output_dirname: | 66 if not output_dirname: |
59 raise Exception("Output directory must be specified") | 67 raise Exception("Output directory must be specified") |
60 output_package = arg_options.output_package | 68 output_package = arg_options.output_package |
61 if not output_package: | 69 if not output_package: |
62 raise Exception("Output package must be specified") | 70 raise Exception("Output package must be specified") |
71 exported_dirname = arg_options.exported_dir | |
72 if not exported_dirname: | |
73 exported_dirname = os.path.join(output_dirname, "exported") | |
74 exported_package = arg_options.exported_package | |
75 if not exported_package: | |
76 exported_package = os.path.join(output_package, "exported") | |
63 string_type = arg_options.string_type | 77 string_type = arg_options.string_type |
64 if not string_type: | 78 if not string_type: |
65 raise Exception("String type must be specified") | 79 raise Exception("String type must be specified") |
66 export_macro = arg_options.export_macro | 80 export_macro = arg_options.export_macro |
67 if not export_macro: | 81 if not export_macro: |
68 raise Exception("Export macro must be specified") | 82 raise Exception("Export macro must be specified") |
69 except Exception: | 83 except Exception: |
70 # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html | 84 # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html |
71 exc = sys.exc_info()[1] | 85 exc = sys.exc_info()[1] |
72 sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc) | 86 sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc) |
73 exit(1) | 87 exit(1) |
74 | 88 |
75 | 89 |
76 input_file = open(protocol_file, "r") | 90 input_file = open(protocol_file, "r") |
77 json_string = input_file.read() | 91 json_string = input_file.read() |
78 parsed_json = json.loads(json_string) | 92 parsed_json = json.loads(json_string) |
79 | 93 |
80 | 94 |
81 # Make gyp / make generatos happy, otherwise make rebuilds world. | 95 # Make gyp / make generatos happy, otherwise make rebuilds world. |
82 def up_to_date(): | 96 def up_to_date(): |
83 template_ts = max( | 97 template_ts = max( |
84 os.path.getmtime(__file__), | 98 os.path.getmtime(__file__), |
85 os.path.getmtime(os.path.join(templates_dir, "TypeBuilder_h.template")), | 99 os.path.getmtime(os.path.join(templates_dir, "TypeBuilder_h.template")), |
86 os.path.getmtime(os.path.join(templates_dir, "TypeBuilder_cpp.template") ), | 100 os.path.getmtime(os.path.join(templates_dir, "TypeBuilder_cpp.template") ), |
101 os.path.getmtime(os.path.join(templates_dir, "Exported_h.template")), | |
87 os.path.getmtime(protocol_file)) | 102 os.path.getmtime(protocol_file)) |
88 | 103 |
89 for domain in parsed_json["domains"]: | 104 for domain in parsed_json["domains"]: |
90 name = domain["domain"] | 105 name = domain["domain"] |
91 h_path = os.path.join(output_dirname, name + ".h") | 106 paths = [os.path.join(output_dirname, name + ".h"), os.path.join(output_ dirname, name + ".cpp")] |
pfeldman
2016/07/22 18:02:40
If imports have changed, we should regenerate.
dgozman
2016/07/22 21:21:19
Done.
| |
92 cpp_path = os.path.join(output_dirname, name + ".cpp") | 107 if domain["has_exports"]: |
93 if not os.path.exists(h_path) or not os.path.exists(cpp_path): | 108 paths.append(os.path.join(exported_dirname, name + ".h")) |
94 return False | 109 for path in paths: |
95 generated_ts = max(os.path.getmtime(h_path), os.path.getmtime(cpp_path)) | 110 if not os.path.exists(path): |
96 if generated_ts < template_ts: | 111 return False |
97 return False | 112 generated_ts = os.path.getmtime(path) |
113 if generated_ts < template_ts: | |
114 return False | |
98 return True | 115 return True |
99 | 116 |
100 | 117 |
101 if up_to_date(): | |
102 sys.exit() | |
103 | |
104 | |
105 def to_title_case(name): | 118 def to_title_case(name): |
106 return name[:1].upper() + name[1:] | 119 return name[:1].upper() + name[1:] |
107 | 120 |
108 | 121 |
109 def dash_to_camelcase(word): | 122 def dash_to_camelcase(word): |
110 return "".join(to_title_case(x) or "-" for x in word.split("-")) | 123 return "".join(to_title_case(x) or "-" for x in word.split("-")) |
111 | 124 |
112 | 125 |
113 def initialize_jinja_env(cache_dir): | 126 def initialize_jinja_env(cache_dir): |
114 jinja_env = jinja2.Environment( | 127 jinja_env = jinja2.Environment( |
115 loader=jinja2.FileSystemLoader(templates_dir), | 128 loader=jinja2.FileSystemLoader(templates_dir), |
116 # Bytecode cache is not concurrency-safe unless pre-cached: | 129 # Bytecode cache is not concurrency-safe unless pre-cached: |
117 # if pre-cached this is read-only, but writing creates a race condition. | 130 # if pre-cached this is read-only, but writing creates a race condition. |
118 bytecode_cache=jinja2.FileSystemBytecodeCache(cache_dir), | 131 bytecode_cache=jinja2.FileSystemBytecodeCache(cache_dir), |
119 keep_trailing_newline=True, # newline-terminate generated files | 132 keep_trailing_newline=True, # newline-terminate generated files |
120 lstrip_blocks=True, # so can indent control flow tags | 133 lstrip_blocks=True, # so can indent control flow tags |
121 trim_blocks=True) | 134 trim_blocks=True) |
122 jinja_env.filters.update({"to_title_case": to_title_case, "dash_to_camelcase ": dash_to_camelcase}) | 135 jinja_env.filters.update({"to_title_case": to_title_case, "dash_to_camelcase ": dash_to_camelcase}) |
123 jinja_env.add_extension("jinja2.ext.loopcontrols") | 136 jinja_env.add_extension("jinja2.ext.loopcontrols") |
124 return jinja_env | 137 return jinja_env |
125 | 138 |
126 | 139 |
127 def output_file(file_name): | 140 def output_file(file_name): |
128 return open(file_name, "w") | 141 return open(file_name, "w") |
129 | 142 |
130 | 143 |
131 def patch_full_qualified_refs(): | 144 def patch_full_qualified_refs(): |
pfeldman
2016/07/22 18:02:40
Copy-paste it.
dgozman
2016/07/22 21:21:19
Done.
| |
132 def patch_full_qualified_refs_in_domain(json, domain_name): | 145 def patch_full_qualified_refs_in_domain(json, domain_name): |
146 has_exports = False | |
133 if isinstance(json, list): | 147 if isinstance(json, list): |
134 for item in json: | 148 for item in json: |
135 patch_full_qualified_refs_in_domain(item, domain_name) | 149 has_exports = patch_full_qualified_refs_in_domain(item, domain_n ame) or has_exports |
136 | 150 |
137 if not isinstance(json, dict): | 151 if not isinstance(json, dict): |
138 return | 152 return has_exports |
153 has_exports = ("exported" in json and json["exported"]) or has_exports | |
139 for key in json: | 154 for key in json: |
140 if key == "type" and json[key] == "string": | 155 if key == "type" and json[key] == "string": |
141 json[key] = domain_name + ".string" | 156 json[key] = domain_name + ".string" |
142 if key != "$ref": | 157 if key != "$ref": |
143 patch_full_qualified_refs_in_domain(json[key], domain_name) | 158 has_exports = patch_full_qualified_refs_in_domain(json[key], dom ain_name) or has_exports |
144 continue | 159 continue |
145 if json["$ref"].find(".") == -1: | 160 if json["$ref"].find(".") == -1: |
146 json["$ref"] = domain_name + "." + json["$ref"] | 161 json["$ref"] = domain_name + "." + json["$ref"] |
162 return has_exports | |
147 | 163 |
164 json_api["has_exports"] = False | |
148 for domain in json_api["domains"]: | 165 for domain in json_api["domains"]: |
149 patch_full_qualified_refs_in_domain(domain, domain["domain"]) | 166 domain["has_exports"] = patch_full_qualified_refs_in_domain(domain, doma in["domain"]) |
167 json_api["has_exports"] = json_api["has_exports"] or domain["has_exports "] | |
168 | |
169 | |
170 def create_include_type_definition(domain_name, type): | |
171 # pylint: disable=W0622 | |
pfeldman
2016/07/22 18:02:40
While you are so smart...
| |
172 return { | |
173 "return_type": "std::unique_ptr<protocol::%s::Exported::%s>" % (domain_n ame, type["id"]), | |
174 "pass_type": "std::unique_ptr<protocol::%s::Exported::%s>" % (domain_nam e, type["id"]), | |
175 "to_raw_type": "%s.get()", | |
176 "to_pass_type": "std::move(%s)", | |
177 "to_rvalue": "std::move(%s)", | |
178 "type": "std::unique_ptr<protocol::%s::Exported::%s>" % (domain_name, ty pe["id"]), | |
179 "raw_type": "protocol::%s::Exported::%s" % (domain_name, type["id"]), | |
180 "raw_pass_type": "protocol::%s::Exported::%s*" % (domain_name, type["id" ]), | |
181 "raw_return_type": "protocol::%s::Exported::%s*" % (domain_name, type["i d"]), | |
182 } | |
150 | 183 |
151 | 184 |
152 def create_user_type_definition(domain_name, type): | 185 def create_user_type_definition(domain_name, type): |
153 return { | 186 return { |
154 "return_type": "std::unique_ptr<protocol::%s::%s>" % (domain_name, type[ "id"]), | 187 "return_type": "std::unique_ptr<protocol::%s::%s>" % (domain_name, type[ "id"]), |
155 "pass_type": "std::unique_ptr<protocol::%s::%s>" % (domain_name, type["i d"]), | 188 "pass_type": "std::unique_ptr<protocol::%s::%s>" % (domain_name, type["i d"]), |
156 "to_raw_type": "%s.get()", | 189 "to_raw_type": "%s.get()", |
157 "to_pass_type": "std::move(%s)", | 190 "to_pass_type": "std::move(%s)", |
158 "to_rvalue": "std::move(%s)", | 191 "to_rvalue": "std::move(%s)", |
159 "type": "std::unique_ptr<protocol::%s::%s>" % (domain_name, type["id"]), | 192 "type": "std::unique_ptr<protocol::%s::%s>" % (domain_name, type["id"]), |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
258 "out_type": "protocol::Array<%s>&" % type["raw_type"], | 291 "out_type": "protocol::Array<%s>&" % type["raw_type"], |
259 } | 292 } |
260 | 293 |
261 | 294 |
262 def create_type_definitions(): | 295 def create_type_definitions(): |
263 for domain in json_api["domains"]: | 296 for domain in json_api["domains"]: |
264 type_definitions[domain["domain"] + ".string"] = create_string_type_defi nition(domain["domain"]) | 297 type_definitions[domain["domain"] + ".string"] = create_string_type_defi nition(domain["domain"]) |
265 if not ("types" in domain): | 298 if not ("types" in domain): |
266 continue | 299 continue |
267 for type in domain["types"]: | 300 for type in domain["types"]: |
268 if type["type"] == "object": | 301 type_name = domain["domain"] + "." + type["id"] |
269 type_definitions[domain["domain"] + "." + type["id"]] = create_u ser_type_definition(domain["domain"], type) | 302 if type["type"] == "object" and domain["domain"] in include_domains: |
303 type_definitions[type_name] = create_include_type_definition(dom ain["domain"], type) | |
304 elif type["type"] == "object": | |
305 type_definitions[type_name] = create_user_type_definition(domain ["domain"], type) | |
270 elif type["type"] == "array": | 306 elif type["type"] == "array": |
271 items_type = type["items"]["type"] | 307 items_type = type["items"]["type"] |
272 type_definitions[domain["domain"] + "." + type["id"]] = wrap_arr ay_definition(type_definitions[items_type]) | 308 type_definitions[type_name] = wrap_array_definition(type_definit ions[items_type]) |
273 elif type["type"] == domain["domain"] + ".string": | 309 elif type["type"] == domain["domain"] + ".string": |
274 type_definitions[domain["domain"] + "." + type["id"]] = create_s tring_type_definition(domain["domain"]) | 310 type_definitions[type_name] = create_string_type_definition(doma in["domain"]) |
275 else: | 311 else: |
276 type_definitions[domain["domain"] + "." + type["id"]] = create_p rimitive_type_definition(type["type"]) | 312 type_definitions[type_name] = create_primitive_type_definition(t ype["type"]) |
277 | 313 |
278 | 314 |
279 def type_definition(name): | 315 def type_definition(name): |
280 return type_definitions[name] | 316 return type_definitions[name] |
281 | 317 |
282 | 318 |
283 def resolve_type(property): | 319 def resolve_type(property): |
284 if "$ref" in property: | 320 if "$ref" in property: |
285 return type_definitions[property["$ref"]] | 321 return type_definitions[property["$ref"]] |
286 if property["type"] == "array": | 322 if property["type"] == "array": |
287 return wrap_array_definition(resolve_type(property["items"])) | 323 return wrap_array_definition(resolve_type(property["items"])) |
288 return type_definitions[property["type"]] | 324 return type_definitions[property["type"]] |
289 | 325 |
290 | 326 |
291 def join_arrays(dict, keys): | 327 def join_arrays(dict, keys): |
292 result = [] | 328 result = [] |
293 for key in keys: | 329 for key in keys: |
294 if key in dict: | 330 if key in dict: |
295 result += dict[key] | 331 result += dict[key] |
296 return result | 332 return result |
297 | 333 |
298 | 334 |
299 def has_disable(commands): | 335 def has_disable(commands): |
300 for command in commands: | 336 for command in commands: |
301 if command["name"] == "disable": | 337 if command["name"] == "disable": |
302 return True | 338 return True |
303 return False | 339 return False |
304 | 340 |
305 | 341 |
342 def generate(domain_object, template, file_name): | |
343 template_context = { | |
344 "domain": domain_object, | |
345 "join_arrays": join_arrays, | |
346 "resolve_type": resolve_type, | |
347 "type_definition": type_definition, | |
348 "has_disable": has_disable, | |
349 "export_macro": export_macro, | |
350 "output_package": output_package, | |
351 "exported_package": exported_package, | |
352 "include_package": include_package | |
353 } | |
354 out_file = output_file(file_name) | |
355 out_file.write(template.render(template_context)) | |
356 out_file.close() | |
357 | |
358 | |
306 generate_domains = [] | 359 generate_domains = [] |
360 include_domains = [] | |
307 json_api = {} | 361 json_api = {} |
308 json_api["domains"] = parsed_json["domains"] | 362 json_api["domains"] = parsed_json["domains"] |
309 | 363 |
310 for domain in parsed_json["domains"]: | 364 for domain in parsed_json["domains"]: |
311 generate_domains.append(domain["domain"]) | 365 generate_domains.append(domain["domain"]) |
312 | 366 |
313 if include_file: | 367 if include_file: |
314 input_file = open(include_file, "r") | 368 input_file = open(include_file, "r") |
315 json_string = input_file.read() | 369 json_string = input_file.read() |
316 parsed_json = json.loads(json_string) | 370 parsed_json = json.loads(json_string) |
371 for domain in parsed_json["domains"]: | |
372 include_domains.append(domain["domain"]) | |
317 json_api["domains"] += parsed_json["domains"] | 373 json_api["domains"] += parsed_json["domains"] |
318 | 374 |
319 | |
320 patch_full_qualified_refs() | 375 patch_full_qualified_refs() |
321 create_type_definitions() | 376 create_type_definitions() |
322 | 377 |
378 if up_to_date(): | |
379 sys.exit() | |
323 if not os.path.exists(output_dirname): | 380 if not os.path.exists(output_dirname): |
324 os.mkdir(output_dirname) | 381 os.mkdir(output_dirname) |
382 if json_api["has_exports"] and not os.path.exists(exported_dirname): | |
383 os.mkdir(exported_dirname) | |
384 | |
325 jinja_env = initialize_jinja_env(output_dirname) | 385 jinja_env = initialize_jinja_env(output_dirname) |
326 | 386 h_template = jinja_env.get_template("/TypeBuilder_h.template") |
327 h_template_name = "/TypeBuilder_h.template" | 387 cpp_template = jinja_env.get_template("/TypeBuilder_cpp.template") |
328 cpp_template_name = "/TypeBuilder_cpp.template" | 388 exported_template = jinja_env.get_template("/Exported_h.template") |
329 h_template = jinja_env.get_template(h_template_name) | 389 imported_template = jinja_env.get_template("/Imported_h.template") |
330 cpp_template = jinja_env.get_template(cpp_template_name) | |
331 | |
332 | |
333 def generate(domain): | |
334 class_name = domain["domain"] | |
335 h_file_name = output_dirname + "/" + class_name + ".h" | |
336 cpp_file_name = output_dirname + "/" + class_name + ".cpp" | |
337 | |
338 template_context = { | |
339 "domain": domain, | |
340 "join_arrays": join_arrays, | |
341 "resolve_type": resolve_type, | |
342 "type_definition": type_definition, | |
343 "has_disable": has_disable, | |
344 "export_macro": export_macro, | |
345 "output_package": output_package, | |
346 } | |
347 h_file = output_file(h_file_name) | |
348 cpp_file = output_file(cpp_file_name) | |
349 h_file.write(h_template.render(template_context)) | |
350 cpp_file.write(cpp_template.render(template_context)) | |
351 h_file.close() | |
352 cpp_file.close() | |
353 | |
354 | 390 |
355 for domain in json_api["domains"]: | 391 for domain in json_api["domains"]: |
392 class_name = domain["domain"] | |
356 if domain["domain"] in generate_domains: | 393 if domain["domain"] in generate_domains: |
357 generate(domain) | 394 generate(domain, h_template, output_dirname + "/" + class_name + ".h") |
395 generate(domain, cpp_template, output_dirname + "/" + class_name + ".cpp ") | |
396 if domain["has_exports"]: | |
397 generate(domain, exported_template, exported_dirname + "/" + class_n ame + ".h") | |
398 if domain["domain"] in include_domains and domain["has_exports"]: | |
399 generate(domain, imported_template, output_dirname + "/" + class_name + ".h") | |
OLD | NEW |