| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2010 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 | 5 |
| 6 """A wrapper script to manage a set of client modules in different SCM. | 6 """A wrapper script to manage a set of client modules in different SCM. |
| 7 | 7 |
| 8 This script is intended to be used to help basic management of client | 8 This script is intended to be used to help basic management of client |
| 9 program sources residing in one or more Subversion modules and Git | 9 program sources residing in one or more Subversion modules and Git |
| 10 repositories, along with other modules it depends on, also in Subversion or Git, | 10 repositories, along with other modules it depends on, also in Subversion or Git, |
| (...skipping 485 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 496 return | 496 return |
| 497 | 497 |
| 498 # Run hooks on the basis of whether the files from the gclient operation | 498 # Run hooks on the basis of whether the files from the gclient operation |
| 499 # match each hook's pattern. | 499 # match each hook's pattern. |
| 500 for hook_dict in hooks: | 500 for hook_dict in hooks: |
| 501 pattern = re.compile(hook_dict['pattern']) | 501 pattern = re.compile(hook_dict['pattern']) |
| 502 matching_file_list = [f for f in file_list if pattern.search(f)] | 502 matching_file_list = [f for f in file_list if pattern.search(f)] |
| 503 if matching_file_list: | 503 if matching_file_list: |
| 504 self._RunHookAction(hook_dict, matching_file_list) | 504 self._RunHookAction(hook_dict, matching_file_list) |
| 505 | 505 |
| 506 def _EnforceRevisions(self, solutions): |
| 507 """Checks for revision overrides.""" |
| 508 revision_overrides = {} |
| 509 solutions = [s['name'] for s in solutions] |
| 510 if self._options.revisions: |
| 511 revision = self._options.revisions[0] |
| 512 # Ignore solution@ |
| 513 rev = revision |
| 514 if '@' in revision: |
| 515 rev = revision.split('@', 1)[1] |
| 516 revision_overrides[solutions[0]] = rev |
| 517 |
| 518 if len(self._options.revisions) > 1: |
| 519 # Enforce solution@rev format for following params. |
| 520 for revision in self._options.revisions[1:]: |
| 521 if not '@' in revision: |
| 522 raise gclient_utils.Error( |
| 523 'Specify the full dependency when specifying a revision number ' |
| 524 'for non-primary solution.') |
| 525 sol, rev = revision.split("@", 1) |
| 526 # Disallow conflicting revs |
| 527 if revision_overrides.get(sol, rev) != rev: |
| 528 raise gclient_utils.Error( |
| 529 'Conflicting revision numbers specified for %s: %s and %s.' % |
| 530 (sol, revision_overrides[sol], rev)) |
| 531 if not sol in solutions: |
| 532 raise gclient_utils.Error('%s is not a valid solution.' % sol) |
| 533 revision_overrides[sol] = rev |
| 534 return revision_overrides |
| 535 |
| 506 def RunOnDeps(self, command, args): | 536 def RunOnDeps(self, command, args): |
| 507 """Runs a command on each dependency in a client and its dependencies. | 537 """Runs a command on each dependency in a client and its dependencies. |
| 508 | 538 |
| 509 The module's dependencies are specified in its top-level DEPS files. | 539 The module's dependencies are specified in its top-level DEPS files. |
| 510 | 540 |
| 511 Args: | 541 Args: |
| 512 command: The command to use (e.g., 'status' or 'diff') | 542 command: The command to use (e.g., 'status' or 'diff') |
| 513 args: list of str - extra arguments to add to the command line. | 543 args: list of str - extra arguments to add to the command line. |
| 514 | 544 |
| 515 Raises: | 545 Raises: |
| 516 Error: If the client has conflicting entries. | 546 Error: If the client has conflicting entries. |
| 517 """ | 547 """ |
| 518 if not command in self.supported_commands: | 548 if not command in self.supported_commands: |
| 519 raise gclient_utils.Error("'%s' is an unsupported command" % command) | 549 raise gclient_utils.Error("'%s' is an unsupported command" % command) |
| 520 | 550 |
| 521 # Check for revision overrides. | |
| 522 revision_overrides = {} | |
| 523 for revision in self._options.revisions: | |
| 524 if revision.find("@") == -1: | |
| 525 raise gclient_utils.Error( | |
| 526 "Specify the full dependency when specifying a revision number.") | |
| 527 revision_elem = revision.split("@") | |
| 528 # Disallow conflicting revs | |
| 529 if revision_overrides.has_key(revision_elem[0]) and \ | |
| 530 revision_overrides[revision_elem[0]] != revision_elem[1]: | |
| 531 raise gclient_utils.Error( | |
| 532 "Conflicting revision numbers specified.") | |
| 533 revision_overrides[revision_elem[0]] = revision_elem[1] | |
| 534 | |
| 535 solutions = self.GetVar("solutions") | 551 solutions = self.GetVar("solutions") |
| 536 if not solutions: | 552 if not solutions: |
| 537 raise gclient_utils.Error("No solution specified") | 553 raise gclient_utils.Error("No solution specified") |
| 554 revision_overrides = self._EnforceRevisions(solutions) |
| 538 | 555 |
| 539 # When running runhooks --force, there's no need to consult the SCM. | 556 # When running runhooks --force, there's no need to consult the SCM. |
| 540 # All known hooks are expected to run unconditionally regardless of working | 557 # All known hooks are expected to run unconditionally regardless of working |
| 541 # copy state, so skip the SCM status check. | 558 # copy state, so skip the SCM status check. |
| 542 run_scm = not (command == 'runhooks' and self._options.force) | 559 run_scm = not (command == 'runhooks' and self._options.force) |
| 543 | 560 |
| 544 entries = {} | 561 entries = {} |
| 545 entries_deps_content = {} | 562 entries_deps_content = {} |
| 546 file_list = [] | 563 file_list = [] |
| 547 # Run on the base solutions first. | 564 # Run on the base solutions first. |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 672 print("\n________ deleting \'%s\' " + | 689 print("\n________ deleting \'%s\' " + |
| 673 "in \'%s\'") % (entry_fixed, self._root_dir) | 690 "in \'%s\'") % (entry_fixed, self._root_dir) |
| 674 gclient_utils.RemoveDirectory(e_dir) | 691 gclient_utils.RemoveDirectory(e_dir) |
| 675 # record the current list of entries for next time | 692 # record the current list of entries for next time |
| 676 self._SaveEntries(entries) | 693 self._SaveEntries(entries) |
| 677 | 694 |
| 678 def PrintRevInfo(self): | 695 def PrintRevInfo(self): |
| 679 """Output revision info mapping for the client and its dependencies. | 696 """Output revision info mapping for the client and its dependencies. |
| 680 | 697 |
| 681 This allows the capture of an overall "revision" for the source tree that | 698 This allows the capture of an overall "revision" for the source tree that |
| 682 can be used to reproduce the same tree in the future. The actual output | 699 can be used to reproduce the same tree in the future. It is only useful for |
| 683 contains enough information (source paths, svn server urls and revisions) | 700 "unpinned dependencies", i.e. DEPS/deps references without a svn revision |
| 684 that it can be used either to generate external svn/git commands (without | 701 number or a git hash. A git branch name isn't "pinned" since the actual |
| 685 gclient) or as input to gclient's --rev option (with some massaging of | 702 commit can change. |
| 686 the data). | |
| 687 | |
| 688 Since we care about the revision of the current source tree, for git | |
| 689 repositories this command uses the revision of the HEAD. For subversion we | |
| 690 use BASE. | |
| 691 | 703 |
| 692 The --snapshot option allows creating a .gclient file to reproduce the tree. | 704 The --snapshot option allows creating a .gclient file to reproduce the tree. |
| 693 | |
| 694 Raises: | |
| 695 Error: If the client has conflicting entries. | |
| 696 """ | 705 """ |
| 697 # Check for revision overrides. | |
| 698 revision_overrides = {} | |
| 699 for revision in self._options.revisions: | |
| 700 if revision.find("@") < 0: | |
| 701 raise gclient_utils.Error( | |
| 702 "Specify the full dependency when specifying a revision number.") | |
| 703 revision_elem = revision.split("@") | |
| 704 # Disallow conflicting revs | |
| 705 if revision_overrides.has_key(revision_elem[0]) and \ | |
| 706 revision_overrides[revision_elem[0]] != revision_elem[1]: | |
| 707 raise gclient_utils.Error( | |
| 708 "Conflicting revision numbers specified.") | |
| 709 revision_overrides[revision_elem[0]] = revision_elem[1] | |
| 710 | |
| 711 solutions = self.GetVar("solutions") | 706 solutions = self.GetVar("solutions") |
| 712 if not solutions: | 707 if not solutions: |
| 713 raise gclient_utils.Error("No solution specified") | 708 raise gclient_utils.Error("No solution specified") |
| 714 | 709 |
| 715 # Inner helper to generate base url and rev tuple | 710 # Inner helper to generate base url and rev tuple |
| 716 def GetURLAndRev(name, original_url): | 711 def GetURLAndRev(name, original_url): |
| 717 url, _ = gclient_utils.SplitUrlRevision(original_url) | 712 url, _ = gclient_utils.SplitUrlRevision(original_url) |
| 718 scm = gclient_scm.CreateSCM(original_url, self._root_dir, name) | 713 scm = gclient_scm.CreateSCM(original_url, self._root_dir, name) |
| 719 return (url, scm.revinfo(self._options, [], None)) | 714 return (url, scm.revinfo(self._options, [], None)) |
| 720 | 715 |
| (...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 926 update src directory to r31000 | 921 update src directory to r31000 |
| 927 """) | 922 """) |
| 928 def CMDsync(parser, args): | 923 def CMDsync(parser, args): |
| 929 """Checkout/update all modules.""" | 924 """Checkout/update all modules.""" |
| 930 parser.add_option("--force", action="store_true", | 925 parser.add_option("--force", action="store_true", |
| 931 help="force update even for unchanged modules") | 926 help="force update even for unchanged modules") |
| 932 parser.add_option("--nohooks", action="store_true", | 927 parser.add_option("--nohooks", action="store_true", |
| 933 help="don't run hooks after the update is complete") | 928 help="don't run hooks after the update is complete") |
| 934 parser.add_option("-r", "--revision", action="append", | 929 parser.add_option("-r", "--revision", action="append", |
| 935 dest="revisions", metavar="REV", default=[], | 930 dest="revisions", metavar="REV", default=[], |
| 936 help="update given solution to specified revision, " | 931 help="Enforces revision/hash for the primary solution. " |
| 937 "can be used multiple times for each solution, " | 932 "To modify a secondary solution, use it at least once " |
| 938 "e.g. -r src@123, -r internal@32") | 933 "for the primary solution and then use the format " |
| 934 "solution@rev/hash, e.g. -r src@123") |
| 939 parser.add_option("--head", action="store_true", | 935 parser.add_option("--head", action="store_true", |
| 940 help="skips any safesync_urls specified in " | 936 help="skips any safesync_urls specified in " |
| 941 "configured solutions and sync to head instead") | 937 "configured solutions and sync to head instead") |
| 942 parser.add_option("--delete_unversioned_trees", action="store_true", | 938 parser.add_option("--delete_unversioned_trees", action="store_true", |
| 943 help="delete any unexpected unversioned trees " | 939 help="delete any unexpected unversioned trees " |
| 944 "that are in the checkout") | 940 "that are in the checkout") |
| 945 parser.add_option("--reset", action="store_true", | 941 parser.add_option("--reset", action="store_true", |
| 946 help="resets any local changes before updating (git only)") | 942 help="resets any local changes before updating (git only)") |
| 947 parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", | 943 parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", |
| 948 help="sync deps for the specified (comma-separated) " | 944 help="sync deps for the specified (comma-separated) " |
| 949 "platform(s); 'all' will sync all platforms") | 945 "platform(s); 'all' will sync all platforms") |
| 950 parser.add_option("--manually_grab_svn_rev", action="store_true", | 946 parser.add_option("--manually_grab_svn_rev", action="store_true", |
| 951 help="Skip svn up whenever possible by requesting " | 947 help="Skip svn up whenever possible by requesting " |
| 952 "actual HEAD revision from the repository") | 948 "actual HEAD revision from the repository") |
| 953 (options, args) = parser.parse_args(args) | 949 (options, args) = parser.parse_args(args) |
| 954 client = GClient.LoadCurrentConfig(options) | 950 client = GClient.LoadCurrentConfig(options) |
| 955 | 951 |
| 956 if not client: | 952 if not client: |
| 957 raise gclient_utils.Error("client not configured; see 'gclient config'") | 953 raise gclient_utils.Error("client not configured; see 'gclient config'") |
| 958 | 954 |
| 959 if not options.head: | 955 if not options.head: |
| 960 solutions = client.GetVar('solutions') | 956 solutions = client.GetVar('solutions') |
| 961 if solutions: | 957 if solutions: |
| 958 first = True |
| 962 for s in solutions: | 959 for s in solutions: |
| 963 if s.get('safesync_url', ''): | 960 if s.get('safesync_url', None): |
| 964 # rip through revisions and make sure we're not over-riding | 961 # rip through revisions and make sure we're not over-riding |
| 965 # something that was explicitly passed | 962 # something that was explicitly passed |
| 966 has_key = False | 963 has_key = False |
| 964 r_first = True |
| 967 for r in options.revisions: | 965 for r in options.revisions: |
| 968 if r.split('@')[0] == s['name']: | 966 if (first and r_first) or r.split('@', 1)[0] == s['name']: |
| 969 has_key = True | 967 has_key = True |
| 970 break | 968 break |
| 969 r_first = False |
| 971 | 970 |
| 972 if not has_key: | 971 if not has_key: |
| 973 handle = urllib.urlopen(s['safesync_url']) | 972 handle = urllib.urlopen(s['safesync_url']) |
| 974 rev = handle.read().strip() | 973 rev = handle.read().strip() |
| 975 handle.close() | 974 handle.close() |
| 976 if len(rev): | 975 if len(rev): |
| 977 options.revisions.append(s['name']+'@'+rev) | 976 options.revisions.append(s['name']+'@'+rev) |
| 977 first = False |
| 978 | 978 |
| 979 if options.verbose: | 979 if options.verbose: |
| 980 # Print out the .gclient file. This is longer than if we just printed the | 980 # Print out the .gclient file. This is longer than if we just printed the |
| 981 # client dict, but more legible, and it might contain helpful comments. | 981 # client dict, but more legible, and it might contain helpful comments. |
| 982 print(client.ConfigContent()) | 982 print(client.ConfigContent()) |
| 983 return client.RunOnDeps('update', args) | 983 return client.RunOnDeps('update', args) |
| 984 | 984 |
| 985 | 985 |
| 986 def CMDupdate(parser, args): | 986 def CMDupdate(parser, args): |
| 987 """Alias for the sync command. Deprecated.""" | 987 """Alias for the sync command. Deprecated.""" |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1116 | 1116 |
| 1117 | 1117 |
| 1118 if "__main__" == __name__: | 1118 if "__main__" == __name__: |
| 1119 try: | 1119 try: |
| 1120 sys.exit(Main(sys.argv[1:])) | 1120 sys.exit(Main(sys.argv[1:])) |
| 1121 except gclient_utils.Error, e: | 1121 except gclient_utils.Error, e: |
| 1122 print >> sys.stderr, "Error: %s" % str(e) | 1122 print >> sys.stderr, "Error: %s" % str(e) |
| 1123 sys.exit(1) | 1123 sys.exit(1) |
| 1124 | 1124 |
| 1125 # vim: ts=2:sw=2:tw=80:et: | 1125 # vim: ts=2:sw=2:tw=80:et: |
| OLD | NEW |