OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 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 import codecs | 6 import codecs |
7 import copy | 7 import copy |
8 import cStringIO | 8 import cStringIO |
9 import ctypes | 9 import ctypes |
10 import json | 10 import json |
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
217 | 217 |
218 class SVNRevisionNotFound(Exception): | 218 class SVNRevisionNotFound(Exception): |
219 pass | 219 pass |
220 | 220 |
221 | 221 |
222 def call(*args, **kwargs): | 222 def call(*args, **kwargs): |
223 """Interactive subprocess call.""" | 223 """Interactive subprocess call.""" |
224 kwargs['stdout'] = subprocess.PIPE | 224 kwargs['stdout'] = subprocess.PIPE |
225 kwargs['stderr'] = subprocess.STDOUT | 225 kwargs['stderr'] = subprocess.STDOUT |
226 stdin_data = kwargs.pop('stdin_data', None) | 226 stdin_data = kwargs.pop('stdin_data', None) |
227 tries = kwargs.pop('tries', RETRIES) | |
227 if stdin_data: | 228 if stdin_data: |
228 kwargs['stdin'] = subprocess.PIPE | 229 kwargs['stdin'] = subprocess.PIPE |
229 out = cStringIO.StringIO() | 230 out = cStringIO.StringIO() |
230 new_env = kwargs.get('env', {}) | 231 new_env = kwargs.get('env', {}) |
231 env = copy.copy(os.environ) | 232 env = copy.copy(os.environ) |
232 env.update(new_env) | 233 env.update(new_env) |
233 kwargs['env'] = env | 234 kwargs['env'] = env |
234 for attempt in xrange(RETRIES): | 235 for attempt in range(tries): |
iannucci
2014/05/08 22:17:01
hm... why not xrange?
Ryan Tseng
2014/05/09 20:59:42
"range" sounds cleaner (xrange isn't even a word!)
| |
235 attempt_msg = ' (retry #%d)' % attempt if attempt else '' | 236 attempt_msg = ' (retry #%d)' % attempt if attempt else '' |
236 if new_env: | 237 if new_env: |
237 print '===Injecting Environment Variables===' | 238 print '===Injecting Environment Variables===' |
238 for k, v in sorted(new_env.items()): | 239 for k, v in sorted(new_env.items()): |
239 print '%s: %s' % (k, v) | 240 print '%s: %s' % (k, v) |
240 print '===Running %s%s===' % (' '.join(args), attempt_msg) | 241 print '===Running %s%s===' % (' '.join(args), attempt_msg) |
241 start_time = time.time() | 242 start_time = time.time() |
242 proc = subprocess.Popen(args, **kwargs) | 243 proc = subprocess.Popen(args, **kwargs) |
243 if stdin_data: | 244 if stdin_data: |
244 proc.stdin.write(stdin_data) | 245 proc.stdin.write(stdin_data) |
(...skipping 17 matching lines...) Expand all Loading... | |
262 code = proc.wait() | 263 code = proc.wait() |
263 elapsed_time = ((time.time() - start_time) / 60.0) | 264 elapsed_time = ((time.time() - start_time) / 60.0) |
264 if not code: | 265 if not code: |
265 print '===Succeeded in %.1f mins===' % elapsed_time | 266 print '===Succeeded in %.1f mins===' % elapsed_time |
266 print | 267 print |
267 return out.getvalue() | 268 return out.getvalue() |
268 print '===Failed in %.1f mins===' % elapsed_time | 269 print '===Failed in %.1f mins===' % elapsed_time |
269 print | 270 print |
270 | 271 |
271 raise SubprocessFailed('%s failed with code %d in %s after %d attempts.' % | 272 raise SubprocessFailed('%s failed with code %d in %s after %d attempts.' % |
272 (' '.join(args), code, os.getcwd(), RETRIES), code) | 273 (' '.join(args), code, os.getcwd(), tries), code) |
273 | 274 |
274 | 275 |
275 def git(*args, **kwargs): | 276 def git(*args, **kwargs): |
276 """Wrapper around call specifically for Git commands.""" | 277 """Wrapper around call specifically for Git commands.""" |
277 if args and args[0] == 'cache': | 278 if args and args[0] == 'cache': |
278 # Rewrite "git cache" calls into "python git_cache.py". | 279 # Rewrite "git cache" calls into "python git_cache.py". |
279 cmd = (sys.executable, '-u', GIT_CACHE_PATH) + args[1:] | 280 cmd = (sys.executable, '-u', GIT_CACHE_PATH) + args[1:] |
280 else: | 281 else: |
281 git_executable = 'git' | 282 git_executable = 'git' |
282 # On windows, subprocess doesn't fuzzy-match 'git' to 'git.bat', so we | 283 # On windows, subprocess doesn't fuzzy-match 'git' to 'git.bat', so we |
(...skipping 288 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
571 | 572 |
572 | 573 |
573 def _last_commit_for_file(filename, repo_base): | 574 def _last_commit_for_file(filename, repo_base): |
574 cmd = ['log', '--format=%H', '--max-count=1', '--', filename] | 575 cmd = ['log', '--format=%H', '--max-count=1', '--', filename] |
575 return git(*cmd, cwd=repo_base).strip() | 576 return git(*cmd, cwd=repo_base).strip() |
576 | 577 |
577 | 578 |
578 def need_to_run_deps2git(repo_base, deps_file, deps_git_file): | 579 def need_to_run_deps2git(repo_base, deps_file, deps_git_file): |
579 """Checks to see if we need to run deps2git. | 580 """Checks to see if we need to run deps2git. |
580 | 581 |
581 Returns True if there was a DEPS change after the last .DEPS.git update. | 582 Returns True if there was a DEPS change after the last .DEPS.git update |
583 or if DEPS has local modifications. | |
582 """ | 584 """ |
583 print 'Checking if %s exists' % deps_git_file | 585 print 'Checking if %s exists' % deps_git_file |
584 if not path.isfile(deps_git_file): | 586 if not path.isfile(deps_git_file): |
585 # .DEPS.git doesn't exist but DEPS does? We probably want to generate one. | 587 # .DEPS.git doesn't exist but DEPS does? We probably want to generate one. |
586 print 'it exists!' | 588 print 'it doesn\'t exist!' |
589 return True | |
590 | |
591 if git('diff', deps_file, cwd=repo_base).strip(): | |
587 return True | 592 return True |
588 | 593 |
589 last_known_deps_ref = _last_commit_for_file(deps_file, repo_base) | 594 last_known_deps_ref = _last_commit_for_file(deps_file, repo_base) |
590 last_known_deps_git_ref = _last_commit_for_file(deps_git_file, repo_base) | 595 last_known_deps_git_ref = _last_commit_for_file(deps_git_file, repo_base) |
591 merge_base_ref = git('merge-base', last_known_deps_ref, | 596 merge_base_ref = git('merge-base', last_known_deps_ref, |
592 last_known_deps_git_ref, cwd=repo_base).strip() | 597 last_known_deps_git_ref, cwd=repo_base).strip() |
593 | 598 |
594 # If the merge base of the last DEPS and last .DEPS.git file is not | 599 # If the merge base of the last DEPS and last .DEPS.git file is not |
595 # equivilent to the hash of the last DEPS file, that means the DEPS file | 600 # equivilent to the hash of the last DEPS file, that means the DEPS file |
596 # was committed after the last .DEPS.git file. | 601 # was committed after the last .DEPS.git file. |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
644 version = m.group(1) | 649 version = m.group(1) |
645 git_buildspec = get_git_buildspec(version) | 650 git_buildspec = get_git_buildspec(version) |
646 with open(deps_git_file, 'wb') as f: | 651 with open(deps_git_file, 'wb') as f: |
647 f.write(git_buildspec) | 652 f.write(git_buildspec) |
648 | 653 |
649 | 654 |
650 def ensure_deps2git(sln_dir, shallow): | 655 def ensure_deps2git(sln_dir, shallow): |
651 repo_base = path.join(os.getcwd(), sln_dir) | 656 repo_base = path.join(os.getcwd(), sln_dir) |
652 deps_file = path.join(repo_base, 'DEPS') | 657 deps_file = path.join(repo_base, 'DEPS') |
653 deps_git_file = path.join(repo_base, '.DEPS.git') | 658 deps_git_file = path.join(repo_base, '.DEPS.git') |
654 print 'Checking if %s is newer than %s' % (deps_file, deps_git_file) | |
655 if not path.isfile(deps_file): | 659 if not path.isfile(deps_file): |
656 return | 660 return |
657 | 661 |
662 print 'Checking if %s is newer than %s' % (deps_file, deps_git_file) | |
658 if not need_to_run_deps2git(repo_base, deps_file, deps_git_file): | 663 if not need_to_run_deps2git(repo_base, deps_file, deps_git_file): |
659 return | 664 return |
660 | 665 |
661 print '===DEPS file modified, need to run deps2git===' | 666 print '===DEPS file modified, need to run deps2git===' |
662 # Magic to get deps2git to work with internal DEPS. | 667 # Magic to get deps2git to work with internal DEPS. |
663 shutil.copyfile(S2G_INTERNAL_FROM_PATH, S2G_INTERNAL_DEST_PATH) | 668 shutil.copyfile(S2G_INTERNAL_FROM_PATH, S2G_INTERNAL_DEST_PATH) |
664 | 669 |
665 # TODO(hinoka): This might need to be smarter if we need to deal with | 670 # TODO(hinoka): This might need to be smarter if we need to deal with |
666 # DEPS changes that are in an internal repository. | 671 # DEPS changes that are in an internal repository. |
667 repo_type = 'public' | 672 repo_type = 'public' |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
795 def _download(url): | 800 def _download(url): |
796 """Fetch url and return content, with retries for flake.""" | 801 """Fetch url and return content, with retries for flake.""" |
797 for attempt in xrange(RETRIES): | 802 for attempt in xrange(RETRIES): |
798 try: | 803 try: |
799 return urllib2.urlopen(url).read() | 804 return urllib2.urlopen(url).read() |
800 except Exception: | 805 except Exception: |
801 if attempt == RETRIES - 1: | 806 if attempt == RETRIES - 1: |
802 raise | 807 raise |
803 | 808 |
804 | 809 |
805 def apply_issue_svn(root, patch_url): | 810 def parse_diff(diff): |
806 patch_data = call('svn', 'cat', patch_url) | 811 """Takes a unified diff and returns a list of diffed files and their diffs. |
807 call(PATCH_TOOL, '-p0', '--remove-empty-files', '--force', '--forward', | 812 |
808 stdin_data=patch_data, cwd=root) | 813 The return format is a list of pairs of: |
814 (<filename>, <diff contents>) | |
815 <diff contents> is inclusive of the diff line. | |
816 """ | |
817 result = [] | |
818 current_diff = '' | |
819 current_header = None | |
820 for line in diff.splitlines(): | |
821 # "diff" is for git style patches, and "Index: " is for SVN style patches. | |
822 if line.startswith('diff') or line.startswith('Index: '): | |
823 if current_header: | |
824 # If we are in a diff portion, then save the diff. | |
825 result.append((current_header, '%s\n' % current_diff)) | |
826 git_header_match = re.match(r'diff (?:--git )?(\S+) (\S+)', line) | |
827 svn_header_match = re.match(r'Index: (.*)', line) | |
828 | |
829 if git_header_match: | |
830 # First, see if its a git style header. | |
831 from_file = git_header_match.group(1) | |
832 to_file = git_header_match.group(2) | |
833 if from_file != to_file and from_file.startswith('a/'): | |
834 # Sometimes git prepends 'a/' and 'b/' in front of file paths. | |
835 from_file = from_file[2:] | |
836 current_header = from_file | |
837 | |
838 elif svn_header_match: | |
839 # Otherwise, check if its an SVN style header. | |
840 current_header = svn_header_match.group(1) | |
841 | |
842 else: | |
843 # Otherwise... I'm not really sure what to do with this. | |
iannucci
2014/05/08 22:17:01
probably should abort loudly
Ryan Tseng
2014/05/09 20:59:42
Done.
| |
844 current_header = line | |
845 | |
846 current_diff = '' | |
847 current_diff += '%s\n' % line | |
848 if current_header: | |
849 # We hit EOF, gotta save the last diff. | |
850 result.append((current_header, current_diff)) | |
851 return result | |
809 | 852 |
810 | 853 |
811 def apply_issue_rietveld(issue, patchset, root, server, rev_map, revision): | 854 def get_svn_patch(patch_url): |
855 """Fetch patch from patch_url, return list of (filename, diff)""" | |
856 patch_data = call('svn', 'cat', patch_url) | |
857 return parse_diff(patch_data) | |
858 | |
859 | |
860 def apply_svn_patch(patch_root, patches, whitelist=None, blacklist=None): | |
861 """Expects a list of (filename, diff), applies it on top of patch_root.""" | |
862 if whitelist: | |
863 patches = [(name, diff) for name, diff in patches if name in whitelist] | |
864 elif blacklist: | |
865 patches = [(name, diff) for name, diff in patches if name not in blacklist] | |
866 diffs = [diff for _, diff in patches] | |
867 patch = ''.join(diffs) | |
868 | |
869 if patch: | |
870 print '===Patching files===' | |
871 for filename, _ in patches: | |
872 print 'Patching %s' % filename | |
873 call(PATCH_TOOL, '-p0', '--remove-empty-files', '--force', '--forward', | |
874 stdin_data=patch, cwd=patch_root, tries=1) | |
875 | |
876 | |
877 def apply_rietveld_issue(issue, patchset, root, server, rev_map, revision, | |
878 whitelist=None, blacklist=None): | |
812 apply_issue_bin = ('apply_issue.bat' if sys.platform.startswith('win') | 879 apply_issue_bin = ('apply_issue.bat' if sys.platform.startswith('win') |
813 else 'apply_issue') | 880 else 'apply_issue') |
814 call(apply_issue_bin, | 881 cmd = [apply_issue_bin, |
815 '--root_dir', root, | 882 # The patch will be applied on top of this directory. |
816 '--issue', issue, | 883 '--root_dir', root, |
817 '--patchset', patchset, | 884 # Tell apply_issue how to fetch the patch. |
818 '--no-auth', | 885 '--issue', issue, |
819 '--server', server, | 886 '--patchset', patchset, |
820 '--base_ref', revision, | 887 '--no-auth', |
821 '--force', | 888 '--server', server, |
822 '--ignore_deps') | 889 # Always run apply_issue.py, otherwise it would see update.flag |
890 # and then bail out. | |
891 '--force', | |
892 # Don't run gclient sync when it sees a DEPS change. | |
893 '--ignore_deps', | |
894 # Don't commit the patch or add it to the index. | |
895 '--no_commit', | |
896 ] | |
897 if whitelist: | |
898 for item in whitelist: | |
899 cmd.extend(['--whitelist', item]) | |
900 elif blacklist: | |
901 for item in blacklist: | |
902 cmd.extend(['--blacklist', item]) | |
903 | |
904 # Only try once, since subsequent failures hide the real failure. | |
905 call(*cmd, tries=1) | |
823 | 906 |
824 | 907 |
825 def check_flag(flag_file): | 908 def check_flag(flag_file): |
826 """Returns True if the flag file is present.""" | 909 """Returns True if the flag file is present.""" |
827 return os.path.isfile(flag_file) | 910 return os.path.isfile(flag_file) |
828 | 911 |
829 | 912 |
830 def delete_flag(flag_file): | 913 def delete_flag(flag_file): |
831 """Remove bot update flag.""" | 914 """Remove bot update flag.""" |
832 if os.path.isfile(flag_file): | 915 if os.path.isfile(flag_file): |
833 os.remove(flag_file) | 916 os.remove(flag_file) |
834 | 917 |
835 | 918 |
836 def emit_flag(flag_file): | 919 def emit_flag(flag_file): |
837 """Deposit a bot update flag on the system to tell gclient not to run.""" | 920 """Deposit a bot update flag on the system to tell gclient not to run.""" |
838 print 'Emitting flag file at %s' % flag_file | 921 print 'Emitting flag file at %s' % flag_file |
839 with open(flag_file, 'wb') as f: | 922 with open(flag_file, 'wb') as f: |
840 f.write('Success!') | 923 f.write('Success!') |
841 | 924 |
842 | 925 |
843 def get_real_git_hash(git_hash, dir_name): | |
844 """Return git_hash or the parent of git_hash if its a patched hash.""" | |
845 log = git('log', '-1', '--format=%B', cwd=dir_name).strip() | |
846 if 'committed patch' in log.lower(): | |
847 return git('log', '-1', '--format=%H', 'HEAD^', cwd=dir_name).strip() | |
848 return git_hash | |
849 | |
850 | |
851 def parse_got_revision(gclient_output, got_revision_mapping, use_svn_revs): | 926 def parse_got_revision(gclient_output, got_revision_mapping, use_svn_revs): |
852 """Translate git gclient revision mapping to build properties. | 927 """Translate git gclient revision mapping to build properties. |
853 | 928 |
854 If use_svn_revs is True, then translate git hashes in the revision mapping | 929 If use_svn_revs is True, then translate git hashes in the revision mapping |
855 to svn revision numbers. | 930 to svn revision numbers. |
856 """ | 931 """ |
857 properties = {} | 932 properties = {} |
858 solutions_output = gclient_output['solutions'] | 933 solutions_output = gclient_output['solutions'] |
859 for dir_name, property_name in got_revision_mapping.iteritems(): | 934 for dir_name, property_name in got_revision_mapping.iteritems(): |
860 if dir_name not in solutions_output: | 935 if dir_name not in solutions_output: |
861 continue | 936 continue |
862 solution_output = solutions_output[dir_name] | 937 solution_output = solutions_output[dir_name] |
863 if solution_output.get('scm') is None: | 938 if solution_output.get('scm') is None: |
864 # This is an ignored DEPS, so the output got_revision should be 'None'. | 939 # This is an ignored DEPS, so the output got_revision should be 'None'. |
865 git_revision = revision = None | 940 git_revision = revision = None |
866 else: | 941 else: |
867 # Since we are using .DEPS.git, everything had better be git. | 942 # Since we are using .DEPS.git, everything had better be git. |
868 assert solution_output.get('scm') == 'git' | 943 assert solution_output.get('scm') == 'git' |
869 git_revision = get_real_git_hash(solution_output['revision'], dir_name) | 944 git_revision = solution_output['revision'] |
870 if use_svn_revs: | 945 if use_svn_revs: |
871 revision = get_svn_rev(git_revision, dir_name) | 946 revision = get_svn_rev(git_revision, dir_name) |
872 if not revision: | 947 if not revision: |
873 revision = git_revision | 948 revision = git_revision |
874 else: | 949 else: |
875 revision = git_revision | 950 revision = git_revision |
876 | 951 |
877 properties[property_name] = revision | 952 properties[property_name] = revision |
878 if revision != git_revision: | 953 if revision != git_revision: |
879 properties['%s_git' % property_name] = git_revision | 954 properties['%s_git' % property_name] = git_revision |
(...skipping 13 matching lines...) Expand all Loading... | |
893 | 968 |
894 def ensure_checkout(solutions, revision, first_sln, target_os, target_os_only, | 969 def ensure_checkout(solutions, revision, first_sln, target_os, target_os_only, |
895 root, issue, patchset, patch_url, rietveld_server, | 970 root, issue, patchset, patch_url, rietveld_server, |
896 revision_mapping, buildspec_name, gyp_env, shallow): | 971 revision_mapping, buildspec_name, gyp_env, shallow): |
897 # Get a checkout of each solution, without DEPS or hooks. | 972 # Get a checkout of each solution, without DEPS or hooks. |
898 # Calling git directly because there is no way to run Gclient without | 973 # Calling git directly because there is no way to run Gclient without |
899 # invoking DEPS. | 974 # invoking DEPS. |
900 print 'Fetching Git checkout' | 975 print 'Fetching Git checkout' |
901 git_ref = git_checkout(solutions, revision, shallow) | 976 git_ref = git_checkout(solutions, revision, shallow) |
902 | 977 |
903 # If either patch_url or issue is passed in, then we need to apply a patch. | 978 patches = None |
904 if patch_url: | 979 if patch_url: |
905 # patch_url takes precidence since its only passed in on gcl try/git try. | 980 patches = get_svn_patch(patch_url) |
906 apply_issue_svn(root, patch_url) | 981 |
907 elif issue: | 982 if root == first_sln: |
908 apply_issue_rietveld(issue, patchset, root, rietveld_server, | 983 # Only top level DEPS patching is supported right now. |
909 revision_mapping, git_ref) | 984 if patches: |
985 apply_svn_patch(root, patches, whitelist=['DEPS']) | |
986 elif issue: | |
987 apply_rietveld_issue(issue, patchset, root, rietveld_server, | |
988 revision_mapping, git_ref, whitelist=['DEPS']) | |
989 | |
910 | 990 |
911 if buildspec_name: | 991 if buildspec_name: |
912 buildspecs2git(root, buildspec_name) | 992 buildspecs2git(root, buildspec_name) |
913 elif first_sln == root: | 993 else: |
914 # Run deps2git if there is a DEPS commit after the last .DEPS.git commit. | 994 # Run deps2git if there is a DEPS change after the last .DEPS.git commit. |
915 # We only need to ensure deps2git if the root is not overridden, since | |
916 # if the root is overridden, it means we are working with a sub repository | |
917 # patch, which means its impossible for it to touch DEPS. | |
918 ensure_deps2git(root, shallow) | 995 ensure_deps2git(root, shallow) |
919 | 996 |
920 # Ensure our build/ directory is set up with the correct .gclient file. | 997 # Ensure our build/ directory is set up with the correct .gclient file. |
921 gclient_configure(solutions, target_os, target_os_only) | 998 gclient_configure(solutions, target_os, target_os_only) |
922 | 999 |
923 # Let gclient do the DEPS syncing. | 1000 # Let gclient do the DEPS syncing. |
924 gclient_output = gclient_sync(buildspec_name) | 1001 gclient_output = gclient_sync(buildspec_name) |
925 if buildspec_name: | 1002 if buildspec_name: |
926 # Run gclient runhooks if we're on an official builder. | 1003 # Run gclient runhooks if we're on an official builder. |
927 # TODO(hinoka): Remove this when the official builders run their own | 1004 # TODO(hinoka): Remove this when the official builders run their own |
928 # runhooks step. | 1005 # runhooks step. |
929 gclient_runhooks(gyp_env) | 1006 gclient_runhooks(gyp_env) |
1007 | |
1008 # Apply the rest of the patch here (sans DEPS) | |
1009 if patches: | |
1010 apply_svn_patch(root, patches, blacklist=['DEPS']) | |
1011 elif issue: | |
1012 apply_rietveld_issue(issue, patchset, root, rietveld_server, | |
1013 revision_mapping, git_ref, blacklist=['DEPS']) | |
1014 | |
930 return gclient_output | 1015 return gclient_output |
931 | 1016 |
932 | 1017 |
933 def parse_args(): | 1018 def parse_args(): |
934 parse = optparse.OptionParser() | 1019 parse = optparse.OptionParser() |
935 | 1020 |
936 parse.add_option('--issue', help='Issue number to patch from.') | 1021 parse.add_option('--issue', help='Issue number to patch from.') |
937 parse.add_option('--patchset', | 1022 parse.add_option('--patchset', |
938 help='Patchset from issue to patch from, if applicable.') | 1023 help='Patchset from issue to patch from, if applicable.') |
939 parse.add_option('--patch_url', help='Optional URL to SVN patch.') | 1024 parse.add_option('--patch_url', help='Optional URL to SVN patch.') |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1088 patch_root=options.root, | 1173 patch_root=options.root, |
1089 step_text=step_text, | 1174 step_text=step_text, |
1090 properties=got_revisions) | 1175 properties=got_revisions) |
1091 else: | 1176 else: |
1092 # If we're not on recipes, tell annotator about our got_revisions. | 1177 # If we're not on recipes, tell annotator about our got_revisions. |
1093 emit_properties(got_revisions) | 1178 emit_properties(got_revisions) |
1094 | 1179 |
1095 | 1180 |
1096 if __name__ == '__main__': | 1181 if __name__ == '__main__': |
1097 sys.exit(main()) | 1182 sys.exit(main()) |
OLD | NEW |