| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 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 280 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 291 # A cache of the files affected by the current operation, necessary for | 291 # A cache of the files affected by the current operation, necessary for |
| 292 # hooks. | 292 # hooks. |
| 293 self._file_list = [] | 293 self._file_list = [] |
| 294 # If it is not set to True, the dependency wasn't processed for its child | 294 # If it is not set to True, the dependency wasn't processed for its child |
| 295 # dependency, i.e. its DEPS wasn't read. | 295 # dependency, i.e. its DEPS wasn't read. |
| 296 self._deps_parsed = False | 296 self._deps_parsed = False |
| 297 # This dependency has been processed, i.e. checked out | 297 # This dependency has been processed, i.e. checked out |
| 298 self._processed = False | 298 self._processed = False |
| 299 # This dependency had its hook run | 299 # This dependency had its hook run |
| 300 self._hooks_ran = False | 300 self._hooks_ran = False |
| 301 # This is the scm used to checkout self.url. It may be used by dependencies |
| 302 # to get the datetime of the revision we checked out. |
| 303 self._used_scm = None |
| 301 | 304 |
| 302 if not self.name and self.parent: | 305 if not self.name and self.parent: |
| 303 raise gclient_utils.Error('Dependency without name') | 306 raise gclient_utils.Error('Dependency without name') |
| 304 | 307 |
| 305 @property | 308 @property |
| 306 def requirements(self): | 309 def requirements(self): |
| 307 """Calculate the list of requirements.""" | 310 """Calculate the list of requirements.""" |
| 308 requirements = set() | 311 requirements = set() |
| 309 # self.parent is implicitly a requirement. This will be recursive by | 312 # self.parent is implicitly a requirement. This will be recursive by |
| 310 # definition. | 313 # definition. |
| (...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 531 self.add_dependencies_and_close(deps_to_add, local_scope.get('hooks', [])) | 534 self.add_dependencies_and_close(deps_to_add, local_scope.get('hooks', [])) |
| 532 logging.info('ParseDepsFile(%s) done' % self.name) | 535 logging.info('ParseDepsFile(%s) done' % self.name) |
| 533 | 536 |
| 534 def add_dependencies_and_close(self, deps_to_add, hooks): | 537 def add_dependencies_and_close(self, deps_to_add, hooks): |
| 535 """Adds the dependencies, hooks and mark the parsing as done.""" | 538 """Adds the dependencies, hooks and mark the parsing as done.""" |
| 536 for dep in deps_to_add: | 539 for dep in deps_to_add: |
| 537 if dep.verify_validity(): | 540 if dep.verify_validity(): |
| 538 self.add_dependency(dep) | 541 self.add_dependency(dep) |
| 539 self._mark_as_parsed(hooks) | 542 self._mark_as_parsed(hooks) |
| 540 | 543 |
| 541 @staticmethod | |
| 542 def maybeGetParentRevision( | 544 def maybeGetParentRevision( |
| 543 command, options, parsed_url, parent_name, revision_overrides): | 545 self, command, options, parsed_url, parent_name, revision_overrides): |
| 544 """If we are performing an update and --transitive is set, set the | 546 """Uses revision/timestamp of parent if no explicit revision was specified. |
| 545 revision to the parent's revision. If we have an explicit revision | 547 |
| 546 do nothing.""" | 548 If we are performing an update and --transitive is set, use |
| 549 - the parent's revision if 'self.url' is in the same repository |
| 550 - the parent's timestamp otherwise |
| 551 to update 'self.url'. The used revision/timestamp will be set in |
| 552 'options.revision'. |
| 553 If we have an explicit revision do nothing. |
| 554 """ |
| 547 if command == 'update' and options.transitive and not options.revision: | 555 if command == 'update' and options.transitive and not options.revision: |
| 548 _, revision = gclient_utils.SplitUrlRevision(parsed_url) | 556 _, revision = gclient_utils.SplitUrlRevision(parsed_url) |
| 549 if not revision: | 557 if not revision: |
| 550 options.revision = revision_overrides.get(parent_name) | 558 options.revision = revision_overrides.get(parent_name) |
| 551 if options.verbose and options.revision: | 559 if (options.revision and |
| 552 print("Using parent's revision date: %s" % options.revision) | 560 not gclient_utils.IsDateRevision(options.revision)): |
| 553 # If the parent has a revision override, then it must have been | 561 assert self.parent and self.parent.used_scm |
| 554 # converted to date format. | 562 # If this dependency is in the same repository as parent it's url will |
| 555 assert (not options.revision or | 563 # start with a slash. If so we take the parent revision instead of |
| 556 gclient_utils.IsDateRevision(options.revision)) | 564 # it's timestamp. |
| 557 | 565 # (The timestamps of commits in google code are broken -- which can |
| 558 @staticmethod | 566 # result in dependencies to be checked out at the wrong revision) |
| 559 def maybeConvertToDateRevision( | 567 if self.url.startswith('/'): |
| 560 command, options, name, scm, revision_overrides): | 568 if options.verbose: |
| 561 """If we are performing an update and --transitive is set, convert the | 569 print('Using parent\'s revision %s since we are in the same ' |
| 562 revision to a date-revision (if necessary). Instead of having | 570 'repository.' % options.revision) |
| 563 -r 101 replace the revision with the time stamp of 101 (e.g. | 571 else: |
| 564 "{2011-18-04}"). | 572 parent_revision_date = self.parent.used_scm.GetRevisionDate( |
| 565 This way dependencies are upgraded to the revision they had at the | 573 options.revision) |
| 566 check-in of revision 101.""" | 574 options.revision = gclient_utils.MakeDateRevision( |
| 567 if (command == 'update' and | 575 parent_revision_date) |
| 568 options.transitive and | 576 if options.verbose: |
| 569 options.revision and | 577 print('Using parent\'s revision date %s since we are in a ' |
| 570 not gclient_utils.IsDateRevision(options.revision)): | 578 'different repository.' % options.revision) |
| 571 revision_date = scm.GetRevisionDate(options.revision) | 579 revision_overrides[self.name] = options.revision |
| 572 revision = gclient_utils.MakeDateRevision(revision_date) | |
| 573 if options.verbose: | |
| 574 print("Updating revision override from %s to %s." % | |
| 575 (options.revision, revision)) | |
| 576 revision_overrides[name] = revision | |
| 577 | 580 |
| 578 # Arguments number differs from overridden method | 581 # Arguments number differs from overridden method |
| 579 # pylint: disable=W0221 | 582 # pylint: disable=W0221 |
| 580 def run(self, revision_overrides, command, args, work_queue, options): | 583 def run(self, revision_overrides, command, args, work_queue, options): |
| 581 """Runs |command| then parse the DEPS file.""" | 584 """Runs |command| then parse the DEPS file.""" |
| 582 logging.info('Dependency(%s).run()' % self.name) | 585 logging.info('Dependency(%s).run()' % self.name) |
| 583 assert self._file_list == [] | 586 assert self._file_list == [] |
| 584 if not self.should_process: | 587 if not self.should_process: |
| 585 return | 588 return |
| 586 # When running runhooks, there's no need to consult the SCM. | 589 # When running runhooks, there's no need to consult the SCM. |
| 587 # All known hooks are expected to run unconditionally regardless of working | 590 # All known hooks are expected to run unconditionally regardless of working |
| 588 # copy state, so skip the SCM status check. | 591 # copy state, so skip the SCM status check. |
| 589 run_scm = command not in ('runhooks', 'recurse', None) | 592 run_scm = command not in ('runhooks', 'recurse', None) |
| 590 parsed_url = self.LateOverride(self.url) | 593 parsed_url = self.LateOverride(self.url) |
| 591 file_list = [] | 594 file_list = [] |
| 592 if run_scm and parsed_url: | 595 if run_scm and parsed_url: |
| 593 if isinstance(parsed_url, self.FileImpl): | 596 if isinstance(parsed_url, self.FileImpl): |
| 594 # Special support for single-file checkout. | 597 # Special support for single-file checkout. |
| 595 if not command in (None, 'cleanup', 'diff', 'pack', 'status'): | 598 if not command in (None, 'cleanup', 'diff', 'pack', 'status'): |
| 596 # Sadly, pylint doesn't realize that parsed_url is of FileImpl. | 599 # Sadly, pylint doesn't realize that parsed_url is of FileImpl. |
| 597 # pylint: disable=E1103 | 600 # pylint: disable=E1103 |
| 598 options.revision = parsed_url.GetRevision() | 601 options.revision = parsed_url.GetRevision() |
| 599 scm = gclient_scm.SVNWrapper(parsed_url.GetPath(), | 602 self._used_scm = gclient_scm.SVNWrapper( |
| 600 self.root.root_dir, | 603 parsed_url.GetPath(), self.root.root_dir, self.name) |
| 601 self.name) | 604 self._used_scm.RunCommand('updatesingle', |
| 602 scm.RunCommand('updatesingle', options, | 605 options, args + [parsed_url.GetFilename()], file_list) |
| 603 args + [parsed_url.GetFilename()], | |
| 604 file_list) | |
| 605 else: | 606 else: |
| 606 # Create a shallow copy to mutate revision. | 607 # Create a shallow copy to mutate revision. |
| 607 options = copy.copy(options) | 608 options = copy.copy(options) |
| 608 options.revision = revision_overrides.get(self.name) | 609 options.revision = revision_overrides.get(self.name) |
| 609 self.maybeGetParentRevision( | 610 self.maybeGetParentRevision( |
| 610 command, options, parsed_url, self.parent.name, revision_overrides) | 611 command, options, parsed_url, self.parent.name, revision_overrides) |
| 611 scm = gclient_scm.CreateSCM(parsed_url, self.root.root_dir, self.name) | 612 self._used_scm = gclient_scm.CreateSCM( |
| 612 scm.RunCommand(command, options, args, file_list) | 613 parsed_url, self.root.root_dir, self.name) |
| 613 self.maybeConvertToDateRevision( | 614 self._used_scm.RunCommand(command, options, args, file_list) |
| 614 command, options, self.name, scm, revision_overrides) | |
| 615 file_list = [os.path.join(self.name, f.strip()) for f in file_list] | 615 file_list = [os.path.join(self.name, f.strip()) for f in file_list] |
| 616 | 616 |
| 617 # TODO(phajdan.jr): We should know exactly when the paths are absolute. | 617 # TODO(phajdan.jr): We should know exactly when the paths are absolute. |
| 618 # Convert all absolute paths to relative. | 618 # Convert all absolute paths to relative. |
| 619 for i in range(len(file_list)): | 619 for i in range(len(file_list)): |
| 620 # It depends on the command being executed (like runhooks vs sync). | 620 # It depends on the command being executed (like runhooks vs sync). |
| 621 if not os.path.isabs(file_list[i]): | 621 if not os.path.isabs(file_list[i]): |
| 622 continue | 622 continue |
| 623 prefix = os.path.commonprefix( | 623 prefix = os.path.commonprefix( |
| 624 [self.root.root_dir.lower(), file_list[i].lower()]) | 624 [self.root.root_dir.lower(), file_list[i].lower()]) |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 821 @gclient_utils.lockedmethod | 821 @gclient_utils.lockedmethod |
| 822 def hooks_ran(self): | 822 def hooks_ran(self): |
| 823 return self._hooks_ran | 823 return self._hooks_ran |
| 824 | 824 |
| 825 @property | 825 @property |
| 826 @gclient_utils.lockedmethod | 826 @gclient_utils.lockedmethod |
| 827 def file_list(self): | 827 def file_list(self): |
| 828 return tuple(self._file_list) | 828 return tuple(self._file_list) |
| 829 | 829 |
| 830 @property | 830 @property |
| 831 def used_scm(self): |
| 832 """SCMWrapper instance for this dependency or None if not processed yet.""" |
| 833 return self._used_scm |
| 834 |
| 835 @property |
| 831 def file_list_and_children(self): | 836 def file_list_and_children(self): |
| 832 result = list(self.file_list) | 837 result = list(self.file_list) |
| 833 for d in self.dependencies: | 838 for d in self.dependencies: |
| 834 result.extend(d.file_list_and_children) | 839 result.extend(d.file_list_and_children) |
| 835 return tuple(result) | 840 return tuple(result) |
| 836 | 841 |
| 837 def __str__(self): | 842 def __str__(self): |
| 838 out = [] | 843 out = [] |
| 839 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps', | 844 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps', |
| 840 'custom_vars', 'deps_hooks', 'file_list', 'should_process', | 845 'custom_vars', 'deps_hooks', 'file_list', 'should_process', |
| (...skipping 934 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1775 except (gclient_utils.Error, subprocess2.CalledProcessError), e: | 1780 except (gclient_utils.Error, subprocess2.CalledProcessError), e: |
| 1776 print >> sys.stderr, 'Error: %s' % str(e) | 1781 print >> sys.stderr, 'Error: %s' % str(e) |
| 1777 return 1 | 1782 return 1 |
| 1778 | 1783 |
| 1779 | 1784 |
| 1780 if '__main__' == __name__: | 1785 if '__main__' == __name__: |
| 1781 fix_encoding.fix_encoding() | 1786 fix_encoding.fix_encoding() |
| 1782 sys.exit(Main(sys.argv[1:])) | 1787 sys.exit(Main(sys.argv[1:])) |
| 1783 | 1788 |
| 1784 # vim: ts=2:sw=2:tw=80:et: | 1789 # vim: ts=2:sw=2:tw=80:et: |
| OLD | NEW |