OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # | 2 # |
3 # Copyright 2008 Google Inc. All Rights Reserved. | 3 # Copyright 2008 Google Inc. All Rights Reserved. |
4 # | 4 # |
5 # Licensed under the Apache License, Version 2.0 (the "License"); | 5 # Licensed under the Apache License, Version 2.0 (the "License"); |
6 # you may not use this file except in compliance with 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 | 7 # You may obtain a copy of the License at |
8 # | 8 # |
9 # http://www.apache.org/licenses/LICENSE-2.0 | 9 # http://www.apache.org/licenses/LICENSE-2.0 |
10 # | 10 # |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
57 to run the command. | 57 to run the command. |
58 | 58 |
59 Example: | 59 Example: |
60 hooks = [ | 60 hooks = [ |
61 { "pattern": "\\.(gif|jpe?g|pr0n|png)$", | 61 { "pattern": "\\.(gif|jpe?g|pr0n|png)$", |
62 "action": ["python", "image_indexer.py", "--all"]}, | 62 "action": ["python", "image_indexer.py", "--all"]}, |
63 ] | 63 ] |
64 """ | 64 """ |
65 | 65 |
66 __author__ = "darinf@gmail.com (Darin Fisher)" | 66 __author__ = "darinf@gmail.com (Darin Fisher)" |
67 __version__ = "0.3.2" | 67 __version__ = "0.3.1" |
68 | 68 |
69 import errno | 69 import errno |
70 import optparse | 70 import optparse |
71 import os | 71 import os |
72 import re | 72 import re |
73 import stat | 73 import stat |
74 import subprocess | 74 import subprocess |
75 import sys | 75 import sys |
76 import time | 76 import time |
77 import urlparse | 77 import urlparse |
(...skipping 312 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
390 try: | 390 try: |
391 os.rmdir(file_path) | 391 os.rmdir(file_path) |
392 except OSError, e: | 392 except OSError, e: |
393 if e.errno != errno.EACCES or sys.platform != 'win32': | 393 if e.errno != errno.EACCES or sys.platform != 'win32': |
394 raise | 394 raise |
395 print 'Failed to remove %s: trying again' % file_path | 395 print 'Failed to remove %s: trying again' % file_path |
396 time.sleep(0.1) | 396 time.sleep(0.1) |
397 os.rmdir(file_path) | 397 os.rmdir(file_path) |
398 | 398 |
399 | 399 |
400 def SubprocessCall(command, in_directory, fail_status=None): | 400 def SubprocessCall(command, in_directory, out, fail_status=None): |
401 """Runs command, a list, in directory in_directory. | 401 """Runs command, a list, in directory in_directory. |
402 | 402 |
403 This function wraps SubprocessCallAndCapture, but does not perform the | 403 This function wraps SubprocessCallAndCapture, but does not perform the |
404 capturing functions. See that function for a more complete usage | 404 capturing functions. See that function for a more complete usage |
405 description. | 405 description. |
406 """ | 406 """ |
407 # Call subprocess and capture nothing: | 407 # Call subprocess and capture nothing: |
408 SubprocessCallAndCapture(command, in_directory, fail_status) | 408 SubprocessCallAndCapture(command, in_directory, out, fail_status) |
409 | 409 |
410 | 410 |
411 def SubprocessCallAndCapture(command, in_directory, fail_status=None, | 411 def SubprocessCallAndCapture(command, in_directory, out, fail_status=None, |
412 pattern=None, capture_list=None): | 412 pattern=None, capture_list=None): |
413 """Runs command, a list, in directory in_directory. | 413 """Runs command, a list, in directory in_directory. |
414 | 414 |
415 A message indicating what is being done, as well as the command's stdout, | 415 A message indicating what is being done, as well as the command's stdout, |
416 is printed to out. | 416 is printed to out. |
417 | 417 |
418 If a pattern is specified, any line in the output matching pattern will have | 418 If a pattern is specified, any line in the output matching pattern will have |
419 its first match group appended to capture_list. | 419 its first match group appended to capture_list. |
420 | 420 |
421 If the command fails, as indicated by a nonzero exit status, gclient will | 421 If the command fails, as indicated by a nonzero exit status, gclient will |
422 exit with an exit status of fail_status. If fail_status is None (the | 422 exit with an exit status of fail_status. If fail_status is None (the |
423 default), gclient will raise an Error exception. | 423 default), gclient will raise an Error exception. |
424 """ | 424 """ |
425 | 425 |
426 print("\n________ running \'%s\' in \'%s\'" | 426 print >> out, ("\n________ running \'%s\' in \'%s\'" |
427 % (' '.join(command), in_directory)) | 427 % (' '.join(command), in_directory)) |
428 | 428 |
429 # *Sigh*: Windows needs shell=True, or else it won't search %PATH% for the | 429 # *Sigh*: Windows needs shell=True, or else it won't search %PATH% for the |
430 # executable, but shell=True makes subprocess on Linux fail when it's called | 430 # executable, but shell=True makes subprocess on Linux fail when it's called |
431 # with a list because it only tries to execute the first item in the list. | 431 # with a list because it only tries to execute the first item in the list. |
432 kid = subprocess.Popen(command, bufsize=0, cwd=in_directory, | 432 kid = subprocess.Popen(command, bufsize=0, cwd=in_directory, |
433 shell=(sys.platform == 'win32'), stdout=subprocess.PIPE) | 433 shell=(sys.platform == 'win32'), stdout=subprocess.PIPE) |
434 | 434 |
435 if pattern: | 435 if pattern: |
436 compiled_pattern = re.compile(pattern) | 436 compiled_pattern = re.compile(pattern) |
437 | 437 |
(...skipping 30 matching lines...) Expand all Loading... |
468 |entries| is a list of paths to check.""" | 468 |entries| is a list of paths to check.""" |
469 for path in paths: | 469 for path in paths: |
470 if os.path.exists(os.path.join(root, path, '.git')): | 470 if os.path.exists(os.path.join(root, path, '.git')): |
471 return True | 471 return True |
472 return False | 472 return False |
473 | 473 |
474 # ----------------------------------------------------------------------------- | 474 # ----------------------------------------------------------------------------- |
475 # SVN utils: | 475 # SVN utils: |
476 | 476 |
477 | 477 |
478 def RunSVN(args, in_directory): | 478 def RunSVN(options, args, in_directory): |
479 """Runs svn, sending output to stdout. | 479 """Runs svn, sending output to stdout. |
480 | 480 |
481 Args: | 481 Args: |
482 args: A sequence of command line parameters to be passed to svn. | 482 args: A sequence of command line parameters to be passed to svn. |
483 in_directory: The directory where svn is to be run. | 483 in_directory: The directory where svn is to be run. |
484 | 484 |
485 Raises: | 485 Raises: |
486 Error: An error occurred while running the svn command. | 486 Error: An error occurred while running the svn command. |
487 """ | 487 """ |
488 c = [SVN_COMMAND] | 488 c = [SVN_COMMAND] |
489 c.extend(args) | 489 c.extend(args) |
490 | 490 |
491 SubprocessCall(c, in_directory) | 491 SubprocessCall(c, in_directory, options.stdout) |
492 | 492 |
493 | 493 |
494 def CaptureSVN(args, in_directory): | 494 def CaptureSVN(options, args, in_directory): |
495 """Runs svn, capturing output sent to stdout as a string. | 495 """Runs svn, capturing output sent to stdout as a string. |
496 | 496 |
497 Args: | 497 Args: |
498 args: A sequence of command line parameters to be passed to svn. | 498 args: A sequence of command line parameters to be passed to svn. |
499 in_directory: The directory where svn is to be run. | 499 in_directory: The directory where svn is to be run. |
500 | 500 |
501 Returns: | 501 Returns: |
502 The output sent to stdout as a string. | 502 The output sent to stdout as a string. |
503 """ | 503 """ |
504 c = [SVN_COMMAND] | 504 c = [SVN_COMMAND] |
505 c.extend(args) | 505 c.extend(args) |
506 | 506 |
507 # *Sigh*: Windows needs shell=True, or else it won't search %PATH% for | 507 # *Sigh*: Windows needs shell=True, or else it won't search %PATH% for |
508 # the svn.exe executable, but shell=True makes subprocess on Linux fail | 508 # the svn.exe executable, but shell=True makes subprocess on Linux fail |
509 # when it's called with a list because it only tries to execute the | 509 # when it's called with a list because it only tries to execute the |
510 # first string ("svn"). | 510 # first string ("svn"). |
511 return subprocess.Popen(c, | 511 return subprocess.Popen(c, cwd=in_directory, shell=(sys.platform == 'win32'), |
512 cwd=in_directory, | |
513 shell=(sys.platform == 'win32'), | |
514 stdout=subprocess.PIPE).communicate()[0] | 512 stdout=subprocess.PIPE).communicate()[0] |
515 | 513 |
516 | 514 |
517 def RunSVNAndGetFileList(args, in_directory, file_list): | 515 def RunSVNAndGetFileList(options, args, in_directory, file_list): |
518 """Runs svn checkout, update, or status, output to stdout. | 516 """Runs svn checkout, update, or status, output to stdout. |
519 | 517 |
520 The first item in args must be either "checkout", "update", or "status". | 518 The first item in args must be either "checkout", "update", or "status". |
521 | 519 |
522 svn's stdout is parsed to collect a list of files checked out or updated. | 520 svn's stdout is parsed to collect a list of files checked out or updated. |
523 These files are appended to file_list. svn's stdout is also printed to | 521 These files are appended to file_list. svn's stdout is also printed to |
524 sys.stdout as in RunSVN. | 522 sys.stdout as in RunSVN. |
525 | 523 |
526 Args: | 524 Args: |
527 args: A sequence of command line parameters to be passed to svn. | 525 args: A sequence of command line parameters to be passed to svn. |
(...skipping 19 matching lines...) Expand all Loading... |
547 # args[0] must be a supported command. This will blow up if it's something | 545 # args[0] must be a supported command. This will blow up if it's something |
548 # else, which is good. Note that the patterns are only effective when | 546 # else, which is good. Note that the patterns are only effective when |
549 # these commands are used in their ordinary forms, the patterns are invalid | 547 # these commands are used in their ordinary forms, the patterns are invalid |
550 # for "svn status --show-updates", for example. | 548 # for "svn status --show-updates", for example. |
551 pattern = { | 549 pattern = { |
552 'checkout': update_pattern, | 550 'checkout': update_pattern, |
553 'status': status_pattern, | 551 'status': status_pattern, |
554 'update': update_pattern, | 552 'update': update_pattern, |
555 }[args[0]] | 553 }[args[0]] |
556 | 554 |
557 SubprocessCallAndCapture(command, | 555 SubprocessCallAndCapture(command, in_directory, options.stdout, |
558 in_directory, | 556 pattern=pattern, capture_list=file_list) |
559 pattern=pattern, | |
560 capture_list=file_list) | |
561 | 557 |
562 | 558 |
563 def CaptureSVNInfo(relpath, in_directory=None): | 559 def CaptureSVNInfo(options, relpath, in_directory): |
564 """Returns a dictionary from the svn info output for the given file. | 560 """Returns a dictionary from the svn info output for the given file. |
565 | 561 |
566 Args: | 562 Args: |
567 relpath: The directory where the working copy resides relative to | 563 relpath: The directory where the working copy resides relative to |
568 the directory given by in_directory. | 564 the directory given by in_directory. |
569 in_directory: The directory where svn is to be run. | 565 in_directory: The directory where svn is to be run. |
570 """ | 566 """ |
571 output = CaptureSVN(["info", "--xml", relpath], in_directory) | 567 dom = ParseXML(CaptureSVN(options, ["info", "--xml", relpath], in_directory)) |
572 dom = ParseXML(output) | |
573 result = {} | 568 result = {} |
574 if dom: | 569 if dom: |
575 def C(item, f): | 570 def C(item, f): |
576 if item is not None: return f(item) | 571 if item is not None: return f(item) |
577 # /info/entry/ | 572 # /info/entry/ |
578 # url | 573 # url |
579 # reposityory/(root|uuid) | 574 # reposityory/(root|uuid) |
580 # wc-info/(schedule|depth) | 575 # wc-info/(schedule|depth) |
581 # commit/(author|date) | 576 # commit/(author|date) |
582 # str() the results because they may be returned as Unicode, which | 577 # str() the results because they may be returned as Unicode, which |
583 # interferes with the higher layers matching up things in the deps | 578 # interferes with the higher layers matching up things in the deps |
584 # dictionary. | 579 # dictionary. |
585 # TODO(maruel): Fix at higher level instead (!) | 580 # TODO(maruel): Fix at higher level instead (!) |
586 result['Repository Root'] = C(GetNamedNodeText(dom, 'root'), str) | 581 result['Repository Root'] = C(GetNamedNodeText(dom, 'root'), str) |
587 result['URL'] = C(GetNamedNodeText(dom, 'url'), str) | 582 result['URL'] = C(GetNamedNodeText(dom, 'url'), str) |
588 result['UUID'] = C(GetNamedNodeText(dom, 'uuid'), str) | 583 result['UUID'] = C(GetNamedNodeText(dom, 'uuid'), str) |
589 result['Revision'] = C(GetNodeNamedAttributeText(dom, 'entry', 'revision'), | 584 result['Revision'] = C(GetNodeNamedAttributeText(dom, 'entry', 'revision'), |
590 int) | 585 int) |
591 result['Node Kind'] = C(GetNodeNamedAttributeText(dom, 'entry', 'kind'), | 586 result['Node Kind'] = C(GetNodeNamedAttributeText(dom, 'entry', 'kind'), |
592 str) | 587 str) |
593 result['Schedule'] = C(GetNamedNodeText(dom, 'schedule'), str) | 588 result['Schedule'] = C(GetNamedNodeText(dom, 'schedule'), str) |
594 result['Path'] = C(GetNodeNamedAttributeText(dom, 'entry', 'path'), str) | 589 result['Path'] = C(GetNodeNamedAttributeText(dom, 'entry', 'path'), str) |
595 result['Copied From URL'] = C(GetNamedNodeText(dom, 'copy-from-url'), str) | 590 result['Copied From URL'] = C(GetNamedNodeText(dom, 'copy-from-url'), str) |
596 result['Copied From Rev'] = C(GetNamedNodeText(dom, 'copy-from-rev'), str) | 591 result['Copied From Rev'] = C(GetNamedNodeText(dom, 'copy-from-rev'), str) |
597 return result | 592 return result |
598 | 593 |
599 | 594 |
600 def CaptureSVNHeadRevision(url): | 595 def CaptureSVNHeadRevision(options, url): |
601 """Get the head revision of a SVN repository. | 596 """Get the head revision of a SVN repository. |
602 | 597 |
603 Returns: | 598 Returns: |
604 Int head revision | 599 Int head revision |
605 """ | 600 """ |
606 info = CaptureSVN(["info", "--xml", url], os.getcwd()) | 601 info = CaptureSVN(options, ["info", "--xml", url], os.getcwd()) |
607 dom = xml.dom.minidom.parseString(info) | 602 dom = xml.dom.minidom.parseString(info) |
608 return int(dom.getElementsByTagName('entry')[0].getAttribute('revision')) | 603 return int(dom.getElementsByTagName('entry')[0].getAttribute('revision')) |
609 | 604 |
610 | 605 |
611 class FileStatus: | 606 class FileStatus: |
612 def __init__(self, path, text_status, props, lock, history): | 607 def __init__(self, path, text_status, props, lock, history): |
613 self.path = path | 608 self.path = path |
614 self.text_status = text_status | 609 self.text_status = text_status |
615 self.props = props | 610 self.props = props |
616 self.lock = lock | 611 self.lock = lock |
617 self.history = history | 612 self.history = history |
618 | 613 |
619 def __str__(self): | 614 def __str__(self): |
620 # Emulate svn status 1.5 output. | 615 # Emulate svn status 1.5 output. |
621 return (self.text_status + self.props + self.lock + self.history + ' ' + | 616 return (self.text_status + self.props + self.lock + self.history + ' ' + |
622 self.path) | 617 self.path) |
623 | 618 |
624 | 619 |
625 def CaptureSVNStatus(path): | 620 def CaptureSVNStatus(options, path): |
626 """Runs 'svn status' on an existing path. | 621 """Runs 'svn status' on an existing path. |
627 | 622 |
628 Args: | 623 Args: |
629 path: The directory to run svn status. | 624 path: The directory to run svn status. |
630 | 625 |
631 Returns: | 626 Returns: |
632 An array of FileStatus corresponding to the emulated output of 'svn status' | 627 An array of FileStatus corresponding to the emulated output of 'svn status' |
633 version 1.5.""" | 628 version 1.5.""" |
634 dom = ParseXML(CaptureSVN(["status", "--xml"], path)) | 629 dom = ParseXML(CaptureSVN(options, ["status", "--xml"], path)) |
635 results = [] | 630 results = [] |
636 if dom: | 631 if dom: |
637 # /status/target/entry/(wc-status|commit|author|date) | 632 # /status/target/entry/(wc-status|commit|author|date) |
638 for target in dom.getElementsByTagName('target'): | 633 for target in dom.getElementsByTagName('target'): |
639 base_path = target.getAttribute('path') | 634 base_path = target.getAttribute('path') |
640 for entry in target.getElementsByTagName('entry'): | 635 for entry in target.getElementsByTagName('entry'): |
641 file = entry.getAttribute('path') | 636 file = entry.getAttribute('path') |
642 wc_status = entry.getElementsByTagName('wc-status') | 637 wc_status = entry.getElementsByTagName('wc-status') |
643 assert len(wc_status) == 1 | 638 assert len(wc_status) == 1 |
644 # Emulate svn 1.5 status ouput... | 639 # Emulate svn 1.5 status ouput... |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
726 | 721 |
727 if not command in commands: | 722 if not command in commands: |
728 raise Error('Unknown command %s' % command) | 723 raise Error('Unknown command %s' % command) |
729 | 724 |
730 return commands[command](options, args, file_list) | 725 return commands[command](options, args, file_list) |
731 | 726 |
732 def cleanup(self, options, args, file_list): | 727 def cleanup(self, options, args, file_list): |
733 """Cleanup working copy.""" | 728 """Cleanup working copy.""" |
734 command = ['cleanup'] | 729 command = ['cleanup'] |
735 command.extend(args) | 730 command.extend(args) |
736 RunSVN(command, os.path.join(self._root_dir, self.relpath)) | 731 RunSVN(options, command, os.path.join(self._root_dir, self.relpath)) |
737 | 732 |
738 def diff(self, options, args, file_list): | 733 def diff(self, options, args, file_list): |
739 # NOTE: This function does not currently modify file_list. | 734 # NOTE: This function does not currently modify file_list. |
740 command = ['diff'] | 735 command = ['diff'] |
741 command.extend(args) | 736 command.extend(args) |
742 RunSVN(command, os.path.join(self._root_dir, self.relpath)) | 737 RunSVN(options, command, os.path.join(self._root_dir, self.relpath)) |
743 | 738 |
744 def update(self, options, args, file_list): | 739 def update(self, options, args, file_list): |
745 """Runs SCM to update or transparently checkout the working copy. | 740 """Runs SCM to update or transparently checkout the working copy. |
746 | 741 |
747 All updated files will be appended to file_list. | 742 All updated files will be appended to file_list. |
748 | 743 |
749 Raises: | 744 Raises: |
750 Error: if can't get URL for relative path. | 745 Error: if can't get URL for relative path. |
751 """ | 746 """ |
752 # Only update if git is not controlling the directory. | 747 # Only update if git is not controlling the directory. |
753 git_path = os.path.join(self._root_dir, self.relpath, '.git') | 748 git_path = os.path.join(self._root_dir, self.relpath, '.git') |
754 if options.path_exists(git_path): | 749 if options.path_exists(git_path): |
755 print("________ found .git directory; skipping %s" % self.relpath) | 750 print >> options.stdout, ( |
| 751 "________ found .git directory; skipping %s" % self.relpath) |
756 return | 752 return |
757 | 753 |
758 if args: | 754 if args: |
759 raise Error("Unsupported argument(s): %s" % ",".join(args)) | 755 raise Error("Unsupported argument(s): %s" % ",".join(args)) |
760 | 756 |
761 url = self.url | 757 url = self.url |
762 components = url.split("@") | 758 components = url.split("@") |
763 revision = None | 759 revision = None |
764 forced_revision = False | 760 forced_revision = False |
765 if options.revision: | 761 if options.revision: |
766 # Override the revision number. | 762 # Override the revision number. |
767 url = '%s@%s' % (components[0], str(options.revision)) | 763 url = '%s@%s' % (components[0], str(options.revision)) |
768 revision = int(options.revision) | 764 revision = int(options.revision) |
769 forced_revision = True | 765 forced_revision = True |
770 elif len(components) == 2: | 766 elif len(components) == 2: |
771 revision = int(components[1]) | 767 revision = int(components[1]) |
772 forced_revision = True | 768 forced_revision = True |
773 | 769 |
774 rev_str = "" | 770 rev_str = "" |
775 if revision: | 771 if revision: |
776 rev_str = ' at %d' % revision | 772 rev_str = ' at %d' % revision |
777 | 773 |
778 if not options.path_exists(os.path.join(self._root_dir, self.relpath)): | 774 if not options.path_exists(os.path.join(self._root_dir, self.relpath)): |
779 # We need to checkout. | 775 # We need to checkout. |
780 command = ['checkout', url, os.path.join(self._root_dir, self.relpath)] | 776 command = ['checkout', url, os.path.join(self._root_dir, self.relpath)] |
781 if revision: | 777 if revision: |
782 command.extend(['--revision', str(revision)]) | 778 command.extend(['--revision', str(revision)]) |
783 RunSVNAndGetFileList(command, self._root_dir, file_list) | 779 RunSVNAndGetFileList(options, command, self._root_dir, file_list) |
784 return | 780 return |
785 | 781 |
786 # Get the existing scm url and the revision number of the current checkout. | 782 # Get the existing scm url and the revision number of the current checkout. |
787 from_info = CaptureSVNInfo(os.path.join(self._root_dir, self.relpath, '.'), | 783 from_info = CaptureSVNInfo(options, |
| 784 os.path.join(self._root_dir, self.relpath, '.'), |
788 '.') | 785 '.') |
789 | 786 |
790 if options.manually_grab_svn_rev: | 787 if options.manually_grab_svn_rev: |
791 # Retrieve the current HEAD version because svn is slow at null updates. | 788 # Retrieve the current HEAD version because svn is slow at null updates. |
792 if not revision: | 789 if not revision: |
793 from_info_live = CaptureSVNInfo(from_info['URL'], '.') | 790 from_info_live = CaptureSVNInfo(options, from_info['URL'], '.') |
794 revision = int(from_info_live['Revision']) | 791 revision = int(from_info_live['Revision']) |
795 rev_str = ' at %d' % revision | 792 rev_str = ' at %d' % revision |
796 | 793 |
797 if from_info['URL'] != components[0]: | 794 if from_info['URL'] != components[0]: |
798 to_info = CaptureSVNInfo(url, '.') | 795 to_info = CaptureSVNInfo(options, url, '.') |
799 if from_info['Repository Root'] != to_info['Repository Root']: | 796 if from_info['Repository Root'] != to_info['Repository Root']: |
800 # We have different roots, so check if we can switch --relocate. | 797 # We have different roots, so check if we can switch --relocate. |
801 # Subversion only permits this if the repository UUIDs match. | 798 # Subversion only permits this if the repository UUIDs match. |
802 if from_info['UUID'] != to_info['UUID']: | 799 if from_info['UUID'] != to_info['UUID']: |
803 raise Error("Can't switch the checkout to %s; UUID don't match. That " | 800 raise Error("Can't switch the checkout to %s; UUID don't match. That " |
804 "simply means in theory, gclient should verify you don't " | 801 "simply means in theory, gclient should verify you don't " |
805 "have a local change, remove the old checkout and do a " | 802 "have a local change, remove the old checkout and do a " |
806 "fresh new checkout of the new repo. Contributions are " | 803 "fresh new checkout of the new repo. Contributions are " |
807 "welcome." % url) | 804 "welcome." % url) |
808 | 805 |
809 # Perform the switch --relocate, then rewrite the from_url | 806 # Perform the switch --relocate, then rewrite the from_url |
810 # to reflect where we "are now." (This is the same way that | 807 # to reflect where we "are now." (This is the same way that |
811 # Subversion itself handles the metadata when switch --relocate | 808 # Subversion itself handles the metadata when switch --relocate |
812 # is used.) This makes the checks below for whether we | 809 # is used.) This makes the checks below for whether we |
813 # can update to a revision or have to switch to a different | 810 # can update to a revision or have to switch to a different |
814 # branch work as expected. | 811 # branch work as expected. |
815 # TODO(maruel): TEST ME ! | 812 # TODO(maruel): TEST ME ! |
816 command = ["switch", "--relocate", | 813 command = ["switch", "--relocate", |
817 from_info['Repository Root'], | 814 from_info['Repository Root'], |
818 to_info['Repository Root'], | 815 to_info['Repository Root'], |
819 self.relpath] | 816 self.relpath] |
820 RunSVN(command, self._root_dir) | 817 RunSVN(options, command, self._root_dir) |
821 from_info['URL'] = from_info['URL'].replace( | 818 from_info['URL'] = from_info['URL'].replace( |
822 from_info['Repository Root'], | 819 from_info['Repository Root'], |
823 to_info['Repository Root']) | 820 to_info['Repository Root']) |
824 | 821 |
825 # If the provided url has a revision number that matches the revision | 822 # If the provided url has a revision number that matches the revision |
826 # number of the existing directory, then we don't need to bother updating. | 823 # number of the existing directory, then we don't need to bother updating. |
827 if not options.force and from_info['Revision'] == revision: | 824 if not options.force and from_info['Revision'] == revision: |
828 if options.verbose or not forced_revision: | 825 if options.verbose or not forced_revision: |
829 print("\n_____ %s%s" % (self.relpath, rev_str)) | 826 print >>options.stdout, ("\n_____ %s%s" % ( |
| 827 self.relpath, rev_str)) |
830 return | 828 return |
831 | 829 |
832 command = ["update", os.path.join(self._root_dir, self.relpath)] | 830 command = ["update", os.path.join(self._root_dir, self.relpath)] |
833 if revision: | 831 if revision: |
834 command.extend(['--revision', str(revision)]) | 832 command.extend(['--revision', str(revision)]) |
835 RunSVNAndGetFileList(command, self._root_dir, file_list) | 833 RunSVNAndGetFileList(options, command, self._root_dir, file_list) |
836 | 834 |
837 def revert(self, options, args, file_list): | 835 def revert(self, options, args, file_list): |
838 """Reverts local modifications. Subversion specific. | 836 """Reverts local modifications. Subversion specific. |
839 | 837 |
840 All reverted files will be appended to file_list, even if Subversion | 838 All reverted files will be appended to file_list, even if Subversion |
841 doesn't know about them. | 839 doesn't know about them. |
842 """ | 840 """ |
843 path = os.path.join(self._root_dir, self.relpath) | 841 path = os.path.join(self._root_dir, self.relpath) |
844 if not os.path.isdir(path): | 842 if not os.path.isdir(path): |
845 # svn revert won't work if the directory doesn't exist. It needs to | 843 # svn revert won't work if the directory doesn't exist. It needs to |
846 # checkout instead. | 844 # checkout instead. |
847 print("\n_____ %s is missing, synching instead" % self.relpath) | 845 print >>options.stdout, ("\n_____ %s is missing, synching instead" % |
| 846 self.relpath) |
848 # Don't reuse the args. | 847 # Don't reuse the args. |
849 return self.update(options, [], file_list) | 848 return self.update(options, [], file_list) |
850 | 849 |
851 files = CaptureSVNStatus(path) | 850 files = CaptureSVNStatus(options, path) |
852 # Batch the command. | 851 # Batch the command. |
853 files_to_revert = [] | 852 files_to_revert = [] |
854 for file in files: | 853 for file in files: |
855 file_path = os.path.join(path, file.path) | 854 file_path = os.path.join(path, file.path) |
856 print(file_path) | 855 print >>options.stdout, file_path |
857 # Unversioned file or unexpected unversioned file. | 856 # Unversioned file or unexpected unversioned file. |
858 if file.text_status in ('?', '~'): | 857 if file.text_status in ('?', '~'): |
859 # Remove extraneous file. Also remove unexpected unversioned | 858 # Remove extraneous file. Also remove unexpected unversioned |
860 # directories. svn won't touch them but we want to delete these. | 859 # directories. svn won't touch them but we want to delete these. |
861 file_list.append(file_path) | 860 file_list.append(file_path) |
862 try: | 861 try: |
863 os.remove(file_path) | 862 os.remove(file_path) |
864 except EnvironmentError: | 863 except EnvironmentError: |
865 RemoveDirectory(file_path) | 864 RemoveDirectory(file_path) |
866 | 865 |
867 if file.text_status != '?': | 866 if file.text_status != '?': |
868 # For any other status, svn revert will work. | 867 # For any other status, svn revert will work. |
869 file_list.append(file_path) | 868 file_list.append(file_path) |
870 files_to_revert.append(file.path) | 869 files_to_revert.append(file.path) |
871 | 870 |
872 # Revert them all at once. | 871 # Revert them all at once. |
873 if files_to_revert: | 872 if files_to_revert: |
874 accumulated_paths = [] | 873 accumulated_paths = [] |
875 accumulated_length = 0 | 874 accumulated_length = 0 |
876 command = ['revert'] | 875 command = ['revert'] |
877 for p in files_to_revert: | 876 for p in files_to_revert: |
878 # Some shell have issues with command lines too long. | 877 # Some shell have issues with command lines too long. |
879 if accumulated_length and accumulated_length + len(p) > 3072: | 878 if accumulated_length and accumulated_length + len(p) > 3072: |
880 RunSVN(command + accumulated_paths, | 879 RunSVN(options, command + accumulated_paths, |
881 os.path.join(self._root_dir, self.relpath)) | 880 os.path.join(self._root_dir, self.relpath)) |
882 accumulated_paths = [] | 881 accumulated_paths = [] |
883 accumulated_length = 0 | 882 accumulated_length = 0 |
884 else: | 883 else: |
885 accumulated_paths.append(p) | 884 accumulated_paths.append(p) |
886 accumulated_length += len(p) | 885 accumulated_length += len(p) |
887 if accumulated_paths: | 886 if accumulated_paths: |
888 RunSVN(command + accumulated_paths, | 887 RunSVN(options, command + accumulated_paths, |
889 os.path.join(self._root_dir, self.relpath)) | 888 os.path.join(self._root_dir, self.relpath)) |
890 | 889 |
891 def status(self, options, args, file_list): | 890 def status(self, options, args, file_list): |
892 """Display status information.""" | 891 """Display status information.""" |
893 path = os.path.join(self._root_dir, self.relpath) | 892 path = os.path.join(self._root_dir, self.relpath) |
894 command = ['status'] | 893 command = ['status'] |
895 command.extend(args) | 894 command.extend(args) |
896 if not os.path.isdir(path): | 895 if not os.path.isdir(path): |
897 # svn status won't work if the directory doesn't exist. | 896 # svn status won't work if the directory doesn't exist. |
898 print("\n________ couldn't run \'%s\' in \'%s\':\nThe directory " | 897 print >> options.stdout, ( |
899 "does not exist." | 898 "\n________ couldn't run \'%s\' in \'%s\':\nThe directory " |
900 % (' '.join(command), path)) | 899 "does not exist." |
| 900 % (' '.join(command), path)) |
901 # There's no file list to retrieve. | 901 # There's no file list to retrieve. |
902 else: | 902 else: |
903 RunSVNAndGetFileList(command, path, file_list) | 903 RunSVNAndGetFileList(options, command, path, file_list) |
904 | 904 |
905 | 905 |
906 ## GClient implementation. | 906 ## GClient implementation. |
907 | 907 |
908 | 908 |
909 class GClient(object): | 909 class GClient(object): |
910 """Object that represent a gclient checkout.""" | 910 """Object that represent a gclient checkout.""" |
911 | 911 |
912 supported_commands = [ | 912 supported_commands = [ |
913 'cleanup', 'diff', 'revert', 'status', 'update', 'runhooks' | 913 'cleanup', 'diff', 'revert', 'status', 'update', 'runhooks' |
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1174 command = hook_dict['action'][:] | 1174 command = hook_dict['action'][:] |
1175 if command[0] == 'python': | 1175 if command[0] == 'python': |
1176 # If the hook specified "python" as the first item, the action is a | 1176 # If the hook specified "python" as the first item, the action is a |
1177 # Python script. Run it by starting a new copy of the same | 1177 # Python script. Run it by starting a new copy of the same |
1178 # interpreter. | 1178 # interpreter. |
1179 command[0] = sys.executable | 1179 command[0] = sys.executable |
1180 | 1180 |
1181 # Use a discrete exit status code of 2 to indicate that a hook action | 1181 # Use a discrete exit status code of 2 to indicate that a hook action |
1182 # failed. Users of this script may wish to treat hook action failures | 1182 # failed. Users of this script may wish to treat hook action failures |
1183 # differently from VC failures. | 1183 # differently from VC failures. |
1184 SubprocessCall(command, self._root_dir, fail_status=2) | 1184 SubprocessCall(command, self._root_dir, self._options.stdout, |
| 1185 fail_status=2) |
1185 | 1186 |
1186 def _RunHooks(self, command, file_list, is_using_git): | 1187 def _RunHooks(self, command, file_list, is_using_git): |
1187 """Evaluates all hooks, running actions as needed. | 1188 """Evaluates all hooks, running actions as needed. |
1188 """ | 1189 """ |
1189 # Hooks only run for these command types. | 1190 # Hooks only run for these command types. |
1190 if not command in ('update', 'revert', 'runhooks'): | 1191 if not command in ('update', 'revert', 'runhooks'): |
1191 return | 1192 return |
1192 | 1193 |
1193 # Get any hooks from the .gclient file. | 1194 # Get any hooks from the .gclient file. |
1194 hooks = self.GetVar("hooks", []) | 1195 hooks = self.GetVar("hooks", []) |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1317 self._RunHooks(command, file_list, is_using_git) | 1318 self._RunHooks(command, file_list, is_using_git) |
1318 | 1319 |
1319 if command == 'update': | 1320 if command == 'update': |
1320 # notify the user if there is an orphaned entry in their working copy. | 1321 # notify the user if there is an orphaned entry in their working copy. |
1321 # TODO(darin): we should delete this directory manually if it doesn't | 1322 # TODO(darin): we should delete this directory manually if it doesn't |
1322 # have any changes in it. | 1323 # have any changes in it. |
1323 prev_entries = self._ReadEntries() | 1324 prev_entries = self._ReadEntries() |
1324 for entry in prev_entries: | 1325 for entry in prev_entries: |
1325 e_dir = os.path.join(self._root_dir, entry) | 1326 e_dir = os.path.join(self._root_dir, entry) |
1326 if entry not in entries and self._options.path_exists(e_dir): | 1327 if entry not in entries and self._options.path_exists(e_dir): |
1327 if CaptureSVNStatus(e_dir): | 1328 if CaptureSVNStatus(self._options, e_dir): |
1328 # There are modified files in this entry | 1329 # There are modified files in this entry |
1329 entries[entry] = None # Keep warning until removed. | 1330 entries[entry] = None # Keep warning until removed. |
1330 print("\nWARNING: \"%s\" is no longer part of this client. " | 1331 print >> self._options.stdout, ( |
1331 "It is recommended that you manually remove it.\n") % entry | 1332 "\nWARNING: \"%s\" is no longer part of this client. " |
| 1333 "It is recommended that you manually remove it.\n") % entry |
1332 else: | 1334 else: |
1333 # Delete the entry | 1335 # Delete the entry |
1334 print("\n________ deleting \'%s\' " + | 1336 print >> self._options.stdout, ("\n________ deleting \'%s\' " + |
1335 "in \'%s\'") % (entry, self._root_dir) | 1337 "in \'%s\'") % (entry, self._root_dir) |
1336 RemoveDirectory(e_dir) | 1338 RemoveDirectory(e_dir) |
1337 # record the current list of entries for next time | 1339 # record the current list of entries for next time |
1338 self._SaveEntries(entries) | 1340 self._SaveEntries(entries) |
1339 | 1341 |
1340 def PrintRevInfo(self): | 1342 def PrintRevInfo(self): |
1341 """Output revision info mapping for the client and its dependencies. This | 1343 """Output revision info mapping for the client and its dependencies. This |
1342 allows the capture of a overall "revision" for the source tree that can | 1344 allows the capture of a overall "revision" for the source tree that can |
1343 be used to reproduce the same tree in the future. The actual output | 1345 be used to reproduce the same tree in the future. The actual output |
1344 contains enough information (source paths, svn server urls and revisions) | 1346 contains enough information (source paths, svn server urls and revisions) |
1345 that it can be used either to generate external svn commands (without | 1347 that it can be used either to generate external svn commands (without |
(...skipping 28 matching lines...) Expand all Loading... |
1374 entries_deps_content = {} | 1376 entries_deps_content = {} |
1375 | 1377 |
1376 # Inner helper to generate base url and rev tuple (including honoring | 1378 # Inner helper to generate base url and rev tuple (including honoring |
1377 # |revision_overrides|) | 1379 # |revision_overrides|) |
1378 def GetURLAndRev(name, original_url): | 1380 def GetURLAndRev(name, original_url): |
1379 if original_url.find("@") < 0: | 1381 if original_url.find("@") < 0: |
1380 if revision_overrides.has_key(name): | 1382 if revision_overrides.has_key(name): |
1381 return (original_url, int(revision_overrides[name])) | 1383 return (original_url, int(revision_overrides[name])) |
1382 else: | 1384 else: |
1383 # TODO(aharper): SVN/SCMWrapper cleanup (non-local commandset) | 1385 # TODO(aharper): SVN/SCMWrapper cleanup (non-local commandset) |
1384 return (original_url, CaptureSVNHeadRevision(original_url)) | 1386 return (original_url, CaptureSVNHeadRevision(self._options, |
| 1387 original_url)) |
1385 else: | 1388 else: |
1386 url_components = original_url.split("@") | 1389 url_components = original_url.split("@") |
1387 if revision_overrides.has_key(name): | 1390 if revision_overrides.has_key(name): |
1388 return (url_components[0], int(revision_overrides[name])) | 1391 return (url_components[0], int(revision_overrides[name])) |
1389 else: | 1392 else: |
1390 return (url_components[0], int(url_components[1])) | 1393 return (url_components[0], int(url_components[1])) |
1391 | 1394 |
1392 # Run on the base solutions first. | 1395 # Run on the base solutions first. |
1393 for solution in solutions: | 1396 for solution in solutions: |
1394 name = solution["name"] | 1397 name = solution["name"] |
1395 if name in entries: | 1398 if name in entries: |
1396 raise Error("solution %s specified more than once" % name) | 1399 raise Error("solution %s specified more than once" % name) |
1397 (url, rev) = GetURLAndRev(name, solution["url"]) | 1400 (url, rev) = GetURLAndRev(name, solution["url"]) |
1398 entries[name] = "%s@%d" % (url, rev) | 1401 entries[name] = "%s@%d" % (url, rev) |
1399 # TODO(aharper): SVN/SCMWrapper cleanup (non-local commandset) | 1402 # TODO(aharper): SVN/SCMWrapper cleanup (non-local commandset) |
1400 entries_deps_content[name] = CaptureSVN( | 1403 entries_deps_content[name] = CaptureSVN( |
| 1404 self._options, |
1401 ["cat", | 1405 ["cat", |
1402 "%s/%s@%d" % (url, | 1406 "%s/%s@%d" % (url, |
1403 self._options.deps_file, | 1407 self._options.deps_file, |
1404 rev)], | 1408 rev)], |
1405 os.getcwd()) | 1409 os.getcwd()) |
1406 | 1410 |
1407 # Process the dependencies next (sort alphanumerically to ensure that | 1411 # Process the dependencies next (sort alphanumerically to ensure that |
1408 # containing directories get populated first and for readability) | 1412 # containing directories get populated first and for readability) |
1409 deps = self._ParseAllDeps(entries, entries_deps_content) | 1413 deps = self._ParseAllDeps(entries, entries_deps_content) |
1410 deps_to_process = deps.keys() | 1414 deps_to_process = deps.keys() |
1411 deps_to_process.sort() | 1415 deps_to_process.sort() |
1412 | 1416 |
1413 # First pass for direct dependencies. | 1417 # First pass for direct dependencies. |
1414 for d in deps_to_process: | 1418 for d in deps_to_process: |
1415 if type(deps[d]) == str: | 1419 if type(deps[d]) == str: |
1416 (url, rev) = GetURLAndRev(d, deps[d]) | 1420 (url, rev) = GetURLAndRev(d, deps[d]) |
1417 entries[d] = "%s@%d" % (url, rev) | 1421 entries[d] = "%s@%d" % (url, rev) |
1418 | 1422 |
1419 # Second pass for inherited deps (via the From keyword) | 1423 # Second pass for inherited deps (via the From keyword) |
1420 for d in deps_to_process: | 1424 for d in deps_to_process: |
1421 if type(deps[d]) != str: | 1425 if type(deps[d]) != str: |
1422 deps_parent_url = entries[deps[d].module_name] | 1426 deps_parent_url = entries[deps[d].module_name] |
1423 if deps_parent_url.find("@") < 0: | 1427 if deps_parent_url.find("@") < 0: |
1424 raise Error("From %s missing revisioned url" % deps[d].module_name) | 1428 raise Error("From %s missing revisioned url" % deps[d].module_name) |
1425 deps_parent_url_components = deps_parent_url.split("@") | 1429 deps_parent_url_components = deps_parent_url.split("@") |
1426 # TODO(aharper): SVN/SCMWrapper cleanup (non-local commandset) | 1430 # TODO(aharper): SVN/SCMWrapper cleanup (non-local commandset) |
1427 deps_parent_content = CaptureSVN( | 1431 deps_parent_content = CaptureSVN( |
| 1432 self._options, |
1428 ["cat", | 1433 ["cat", |
1429 "%s/%s@%s" % (deps_parent_url_components[0], | 1434 "%s/%s@%s" % (deps_parent_url_components[0], |
1430 self._options.deps_file, | 1435 self._options.deps_file, |
1431 deps_parent_url_components[1])], | 1436 deps_parent_url_components[1])], |
1432 os.getcwd()) | 1437 os.getcwd()) |
1433 sub_deps = self._ParseSolutionDeps( | 1438 sub_deps = self._ParseSolutionDeps( |
1434 deps[d].module_name, | 1439 deps[d].module_name, |
1435 FileRead(os.path.join(self._root_dir, | 1440 FileRead(os.path.join(self._root_dir, |
1436 deps[d].module_name, | 1441 deps[d].module_name, |
1437 self._options.deps_file)), | 1442 self._options.deps_file)), |
1438 {}) | 1443 {}) |
1439 (url, rev) = GetURLAndRev(d, sub_deps[d]) | 1444 (url, rev) = GetURLAndRev(d, sub_deps[d]) |
1440 entries[d] = "%s@%d" % (url, rev) | 1445 entries[d] = "%s@%d" % (url, rev) |
1441 | 1446 |
1442 print(";".join(["%s,%s" % (x, entries[x]) for x in sorted(entries.keys())])) | 1447 print ";".join(["%s,%s" % (x, entries[x]) for x in sorted(entries.keys())]) |
1443 | 1448 |
1444 | 1449 |
1445 ## gclient commands. | 1450 ## gclient commands. |
1446 | 1451 |
1447 | 1452 |
1448 def DoCleanup(options, args): | 1453 def DoCleanup(options, args): |
1449 """Handle the cleanup subcommand. | 1454 """Handle the cleanup subcommand. |
1450 | 1455 |
1451 Raises: | 1456 Raises: |
1452 Error: if client isn't configured properly. | 1457 Error: if client isn't configured properly. |
1453 """ | 1458 """ |
1454 client = options.gclient.LoadCurrentConfig(options) | 1459 client = options.gclient.LoadCurrentConfig(options) |
1455 if not client: | 1460 if not client: |
1456 raise Error("client not configured; see 'gclient config'") | 1461 raise Error("client not configured; see 'gclient config'") |
1457 if options.verbose: | 1462 if options.verbose: |
1458 # Print out the .gclient file. This is longer than if we just printed the | 1463 # Print out the .gclient file. This is longer than if we just printed the |
1459 # client dict, but more legible, and it might contain helpful comments. | 1464 # client dict, but more legible, and it might contain helpful comments. |
1460 print(client.ConfigContent()) | 1465 print >>options.stdout, client.ConfigContent() |
1461 options.verbose = True | 1466 options.verbose = True |
1462 return client.RunOnDeps('cleanup', args) | 1467 return client.RunOnDeps('cleanup', args) |
1463 | 1468 |
1464 | 1469 |
1465 def DoConfig(options, args): | 1470 def DoConfig(options, args): |
1466 """Handle the config subcommand. | 1471 """Handle the config subcommand. |
1467 | 1472 |
1468 Args: | 1473 Args: |
1469 options: If options.spec set, a string providing contents of config file. | 1474 options: If options.spec set, a string providing contents of config file. |
1470 args: The command line args. If spec is not set, | 1475 args: The command line args. If spec is not set, |
(...skipping 22 matching lines...) Expand all Loading... |
1493 client.SaveConfig() | 1498 client.SaveConfig() |
1494 | 1499 |
1495 | 1500 |
1496 def DoHelp(options, args): | 1501 def DoHelp(options, args): |
1497 """Handle the help subcommand giving help for another subcommand. | 1502 """Handle the help subcommand giving help for another subcommand. |
1498 | 1503 |
1499 Raises: | 1504 Raises: |
1500 Error: if the command is unknown. | 1505 Error: if the command is unknown. |
1501 """ | 1506 """ |
1502 if len(args) == 1 and args[0] in COMMAND_USAGE_TEXT: | 1507 if len(args) == 1 and args[0] in COMMAND_USAGE_TEXT: |
1503 print(COMMAND_USAGE_TEXT[args[0]]) | 1508 print >>options.stdout, COMMAND_USAGE_TEXT[args[0]] |
1504 else: | 1509 else: |
1505 raise Error("unknown subcommand '%s'; see 'gclient help'" % args[0]) | 1510 raise Error("unknown subcommand '%s'; see 'gclient help'" % args[0]) |
1506 | 1511 |
1507 | 1512 |
1508 def DoStatus(options, args): | 1513 def DoStatus(options, args): |
1509 """Handle the status subcommand. | 1514 """Handle the status subcommand. |
1510 | 1515 |
1511 Raises: | 1516 Raises: |
1512 Error: if client isn't configured properly. | 1517 Error: if client isn't configured properly. |
1513 """ | 1518 """ |
1514 client = options.gclient.LoadCurrentConfig(options) | 1519 client = options.gclient.LoadCurrentConfig(options) |
1515 if not client: | 1520 if not client: |
1516 raise Error("client not configured; see 'gclient config'") | 1521 raise Error("client not configured; see 'gclient config'") |
1517 if options.verbose: | 1522 if options.verbose: |
1518 # Print out the .gclient file. This is longer than if we just printed the | 1523 # Print out the .gclient file. This is longer than if we just printed the |
1519 # client dict, but more legible, and it might contain helpful comments. | 1524 # client dict, but more legible, and it might contain helpful comments. |
1520 print(client.ConfigContent()) | 1525 print >>options.stdout, client.ConfigContent() |
1521 options.verbose = True | 1526 options.verbose = True |
1522 return client.RunOnDeps('status', args) | 1527 return client.RunOnDeps('status', args) |
1523 | 1528 |
1524 | 1529 |
1525 def DoUpdate(options, args): | 1530 def DoUpdate(options, args): |
1526 """Handle the update and sync subcommands. | 1531 """Handle the update and sync subcommands. |
1527 | 1532 |
1528 Raises: | 1533 Raises: |
1529 Error: if client isn't configured properly. | 1534 Error: if client isn't configured properly. |
1530 """ | 1535 """ |
(...skipping 18 matching lines...) Expand all Loading... |
1549 if not has_key: | 1554 if not has_key: |
1550 handle = urllib.urlopen(s['safesync_url']) | 1555 handle = urllib.urlopen(s['safesync_url']) |
1551 rev = handle.read().strip() | 1556 rev = handle.read().strip() |
1552 handle.close() | 1557 handle.close() |
1553 if len(rev): | 1558 if len(rev): |
1554 options.revisions.append(s['name']+'@'+rev) | 1559 options.revisions.append(s['name']+'@'+rev) |
1555 | 1560 |
1556 if options.verbose: | 1561 if options.verbose: |
1557 # Print out the .gclient file. This is longer than if we just printed the | 1562 # Print out the .gclient file. This is longer than if we just printed the |
1558 # client dict, but more legible, and it might contain helpful comments. | 1563 # client dict, but more legible, and it might contain helpful comments. |
1559 print(client.ConfigContent()) | 1564 print >>options.stdout, client.ConfigContent() |
1560 return client.RunOnDeps('update', args) | 1565 return client.RunOnDeps('update', args) |
1561 | 1566 |
1562 | 1567 |
1563 def DoDiff(options, args): | 1568 def DoDiff(options, args): |
1564 """Handle the diff subcommand. | 1569 """Handle the diff subcommand. |
1565 | 1570 |
1566 Raises: | 1571 Raises: |
1567 Error: if client isn't configured properly. | 1572 Error: if client isn't configured properly. |
1568 """ | 1573 """ |
1569 client = options.gclient.LoadCurrentConfig(options) | 1574 client = options.gclient.LoadCurrentConfig(options) |
1570 if not client: | 1575 if not client: |
1571 raise Error("client not configured; see 'gclient config'") | 1576 raise Error("client not configured; see 'gclient config'") |
1572 if options.verbose: | 1577 if options.verbose: |
1573 # Print out the .gclient file. This is longer than if we just printed the | 1578 # Print out the .gclient file. This is longer than if we just printed the |
1574 # client dict, but more legible, and it might contain helpful comments. | 1579 # client dict, but more legible, and it might contain helpful comments. |
1575 print(client.ConfigContent()) | 1580 print >>options.stdout, client.ConfigContent() |
1576 options.verbose = True | 1581 options.verbose = True |
1577 return client.RunOnDeps('diff', args) | 1582 return client.RunOnDeps('diff', args) |
1578 | 1583 |
1579 | 1584 |
1580 def DoRevert(options, args): | 1585 def DoRevert(options, args): |
1581 """Handle the revert subcommand. | 1586 """Handle the revert subcommand. |
1582 | 1587 |
1583 Raises: | 1588 Raises: |
1584 Error: if client isn't configured properly. | 1589 Error: if client isn't configured properly. |
1585 """ | 1590 """ |
1586 client = options.gclient.LoadCurrentConfig(options) | 1591 client = options.gclient.LoadCurrentConfig(options) |
1587 if not client: | 1592 if not client: |
1588 raise Error("client not configured; see 'gclient config'") | 1593 raise Error("client not configured; see 'gclient config'") |
1589 return client.RunOnDeps('revert', args) | 1594 return client.RunOnDeps('revert', args) |
1590 | 1595 |
1591 | 1596 |
1592 def DoRunHooks(options, args): | 1597 def DoRunHooks(options, args): |
1593 """Handle the runhooks subcommand. | 1598 """Handle the runhooks subcommand. |
1594 | 1599 |
1595 Raises: | 1600 Raises: |
1596 Error: if client isn't configured properly. | 1601 Error: if client isn't configured properly. |
1597 """ | 1602 """ |
1598 client = options.gclient.LoadCurrentConfig(options) | 1603 client = options.gclient.LoadCurrentConfig(options) |
1599 if not client: | 1604 if not client: |
1600 raise Error("client not configured; see 'gclient config'") | 1605 raise Error("client not configured; see 'gclient config'") |
1601 if options.verbose: | 1606 if options.verbose: |
1602 # Print out the .gclient file. This is longer than if we just printed the | 1607 # Print out the .gclient file. This is longer than if we just printed the |
1603 # client dict, but more legible, and it might contain helpful comments. | 1608 # client dict, but more legible, and it might contain helpful comments. |
1604 print(client.ConfigContent()) | 1609 print >>options.stdout, client.ConfigContent() |
1605 return client.RunOnDeps('runhooks', args) | 1610 return client.RunOnDeps('runhooks', args) |
1606 | 1611 |
1607 | 1612 |
1608 def DoRevInfo(options, args): | 1613 def DoRevInfo(options, args): |
1609 """Handle the revinfo subcommand. | 1614 """Handle the revinfo subcommand. |
1610 | 1615 |
1611 Raises: | 1616 Raises: |
1612 Error: if client isn't configured properly. | 1617 Error: if client isn't configured properly. |
1613 """ | 1618 """ |
1614 client = options.gclient.LoadCurrentConfig(options) | 1619 client = options.gclient.LoadCurrentConfig(options) |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1693 | 1698 |
1694 if len(argv) < 3 and command == "help": | 1699 if len(argv) < 3 and command == "help": |
1695 option_parser.print_help() | 1700 option_parser.print_help() |
1696 return 0 | 1701 return 0 |
1697 | 1702 |
1698 # Files used for configuration and state saving. | 1703 # Files used for configuration and state saving. |
1699 options.config_filename = os.environ.get("GCLIENT_FILE", ".gclient") | 1704 options.config_filename = os.environ.get("GCLIENT_FILE", ".gclient") |
1700 options.entries_filename = ".gclient_entries" | 1705 options.entries_filename = ".gclient_entries" |
1701 options.deps_file = "DEPS" | 1706 options.deps_file = "DEPS" |
1702 | 1707 |
| 1708 # These are overridded when testing. They are not externally visible. |
| 1709 options.stdout = sys.stdout |
1703 options.path_exists = os.path.exists | 1710 options.path_exists = os.path.exists |
1704 options.gclient = GClient | 1711 options.gclient = GClient |
1705 options.scm_wrapper = SCMWrapper | 1712 options.scm_wrapper = SCMWrapper |
1706 options.platform = sys.platform | 1713 options.platform = sys.platform |
1707 return DispatchCommand(command, options, args) | 1714 return DispatchCommand(command, options, args) |
1708 | 1715 |
1709 | 1716 |
1710 if "__main__" == __name__: | 1717 if "__main__" == __name__: |
1711 try: | 1718 try: |
1712 result = Main(sys.argv) | 1719 result = Main(sys.argv) |
1713 except Error, e: | 1720 except Error, e: |
1714 print >> sys.stderr, "Error: %s" % str(e) | 1721 print "Error: %s" % str(e) |
1715 result = 1 | 1722 result = 1 |
1716 sys.exit(result) | 1723 sys.exit(result) |
1717 | 1724 |
1718 # vim: ts=2:sw=2:tw=80:et: | 1725 # vim: ts=2:sw=2:tw=80:et: |
OLD | NEW |