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 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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: |
OLD | NEW |