| 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 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 339 dep for dep in self.root.subtree(True) if url.module_name == dep.name | 339 dep for dep in self.root.subtree(True) if url.module_name == dep.name |
| 340 ] | 340 ] |
| 341 if not ref: | 341 if not ref: |
| 342 raise gclient_utils.Error('Failed to find one reference to %s. %s' % ( | 342 raise gclient_utils.Error('Failed to find one reference to %s. %s' % ( |
| 343 url.module_name, ref)) | 343 url.module_name, ref)) |
| 344 # It may happen that len(ref) > 1 but it's no big deal. | 344 # It may happen that len(ref) > 1 but it's no big deal. |
| 345 ref = ref[0] | 345 ref = ref[0] |
| 346 sub_target = url.sub_target_name or self.name | 346 sub_target = url.sub_target_name or self.name |
| 347 # Make sure the referenced dependency DEPS file is loaded and file the | 347 # Make sure the referenced dependency DEPS file is loaded and file the |
| 348 # inner referenced dependency. | 348 # inner referenced dependency. |
| 349 # TODO(maruel): Shouldn't do that. |
| 349 ref.ParseDepsFile() | 350 ref.ParseDepsFile() |
| 350 found_dep = None | 351 found_dep = None |
| 351 for d in ref.dependencies: | 352 for d in ref.dependencies: |
| 352 if d.name == sub_target: | 353 if d.name == sub_target: |
| 353 found_dep = d | 354 found_dep = d |
| 354 break | 355 break |
| 355 if not found_dep: | 356 if not found_dep: |
| 356 raise gclient_utils.Error( | 357 raise gclient_utils.Error( |
| 357 'Couldn\'t find %s in %s, referenced by %s (parent: %s)\n%s' % ( | 358 'Couldn\'t find %s in %s, referenced by %s (parent: %s)\n%s' % ( |
| 358 sub_target, ref.name, self.name, self.parent.name, | 359 sub_target, ref.name, self.name, self.parent.name, |
| (...skipping 24 matching lines...) Expand all Loading... |
| 383 logging.info('LateOverride(%s, %s) -> %s' % (self.name, url, parsed_url)) | 384 logging.info('LateOverride(%s, %s) -> %s' % (self.name, url, parsed_url)) |
| 384 return parsed_url | 385 return parsed_url |
| 385 | 386 |
| 386 if isinstance(url, self.FileImpl): | 387 if isinstance(url, self.FileImpl): |
| 387 logging.info('LateOverride(%s, %s) -> %s (File)' % (self.name, url, url)) | 388 logging.info('LateOverride(%s, %s) -> %s (File)' % (self.name, url, url)) |
| 388 return url | 389 return url |
| 389 | 390 |
| 390 if url is None: | 391 if url is None: |
| 391 logging.info('LateOverride(%s, %s) -> %s' % (self.name, url, url)) | 392 logging.info('LateOverride(%s, %s) -> %s' % (self.name, url, url)) |
| 392 return url | 393 return url |
| 393 else: | 394 |
| 394 raise gclient_utils.Error('Unknown url type') | 395 raise gclient_utils.Error('Unknown url type') |
| 395 | 396 |
| 396 def ParseDepsFile(self): | 397 def ParseDepsFile(self): |
| 397 """Parses the DEPS file for this dependency.""" | 398 """Parses the DEPS file for this dependency.""" |
| 398 assert self.processed == True | |
| 399 if self.deps_parsed: | 399 if self.deps_parsed: |
| 400 logging.debug('%s was already parsed' % self.name) | 400 logging.debug('%s was already parsed' % self.name) |
| 401 return | 401 return |
| 402 # One thing is unintuitive, vars= {} must happen before Var() use. | 402 assert not self.dependencies |
| 403 # One thing is unintuitive, vars = {} must happen before Var() use. |
| 403 local_scope = {} | 404 local_scope = {} |
| 404 var = self.VarImpl(self.custom_vars, local_scope) | 405 var = self.VarImpl(self.custom_vars, local_scope) |
| 405 global_scope = { | 406 global_scope = { |
| 406 'File': self.FileImpl, | 407 'File': self.FileImpl, |
| 407 'From': self.FromImpl, | 408 'From': self.FromImpl, |
| 408 'Var': var.Lookup, | 409 'Var': var.Lookup, |
| 409 'deps_os': {}, | 410 'deps_os': {}, |
| 410 } | 411 } |
| 411 filepath = os.path.join(self.root.root_dir, self.name, self.deps_file) | 412 filepath = os.path.join(self.root.root_dir, self.name, self.deps_file) |
| 412 if not os.path.isfile(filepath): | 413 if not os.path.isfile(filepath): |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 465 self.add_dependencies_and_close(deps_to_add, local_scope.get('hooks', [])) | 466 self.add_dependencies_and_close(deps_to_add, local_scope.get('hooks', [])) |
| 466 logging.info('ParseDepsFile(%s) done' % self.name) | 467 logging.info('ParseDepsFile(%s) done' % self.name) |
| 467 | 468 |
| 468 def add_dependencies_and_close(self, deps_to_add, hooks): | 469 def add_dependencies_and_close(self, deps_to_add, hooks): |
| 469 """Adds the dependencies, hooks and mark the parsing as done.""" | 470 """Adds the dependencies, hooks and mark the parsing as done.""" |
| 470 for dep in deps_to_add: | 471 for dep in deps_to_add: |
| 471 if dep.setup_requirements(): | 472 if dep.setup_requirements(): |
| 472 self.add_dependency(dep) | 473 self.add_dependency(dep) |
| 473 self._mark_as_parsed(hooks) | 474 self._mark_as_parsed(hooks) |
| 474 | 475 |
| 476 @staticmethod |
| 477 def maybeGetParentRevision( |
| 478 command, options, parsed_url, parent_name, revision_overrides): |
| 479 """If we are performing an update and --transitive is set, set the |
| 480 revision to the parent's revision. If we have an explicit revision |
| 481 do nothing.""" |
| 482 if command == 'update' and options.transitive and not options.revision: |
| 483 _, revision = gclient_utils.SplitUrlRevision(parsed_url) |
| 484 if not revision: |
| 485 options.revision = revision_overrides.get(parent_name) |
| 486 if options.verbose and options.revision: |
| 487 print("Using parent's revision date: %s" % options.revision) |
| 488 # If the parent has a revision override, then it must have been |
| 489 # converted to date format. |
| 490 assert (not options.revision or |
| 491 gclient_utils.IsDateRevision(options.revision)) |
| 492 |
| 493 @staticmethod |
| 494 def maybeConvertToDateRevision( |
| 495 command, options, name, scm, revision_overrides): |
| 496 """If we are performing an update and --transitive is set, convert the |
| 497 revision to a date-revision (if necessary). Instead of having |
| 498 -r 101 replace the revision with the time stamp of 101 (e.g. |
| 499 "{2011-18-04}"). |
| 500 This way dependencies are upgraded to the revision they had at the |
| 501 check-in of revision 101.""" |
| 502 if (command == 'update' and |
| 503 options.transitive and |
| 504 options.revision and |
| 505 not gclient_utils.IsDateRevision(options.revision)): |
| 506 revision_date = scm.GetRevisionDate(options.revision) |
| 507 revision = gclient_utils.MakeDateRevision(revision_date) |
| 508 if options.verbose: |
| 509 print("Updating revision override from %s to %s." % |
| 510 (options.revision, revision)) |
| 511 revision_overrides[name] = revision |
| 512 |
| 475 # Arguments number differs from overridden method | 513 # Arguments number differs from overridden method |
| 476 # pylint: disable=W0221 | 514 # pylint: disable=W0221 |
| 477 def run(self, revision_overrides, command, args, work_queue, options): | 515 def run(self, revision_overrides, command, args, work_queue, options): |
| 478 """Runs 'command' before parsing the DEPS in case it's a initial checkout | 516 """Runs |command| then parse the DEPS file.""" |
| 479 or a revert.""" | |
| 480 | |
| 481 def maybeGetParentRevision(options): | |
| 482 """If we are performing an update and --transitive is set, set the | |
| 483 revision to the parent's revision. If we have an explicit revision | |
| 484 do nothing.""" | |
| 485 if command == 'update' and options.transitive and not options.revision: | |
| 486 _, revision = gclient_utils.SplitUrlRevision(self.parsed_url) | |
| 487 if not revision: | |
| 488 options.revision = revision_overrides.get(self.parent.name) | |
| 489 if options.verbose and options.revision: | |
| 490 print("Using parent's revision date: %s" % options.revision) | |
| 491 # If the parent has a revision override, then it must have been | |
| 492 # converted to date format. | |
| 493 assert (not options.revision or | |
| 494 gclient_utils.IsDateRevision(options.revision)) | |
| 495 | |
| 496 def maybeConvertToDateRevision(options): | |
| 497 """If we are performing an update and --transitive is set, convert the | |
| 498 revision to a date-revision (if necessary). Instead of having | |
| 499 -r 101 replace the revision with the time stamp of 101 (e.g. | |
| 500 "{2011-18-04}"). | |
| 501 This way dependencies are upgraded to the revision they had at the | |
| 502 check-in of revision 101.""" | |
| 503 if (command == 'update' and | |
| 504 options.transitive and | |
| 505 options.revision and | |
| 506 not gclient_utils.IsDateRevision(options.revision)): | |
| 507 revision_date = scm.GetRevisionDate(options.revision) | |
| 508 revision = gclient_utils.MakeDateRevision(revision_date) | |
| 509 if options.verbose: | |
| 510 print("Updating revision override from %s to %s." % | |
| 511 (options.revision, revision)) | |
| 512 revision_overrides[self.name] = revision | |
| 513 | |
| 514 assert self._file_list == [] | 517 assert self._file_list == [] |
| 515 if not self.should_process: | 518 if not self.should_process: |
| 516 return | 519 return |
| 517 # When running runhooks, there's no need to consult the SCM. | 520 # When running runhooks, there's no need to consult the SCM. |
| 518 # All known hooks are expected to run unconditionally regardless of working | 521 # All known hooks are expected to run unconditionally regardless of working |
| 519 # copy state, so skip the SCM status check. | 522 # copy state, so skip the SCM status check. |
| 520 run_scm = command not in ('runhooks', None) | 523 run_scm = command not in ('runhooks', None) |
| 521 self._parsed_url = self.LateOverride(self.url) | 524 parsed_url = self.LateOverride(self.url) |
| 522 if run_scm and self.parsed_url: | 525 file_list = [] |
| 523 if isinstance(self.parsed_url, self.FileImpl): | 526 if run_scm and parsed_url: |
| 527 if isinstance(parsed_url, self.FileImpl): |
| 524 # Special support for single-file checkout. | 528 # Special support for single-file checkout. |
| 525 if not command in (None, 'cleanup', 'diff', 'pack', 'status'): | 529 if not command in (None, 'cleanup', 'diff', 'pack', 'status'): |
| 526 options.revision = self.parsed_url.GetRevision() | 530 # Sadly, pylint doesn't realize that parsed_url is of FileImpl. |
| 527 scm = gclient_scm.SVNWrapper(self.parsed_url.GetPath(), | 531 # pylint: disable=E1103 |
| 532 options.revision = parsed_url.GetRevision() |
| 533 scm = gclient_scm.SVNWrapper(parsed_url.GetPath(), |
| 528 self.root.root_dir, | 534 self.root.root_dir, |
| 529 self.name) | 535 self.name) |
| 530 scm.RunCommand('updatesingle', options, | 536 scm.RunCommand('updatesingle', options, |
| 531 args + [self.parsed_url.GetFilename()], | 537 args + [parsed_url.GetFilename()], |
| 532 self._file_list) | 538 file_list) |
| 533 else: | 539 else: |
| 534 # Create a shallow copy to mutate revision. | 540 # Create a shallow copy to mutate revision. |
| 535 options = copy.copy(options) | 541 options = copy.copy(options) |
| 536 options.revision = revision_overrides.get(self.name) | 542 options.revision = revision_overrides.get(self.name) |
| 537 maybeGetParentRevision(options) | 543 self.maybeGetParentRevision( |
| 538 scm = gclient_scm.CreateSCM( | 544 command, options, parsed_url, self.parent.name, revision_overrides) |
| 539 self.parsed_url, self.root.root_dir, self.name) | 545 scm = gclient_scm.CreateSCM(parsed_url, self.root.root_dir, self.name) |
| 540 scm.RunCommand(command, options, args, self._file_list) | 546 scm.RunCommand(command, options, args, file_list) |
| 541 maybeConvertToDateRevision(options) | 547 self.maybeConvertToDateRevision( |
| 542 self._file_list = [os.path.join(self.name, f.strip()) | 548 command, options, self.name, scm, revision_overrides) |
| 543 for f in self._file_list] | 549 file_list = [os.path.join(self.name, f.strip()) for f in file_list] |
| 544 | 550 |
| 545 # TODO(phajdan.jr): We should know exactly when the paths are absolute. | 551 # TODO(phajdan.jr): We should know exactly when the paths are absolute. |
| 546 # Convert all absolute paths to relative. | 552 # Convert all absolute paths to relative. |
| 547 for i in range(len(self._file_list)): | 553 for i in range(len(file_list)): |
| 548 # It depends on the command being executed (like runhooks vs sync). | 554 # It depends on the command being executed (like runhooks vs sync). |
| 549 if not os.path.isabs(self._file_list[i]): | 555 if not os.path.isabs(file_list[i]): |
| 550 continue | 556 continue |
| 551 prefix = os.path.commonprefix( | 557 prefix = os.path.commonprefix( |
| 552 [self.root.root_dir.lower(), self._file_list[i].lower()]) | 558 [self.root.root_dir.lower(), file_list[i].lower()]) |
| 553 self._file_list[i] = self._file_list[i][len(prefix):] | 559 file_list[i] = file_list[i][len(prefix):] |
| 554 # Strip any leading path separators. | 560 # Strip any leading path separators. |
| 555 while (self._file_list[i].startswith('\\') or | 561 while file_list[i].startswith(('\\', '/')): |
| 556 self._file_list[i].startswith('/')): | 562 file_list[i] = file_list[i][1:] |
| 557 self._file_list[i] = self._file_list[i][1:] | 563 |
| 558 self._processed = True | |
| 559 if self.recursion_limit: | 564 if self.recursion_limit: |
| 560 # Then we can parse the DEPS file. | 565 # Then we can parse the DEPS file. |
| 561 self.ParseDepsFile() | 566 self.ParseDepsFile() |
| 562 | 567 |
| 568 self._run_is_done(file_list, parsed_url) |
| 569 |
| 570 if self.recursion_limit: |
| 563 # Parse the dependencies of this dependency. | 571 # Parse the dependencies of this dependency. |
| 564 for s in self.dependencies: | 572 for s in self.dependencies: |
| 565 work_queue.enqueue(s) | 573 work_queue.enqueue(s) |
| 566 | 574 |
| 575 @gclient_utils.lockedmethod |
| 576 def _run_is_done(self, file_list, parsed_url): |
| 577 # Both these are kept for hooks that are run as a separate tree traversal. |
| 578 self._file_list = file_list |
| 579 self._parsed_url = parsed_url |
| 580 self._processed = True |
| 581 |
| 567 def RunHooksRecursively(self, options): | 582 def RunHooksRecursively(self, options): |
| 568 """Evaluates all hooks, running actions as needed. run() | 583 """Evaluates all hooks, running actions as needed. run() |
| 569 must have been called before to load the DEPS.""" | 584 must have been called before to load the DEPS.""" |
| 570 assert self.hooks_ran == False | 585 assert self.hooks_ran == False |
| 571 if not self.should_process or not self.recursion_limit: | 586 if not self.should_process or not self.recursion_limit: |
| 572 # Don't run the hook when it is above recursion_limit. | 587 # Don't run the hook when it is above recursion_limit. |
| 573 return | 588 return |
| 574 # If "--force" was specified, run all hooks regardless of what files have | 589 # If "--force" was specified, run all hooks regardless of what files have |
| 575 # changed. | 590 # changed. |
| 576 if self.deps_hooks: | 591 if self.deps_hooks: |
| 577 # TODO(maruel): If the user is using git or git-svn, then we don't know | 592 # TODO(maruel): If the user is using git or git-svn, then we don't know |
| 578 # what files have changed so we always run all hooks. It'd be nice to fix | 593 # what files have changed so we always run all hooks. It'd be nice to fix |
| 579 # that. | 594 # that. |
| 580 if (options.force or | 595 if (options.force or |
| 581 isinstance(self.parsed_url, self.FileImpl) or | 596 isinstance(self.parsed_url, self.FileImpl) or |
| 582 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or | 597 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or |
| 583 os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))): | 598 os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))): |
| 584 for hook_dict in self.deps_hooks: | 599 for hook_dict in self.deps_hooks: |
| 585 self._RunHookAction(hook_dict, []) | 600 self._RunHookAction(hook_dict, []) |
| 586 else: | 601 else: |
| 587 # Run hooks on the basis of whether the files from the gclient operation | 602 # Run hooks on the basis of whether the files from the gclient operation |
| 588 # match each hook's pattern. | 603 # match each hook's pattern. |
| 589 for hook_dict in self.deps_hooks: | 604 for hook_dict in self.deps_hooks: |
| 590 pattern = re.compile(hook_dict['pattern']) | 605 pattern = re.compile(hook_dict['pattern']) |
| 591 matching_file_list = [f for f in self.file_list if pattern.search(f)] | 606 matching_file_list = [ |
| 607 f for f in self.file_list_and_children if pattern.search(f) |
| 608 ] |
| 592 if matching_file_list: | 609 if matching_file_list: |
| 593 self._RunHookAction(hook_dict, matching_file_list) | 610 self._RunHookAction(hook_dict, matching_file_list) |
| 594 for s in self.dependencies: | 611 for s in self.dependencies: |
| 595 s.RunHooksRecursively(options) | 612 s.RunHooksRecursively(options) |
| 596 | 613 |
| 597 def _RunHookAction(self, hook_dict, matching_file_list): | 614 def _RunHookAction(self, hook_dict, matching_file_list): |
| 598 """Runs the action from a single hook.""" | 615 """Runs the action from a single hook.""" |
| 599 # A single DEPS file can specify multiple hooks so this function can be | 616 # A single DEPS file can specify multiple hooks so this function can be |
| 600 # called multiple times on a single Dependency. | 617 # called multiple times on a single Dependency. |
| 601 #assert self.hooks_ran == False | 618 #assert self.hooks_ran == False |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 649 def _mark_as_parsed(self, new_hooks): | 666 def _mark_as_parsed(self, new_hooks): |
| 650 self._deps_hooks.extend(new_hooks) | 667 self._deps_hooks.extend(new_hooks) |
| 651 self._deps_parsed = True | 668 self._deps_parsed = True |
| 652 | 669 |
| 653 @property | 670 @property |
| 654 @gclient_utils.lockedmethod | 671 @gclient_utils.lockedmethod |
| 655 def dependencies(self): | 672 def dependencies(self): |
| 656 return tuple(self._dependencies) | 673 return tuple(self._dependencies) |
| 657 | 674 |
| 658 @property | 675 @property |
| 676 @gclient_utils.lockedmethod |
| 659 def deps_hooks(self): | 677 def deps_hooks(self): |
| 660 return tuple(self._deps_hooks) | 678 return tuple(self._deps_hooks) |
| 661 | 679 |
| 662 @property | 680 @property |
| 681 @gclient_utils.lockedmethod |
| 663 def parsed_url(self): | 682 def parsed_url(self): |
| 664 return self._parsed_url | 683 return self._parsed_url |
| 665 | 684 |
| 666 @property | 685 @property |
| 686 @gclient_utils.lockedmethod |
| 667 def deps_parsed(self): | 687 def deps_parsed(self): |
| 668 return self._deps_parsed | 688 return self._deps_parsed |
| 669 | 689 |
| 670 @property | 690 @property |
| 691 @gclient_utils.lockedmethod |
| 671 def processed(self): | 692 def processed(self): |
| 672 return self._processed | 693 return self._processed |
| 673 | 694 |
| 674 @property | 695 @property |
| 696 @gclient_utils.lockedmethod |
| 675 def hooks_ran(self): | 697 def hooks_ran(self): |
| 676 return self._hooks_ran | 698 return self._hooks_ran |
| 677 | 699 |
| 678 @property | 700 @property |
| 701 @gclient_utils.lockedmethod |
| 679 def file_list(self): | 702 def file_list(self): |
| 680 result = self._file_list[:] | 703 return tuple(self._file_list) |
| 704 |
| 705 @property |
| 706 def file_list_and_children(self): |
| 707 result = list(self.file_list) |
| 681 for d in self.dependencies: | 708 for d in self.dependencies: |
| 682 result.extend(d.file_list) | 709 result.extend(d.file_list_and_children) |
| 683 return tuple(result) | 710 return tuple(result) |
| 684 | 711 |
| 685 def __str__(self): | 712 def __str__(self): |
| 686 out = [] | 713 out = [] |
| 687 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps', | 714 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps', |
| 688 'custom_vars', 'deps_hooks', 'file_list', 'should_process', | 715 'custom_vars', 'deps_hooks', 'file_list', 'should_process', |
| 689 'processed', 'hooks_ran', 'deps_parsed', 'requirements'): | 716 'processed', 'hooks_ran', 'deps_parsed', 'requirements'): |
| 690 # First try the native property if it exists. | 717 # First try the native property if it exists. |
| 691 if hasattr(self, '_' + i): | 718 if hasattr(self, '_' + i): |
| 692 value = getattr(self, '_' + i, False) | 719 value = getattr(self, '_' + i, False) |
| (...skipping 770 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1463 except (gclient_utils.Error, subprocess2.CalledProcessError), e: | 1490 except (gclient_utils.Error, subprocess2.CalledProcessError), e: |
| 1464 print >> sys.stderr, 'Error: %s' % str(e) | 1491 print >> sys.stderr, 'Error: %s' % str(e) |
| 1465 return 1 | 1492 return 1 |
| 1466 | 1493 |
| 1467 | 1494 |
| 1468 if '__main__' == __name__: | 1495 if '__main__' == __name__: |
| 1469 fix_encoding.fix_encoding() | 1496 fix_encoding.fix_encoding() |
| 1470 sys.exit(Main(sys.argv[1:])) | 1497 sys.exit(Main(sys.argv[1:])) |
| 1471 | 1498 |
| 1472 # vim: ts=2:sw=2:tw=80:et: | 1499 # vim: ts=2:sw=2:tw=80:et: |
| OLD | NEW |