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

Side by Side Diff: third_party/WebKit/Source/devtools/scripts/compile_frontend.py

Issue 2588843002: DevTools: speed up closure dependency checking (Closed)
Patch Set: update compile_frontend Created 3 years, 12 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 unified diff | Download patch
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 Google Inc. All rights reserved. 2 # Copyright (c) 2012 Google Inc. All rights reserved.
3 # 3 #
4 # Redistribution and use in source and binary forms, with or without 4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are 5 # modification, are permitted provided that the following conditions are
6 # met: 6 # met:
7 # 7 #
8 # * Redistributions of source code must retain the above copyright 8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer. 9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above 10 # * Redistributions in binary form must reproduce the above
(...skipping 20 matching lines...) Expand all
31 import os.path as path 31 import os.path as path
32 import re 32 import re
33 import shutil 33 import shutil
34 import subprocess 34 import subprocess
35 import sys 35 import sys
36 import tempfile 36 import tempfile
37 37
38 from build import modular_build 38 from build import modular_build
39 from build import generate_protocol_externs 39 from build import generate_protocol_externs
40 40
41 import dependency_checker
41 import utils 42 import utils
42 43
43 try: 44 try:
44 import simplejson as json 45 import simplejson as json
45 except ImportError: 46 except ImportError:
46 import json 47 import json
47 48
48
49 if len(sys.argv) == 2 and sys.argv[1] == '--help':
50 print("Usage: %s [module_names]" % path.basename(sys.argv[0]))
51 print(" module_names list of modules for which the Closure compilation s hould run.")
52 print(" If absent, the entire frontend will be compiled.")
53 sys.exit(0)
54
55 is_cygwin = sys.platform == 'cygwin' 49 is_cygwin = sys.platform == 'cygwin'
56 50
57 51
58 def popen(arguments): 52 def popen(arguments):
59 return subprocess.Popen(arguments, stdout=subprocess.PIPE, stderr=subprocess .STDOUT) 53 return subprocess.Popen(arguments, stdout=subprocess.PIPE, stderr=subprocess .STDOUT)
60 54
61 def to_platform_path(filepath): 55 def to_platform_path(filepath):
62 if not is_cygwin: 56 if not is_cygwin:
63 return filepath 57 return filepath
64 return re.sub(r'^/cygdrive/(\w)', '\\1:', filepath) 58 return re.sub(r'^/cygdrive/(\w)', '\\1:', filepath)
65 59
66 60
67 def to_platform_path_exact(filepath): 61 def to_platform_path_exact(filepath):
68 if not is_cygwin: 62 if not is_cygwin:
69 return filepath 63 return filepath
70 output, _ = popen(['cygpath', '-w', filepath]).communicate() 64 output, _ = popen(['cygpath', '-w', filepath]).communicate()
71 # pylint: disable=E1103 65 # pylint: disable=E1103
72 return output.strip().replace('\\', '\\\\') 66 return output.strip().replace('\\', '\\\\')
73 67
74 scripts_path = path.dirname(path.abspath(__file__)) 68 scripts_path = path.dirname(path.abspath(__file__))
75 devtools_path = path.dirname(scripts_path) 69 devtools_path = path.dirname(scripts_path)
76 inspector_path = path.join(path.dirname(devtools_path), 'core', 'inspector') 70 inspector_path = path.join(path.dirname(devtools_path), 'core', 'inspector')
77 # TODO(dgozman): move these checks to v8. 71 # TODO(dgozman): move these checks to v8.
78 v8_inspector_path = path.normpath(path.join(path.dirname(devtools_path), os.pard ir, os.pardir, os.pardir, 'v8', 'src', 'inspector')) 72 v8_inspector_path = path.normpath(path.join(path.dirname(devtools_path), os.pard ir, os.pardir, os.pardir, 'v8', 'src', 'inspector'))
79 devtools_frontend_path = path.join(devtools_path, 'front_end') 73 devtools_frontend_path = path.join(devtools_path, 'front_end')
80 global_externs_file = to_platform_path(path.join(devtools_frontend_path, 'extern s.js')) 74 global_externs_file = to_platform_path(path.join(devtools_frontend_path, 'extern s.js'))
81 protocol_externs_file = path.join(devtools_frontend_path, 'protocol_externs.js') 75 protocol_externs_file = path.join(devtools_frontend_path, 'protocol_externs.js')
82 76 runtime_file = to_platform_path(path.join(devtools_frontend_path, 'Runtime.js'))
83 jsmodule_name_prefix = 'jsmodule_'
84 runtime_module_name = '_runtime'
85 77
86 type_checked_jsdoc_tags_list = ['param', 'return', 'type', 'enum'] 78 type_checked_jsdoc_tags_list = ['param', 'return', 'type', 'enum']
87 type_checked_jsdoc_tags_or = '|'.join(type_checked_jsdoc_tags_list) 79 type_checked_jsdoc_tags_or = '|'.join(type_checked_jsdoc_tags_list)
88 80
89 # Basic regex for invalid JsDoc types: an object type name ([A-Z][_A-Za-z0-9.]+[ A-Za-z0-9]) not preceded by '!', '?', ':' (this, new), or '.' (object property). 81 # Basic regex for invalid JsDoc types: an object type name ([A-Z][_A-Za-z0-9.]+[ A-Za-z0-9]) not preceded by '!', '?', ':' (this, new), or '.' (object property).
90 invalid_type_regex = re.compile(r'@(?:' + type_checked_jsdoc_tags_or + r')\s*\{. *(?<![!?:._A-Za-z0-9])([A-Z][_A-Za-z0-9.]+[A-Za-z0-9])[^/]*\}') 82 invalid_type_regex = re.compile(r'@(?:' + type_checked_jsdoc_tags_or + r')\s*\{. *(?<![!?:._A-Za-z0-9])([A-Z][_A-Za-z0-9.]+[A-Za-z0-9])[^/]*\}')
91 invalid_type_designator_regex = re.compile(r'@(?:' + type_checked_jsdoc_tags_or + r')\s*.*(?<![{: ])([?!])=?\}') 83 invalid_type_designator_regex = re.compile(r'@(?:' + type_checked_jsdoc_tags_or + r')\s*.*(?<![{: ])([?!])=?\}')
92 invalid_non_object_type_regex = re.compile(r'@(?:' + type_checked_jsdoc_tags_or + r')\s*\{.*(![a-z]+)[^/]*\}') 84 invalid_non_object_type_regex = re.compile(r'@(?:' + type_checked_jsdoc_tags_or + r')\s*\{.*(![a-z]+)[^/]*\}')
93 error_warning_regex = re.compile(r'WARNING|ERROR') 85 error_warning_regex = re.compile(r'WARNING|ERROR')
94 loaded_css_regex = re.compile(r'(?:registerRequiredCSS|WebInspector\.View\.creat eStyleElement)\s*\(\s*"(.+)"\s*\)') 86 loaded_css_regex = re.compile(r'(?:registerRequiredCSS|WebInspector\.View\.creat eStyleElement)\s*\(\s*"(.+)"\s*\)')
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
216 208
217 if not is_ok: 209 if not is_ok:
218 print 'NOTE: Java executable version %d.%d or above not found in $PATH.' % (required_major, required_minor) 210 print 'NOTE: Java executable version %d.%d or above not found in $PATH.' % (required_major, required_minor)
219 sys.exit(1) 211 sys.exit(1)
220 print 'Java executable: %s%s' % (java_path, '' if has_server_jvm else ' (no server JVM)') 212 print 'Java executable: %s%s' % (java_path, '' if has_server_jvm else ' (no server JVM)')
221 return exec_command 213 return exec_command
222 214
223 java_exec = find_java() 215 java_exec = find_java()
224 216
225 closure_compiler_jar = to_platform_path(path.join(scripts_path, 'closure', 'comp iler.jar')) 217 closure_compiler_jar = to_platform_path(path.join(scripts_path, 'closure', 'comp iler.jar'))
226 closure_runner_jar = to_platform_path(path.join(scripts_path, 'closure', 'closur e_runner', 'closure_runner.jar'))
227 jsdoc_validator_jar = to_platform_path(path.join(scripts_path, 'jsdoc_validator' , 'jsdoc_validator.jar')) 218 jsdoc_validator_jar = to_platform_path(path.join(scripts_path, 'jsdoc_validator' , 'jsdoc_validator.jar'))
228 219
229 modules_dir = tempfile.mkdtemp()
230 common_closure_args = [ 220 common_closure_args = [
231 '--summary_detail_level', '3', 221 '--summary_detail_level', '3',
232 '--jscomp_error', 'visibility', 222 '--jscomp_error', 'visibility',
233 '--jscomp_warning', 'missingOverride', 223 '--jscomp_warning', 'missingOverride',
234 '--compilation_level', 'SIMPLE_OPTIMIZATIONS', 224 '--compilation_level', 'SIMPLE_OPTIMIZATIONS',
235 '--warning_level', 'VERBOSE', 225 '--warning_level', 'VERBOSE',
236 '--language_in=ES6_STRICT', 226 '--language_in=ES6_STRICT',
237 '--language_out=ES5_STRICT', 227 '--language_out=ES5_STRICT',
238 '--extra_annotation_name', 'suppressReceiverCheck', 228 '--extra_annotation_name', 'suppressReceiverCheck',
239 '--extra_annotation_name', 'suppressGlobalPropertiesCheck', 229 '--extra_annotation_name', 'suppressGlobalPropertiesCheck',
240 '--checks-only', 230 '--checks-only',
241 '--module_output_path_prefix', to_platform_path_exact(modules_dir + path.sep )
242 ] 231 ]
243 232
244 worker_modules_by_name = {} 233 worker_modules_by_name = {}
245 dependents_by_module_name = {} 234 dependents_by_module_name = {}
246 235
247 for module_name in descriptors.application: 236 for module_name in descriptors.application:
248 module = descriptors.modules[module_name] 237 module = descriptors.modules[module_name]
249 if descriptors.application[module_name].get('type', None) == 'worker': 238 if descriptors.application[module_name].get('type', None) == 'worker':
250 worker_modules_by_name[module_name] = module 239 worker_modules_by_name[module_name] = module
251 for dep in module.get('dependencies', []): 240 for dep in module.get('dependencies', []):
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
294 if referencing_module: 283 if referencing_module:
295 log_error('Duplicate use of %s in "%s" (previously seen in "%s") ' % (source, name, referencing_module)) 284 log_error('Duplicate use of %s in "%s" (previously seen in "%s") ' % (source, name, referencing_module))
296 seen_files[source] = name 285 seen_files[source] = name
297 286
298 for module_name in worker_modules_by_name: 287 for module_name in worker_modules_by_name:
299 check_module(worker_modules_by_name[module_name], {}, {}) 288 check_module(worker_modules_by_name[module_name], {}, {})
300 289
301 print 'Checking duplicate files across modules...' 290 print 'Checking duplicate files across modules...'
302 check_duplicate_files() 291 check_duplicate_files()
303 292
304
305 def module_arg(module_name):
306 return ' --module ' + jsmodule_name_prefix + module_name
307
308
309 def modules_to_check():
310 if len(sys.argv) == 1:
311 return descriptors.sorted_modules()
312 print 'Compiling only these modules: %s' % sys.argv[1:]
313 return [module for module in descriptors.sorted_modules() if module in set(s ys.argv[1:])]
314
315
316 def dump_module(name, recursively, processed_modules):
317 if name in processed_modules:
318 return ''
319 processed_modules[name] = True
320 module = modules_by_name[name]
321
322 command = ''
323 dependencies = module.get('dependencies', [])
324 if recursively:
325 for dependency in dependencies:
326 command += dump_module(dependency, recursively, processed_modules)
327 command += module_arg(name) + ':'
328 filtered_scripts = descriptors.module_compiled_files(name)
329 filtered_scripts = [path.join(devtools_frontend_path, name, script) for scri pt in filtered_scripts]
330 # TODO(dgozman): move to separate module
331 if name == 'sdk':
332 filtered_scripts.append(protocol_externs_file)
333 command += str(len(filtered_scripts))
334 first_dependency = True
335 for dependency in dependencies + [runtime_module_name]:
336 if first_dependency:
337 command += ':'
338 else:
339 command += ','
340 first_dependency = False
341 command += jsmodule_name_prefix + dependency
342 for script in filtered_scripts:
343 command += ' --js ' + to_platform_path(script)
344 return command
345
346 print 'Compiling frontend...' 293 print 'Compiling frontend...'
347 294
348 compiler_args_file = tempfile.NamedTemporaryFile(mode='wt', delete=False) 295 closure_compiler_command = java_exec + [
349 try:
350 runtime_js_path = to_platform_path(path.join(devtools_frontend_path, 'Runtim e.js'))
351 checked_modules = modules_to_check()
352 for name in checked_modules:
353 closure_args = ' '.join(common_closure_args)
354 closure_args += ' --externs ' + to_platform_path(global_externs_file)
355 runtime_module = module_arg(runtime_module_name) + ':1 --js ' + runtime_ js_path
356 closure_args += runtime_module + dump_module(name, True, {})
357 compiler_args_file.write('%s %s%s' % (name, closure_args, os.linesep))
358 finally:
359 compiler_args_file.close()
360
361 modular_compiler_proc = popen(java_exec + ['-jar', closure_runner_jar, '--compil er-args-file', to_platform_path_exact(compiler_args_file.name)])
362
363 spawned_compiler_command = java_exec + [
364 '-jar', 296 '-jar',
365 closure_compiler_jar 297 closure_compiler_jar
366 ] + common_closure_args 298 ] + common_closure_args
367 299
300 temp_devtools_path = tempfile.mkdtemp()
301 temp_frontend_path = path.join(temp_devtools_path, 'front_end')
302
303
304 def prepare_closure_frontend_compile():
305 checker = dependency_checker.DependencyChecker(descriptors, temp_frontend_pa th)
306 checker.enforce_dependencies()
307
308 command = closure_compiler_command + [
309 '--externs', to_platform_path(global_externs_file),
310 '--js', runtime_file,
311 ]
312
313 all_files = descriptors.all_compiled_files()
314 args = []
315 for file in all_files:
316 args.extend(['--js', file])
317 if "InspectorBackend.js" in file:
318 # TODO(dgozman): move to separate module
319 args.extend(['--js', protocol_externs_file])
320 command += args
321 command = [arg.replace(devtools_frontend_path, temp_frontend_path) for arg i n command]
322 return command
323
324 frontend_compile_proc = popen(prepare_closure_frontend_compile())
325
368 print 'Compiling devtools_compatibility.js...' 326 print 'Compiling devtools_compatibility.js...'
369 327
370 command = spawned_compiler_command + [ 328 devtools_js_compile_command = closure_compiler_command + [
371 '--externs', to_platform_path(global_externs_file), 329 '--externs', to_platform_path(global_externs_file),
372 '--externs', to_platform_path(path.join(devtools_frontend_path, 'host', 'Ins pectorFrontendHostAPI.js')), 330 '--externs', to_platform_path(path.join(devtools_frontend_path, 'host', 'Ins pectorFrontendHostAPI.js')),
373 '--jscomp_off=externsValidation', 331 '--jscomp_off=externsValidation',
374 '--module', jsmodule_name_prefix + 'devtools__compatibility_js' + ':1',
375 '--js', to_platform_path(path.join(devtools_frontend_path, 'devtools_compati bility.js')) 332 '--js', to_platform_path(path.join(devtools_frontend_path, 'devtools_compati bility.js'))
376 ] 333 ]
377 devtools_js_compile_proc = popen(command) 334 devtools_js_compile_proc = popen(devtools_js_compile_command)
378 335
379 print 'Verifying JSDoc comments...' 336 print 'Verifying JSDoc comments...'
380 errors_found |= verify_jsdoc() 337 errors_found |= verify_jsdoc()
381 (jsdoc_validator_proc, jsdoc_validator_file_list) = verify_jsdoc_extra() 338 (jsdoc_validator_proc, jsdoc_validator_file_list) = verify_jsdoc_extra()
382 339
383 print 340 print
384 341
385 (jsdoc_validator_out, _) = jsdoc_validator_proc.communicate() 342 (jsdoc_validator_out, _) = jsdoc_validator_proc.communicate()
386 if jsdoc_validator_out: 343 if jsdoc_validator_out:
387 print ('JSDoc validator output:%s%s' % (os.linesep, jsdoc_validator_out)) 344 print ('JSDoc validator output:%s%s' % (os.linesep, jsdoc_validator_out))
388 errors_found = True 345 errors_found = True
389 346
390 os.remove(jsdoc_validator_file_list.name) 347 os.remove(jsdoc_validator_file_list.name)
391 348
392 (module_compile_out, _) = modular_compiler_proc.communicate()
393 print 'Modular compilation output:'
394
395 start_module_regex = re.compile(r'^@@ START_MODULE:(.+) @@$')
396 end_module_regex = re.compile(r'^@@ END_MODULE @@$')
397
398 in_module = False
399 skipped_modules = {}
400 error_count = 0
401
402 def skip_dependents(module_name):
403 for skipped_module in dependents_by_module_name.get(module_name, []):
404 skipped_modules[skipped_module] = True
405
406 has_module_output = False
407
408 # pylint: disable=E1103
409 for line in module_compile_out.splitlines():
410 if not in_module:
411 match = re.search(start_module_regex, line)
412 if not match:
413 continue
414 in_module = True
415 has_module_output = True
416 module_error_count = 0
417 module_output = []
418 module_name = match.group(1)
419 skip_module = skipped_modules.get(module_name)
420 if skip_module:
421 skip_dependents(module_name)
422 else:
423 match = re.search(end_module_regex, line)
424 if not match:
425 if not skip_module:
426 module_output.append(line)
427 if has_errors(line):
428 error_count += 1
429 module_error_count += 1
430 skip_dependents(module_name)
431 continue
432
433 in_module = False
434 if skip_module:
435 print 'Skipping module %s...' % module_name
436 elif not module_error_count:
437 print 'Module %s compiled successfully: %s' % (module_name, module_o utput[0])
438 else:
439 print 'Module %s compile failed: %s errors%s' % (module_name, module _error_count, os.linesep)
440 print os.linesep.join(module_output)
441
442 if not has_module_output:
443 print module_compile_out
444
445 if error_count:
446 print 'Total Closure errors: %d%s' % (error_count, os.linesep)
447 errors_found = True
448
449 (devtools_js_compile_out, _) = devtools_js_compile_proc.communicate() 349 (devtools_js_compile_out, _) = devtools_js_compile_proc.communicate()
450 print 'devtools_compatibility.js compilation output:%s' % os.linesep, devtools_j s_compile_out 350 print 'devtools_compatibility.js compilation output:%s' % os.linesep, devtools_j s_compile_out
451 errors_found |= has_errors(devtools_js_compile_out) 351 errors_found |= has_errors(devtools_js_compile_out)
452 352
453 os.remove(compiler_args_file.name) 353 (frontend_compile_out, _) = frontend_compile_proc.communicate()
354 print 'devtools frontend compilation output:%s' % os.linesep, frontend_compile_o ut
355 errors_found |= has_errors(devtools_js_compile_out)
356
454 os.remove(protocol_externs_file) 357 os.remove(protocol_externs_file)
455 shutil.rmtree(modules_dir, True) 358 shutil.rmtree(temp_devtools_path, True)
456 359
457 if errors_found: 360 if errors_found:
458 print 'ERRORS DETECTED' 361 print 'ERRORS DETECTED'
459 sys.exit(1) 362 sys.exit(1)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698