| OLD | NEW |
| 1 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """SCM-specific utility classes.""" | 5 """SCM-specific utility classes.""" |
| 6 | 6 |
| 7 import cStringIO | 7 import cStringIO |
| 8 import glob | 8 import glob |
| 9 import os | 9 import os |
| 10 import re | 10 import re |
| 11 import shutil | 11 import shutil |
| 12 import subprocess | 12 import subprocess |
| 13 import sys | 13 import sys |
| 14 import tempfile | 14 import tempfile |
| 15 import time | 15 import time |
| 16 import xml.dom.minidom | 16 import xml.dom.minidom |
| 17 | 17 |
| 18 import gclient_utils | 18 import gclient_utils |
| 19 | 19 |
| 20 def ValidateEmail(email): | 20 def ValidateEmail(email): |
| 21 return (re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email) | 21 return (re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email) |
| 22 is not None) | 22 is not None) |
| 23 | 23 |
| 24 | 24 |
| 25 def GetCasedPath(path): | 25 def GetCasedPath(path): |
| 26 """Elcheapos way to get the real path case on Windows.""" | 26 """Elcheapos way to get the real path case on Windows.""" |
| 27 if sys.platform.startswith('win') and os.path.exists(path): | 27 if sys.platform.startswith('win') and os.path.exists(path): |
| 28 # Reconstruct the path. | 28 # Reconstruct the path. |
| 29 path = os.path.abspath(path) | 29 path = os.path.abspath(path) |
| 30 paths = path.split('\\') | 30 paths = path.split('\\') |
| 31 for i in range(len(paths)): | 31 for i in range(len(paths)): |
| 32 if i == 0: | 32 if i == 0: |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 114 if not m: | 114 if not m: |
| 115 raise Exception("status currently unsupported: %s" % statusline) | 115 raise Exception("status currently unsupported: %s" % statusline) |
| 116 results.append(('%s ' % m.group(1), m.group(2))) | 116 results.append(('%s ' % m.group(1), m.group(2))) |
| 117 return results | 117 return results |
| 118 | 118 |
| 119 @staticmethod | 119 @staticmethod |
| 120 def RunAndFilterOutput(args, | 120 def RunAndFilterOutput(args, |
| 121 in_directory, | 121 in_directory, |
| 122 print_messages, | 122 print_messages, |
| 123 print_stdout, | 123 print_stdout, |
| 124 filter): | 124 filter_fn): |
| 125 """Runs a command, optionally outputting to stdout. | 125 """Runs a command, optionally outputting to stdout. |
| 126 | 126 |
| 127 stdout is passed line-by-line to the given filter function. If | 127 stdout is passed line-by-line to the given filter_fn function. If |
| 128 print_stdout is true, it is also printed to sys.stdout as in Run. | 128 print_stdout is true, it is also printed to sys.stdout as in Run. |
| 129 | 129 |
| 130 Args: | 130 Args: |
| 131 args: A sequence of command line parameters to be passed. | 131 args: A sequence of command line parameters to be passed. |
| 132 in_directory: The directory where git is to be run. | 132 in_directory: The directory where git is to be run. |
| 133 print_messages: Whether to print status messages to stdout about | 133 print_messages: Whether to print status messages to stdout about |
| 134 which commands are being run. | 134 which commands are being run. |
| 135 print_stdout: Whether to forward program's output to stdout. | 135 print_stdout: Whether to forward program's output to stdout. |
| 136 filter: A function taking one argument (a string) which will be | 136 filter_fn: A function taking one argument (a string) which will be |
| 137 passed each line (with the ending newline character removed) of | 137 passed each line (with the ending newline character removed) of |
| 138 program's output for filtering. | 138 program's output for filtering. |
| 139 | 139 |
| 140 Raises: | 140 Raises: |
| 141 gclient_utils.Error: An error occurred while running the command. | 141 gclient_utils.Error: An error occurred while running the command. |
| 142 """ | 142 """ |
| 143 command = [GIT.COMMAND] | 143 command = [GIT.COMMAND] |
| 144 command.extend(args) | 144 command.extend(args) |
| 145 gclient_utils.SubprocessCallAndFilter(command, | 145 gclient_utils.SubprocessCallAndFilter(command, |
| 146 in_directory, | 146 in_directory, |
| 147 print_messages, | 147 print_messages, |
| 148 print_stdout, | 148 print_stdout, |
| 149 filter=filter) | 149 filter_fn=filter_fn) |
| 150 | 150 |
| 151 @staticmethod | 151 @staticmethod |
| 152 def GetEmail(repo_root): | 152 def GetEmail(repo_root): |
| 153 """Retrieves the user email address if known.""" | 153 """Retrieves the user email address if known.""" |
| 154 # We could want to look at the svn cred when it has a svn remote but it | 154 # We could want to look at the svn cred when it has a svn remote but it |
| 155 # should be fine for now, users should simply configure their git settings. | 155 # should be fine for now, users should simply configure their git settings. |
| 156 return GIT.Capture(['config', 'user.email'], | 156 return GIT.Capture(['config', 'user.email'], |
| 157 repo_root, error_ok=True)[0].strip() | 157 repo_root, error_ok=True)[0].strip() |
| 158 | 158 |
| 159 @staticmethod | 159 @staticmethod |
| (...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 457 continue | 457 continue |
| 458 # No progress was made or an unknown error we aren't sure, bail out. | 458 # No progress was made or an unknown error we aren't sure, bail out. |
| 459 raise | 459 raise |
| 460 break | 460 break |
| 461 | 461 |
| 462 @staticmethod | 462 @staticmethod |
| 463 def RunAndFilterOutput(args, | 463 def RunAndFilterOutput(args, |
| 464 in_directory, | 464 in_directory, |
| 465 print_messages, | 465 print_messages, |
| 466 print_stdout, | 466 print_stdout, |
| 467 filter): | 467 filter_fn): |
| 468 """Runs a command, optionally outputting to stdout. | 468 """Runs a command, optionally outputting to stdout. |
| 469 | 469 |
| 470 stdout is passed line-by-line to the given filter function. If | 470 stdout is passed line-by-line to the given filter_fn function. If |
| 471 print_stdout is true, it is also printed to sys.stdout as in Run. | 471 print_stdout is true, it is also printed to sys.stdout as in Run. |
| 472 | 472 |
| 473 Args: | 473 Args: |
| 474 args: A sequence of command line parameters to be passed. | 474 args: A sequence of command line parameters to be passed. |
| 475 in_directory: The directory where svn is to be run. | 475 in_directory: The directory where svn is to be run. |
| 476 print_messages: Whether to print status messages to stdout about | 476 print_messages: Whether to print status messages to stdout about |
| 477 which commands are being run. | 477 which commands are being run. |
| 478 print_stdout: Whether to forward program's output to stdout. | 478 print_stdout: Whether to forward program's output to stdout. |
| 479 filter: A function taking one argument (a string) which will be | 479 filter_fn: A function taking one argument (a string) which will be |
| 480 passed each line (with the ending newline character removed) of | 480 passed each line (with the ending newline character removed) of |
| 481 program's output for filtering. | 481 program's output for filtering. |
| 482 | 482 |
| 483 Raises: | 483 Raises: |
| 484 gclient_utils.Error: An error occurred while running the command. | 484 gclient_utils.Error: An error occurred while running the command. |
| 485 """ | 485 """ |
| 486 command = [SVN.COMMAND] | 486 command = [SVN.COMMAND] |
| 487 command.extend(args) | 487 command.extend(args) |
| 488 gclient_utils.SubprocessCallAndFilter(command, | 488 gclient_utils.SubprocessCallAndFilter(command, |
| 489 in_directory, | 489 in_directory, |
| 490 print_messages, | 490 print_messages, |
| 491 print_stdout, | 491 print_stdout, |
| 492 filter=filter) | 492 filter_fn=filter_fn) |
| 493 | 493 |
| 494 @staticmethod | 494 @staticmethod |
| 495 def CaptureInfo(relpath, in_directory=None, print_error=True): | 495 def CaptureInfo(relpath, in_directory=None, print_error=True): |
| 496 """Returns a dictionary from the svn info output for the given file. | 496 """Returns a dictionary from the svn info output for the given file. |
| 497 | 497 |
| 498 Args: | 498 Args: |
| 499 relpath: The directory where the working copy resides relative to | 499 relpath: The directory where the working copy resides relative to |
| 500 the directory given by in_directory. | 500 the directory given by in_directory. |
| 501 in_directory: The directory where svn is to be run. | 501 in_directory: The directory where svn is to be run. |
| 502 """ | 502 """ |
| 503 output = SVN.Capture(["info", "--xml", relpath], in_directory, print_error) | 503 output = SVN.Capture(["info", "--xml", relpath], in_directory, print_error) |
| 504 dom = gclient_utils.ParseXML(output) | 504 dom = gclient_utils.ParseXML(output) |
| 505 result = {} | 505 result = {} |
| 506 if dom: | 506 if dom: |
| 507 GetNamedNodeText = gclient_utils.GetNamedNodeText | 507 GetNamedNodeText = gclient_utils.GetNamedNodeText |
| 508 GetNodeNamedAttributeText = gclient_utils.GetNodeNamedAttributeText | 508 GetNodeNamedAttributeText = gclient_utils.GetNodeNamedAttributeText |
| 509 def C(item, f): | 509 def C(item, f): |
| 510 if item is not None: return f(item) | 510 if item is not None: |
| 511 return f(item) |
| 511 # /info/entry/ | 512 # /info/entry/ |
| 512 # url | 513 # url |
| 513 # reposityory/(root|uuid) | 514 # reposityory/(root|uuid) |
| 514 # wc-info/(schedule|depth) | 515 # wc-info/(schedule|depth) |
| 515 # commit/(author|date) | 516 # commit/(author|date) |
| 516 # str() the results because they may be returned as Unicode, which | 517 # str() the results because they may be returned as Unicode, which |
| 517 # interferes with the higher layers matching up things in the deps | 518 # interferes with the higher layers matching up things in the deps |
| 518 # dictionary. | 519 # dictionary. |
| 519 # TODO(maruel): Fix at higher level instead (!) | |
| 520 result['Repository Root'] = C(GetNamedNodeText(dom, 'root'), str) | 520 result['Repository Root'] = C(GetNamedNodeText(dom, 'root'), str) |
| 521 result['URL'] = C(GetNamedNodeText(dom, 'url'), str) | 521 result['URL'] = C(GetNamedNodeText(dom, 'url'), str) |
| 522 result['UUID'] = C(GetNamedNodeText(dom, 'uuid'), str) | 522 result['UUID'] = C(GetNamedNodeText(dom, 'uuid'), str) |
| 523 result['Revision'] = C(GetNodeNamedAttributeText(dom, 'entry', | 523 result['Revision'] = C(GetNodeNamedAttributeText(dom, 'entry', |
| 524 'revision'), | 524 'revision'), |
| 525 int) | 525 int) |
| 526 result['Node Kind'] = C(GetNodeNamedAttributeText(dom, 'entry', 'kind'), | 526 result['Node Kind'] = C(GetNodeNamedAttributeText(dom, 'entry', 'kind'), |
| 527 str) | 527 str) |
| 528 # Differs across versions. | 528 # Differs across versions. |
| 529 if result['Node Kind'] == 'dir': | 529 if result['Node Kind'] == 'dir': |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 640 return SVN.IsMovedInfo(SVN.CaptureInfo(filename)) | 640 return SVN.IsMovedInfo(SVN.CaptureInfo(filename)) |
| 641 | 641 |
| 642 @staticmethod | 642 @staticmethod |
| 643 def IsMovedInfo(info): | 643 def IsMovedInfo(info): |
| 644 """Determine if a file has been added through svn mv""" | 644 """Determine if a file has been added through svn mv""" |
| 645 return (info.get('Copied From URL') and | 645 return (info.get('Copied From URL') and |
| 646 info.get('Copied From Rev') and | 646 info.get('Copied From Rev') and |
| 647 info.get('Schedule') == 'add') | 647 info.get('Schedule') == 'add') |
| 648 | 648 |
| 649 @staticmethod | 649 @staticmethod |
| 650 def GetFileProperty(file, property_name): | 650 def GetFileProperty(filename, property_name): |
| 651 """Returns the value of an SVN property for the given file. | 651 """Returns the value of an SVN property for the given file. |
| 652 | 652 |
| 653 Args: | 653 Args: |
| 654 file: The file to check | 654 filename: The file to check |
| 655 property_name: The name of the SVN property, e.g. "svn:mime-type" | 655 property_name: The name of the SVN property, e.g. "svn:mime-type" |
| 656 | 656 |
| 657 Returns: | 657 Returns: |
| 658 The value of the property, which will be the empty string if the property | 658 The value of the property, which will be the empty string if the property |
| 659 is not set on the file. If the file is not under version control, the | 659 is not set on the file. If the file is not under version control, the |
| 660 empty string is also returned. | 660 empty string is also returned. |
| 661 """ | 661 """ |
| 662 output = SVN.Capture(["propget", property_name, file]) | 662 output = SVN.Capture(["propget", property_name, filename]) |
| 663 if (output.startswith("svn: ") and | 663 if (output.startswith("svn: ") and |
| 664 output.endswith("is not under version control")): | 664 output.endswith("is not under version control")): |
| 665 return "" | 665 return "" |
| 666 else: | 666 else: |
| 667 return output | 667 return output |
| 668 | 668 |
| 669 @staticmethod | 669 @staticmethod |
| 670 def DiffItem(filename, full_move=False, revision=None): | 670 def DiffItem(filename, full_move=False, revision=None): |
| 671 """Diffs a single file. | 671 """Diffs a single file. |
| 672 | 672 |
| 673 Should be simple, eh? No it isn't. | 673 Should be simple, eh? No it isn't. |
| 674 Be sure to be in the appropriate directory before calling to have the | 674 Be sure to be in the appropriate directory before calling to have the |
| 675 expected relative path. | 675 expected relative path. |
| 676 full_move means that move or copy operations should completely recreate the | 676 full_move means that move or copy operations should completely recreate the |
| 677 files, usually in the prospect to apply the patch for a try job.""" | 677 files, usually in the prospect to apply the patch for a try job.""" |
| 678 # If the user specified a custom diff command in their svn config file, | 678 # If the user specified a custom diff command in their svn config file, |
| 679 # then it'll be used when we do svn diff, which we don't want to happen | 679 # then it'll be used when we do svn diff, which we don't want to happen |
| 680 # since we want the unified diff. Using --diff-cmd=diff doesn't always | 680 # since we want the unified diff. Using --diff-cmd=diff doesn't always |
| 681 # work, since they can have another diff executable in their path that | 681 # work, since they can have another diff executable in their path that |
| 682 # gives different line endings. So we use a bogus temp directory as the | 682 # gives different line endings. So we use a bogus temp directory as the |
| 683 # config directory, which gets around these problems. | 683 # config directory, which gets around these problems. |
| 684 bogus_dir = tempfile.mkdtemp() | 684 bogus_dir = tempfile.mkdtemp() |
| 685 try: | 685 try: |
| 686 # Use "svn info" output instead of os.path.isdir because the latter fails | 686 # Use "svn info" output instead of os.path.isdir because the latter fails |
| 687 # when the file is deleted. | 687 # when the file is deleted. |
| 688 return SVN._DiffItemInternal(SVN.CaptureInfo(filename), | 688 return SVN._DiffItemInternal(filename, SVN.CaptureInfo(filename), |
| 689 bogus_dir, |
| 689 full_move=full_move, revision=revision) | 690 full_move=full_move, revision=revision) |
| 690 finally: | 691 finally: |
| 691 shutil.rmtree(bogus_dir) | 692 shutil.rmtree(bogus_dir) |
| 692 | 693 |
| 693 @staticmethod | 694 @staticmethod |
| 694 def _DiffItemInternal(filename, info, bogus_dir, full_move=False, | 695 def _DiffItemInternal(filename, info, bogus_dir, full_move=False, |
| 695 revision=None): | 696 revision=None): |
| 696 """Grabs the diff data.""" | 697 """Grabs the diff data.""" |
| 697 command = ["diff", "--config-dir", bogus_dir, filename] | 698 command = ["diff", "--config-dir", bogus_dir, filename] |
| 698 if revision: | 699 if revision: |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 828 'svn.simple') | 829 'svn.simple') |
| 829 for credfile in os.listdir(auth_dir): | 830 for credfile in os.listdir(auth_dir): |
| 830 cred_info = SVN.ReadSimpleAuth(os.path.join(auth_dir, credfile)) | 831 cred_info = SVN.ReadSimpleAuth(os.path.join(auth_dir, credfile)) |
| 831 if regexp.match(cred_info.get('svn:realmstring')): | 832 if regexp.match(cred_info.get('svn:realmstring')): |
| 832 return cred_info.get('username') | 833 return cred_info.get('username') |
| 833 | 834 |
| 834 @staticmethod | 835 @staticmethod |
| 835 def ReadSimpleAuth(filename): | 836 def ReadSimpleAuth(filename): |
| 836 f = open(filename, 'r') | 837 f = open(filename, 'r') |
| 837 values = {} | 838 values = {} |
| 838 def ReadOneItem(type): | 839 def ReadOneItem(item_type): |
| 839 m = re.match(r'%s (\d+)' % type, f.readline()) | 840 m = re.match(r'%s (\d+)' % item_type, f.readline()) |
| 840 if not m: | 841 if not m: |
| 841 return None | 842 return None |
| 842 data = f.read(int(m.group(1))) | 843 data = f.read(int(m.group(1))) |
| 843 if f.read(1) != '\n': | 844 if f.read(1) != '\n': |
| 844 return None | 845 return None |
| 845 return data | 846 return data |
| 846 | 847 |
| 847 while True: | 848 while True: |
| 848 key = ReadOneItem('K') | 849 key = ReadOneItem('K') |
| 849 if not key: | 850 if not key: |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 885 if not SVN.current_version: | 886 if not SVN.current_version: |
| 886 SVN.current_version = SVN.Capture(['--version']).split()[2] | 887 SVN.current_version = SVN.Capture(['--version']).split()[2] |
| 887 current_version_list = map(only_int, SVN.current_version.split('.')) | 888 current_version_list = map(only_int, SVN.current_version.split('.')) |
| 888 for min_ver in map(int, min_version.split('.')): | 889 for min_ver in map(int, min_version.split('.')): |
| 889 ver = current_version_list.pop(0) | 890 ver = current_version_list.pop(0) |
| 890 if ver < min_ver: | 891 if ver < min_ver: |
| 891 return (False, SVN.current_version) | 892 return (False, SVN.current_version) |
| 892 elif ver > min_ver: | 893 elif ver > min_ver: |
| 893 return (True, SVN.current_version) | 894 return (True, SVN.current_version) |
| 894 return (True, SVN.current_version) | 895 return (True, SVN.current_version) |
| OLD | NEW |