12da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#!/usr/bin/env python
22da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
32da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Copyright 2008 The Closure Linter Authors. All Rights Reserved.
42da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
52da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Licensed under the Apache License, Version 2.0 (the "License");
62da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# you may not use this file except in compliance with the License.
72da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# You may obtain a copy of the License at
82da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
92da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#      http://www.apache.org/licenses/LICENSE-2.0
102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Unless required by applicable law or agreed to in writing, software
122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# distributed under the License is distributed on an "AS-IS" BASIS,
132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# See the License for the specific language governing permissions and
152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# limitations under the License.
162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis"""Determines the list of files to be checked from command line arguments."""
182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis__author__ = ('robbyw@google.com (Robert Walker)',
202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              'ajp@google.com (Andy Perelson)')
212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
222da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport glob
232da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport os
242da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport re
252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
262da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport gflags as flags
272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
292da489cd246702bee5938545b18a6f710ed214bcJamie GennisFLAGS = flags.FLAGS
302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
312da489cd246702bee5938545b18a6f710ed214bcJamie Gennisflags.DEFINE_multistring(
322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    'recurse',
332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    None,
342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    'Recurse in to the subdirectories of the given path',
352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    short_name='r')
362da489cd246702bee5938545b18a6f710ed214bcJamie Gennisflags.DEFINE_list(
372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    'exclude_directories',
382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    ('_demos'),
392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    'Exclude the specified directories (only applicable along with -r or '
402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    '--presubmit)',
412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    short_name='e')
422da489cd246702bee5938545b18a6f710ed214bcJamie Gennisflags.DEFINE_list(
432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    'exclude_files',
442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    ('deps.js'),
452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    'Exclude the specified files',
462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    short_name='x')
472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
492da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef MatchesSuffixes(filename, suffixes):
502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Returns whether the given filename matches one of the given suffixes.
512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Args:
532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    filename: Filename to check.
542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    suffixes: Sequence of suffixes to check.
552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Returns:
572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Whether the given filename matches one of the given suffixes.
582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  suffix = filename[filename.rfind('.'):]
602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return suffix in suffixes
612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
632da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _GetUserSpecifiedFiles(argv, suffixes):
642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Returns files to be linted, specified directly on the command line.
652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Can handle the '*' wildcard in filenames, but no other wildcards.
672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Args:
692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    argv: Sequence of command line arguments. The second and following arguments
702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      are assumed to be files that should be linted.
712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    suffixes: Expected suffixes for the file type being checked.
722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Returns:
742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    A sequence of files to be linted.
752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  files = argv[1:] or []
772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  all_files = []
782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  lint_files = []
792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   # Perform any necessary globs.
812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  for f in files:
822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if f.find('*') != -1:
832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      for result in glob.glob(f):
842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        all_files.append(result)
852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    else:
862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      all_files.append(f)
872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  for f in all_files:
892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if MatchesSuffixes(f, suffixes):
902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      lint_files.append(f)
912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return lint_files
922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
942da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _GetRecursiveFiles(suffixes):
952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Returns files to be checked specified by the --recurse flag.
962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Args:
982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    suffixes: Expected suffixes for the file type being checked.
992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Returns:
1012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    A list of files to be checked.
1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
1032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  lint_files = []
1042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # Perform any request recursion
1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  if FLAGS.recurse:
1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for start in FLAGS.recurse:
1072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      for root, subdirs, files in os.walk(start):
1082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for f in files:
1092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          if MatchesSuffixes(f, suffixes):
1102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            lint_files.append(os.path.join(root, f))
1112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return lint_files
1122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1142da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef GetAllSpecifiedFiles(argv, suffixes):
1152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Returns all files specified by the user on the commandline.
1162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Args:
1182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    argv: Sequence of command line arguments. The second and following arguments
1192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      are assumed to be files that should be linted.
1202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    suffixes: Expected suffixes for the file type
1212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Returns:
1232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    A list of all files specified directly or indirectly (via flags) on the
1242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    command line by the user.
1252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
1262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  files = _GetUserSpecifiedFiles(argv, suffixes)
1272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  if FLAGS.recurse:
1292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    files += _GetRecursiveFiles(suffixes)
1302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return FilterFiles(files)
1322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1342da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef FilterFiles(files):
1352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Filters the list of files to be linted be removing any excluded files.
1362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Filters out files excluded using --exclude_files and  --exclude_directories.
1382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Args:
1402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    files: Sequence of files that needs filtering.
1412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Returns:
1432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Filtered list of files to be linted.
1442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
1452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  num_files = len(files)
1462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  ignore_dirs_regexs = []
1482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  for ignore in FLAGS.exclude_directories:
1492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    ignore_dirs_regexs.append(re.compile(r'(^|[\\/])%s[\\/]' % ignore))
1502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  result_files = []
1522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  for f in files:
1532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    add_file = True
1542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for exclude in FLAGS.exclude_files:
1552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if f.endswith('/' + exclude) or f == exclude:
1562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        add_file = False
1572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        break
1582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for ignore in ignore_dirs_regexs:
1592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if ignore.search(f):
1602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Break out of ignore loop so we don't add to
1612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # filtered files.
1622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        add_file = False
1632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        break
1642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if add_file:
1652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # Convert everything to absolute paths so we can easily remove duplicates
1662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # using a set.
1672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      result_files.append(os.path.abspath(f))
1682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  skipped = num_files - len(result_files)
1702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  if skipped:
1712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    print 'Skipping %d file(s).' % skipped
1722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return set(result_files)
1742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1762da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef GetFileList(argv, file_type, suffixes):
1772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Parse the flags and return the list of files to check.
1782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Args:
1802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    argv: Sequence of command line arguments.
1812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    suffixes: Sequence of acceptable suffixes for the file type.
1822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Returns:
1842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    The list of files to check.
1852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
1862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return sorted(GetAllSpecifiedFiles(argv, suffixes))
1872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1892da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef IsEmptyArgumentList(argv):
1902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return not (len(argv[1:]) or FLAGS.recurse)
191