OLD | NEW |
---|---|
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 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
131 """Implements the Var syntax.""" | 131 """Implements the Var syntax.""" |
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 DEPS_FILE = 'DEPS' | |
142 | 141 |
143 def __init__(self, parent, name, url, safesync_url, custom_deps, | 142 def __init__(self, parent, name, url, safesync_url, custom_deps, |
144 custom_vars, deps_file, should_process): | 143 custom_vars, deps_file, should_process): |
145 GClientKeywords.__init__(self) | 144 GClientKeywords.__init__(self) |
146 gclient_utils.WorkItem.__init__(self) | 145 gclient_utils.WorkItem.__init__(self) |
147 self.parent = parent | 146 self.parent = parent |
148 self.name = name | 147 self.name = name |
149 self.url = url | 148 self.url = url |
150 self.parsed_url = None | 149 self.parsed_url = None |
151 # 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. |
152 self.safesync_url = safesync_url | 151 self.safesync_url = safesync_url |
153 self.custom_vars = custom_vars or {} | 152 self.custom_vars = custom_vars or {} |
154 self.custom_deps = custom_deps or {} | 153 self.custom_deps = custom_deps or {} |
155 self.deps_hooks = [] | 154 self.deps_hooks = [] |
156 self.dependencies = [] | 155 self.dependencies = [] |
157 self.deps_file = deps_file or self.DEPS_FILE | 156 self.deps_file = deps_file |
158 # A cache of the files affected by the current operation, necessary for | 157 # A cache of the files affected by the current operation, necessary for |
159 # hooks. | 158 # hooks. |
160 self._file_list = [] | 159 self._file_list = [] |
161 # If it is not set to True, the dependency wasn't processed for its child | 160 # If it is not set to True, the dependency wasn't processed for its child |
162 # dependency, i.e. its DEPS wasn't read. | 161 # dependency, i.e. its DEPS wasn't read. |
163 self.deps_parsed = False | 162 self.deps_parsed = False |
164 # This dependency should be processed, i.e. checked out | 163 # This dependency should be processed, i.e. checked out |
165 self.should_process = should_process | 164 self.should_process = should_process |
166 # This dependency has been processed, i.e. checked out | 165 # This dependency has been processed, i.e. checked out |
167 self.processed = False | 166 self.processed = False |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
259 local_scope = {} | 258 local_scope = {} |
260 var = self.VarImpl(self.custom_vars, local_scope) | 259 var = self.VarImpl(self.custom_vars, local_scope) |
261 global_scope = { | 260 global_scope = { |
262 'File': self.FileImpl, | 261 'File': self.FileImpl, |
263 'From': self.FromImpl, | 262 'From': self.FromImpl, |
264 'Var': var.Lookup, | 263 'Var': var.Lookup, |
265 'deps_os': {}, | 264 'deps_os': {}, |
266 } | 265 } |
267 filepath = os.path.join(self.root_dir(), self.name, self.deps_file) | 266 filepath = os.path.join(self.root_dir(), self.name, self.deps_file) |
268 if not os.path.isfile(filepath): | 267 if not os.path.isfile(filepath): |
269 logging.info('%s: No DEPS file found at %s' % (self.name, filepath)) | 268 logging.info('%s: No %s file found at %s' % (self.name, self.deps_file, |
269 filepath)) | |
270 else: | 270 else: |
271 deps_content = gclient_utils.FileRead(filepath) | 271 deps_content = gclient_utils.FileRead(filepath) |
272 logging.debug(deps_content) | 272 logging.debug(deps_content) |
273 # Eval the content. | 273 # Eval the content. |
274 try: | 274 try: |
275 exec(deps_content, global_scope, local_scope) | 275 exec(deps_content, global_scope, local_scope) |
276 except SyntaxError, e: | 276 except SyntaxError, e: |
277 gclient_utils.SyntaxErrorToError(filepath, e) | 277 gclient_utils.SyntaxErrorToError(filepath, e) |
278 deps = local_scope.get('deps', {}) | 278 deps = local_scope.get('deps', {}) |
279 # load os specific dependencies if defined. these dependencies may | 279 # load os specific dependencies if defined. these dependencies may |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
324 logging.info('Won\'t process duplicate dependency %s' % tree[name]) | 324 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 | 325 # In theory we could keep it as a shadow of the other one. In |
326 # practice, simply ignore it. | 326 # practice, simply ignore it. |
327 #should_process = False | 327 #should_process = False |
328 continue | 328 continue |
329 else: | 329 else: |
330 raise gclient_utils.Error( | 330 raise gclient_utils.Error( |
331 'Dependency %s specified more than once:\n %s\nvs\n %s' % | 331 'Dependency %s specified more than once:\n %s\nvs\n %s' % |
332 (name, tree[name].hierarchy(), self.hierarchy())) | 332 (name, tree[name].hierarchy(), self.hierarchy())) |
333 self.dependencies.append(Dependency(self, name, url, None, None, None, | 333 self.dependencies.append(Dependency(self, name, url, None, None, None, |
334 None, should_process)) | 334 self.deps_file, should_process)) |
335 logging.debug('Loaded: %s' % str(self)) | 335 logging.debug('Loaded: %s' % str(self)) |
336 | 336 |
337 # Arguments number differs from overridden method | 337 # Arguments number differs from overridden method |
338 # pylint: disable=W0221 | 338 # pylint: disable=W0221 |
339 def run(self, revision_overrides, command, args, work_queue, options): | 339 def run(self, revision_overrides, command, args, work_queue, options): |
340 """Runs 'command' before parsing the DEPS in case it's a initial checkout | 340 """Runs 'command' before parsing the DEPS in case it's a initial checkout |
341 or a revert.""" | 341 or a revert.""" |
342 | 342 |
343 def maybeGetParentRevision(options): | 343 def maybeGetParentRevision(options): |
344 """If we are performing an update and --transitive is set, set the | 344 """If we are performing an update and --transitive is set, set the |
(...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
578 "mac": "mac", | 578 "mac": "mac", |
579 "unix": "unix", | 579 "unix": "unix", |
580 "linux": "unix", | 580 "linux": "unix", |
581 "linux2": "unix", | 581 "linux2": "unix", |
582 } | 582 } |
583 | 583 |
584 DEFAULT_CLIENT_FILE_TEXT = ("""\ | 584 DEFAULT_CLIENT_FILE_TEXT = ("""\ |
585 solutions = [ | 585 solutions = [ |
586 { "name" : "%(solution_name)s", | 586 { "name" : "%(solution_name)s", |
587 "url" : "%(solution_url)s", | 587 "url" : "%(solution_url)s", |
588 "deps_file" : "%(deps_file)s", | |
588 "custom_deps" : { | 589 "custom_deps" : { |
589 }, | 590 }, |
590 "safesync_url": "%(safesync_url)s", | 591 "safesync_url": "%(safesync_url)s", |
591 }, | 592 }, |
592 ] | 593 ] |
593 """) | 594 """) |
594 | 595 |
595 DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\ | 596 DEFAULT_SNAPSHOT_SOLUTION_TEXT = ("""\ |
596 { "name" : "%(solution_name)s", | 597 { "name" : "%(solution_name)s", |
597 "url" : "%(solution_url)s", | 598 "url" : "%(solution_url)s", |
599 "deps_file" : "%(deps_file)s", | |
598 "custom_deps" : { | 600 "custom_deps" : { |
599 %(solution_deps)s }, | 601 %(solution_deps)s }, |
600 "safesync_url": "%(safesync_url)s", | 602 "safesync_url": "%(safesync_url)s", |
601 }, | 603 }, |
602 """) | 604 """) |
603 | 605 |
604 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\ | 606 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\ |
605 # Snapshot generated with gclient revinfo --snapshot | 607 # Snapshot generated with gclient revinfo --snapshot |
606 solutions = [ | 608 solutions = [ |
607 %(solution_list)s] | 609 %(solution_list)s] |
608 """) | 610 """) |
609 | 611 |
610 def __init__(self, root_dir, options): | 612 def __init__(self, root_dir, options): |
611 # Do not change previous behavior. Only solution level and immediate DEPS | 613 # Do not change previous behavior. Only solution level and immediate DEPS |
612 # are processed. | 614 # are processed. |
613 self._recursion_limit = 2 | 615 self._recursion_limit = 2 |
614 Dependency.__init__(self, None, None, None, None, None, None, None, True) | 616 Dependency.__init__(self, None, None, None, None, None, None, 'unused', |
617 True) | |
615 self._options = options | 618 self._options = options |
616 if options.deps_os: | 619 if options.deps_os: |
617 enforced_os = options.deps_os.split(',') | 620 enforced_os = options.deps_os.split(',') |
618 else: | 621 else: |
619 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')] | 622 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')] |
620 if 'all' in enforced_os: | 623 if 'all' in enforced_os: |
621 enforced_os = self.DEPS_OS_CHOICES.itervalues() | 624 enforced_os = self.DEPS_OS_CHOICES.itervalues() |
622 self._enforced_os = list(set(enforced_os)) | 625 self._enforced_os = list(set(enforced_os)) |
623 self._root_dir = root_dir | 626 self._root_dir = root_dir |
624 self.config_content = None | 627 self.config_content = None |
(...skipping 10 matching lines...) Expand all Loading... | |
635 try: | 638 try: |
636 tree = dict((d.name, d) for d in self.tree(False)) | 639 tree = dict((d.name, d) for d in self.tree(False)) |
637 if s['name'] in tree: | 640 if s['name'] in tree: |
638 raise gclient_utils.Error( | 641 raise gclient_utils.Error( |
639 'Dependency %s specified more than once in .gclient' % s['name']) | 642 'Dependency %s specified more than once in .gclient' % s['name']) |
640 self.dependencies.append(Dependency( | 643 self.dependencies.append(Dependency( |
641 self, s['name'], s['url'], | 644 self, s['name'], s['url'], |
642 s.get('safesync_url', None), | 645 s.get('safesync_url', None), |
643 s.get('custom_deps', {}), | 646 s.get('custom_deps', {}), |
644 s.get('custom_vars', {}), | 647 s.get('custom_vars', {}), |
645 None, | 648 s.get('deps_file', 'DEPS'), |
M-A Ruel
2011/05/28 22:58:23
self.deps_file?
| |
646 True)) | 649 True)) |
647 except KeyError: | 650 except KeyError: |
648 raise gclient_utils.Error('Invalid .gclient file. Solution is ' | 651 raise gclient_utils.Error('Invalid .gclient file. Solution is ' |
649 'incomplete: %s' % s) | 652 'incomplete: %s' % s) |
650 # .gclient can have hooks. | 653 # .gclient can have hooks. |
651 self.deps_hooks = config_dict.get('hooks', []) | 654 self.deps_hooks = config_dict.get('hooks', []) |
652 self.deps_parsed = True | 655 self.deps_parsed = True |
653 | 656 |
654 def SaveConfig(self): | 657 def SaveConfig(self): |
655 gclient_utils.FileWrite(os.path.join(self.root_dir(), | 658 gclient_utils.FileWrite(os.path.join(self.root_dir(), |
656 self._options.config_filename), | 659 self._options.config_filename), |
657 self.config_content) | 660 self.config_content) |
658 | 661 |
659 @staticmethod | 662 @staticmethod |
660 def LoadCurrentConfig(options): | 663 def LoadCurrentConfig(options): |
661 """Searches for and loads a .gclient file relative to the current working | 664 """Searches for and loads a .gclient file relative to the current working |
662 dir. Returns a GClient object.""" | 665 dir. Returns a GClient object.""" |
663 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename) | 666 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename) |
664 if not path: | 667 if not path: |
665 return None | 668 return None |
666 client = GClient(path, options) | 669 client = GClient(path, options) |
667 client.SetConfig(gclient_utils.FileRead( | 670 client.SetConfig(gclient_utils.FileRead( |
668 os.path.join(path, options.config_filename))) | 671 os.path.join(path, options.config_filename))) |
669 return client | 672 return client |
670 | 673 |
671 def SetDefaultConfig(self, solution_name, solution_url, safesync_url): | 674 def SetDefaultConfig(self, solution_name, deps_file, solution_url, |
675 safesync_url): | |
672 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % { | 676 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % { |
673 'solution_name': solution_name, | 677 'solution_name': solution_name, |
674 'solution_url': solution_url, | 678 'solution_url': solution_url, |
679 'deps_file': deps_file, | |
675 'safesync_url' : safesync_url, | 680 'safesync_url' : safesync_url, |
676 }) | 681 }) |
677 | 682 |
678 def _SaveEntries(self): | 683 def _SaveEntries(self): |
679 """Creates a .gclient_entries file to record the list of unique checkouts. | 684 """Creates a .gclient_entries file to record the list of unique checkouts. |
680 | 685 |
681 The .gclient_entries file lives in the same directory as .gclient. | 686 The .gclient_entries file lives in the same directory as .gclient. |
682 """ | 687 """ |
683 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It | 688 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It |
684 # makes testing a bit too fun. | 689 # makes testing a bit too fun. |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
839 custom_deps = [] | 844 custom_deps = [] |
840 for k in sorted(entries.keys()): | 845 for k in sorted(entries.keys()): |
841 if entries[k]: | 846 if entries[k]: |
842 # Quotes aren't escaped... | 847 # Quotes aren't escaped... |
843 custom_deps.append(' \"%s\": \'%s\',\n' % (k, entries[k])) | 848 custom_deps.append(' \"%s\": \'%s\',\n' % (k, entries[k])) |
844 else: | 849 else: |
845 custom_deps.append(' \"%s\": None,\n' % k) | 850 custom_deps.append(' \"%s\": None,\n' % k) |
846 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % { | 851 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % { |
847 'solution_name': d.name, | 852 'solution_name': d.name, |
848 'solution_url': d.url, | 853 'solution_url': d.url, |
854 'deps_file': d.deps_file, | |
849 'safesync_url' : d.safesync_url or '', | 855 'safesync_url' : d.safesync_url or '', |
850 'solution_deps': ''.join(custom_deps), | 856 'solution_deps': ''.join(custom_deps), |
851 } | 857 } |
852 # Print the snapshot configuration file | 858 # Print the snapshot configuration file |
853 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient}) | 859 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient}) |
854 else: | 860 else: |
855 entries = {} | 861 entries = {} |
856 for d in self.tree(False): | 862 for d in self.tree(False): |
857 if self._options.actual: | 863 if self._options.actual: |
858 entries[d.name] = GetURLAndRev(d) | 864 entries[d.name] = GetURLAndRev(d) |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
957 modules to operate on as well. If optional [url] parameter is | 963 modules to operate on as well. If optional [url] parameter is |
958 provided, then configuration is read from a specified Subversion server | 964 provided, then configuration is read from a specified Subversion server |
959 URL. | 965 URL. |
960 """ | 966 """ |
961 parser.add_option('--spec', | 967 parser.add_option('--spec', |
962 help='create a gclient file containing the provided ' | 968 help='create a gclient file containing the provided ' |
963 'string. Due to Cygwin/Python brokenness, it ' | 969 'string. Due to Cygwin/Python brokenness, it ' |
964 'probably can\'t contain any newlines.') | 970 'probably can\'t contain any newlines.') |
965 parser.add_option('--name', | 971 parser.add_option('--name', |
966 help='overrides the default name for the solution') | 972 help='overrides the default name for the solution') |
973 parser.add_option('--deps-file', default='DEPS', | |
974 help='overrides the default name for the DEPS file for the' | |
975 'main solutions and all sub-dependencies') | |
976 parser.add_option('--git-deps', action='store_true', | |
977 help='sets the deps file to ".DEPS.git" instead of "DEPS"') | |
967 (options, args) = parser.parse_args(args) | 978 (options, args) = parser.parse_args(args) |
968 if ((options.spec and args) or len(args) > 2 or | 979 if ((options.spec and args) or len(args) > 2 or |
969 (not options.spec and not args)): | 980 (not options.spec and not args)): |
970 parser.error('Inconsistent arguments. Use either --spec or one or 2 args') | 981 parser.error('Inconsistent arguments. Use either --spec or one or 2 args') |
971 | 982 |
972 if os.path.exists(options.config_filename): | 983 if os.path.exists(options.config_filename): |
973 raise gclient_utils.Error('%s file already exists in the current directory' | 984 raise gclient_utils.Error('%s file already exists in the current directory' |
974 % options.config_filename) | 985 % options.config_filename) |
975 client = GClient('.', options) | 986 client = GClient('.', options) |
976 if options.spec: | 987 if options.spec: |
977 client.SetConfig(options.spec) | 988 client.SetConfig(options.spec) |
978 else: | 989 else: |
979 base_url = args[0].rstrip('/') | 990 base_url = args[0].rstrip('/') |
980 if not options.name: | 991 if not options.name: |
981 name = base_url.split('/')[-1] | 992 name = base_url.split('/')[-1] |
982 else: | 993 else: |
983 # specify an alternate relpath for the given URL. | 994 # specify an alternate relpath for the given URL. |
984 name = options.name | 995 name = options.name |
996 deps_file = options.deps_file | |
997 if options.git_deps: | |
998 deps_file = '.DEPS.git' | |
985 safesync_url = '' | 999 safesync_url = '' |
986 if len(args) > 1: | 1000 if len(args) > 1: |
987 safesync_url = args[1] | 1001 safesync_url = args[1] |
988 client.SetDefaultConfig(name, base_url, safesync_url) | 1002 client.SetDefaultConfig(name, deps_file, base_url, safesync_url) |
989 client.SaveConfig() | 1003 client.SaveConfig() |
990 return 0 | 1004 return 0 |
991 | 1005 |
992 | 1006 |
993 @attr('epilog', """Example: | 1007 @attr('epilog', """Example: |
994 gclient pack > patch.txt | 1008 gclient pack > patch.txt |
995 generate simple patch for configured client and dependences | 1009 generate simple patch for configured client and dependences |
996 """) | 1010 """) |
997 def CMDpack(parser, args): | 1011 def CMDpack(parser, args): |
998 """Generate a patch which can be applied at the root of the tree. | 1012 """Generate a patch which can be applied at the root of the tree. |
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1285 except (gclient_utils.Error, subprocess2.CalledProcessError), e: | 1299 except (gclient_utils.Error, subprocess2.CalledProcessError), e: |
1286 print >> sys.stderr, 'Error: %s' % str(e) | 1300 print >> sys.stderr, 'Error: %s' % str(e) |
1287 return 1 | 1301 return 1 |
1288 | 1302 |
1289 | 1303 |
1290 if '__main__' == __name__: | 1304 if '__main__' == __name__: |
1291 fix_encoding.fix_encoding() | 1305 fix_encoding.fix_encoding() |
1292 sys.exit(Main(sys.argv[1:])) | 1306 sys.exit(Main(sys.argv[1:])) |
1293 | 1307 |
1294 # vim: ts=2:sw=2:tw=80:et: | 1308 # vim: ts=2:sw=2:tw=80:et: |
OLD | NEW |