| 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 240 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 251 # hooks. | 251 # hooks. |
| 252 self._file_list = [] | 252 self._file_list = [] |
| 253 # If it is not set to True, the dependency wasn't processed for its child | 253 # If it is not set to True, the dependency wasn't processed for its child |
| 254 # dependency, i.e. its DEPS wasn't read. | 254 # dependency, i.e. its DEPS wasn't read. |
| 255 self._deps_parsed = False | 255 self._deps_parsed = False |
| 256 # This dependency has been processed, i.e. checked out | 256 # This dependency has been processed, i.e. checked out |
| 257 self._processed = False | 257 self._processed = False |
| 258 # This dependency had its hook run | 258 # This dependency had its hook run |
| 259 self._hooks_ran = False | 259 self._hooks_ran = False |
| 260 | 260 |
| 261 # Setup self.requirements and find any other dependency who would have self | 261 if not self.name and self.parent: |
| 262 # as a requirement. | 262 raise gclient_utils.Error('Dependency without name') |
| 263 |
| 264 def setup_requirements(self): |
| 265 """Setup self.requirements and find any other dependency who would have self |
| 266 as a requirement. |
| 267 |
| 268 Returns True if this entry should be added, False if it is a duplicate of |
| 269 another entry. |
| 270 """ |
| 271 if self.name in [s.name for s in self.parent.dependencies]: |
| 272 raise gclient_utils.Error( |
| 273 'The same name "%s" appears multiple times in the deps section' % |
| 274 self.name) |
| 275 if self.should_process: |
| 276 siblings = [d for d in self.root.subtree(False) if d.name == self.name] |
| 277 for sibling in siblings: |
| 278 if self.url != sibling.url: |
| 279 raise gclient_utils.Error( |
| 280 'Dependency %s specified more than once:\n %s\nvs\n %s' % |
| 281 (self.name, sibling.hierarchy(), self.hierarchy())) |
| 282 # In theory we could keep it as a shadow of the other one. In |
| 283 # practice, simply ignore it. |
| 284 logging.warn('Won\'t process duplicate dependency %s' % sibling) |
| 285 return False |
| 263 | 286 |
| 264 # self.parent is implicitly a requirement. This will be recursive by | 287 # self.parent is implicitly a requirement. This will be recursive by |
| 265 # definition. | 288 # definition. |
| 266 if self.parent and self.parent.name: | 289 if self.parent and self.parent.name: |
| 267 self.add_requirement(self.parent.name) | 290 self.add_requirement(self.parent.name) |
| 268 | 291 |
| 269 # For a tree with at least 2 levels*, the leaf node needs to depend | 292 # For a tree with at least 2 levels*, the leaf node needs to depend |
| 270 # on the level higher up in an orderly way. | 293 # on the level higher up in an orderly way. |
| 271 # This becomes messy for >2 depth as the DEPS file format is a dictionary, | 294 # This becomes messy for >2 depth as the DEPS file format is a dictionary, |
| 272 # thus unsorted, while the .gclient format is a list thus sorted. | 295 # thus unsorted, while the .gclient format is a list thus sorted. |
| (...skipping 18 matching lines...) Expand all Loading... |
| 291 if self.name and self.should_process: | 314 if self.name and self.should_process: |
| 292 for obj in self.root.depth_first_tree(): | 315 for obj in self.root.depth_first_tree(): |
| 293 if obj is self or not obj.name: | 316 if obj is self or not obj.name: |
| 294 continue | 317 continue |
| 295 # Step 1: Find any requirements self may need. | 318 # Step 1: Find any requirements self may need. |
| 296 if self.name.startswith(posixpath.join(obj.name, '')): | 319 if self.name.startswith(posixpath.join(obj.name, '')): |
| 297 self.add_requirement(obj.name) | 320 self.add_requirement(obj.name) |
| 298 # Step 2: Find any requirements self may impose. | 321 # Step 2: Find any requirements self may impose. |
| 299 if obj.name.startswith(posixpath.join(self.name, '')): | 322 if obj.name.startswith(posixpath.join(self.name, '')): |
| 300 obj.add_requirement(self.name) | 323 obj.add_requirement(self.name) |
| 301 | 324 return True |
| 302 if not self.name and self.parent: | |
| 303 raise gclient_utils.Error('Dependency without name') | |
| 304 | 325 |
| 305 def LateOverride(self, url): | 326 def LateOverride(self, url): |
| 306 """Resolves the parsed url from url. | 327 """Resolves the parsed url from url. |
| 307 | 328 |
| 308 Manages From() keyword accordingly. Do not touch self.parsed_url nor | 329 Manages From() keyword accordingly. Do not touch self.parsed_url nor |
| 309 self.url because it may called with other urls due to From().""" | 330 self.url because it may called with other urls due to From().""" |
| 310 assert self.parsed_url == None or not self.should_process, self.parsed_url | 331 assert self.parsed_url == None or not self.should_process, self.parsed_url |
| 311 parsed_url = self.get_custom_deps(self.name, url) | 332 parsed_url = self.get_custom_deps(self.name, url) |
| 312 if parsed_url != url: | 333 if parsed_url != url: |
| 313 logging.info('LateOverride(%s, %s) -> %s' % (self.name, url, parsed_url)) | 334 logging.info('LateOverride(%s, %s) -> %s' % (self.name, url, parsed_url)) |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 409 os_deps = local_scope['deps_os'].get(deps_os_key, {}) | 430 os_deps = local_scope['deps_os'].get(deps_os_key, {}) |
| 410 if len(enforced_os) > 1: | 431 if len(enforced_os) > 1: |
| 411 # Ignore any conflict when including deps for more than one | 432 # Ignore any conflict when including deps for more than one |
| 412 # platform, so we collect the broadest set of dependencies | 433 # platform, so we collect the broadest set of dependencies |
| 413 # available. We may end up with the wrong revision of something for | 434 # available. We may end up with the wrong revision of something for |
| 414 # our platform, but this is the best we can do. | 435 # our platform, but this is the best we can do. |
| 415 deps.update([x for x in os_deps.items() if not x[0] in deps]) | 436 deps.update([x for x in os_deps.items() if not x[0] in deps]) |
| 416 else: | 437 else: |
| 417 deps.update(os_deps) | 438 deps.update(os_deps) |
| 418 | 439 |
| 419 self._deps_hooks.extend(local_scope.get('hooks', [])) | |
| 420 | |
| 421 # If a line is in custom_deps, but not in the solution, we want to append | 440 # If a line is in custom_deps, but not in the solution, we want to append |
| 422 # this line to the solution. | 441 # this line to the solution. |
| 423 for d in self.custom_deps: | 442 for d in self.custom_deps: |
| 424 if d not in deps: | 443 if d not in deps: |
| 425 deps[d] = self.custom_deps[d] | 444 deps[d] = self.custom_deps[d] |
| 426 | 445 |
| 427 # If use_relative_paths is set in the DEPS file, regenerate | 446 # If use_relative_paths is set in the DEPS file, regenerate |
| 428 # the dictionary using paths relative to the directory containing | 447 # the dictionary using paths relative to the directory containing |
| 429 # the DEPS file. | 448 # the DEPS file. |
| 430 use_relative_paths = local_scope.get('use_relative_paths', False) | 449 use_relative_paths = local_scope.get('use_relative_paths', False) |
| 431 if use_relative_paths: | 450 if use_relative_paths: |
| 432 rel_deps = {} | 451 rel_deps = {} |
| 433 for d, url in deps.items(): | 452 for d, url in deps.items(): |
| 434 # normpath is required to allow DEPS to use .. in their | 453 # normpath is required to allow DEPS to use .. in their |
| 435 # dependency local path. | 454 # dependency local path. |
| 436 rel_deps[os.path.normpath(os.path.join(self.name, d))] = url | 455 rel_deps[os.path.normpath(os.path.join(self.name, d))] = url |
| 437 deps = rel_deps | 456 deps = rel_deps |
| 438 | 457 |
| 439 # Convert the deps into real Dependency. | 458 # Convert the deps into real Dependency. |
| 459 deps_to_add = [] |
| 440 for name, url in deps.iteritems(): | 460 for name, url in deps.iteritems(): |
| 441 if name in [s.name for s in self._dependencies]: | |
| 442 raise gclient_utils.Error( | |
| 443 'The same name "%s" appears multiple times in the deps section' % | |
| 444 name) | |
| 445 should_process = self.recursion_limit and self.should_process | 461 should_process = self.recursion_limit and self.should_process |
| 446 if should_process: | 462 deps_to_add.append(Dependency( |
| 447 tree = dict((d.name, d) for d in self.root.subtree(False)) | 463 self, name, url, None, None, None, None, |
| 448 if name in tree: | 464 self.deps_file, should_process)) |
| 449 if url == tree[name].url: | 465 self.add_dependencies_and_close(deps_to_add, local_scope.get('hooks', [])) |
| 450 logging.info('Won\'t process duplicate dependency %s' % tree[name]) | 466 logging.info('ParseDepsFile(%s) done' % self.name) |
| 451 # In theory we could keep it as a shadow of the other one. In | 467 |
| 452 # practice, simply ignore it. | 468 def add_dependencies_and_close(self, deps_to_add, hooks): |
| 453 #should_process = False | 469 """Adds the dependencies, hooks and mark the parsing as done.""" |
| 454 continue | 470 for dep in deps_to_add: |
| 455 else: | 471 if dep.setup_requirements(): |
| 456 raise gclient_utils.Error( | 472 self.add_dependency(dep) |
| 457 'Dependency %s specified more than once:\n %s\nvs\n %s' % | 473 self._mark_as_parsed(hooks) |
| 458 (name, tree[name].hierarchy(), self.hierarchy())) | |
| 459 self._dependencies.append( | |
| 460 Dependency( | |
| 461 self, name, url, None, None, None, None, | |
| 462 self.deps_file, should_process)) | |
| 463 self._deps_parsed = True | |
| 464 logging.debug('Loaded: %s' % str(self)) | |
| 465 | 474 |
| 466 # Arguments number differs from overridden method | 475 # Arguments number differs from overridden method |
| 467 # pylint: disable=W0221 | 476 # pylint: disable=W0221 |
| 468 def run(self, revision_overrides, command, args, work_queue, options): | 477 def run(self, revision_overrides, command, args, work_queue, options): |
| 469 """Runs 'command' before parsing the DEPS in case it's a initial checkout | 478 """Runs 'command' before parsing the DEPS in case it's a initial checkout |
| 470 or a revert.""" | 479 or a revert.""" |
| 471 | 480 |
| 472 def maybeGetParentRevision(options): | 481 def maybeGetParentRevision(options): |
| 473 """If we are performing an update and --transitive is set, set the | 482 """If we are performing an update and --transitive is set, set the |
| 474 revision to the parent's revision. If we have an explicit revision | 483 revision to the parent's revision. If we have an explicit revision |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 625 yield i | 634 yield i |
| 626 | 635 |
| 627 def depth_first_tree(self): | 636 def depth_first_tree(self): |
| 628 """Depth-first recursion including the root node.""" | 637 """Depth-first recursion including the root node.""" |
| 629 yield self | 638 yield self |
| 630 for i in self.dependencies: | 639 for i in self.dependencies: |
| 631 for j in i.depth_first_tree(): | 640 for j in i.depth_first_tree(): |
| 632 if j.should_process: | 641 if j.should_process: |
| 633 yield j | 642 yield j |
| 634 | 643 |
| 644 @gclient_utils.lockedmethod |
| 645 def add_dependency(self, new_dep): |
| 646 self._dependencies.append(new_dep) |
| 647 |
| 648 @gclient_utils.lockedmethod |
| 649 def _mark_as_parsed(self, new_hooks): |
| 650 self._deps_hooks.extend(new_hooks) |
| 651 self._deps_parsed = True |
| 652 |
| 635 @property | 653 @property |
| 654 @gclient_utils.lockedmethod |
| 636 def dependencies(self): | 655 def dependencies(self): |
| 637 return tuple(self._dependencies) | 656 return tuple(self._dependencies) |
| 638 | 657 |
| 639 @property | 658 @property |
| 640 def deps_hooks(self): | 659 def deps_hooks(self): |
| 641 return tuple(self._deps_hooks) | 660 return tuple(self._deps_hooks) |
| 642 | 661 |
| 643 @property | 662 @property |
| 644 def parsed_url(self): | 663 def parsed_url(self): |
| 645 return self._parsed_url | 664 return self._parsed_url |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 758 self.config_content = None | 777 self.config_content = None |
| 759 | 778 |
| 760 def SetConfig(self, content): | 779 def SetConfig(self, content): |
| 761 assert not self.dependencies | 780 assert not self.dependencies |
| 762 config_dict = {} | 781 config_dict = {} |
| 763 self.config_content = content | 782 self.config_content = content |
| 764 try: | 783 try: |
| 765 exec(content, config_dict) | 784 exec(content, config_dict) |
| 766 except SyntaxError, e: | 785 except SyntaxError, e: |
| 767 gclient_utils.SyntaxErrorToError('.gclient', e) | 786 gclient_utils.SyntaxErrorToError('.gclient', e) |
| 787 |
| 788 deps_to_add = [] |
| 768 for s in config_dict.get('solutions', []): | 789 for s in config_dict.get('solutions', []): |
| 769 try: | 790 try: |
| 770 tree = dict((d.name, d) for d in self.root.subtree(False)) | 791 deps_to_add.append(Dependency( |
| 771 if s['name'] in tree: | |
| 772 raise gclient_utils.Error( | |
| 773 'Dependency %s specified more than once in .gclient' % s['name']) | |
| 774 self._dependencies.append(Dependency( | |
| 775 self, s['name'], s['url'], | 792 self, s['name'], s['url'], |
| 776 s.get('safesync_url', None), | 793 s.get('safesync_url', None), |
| 777 s.get('managed', True), | 794 s.get('managed', True), |
| 778 s.get('custom_deps', {}), | 795 s.get('custom_deps', {}), |
| 779 s.get('custom_vars', {}), | 796 s.get('custom_vars', {}), |
| 780 s.get('deps_file', 'DEPS'), | 797 s.get('deps_file', 'DEPS'), |
| 781 True)) | 798 True)) |
| 782 except KeyError: | 799 except KeyError: |
| 783 raise gclient_utils.Error('Invalid .gclient file. Solution is ' | 800 raise gclient_utils.Error('Invalid .gclient file. Solution is ' |
| 784 'incomplete: %s' % s) | 801 'incomplete: %s' % s) |
| 785 # .gclient can have hooks. | 802 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', [])) |
| 786 self._deps_hooks = config_dict.get('hooks', []) | 803 logging.info('SetConfig() done') |
| 787 self._deps_parsed = True | |
| 788 | 804 |
| 789 def SaveConfig(self): | 805 def SaveConfig(self): |
| 790 gclient_utils.FileWrite(os.path.join(self.root_dir, | 806 gclient_utils.FileWrite(os.path.join(self.root_dir, |
| 791 self._options.config_filename), | 807 self._options.config_filename), |
| 792 self.config_content) | 808 self.config_content) |
| 793 | 809 |
| 794 @staticmethod | 810 @staticmethod |
| 795 def LoadCurrentConfig(options): | 811 def LoadCurrentConfig(options): |
| 796 """Searches for and loads a .gclient file relative to the current working | 812 """Searches for and loads a .gclient file relative to the current working |
| 797 dir. Returns a GClient object.""" | 813 dir. Returns a GClient object.""" |
| (...skipping 649 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1447 except (gclient_utils.Error, subprocess2.CalledProcessError), e: | 1463 except (gclient_utils.Error, subprocess2.CalledProcessError), e: |
| 1448 print >> sys.stderr, 'Error: %s' % str(e) | 1464 print >> sys.stderr, 'Error: %s' % str(e) |
| 1449 return 1 | 1465 return 1 |
| 1450 | 1466 |
| 1451 | 1467 |
| 1452 if '__main__' == __name__: | 1468 if '__main__' == __name__: |
| 1453 fix_encoding.fix_encoding() | 1469 fix_encoding.fix_encoding() |
| 1454 sys.exit(Main(sys.argv[1:])) | 1470 sys.exit(Main(sys.argv[1:])) |
| 1455 | 1471 |
| 1456 # vim: ts=2:sw=2:tw=80:et: | 1472 # vim: ts=2:sw=2:tw=80:et: |
| OLD | NEW |