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

Side by Side Diff: gclient.py

Issue 8135019: Move all mutations into a specific place. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: address review comments 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 | tests/gclient_test.py » ('j') | 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 240 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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
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:
OLDNEW
« no previous file with comments | « no previous file | tests/gclient_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698