check_chromevox.py revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
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
9With no arguments, checks all ChromeVox scripts.  If any arguments are
10specified, only scripts that include any of the specified files will be
11compiled.  A useful argument list is the output of the command
12'git diff --name-only --relative'.
13'''
14
15import optparse
16import os
17import re
18import sys
19
20from multiprocessing import pool
21
22from jsbundler import Bundle, CalcDeps, ReadSources
23from 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
30def 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
56def _Compile(js_files, externs):
57  try:
58    return RunCompiler(js_files, externs)
59  except KeyboardInterrupt:
60    return (False, 'KeyboardInterrupt')
61
62
63def 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
114def 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
128if __name__ == '__main__':
129  sys.exit(main())
130