16e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#!/usr/bin/python
25f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved.
35f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
45f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# found in the LICENSE file.
55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
66e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)"""Runs Closure compiler on a JavaScript file to check for errors."""
76e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
86e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import argparse
95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import os
105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import re
115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import subprocess
125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import sys
135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import tempfile
146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)import processor
155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class Checker(object):
186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  """Runs the Closure compiler on a given source file and returns the
196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  success/errors."""
206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  _COMMON_CLOSURE_ARGS = [
225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--accept_const_keyword",
235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=accessControls",
245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=ambiguousFunctionDecl",
255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=checkStructDictInheritance",
265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=checkTypes",
275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=checkVars",
285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=constantProperty",
295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=deprecated",
305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=externsValidation",
315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=globalThis",
325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=invalidCasts",
335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=misplacedTypeAnnotation",
345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=missingProperties",
355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=missingReturn",
365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=nonStandardJsDocs",
375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=suspiciousCode",
385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=undefinedNames",
395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=undefinedVars",
405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=unknownDefines",
415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=uselessCode",
425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_error=visibility",
435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # TODO(dbeam): happens when the same file is <include>d multiple times.
445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "--jscomp_off=duplicate",
4503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    "--language_in=ECMASCRIPT5_STRICT",
4603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    "--summary_detail_level=3",
475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  ]
485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  _JAR_COMMAND = [
505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "java",
515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "-jar",
525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "-Xms1024m",
5303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    "-client",
545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    "-XX:+TieredCompilation"
555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  ]
565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  _found_java = False
586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def __init__(self, verbose=False):
605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    current_dir = os.path.join(os.path.dirname(__file__))
615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._compiler_jar = os.path.join(current_dir, "lib", "compiler.jar")
625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._runner_jar = os.path.join(current_dir, "runner", "runner.jar")
635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._temp_files = []
645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._verbose = verbose
655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _clean_up(self):
675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if not self._temp_files:
685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return
695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    self._debug("Deleting temporary files: %s" % ", ".join(self._temp_files))
715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    for f in self._temp_files:
725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      os.remove(f)
735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._temp_files = []
745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _debug(self, msg, error=False):
765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if self._verbose:
776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      print "(INFO) %s" % msg
785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _error(self, msg):
806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    print >> sys.stderr, "(ERROR) %s" % msg
815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._clean_up()
825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _run_command(self, cmd):
846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    """Runs a shell command.
856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    Args:
876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        cmd: A list of tokens to be joined into a shell command.
886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    Return:
906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        True if the exit code was 0, else False.
916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    """
925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cmd_str = " ".join(cmd)
936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    self._debug("Running command: %s" % cmd_str)
945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    devnull = open(os.devnull, "w")
965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return subprocess.Popen(
975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        cmd_str, stdout=devnull, stderr=subprocess.PIPE, shell=True)
985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _check_java_path(self):
1006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    """Checks that `java` is on the system path."""
1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if not self._found_java:
1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      proc = self._run_command(["which", "java"])
1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      proc.communicate()
1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if proc.returncode == 0:
1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self._found_java = True
1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      else:
1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self._error("Cannot find java (`which java` => %s)" % proc.returncode)
1085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return self._found_java
1105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  def _run_jar(self, jar, args=None):
1126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    args = args or []
1135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._check_java_path()
1146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return self._run_command(self._JAR_COMMAND + [jar] + args)
1155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _fix_line_number(self, match):
1176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    """Changes a line number from /tmp/file:300 to /orig/file:100.
1186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    Args:
1206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        match: A re.MatchObject from matching against a line number regex.
1216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    Returns:
1236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        The fixed up /file and :line number.
1246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    """
1256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    real_file = self._processor.get_file_from_line(match.group(1))
1265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return "%s:%d" % (os.path.abspath(real_file.file), real_file.line_number)
1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _fix_up_error(self, error):
1296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    """Filter out irrelevant errors or fix line numbers.
1306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    Args:
1326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        error: A Closure compiler error (2 line string with error and source).
1336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    Return:
1356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        The fixed up erorr string (blank if it should be ignored).
1366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    """
1375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if " first declared in " in error:
1385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      # Ignore "Variable x first declared in /same/file".
1395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return ""
1405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    expanded_file = self._expanded_file
1426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    fixed = re.sub("%s:(\d+)" % expanded_file, self._fix_line_number, error)
1436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return fixed.replace(expanded_file, os.path.abspath(self._file_arg))
1445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _format_errors(self, errors):
1466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    """Formats Closure compiler errors to easily spot compiler output."""
1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    errors = filter(None, errors)
1486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    contents = "\n## ".join("\n\n".join(errors).splitlines())
1496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return "## %s" % contents if contents else ""
1505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _create_temp_file(self, contents):
1526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    with tempfile.NamedTemporaryFile(mode="wt", delete=False) as tmp_file:
1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self._temp_files.append(tmp_file.name)
1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      tmp_file.write(contents)
1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return tmp_file.name
1565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  def check(self, source_file, depends=None, externs=None):
1586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    """Closure compile a file and check for errors.
1596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    Args:
1616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        source_file: A file to check.
1626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        depends: Other files that would be included with a <script> earlier in
1636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            the page.
1646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        externs: @extern files that inform the compiler about custom globals.
1656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    Returns:
1676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        (exitcode, output) The exit code of the Closure compiler (as a number)
1686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            and its output (as a string).
1696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    """
1706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    depends = depends or []
1716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    externs = externs or []
1726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if not self._check_java_path():
1745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return 1, ""
1755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    self._debug("FILE: %s" % source_file)
1775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if source_file.endswith("_externs.js"):
1796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      self._debug("Skipping externs: %s" % source_file)
1805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return
1815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    self._file_arg = source_file
1835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    tmp_dir = tempfile.gettempdir()
1855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    rel_path = lambda f: os.path.join(os.path.relpath(os.getcwd(), tmp_dir), f)
1865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    includes = [rel_path(f) for f in depends + [source_file]]
1886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    contents = ['<include src="%s">' % i for i in includes]
1895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    meta_file = self._create_temp_file("\n".join(contents))
1906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    self._debug("Meta file: %s" % meta_file)
1915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    self._processor = processor.Processor(meta_file)
1936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    self._expanded_file = self._create_temp_file(self._processor.contents)
1946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    self._debug("Expanded file: %s" % self._expanded_file)
1955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    args = ["--js=%s" % self._expanded_file]
1976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    args += ["--externs=%s" % e for e in externs]
1986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    args_file_content = " %s" % " ".join(self._COMMON_CLOSURE_ARGS + args)
1996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    self._debug("Args: %s" % args_file_content.strip())
2005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    args_file = self._create_temp_file(args_file_content)
2026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    self._debug("Args file: %s" % args_file)
2035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    runner_args = ["--compiler-args-file=%s" % args_file]
2055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    runner_cmd = self._run_jar(self._runner_jar, args=runner_args)
2065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    (_, stderr) = runner_cmd.communicate()
2075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    errors = stderr.strip().split("\n\n")
2096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    self._debug("Summary: %s" % errors.pop())
2105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    output = self._format_errors(map(self._fix_up_error, errors))
2125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if runner_cmd.returncode:
2136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      self._error("Error in: %s%s" % (source_file, "\n" + output if output else ""))
2145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    elif output:
2156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      self._debug("Output: %s" % output)
2165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._clean_up()
2185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return runner_cmd.returncode, output
2206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)if __name__ == "__main__":
2236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  parser = argparse.ArgumentParser(
2246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      description="Typecheck JavaScript using Closure compiler")
2256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  parser.add_argument("sources", nargs=argparse.ONE_OR_MORE,
2266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                      help="Path to a source file to typecheck")
2276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  parser.add_argument("-d", "--depends", nargs=argparse.ZERO_OR_MORE)
2286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  parser.add_argument("-e", "--externs", nargs=argparse.ZERO_OR_MORE)
2296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  parser.add_argument("-o", "--out_file", help="A place to output results")
2306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  parser.add_argument("-v", "--verbose", action="store_true",
2316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                      help="Show more information as this script runs")
2326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  opts = parser.parse_args()
2336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  checker = Checker(verbose=opts.verbose)
2356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  for source in opts.sources:
23603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    exit, _ = checker.check(source, depends=opts.depends, externs=opts.externs)
23703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if exit != 0:
23803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      sys.exit(exit)
2396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if opts.out_file:
2416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      out_dir = os.path.dirname(opts.out_file)
2426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      if not os.path.exists(out_dir):
2436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        os.makedirs(out_dir)
2446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      # TODO(dbeam): write compiled file to |opts.out_file|.
2456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      open(opts.out_file, "w").write("")
246