Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(691)

Side by Side Diff: gclient.py

Issue 7777002: Add a --unmanaged flag to gclient config to allow the main solution to be unmanaged by the scm. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools/
Patch Set: Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | gclient_scm.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2011 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 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
132 if var_name in self._custom_vars: 132 if var_name in self._custom_vars:
133 return self._custom_vars[var_name] 133 return self._custom_vars[var_name]
134 elif var_name in self._local_scope.get("vars", {}): 134 elif var_name in self._local_scope.get("vars", {}):
135 return self._local_scope["vars"][var_name] 135 return self._local_scope["vars"][var_name]
136 raise gclient_utils.Error("Var is not defined: %s" % var_name) 136 raise gclient_utils.Error("Var is not defined: %s" % var_name)
137 137
138 138
139 class Dependency(GClientKeywords, gclient_utils.WorkItem): 139 class Dependency(GClientKeywords, gclient_utils.WorkItem):
140 """Object that represents a dependency checkout.""" 140 """Object that represents a dependency checkout."""
141 141
142 def __init__(self, parent, name, url, safesync_url, custom_deps, 142 def __init__(self, parent, name, url, safesync_url, managed, custom_deps,
143 custom_vars, deps_file, should_process): 143 custom_vars, deps_file, should_process):
144 GClientKeywords.__init__(self) 144 GClientKeywords.__init__(self)
145 gclient_utils.WorkItem.__init__(self) 145 gclient_utils.WorkItem.__init__(self)
146 self.parent = parent 146 self.parent = parent
147 self.name = name 147 self.name = name
148 self.url = url 148 self.url = url
149 self.parsed_url = None 149 self.parsed_url = None
150 # These 2 are only set in .gclient and not in DEPS files. 150 # These 2 are only set in .gclient and not in DEPS files.
151 self.safesync_url = safesync_url 151 self.safesync_url = safesync_url
152 self.managed = managed
152 self.custom_vars = custom_vars or {} 153 self.custom_vars = custom_vars or {}
153 self.custom_deps = custom_deps or {} 154 self.custom_deps = custom_deps or {}
154 self.deps_hooks = [] 155 self.deps_hooks = []
155 self.dependencies = [] 156 self.dependencies = []
156 self.deps_file = deps_file 157 self.deps_file = deps_file
157 # A cache of the files affected by the current operation, necessary for 158 # A cache of the files affected by the current operation, necessary for
158 # hooks. 159 # hooks.
159 self._file_list = [] 160 self._file_list = []
160 # If it is not set to True, the dependency wasn't processed for its child 161 # If it is not set to True, the dependency wasn't processed for its child
161 # dependency, i.e. its DEPS wasn't read. 162 # dependency, i.e. its DEPS wasn't read.
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after
324 logging.info('Won\'t process duplicate dependency %s' % tree[name]) 325 logging.info('Won\'t process duplicate dependency %s' % tree[name])
325 # In theory we could keep it as a shadow of the other one. In 326 # In theory we could keep it as a shadow of the other one. In
326 # practice, simply ignore it. 327 # practice, simply ignore it.
327 #should_process = False 328 #should_process = False
328 continue 329 continue
329 else: 330 else:
330 raise gclient_utils.Error( 331 raise gclient_utils.Error(
331 'Dependency %s specified more than once:\n %s\nvs\n %s' % 332 'Dependency %s specified more than once:\n %s\nvs\n %s' %
332 (name, tree[name].hierarchy(), self.hierarchy())) 333 (name, tree[name].hierarchy(), self.hierarchy()))
333 self.dependencies.append(Dependency(self, name, url, None, None, None, 334 self.dependencies.append(Dependency(self, name, url, None, None, None,
334 self.deps_file, should_process)) 335 None, self.deps_file, should_process))
M-A Ruel 2011/08/28 13:03:31 So it's not inheritable?
nsylvain 2011/08/29 18:03:58 Right
335 logging.debug('Loaded: %s' % str(self)) 336 logging.debug('Loaded: %s' % str(self))
336 337
337 # Arguments number differs from overridden method 338 # Arguments number differs from overridden method
338 # pylint: disable=W0221 339 # pylint: disable=W0221
339 def run(self, revision_overrides, command, args, work_queue, options): 340 def run(self, revision_overrides, command, args, work_queue, options):
340 """Runs 'command' before parsing the DEPS in case it's a initial checkout 341 """Runs 'command' before parsing the DEPS in case it's a initial checkout
341 or a revert.""" 342 or a revert."""
342 343
343 def maybeGetParentRevision(options): 344 def maybeGetParentRevision(options):
344 """If we are performing an update and --transitive is set, set the 345 """If we are performing an update and --transitive is set, set the
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
390 self.root_dir(), 391 self.root_dir(),
391 self.name) 392 self.name)
392 scm.RunCommand('updatesingle', options, 393 scm.RunCommand('updatesingle', options,
393 args + [self.parsed_url.GetFilename()], 394 args + [self.parsed_url.GetFilename()],
394 self._file_list) 395 self._file_list)
395 else: 396 else:
396 # Create a shallow copy to mutate revision. 397 # Create a shallow copy to mutate revision.
397 options = copy.copy(options) 398 options = copy.copy(options)
398 options.revision = revision_overrides.get(self.name) 399 options.revision = revision_overrides.get(self.name)
399 maybeGetParentRevision(options) 400 maybeGetParentRevision(options)
400 scm = gclient_scm.CreateSCM(self.parsed_url, self.root_dir(), self.name) 401 scm = gclient_scm.CreateSCM(self.parsed_url, self.root_dir(), self.name)
M-A Ruel 2011/08/28 13:03:31 I'd modify it here instead. I'd probably make a me
nsylvain 2011/08/29 18:03:58 I'll look into this
401 scm.RunCommand(command, options, args, self._file_list) 402 scm.RunCommand(command, options, args, self._file_list)
402 maybeConvertToDateRevision(options) 403 maybeConvertToDateRevision(options)
403 self._file_list = [os.path.join(self.name, f.strip()) 404 self._file_list = [os.path.join(self.name, f.strip())
404 for f in self._file_list] 405 for f in self._file_list]
405 self.processed = True 406 self.processed = True
406 if self.recursion_limit() > 0: 407 if self.recursion_limit() > 0:
407 # Then we can parse the DEPS file. 408 # Then we can parse the DEPS file.
408 self.ParseDepsFile() 409 self.ParseDepsFile()
409 # Adjust the implicit dependency requirement; e.g. if a DEPS file contains 410 # Adjust the implicit dependency requirement; e.g. if a DEPS file contains
410 # both src/foo and src/foo/bar, src/foo/bar is implicitly dependent of 411 # both src/foo and src/foo/bar, src/foo/bar is implicitly dependent of
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
580 "linux": "unix", 581 "linux": "unix",
581 "linux2": "unix", 582 "linux2": "unix",
582 "linux3": "unix", 583 "linux3": "unix",
583 } 584 }
584 585
585 DEFAULT_CLIENT_FILE_TEXT = ("""\ 586 DEFAULT_CLIENT_FILE_TEXT = ("""\
586 solutions = [ 587 solutions = [
587 { "name" : "%(solution_name)s", 588 { "name" : "%(solution_name)s",
588 "url" : "%(solution_url)s", 589 "url" : "%(solution_url)s",
589 "deps_file" : "%(deps_file)s", 590 "deps_file" : "%(deps_file)s",
591 "managed" : %(managed)s,
590 "custom_deps" : { 592 "custom_deps" : {
591 }, 593 },
592 "safesync_url": "%(safesync_url)s", 594 "safesync_url": "%(safesync_url)s",
593 }, 595 },
594 ] 596 ]
595 """) 597 """)
596 598
597 DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\ 599 DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\
598 { "name" : "%(solution_name)s", 600 { "name" : "%(solution_name)s",
599 "url" : "%(solution_url)s", 601 "url" : "%(solution_url)s",
600 "deps_file" : "%(deps_file)s", 602 "deps_file" : "%(deps_file)s",
603 "managed" : %(managed)s,
601 "custom_deps" : { 604 "custom_deps" : {
602 %(solution_deps)s }, 605 %(solution_deps)s },
603 "safesync_url": "%(safesync_url)s", 606 "safesync_url": "%(safesync_url)s",
604 }, 607 },
605 """) 608 """)
606 609
607 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\ 610 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\
608 # Snapshot generated with gclient revinfo --snapshot 611 # Snapshot generated with gclient revinfo --snapshot
609 solutions = [ 612 solutions = [
610 %(solution_list)s] 613 %(solution_list)s]
611 """) 614 """)
612 615
613 def __init__(self, root_dir, options): 616 def __init__(self, root_dir, options):
614 # Do not change previous behavior. Only solution level and immediate DEPS 617 # Do not change previous behavior. Only solution level and immediate DEPS
615 # are processed. 618 # are processed.
616 self._recursion_limit = 2 619 self._recursion_limit = 2
617 Dependency.__init__(self, None, None, None, None, None, None, 'unused', 620 Dependency.__init__(self, None, None, None, None, None, None, None,
618 True) 621 'unused', True)
619 self._options = options 622 self._options = options
620 if options.deps_os: 623 if options.deps_os:
621 enforced_os = options.deps_os.split(',') 624 enforced_os = options.deps_os.split(',')
622 else: 625 else:
623 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')] 626 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')]
624 if 'all' in enforced_os: 627 if 'all' in enforced_os:
625 enforced_os = self.DEPS_OS_CHOICES.itervalues() 628 enforced_os = self.DEPS_OS_CHOICES.itervalues()
626 self._enforced_os = list(set(enforced_os)) 629 self._enforced_os = list(set(enforced_os))
627 self._root_dir = root_dir 630 self._root_dir = root_dir
628 self.config_content = None 631 self.config_content = None
629 632
630 def SetConfig(self, content): 633 def SetConfig(self, content):
631 assert self.dependencies == [] 634 assert self.dependencies == []
632 config_dict = {} 635 config_dict = {}
633 self.config_content = content 636 self.config_content = content
634 try: 637 try:
635 exec(content, config_dict) 638 exec(content, config_dict)
636 except SyntaxError, e: 639 except SyntaxError, e:
637 gclient_utils.SyntaxErrorToError('.gclient', e) 640 gclient_utils.SyntaxErrorToError('.gclient', e)
638 for s in config_dict.get('solutions', []): 641 for s in config_dict.get('solutions', []):
639 try: 642 try:
640 tree = dict((d.name, d) for d in self.tree(False)) 643 tree = dict((d.name, d) for d in self.tree(False))
641 if s['name'] in tree: 644 if s['name'] in tree:
642 raise gclient_utils.Error( 645 raise gclient_utils.Error(
643 'Dependency %s specified more than once in .gclient' % s['name']) 646 'Dependency %s specified more than once in .gclient' % s['name'])
644 self.dependencies.append(Dependency( 647 self.dependencies.append(Dependency(
645 self, s['name'], s['url'], 648 self, s['name'], s['url'],
646 s.get('safesync_url', None), 649 s.get('safesync_url', None),
650 s.get('managed', True),
647 s.get('custom_deps', {}), 651 s.get('custom_deps', {}),
648 s.get('custom_vars', {}), 652 s.get('custom_vars', {}),
649 s.get('deps_file', 'DEPS'), 653 s.get('deps_file', 'DEPS'),
650 True)) 654 True))
651 except KeyError: 655 except KeyError:
652 raise gclient_utils.Error('Invalid .gclient file. Solution is ' 656 raise gclient_utils.Error('Invalid .gclient file. Solution is '
653 'incomplete: %s' % s) 657 'incomplete: %s' % s)
654 # .gclient can have hooks. 658 # .gclient can have hooks.
655 self.deps_hooks = config_dict.get('hooks', []) 659 self.deps_hooks = config_dict.get('hooks', [])
656 self.deps_parsed = True 660 self.deps_parsed = True
657 661
658 def SaveConfig(self): 662 def SaveConfig(self):
659 gclient_utils.FileWrite(os.path.join(self.root_dir(), 663 gclient_utils.FileWrite(os.path.join(self.root_dir(),
660 self._options.config_filename), 664 self._options.config_filename),
661 self.config_content) 665 self.config_content)
662 666
663 @staticmethod 667 @staticmethod
664 def LoadCurrentConfig(options): 668 def LoadCurrentConfig(options):
665 """Searches for and loads a .gclient file relative to the current working 669 """Searches for and loads a .gclient file relative to the current working
666 dir. Returns a GClient object.""" 670 dir. Returns a GClient object."""
667 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename) 671 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename)
668 if not path: 672 if not path:
669 return None 673 return None
670 client = GClient(path, options) 674 client = GClient(path, options)
671 client.SetConfig(gclient_utils.FileRead( 675 client.SetConfig(gclient_utils.FileRead(
672 os.path.join(path, options.config_filename))) 676 os.path.join(path, options.config_filename)))
673 return client 677 return client
674 678
675 def SetDefaultConfig(self, solution_name, deps_file, solution_url, 679 def SetDefaultConfig(self, solution_name, deps_file, solution_url,
676 safesync_url): 680 safesync_url, managed):
677 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % { 681 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % {
678 'solution_name': solution_name, 682 'solution_name': solution_name,
679 'solution_url': solution_url, 683 'solution_url': solution_url,
680 'deps_file': deps_file, 684 'deps_file': deps_file,
681 'safesync_url' : safesync_url, 685 'safesync_url' : safesync_url,
686 'managed': managed,
682 }) 687 })
683 688
684 def _SaveEntries(self): 689 def _SaveEntries(self):
685 """Creates a .gclient_entries file to record the list of unique checkouts. 690 """Creates a .gclient_entries file to record the list of unique checkouts.
686 691
687 The .gclient_entries file lives in the same directory as .gclient. 692 The .gclient_entries file lives in the same directory as .gclient.
688 """ 693 """
689 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It 694 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It
690 # makes testing a bit too fun. 695 # makes testing a bit too fun.
691 result = 'entries = {\n' 696 result = 'entries = {\n'
(...skipping 25 matching lines...) Expand all
717 return scope['entries'] 722 return scope['entries']
718 723
719 def _EnforceRevisions(self): 724 def _EnforceRevisions(self):
720 """Checks for revision overrides.""" 725 """Checks for revision overrides."""
721 revision_overrides = {} 726 revision_overrides = {}
722 if self._options.head: 727 if self._options.head:
723 return revision_overrides 728 return revision_overrides
724 # Do not check safesync_url if one or more --revision flag is specified. 729 # Do not check safesync_url if one or more --revision flag is specified.
725 if not self._options.revisions: 730 if not self._options.revisions:
726 for s in self.dependencies: 731 for s in self.dependencies:
727 if not s.safesync_url: 732 if not s.managed:
728 continue 733 self._options.revisions.append('%s@unmanaged' % s.name)
729 handle = urllib.urlopen(s.safesync_url) 734 elif s.safesync_url:
730 rev = handle.read().strip() 735 handle = urllib.urlopen(s.safesync_url)
731 handle.close() 736 rev = handle.read().strip()
732 if len(rev): 737 handle.close()
733 self._options.revisions.append('%s@%s' % (s.name, rev)) 738 if len(rev):
739 self._options.revisions.append('%s@%s' % (s.name, rev))
734 if not self._options.revisions: 740 if not self._options.revisions:
735 return revision_overrides 741 return revision_overrides
736 solutions_names = [s.name for s in self.dependencies] 742 solutions_names = [s.name for s in self.dependencies]
737 index = 0 743 index = 0
738 for revision in self._options.revisions: 744 for revision in self._options.revisions:
739 if not '@' in revision: 745 if not '@' in revision:
740 # Support for --revision 123 746 # Support for --revision 123
741 revision = '%s@%s' % (solutions_names[index], revision) 747 revision = '%s@%s' % (solutions_names[index], revision)
742 sol, rev = revision.split('@', 1) 748 sol, rev = revision.split('@', 1)
743 if not sol in solutions_names: 749 if not sol in solutions_names:
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
847 if entries[k]: 853 if entries[k]:
848 # Quotes aren't escaped... 854 # Quotes aren't escaped...
849 custom_deps.append(' \"%s\": \'%s\',\n' % (k, entries[k])) 855 custom_deps.append(' \"%s\": \'%s\',\n' % (k, entries[k]))
850 else: 856 else:
851 custom_deps.append(' \"%s\": None,\n' % k) 857 custom_deps.append(' \"%s\": None,\n' % k)
852 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % { 858 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % {
853 'solution_name': d.name, 859 'solution_name': d.name,
854 'solution_url': d.url, 860 'solution_url': d.url,
855 'deps_file': d.deps_file, 861 'deps_file': d.deps_file,
856 'safesync_url' : d.safesync_url or '', 862 'safesync_url' : d.safesync_url or '',
863 'managed': d.managed,
857 'solution_deps': ''.join(custom_deps), 864 'solution_deps': ''.join(custom_deps),
858 } 865 }
859 # Print the snapshot configuration file 866 # Print the snapshot configuration file
860 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient}) 867 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient})
861 else: 868 else:
862 entries = {} 869 entries = {}
863 for d in self.tree(False): 870 for d in self.tree(False):
864 if self._options.actual: 871 if self._options.actual:
865 entries[d.name] = GetURLAndRev(d) 872 entries[d.name] = GetURLAndRev(d)
866 else: 873 else:
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
967 """ 974 """
968 parser.add_option('--spec', 975 parser.add_option('--spec',
969 help='create a gclient file containing the provided ' 976 help='create a gclient file containing the provided '
970 'string. Due to Cygwin/Python brokenness, it ' 977 'string. Due to Cygwin/Python brokenness, it '
971 'probably can\'t contain any newlines.') 978 'probably can\'t contain any newlines.')
972 parser.add_option('--name', 979 parser.add_option('--name',
973 help='overrides the default name for the solution') 980 help='overrides the default name for the solution')
974 parser.add_option('--deps-file', default='DEPS', 981 parser.add_option('--deps-file', default='DEPS',
975 help='overrides the default name for the DEPS file for the' 982 help='overrides the default name for the DEPS file for the'
976 'main solutions and all sub-dependencies') 983 'main solutions and all sub-dependencies')
984 parser.add_option('--unmanaged', action='store_true',
M-A Ruel 2011/08/28 13:03:31 Why add the flag if it's accessible from .gclient?
nsylvain 2011/08/29 18:03:58 because it's user friendly? Same reason we have --
Dirk Pranke 2011/08/29 20:56:13 It's unclear to me if both of these options are ne
985 help='overrides the default behavior to make it possible to'
986 'have the main solution untouched by gclient')
977 parser.add_option('--git-deps', action='store_true', 987 parser.add_option('--git-deps', action='store_true',
978 help='sets the deps file to ".DEPS.git" instead of "DEPS"') 988 help='sets the deps file to ".DEPS.git" instead of "DEPS"')
979 (options, args) = parser.parse_args(args) 989 (options, args) = parser.parse_args(args)
980 if ((options.spec and args) or len(args) > 2 or 990 if ((options.spec and args) or len(args) > 2 or
981 (not options.spec and not args)): 991 (not options.spec and not args)):
982 parser.error('Inconsistent arguments. Use either --spec or one or 2 args') 992 parser.error('Inconsistent arguments. Use either --spec or one or 2 args')
983 993
984 client = GClient('.', options) 994 client = GClient('.', options)
985 if options.spec: 995 if options.spec:
986 client.SetConfig(options.spec) 996 client.SetConfig(options.spec)
987 else: 997 else:
988 base_url = args[0].rstrip('/') 998 base_url = args[0].rstrip('/')
989 if not options.name: 999 if not options.name:
990 name = base_url.split('/')[-1] 1000 name = base_url.split('/')[-1]
991 if name.endswith('.git'): 1001 if name.endswith('.git'):
992 name = name[:-4] 1002 name = name[:-4]
993 else: 1003 else:
994 # specify an alternate relpath for the given URL. 1004 # specify an alternate relpath for the given URL.
995 name = options.name 1005 name = options.name
996 deps_file = options.deps_file 1006 deps_file = options.deps_file
997 if options.git_deps: 1007 if options.git_deps:
998 deps_file = '.DEPS.git' 1008 deps_file = '.DEPS.git'
1009 managed = True
1010 if options.unmanaged:
1011 managed = False
999 safesync_url = '' 1012 safesync_url = ''
1000 if len(args) > 1: 1013 if len(args) > 1:
1001 safesync_url = args[1] 1014 safesync_url = args[1]
1002 client.SetDefaultConfig(name, deps_file, base_url, safesync_url) 1015 client.SetDefaultConfig(name, deps_file, base_url, safesync_url, managed)
1003 client.SaveConfig() 1016 client.SaveConfig()
1004 return 0 1017 return 0
1005 1018
1006 1019
1007 @attr('epilog', """Example: 1020 @attr('epilog', """Example:
1008 gclient pack > patch.txt 1021 gclient pack > patch.txt
1009 generate simple patch for configured client and dependences 1022 generate simple patch for configured client and dependences
1010 """) 1023 """)
1011 def CMDpack(parser, args): 1024 def CMDpack(parser, args):
1012 """Generate a patch which can be applied at the root of the tree. 1025 """Generate a patch which can be applied at the root of the tree.
(...skipping 295 matching lines...) Expand 10 before | Expand all | Expand 10 after
1308 except (gclient_utils.Error, subprocess2.CalledProcessError), e: 1321 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
1309 print >> sys.stderr, 'Error: %s' % str(e) 1322 print >> sys.stderr, 'Error: %s' % str(e)
1310 return 1 1323 return 1
1311 1324
1312 1325
1313 if '__main__' == __name__: 1326 if '__main__' == __name__:
1314 fix_encoding.fix_encoding() 1327 fix_encoding.fix_encoding()
1315 sys.exit(Main(sys.argv[1:])) 1328 sys.exit(Main(sys.argv[1:]))
1316 1329
1317 # vim: ts=2:sw=2:tw=80:et: 1330 # vim: ts=2:sw=2:tw=80:et:
OLDNEW
« no previous file with comments | « no previous file | gclient_scm.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698