1#!/usr/bin/env python
2#
3# Copyright 2008 The Closure Linter Authors. All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS-IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Determines the list of files to be checked from command line arguments."""
18
19__author__ = ('robbyw@google.com (Robert Walker)',
20              'ajp@google.com (Andy Perelson)')
21
22import glob
23import os
24import re
25
26import gflags as flags
27
28
29FLAGS = flags.FLAGS
30
31flags.DEFINE_multistring(
32    'recurse',
33    None,
34    'Recurse in to the subdirectories of the given path',
35    short_name='r')
36flags.DEFINE_list(
37    'exclude_directories',
38    ('_demos'),
39    'Exclude the specified directories (only applicable along with -r or '
40    '--presubmit)',
41    short_name='e')
42flags.DEFINE_list(
43    'exclude_files',
44    ('deps.js'),
45    'Exclude the specified files',
46    short_name='x')
47
48
49def MatchesSuffixes(filename, suffixes):
50  """Returns whether the given filename matches one of the given suffixes.
51
52  Args:
53    filename: Filename to check.
54    suffixes: Sequence of suffixes to check.
55
56  Returns:
57    Whether the given filename matches one of the given suffixes.
58  """
59  suffix = filename[filename.rfind('.'):]
60  return suffix in suffixes
61
62
63def _GetUserSpecifiedFiles(argv, suffixes):
64  """Returns files to be linted, specified directly on the command line.
65
66  Can handle the '*' wildcard in filenames, but no other wildcards.
67
68  Args:
69    argv: Sequence of command line arguments. The second and following arguments
70      are assumed to be files that should be linted.
71    suffixes: Expected suffixes for the file type being checked.
72
73  Returns:
74    A sequence of files to be linted.
75  """
76  files = argv[1:] or []
77  all_files = []
78  lint_files = []
79
80   # Perform any necessary globs.
81  for f in files:
82    if f.find('*') != -1:
83      for result in glob.glob(f):
84        all_files.append(result)
85    else:
86      all_files.append(f)
87
88  for f in all_files:
89    if MatchesSuffixes(f, suffixes):
90      lint_files.append(f)
91  return lint_files
92
93
94def _GetRecursiveFiles(suffixes):
95  """Returns files to be checked specified by the --recurse flag.
96
97  Args:
98    suffixes: Expected suffixes for the file type being checked.
99
100  Returns:
101    A list of files to be checked.
102  """
103  lint_files = []
104  # Perform any request recursion
105  if FLAGS.recurse:
106    for start in FLAGS.recurse:
107      for root, subdirs, files in os.walk(start):
108        for f in files:
109          if MatchesSuffixes(f, suffixes):
110            lint_files.append(os.path.join(root, f))
111  return lint_files
112
113
114def GetAllSpecifiedFiles(argv, suffixes):
115  """Returns all files specified by the user on the commandline.
116
117  Args:
118    argv: Sequence of command line arguments. The second and following arguments
119      are assumed to be files that should be linted.
120    suffixes: Expected suffixes for the file type
121
122  Returns:
123    A list of all files specified directly or indirectly (via flags) on the
124    command line by the user.
125  """
126  files = _GetUserSpecifiedFiles(argv, suffixes)
127
128  if FLAGS.recurse:
129    files += _GetRecursiveFiles(suffixes)
130
131  return FilterFiles(files)
132
133
134def FilterFiles(files):
135  """Filters the list of files to be linted be removing any excluded files.
136
137  Filters out files excluded using --exclude_files and  --exclude_directories.
138
139  Args:
140    files: Sequence of files that needs filtering.
141
142  Returns:
143    Filtered list of files to be linted.
144  """
145  num_files = len(files)
146
147  ignore_dirs_regexs = []
148  for ignore in FLAGS.exclude_directories:
149    ignore_dirs_regexs.append(re.compile(r'(^|[\\/])%s[\\/]' % ignore))
150
151  result_files = []
152  for f in files:
153    add_file = True
154    for exclude in FLAGS.exclude_files:
155      if f.endswith('/' + exclude) or f == exclude:
156        add_file = False
157        break
158    for ignore in ignore_dirs_regexs:
159      if ignore.search(f):
160        # Break out of ignore loop so we don't add to
161        # filtered files.
162        add_file = False
163        break
164    if add_file:
165      # Convert everything to absolute paths so we can easily remove duplicates
166      # using a set.
167      result_files.append(os.path.abspath(f))
168
169  skipped = num_files - len(result_files)
170  if skipped:
171    print 'Skipping %d file(s).' % skipped
172
173  return set(result_files)
174
175
176def GetFileList(argv, file_type, suffixes):
177  """Parse the flags and return the list of files to check.
178
179  Args:
180    argv: Sequence of command line arguments.
181    suffixes: Sequence of acceptable suffixes for the file type.
182
183  Returns:
184    The list of files to check.
185  """
186  return sorted(GetAllSpecifiedFiles(argv, suffixes))
187
188
189def IsEmptyArgumentList(argv):
190  return not (len(argv[1:]) or FLAGS.recurse)
191