1 #!/usr/bin/env python 2 3 # Copyright 2014 The Chromium Authors. All rights reserved. 4 # Use of this source code is governed by a BSD-style license that can be 5 # found in the LICENSE file. 6 7 '''Uses the closure compiler to check the ChromeVox javascript files. 8 9 With no arguments, checks all ChromeVox scripts. If any arguments are 10 specified, only scripts that include any of the specified files will be 11 compiled. A useful argument list is the output of the command 12 'git diff --name-only --relative'. 13 ''' 14 15 import optparse 16 import os 17 import re 18 import sys 19 20 from multiprocessing import pool 21 22 from jsbundler import Bundle, CalcDeps, ReadSources 23 from jscompilerwrapper import RunCompiler 24 25 _SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 26 _CHROME_SOURCE_DIR = os.path.normpath( 27 os.path.join(_SCRIPT_DIR, *[os.path.pardir] * 6)) 28 29 30 def CVoxPath(path='.'): 31 '''Converts a path relative to the top-level chromevox directory to a 32 path relative to the current directory. 33 ''' 34 return os.path.relpath(os.path.join(_SCRIPT_DIR, '..', path)) 35 36 37 # Externs common to many ChromeVox scripts. 38 _COMMON_EXTERNS = [ 39 CVoxPath('common/externs.js'), 40 CVoxPath('common/chrome_extension_externs.js'), 41 CVoxPath('chromevox/background/externs.js'), 42 CVoxPath('chromevox/injected/externs.js'), 43 CVoxPath('liblouis_nacl/externs.js'), 44 CVoxPath('host/chrome/externs.js')] 45 46 # List of top-level scripts and externs that we can check. 47 _TOP_LEVEL_SCRIPTS = [ 48 [[CVoxPath('chromevox/background/kbexplorer_loader.js')], 49 [CVoxPath('common/chrome_extension_externs.js')]], 50 [[CVoxPath('chromevox/background/loader.js')], _COMMON_EXTERNS], 51 [[CVoxPath('chromevox/background/options_loader.js')], _COMMON_EXTERNS], 52 [[CVoxPath('chromevox/injected/loader.js')], _COMMON_EXTERNS], 53 ] 54 55 56 def _Compile(js_files, externs): 57 try: 58 return RunCompiler(js_files, externs) 59 except KeyboardInterrupt: 60 return (False, 'KeyboardInterrupt') 61 62 63 def CheckChromeVox(changed_files=None): 64 if changed_files is not None: 65 changed_files_set = frozenset( 66 (os.path.relpath(path) for path in changed_files)) 67 if len(changed_files_set) == 0: 68 return (True, '') 69 else: 70 changed_files_set = None 71 ret_success = True 72 ret_output = '' 73 roots = [CVoxPath(), 74 os.path.relpath( 75 os.path.join( 76 _CHROME_SOURCE_DIR, 77 'chrome/third_party/chromevox/third_party/closure-library/' 78 'closure/goog'))] 79 sources = ReadSources(roots, need_source_text=True, 80 exclude=[re.compile('testing')]) 81 work_pool = pool.Pool(len(_TOP_LEVEL_SCRIPTS)) 82 try: 83 results = [] 84 for top_level in _TOP_LEVEL_SCRIPTS: 85 tl_files, externs = top_level 86 bundle = Bundle() 87 CalcDeps(bundle, sources, tl_files) 88 bundle.Add((sources[name] for name in tl_files)) 89 ordered_paths = list(bundle.GetInPaths()) 90 if (changed_files_set is not None and 91 changed_files_set.isdisjoint(ordered_paths + externs)): 92 continue 93 print 'Compiling %s' % ','.join(tl_files) 94 results.append([tl_files, 95 work_pool.apply_async( 96 _Compile, 97 args=[ordered_paths, externs])]) 98 for result in results: 99 tl_files = result[0] 100 success, output = result[1].get() 101 if not success: 102 ret_output += '\nFrom compiling %s:\n%s\n' % (','.join(tl_files), 103 output) 104 ret_success = False 105 work_pool.close() 106 except: 107 work_pool.terminate() 108 raise 109 finally: 110 work_pool.join() 111 return (ret_success, ret_output) 112 113 114 def main(): 115 parser = optparse.OptionParser(description=__doc__) 116 parser.usage = '%prog [<changed_file>...]' 117 _, args = parser.parse_args() 118 119 changed_paths = None 120 if len(args) > 0: 121 changed_paths = (os.path.relpath(p) for p in args) 122 success, output = CheckChromeVox(changed_paths) 123 if len(output) > 0: 124 print output 125 return int(not success) 126 127 128 if __name__ == '__main__': 129 sys.exit(main()) 130