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 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
156 # This is in both .gclient and DEPS files: | 156 # This is in both .gclient and DEPS files: |
157 self.url = url | 157 self.url = url |
158 | 158 |
159 # These are only set in .gclient and not in DEPS files. | 159 # These are only set in .gclient and not in DEPS files. |
160 # 'managed' determines whether or not this dependency is synced/updated by | 160 # 'managed' determines whether or not this dependency is synced/updated by |
161 # gclient after gclient checks it out initially. The difference between | 161 # gclient after gclient checks it out initially. The difference between |
162 # 'managed' and 'should_process' (defined below) is that the user specifies | 162 # 'managed' and 'should_process' (defined below) is that the user specifies |
163 # 'managed' via the --unmanaged command-line flag or a .gclient config, | 163 # 'managed' via the --unmanaged command-line flag or a .gclient config, |
164 # where 'should_process' is dynamically set by gclient if it goes over its | 164 # where 'should_process' is dynamically set by gclient if it goes over its |
165 # recursion limit and controls gclient's behavior so it does not misbehave. | 165 # recursion limit and controls gclient's behavior so it does not misbehave. |
166 self.managed = managed | 166 self._managed = managed |
167 self.custom_vars = custom_vars or {} | 167 self._custom_vars = custom_vars or {} |
168 self.custom_deps = custom_deps or {} | 168 self._custom_deps = custom_deps or {} |
169 self.deps_hooks = [] | 169 self.deps_hooks = [] |
170 | 170 |
171 # Calculates properties: | 171 # Calculates properties: |
172 self.parsed_url = None | 172 self.parsed_url = None |
173 self.dependencies = [] | 173 self.dependencies = [] |
174 # A cache of the files affected by the current operation, necessary for | 174 # A cache of the files affected by the current operation, necessary for |
175 # hooks. | 175 # hooks. |
176 self._file_list = [] | 176 self._file_list = [] |
177 # If it is not set to True, the dependency wasn't processed for its child | 177 # If it is not set to True, the dependency wasn't processed for its child |
178 # dependency, i.e. its DEPS wasn't read. | 178 # dependency, i.e. its DEPS wasn't read. |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
219 # thus unsorted, while the .gclient format is a list thus sorted. | 219 # thus unsorted, while the .gclient format is a list thus sorted. |
220 # | 220 # |
221 # * _recursion_limit is hard coded 2 and there is no hope to change this | 221 # * _recursion_limit is hard coded 2 and there is no hope to change this |
222 # value. | 222 # value. |
223 # | 223 # |
224 # Interestingly enough, the following condition only works in the case we | 224 # Interestingly enough, the following condition only works in the case we |
225 # want: self is a 2nd level node. 3nd level node wouldn't need this since | 225 # want: self is a 2nd level node. 3nd level node wouldn't need this since |
226 # they already have their parent as a requirement. | 226 # they already have their parent as a requirement. |
227 root_deps = self.root.dependencies | 227 root_deps = self.root.dependencies |
228 if self.parent in root_deps: | 228 if self.parent in root_deps: |
229 for i in range(0, root_deps.index(self.parent)): | 229 for i in root_deps: |
230 value = root_deps[i] | 230 if i is self.parent: |
231 if value.name: | 231 break |
232 self._requirements.add(value.name) | 232 if i.name: |
| 233 self._requirements.add(i.name) |
233 | 234 |
234 if isinstance(self.url, self.FromImpl): | 235 if isinstance(self.url, self.FromImpl): |
235 self._requirements.add(self.url.module_name) | 236 self._requirements.add(self.url.module_name) |
236 | 237 |
237 if self.name and self.should_process: | 238 if self.name and self.should_process: |
238 def yield_full_tree(root): | 239 def yield_full_tree(root): |
239 """Depth-first recursion.""" | 240 """Depth-first recursion.""" |
240 yield root | 241 yield root |
241 for i in root.dependencies: | 242 for i in root.dependencies: |
242 for j in yield_full_tree(i): | 243 for j in yield_full_tree(i): |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
350 logging.debug(deps_content) | 351 logging.debug(deps_content) |
351 # Eval the content. | 352 # Eval the content. |
352 try: | 353 try: |
353 exec(deps_content, global_scope, local_scope) | 354 exec(deps_content, global_scope, local_scope) |
354 except SyntaxError, e: | 355 except SyntaxError, e: |
355 gclient_utils.SyntaxErrorToError(filepath, e) | 356 gclient_utils.SyntaxErrorToError(filepath, e) |
356 deps = local_scope.get('deps', {}) | 357 deps = local_scope.get('deps', {}) |
357 # load os specific dependencies if defined. these dependencies may | 358 # load os specific dependencies if defined. these dependencies may |
358 # override or extend the values defined by the 'deps' member. | 359 # override or extend the values defined by the 'deps' member. |
359 if 'deps_os' in local_scope: | 360 if 'deps_os' in local_scope: |
360 for deps_os_key in self.root.enforced_os: | 361 enforced_os = self.root.enforced_os |
| 362 for deps_os_key in enforced_os: |
361 os_deps = local_scope['deps_os'].get(deps_os_key, {}) | 363 os_deps = local_scope['deps_os'].get(deps_os_key, {}) |
362 if len(self.root.enforced_os) > 1: | 364 if len(enforced_os) > 1: |
363 # Ignore any conflict when including deps for more than one | 365 # Ignore any conflict when including deps for more than one |
364 # platform, so we collect the broadest set of dependencies | 366 # platform, so we collect the broadest set of dependencies |
365 # available. We may end up with the wrong revision of something for | 367 # available. We may end up with the wrong revision of something for |
366 # our platform, but this is the best we can do. | 368 # our platform, but this is the best we can do. |
367 deps.update([x for x in os_deps.items() if not x[0] in deps]) | 369 deps.update([x for x in os_deps.items() if not x[0] in deps]) |
368 else: | 370 else: |
369 deps.update(os_deps) | 371 deps.update(os_deps) |
370 | 372 |
371 self.deps_hooks.extend(local_scope.get('hooks', [])) | 373 self.deps_hooks.extend(local_scope.get('hooks', [])) |
372 | 374 |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
559 except (gclient_utils.Error, subprocess2.CalledProcessError), e: | 561 except (gclient_utils.Error, subprocess2.CalledProcessError), e: |
560 # Use a discrete exit status code of 2 to indicate that a hook action | 562 # Use a discrete exit status code of 2 to indicate that a hook action |
561 # failed. Users of this script may wish to treat hook action failures | 563 # failed. Users of this script may wish to treat hook action failures |
562 # differently from VC failures. | 564 # differently from VC failures. |
563 print >> sys.stderr, 'Error: %s' % str(e) | 565 print >> sys.stderr, 'Error: %s' % str(e) |
564 sys.exit(2) | 566 sys.exit(2) |
565 | 567 |
566 def subtree(self, include_all): | 568 def subtree(self, include_all): |
567 """Breadth first""" | 569 """Breadth first""" |
568 result = [] | 570 result = [] |
569 for d in self.dependencies: | 571 dependencies = self.dependencies |
| 572 for d in dependencies: |
570 if d.should_process or include_all: | 573 if d.should_process or include_all: |
571 result.append(d) | 574 result.append(d) |
572 for d in self.dependencies: | 575 for d in dependencies: |
573 result.extend(d.subtree(include_all)) | 576 result.extend(d.subtree(include_all)) |
574 return result | 577 return result |
575 | 578 |
576 def get_custom_deps(self, name, url): | 579 def get_custom_deps(self, name, url): |
577 """Returns a custom deps if applicable.""" | 580 """Returns a custom deps if applicable.""" |
578 if self.parent: | 581 if self.parent: |
579 url = self.parent.get_custom_deps(name, url) | 582 url = self.parent.get_custom_deps(name, url) |
580 # None is a valid return value to disable a dependency. | 583 # None is a valid return value to disable a dependency. |
581 return self.custom_deps.get(name, url) | 584 return self.custom_deps.get(name, url) |
582 | 585 |
583 @property | 586 @property |
584 def recursion_limit(self): | 587 def recursion_limit(self): |
585 """Returns > 0 if this dependency is not too recursed to be processed.""" | 588 """Returns > 0 if this dependency is not too recursed to be processed. |
| 589 |
| 590 Immutable so no need to lock.""" |
586 return max(self.parent.recursion_limit - 1, 0) | 591 return max(self.parent.recursion_limit - 1, 0) |
587 | 592 |
588 @property | 593 @property |
589 def deps_file(self): | 594 def deps_file(self): |
| 595 """Immutable so no need to lock.""" |
590 return self._deps_file | 596 return self._deps_file |
591 | 597 |
592 @property | 598 @property |
| 599 def managed(self): |
| 600 """Immutable so no need to lock.""" |
| 601 return self._managed |
| 602 |
| 603 @property |
593 def safesync_url(self): | 604 def safesync_url(self): |
| 605 """Immutable so no need to lock.""" |
594 return self._safesync_url | 606 return self._safesync_url |
595 | 607 |
596 @property | 608 @property |
597 def should_process(self): | 609 def should_process(self): |
598 """True if this dependency should be processed, i.e. checked out.""" | 610 """True if this dependency should be processed, i.e. checked out.""" |
599 return self._should_process | 611 return self._should_process |
600 | 612 |
601 @property | 613 @property |
602 def parent(self): | 614 def parent(self): |
| 615 """Immutable so no need to lock.""" |
603 return self._parent | 616 return self._parent |
604 | 617 |
605 @property | 618 @property |
| 619 def custom_vars(self): |
| 620 """Immutable so no need to lock.""" |
| 621 return self._custom_vars.copy() |
| 622 |
| 623 @property |
| 624 def custom_deps(self): |
| 625 """Immutable so no need to lock.""" |
| 626 return self._custom_deps.copy() |
| 627 |
| 628 @property |
| 629 @gclient_utils.lockedmethod |
606 def file_list(self): | 630 def file_list(self): |
607 result = self._file_list[:] | 631 result = self._file_list[:] |
608 for d in self.dependencies: | 632 for d in self.dependencies: |
609 result.extend(d.file_list) | 633 result.extend(d.file_list) |
610 return tuple(result) | 634 return tuple(result) |
611 | 635 |
612 def __str__(self): | 636 def __str__(self): |
613 out = [] | 637 out = [] |
614 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps', | 638 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps', |
615 'custom_vars', 'deps_hooks', 'file_list', 'should_process', | 639 'custom_vars', 'deps_hooks', 'file_list', 'should_process', |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
705 enforced_os = options.deps_os.split(',') | 729 enforced_os = options.deps_os.split(',') |
706 else: | 730 else: |
707 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')] | 731 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')] |
708 if 'all' in enforced_os: | 732 if 'all' in enforced_os: |
709 enforced_os = self.DEPS_OS_CHOICES.itervalues() | 733 enforced_os = self.DEPS_OS_CHOICES.itervalues() |
710 self._enforced_os = tuple(set(enforced_os)) | 734 self._enforced_os = tuple(set(enforced_os)) |
711 self._root_dir = root_dir | 735 self._root_dir = root_dir |
712 self.config_content = None | 736 self.config_content = None |
713 | 737 |
714 def SetConfig(self, content): | 738 def SetConfig(self, content): |
715 assert self.dependencies == [] | 739 assert not self.dependencies |
716 config_dict = {} | 740 config_dict = {} |
717 self.config_content = content | 741 self.config_content = content |
718 try: | 742 try: |
719 exec(content, config_dict) | 743 exec(content, config_dict) |
720 except SyntaxError, e: | 744 except SyntaxError, e: |
721 gclient_utils.SyntaxErrorToError('.gclient', e) | 745 gclient_utils.SyntaxErrorToError('.gclient', e) |
722 for s in config_dict.get('solutions', []): | 746 for s in config_dict.get('solutions', []): |
723 try: | 747 try: |
724 tree = dict((d.name, d) for d in self.root.subtree(False)) | 748 tree = dict((d.name, d) for d in self.root.subtree(False)) |
725 if s['name'] in tree: | 749 if s['name'] in tree: |
(...skipping 675 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1401 except (gclient_utils.Error, subprocess2.CalledProcessError), e: | 1425 except (gclient_utils.Error, subprocess2.CalledProcessError), e: |
1402 print >> sys.stderr, 'Error: %s' % str(e) | 1426 print >> sys.stderr, 'Error: %s' % str(e) |
1403 return 1 | 1427 return 1 |
1404 | 1428 |
1405 | 1429 |
1406 if '__main__' == __name__: | 1430 if '__main__' == __name__: |
1407 fix_encoding.fix_encoding() | 1431 fix_encoding.fix_encoding() |
1408 sys.exit(Main(sys.argv[1:])) | 1432 sys.exit(Main(sys.argv[1:])) |
1409 | 1433 |
1410 # vim: ts=2:sw=2:tw=80:et: | 1434 # vim: ts=2:sw=2:tw=80:et: |
OLD | NEW |