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

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

Issue 2588843002: DevTools: speed up closure dependency checking (Closed)
Patch Set: fix Created 3 years, 11 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_preprocessor
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 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
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')) 218 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')) 219 jsdoc_validator_jar = to_platform_path(path.join(scripts_path, 'jsdoc_validator' , 'jsdoc_validator.jar'))
228 220
229 modules_dir = tempfile.mkdtemp()
230 common_closure_args = [ 221 common_closure_args = [
231 '--summary_detail_level', '3', 222 '--summary_detail_level', '3',
232 '--jscomp_error', 'visibility', 223 '--jscomp_error', 'visibility',
233 '--jscomp_warning', 'missingOverride', 224 '--jscomp_warning', 'missingOverride',
234 '--compilation_level', 'SIMPLE_OPTIMIZATIONS', 225 '--compilation_level', 'SIMPLE_OPTIMIZATIONS',
235 '--warning_level', 'VERBOSE', 226 '--warning_level', 'VERBOSE',
236 '--language_in=ES6_STRICT', 227 '--language_in=ES6_STRICT',
237 '--language_out=ES5_STRICT', 228 '--language_out=ES5_STRICT',
238 '--extra_annotation_name', 'suppressReceiverCheck', 229 '--extra_annotation_name', 'suppressReceiverCheck',
239 '--extra_annotation_name', 'suppressGlobalPropertiesCheck', 230 '--extra_annotation_name', 'suppressGlobalPropertiesCheck',
240 '--checks-only', 231 '--checks-only',
241 '--module_output_path_prefix', to_platform_path_exact(modules_dir + path.sep )
242 ] 232 ]
243 233
244 worker_modules_by_name = {} 234 worker_modules_by_name = {}
245 dependents_by_module_name = {} 235 dependents_by_module_name = {}
246 236
247 for module_name in descriptors.application: 237 for module_name in descriptors.application:
248 module = descriptors.modules[module_name] 238 module = descriptors.modules[module_name]
249 if descriptors.application[module_name].get('type', None) == 'worker': 239 if descriptors.application[module_name].get('type', None) == 'worker':
dgozman 2017/01/10 21:47:35 FYI: this is never true anymore, so we should foll
chenwilliam 2017/01/10 23:08:56 made a todo.
250 worker_modules_by_name[module_name] = module 240 worker_modules_by_name[module_name] = module
251 for dep in module.get('dependencies', []): 241 for dep in module.get('dependencies', []):
252 list = dependents_by_module_name.get(dep) 242 list = dependents_by_module_name.get(dep)
253 if not list: 243 if not list:
254 list = [] 244 list = []
255 dependents_by_module_name[dep] = list 245 dependents_by_module_name[dep] = list
256 list.append(module_name) 246 list.append(module_name)
257 247
258 248
259 def check_conditional_dependencies(): 249 def check_conditional_dependencies():
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
294 if referencing_module: 284 if referencing_module:
295 log_error('Duplicate use of %s in "%s" (previously seen in "%s") ' % (source, name, referencing_module)) 285 log_error('Duplicate use of %s in "%s" (previously seen in "%s") ' % (source, name, referencing_module))
296 seen_files[source] = name 286 seen_files[source] = name
297 287
298 for module_name in worker_modules_by_name: 288 for module_name in worker_modules_by_name:
299 check_module(worker_modules_by_name[module_name], {}, {}) 289 check_module(worker_modules_by_name[module_name], {}, {})
300 290
301 print 'Checking duplicate files across modules...' 291 print 'Checking duplicate files across modules...'
302 check_duplicate_files() 292 check_duplicate_files()
303 293
294 print 'Compiling frontend...'
304 295
305 def module_arg(module_name): 296 temp_devtools_path = tempfile.mkdtemp()
306 return ' --module ' + jsmodule_name_prefix + module_name
307 297
308 298
309 def modules_to_check(): 299 def prepare_closure_frontend_compile():
310 if len(sys.argv) == 1: 300 temp_frontend_path = path.join(temp_devtools_path, 'front_end')
311 return descriptors.sorted_modules() 301 checker = dependency_preprocessor.DependencyPreprocessor(descriptors, temp_f rontend_path, devtools_frontend_path)
312 print 'Compiling only these modules: %s' % sys.argv[1:] 302 checker.enforce_dependencies()
313 return [module for module in descriptors.sorted_modules() if module in set(s ys.argv[1:])]
314 303
304 command = common_closure_args + [
305 '--externs', to_platform_path(global_externs_file),
306 '--js', runtime_file,
307 ]
315 308
316 def dump_module(name, recursively, processed_modules): 309 all_files = descriptors.all_compiled_files()
317 if name in processed_modules: 310 args = []
318 return '' 311 for file in all_files:
319 processed_modules[name] = True 312 args.extend(['--js', file])
320 module = modules_by_name[name] 313 if "InspectorBackend.js" in file:
314 args.extend(['--js', protocol_externs_file])
315 command += args
316 command = [arg.replace(devtools_frontend_path, temp_frontend_path) for arg i n command]
dgozman 2017/01/10 21:47:34 Please double this is necessary.
chenwilliam 2017/01/10 23:08:56 just double checked it's a full path.
317 compiler_args_file = tempfile.NamedTemporaryFile(mode='wt', delete=False)
318 try:
319 compiler_args_file.write('devtools_frontend %s' % (' '.join(command)))
320 finally:
321 compiler_args_file.close()
322 return compiler_args_file.name
321 323
322 command = '' 324 compiler_args_file_path = prepare_closure_frontend_compile()
323 dependencies = module.get('dependencies', []) 325 frontend_compile_proc = popen(java_exec + ['-jar', closure_runner_jar, '--compil er-args-file', to_platform_path_exact(compiler_args_file_path)])
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 if name == 'protocol':
331 filtered_scripts.append(protocol_externs_file)
332 command += str(len(filtered_scripts))
333 first_dependency = True
334 for dependency in dependencies + [runtime_module_name]:
335 if first_dependency:
336 command += ':'
337 else:
338 command += ','
339 first_dependency = False
340 command += jsmodule_name_prefix + dependency
341 for script in filtered_scripts:
342 command += ' --js ' + to_platform_path(script)
343 return command
344 326
345 print 'Compiling frontend...' 327 print 'Compiling devtools_compatibility.js...'
346 328
347 compiler_args_file = tempfile.NamedTemporaryFile(mode='wt', delete=False) 329 closure_compiler_command = java_exec + [
348 try:
349 runtime_js_path = to_platform_path(path.join(devtools_frontend_path, 'Runtim e.js'))
350 checked_modules = modules_to_check()
351 for name in checked_modules:
352 closure_args = ' '.join(common_closure_args)
353 closure_args += ' --externs ' + to_platform_path(global_externs_file)
354 runtime_module = module_arg(runtime_module_name) + ':1 --js ' + runtime_ js_path
355 closure_args += runtime_module + dump_module(name, True, {})
356 compiler_args_file.write('%s %s%s' % (name, closure_args, os.linesep))
357 finally:
358 compiler_args_file.close()
359
360 modular_compiler_proc = popen(java_exec + ['-jar', closure_runner_jar, '--compil er-args-file', to_platform_path_exact(compiler_args_file.name)])
361
362 spawned_compiler_command = java_exec + [
363 '-jar', 330 '-jar',
364 closure_compiler_jar 331 closure_compiler_jar
365 ] + common_closure_args 332 ] + common_closure_args
366 333
367 print 'Compiling devtools_compatibility.js...' 334 devtools_js_compile_command = closure_compiler_command + [
368
369 command = spawned_compiler_command + [
370 '--externs', to_platform_path(global_externs_file), 335 '--externs', to_platform_path(global_externs_file),
371 '--externs', to_platform_path(path.join(devtools_frontend_path, 'host', 'Ins pectorFrontendHostAPI.js')), 336 '--externs', to_platform_path(path.join(devtools_frontend_path, 'host', 'Ins pectorFrontendHostAPI.js')),
372 '--jscomp_off=externsValidation', 337 '--jscomp_off=externsValidation',
373 '--module', jsmodule_name_prefix + 'devtools__compatibility_js' + ':1',
374 '--js', to_platform_path(path.join(devtools_frontend_path, 'devtools_compati bility.js')) 338 '--js', to_platform_path(path.join(devtools_frontend_path, 'devtools_compati bility.js'))
375 ] 339 ]
376 devtools_js_compile_proc = popen(command) 340 devtools_js_compile_proc = popen(devtools_js_compile_command)
377 341
378 print 'Verifying JSDoc comments...' 342 print 'Verifying JSDoc comments...'
379 errors_found |= verify_jsdoc() 343 errors_found |= verify_jsdoc()
380 (jsdoc_validator_proc, jsdoc_validator_file_list) = verify_jsdoc_extra() 344 (jsdoc_validator_proc, jsdoc_validator_file_list) = verify_jsdoc_extra()
381 345
382 print 346 print
383 347
384 (jsdoc_validator_out, _) = jsdoc_validator_proc.communicate() 348 (jsdoc_validator_out, _) = jsdoc_validator_proc.communicate()
385 if jsdoc_validator_out: 349 if jsdoc_validator_out:
386 print ('JSDoc validator output:%s%s' % (os.linesep, jsdoc_validator_out)) 350 print ('JSDoc validator output:%s%s' % (os.linesep, jsdoc_validator_out))
387 errors_found = True 351 errors_found = True
388 352
389 os.remove(jsdoc_validator_file_list.name) 353 os.remove(jsdoc_validator_file_list.name)
390 354
391 (module_compile_out, _) = modular_compiler_proc.communicate()
392 print 'Modular compilation output:'
393
394 start_module_regex = re.compile(r'^@@ START_MODULE:(.+) @@$')
395 end_module_regex = re.compile(r'^@@ END_MODULE @@$')
396
397 in_module = False
398 skipped_modules = {}
399 error_count = 0
400
401 def skip_dependents(module_name):
402 for skipped_module in dependents_by_module_name.get(module_name, []):
403 skipped_modules[skipped_module] = True
404
405 has_module_output = False
406
407 # pylint: disable=E1103
408 for line in module_compile_out.splitlines():
409 if not in_module:
410 match = re.search(start_module_regex, line)
411 if not match:
412 continue
413 in_module = True
414 has_module_output = True
415 module_error_count = 0
416 module_output = []
417 module_name = match.group(1)
418 skip_module = skipped_modules.get(module_name)
419 if skip_module:
420 skip_dependents(module_name)
421 else:
422 match = re.search(end_module_regex, line)
423 if not match:
424 if not skip_module:
425 module_output.append(line)
426 if has_errors(line):
427 error_count += 1
428 module_error_count += 1
429 skip_dependents(module_name)
430 continue
431
432 in_module = False
433 if skip_module:
434 print 'Skipping module %s...' % module_name
435 elif not module_error_count:
436 print 'Module %s compiled successfully: %s' % (module_name, module_o utput[0])
437 else:
438 print 'Module %s compile failed: %s errors%s' % (module_name, module _error_count, os.linesep)
439 print os.linesep.join(module_output)
440
441 if not has_module_output:
442 print module_compile_out
443
444 if error_count:
445 print 'Total Closure errors: %d%s' % (error_count, os.linesep)
446 errors_found = True
447
448 (devtools_js_compile_out, _) = devtools_js_compile_proc.communicate() 355 (devtools_js_compile_out, _) = devtools_js_compile_proc.communicate()
449 print 'devtools_compatibility.js compilation output:%s' % os.linesep, devtools_j s_compile_out 356 print 'devtools_compatibility.js compilation output:%s' % os.linesep, devtools_j s_compile_out
450 errors_found |= has_errors(devtools_js_compile_out) 357 errors_found |= has_errors(devtools_js_compile_out)
451 358
452 os.remove(compiler_args_file.name) 359 (frontend_compile_out, _) = frontend_compile_proc.communicate()
360 print 'devtools frontend compilation output:'
361 for line in frontend_compile_out.splitlines():
362 if "@@ START_MODULE" in line or "@@ END_MODULE" in line:
363 continue
364 print line
365 errors_found |= has_errors(frontend_compile_out)
366
453 os.remove(protocol_externs_file) 367 os.remove(protocol_externs_file)
454 shutil.rmtree(modules_dir, True) 368 os.remove(compiler_args_file_path)
369 shutil.rmtree(temp_devtools_path, True)
455 370
456 if errors_found: 371 if errors_found:
457 print 'ERRORS DETECTED' 372 print 'ERRORS DETECTED'
458 sys.exit(1) 373 sys.exit(1)
374 print 'DONE - compiled without errors'
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698