Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Side by Side Diff: release/mac/gcl.py

Issue 18639: Have gcl report when unit tests are (or might be) missing from a change.... (Closed) Base URL: http://src.chromium.org/svn/trunk/depot_tools/
Patch Set: '' Created 11 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « release/linux/gcl.py ('k') | release/win/gcl.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
2 # Wrapper script around Rietveld's upload.py that groups files into 6 # Wrapper script around Rietveld's upload.py that groups files into
3 # changelists. 7 # changelists.
4 8
5 import getpass 9 import getpass
6 import linecache 10 import linecache
7 import os 11 import os
8 import random 12 import random
9 import re 13 import re
10 import string 14 import string
11 import subprocess 15 import subprocess
12 import sys 16 import sys
13 import tempfile 17 import tempfile
14 import upload 18 import upload
15 import urllib2 19 import urllib2
16 20
17 CODEREVIEW_SETTINGS = { 21 CODEREVIEW_SETTINGS = {
18 # Default values. 22 # Default values.
19 "CODE_REVIEW_SERVER": "codereview.chromium.org", 23 "CODE_REVIEW_SERVER": "codereview.chromium.org",
20 "CC_LIST": "chromium-reviews@googlegroups.com", 24 "CC_LIST": "chromium-reviews@googlegroups.com",
21 "VIEW_VC": "http://src.chromium.org/viewvc/chrome?view=rev&revision=", 25 "VIEW_VC": "http://src.chromium.org/viewvc/chrome?view=rev&revision=",
22 } 26 }
23 27
24 # Use a shell for subcommands on Windows to get a PATH search, and because svn 28 # Use a shell for subcommands on Windows to get a PATH search, and because svn
25 # may be a batch file. 29 # may be a batch file.
26 use_shell = sys.platform.startswith("win") 30 use_shell = sys.platform.startswith("win")
27 31
28 # globals that store the root of the current repositary and the directory where 32 # globals that store the root of the current repository and the directory where
29 # we store information about changelists. 33 # we store information about changelists.
30 repository_root = "" 34 repository_root = ""
31 gcl_info_dir = "" 35 gcl_info_dir = ""
32 36
33 # Filename where we store repository specific information for gcl. 37 # Filename where we store repository specific information for gcl.
34 CODEREVIEW_SETTINGS_FILE = "codereview.settings" 38 CODEREVIEW_SETTINGS_FILE = "codereview.settings"
35 39
40 # Warning message when the change appears to be missing tests.
41 MISSING_TEST_MSG = "Change contains new or modified methods, but no new tests!"
42
36 # Caches whether we read the codereview.settings file yet or not. 43 # Caches whether we read the codereview.settings file yet or not.
37 read_gcl_info = False 44 read_gcl_info = False
38 45
39 46
40 def GetSVNFileInfo(file, field): 47 def GetSVNFileInfo(file, field):
41 """Returns a field from the svn info output for the given file.""" 48 """Returns a field from the svn info output for the given file."""
42 output = RunShell(["svn", "info", file]) 49 output = RunShell(["svn", "info", file])
43 for line in output.splitlines(): 50 for line in output.splitlines():
44 search = field + ": " 51 search = field + ": "
45 if line.startswith(search): 52 if line.startswith(search):
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
114 121
115 def IsTreeOpen(): 122 def IsTreeOpen():
116 """Fetches the tree status and returns either True or False.""" 123 """Fetches the tree status and returns either True or False."""
117 url = GetCodeReviewSetting('STATUS') 124 url = GetCodeReviewSetting('STATUS')
118 status = "" 125 status = ""
119 if url: 126 if url:
120 status = urllib2.urlopen(url).read() 127 status = urllib2.urlopen(url).read()
121 return status.find('0') == -1 128 return status.find('0') == -1
122 129
123 130
124 def ErrorExit(msg): 131 def Warn(msg):
125 """Print an error message to stderr and exit.""" 132 ErrorExit(msg, exit=False)
133
134
135 def ErrorExit(msg, exit=True):
136 """Print an error message to stderr and optionally exit."""
126 print >>sys.stderr, msg 137 print >>sys.stderr, msg
127 sys.exit(1) 138 sys.exit(1)
128 139
129 140
130 def RunShellWithReturnCode(command, print_output=False): 141 def RunShellWithReturnCode(command, print_output=False):
131 """Executes a command and returns the output and the return code.""" 142 """Executes a command and returns the output and the return code."""
132 p = subprocess.Popen(command, stdout=subprocess.PIPE, 143 p = subprocess.Popen(command, stdout=subprocess.PIPE,
133 stderr=subprocess.STDOUT, shell=use_shell, 144 stderr=subprocess.STDOUT, shell=use_shell,
134 universal_newlines=True) 145 universal_newlines=True)
135 if print_output: 146 if print_output:
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 issue: the Rietveld issue number, of "" if it hasn't been uploaded yet. 186 issue: the Rietveld issue number, of "" if it hasn't been uploaded yet.
176 description: the description. 187 description: the description.
177 files: a list of 2 tuple containing (status, filename) of changed files, 188 files: a list of 2 tuple containing (status, filename) of changed files,
178 with paths being relative to the top repository directory. 189 with paths being relative to the top repository directory.
179 """ 190 """
180 def __init__(self, name="", issue="", description="", files=[]): 191 def __init__(self, name="", issue="", description="", files=[]):
181 self.name = name 192 self.name = name
182 self.issue = issue 193 self.issue = issue
183 self.description = description 194 self.description = description
184 self.files = files 195 self.files = files
196 self.patch = None
185 197
186 def FileList(self): 198 def FileList(self):
187 """Returns a list of files.""" 199 """Returns a list of files."""
188 return [file[1] for file in self.files] 200 return [file[1] for file in self.files]
189 201
202 def _NonDeletedFileList(self):
203 """Returns a list of files in this change, not including deleted files."""
204 return [file[1] for file in self.files if file[0] != "D"]
205
190 def Save(self): 206 def Save(self):
191 """Writes the changelist information to disk.""" 207 """Writes the changelist information to disk."""
192 data = SEPARATOR.join([self.issue, 208 data = SEPARATOR.join([self.issue,
193 "\n".join([f[0] + f[1] for f in self.files]), 209 "\n".join([f[0] + f[1] for f in self.files]),
194 self.description]) 210 self.description])
195 WriteFile(GetChangelistInfoFile(self.name), data) 211 WriteFile(GetChangelistInfoFile(self.name), data)
196 212
197 def Delete(self): 213 def Delete(self):
198 """Removes the changelist information from disk.""" 214 """Removes the changelist information from disk."""
199 os.remove(GetChangelistInfoFile(self.name)) 215 os.remove(GetChangelistInfoFile(self.name))
200 216
201 def CloseIssue(self): 217 def CloseIssue(self):
202 """Closes the Rietveld issue for this changelist.""" 218 """Closes the Rietveld issue for this changelist."""
203 data = [("description", self.description),] 219 data = [("description", self.description),]
204 ctype, body = upload.EncodeMultipartFormData(data, []) 220 ctype, body = upload.EncodeMultipartFormData(data, [])
205 SendToRietveld("/" + self.issue + "/close", body, ctype) 221 SendToRietveld("/" + self.issue + "/close", body, ctype)
206 222
207 def UpdateRietveldDescription(self): 223 def UpdateRietveldDescription(self):
208 """Sets the description for an issue on Rietveld.""" 224 """Sets the description for an issue on Rietveld."""
209 data = [("description", self.description),] 225 data = [("description", self.description),]
210 ctype, body = upload.EncodeMultipartFormData(data, []) 226 ctype, body = upload.EncodeMultipartFormData(data, [])
211 SendToRietveld("/" + self.issue + "/description", body, ctype) 227 SendToRietveld("/" + self.issue + "/description", body, ctype)
212 228
213 229 def MissingTests(self):
230 """Returns True if the change looks like it needs unit tests but has none.
231
232 A change needs unit tests if it contains any new source files or methods.
233 """
234 SOURCE_SUFFIXES = [".cc", ".cpp", ".c", ".m", ".mm"]
235 # Ignore third_party entirely.
236 files = [file for file in self._NonDeletedFileList()
237 if file.find("third_party") == -1]
238
239 # Any new or modified test files?
240 # A test file's name ends with "test.*" or "tests.*".
241 test_files = [test for test in files
242 if os.path.splitext(test)[0].rstrip("s").endswith("test")]
243 if len(test_files) > 0:
244 return False
245
246 # Any new source files?
247 source_files = [file for file in files
248 if os.path.splitext(file)[1] in SOURCE_SUFFIXES]
249 if len(source_files) > 0:
250 return True
251
252 # Do the long test, checking the files for new methods.
253 return self._HasNewMethod()
254
255 def _HasNewMethod(self):
256 """Returns True if the changeset contains any new functions, or if a
257 function signature has been changed.
258
259 A function is identified by starting flush left, containing a "(" before
260 the next flush-left line, and either ending with "{" before the next
261 flush-left line or being followed by an unindented "{".
262
263 Currently this returns True for new methods, new static functions, and
264 methods or functions whose signatures have been changed.
265
266 Inline methods added to header files won't be detected by this. That's
267 acceptable for purposes of determining if a unit test is needed, since
268 inline methods should be trivial.
269 """
270 # To check for methods added to source or header files, we need the diffs.
271 # We'll generate them all, since there aren't likely to be many files
272 # apart from source and headers; besides, we'll want them all if we're
273 # uploading anyway.
274 if self.patch is None:
275 self.patch = GenerateDiff(self.FileList())
276
277 definition = ""
278 for line in self.patch.splitlines():
279 if not line.startswith("+"):
280 continue
281 line = line.strip("+").rstrip(" \t")
282 # Skip empty lines, comments, and preprocessor directives.
283 # TODO(pamg): Handle multiline comments if it turns out to be a problem.
284 if line == "" or line.startswith("/") or line.startswith("#"):
285 continue
286
287 # A possible definition ending with "{" is complete, so check it.
288 if definition.endswith("{"):
289 if definition.find("(") != -1:
290 return True
291 definition = ""
292
293 # A { or an indented line, when we're in a definition, continues it.
294 if (definition != "" and
295 (line == "{" or line.startswith(" ") or line.startswith("\t"))):
296 definition += line
297
298 # A flush-left line starts a new possible function definition.
299 elif not line.startswith(" ") and not line.startswith("\t"):
300 definition = line
301
302 return False
303
304
214 SEPARATOR = "\n-----\n" 305 SEPARATOR = "\n-----\n"
215 # The info files have the following format: 306 # The info files have the following format:
216 # issue_id\n 307 # issue_id\n
217 # SEPARATOR\n 308 # SEPARATOR\n
218 # filepath1\n 309 # filepath1\n
219 # filepath2\n 310 # filepath2\n
220 # . 311 # .
221 # . 312 # .
222 # filepathn\n 313 # filepathn\n
223 # SEPARATOR\n 314 # SEPARATOR\n
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
310 random.choice(string.ascii_lowercase) + 401 random.choice(string.ascii_lowercase) +
311 random.choice(string.digits)) 402 random.choice(string.digits))
312 if cl_name not in current_cl_names: 403 if cl_name not in current_cl_names:
313 return cl_name 404 return cl_name
314 405
315 406
316 def GetModifiedFiles(): 407 def GetModifiedFiles():
317 """Returns a set that maps from changelist name to (status,filename) tuples. 408 """Returns a set that maps from changelist name to (status,filename) tuples.
318 409
319 Files not in a changelist have an empty changelist name. Filenames are in 410 Files not in a changelist have an empty changelist name. Filenames are in
320 relation to the top level directory of the current repositary. Note that 411 relation to the top level directory of the current repository. Note that
321 only the current directory and subdirectories are scanned, in order to 412 only the current directory and subdirectories are scanned, in order to
322 improve performance while still being flexible. 413 improve performance while still being flexible.
323 """ 414 """
324 files = {} 415 files = {}
325 416
326 # Since the files are normalized to the root folder of the repositary, figure 417 # Since the files are normalized to the root folder of the repositary, figure
327 # out what we need to add to the paths. 418 # out what we need to add to the paths.
328 dir_prefix = os.getcwd()[len(GetRepositoryRoot()):].strip(os.sep) 419 dir_prefix = os.getcwd()[len(GetRepositoryRoot()):].strip(os.sep)
329 420
330 # Get a list of all files in changelists. 421 # Get a list of all files in changelists.
331 files_in_cl = {} 422 files_in_cl = {}
332 for cl in GetCLs(): 423 for cl in GetCLs():
333 change_info = LoadChangelistInfo(cl) 424 change_info = LoadChangelistInfo(cl)
334 for status, filename in change_info.files: 425 for status, filename in change_info.files:
335 files_in_cl[filename] = change_info.name 426 files_in_cl[filename] = change_info.name
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after
494 if sys.platform.startswith("win"): 585 if sys.platform.startswith("win"):
495 editor = "notepad" 586 editor = "notepad"
496 else: 587 else:
497 editor = "vi" 588 editor = "vi"
498 589
499 return editor 590 return editor
500 591
501 592
502 def GenerateDiff(files): 593 def GenerateDiff(files):
503 """Returns a string containing the diff for the given file list.""" 594 """Returns a string containing the diff for the given file list."""
595 previous_cwd = os.getcwd()
596 os.chdir(GetRepositoryRoot())
597
504 diff = [] 598 diff = []
505 for file in files: 599 for file in files:
506 # Use svn info output instead of os.path.isdir because the latter fails 600 # Use svn info output instead of os.path.isdir because the latter fails
507 # when the file is deleted. 601 # when the file is deleted.
508 if GetSVNFileInfo(file, "Node Kind") == "directory": 602 if GetSVNFileInfo(file, "Node Kind") == "directory":
509 continue 603 continue
510 # If the user specified a custom diff command in their svn config file, 604 # If the user specified a custom diff command in their svn config file,
511 # then it'll be used when we do svn diff, which we don't want to happen 605 # then it'll be used when we do svn diff, which we don't want to happen
512 # since we want the unified diff. Using --diff-cmd=diff doesn't always 606 # since we want the unified diff. Using --diff-cmd=diff doesn't always
513 # work, since they can have another diff executable in their path that 607 # work, since they can have another diff executable in their path that
514 # gives different line endings. So we use a bogus temp directory as the 608 # gives different line endings. So we use a bogus temp directory as the
515 # config directory, which gets around these problems. 609 # config directory, which gets around these problems.
516 if sys.platform.startswith("win"): 610 if sys.platform.startswith("win"):
517 parent_dir = tempfile.gettempdir() 611 parent_dir = tempfile.gettempdir()
518 else: 612 else:
519 parent_dir = sys.path[0] # tempdir is not secure. 613 parent_dir = sys.path[0] # tempdir is not secure.
520 bogus_dir = os.path.join(parent_dir, "temp_svn_config") 614 bogus_dir = os.path.join(parent_dir, "temp_svn_config")
521 if not os.path.exists(bogus_dir): 615 if not os.path.exists(bogus_dir):
522 os.mkdir(bogus_dir) 616 os.mkdir(bogus_dir)
523 diff.append(RunShell(["svn", "diff", "--config-dir", bogus_dir, file])) 617 diff.append(RunShell(["svn", "diff", "--config-dir", bogus_dir, file]))
618 os.chdir(previous_cwd)
524 return "".join(diff) 619 return "".join(diff)
525 620
526 621
527 def UploadCL(change_info, args): 622 def UploadCL(change_info, args):
528 if not change_info.FileList(): 623 if not change_info.FileList():
529 print "Nothing to upload, changelist is empty." 624 print "Nothing to upload, changelist is empty."
530 return 625 return
531 626
532 no_try = "--no_try" in args 627 no_try = "--no_try" in args
533 if no_try: 628 if no_try:
534 args.remove("--no_try") 629 args.remove("--no_try")
535 630
631 # TODO(pamg): Do something when tests are missing. The plan is to upload a
632 # message to Rietveld and have it shown in the UI attached to this patch.
633
536 upload_arg = ["upload.py", "-y"] 634 upload_arg = ["upload.py", "-y"]
537 upload_arg.append("--server=" + GetCodeReviewSetting("CODE_REVIEW_SERVER")) 635 upload_arg.append("--server=" + GetCodeReviewSetting("CODE_REVIEW_SERVER"))
538 upload_arg.extend(args) 636 upload_arg.extend(args)
539 637
540 desc_file = "" 638 desc_file = ""
541 if change_info.issue: # Uploading a new patchset. 639 if change_info.issue: # Uploading a new patchset.
542 found_message = False 640 found_message = False
543 for arg in args: 641 for arg in args:
544 if arg.startswith("--message") or arg.startswith("-m"): 642 if arg.startswith("--message") or arg.startswith("-m"):
545 found_message = True 643 found_message = True
(...skipping 12 matching lines...) Expand all
558 upload_arg.append("--description_file=" + desc_file + "") 656 upload_arg.append("--description_file=" + desc_file + "")
559 if change_info.description: 657 if change_info.description:
560 subject = change_info.description[:77] 658 subject = change_info.description[:77]
561 if subject.find("\r\n") != -1: 659 if subject.find("\r\n") != -1:
562 subject = subject[:subject.find("\r\n")] 660 subject = subject[:subject.find("\r\n")]
563 if subject.find("\n") != -1: 661 if subject.find("\n") != -1:
564 subject = subject[:subject.find("\n")] 662 subject = subject[:subject.find("\n")]
565 if len(change_info.description) > 77: 663 if len(change_info.description) > 77:
566 subject = subject + "..." 664 subject = subject + "..."
567 upload_arg.append("--message=" + subject) 665 upload_arg.append("--message=" + subject)
568 666
569 # Change the current working directory before calling upload.py so that it 667 # Change the current working directory before calling upload.py so that it
570 # shows the correct base. 668 # shows the correct base.
669 previous_cwd = os.getcwd()
571 os.chdir(GetRepositoryRoot()) 670 os.chdir(GetRepositoryRoot())
572 671
573 # If we have a lot of files with long paths, then we won't be able to fit 672 # If we have a lot of files with long paths, then we won't be able to fit
574 # the command to "svn diff". Instead, we generate the diff manually for 673 # the command to "svn diff". Instead, we generate the diff manually for
575 # each file and concatenate them before passing it to upload.py. 674 # each file and concatenate them before passing it to upload.py.
576 issue, patchset = upload.RealMain(upload_arg, 675 if change_info.patch is None:
577 GenerateDiff(change_info.FileList())) 676 change_info.patch = GenerateDiff(change_info.FileList())
677 issue, patchset = upload.RealMain(upload_arg, change_info.patch)
578 if issue and issue != change_info.issue: 678 if issue and issue != change_info.issue:
579 change_info.issue = issue 679 change_info.issue = issue
580 change_info.Save() 680 change_info.Save()
581 681
582 if desc_file: 682 if desc_file:
583 os.remove(desc_file) 683 os.remove(desc_file)
584 684
585 # Do background work on Rietveld to lint the file so that the results are 685 # Do background work on Rietveld to lint the file so that the results are
586 # ready when the issue is viewed. 686 # ready when the issue is viewed.
587 SendToRietveld("/lint/issue%s_%s" % (issue, patchset), timeout=0.5) 687 SendToRietveld("/lint/issue%s_%s" % (issue, patchset), timeout=0.5)
588 688
589 # Once uploaded to Rietveld, send it to the try server. 689 # Once uploaded to Rietveld, send it to the try server.
590 if not no_try and GetCodeReviewSetting('TRY_ON_UPLOAD').lower() == 'true': 690 if not no_try and GetCodeReviewSetting('TRY_ON_UPLOAD').lower() == 'true':
591 # Use the local diff. 691 # Use the local diff.
592 TryChange(change_info, [], True) 692 TryChange(change_info, [], True)
593 693
694 os.chdir(previous_cwd)
695
594 696
595 def TryChange(change_info, args, swallow_exception=False, patchset=None): 697 def TryChange(change_info, args, swallow_exception=False, patchset=None):
596 """Create a diff file of change_info and send it to the try server.""" 698 """Create a diff file of change_info and send it to the try server."""
597 try: 699 try:
598 import trychange 700 import trychange
599 except ImportError: 701 except ImportError:
600 if swallow_exception: 702 if swallow_exception:
601 return 703 return
602 ErrorExit("You need to install trychange.py to use the try server.") 704 ErrorExit("You need to install trychange.py to use the try server.")
603 705
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
635 os.write(handle, commit_message) 737 os.write(handle, commit_message)
636 os.close(handle) 738 os.close(handle)
637 739
638 handle, targets_filename = tempfile.mkstemp(text=True) 740 handle, targets_filename = tempfile.mkstemp(text=True)
639 os.write(handle, "\n".join(change_info.FileList())) 741 os.write(handle, "\n".join(change_info.FileList()))
640 os.close(handle) 742 os.close(handle)
641 743
642 commit_cmd += ['--file=' + commit_filename] 744 commit_cmd += ['--file=' + commit_filename]
643 commit_cmd += ['--targets=' + targets_filename] 745 commit_cmd += ['--targets=' + targets_filename]
644 # Change the current working directory before calling commit. 746 # Change the current working directory before calling commit.
747 previous_cwd = os.getcwd()
645 os.chdir(GetRepositoryRoot()) 748 os.chdir(GetRepositoryRoot())
646 output = RunShell(commit_cmd, True) 749 output = RunShell(commit_cmd, True)
647 os.remove(commit_filename) 750 os.remove(commit_filename)
648 os.remove(targets_filename) 751 os.remove(targets_filename)
649 if output.find("Committed revision") != -1: 752 if output.find("Committed revision") != -1:
650 change_info.Delete() 753 change_info.Delete()
651 754
652 if change_info.issue: 755 if change_info.issue:
653 revision = re.compile(".*?\nCommitted revision (\d+)", 756 revision = re.compile(".*?\nCommitted revision (\d+)",
654 re.DOTALL).match(output).group(1) 757 re.DOTALL).match(output).group(1)
655 viewvc_url = GetCodeReviewSetting("VIEW_VC") 758 viewvc_url = GetCodeReviewSetting("VIEW_VC")
656 change_info.description = (change_info.description + 759 change_info.description = (change_info.description +
657 "\n\nCommitted: " + viewvc_url + revision) 760 "\n\nCommitted: " + viewvc_url + revision)
658 change_info.CloseIssue() 761 change_info.CloseIssue()
762 os.chdir(previous_cwd)
659 763
660 764
661 def Change(change_info): 765 def Change(change_info):
662 """Creates/edits a changelist.""" 766 """Creates/edits a changelist."""
663 if change_info.issue: 767 if change_info.issue:
664 try: 768 try:
665 description = GetIssueDescription(change_info.issue) 769 description = GetIssueDescription(change_info.issue)
666 except urllib2.HTTPError, err: 770 except urllib2.HTTPError, err:
667 if err.code == 404: 771 if err.code == 404:
668 # The user deleted the issue in Rietveld, so forget the old issue id. 772 # The user deleted the issue in Rietveld, so forget the old issue id.
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
714 continue 818 continue
715 if line.startswith("---"): 819 if line.startswith("---"):
716 break 820 break
717 status = line[:7] 821 status = line[:7]
718 file = line[7:] 822 file = line[7:]
719 new_cl_files.append((status, file)) 823 new_cl_files.append((status, file))
720 change_info.files = new_cl_files 824 change_info.files = new_cl_files
721 825
722 change_info.Save() 826 change_info.Save()
723 print change_info.name + " changelist saved." 827 print change_info.name + " changelist saved."
828 if change_info.MissingTests():
829 Warn("WARNING: " + MISSING_TEST_MSG)
724 830
725 # We don't lint files in these path prefixes. 831 # We don't lint files in these path prefixes.
726 IGNORE_PATHS = ("webkit",) 832 IGNORE_PATHS = ("webkit",)
727 833
728 # Valid extensions for files we want to lint. 834 # Valid extensions for files we want to lint.
729 CPP_EXTENSIONS = ("cpp", "cc", "h") 835 CPP_EXTENSIONS = ("cpp", "cc", "h")
730 836
731 def Lint(change_info, args): 837 def Lint(change_info, args):
732 """Runs cpplint.py on all the files in |change_info|""" 838 """Runs cpplint.py on all the files in |change_info|"""
733 try: 839 try:
734 import cpplint 840 import cpplint
735 except ImportError: 841 except ImportError:
736 ErrorExit("You need to install cpplint.py to lint C++ files.") 842 ErrorExit("You need to install cpplint.py to lint C++ files.")
737 843
738 # Change the current working directory before calling lint so that it 844 # Change the current working directory before calling lint so that it
739 # shows the correct base. 845 # shows the correct base.
846 previous_cwd = os.getcwd()
740 os.chdir(GetRepositoryRoot()) 847 os.chdir(GetRepositoryRoot())
741 848
742 # Process cpplints arguments if any. 849 # Process cpplints arguments if any.
743 filenames = cpplint.ParseArguments(args + change_info.FileList()) 850 filenames = cpplint.ParseArguments(args + change_info.FileList())
744 851
745 for file in filenames: 852 for file in filenames:
746 if len([file for suffix in CPP_EXTENSIONS if file.endswith(suffix)]): 853 if len([file for suffix in CPP_EXTENSIONS if file.endswith(suffix)]):
747 if len([file for prefix in IGNORE_PATHS if file.startswith(prefix)]): 854 if len([file for prefix in IGNORE_PATHS if file.startswith(prefix)]):
748 print "Ignoring non-Google styled file %s" % file 855 print "Ignoring non-Google styled file %s" % file
749 else: 856 else:
750 cpplint.ProcessFile(file, cpplint._cpplint_state.verbose_level) 857 cpplint.ProcessFile(file, cpplint._cpplint_state.verbose_level)
751 858
752 print "Total errors found: %d\n" % cpplint._cpplint_state.error_count 859 print "Total errors found: %d\n" % cpplint._cpplint_state.error_count
860 os.chdir(previous_cwd)
753 861
754 862
755 def Changes(): 863 def Changes():
756 """Print all the changlists and their files.""" 864 """Print all the changelists and their files."""
757 for cl in GetCLs(): 865 for cl in GetCLs():
758 change_info = LoadChangelistInfo(cl, True, True) 866 change_info = LoadChangelistInfo(cl, True, True)
759 print "\n--- Changelist " + change_info.name + ":" 867 print "\n--- Changelist " + change_info.name + ":"
760 for file in change_info.files: 868 for file in change_info.files:
761 print "".join(file) 869 print "".join(file)
762 870
763 871
764 def main(argv=None): 872 def main(argv=None):
765 if argv is None: 873 if argv is None:
766 argv = sys.argv 874 argv = sys.argv
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
839 # the files. This allows commands such as 'gcl diff xxx' to work. 947 # the files. This allows commands such as 'gcl diff xxx' to work.
840 args =["svn", command] 948 args =["svn", command]
841 root = GetRepositoryRoot() 949 root = GetRepositoryRoot()
842 args.extend([os.path.join(root, x) for x in change_info.FileList()]) 950 args.extend([os.path.join(root, x) for x in change_info.FileList()])
843 RunShell(args, True) 951 RunShell(args, True)
844 return 0 952 return 0
845 953
846 954
847 if __name__ == "__main__": 955 if __name__ == "__main__":
848 sys.exit(main()) 956 sys.exit(main())
OLDNEW
« no previous file with comments | « release/linux/gcl.py ('k') | release/win/gcl.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698