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 |