Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(431)

Side by Side Diff: gclient.py

Issue 8142030: Move mutation in run() at the end. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Fix regression introduced Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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:
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698