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

Side by Side Diff: gclient.py

Issue 8174014: Stop modifiying requirements out of thread and generate it instead. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Fix the lack of constant ordering in GClientSmokeBoth.testMultiSolutionsJobs 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 | gclient_utils.py » ('j') | tests/gclient_smoketest.py » ('J')
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 243 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 if not self.name and self.parent: 261 if not self.name and self.parent:
262 raise gclient_utils.Error('Dependency without name') 262 raise gclient_utils.Error('Dependency without name')
263 263
264 def setup_requirements(self): 264 @property
265 """Setup self.requirements and find any other dependency who would have self 265 def requirements(self):
266 as a requirement. 266 """Calculate the list of requirements."""
267 267 requirements = set()
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
286
287 # self.parent is implicitly a requirement. This will be recursive by 268 # self.parent is implicitly a requirement. This will be recursive by
288 # definition. 269 # definition.
289 if self.parent and self.parent.name: 270 if self.parent and self.parent.name:
290 self.add_requirement(self.parent.name) 271 requirements.add(self.parent.name)
291 272
292 # For a tree with at least 2 levels*, the leaf node needs to depend 273 # For a tree with at least 2 levels*, the leaf node needs to depend
293 # on the level higher up in an orderly way. 274 # on the level higher up in an orderly way.
294 # This becomes messy for >2 depth as the DEPS file format is a dictionary, 275 # This becomes messy for >2 depth as the DEPS file format is a dictionary,
295 # thus unsorted, while the .gclient format is a list thus sorted. 276 # thus unsorted, while the .gclient format is a list thus sorted.
296 # 277 #
297 # * _recursion_limit is hard coded 2 and there is no hope to change this 278 # * _recursion_limit is hard coded 2 and there is no hope to change this
298 # value. 279 # value.
299 # 280 #
300 # Interestingly enough, the following condition only works in the case we 281 # Interestingly enough, the following condition only works in the case we
301 # want: self is a 2nd level node. 3nd level node wouldn't need this since 282 # want: self is a 2nd level node. 3nd level node wouldn't need this since
302 # they already have their parent as a requirement. 283 # they already have their parent as a requirement.
303 root_deps = self.root.dependencies 284 if self.parent and self.parent.parent and not self.parent.parent.parent:
304 if self.parent in root_deps: 285 requirements |= set(i.name for i in self.root.dependencies if i.name)
305 for i in root_deps:
306 if i is self.parent:
307 break
308 if i.name:
309 self.add_requirement(i.name)
310 286
311 if isinstance(self.url, self.FromImpl): 287 if isinstance(self.url, self.FromImpl):
312 self.add_requirement(self.url.module_name) 288 requirements.add(self.url.module_name)
313 289
314 if self.name and self.should_process: 290 if self.name:
315 for obj in self.root.depth_first_tree(): 291 requirements |= set(
316 if obj is self or not obj.name: 292 obj.name for obj in self.root.subtree(False)
317 continue 293 if (obj is not self
318 # Step 1: Find any requirements self may need. 294 and obj.name and
319 if self.name.startswith(posixpath.join(obj.name, '')): 295 self.name.startswith(posixpath.join(obj.name, ''))))
320 self.add_requirement(obj.name) 296 requirements = tuple(sorted(requirements))
321 # Step 2: Find any requirements self may impose. 297 logging.info('Dependency(%s).requirements = %s' % (self.name, requirements))
322 if obj.name.startswith(posixpath.join(self.name, '')): 298 return requirements
323 obj.add_requirement(self.name) 299
300 def verify_validity(self):
301 """Verifies that this Dependency is fine to add as a child of another one.
302
303 Returns True if this entry should be added, False if it is a duplicate of
304 another entry.
305 """
306 logging.info('Dependency(%s).verify_validity()' % self.name)
307 if self.name in [s.name for s in self.parent.dependencies]:
308 raise gclient_utils.Error(
309 'The same name "%s" appears multiple times in the deps section' %
310 self.name)
311 if not self.should_process:
312 # Return early, no need to set requirements.
313 return True
314
315 # This require a full tree traversal with locks.
316 siblings = [d for d in self.root.subtree(False) if d.name == self.name]
317 for sibling in siblings:
318 if self.url != sibling.url:
319 raise gclient_utils.Error(
320 'Dependency %s specified more than once:\n %s\nvs\n %s' %
321 (self.name, sibling.hierarchy(), self.hierarchy()))
322 # In theory we could keep it as a shadow of the other one. In
323 # practice, simply ignore it.
324 logging.warn('Won\'t process duplicate dependency %s' % sibling)
325 return False
324 return True 326 return True
325 327
326 def LateOverride(self, url): 328 def LateOverride(self, url):
327 """Resolves the parsed url from url. 329 """Resolves the parsed url from url.
328 330
329 Manages From() keyword accordingly. Do not touch self.parsed_url nor 331 Manages From() keyword accordingly. Do not touch self.parsed_url nor
330 self.url because it may called with other urls due to From().""" 332 self.url because it may called with other urls due to From()."""
331 assert self.parsed_url == None or not self.should_process, self.parsed_url 333 assert self.parsed_url == None or not self.should_process, self.parsed_url
332 parsed_url = self.get_custom_deps(self.name, url) 334 parsed_url = self.get_custom_deps(self.name, url)
333 if parsed_url != url: 335 if parsed_url != url:
334 logging.info('LateOverride(%s, %s) -> %s' % (self.name, url, parsed_url)) 336 logging.info(
337 'Dependency(%s).LateOverride(%s) -> %s' %
338 (self.name, url, parsed_url))
335 return parsed_url 339 return parsed_url
336 340
337 if isinstance(url, self.FromImpl): 341 if isinstance(url, self.FromImpl):
342 # Requires tree traversal.
338 ref = [ 343 ref = [
339 dep for dep in self.root.subtree(True) if url.module_name == dep.name 344 dep for dep in self.root.subtree(True) if url.module_name == dep.name
340 ] 345 ]
341 if not ref: 346 if not ref:
342 raise gclient_utils.Error('Failed to find one reference to %s. %s' % ( 347 raise gclient_utils.Error('Failed to find one reference to %s. %s' % (
343 url.module_name, ref)) 348 url.module_name, ref))
344 # It may happen that len(ref) > 1 but it's no big deal. 349 # It may happen that len(ref) > 1 but it's no big deal.
345 ref = ref[0] 350 ref = ref[0]
346 sub_target = url.sub_target_name or self.name 351 sub_target = url.sub_target_name or self.name
347 found_deps = [d for d in ref.dependencies if d.name == sub_target] 352 found_deps = [d for d in ref.dependencies if d.name == sub_target]
348 if len(found_deps) != 1: 353 if len(found_deps) != 1:
349 raise gclient_utils.Error( 354 raise gclient_utils.Error(
350 'Couldn\'t find %s in %s, referenced by %s (parent: %s)\n%s' % ( 355 'Couldn\'t find %s in %s, referenced by %s (parent: %s)\n%s' % (
351 sub_target, ref.name, self.name, self.parent.name, 356 sub_target, ref.name, self.name, self.parent.name,
352 str(self.root))) 357 str(self.root)))
353 358
354 # Call LateOverride() again. 359 # Call LateOverride() again.
355 found_dep = found_deps[0] 360 found_dep = found_deps[0]
356 parsed_url = found_dep.LateOverride(found_dep.url) 361 parsed_url = found_dep.LateOverride(found_dep.url)
357 logging.info( 362 logging.info(
358 'LateOverride(%s, %s) -> %s (From)' % (self.name, url, parsed_url)) 363 'Dependency(%s).LateOverride(%s) -> %s (From)' %
364 (self.name, url, parsed_url))
359 return parsed_url 365 return parsed_url
360 366
361 if isinstance(url, basestring): 367 if isinstance(url, basestring):
362 parsed_url = urlparse.urlparse(url) 368 parsed_url = urlparse.urlparse(url)
363 if not parsed_url[0]: 369 if not parsed_url[0]:
364 # A relative url. Fetch the real base. 370 # A relative url. Fetch the real base.
365 path = parsed_url[2] 371 path = parsed_url[2]
366 if not path.startswith('/'): 372 if not path.startswith('/'):
367 raise gclient_utils.Error( 373 raise gclient_utils.Error(
368 'relative DEPS entry \'%s\' must begin with a slash' % url) 374 'relative DEPS entry \'%s\' must begin with a slash' % url)
369 # Create a scm just to query the full url. 375 # Create a scm just to query the full url.
370 parent_url = self.parent.parsed_url 376 parent_url = self.parent.parsed_url
371 if isinstance(parent_url, self.FileImpl): 377 if isinstance(parent_url, self.FileImpl):
372 parent_url = parent_url.file_location 378 parent_url = parent_url.file_location
373 scm = gclient_scm.CreateSCM(parent_url, self.root.root_dir, None) 379 scm = gclient_scm.CreateSCM(parent_url, self.root.root_dir, None)
374 parsed_url = scm.FullUrlForRelativeUrl(url) 380 parsed_url = scm.FullUrlForRelativeUrl(url)
375 else: 381 else:
376 parsed_url = url 382 parsed_url = url
377 logging.info('LateOverride(%s, %s) -> %s' % (self.name, url, parsed_url)) 383 logging.info(
384 'Dependency(%s).LateOverride(%s) -> %s' %
385 (self.name, url, parsed_url))
378 return parsed_url 386 return parsed_url
379 387
380 if isinstance(url, self.FileImpl): 388 if isinstance(url, self.FileImpl):
381 logging.info('LateOverride(%s, %s) -> %s (File)' % (self.name, url, url)) 389 logging.info(
390 'Dependency(%s).LateOverride(%s) -> %s (File)' %
391 (self.name, url, url))
382 return url 392 return url
383 393
384 if url is None: 394 if url is None:
385 logging.info('LateOverride(%s, %s) -> %s' % (self.name, url, url)) 395 logging.info(
396 'Dependency(%s).LateOverride(%s) -> %s' % (self.name, url, url))
386 return url 397 return url
387 398
388 raise gclient_utils.Error('Unknown url type') 399 raise gclient_utils.Error('Unknown url type')
389 400
390 def ParseDepsFile(self): 401 def ParseDepsFile(self):
391 """Parses the DEPS file for this dependency.""" 402 """Parses the DEPS file for this dependency."""
392 assert not self.deps_parsed 403 assert not self.deps_parsed
393 assert not self.dependencies 404 assert not self.dependencies
394 # One thing is unintuitive, vars = {} must happen before Var() use. 405 # One thing is unintuitive, vars = {} must happen before Var() use.
395 local_scope = {} 406 local_scope = {}
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
452 for name, url in deps.iteritems(): 463 for name, url in deps.iteritems():
453 should_process = self.recursion_limit and self.should_process 464 should_process = self.recursion_limit and self.should_process
454 deps_to_add.append(Dependency( 465 deps_to_add.append(Dependency(
455 self, name, url, None, None, None, None, 466 self, name, url, None, None, None, None,
456 self.deps_file, should_process)) 467 self.deps_file, should_process))
457 self.add_dependencies_and_close(deps_to_add, local_scope.get('hooks', [])) 468 self.add_dependencies_and_close(deps_to_add, local_scope.get('hooks', []))
458 logging.info('ParseDepsFile(%s) done' % self.name) 469 logging.info('ParseDepsFile(%s) done' % self.name)
459 470
460 def add_dependencies_and_close(self, deps_to_add, hooks): 471 def add_dependencies_and_close(self, deps_to_add, hooks):
461 """Adds the dependencies, hooks and mark the parsing as done.""" 472 """Adds the dependencies, hooks and mark the parsing as done."""
462 for dep in deps_to_add: 473 for dep in sorted(deps_to_add, key=lambda x: x.name):
463 if dep.setup_requirements(): 474 if dep.verify_validity():
464 self.add_dependency(dep) 475 self.add_dependency(dep)
465 self._mark_as_parsed(hooks) 476 self._mark_as_parsed(hooks)
466 477
467 @staticmethod 478 @staticmethod
468 def maybeGetParentRevision( 479 def maybeGetParentRevision(
469 command, options, parsed_url, parent_name, revision_overrides): 480 command, options, parsed_url, parent_name, revision_overrides):
470 """If we are performing an update and --transitive is set, set the 481 """If we are performing an update and --transitive is set, set the
471 revision to the parent's revision. If we have an explicit revision 482 revision to the parent's revision. If we have an explicit revision
472 do nothing.""" 483 do nothing."""
473 if command == 'update' and options.transitive and not options.revision: 484 if command == 'update' and options.transitive and not options.revision:
(...skipping 24 matching lines...) Expand all
498 revision = gclient_utils.MakeDateRevision(revision_date) 509 revision = gclient_utils.MakeDateRevision(revision_date)
499 if options.verbose: 510 if options.verbose:
500 print("Updating revision override from %s to %s." % 511 print("Updating revision override from %s to %s." %
501 (options.revision, revision)) 512 (options.revision, revision))
502 revision_overrides[name] = revision 513 revision_overrides[name] = revision
503 514
504 # Arguments number differs from overridden method 515 # Arguments number differs from overridden method
505 # pylint: disable=W0221 516 # pylint: disable=W0221
506 def run(self, revision_overrides, command, args, work_queue, options): 517 def run(self, revision_overrides, command, args, work_queue, options):
507 """Runs |command| then parse the DEPS file.""" 518 """Runs |command| then parse the DEPS file."""
519 logging.info('Dependency(%s).run()' % self.name)
508 assert self._file_list == [] 520 assert self._file_list == []
509 if not self.should_process: 521 if not self.should_process:
510 return 522 return
511 # When running runhooks, there's no need to consult the SCM. 523 # When running runhooks, there's no need to consult the SCM.
512 # All known hooks are expected to run unconditionally regardless of working 524 # All known hooks are expected to run unconditionally regardless of working
513 # copy state, so skip the SCM status check. 525 # copy state, so skip the SCM status check.
514 run_scm = command not in ('runhooks', None) 526 run_scm = command not in ('runhooks', None)
515 parsed_url = self.LateOverride(self.url) 527 parsed_url = self.LateOverride(self.url)
516 file_list = [] 528 file_list = []
517 if run_scm and parsed_url: 529 if run_scm and parsed_url:
(...skipping 963 matching lines...) Expand 10 before | Expand all | Expand 10 after
1481 except (gclient_utils.Error, subprocess2.CalledProcessError), e: 1493 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
1482 print >> sys.stderr, 'Error: %s' % str(e) 1494 print >> sys.stderr, 'Error: %s' % str(e)
1483 return 1 1495 return 1
1484 1496
1485 1497
1486 if '__main__' == __name__: 1498 if '__main__' == __name__:
1487 fix_encoding.fix_encoding() 1499 fix_encoding.fix_encoding()
1488 sys.exit(Main(sys.argv[1:])) 1500 sys.exit(Main(sys.argv[1:]))
1489 1501
1490 # vim: ts=2:sw=2:tw=80:et: 1502 # vim: ts=2:sw=2:tw=80:et:
OLDNEW
« no previous file with comments | « no previous file | gclient_utils.py » ('j') | tests/gclient_smoketest.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698