| 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 600 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 611 for i in range(len(file_list)): | 611 for i in range(len(file_list)): |
| 612 # It depends on the command being executed (like runhooks vs sync). | 612 # It depends on the command being executed (like runhooks vs sync). |
| 613 if not os.path.isabs(file_list[i]): | 613 if not os.path.isabs(file_list[i]): |
| 614 continue | 614 continue |
| 615 prefix = os.path.commonprefix( | 615 prefix = os.path.commonprefix( |
| 616 [self.root.root_dir.lower(), file_list[i].lower()]) | 616 [self.root.root_dir.lower(), file_list[i].lower()]) |
| 617 file_list[i] = file_list[i][len(prefix):] | 617 file_list[i] = file_list[i][len(prefix):] |
| 618 # Strip any leading path separators. | 618 # Strip any leading path separators. |
| 619 while file_list[i].startswith(('\\', '/')): | 619 while file_list[i].startswith(('\\', '/')): |
| 620 file_list[i] = file_list[i][1:] | 620 file_list[i] = file_list[i][1:] |
| 621 elif command == 'recurse': | 621 |
| 622 # Always parse the DEPS file. |
| 623 self.ParseDepsFile() |
| 624 |
| 625 self._run_is_done(file_list, parsed_url) |
| 626 |
| 627 if self.recursion_limit: |
| 628 # Parse the dependencies of this dependency. |
| 629 for s in self.dependencies: |
| 630 work_queue.enqueue(s) |
| 631 |
| 632 if command == 'recurse': |
| 622 if not isinstance(parsed_url, self.FileImpl): | 633 if not isinstance(parsed_url, self.FileImpl): |
| 623 # Skip file only checkout. | 634 # Skip file only checkout. |
| 624 scm = gclient_scm.GetScmName(parsed_url) | 635 scm = gclient_scm.GetScmName(parsed_url) |
| 625 if not options.scm or scm in options.scm: | 636 if not options.scm or scm in options.scm: |
| 626 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name)) | 637 cwd = os.path.normpath(os.path.join(self.root.root_dir, self.name)) |
| 627 # Pass in the SCM type as an env variable | 638 # Pass in the SCM type as an env variable |
| 628 env = os.environ.copy() | 639 env = os.environ.copy() |
| 629 if scm: | 640 if scm: |
| 630 env['GCLIENT_SCM'] = scm | 641 env['GCLIENT_SCM'] = scm |
| 631 if parsed_url: | 642 if parsed_url: |
| 632 env['GCLIENT_URL'] = parsed_url | 643 env['GCLIENT_URL'] = parsed_url |
| 644 if options.prepend_dir: |
| 645 print_stdout = False |
| 646 def filter_fn(line): |
| 647 items = line.split('\0') |
| 648 if len(items) == 1: |
| 649 match = re.match('Binary file (.*) matches$', line) |
| 650 if match: |
| 651 print 'Binary file %s matches' % os.path.join( |
| 652 self.name, match.group(1)) |
| 653 else: |
| 654 print line |
| 655 elif len(items) == 2 and items[1]: |
| 656 print '%s : %s' % (os.path.join(self.name, items[0]), items[1]) |
| 657 else: |
| 658 # Multiple null bytes or a single trailing null byte indicate |
| 659 # git is likely displaying filenames only (such as with -l) |
| 660 print '\n'.join(os.path.join(self.name, f) for f in items if f) |
| 661 else: |
| 662 print_stdout = True |
| 663 filter_fn = None |
| 664 |
| 633 if os.path.isdir(cwd): | 665 if os.path.isdir(cwd): |
| 634 try: | 666 try: |
| 635 gclient_utils.CheckCallAndFilter( | 667 gclient_utils.CheckCallAndFilter( |
| 636 args, cwd=cwd, env=env, print_stdout=True) | 668 args, cwd=cwd, env=env, print_stdout=print_stdout, |
| 669 filter_fn=filter_fn, |
| 670 ) |
| 637 except subprocess2.CalledProcessError: | 671 except subprocess2.CalledProcessError: |
| 638 if not options.ignore: | 672 if not options.ignore: |
| 639 raise | 673 raise |
| 640 else: | 674 else: |
| 641 print >> sys.stderr, 'Skipped missing %s' % cwd | 675 print >> sys.stderr, 'Skipped missing %s' % cwd |
| 642 | 676 |
| 643 # Always parse the DEPS file. | |
| 644 self.ParseDepsFile() | |
| 645 | |
| 646 self._run_is_done(file_list, parsed_url) | |
| 647 | |
| 648 if self.recursion_limit: | |
| 649 # Parse the dependencies of this dependency. | |
| 650 for s in self.dependencies: | |
| 651 work_queue.enqueue(s) | |
| 652 | 677 |
| 653 @gclient_utils.lockedmethod | 678 @gclient_utils.lockedmethod |
| 654 def _run_is_done(self, file_list, parsed_url): | 679 def _run_is_done(self, file_list, parsed_url): |
| 655 # Both these are kept for hooks that are run as a separate tree traversal. | 680 # Both these are kept for hooks that are run as a separate tree traversal. |
| 656 self._file_list = file_list | 681 self._file_list = file_list |
| 657 self._parsed_url = parsed_url | 682 self._parsed_url = parsed_url |
| 658 self._processed = True | 683 self._processed = True |
| 659 | 684 |
| 660 @staticmethod | 685 @staticmethod |
| 661 def GetHookAction(hook_dict, matching_file_list): | 686 def GetHookAction(hook_dict, matching_file_list): |
| (...skipping 381 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1043 raise gclient_utils.Error( | 1068 raise gclient_utils.Error( |
| 1044 'It appears your safesync_url (%s) is not working properly\n' | 1069 'It appears your safesync_url (%s) is not working properly\n' |
| 1045 '(as it returned an empty response). Check your config.' % | 1070 '(as it returned an empty response). Check your config.' % |
| 1046 dep.safesync_url) | 1071 dep.safesync_url) |
| 1047 scm = gclient_scm.CreateSCM(dep.url, dep.root.root_dir, dep.name) | 1072 scm = gclient_scm.CreateSCM(dep.url, dep.root.root_dir, dep.name) |
| 1048 safe_rev = scm.GetUsableRev(rev=rev, options=self._options) | 1073 safe_rev = scm.GetUsableRev(rev=rev, options=self._options) |
| 1049 if self._options.verbose: | 1074 if self._options.verbose: |
| 1050 print('Using safesync_url revision: %s.\n' % safe_rev) | 1075 print('Using safesync_url revision: %s.\n' % safe_rev) |
| 1051 self._options.revisions.append('%s@%s' % (dep.name, safe_rev)) | 1076 self._options.revisions.append('%s@%s' % (dep.name, safe_rev)) |
| 1052 | 1077 |
| 1053 def RunOnDeps(self, command, args): | 1078 def RunOnDeps(self, command, args, ignore_requirements=False, progress=True): |
| 1054 """Runs a command on each dependency in a client and its dependencies. | 1079 """Runs a command on each dependency in a client and its dependencies. |
| 1055 | 1080 |
| 1056 Args: | 1081 Args: |
| 1057 command: The command to use (e.g., 'status' or 'diff') | 1082 command: The command to use (e.g., 'status' or 'diff') |
| 1058 args: list of str - extra arguments to add to the command line. | 1083 args: list of str - extra arguments to add to the command line. |
| 1059 """ | 1084 """ |
| 1060 if not self.dependencies: | 1085 if not self.dependencies: |
| 1061 raise gclient_utils.Error('No solution specified') | 1086 raise gclient_utils.Error('No solution specified') |
| 1062 revision_overrides = {} | 1087 revision_overrides = {} |
| 1063 # It's unnecessary to check for revision overrides for 'recurse'. | 1088 # It's unnecessary to check for revision overrides for 'recurse'. |
| 1064 # Save a few seconds by not calling _EnforceRevisions() in that case. | 1089 # Save a few seconds by not calling _EnforceRevisions() in that case. |
| 1065 if command not in ('diff', 'recurse', 'runhooks', 'status'): | 1090 if command not in ('diff', 'recurse', 'runhooks', 'status'): |
| 1066 revision_overrides = self._EnforceRevisions() | 1091 revision_overrides = self._EnforceRevisions() |
| 1067 pm = None | 1092 pm = None |
| 1068 # Disable progress for non-tty stdout. | 1093 # Disable progress for non-tty stdout. |
| 1069 if (sys.stdout.isatty() and not self._options.verbose): | 1094 if (sys.stdout.isatty() and not self._options.verbose and progress): |
| 1070 if command in ('update', 'revert'): | 1095 if command in ('update', 'revert'): |
| 1071 pm = Progress('Syncing projects', 1) | 1096 pm = Progress('Syncing projects', 1) |
| 1072 elif command == 'recurse': | 1097 elif command == 'recurse': |
| 1073 pm = Progress(' '.join(args), 1) | 1098 pm = Progress(' '.join(args), 1) |
| 1074 work_queue = gclient_utils.ExecutionQueue(self._options.jobs, pm) | 1099 work_queue = gclient_utils.ExecutionQueue( |
| 1100 self._options.jobs, pm, ignore_requirements=ignore_requirements) |
| 1075 for s in self.dependencies: | 1101 for s in self.dependencies: |
| 1076 work_queue.enqueue(s) | 1102 work_queue.enqueue(s) |
| 1077 work_queue.flush(revision_overrides, command, args, options=self._options) | 1103 work_queue.flush(revision_overrides, command, args, options=self._options) |
| 1078 | 1104 |
| 1079 # Once all the dependencies have been processed, it's now safe to run the | 1105 # Once all the dependencies have been processed, it's now safe to run the |
| 1080 # hooks. | 1106 # hooks. |
| 1081 if not self._options.nohooks: | 1107 if not self._options.nohooks: |
| 1082 self.RunHooksRecursively(self._options) | 1108 self.RunHooksRecursively(self._options) |
| 1083 | 1109 |
| 1084 if command == 'update': | 1110 if command == 'update': |
| (...skipping 27 matching lines...) Expand all Loading... |
| 1112 entry_fixed, self.root_dir)) | 1138 entry_fixed, self.root_dir)) |
| 1113 gclient_utils.RemoveDirectory(e_dir) | 1139 gclient_utils.RemoveDirectory(e_dir) |
| 1114 # record the current list of entries for next time | 1140 # record the current list of entries for next time |
| 1115 self._SaveEntries() | 1141 self._SaveEntries() |
| 1116 return 0 | 1142 return 0 |
| 1117 | 1143 |
| 1118 def PrintRevInfo(self): | 1144 def PrintRevInfo(self): |
| 1119 if not self.dependencies: | 1145 if not self.dependencies: |
| 1120 raise gclient_utils.Error('No solution specified') | 1146 raise gclient_utils.Error('No solution specified') |
| 1121 # Load all the settings. | 1147 # Load all the settings. |
| 1122 work_queue = gclient_utils.ExecutionQueue(self._options.jobs, None) | 1148 work_queue = gclient_utils.ExecutionQueue(self._options.jobs, None, False) |
| 1123 for s in self.dependencies: | 1149 for s in self.dependencies: |
| 1124 work_queue.enqueue(s) | 1150 work_queue.enqueue(s) |
| 1125 work_queue.flush({}, None, [], options=self._options) | 1151 work_queue.flush({}, None, [], options=self._options) |
| 1126 | 1152 |
| 1127 def GetURLAndRev(dep): | 1153 def GetURLAndRev(dep): |
| 1128 """Returns the revision-qualified SCM url for a Dependency.""" | 1154 """Returns the revision-qualified SCM url for a Dependency.""" |
| 1129 if dep.parsed_url is None: | 1155 if dep.parsed_url is None: |
| 1130 return None | 1156 return None |
| 1131 if isinstance(dep.parsed_url, self.FileImpl): | 1157 if isinstance(dep.parsed_url, self.FileImpl): |
| 1132 original_url = dep.parsed_url.file_location | 1158 original_url = dep.parsed_url.file_location |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1227 | 1253 |
| 1228 @attr('usage', '[command] [args ...]') | 1254 @attr('usage', '[command] [args ...]') |
| 1229 def CMDrecurse(parser, args): | 1255 def CMDrecurse(parser, args): |
| 1230 """Operates on all the entries. | 1256 """Operates on all the entries. |
| 1231 | 1257 |
| 1232 Runs a shell command on all entries. | 1258 Runs a shell command on all entries. |
| 1233 """ | 1259 """ |
| 1234 # Stop parsing at the first non-arg so that these go through to the command | 1260 # Stop parsing at the first non-arg so that these go through to the command |
| 1235 parser.disable_interspersed_args() | 1261 parser.disable_interspersed_args() |
| 1236 parser.add_option('-s', '--scm', action='append', default=[], | 1262 parser.add_option('-s', '--scm', action='append', default=[], |
| 1237 help='choose scm types to operate upon') | 1263 help='Choose scm types to operate upon.') |
| 1238 parser.add_option('-i', '--ignore', action='store_true', | 1264 parser.add_option('-i', '--ignore', action='store_true', |
| 1239 help='continue processing in case of non zero return code') | 1265 help='Ignore non-zero return codes from subcommands.') |
| 1266 parser.add_option('--prepend-dir', action='store_true', |
| 1267 help='Prepend relative dir for use with git <cmd> --null.') |
| 1268 parser.add_option('--no-progress', action='store_true', |
| 1269 help='Disable progress bar that shows sub-command updates') |
| 1240 options, args = parser.parse_args(args) | 1270 options, args = parser.parse_args(args) |
| 1241 if not args: | 1271 if not args: |
| 1242 print >> sys.stderr, 'Need to supply a command!' | 1272 print >> sys.stderr, 'Need to supply a command!' |
| 1243 return 1 | 1273 return 1 |
| 1244 root_and_entries = gclient_utils.GetGClientRootAndEntries() | 1274 root_and_entries = gclient_utils.GetGClientRootAndEntries() |
| 1245 if not root_and_entries: | 1275 if not root_and_entries: |
| 1246 print >> sys.stderr, ( | 1276 print >> sys.stderr, ( |
| 1247 'You need to run gclient sync at least once to use \'recurse\'.\n' | 1277 'You need to run gclient sync at least once to use \'recurse\'.\n' |
| 1248 'This is because .gclient_entries needs to exist and be up to date.') | 1278 'This is because .gclient_entries needs to exist and be up to date.') |
| 1249 return 1 | 1279 return 1 |
| 1250 | 1280 |
| 1251 # Normalize options.scm to a set() | 1281 # Normalize options.scm to a set() |
| 1252 scm_set = set() | 1282 scm_set = set() |
| 1253 for scm in options.scm: | 1283 for scm in options.scm: |
| 1254 scm_set.update(scm.split(',')) | 1284 scm_set.update(scm.split(',')) |
| 1255 options.scm = scm_set | 1285 options.scm = scm_set |
| 1256 | 1286 |
| 1257 options.nohooks = True | 1287 options.nohooks = True |
| 1258 client = GClient.LoadCurrentConfig(options) | 1288 client = GClient.LoadCurrentConfig(options) |
| 1259 return client.RunOnDeps('recurse', args) | 1289 return client.RunOnDeps('recurse', args, ignore_requirements=True, |
| 1290 progress=not options.no_progress) |
| 1260 | 1291 |
| 1261 | 1292 |
| 1262 @attr('usage', '[args ...]') | 1293 @attr('usage', '[args ...]') |
| 1263 def CMDfetch(parser, args): | 1294 def CMDfetch(parser, args): |
| 1264 """Fetches upstream commits for all modules. | 1295 """Fetches upstream commits for all modules. |
| 1265 | 1296 |
| 1266 Completely git-specific. Simply runs 'git fetch [args ...]' for each module. | 1297 Completely git-specific. Simply runs 'git fetch [args ...]' for each module. |
| 1267 """ | 1298 """ |
| 1268 (options, args) = parser.parse_args(args) | 1299 (options, args) = parser.parse_args(args) |
| 1269 args = ['-j%d' % options.jobs, '-s', 'git', 'git', 'fetch'] + args | 1300 return CMDrecurse(Parser(), [ |
| 1270 return CMDrecurse(parser, args) | 1301 '--jobs=%d' % options.jobs, '--scm=git', 'git', 'fetch'] + args) |
| 1302 |
| 1303 |
| 1304 def CMDgrep(parser, args): |
| 1305 """Greps through git repos managed by gclient. |
| 1306 |
| 1307 Runs 'git grep [args...]' for each module. |
| 1308 """ |
| 1309 |
| 1310 # We can't use optparse because it will try to parse arguments sent |
| 1311 # to git grep and throw an error. :-( |
| 1312 if not args or re.match('(-h|--help)$', args[0]): |
| 1313 print >> sys.stderr, ( |
| 1314 'Usage: gclient grep [-j <N>] git-grep-args...\n\n' |
| 1315 'Example: "gclient grep -j10 -A2 RefCountedBase" runs\n"git grep ' |
| 1316 '-A2 RefCountedBase" on each of gclient\'s git\nrepos with up to ' |
| 1317 '10 jobs.\n\nBonus: page output by appending "|& less -FRSX" to the' |
| 1318 ' end of your query.' |
| 1319 ) |
| 1320 return 1 |
| 1321 |
| 1322 jobs_arg = ['--jobs=1'] |
| 1323 if re.match(r'(-j|--jobs=)\d+$', args[0]): |
| 1324 jobs_arg, args = args[:1], args[1:] |
| 1325 elif re.match(r'(-j|--jobs)$', args[0]): |
| 1326 jobs_arg, args = args[:2], args[2:] |
| 1327 |
| 1328 return CMDrecurse( |
| 1329 parser, |
| 1330 jobs_arg + ['--ignore', '--prepend-dir', '--no-progress', '--scm=git', |
| 1331 'git', 'grep', '--null', '--color=Always'] + args) |
| 1271 | 1332 |
| 1272 | 1333 |
| 1273 @attr('usage', '[url] [safesync url]') | 1334 @attr('usage', '[url] [safesync url]') |
| 1274 def CMDconfig(parser, args): | 1335 def CMDconfig(parser, args): |
| 1275 """Create a .gclient file in the current directory. | 1336 """Create a .gclient file in the current directory. |
| 1276 | 1337 |
| 1277 This specifies the configuration for further commands. After update/sync, | 1338 This specifies the configuration for further commands. After update/sync, |
| 1278 top-level DEPS files in each module are read to determine dependent | 1339 top-level DEPS files in each module are read to determine dependent |
| 1279 modules to operate on as well. If optional [url] parameter is | 1340 modules to operate on as well. If optional [url] parameter is |
| 1280 provided, then configuration is read from a specified Subversion server | 1341 provided, then configuration is read from a specified Subversion server |
| (...skipping 397 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1678 except (gclient_utils.Error, subprocess2.CalledProcessError), e: | 1739 except (gclient_utils.Error, subprocess2.CalledProcessError), e: |
| 1679 print >> sys.stderr, 'Error: %s' % str(e) | 1740 print >> sys.stderr, 'Error: %s' % str(e) |
| 1680 return 1 | 1741 return 1 |
| 1681 | 1742 |
| 1682 | 1743 |
| 1683 if '__main__' == __name__: | 1744 if '__main__' == __name__: |
| 1684 fix_encoding.fix_encoding() | 1745 fix_encoding.fix_encoding() |
| 1685 sys.exit(Main(sys.argv[1:])) | 1746 sys.exit(Main(sys.argv[1:])) |
| 1686 | 1747 |
| 1687 # vim: ts=2:sw=2:tw=80:et: | 1748 # vim: ts=2:sw=2:tw=80:et: |
| OLD | NEW |