Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 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 | 5 |
| 6 """Meta checkout manager supporting both Subversion and GIT. | 6 """Meta checkout manager supporting both Subversion and GIT. |
| 7 | 7 |
| 8 Files | 8 Files |
| 9 .gclient : Current client configuration, written by 'config' command. | 9 .gclient : Current client configuration, written by 'config' command. |
| 10 Format is a Python script defining 'solutions', a list whose | 10 Format is a Python script defining 'solutions', a list whose |
| (...skipping 507 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 518 # pylint: disable=W0221 | 518 # pylint: disable=W0221 |
| 519 def run(self, revision_overrides, command, args, work_queue, options): | 519 def run(self, revision_overrides, command, args, work_queue, options): |
| 520 """Runs |command| then parse the DEPS file.""" | 520 """Runs |command| then parse the DEPS file.""" |
| 521 logging.info('Dependency(%s).run()' % self.name) | 521 logging.info('Dependency(%s).run()' % self.name) |
| 522 assert self._file_list == [] | 522 assert self._file_list == [] |
| 523 if not self.should_process: | 523 if not self.should_process: |
| 524 return | 524 return |
| 525 # When running runhooks, there's no need to consult the SCM. | 525 # When running runhooks, there's no need to consult the SCM. |
| 526 # All known hooks are expected to run unconditionally regardless of working | 526 # All known hooks are expected to run unconditionally regardless of working |
| 527 # copy state, so skip the SCM status check. | 527 # copy state, so skip the SCM status check. |
| 528 run_scm = command not in ('runhooks', None) | 528 run_scm = command not in ('runhooks', 'recurse', None) |
|
davidbarr
2012/03/02 03:21:55
It felt a bit too tricky to implicitly rely on thi
| |
| 529 parsed_url = self.LateOverride(self.url) | 529 parsed_url = self.LateOverride(self.url) |
| 530 file_list = [] | 530 file_list = [] |
| 531 if run_scm and parsed_url: | 531 if run_scm and parsed_url: |
| 532 if isinstance(parsed_url, self.FileImpl): | 532 if isinstance(parsed_url, self.FileImpl): |
| 533 # Special support for single-file checkout. | 533 # Special support for single-file checkout. |
| 534 if not command in (None, 'cleanup', 'diff', 'pack', 'status'): | 534 if not command in (None, 'cleanup', 'diff', 'pack', 'status'): |
| 535 # Sadly, pylint doesn't realize that parsed_url is of FileImpl. | 535 # Sadly, pylint doesn't realize that parsed_url is of FileImpl. |
| 536 # pylint: disable=E1103 | 536 # pylint: disable=E1103 |
| 537 options.revision = parsed_url.GetRevision() | 537 options.revision = parsed_url.GetRevision() |
| 538 scm = gclient_scm.SVNWrapper(parsed_url.GetPath(), | 538 scm = gclient_scm.SVNWrapper(parsed_url.GetPath(), |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 558 for i in range(len(file_list)): | 558 for i in range(len(file_list)): |
| 559 # It depends on the command being executed (like runhooks vs sync). | 559 # It depends on the command being executed (like runhooks vs sync). |
| 560 if not os.path.isabs(file_list[i]): | 560 if not os.path.isabs(file_list[i]): |
| 561 continue | 561 continue |
| 562 prefix = os.path.commonprefix( | 562 prefix = os.path.commonprefix( |
| 563 [self.root.root_dir.lower(), file_list[i].lower()]) | 563 [self.root.root_dir.lower(), file_list[i].lower()]) |
| 564 file_list[i] = file_list[i][len(prefix):] | 564 file_list[i] = file_list[i][len(prefix):] |
| 565 # Strip any leading path separators. | 565 # Strip any leading path separators. |
| 566 while file_list[i].startswith(('\\', '/')): | 566 while file_list[i].startswith(('\\', '/')): |
| 567 file_list[i] = file_list[i][1:] | 567 file_list[i] = file_list[i][1:] |
| 568 elif command is 'recurse': | |
| 569 if not isinstance(parsed_url, self.FileImpl): | |
| 570 # Skip file only checkout. | |
| 571 scm = gclient_scm.GetScmName(parsed_url) | |
| 572 if not options.scm or scm in options.scm: | |
| 573 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name)) | |
| 574 # Pass in the SCM type as an env variable | |
| 575 env = os.environ.copy() | |
| 576 if scm: | |
| 577 env['GCLIENT_SCM'] = scm | |
| 578 if parsed_url: | |
| 579 env['GCLIENT_URL'] = parsed_url | |
| 580 if os.path.isdir(cwd): | |
| 581 gclient_utils.CheckCallAndFilterAndHeader(args, cwd=cwd, env=env) | |
| 582 else: | |
| 583 print >> sys.stderr, 'Skipped missing %s' % cwd | |
| 568 | 584 |
| 569 # Always parse the DEPS file. | 585 # Always parse the DEPS file. |
| 570 self.ParseDepsFile() | 586 self.ParseDepsFile() |
| 571 | 587 |
| 572 self._run_is_done(file_list, parsed_url) | 588 self._run_is_done(file_list, parsed_url) |
| 573 | 589 |
| 574 if self.recursion_limit: | 590 if self.recursion_limit: |
| 575 # Parse the dependencies of this dependency. | 591 # Parse the dependencies of this dependency. |
| 576 for s in self.dependencies: | 592 for s in self.dependencies: |
| 577 work_queue.enqueue(s) | 593 work_queue.enqueue(s) |
| (...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 958 | 974 |
| 959 def RunOnDeps(self, command, args): | 975 def RunOnDeps(self, command, args): |
| 960 """Runs a command on each dependency in a client and its dependencies. | 976 """Runs a command on each dependency in a client and its dependencies. |
| 961 | 977 |
| 962 Args: | 978 Args: |
| 963 command: The command to use (e.g., 'status' or 'diff') | 979 command: The command to use (e.g., 'status' or 'diff') |
| 964 args: list of str - extra arguments to add to the command line. | 980 args: list of str - extra arguments to add to the command line. |
| 965 """ | 981 """ |
| 966 if not self.dependencies: | 982 if not self.dependencies: |
| 967 raise gclient_utils.Error('No solution specified') | 983 raise gclient_utils.Error('No solution specified') |
| 968 revision_overrides = self._EnforceRevisions() | 984 revision_overrides = {} |
| 985 if command is not 'recurse': | |
| 986 revision_overrides = self._EnforceRevisions() | |
|
davidbarr
2012/03/02 03:21:55
Is _EnforceRevisions() necessary in the 'recurse'
M-A Ruel
2012/03/02 14:25:30
Just add a comment that it's not strictly necessar
| |
| 969 pm = None | 987 pm = None |
| 970 # Disable progress for non-tty stdout. | 988 # Disable progress for non-tty stdout. |
| 971 if (command in ('update', 'revert') and sys.stdout.isatty() and not | 989 if (sys.stdout.isatty() and not self._options.verbose): |
| 972 self._options.verbose): | 990 if command in ('update', 'revert'): |
| 973 pm = Progress('Syncing projects', 1) | 991 pm = Progress('Syncing projects', 1) |
| 992 elif command is 'recurse': | |
| 993 pm = Progress(' '.join(args), 1) | |
| 974 work_queue = gclient_utils.ExecutionQueue(self._options.jobs, pm) | 994 work_queue = gclient_utils.ExecutionQueue(self._options.jobs, pm) |
| 975 for s in self.dependencies: | 995 for s in self.dependencies: |
| 976 work_queue.enqueue(s) | 996 work_queue.enqueue(s) |
| 977 work_queue.flush(revision_overrides, command, args, options=self._options) | 997 work_queue.flush(revision_overrides, command, args, options=self._options) |
| 978 | 998 |
| 979 # Once all the dependencies have been processed, it's now safe to run the | 999 # Once all the dependencies have been processed, it's now safe to run the |
| 980 # hooks. | 1000 # hooks. |
| 981 if not self._options.nohooks: | 1001 if not self._options.nohooks: |
| 982 self.RunHooksRecursively(self._options) | 1002 self.RunHooksRecursively(self._options) |
| 983 | 1003 |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1124 @attr('usage', '[command] [args ...]') | 1144 @attr('usage', '[command] [args ...]') |
| 1125 def CMDrecurse(parser, args): | 1145 def CMDrecurse(parser, args): |
| 1126 """Operates on all the entries. | 1146 """Operates on all the entries. |
| 1127 | 1147 |
| 1128 Runs a shell command on all entries. | 1148 Runs a shell command on all entries. |
| 1129 """ | 1149 """ |
| 1130 # Stop parsing at the first non-arg so that these go through to the command | 1150 # Stop parsing at the first non-arg so that these go through to the command |
| 1131 parser.disable_interspersed_args() | 1151 parser.disable_interspersed_args() |
| 1132 parser.add_option('-s', '--scm', action='append', default=[], | 1152 parser.add_option('-s', '--scm', action='append', default=[], |
| 1133 help='choose scm types to operate upon') | 1153 help='choose scm types to operate upon') |
| 1134 parser.remove_option('--jobs') | |
| 1135 options, args = parser.parse_args(args) | 1154 options, args = parser.parse_args(args) |
| 1136 if not args: | 1155 if not args: |
| 1137 print >> sys.stderr, 'Need to supply a command!' | 1156 print >> sys.stderr, 'Need to supply a command!' |
| 1138 return 1 | 1157 return 1 |
| 1139 root_and_entries = gclient_utils.GetGClientRootAndEntries() | 1158 root_and_entries = gclient_utils.GetGClientRootAndEntries() |
| 1140 if not root_and_entries: | 1159 if not root_and_entries: |
| 1141 print >> sys.stderr, ( | 1160 print >> sys.stderr, ( |
| 1142 'You need to run gclient sync at least once to use \'recurse\'.\n' | 1161 'You need to run gclient sync at least once to use \'recurse\'.\n' |
| 1143 'This is because .gclient_entries needs to exist and be up to date.') | 1162 'This is because .gclient_entries needs to exist and be up to date.') |
| 1144 return 1 | 1163 return 1 |
| 1145 root, entries = root_and_entries | 1164 |
| 1165 # Normalize options.scm to a set() | |
| 1146 scm_set = set() | 1166 scm_set = set() |
| 1147 for scm in options.scm: | 1167 for scm in options.scm: |
| 1148 scm_set.update(scm.split(',')) | 1168 scm_set.update(scm.split(',')) |
| 1169 options.scm = scm_set | |
| 1149 | 1170 |
| 1150 # Pass in the SCM type as an env variable | 1171 options.nohooks = True |
| 1151 env = os.environ.copy() | 1172 client = GClient.LoadCurrentConfig(options) |
| 1152 | 1173 return client.RunOnDeps('recurse', args) |
| 1153 for path, url in entries.iteritems(): | |
| 1154 scm = gclient_scm.GetScmName(url) | |
| 1155 if scm_set and scm not in scm_set: | |
| 1156 continue | |
| 1157 cwd = os.path.normpath(os.path.join(root, path)) | |
| 1158 if scm: | |
| 1159 env['GCLIENT_SCM'] = scm | |
| 1160 if url: | |
| 1161 env['GCLIENT_URL'] = url | |
| 1162 if os.path.isdir(cwd): | |
| 1163 subprocess2.call(args, cwd=cwd, env=env) | |
| 1164 else: | |
| 1165 print >> sys.stderr, 'Skipped missing %s' % cwd | |
| 1166 return 0 | |
| 1167 | 1174 |
| 1168 | 1175 |
| 1169 @attr('usage', '[args ...]') | 1176 @attr('usage', '[args ...]') |
| 1170 def CMDfetch(parser, args): | 1177 def CMDfetch(parser, args): |
| 1171 """Fetches upstream commits for all modules. | 1178 """Fetches upstream commits for all modules. |
| 1172 | 1179 |
| 1173 Completely git-specific. Simply runs 'git fetch [args ...]' for each module. | 1180 Completely git-specific. Simply runs 'git fetch [args ...]' for each module. |
| 1174 """ | 1181 """ |
| 1175 (_, args) = parser.parse_args(args) | 1182 (options, args) = parser.parse_args(args) |
| 1176 args = ['-s', 'git', 'git', 'fetch'] + args | 1183 args = ['-j%d' % options.jobs, '-s', 'git', 'git', 'fetch'] + args |
| 1177 return CMDrecurse(parser, args) | 1184 return CMDrecurse(parser, args) |
| 1178 | 1185 |
| 1179 | 1186 |
| 1180 @attr('usage', '[url] [safesync url]') | 1187 @attr('usage', '[url] [safesync url]') |
| 1181 def CMDconfig(parser, args): | 1188 def CMDconfig(parser, args): |
| 1182 """Create a .gclient file in the current directory. | 1189 """Create a .gclient file in the current directory. |
| 1183 | 1190 |
| 1184 This specifies the configuration for further commands. After update/sync, | 1191 This specifies the configuration for further commands. After update/sync, |
| 1185 top-level DEPS files in each module are read to determine dependent | 1192 top-level DEPS files in each module are read to determine dependent |
| 1186 modules to operate on as well. If optional [url] parameter is | 1193 modules to operate on as well. If optional [url] parameter is |
| (...skipping 362 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1549 except (gclient_utils.Error, subprocess2.CalledProcessError), e: | 1556 except (gclient_utils.Error, subprocess2.CalledProcessError), e: |
| 1550 print >> sys.stderr, 'Error: %s' % str(e) | 1557 print >> sys.stderr, 'Error: %s' % str(e) |
| 1551 return 1 | 1558 return 1 |
| 1552 | 1559 |
| 1553 | 1560 |
| 1554 if '__main__' == __name__: | 1561 if '__main__' == __name__: |
| 1555 fix_encoding.fix_encoding() | 1562 fix_encoding.fix_encoding() |
| 1556 sys.exit(Main(sys.argv[1:])) | 1563 sys.exit(Main(sys.argv[1:])) |
| 1557 | 1564 |
| 1558 # vim: ts=2:sw=2:tw=80:et: | 1565 # vim: ts=2:sw=2:tw=80:et: |
| OLD | NEW |