1#!/usr/bin/env python
2#
3# litlint
4#
5# Ensure RUN commands in lit tests are free of common errors.
6#
7# If any errors are detected, litlint returns a nonzero exit code.
8#
9
10import optparse
11import re
12import sys
13from io import open
14
15# Compile regex once for all files
16runRegex = re.compile(r'(?<!-o)(?<!%run) %t\s')
17
18def LintLine(s):
19  """ Validate a line
20
21  Args:
22    s: str, the line to validate
23
24  Returns:
25    Returns an error message and a 1-based column number if an error was
26    detected, otherwise (None, None).
27  """
28
29  # Check that RUN command can be executed with an emulator
30  m = runRegex.search(s)
31  if m:
32    start, end = m.span()
33    return ('missing %run before %t', start + 2)
34
35  # No errors
36  return (None, None)
37
38
39def LintFile(p):
40  """ Check that each RUN command can be executed with an emulator
41
42  Args:
43    p: str, valid path to a file
44
45  Returns:
46    The number of errors detected.
47  """
48  errs = 0
49  with open(p, 'r', encoding='utf-8') as f:
50    for i, s in enumerate(f.readlines(), start=1):
51      msg, col = LintLine(s)
52      if msg != None:
53        errs += 1
54        errorMsg = 'litlint: {}:{}:{}: error: {}.\n{}{}\n'
55        arrow = (col-1) * ' ' + '^'
56        sys.stderr.write(errorMsg.format(p, i, col, msg, s, arrow))
57  return errs
58
59
60if __name__ == "__main__":
61  # Parse args
62  parser = optparse.OptionParser()
63  parser.add_option('--filter')  # ignored
64  (options, filenames) = parser.parse_args()
65
66  # Lint each file
67  errs = 0
68  for p in filenames:
69    errs += LintFile(p)
70
71  # If errors, return nonzero
72  if errs > 0:
73    sys.exit(1)
74