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