| OLD | NEW |
| 1 # coding=utf8 | 1 # coding=utf8 |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 """Manages a project checkout. | 5 """Manages a project checkout. |
| 6 | 6 |
| 7 Includes support for svn, git-svn and git. | 7 Includes support for svn, git-svn and git. |
| 8 """ | 8 """ |
| 9 | 9 |
| 10 import ConfigParser | 10 import ConfigParser |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 42 settings[k.strip()] = v.strip() | 42 settings[k.strip()] = v.strip() |
| 43 finally: | 43 finally: |
| 44 settings_file.close() | 44 settings_file.close() |
| 45 except IOError: | 45 except IOError: |
| 46 return None | 46 return None |
| 47 return settings.get(key, None) | 47 return settings.get(key, None) |
| 48 | 48 |
| 49 | 49 |
| 50 class PatchApplicationFailed(Exception): | 50 class PatchApplicationFailed(Exception): |
| 51 """Patch failed to be applied.""" | 51 """Patch failed to be applied.""" |
| 52 def __init__(self, filename, status): | 52 def __init__(self, p, status): |
| 53 super(PatchApplicationFailed, self).__init__(filename, status) | 53 super(PatchApplicationFailed, self).__init__(p, status) |
| 54 self.filename = filename | 54 self.patch = p |
| 55 self.status = status | 55 self.status = status |
| 56 | 56 |
| 57 @property |
| 58 def filename(self): |
| 59 if self.patch: |
| 60 return self.patch.filename |
| 61 |
| 62 def __str__(self): |
| 63 out = [] |
| 64 if self.filename: |
| 65 out.append('Failed to apply patch for %s:' % self.filename) |
| 66 if self.status: |
| 67 out.append(self.status) |
| 68 return '\n'.join(out) |
| 69 |
| 57 | 70 |
| 58 class CheckoutBase(object): | 71 class CheckoutBase(object): |
| 59 # Set to None to have verbose output. | 72 # Set to None to have verbose output. |
| 60 VOID = subprocess2.VOID | 73 VOID = subprocess2.VOID |
| 61 | 74 |
| 62 def __init__(self, root_dir, project_name, post_processors): | 75 def __init__(self, root_dir, project_name, post_processors): |
| 63 """ | 76 """ |
| 64 Args: | 77 Args: |
| 65 post_processor: list of lambda(checkout, patches) to call on each of the | 78 post_processor: list of lambda(checkout, patches) to call on each of the |
| 66 modified files. | 79 modified files. |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 133 os.makedirs(full_dir) | 146 os.makedirs(full_dir) |
| 134 | 147 |
| 135 filepath = os.path.join(self.project_path, p.filename) | 148 filepath = os.path.join(self.project_path, p.filename) |
| 136 if p.is_binary: | 149 if p.is_binary: |
| 137 with open(filepath, 'wb') as f: | 150 with open(filepath, 'wb') as f: |
| 138 f.write(p.get()) | 151 f.write(p.get()) |
| 139 else: | 152 else: |
| 140 if p.source_filename: | 153 if p.source_filename: |
| 141 if not p.is_new: | 154 if not p.is_new: |
| 142 raise PatchApplicationFailed( | 155 raise PatchApplicationFailed( |
| 143 p.filename, | 156 p, |
| 144 'File has a source filename specified but is not new') | 157 'File has a source filename specified but is not new') |
| 145 # Copy the file first. | 158 # Copy the file first. |
| 146 if os.path.isfile(filepath): | 159 if os.path.isfile(filepath): |
| 147 raise PatchApplicationFailed( | 160 raise PatchApplicationFailed( |
| 148 p.filename, 'File exist but was about to be overwriten') | 161 p, 'File exist but was about to be overwriten') |
| 149 shutil.copy2( | 162 shutil.copy2( |
| 150 os.path.join(self.project_path, p.source_filename), filepath) | 163 os.path.join(self.project_path, p.source_filename), filepath) |
| 151 if p.diff_hunks: | 164 if p.diff_hunks: |
| 152 stdout = subprocess2.check_output( | 165 stdout = subprocess2.check_output( |
| 153 ['patch', '-u', '--binary', '-p%s' % p.patchlevel], | 166 ['patch', '-u', '--binary', '-p%s' % p.patchlevel], |
| 154 stdin=p.get(False), | 167 stdin=p.get(False), |
| 155 stderr=subprocess2.STDOUT, | 168 stderr=subprocess2.STDOUT, |
| 156 cwd=self.project_path) | 169 cwd=self.project_path) |
| 157 elif p.is_new and not os.path.exists(filepath): | 170 elif p.is_new and not os.path.exists(filepath): |
| 158 # There is only a header. Just create the file. | 171 # There is only a header. Just create the file. |
| 159 open(filepath, 'w').close() | 172 open(filepath, 'w').close() |
| 160 for post in post_processors: | 173 for post in post_processors: |
| 161 post(self, p) | 174 post(self, p) |
| 162 except OSError, e: | 175 except OSError, e: |
| 163 raise PatchApplicationFailed(p.filename, '%s%s' % (stdout, e)) | 176 raise PatchApplicationFailed(p, '%s%s' % (stdout, e)) |
| 164 except subprocess.CalledProcessError, e: | 177 except subprocess.CalledProcessError, e: |
| 165 raise PatchApplicationFailed( | 178 raise PatchApplicationFailed( |
| 166 p.filename, '%s%s' % (stdout, getattr(e, 'stdout', None))) | 179 p, '%s%s' % (stdout, getattr(e, 'stdout', None))) |
| 167 | 180 |
| 168 def commit(self, commit_message, user): | 181 def commit(self, commit_message, user): |
| 169 """Stubbed out.""" | 182 """Stubbed out.""" |
| 170 raise NotImplementedError('RawCheckout can\'t commit') | 183 raise NotImplementedError('RawCheckout can\'t commit') |
| 171 | 184 |
| 172 | 185 |
| 173 class SvnConfig(object): | 186 class SvnConfig(object): |
| 174 """Parses a svn configuration file.""" | 187 """Parses a svn configuration file.""" |
| 175 def __init__(self, svn_config_dir=None): | 188 def __init__(self, svn_config_dir=None): |
| 176 super(SvnConfig, self).__init__() | 189 super(SvnConfig, self).__init__() |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 300 ['add', dir_to_create, '--force'], credentials=False) | 313 ['add', dir_to_create, '--force'], credentials=False) |
| 301 | 314 |
| 302 filepath = os.path.join(self.project_path, p.filename) | 315 filepath = os.path.join(self.project_path, p.filename) |
| 303 if p.is_binary: | 316 if p.is_binary: |
| 304 with open(filepath, 'wb') as f: | 317 with open(filepath, 'wb') as f: |
| 305 f.write(p.get()) | 318 f.write(p.get()) |
| 306 else: | 319 else: |
| 307 if p.source_filename: | 320 if p.source_filename: |
| 308 if not p.is_new: | 321 if not p.is_new: |
| 309 raise PatchApplicationFailed( | 322 raise PatchApplicationFailed( |
| 310 p.filename, | 323 p, |
| 311 'File has a source filename specified but is not new') | 324 'File has a source filename specified but is not new') |
| 312 # Copy the file first. | 325 # Copy the file first. |
| 313 if os.path.isfile(filepath): | 326 if os.path.isfile(filepath): |
| 314 raise PatchApplicationFailed( | 327 raise PatchApplicationFailed( |
| 315 p.filename, 'File exist but was about to be overwriten') | 328 p, 'File exist but was about to be overwriten') |
| 316 self._check_output_svn( | 329 self._check_output_svn( |
| 317 [ | 330 [ |
| 318 'copy', | 331 'copy', |
| 319 os.path.join(self.project_path, p.source_filename), | 332 os.path.join(self.project_path, p.source_filename), |
| 320 filepath | 333 filepath |
| 321 ]) | 334 ]) |
| 322 if p.diff_hunks: | 335 if p.diff_hunks: |
| 323 cmd = ['patch', '-p%s' % p.patchlevel, '--forward', '--force'] | 336 cmd = ['patch', '-p%s' % p.patchlevel, '--forward', '--force'] |
| 324 stdout += subprocess2.check_output( | 337 stdout += subprocess2.check_output( |
| 325 cmd, stdin=p.get(False), cwd=self.project_path) | 338 cmd, stdin=p.get(False), cwd=self.project_path) |
| (...skipping 14 matching lines...) Expand all Loading... |
| 340 for value in values.split(';'): | 353 for value in values.split(';'): |
| 341 if '=' not in value: | 354 if '=' not in value: |
| 342 params = [value, '*'] | 355 params = [value, '*'] |
| 343 else: | 356 else: |
| 344 params = value.split('=', 1) | 357 params = value.split('=', 1) |
| 345 stdout += self._check_output_svn( | 358 stdout += self._check_output_svn( |
| 346 ['propset'] + params + [p.filename], credentials=False) | 359 ['propset'] + params + [p.filename], credentials=False) |
| 347 for post in post_processors: | 360 for post in post_processors: |
| 348 post(self, p) | 361 post(self, p) |
| 349 except OSError, e: | 362 except OSError, e: |
| 350 raise PatchApplicationFailed(p.filename, '%s%s' % (stdout, e)) | 363 raise PatchApplicationFailed(p, '%s%s' % (stdout, e)) |
| 351 except subprocess.CalledProcessError, e: | 364 except subprocess.CalledProcessError, e: |
| 352 raise PatchApplicationFailed( | 365 raise PatchApplicationFailed( |
| 353 p.filename, | 366 p, |
| 354 'While running %s;\n%s%s' % ( | 367 'While running %s;\n%s%s' % ( |
| 355 ' '.join(e.cmd), stdout, getattr(e, 'stdout', ''))) | 368 ' '.join(e.cmd), stdout, getattr(e, 'stdout', ''))) |
| 356 | 369 |
| 357 def commit(self, commit_message, user): | 370 def commit(self, commit_message, user): |
| 358 logging.info('Committing patch for %s' % user) | 371 logging.info('Committing patch for %s' % user) |
| 359 assert self.commit_user | 372 assert self.commit_user |
| 360 assert isinstance(commit_message, unicode) | 373 assert isinstance(commit_message, unicode) |
| 361 handle, commit_filename = tempfile.mkstemp(text=True) | 374 handle, commit_filename = tempfile.mkstemp(text=True) |
| 362 try: | 375 try: |
| 363 # Shouldn't assume default encoding is UTF-8. But really, if you are using | 376 # Shouldn't assume default encoding is UTF-8. But really, if you are using |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 496 # handled. | 509 # handled. |
| 497 if not prop[0] in ( | 510 if not prop[0] in ( |
| 498 'svn:eol-style', 'svn:executable', 'svn:mime-type'): | 511 'svn:eol-style', 'svn:executable', 'svn:mime-type'): |
| 499 raise patch.UnsupportedPatchFormat( | 512 raise patch.UnsupportedPatchFormat( |
| 500 p.filename, | 513 p.filename, |
| 501 'Cannot apply svn property %s to file %s.' % ( | 514 'Cannot apply svn property %s to file %s.' % ( |
| 502 prop[0], p.filename)) | 515 prop[0], p.filename)) |
| 503 for post in post_processors: | 516 for post in post_processors: |
| 504 post(self, p) | 517 post(self, p) |
| 505 except OSError, e: | 518 except OSError, e: |
| 506 raise PatchApplicationFailed(p.filename, '%s%s' % (stdout, e)) | 519 raise PatchApplicationFailed(p, '%s%s' % (stdout, e)) |
| 507 except subprocess.CalledProcessError, e: | 520 except subprocess.CalledProcessError, e: |
| 508 raise PatchApplicationFailed( | 521 raise PatchApplicationFailed( |
| 509 p.filename, '%s%s' % (stdout, getattr(e, 'stdout', None))) | 522 p, '%s%s' % (stdout, getattr(e, 'stdout', None))) |
| 510 # Once all the patches are processed and added to the index, commit the | 523 # Once all the patches are processed and added to the index, commit the |
| 511 # index. | 524 # index. |
| 512 self._check_call_git(['commit', '-m', 'Committed patch']) | 525 self._check_call_git(['commit', '-m', 'Committed patch']) |
| 513 # TODO(maruel): Weirdly enough they don't match, need to investigate. | 526 # TODO(maruel): Weirdly enough they don't match, need to investigate. |
| 514 #found_files = self._check_output_git( | 527 #found_files = self._check_output_git( |
| 515 # ['diff', 'master', '--name-only']).splitlines(False) | 528 # ['diff', 'master', '--name-only']).splitlines(False) |
| 516 #assert sorted(patches.filenames) == sorted(found_files), ( | 529 #assert sorted(patches.filenames) == sorted(found_files), ( |
| 517 # sorted(out), sorted(found_files)) | 530 # sorted(out), sorted(found_files)) |
| 518 | 531 |
| 519 def commit(self, commit_message, user): | 532 def commit(self, commit_message, user): |
| (...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 753 user, message)) | 766 user, message)) |
| 754 return 'FAKE' | 767 return 'FAKE' |
| 755 | 768 |
| 756 @property | 769 @property |
| 757 def project_name(self): | 770 def project_name(self): |
| 758 return self.checkout.project_name | 771 return self.checkout.project_name |
| 759 | 772 |
| 760 @property | 773 @property |
| 761 def project_path(self): | 774 def project_path(self): |
| 762 return self.checkout.project_path | 775 return self.checkout.project_path |
| OLD | NEW |