Chris Craik | 93216d0 | 2015-03-05 13:58:42 -0800 | [diff] [blame] | 1 | # Copyright (c) 2015 The Chromium Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | import os |
| 5 | import re |
| 6 | import subprocess |
| 7 | import sys |
| 8 | |
| 9 | from trace_viewer import trace_viewer_project |
| 10 | |
| 11 | class AffectedFile(object): |
| 12 | def __init__(self, input_api, filename): |
| 13 | self._filename = filename |
| 14 | self._input_api = input_api |
| 15 | self._cached_contents = None |
| 16 | self._cached_changed_contents = None |
| 17 | self._cached_new_contents = None |
| 18 | |
| 19 | def __repr__(self): |
| 20 | return self._filename |
| 21 | |
| 22 | @property |
| 23 | def filename(self): |
| 24 | return self._filename |
| 25 | |
| 26 | @property |
| 27 | def contents(self): |
| 28 | if self._cached_contents is None: |
| 29 | self._cached_contents = self._input_api._git( |
| 30 | ['show', ':%s' % self._filename]) |
| 31 | return self._cached_contents |
| 32 | |
| 33 | @property |
| 34 | def is_added(self): |
| 35 | return self.fileame in self._input_api.added_files |
| 36 | |
| 37 | @property |
| 38 | def contents_as_lines(self): |
| 39 | """Returns an iterator over the lines in the new version of file. |
| 40 | |
| 41 | The new version is the file in the user's workspace, i.e. the "right hand |
| 42 | side". |
| 43 | |
| 44 | Contents will be empty if the file is a directory or does not exist. |
| 45 | Note: The carriage returns (LF or CR) are stripped off. |
| 46 | """ |
| 47 | if self._cached_new_contents is None: |
| 48 | self._cached_new_contents = self.contents.splitlines() |
| 49 | return self._cached_new_contents[:] |
| 50 | |
| 51 | @property |
| 52 | def changed_lines(self): |
| 53 | """Returns a list of tuples (line number, line text) of all new lines. |
| 54 | |
| 55 | This relies on the scm diff output describing each changed code section |
| 56 | with a line of the form |
| 57 | |
| 58 | ^@@ <old line num>,<old size> <new line num>,<new size> @@$ |
| 59 | """ |
| 60 | if self._cached_changed_contents is not None: |
| 61 | return self._cached_changed_contents[:] |
| 62 | self._cached_changed_contents = [] |
| 63 | line_num = 0 |
| 64 | |
| 65 | for line in self.GenerateDiff().splitlines(): |
| 66 | m = re.match(r'^@@ [0-9\,\+\-]+ \+([0-9]+)\,[0-9]+ @@', line) |
| 67 | if m: |
| 68 | line_num = int(m.groups(1)[0]) |
| 69 | continue |
| 70 | if line.startswith('+') and not line.startswith('++'): |
| 71 | self._cached_changed_contents.append((line_num, line[1:])) |
| 72 | if not line.startswith('-'): |
| 73 | line_num += 1 |
| 74 | return self._cached_changed_contents[:] |
| 75 | |
| 76 | def GenerateDiff(self): |
| 77 | return self._input_api._git(['diff', '--cached', self.filename]) |
| 78 | |
| 79 | |
| 80 | class InputAPI(object): |
| 81 | def __init__(self, tvp): |
| 82 | self.DEFAULT_BLACK_LIST = [] |
| 83 | self._tvp = tvp |
| 84 | self._filename_statuses = None |
| 85 | self._added_files = None |
| 86 | |
| 87 | def _git(self, args): |
| 88 | assert isinstance(args, list) |
| 89 | args = ['git'] + args |
| 90 | p = subprocess.Popen( |
| 91 | args, |
| 92 | stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
| 93 | cwd=self.repository_root) |
| 94 | res = p.communicate() |
| 95 | if p.wait() != 0: |
| 96 | raise Exception(res[1]) |
| 97 | return res[0] |
| 98 | |
| 99 | @property |
| 100 | def repository_root(self): |
| 101 | return self._tvp.trace_viewer_path |
| 102 | |
| 103 | @property |
| 104 | def added_files(self): |
| 105 | if not self._added_files: |
| 106 | self._added_files = set() |
| 107 | for filename, status_char in filename_statuses: |
| 108 | if status_char == 'A': |
| 109 | self._added_files.Add(filename) |
| 110 | return self._added_files |
| 111 | |
| 112 | @property |
| 113 | def affected_files(self): |
| 114 | return self.AffectedFiles(include_deletes=True) |
| 115 | |
| 116 | def AffectedFiles(self, |
| 117 | include_deletes=False, |
| 118 | file_filter=lambda t: True): |
| 119 | filename_statuses = self._GetFilenameStatuses() |
| 120 | for filename, status_char in filename_statuses: |
| 121 | if status_char == 'D': |
| 122 | if include_deletes: |
| 123 | if file_filter(filename): |
| 124 | yield AffectedFile(self, filename) |
| 125 | else: |
| 126 | if file_filter(filename): |
| 127 | yield AffectedFile(self, filename) |
| 128 | |
| 129 | def _GetFilenameStatuses(self): |
| 130 | if self._filename_statuses != None: |
| 131 | return self._filename_statuses |
| 132 | |
| 133 | self._filename_statuses = [] |
| 134 | stdout = self._git(['diff', '--cached', '--name-status']) |
| 135 | for line in stdout.split('\n'): |
| 136 | line = line.strip() |
| 137 | if len(line) == 0: |
| 138 | continue |
| 139 | m = re.match('([ACDMRTUXB])\s+(.+)', line) |
| 140 | if not m: |
| 141 | import pdb; pdb.set_trace() |
| 142 | assert m |
| 143 | |
| 144 | status_char = m.group(1) |
| 145 | filename = m.group(2) |
| 146 | self._filename_statuses.append((filename, status_char)) |
| 147 | return self._filename_statuses |
| 148 | |
| 149 | |
| 150 | def RunChecks(input_api): |
| 151 | results = [] |
| 152 | |
| 153 | from hooks import pre_commit_checks |
| 154 | results += pre_commit_checks.RunChecks(input_api) |
| 155 | |
| 156 | from trace_viewer.build import check_gyp |
| 157 | err = check_gyp.GypCheck() |
| 158 | if err: |
| 159 | results += [err] |
| 160 | |
| 161 | from trace_viewer.build import check_gn |
| 162 | err = check_gn.GnCheck() |
| 163 | if err: |
| 164 | results += [err] |
| 165 | |
| 166 | from trace_viewer.build import check_modules |
| 167 | err = check_modules.CheckModules() |
| 168 | if err: |
| 169 | results += [err] |
| 170 | |
| 171 | from hooks import js_checks |
| 172 | results += js_checks.RunChecks(input_api) |
| 173 | |
| 174 | return results |
| 175 | |
| 176 | |
| 177 | def Main(args): |
| 178 | tvp = trace_viewer_project.TraceViewerProject() |
| 179 | input_api = InputAPI(tvp) |
| 180 | results = RunChecks(input_api) |
| 181 | print '\n\n'.join(results) |
| 182 | |
| 183 | if len(results): |
| 184 | return 255 |
| 185 | return 0 |