OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2010 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 """A wrapper script to manage a set of client modules in different SCM. | 6 """A wrapper script to manage a set of client modules in different SCM. |
7 | 7 |
8 This script is intended to be used to help basic management of client | 8 This script is intended to be used to help basic management of client |
9 program sources residing in one or more Subversion modules and Git | 9 program sources residing in one or more Subversion modules and Git |
10 repositories, along with other modules it depends on, also in Subversion or Git, | 10 repositories, along with other modules it depends on, also in Subversion or Git, |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
148 | 148 |
149 def Lookup(self, var_name): | 149 def Lookup(self, var_name): |
150 """Implements the Var syntax.""" | 150 """Implements the Var syntax.""" |
151 if var_name in self._custom_vars: | 151 if var_name in self._custom_vars: |
152 return self._custom_vars[var_name] | 152 return self._custom_vars[var_name] |
153 elif var_name in self._local_scope.get("vars", {}): | 153 elif var_name in self._local_scope.get("vars", {}): |
154 return self._local_scope["vars"][var_name] | 154 return self._local_scope["vars"][var_name] |
155 raise gclient_utils.Error("Var is not defined: %s" % var_name) | 155 raise gclient_utils.Error("Var is not defined: %s" % var_name) |
156 | 156 |
157 | 157 |
158 class GClient(GClientKeywords): | 158 class Dependency(GClientKeywords): |
159 """Object that represent a gclient checkout.""" | 159 """Object that represents a dependency checkout.""" |
160 DEPS_FILE = 'DEPS' | 160 DEPS_FILE = 'DEPS' |
| 161 def __init__(self, parent, name, url, safesync_url=None, custom_deps=None, |
| 162 custom_vars=None, deps_file=None): |
| 163 GClientKeywords.__init__(self) |
| 164 self.parent = parent |
| 165 self.name = name |
| 166 self.url = url |
| 167 # These 2 are only set in .gclient and not in DEPS files. |
| 168 self.safesync_url = safesync_url |
| 169 self.custom_vars = custom_vars or {} |
| 170 self.custom_deps = custom_deps or {} |
| 171 self.dependencies = [] |
| 172 self.deps_file = deps_file or self.DEPS_FILE |
| 173 self._deps_hooks = [] |
161 | 174 |
| 175 # Sanity checks |
| 176 if not self.name and self.parent: |
| 177 raise gclient_utils.Error('Dependency without name') |
| 178 if not isinstance(self.url, |
| 179 (basestring, self.FromImpl, self.FileImpl, None.__class__)): |
| 180 raise gclient_utils.Error('dependency url must be either a string, None, ' |
| 181 'File() or From() instead of %s' % |
| 182 self.url.__class__.__name__) |
| 183 if '/' in self.deps_file or '\\' in self.deps_file: |
| 184 raise gclient_utils.Error('deps_file name must not be a path, just a ' |
| 185 'filename. %s' % self.deps_file) |
| 186 |
| 187 |
| 188 class GClient(Dependency): |
| 189 """Main gclient checkout root where .gclient resides.""" |
162 SUPPORTED_COMMANDS = [ | 190 SUPPORTED_COMMANDS = [ |
163 'cleanup', 'diff', 'export', 'pack', 'revert', 'status', 'update', | 191 'cleanup', 'diff', 'export', 'pack', 'revert', 'status', 'update', |
164 'runhooks' | 192 'runhooks' |
165 ] | 193 ] |
166 | 194 |
167 DEPS_OS_CHOICES = { | 195 DEPS_OS_CHOICES = { |
168 "win32": "win", | 196 "win32": "win", |
169 "win": "win", | 197 "win": "win", |
170 "cygwin": "win", | 198 "cygwin": "win", |
171 "darwin": "mac", | 199 "darwin": "mac", |
(...skipping 25 matching lines...) Expand all Loading... |
197 """) | 225 """) |
198 | 226 |
199 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\ | 227 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\ |
200 # Snapshot generated with gclient revinfo --snapshot | 228 # Snapshot generated with gclient revinfo --snapshot |
201 solutions = [ | 229 solutions = [ |
202 %(solution_list)s | 230 %(solution_list)s |
203 ] | 231 ] |
204 """) | 232 """) |
205 | 233 |
206 def __init__(self, root_dir, options): | 234 def __init__(self, root_dir, options): |
| 235 Dependency.__init__(self, None, None, None) |
207 self._root_dir = root_dir | 236 self._root_dir = root_dir |
208 self._options = options | 237 self._options = options |
209 self.config_content = None | 238 self.config_content = None |
210 self._config_dict = {} | |
211 self._deps_hooks = [] | |
212 | 239 |
213 def SetConfig(self, content): | 240 def SetConfig(self, content): |
214 self._config_dict = {} | 241 config_dict = {} |
215 self.config_content = content | 242 self.config_content = content |
216 try: | 243 try: |
217 exec(content, self._config_dict) | 244 exec(content, config_dict) |
218 except SyntaxError, e: | 245 except SyntaxError, e: |
219 try: | 246 try: |
220 # Try to construct a human readable error message | 247 # Try to construct a human readable error message |
221 error_message = [ | 248 error_message = [ |
222 'There is a syntax error in your configuration file.', | 249 'There is a syntax error in your configuration file.', |
223 'Line #%s, character %s:' % (e.lineno, e.offset), | 250 'Line #%s, character %s:' % (e.lineno, e.offset), |
224 '"%s"' % re.sub(r'[\r\n]*$', '', e.text) ] | 251 '"%s"' % re.sub(r'[\r\n]*$', '', e.text) ] |
225 except: | 252 except: |
226 # Something went wrong, re-raise the original exception | 253 # Something went wrong, re-raise the original exception |
227 raise e | 254 raise e |
228 else: | 255 else: |
229 # Raise a new exception with the human readable message: | 256 # Raise a new exception with the human readable message: |
230 raise gclient_utils.Error('\n'.join(error_message)) | 257 raise gclient_utils.Error('\n'.join(error_message)) |
| 258 for s in config_dict.get('solutions', []): |
| 259 self.dependencies.append(Dependency( |
| 260 self, s['name'], s['url'], |
| 261 s.get('safesync_url', None), |
| 262 s.get('custom_deps', {}), |
| 263 s.get('custom_vars', {}))) |
| 264 # .gclient can have hooks. |
| 265 self._deps_hooks = config_dict.get('hooks', []) |
231 | 266 |
232 def SaveConfig(self): | 267 def SaveConfig(self): |
233 gclient_utils.FileWrite(os.path.join(self.root_dir(), | 268 gclient_utils.FileWrite(os.path.join(self.root_dir(), |
234 self._options.config_filename), | 269 self._options.config_filename), |
235 self.config_content) | 270 self.config_content) |
236 | 271 |
237 def _LoadConfig(self): | 272 def _LoadConfig(self): |
238 client_source = gclient_utils.FileRead( | 273 client_source = gclient_utils.FileRead( |
239 os.path.join(self.root_dir(), self._options.config_filename)) | 274 os.path.join(self.root_dir(), self._options.config_filename)) |
240 self.SetConfig(client_source) | 275 self.SetConfig(client_source) |
241 | 276 |
242 def GetVar(self, key, default=None): | |
243 return self._config_dict.get(key, default) | |
244 | |
245 @staticmethod | 277 @staticmethod |
246 def LoadCurrentConfig(options, from_dir=None): | 278 def LoadCurrentConfig(options, from_dir=None): |
247 """Searches for and loads a .gclient file relative to the current working | 279 """Searches for and loads a .gclient file relative to the current working |
248 dir. | 280 dir. |
249 | 281 |
250 Returns: | 282 Returns: |
251 A dict representing the contents of the .gclient file or an empty dict if | 283 A dict representing the contents of the .gclient file or an empty dict if |
252 the .gclient file doesn't exist. | 284 the .gclient file doesn't exist. |
253 """ | 285 """ |
254 if not from_dir: | 286 if not from_dir: |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
348 if len(deps_to_include) > 1: | 380 if len(deps_to_include) > 1: |
349 # Ignore any overrides when including deps for more than one | 381 # Ignore any overrides when including deps for more than one |
350 # platform, so we collect the broadest set of dependencies available. | 382 # platform, so we collect the broadest set of dependencies available. |
351 # We may end up with the wrong revision of something for our | 383 # We may end up with the wrong revision of something for our |
352 # platform, but this is the best we can do. | 384 # platform, but this is the best we can do. |
353 deps.update([x for x in os_deps.items() if not x[0] in deps]) | 385 deps.update([x for x in os_deps.items() if not x[0] in deps]) |
354 else: | 386 else: |
355 deps.update(os_deps) | 387 deps.update(os_deps) |
356 | 388 |
357 if 'hooks' in local_scope and parse_hooks: | 389 if 'hooks' in local_scope and parse_hooks: |
358 self._deps_hooks.extend(local_scope['hooks']) | 390 # TODO(maruel): Temporary Hack. Since this function is misplaced, find the |
| 391 # right 'self' to add the hooks. |
| 392 for d in self.dependencies: |
| 393 if d.name == solution_name: |
| 394 d._deps_hooks.extend(local_scope['hooks']) |
| 395 break |
359 | 396 |
360 # If use_relative_paths is set in the DEPS file, regenerate | 397 # If use_relative_paths is set in the DEPS file, regenerate |
361 # the dictionary using paths relative to the directory containing | 398 # the dictionary using paths relative to the directory containing |
362 # the DEPS file. | 399 # the DEPS file. |
363 if local_scope.get('use_relative_paths'): | 400 if local_scope.get('use_relative_paths'): |
364 rel_deps = {} | 401 rel_deps = {} |
365 for d, url in deps.items(): | 402 for d, url in deps.items(): |
366 # normpath is required to allow DEPS to use .. in their | 403 # normpath is required to allow DEPS to use .. in their |
367 # dependency local path. | 404 # dependency local path. |
368 rel_deps[os.path.normpath(os.path.join(solution_name, d))] = url | 405 rel_deps[os.path.normpath(os.path.join(solution_name, d))] = url |
(...skipping 12 matching lines...) Expand all Loading... |
381 of their DEPS files | 418 of their DEPS files |
382 | 419 |
383 Returns: | 420 Returns: |
384 A dict mapping module names (as relative paths) to URLs corresponding | 421 A dict mapping module names (as relative paths) to URLs corresponding |
385 to the entire set of dependencies to checkout for the given client. | 422 to the entire set of dependencies to checkout for the given client. |
386 | 423 |
387 Raises: | 424 Raises: |
388 Error: If a dependency conflicts with another dependency or of a solution. | 425 Error: If a dependency conflicts with another dependency or of a solution. |
389 """ | 426 """ |
390 deps = {} | 427 deps = {} |
391 for solution in self.GetVar("solutions"): | 428 for solution in self.dependencies: |
392 custom_vars = solution.get("custom_vars", {}) | |
393 solution_deps = self._ParseSolutionDeps( | 429 solution_deps = self._ParseSolutionDeps( |
394 solution["name"], | 430 solution.name, |
395 solution_deps_content[solution["name"]], | 431 solution_deps_content[solution.name], |
396 custom_vars, | 432 solution.custom_vars, |
397 True) | 433 True) |
398 | 434 |
399 # If a line is in custom_deps, but not in the solution, we want to append | 435 # If a line is in custom_deps, but not in the solution, we want to append |
400 # this line to the solution. | 436 # this line to the solution. |
401 if "custom_deps" in solution: | 437 for d in solution.custom_deps: |
402 for d in solution["custom_deps"]: | 438 if d not in solution_deps: |
403 if d not in solution_deps: | 439 solution_deps[d] = solution.custom_deps[d] |
404 solution_deps[d] = solution["custom_deps"][d] | |
405 | 440 |
406 for d in solution_deps: | 441 for d in solution_deps: |
407 if "custom_deps" in solution and d in solution["custom_deps"]: | 442 if d in solution.custom_deps: |
408 # Dependency is overriden. | 443 # Dependency is overriden. |
409 url = solution["custom_deps"][d] | 444 url = solution.custom_deps[d] |
410 if url is None: | 445 if url is None: |
411 continue | 446 continue |
412 else: | 447 else: |
413 url = solution_deps[d] | 448 url = solution_deps[d] |
414 # if we have a From reference dependent on another solution, then | 449 # if we have a From reference dependent on another solution, then |
415 # just skip the From reference. When we pull deps for the solution, | 450 # just skip the From reference. When we pull deps for the solution, |
416 # we will take care of this dependency. | 451 # we will take care of this dependency. |
417 # | 452 # |
418 # If multiple solutions all have the same From reference, then we | 453 # If multiple solutions all have the same From reference, then we |
419 # should only add one to our list of dependencies. | 454 # should only add one to our list of dependencies. |
420 if isinstance(url, self.FromImpl): | 455 if isinstance(url, self.FromImpl): |
421 if url.module_name in solution_urls: | 456 if url.module_name in solution_urls: |
422 # Already parsed. | 457 # Already parsed. |
423 continue | 458 continue |
424 if d in deps and type(deps[d]) != str: | 459 if d in deps and type(deps[d]) != str: |
425 if url.module_name == deps[d].module_name: | 460 if url.module_name == deps[d].module_name: |
426 continue | 461 continue |
427 elif isinstance(url, str): | 462 elif isinstance(url, str): |
428 parsed_url = urlparse.urlparse(url) | 463 parsed_url = urlparse.urlparse(url) |
429 scheme = parsed_url[0] | 464 scheme = parsed_url[0] |
430 if not scheme: | 465 if not scheme: |
431 # A relative url. Fetch the real base. | 466 # A relative url. Fetch the real base. |
432 path = parsed_url[2] | 467 path = parsed_url[2] |
433 if path[0] != "/": | 468 if path[0] != "/": |
434 raise gclient_utils.Error( | 469 raise gclient_utils.Error( |
435 "relative DEPS entry \"%s\" must begin with a slash" % d) | 470 "relative DEPS entry \"%s\" must begin with a slash" % d) |
436 # Create a scm just to query the full url. | 471 # Create a scm just to query the full url. |
437 scm = gclient_scm.CreateSCM(solution["url"], self.root_dir(), | 472 scm = gclient_scm.CreateSCM(solution.url, self.root_dir(), |
438 None) | 473 None) |
439 url = scm.FullUrlForRelativeUrl(url) | 474 url = scm.FullUrlForRelativeUrl(url) |
440 if d in deps and deps[d] != url: | 475 if d in deps and deps[d] != url: |
441 raise gclient_utils.Error( | 476 raise gclient_utils.Error( |
442 "Solutions have conflicting versions of dependency \"%s\"" % d) | 477 "Solutions have conflicting versions of dependency \"%s\"" % d) |
443 if d in solution_urls and solution_urls[d] != url: | 478 if d in solution_urls and solution_urls[d] != url: |
444 raise gclient_utils.Error( | 479 raise gclient_utils.Error( |
445 "Dependency \"%s\" conflicts with specified solution" % d) | 480 "Dependency \"%s\" conflicts with specified solution" % d) |
446 # Grab the dependency. | 481 # Grab the dependency. |
447 deps[d] = url | 482 deps[d] = url |
448 return deps | 483 return deps |
(...skipping 24 matching lines...) Expand all Loading... |
473 """ | 508 """ |
474 # Hooks only run for these command types. | 509 # Hooks only run for these command types. |
475 if not command in ('update', 'revert', 'runhooks'): | 510 if not command in ('update', 'revert', 'runhooks'): |
476 return | 511 return |
477 | 512 |
478 # Hooks only run when --nohooks is not specified | 513 # Hooks only run when --nohooks is not specified |
479 if self._options.nohooks: | 514 if self._options.nohooks: |
480 return | 515 return |
481 | 516 |
482 # Get any hooks from the .gclient file. | 517 # Get any hooks from the .gclient file. |
483 hooks = self.GetVar("hooks", []) | 518 hooks = self._deps_hooks[:] |
484 # Add any hooks found in DEPS files. | 519 # Add any hooks found in DEPS files. |
485 hooks.extend(self._deps_hooks) | 520 for d in self.dependencies: |
| 521 hooks.extend(d._deps_hooks) |
486 | 522 |
487 # If "--force" was specified, run all hooks regardless of what files have | 523 # If "--force" was specified, run all hooks regardless of what files have |
488 # changed. If the user is using git, then we don't know what files have | 524 # changed. If the user is using git, then we don't know what files have |
489 # changed so we always run all hooks. | 525 # changed so we always run all hooks. |
490 if self._options.force or is_using_git: | 526 if self._options.force or is_using_git: |
491 for hook_dict in hooks: | 527 for hook_dict in hooks: |
492 self._RunHookAction(hook_dict, []) | 528 self._RunHookAction(hook_dict, []) |
493 return | 529 return |
494 | 530 |
495 # Run hooks on the basis of whether the files from the gclient operation | 531 # Run hooks on the basis of whether the files from the gclient operation |
496 # match each hook's pattern. | 532 # match each hook's pattern. |
497 for hook_dict in hooks: | 533 for hook_dict in hooks: |
498 pattern = re.compile(hook_dict['pattern']) | 534 pattern = re.compile(hook_dict['pattern']) |
499 matching_file_list = [f for f in file_list if pattern.search(f)] | 535 matching_file_list = [f for f in file_list if pattern.search(f)] |
500 if matching_file_list: | 536 if matching_file_list: |
501 self._RunHookAction(hook_dict, matching_file_list) | 537 self._RunHookAction(hook_dict, matching_file_list) |
502 | 538 |
503 def _EnforceRevisions(self, solutions): | 539 def _EnforceRevisions(self): |
504 """Checks for revision overrides.""" | 540 """Checks for revision overrides.""" |
505 revision_overrides = {} | 541 revision_overrides = {} |
506 if self._options.head: | 542 if self._options.head: |
507 return revision_overrides | 543 return revision_overrides |
508 for s in solutions: | 544 for s in self.dependencies: |
509 if not s.get('safesync_url', None): | 545 if not s.safesync_url: |
510 continue | 546 continue |
511 handle = urllib.urlopen(s['safesync_url']) | 547 handle = urllib.urlopen(s.safesync_url) |
512 rev = handle.read().strip() | 548 rev = handle.read().strip() |
513 handle.close() | 549 handle.close() |
514 if len(rev): | 550 if len(rev): |
515 self._options.revisions.append('%s@%s' % (s['name'], rev)) | 551 self._options.revisions.append('%s@%s' % (s.name, rev)) |
516 if not self._options.revisions: | 552 if not self._options.revisions: |
517 return revision_overrides | 553 return revision_overrides |
518 # --revision will take over safesync_url. | 554 # --revision will take over safesync_url. |
519 solutions_names = [s['name'] for s in solutions] | 555 solutions_names = [s.name for s in self.dependencies] |
520 index = 0 | 556 index = 0 |
521 for revision in self._options.revisions: | 557 for revision in self._options.revisions: |
522 if not '@' in revision: | 558 if not '@' in revision: |
523 # Support for --revision 123 | 559 # Support for --revision 123 |
524 revision = '%s@%s' % (solutions_names[index], revision) | 560 revision = '%s@%s' % (solutions_names[index], revision) |
525 sol, rev = revision.split("@", 1) | 561 sol, rev = revision.split('@', 1) |
526 if not sol in solutions_names: | 562 if not sol in solutions_names: |
527 #raise gclient_utils.Error('%s is not a valid solution.' % sol) | 563 #raise gclient_utils.Error('%s is not a valid solution.' % sol) |
528 print >> sys.stderr, ('Please fix your script, having invalid ' | 564 print >> sys.stderr, ('Please fix your script, having invalid ' |
529 '--revision flags will soon considered an error.') | 565 '--revision flags will soon considered an error.') |
530 else: | 566 else: |
531 revision_overrides[sol] = rev | 567 revision_overrides[sol] = rev |
532 index += 1 | 568 index += 1 |
533 return revision_overrides | 569 return revision_overrides |
534 | 570 |
535 def RunOnDeps(self, command, args): | 571 def RunOnDeps(self, command, args): |
536 """Runs a command on each dependency in a client and its dependencies. | 572 """Runs a command on each dependency in a client and its dependencies. |
537 | 573 |
538 The module's dependencies are specified in its top-level DEPS files. | 574 The module's dependencies are specified in its top-level DEPS files. |
539 | 575 |
540 Args: | 576 Args: |
541 command: The command to use (e.g., 'status' or 'diff') | 577 command: The command to use (e.g., 'status' or 'diff') |
542 args: list of str - extra arguments to add to the command line. | 578 args: list of str - extra arguments to add to the command line. |
543 | 579 |
544 Raises: | 580 Raises: |
545 Error: If the client has conflicting entries. | 581 Error: If the client has conflicting entries. |
546 """ | 582 """ |
547 if not command in self.SUPPORTED_COMMANDS: | 583 if not command in self.SUPPORTED_COMMANDS: |
548 raise gclient_utils.Error("'%s' is an unsupported command" % command) | 584 raise gclient_utils.Error("'%s' is an unsupported command" % command) |
549 | 585 |
550 solutions = self.GetVar("solutions") | 586 if not self.dependencies: |
551 if not solutions: | |
552 raise gclient_utils.Error("No solution specified") | 587 raise gclient_utils.Error("No solution specified") |
553 revision_overrides = self._EnforceRevisions(solutions) | 588 revision_overrides = self._EnforceRevisions() |
554 | 589 |
555 # When running runhooks --force, there's no need to consult the SCM. | 590 # When running runhooks --force, there's no need to consult the SCM. |
556 # All known hooks are expected to run unconditionally regardless of working | 591 # All known hooks are expected to run unconditionally regardless of working |
557 # copy state, so skip the SCM status check. | 592 # copy state, so skip the SCM status check. |
558 run_scm = not (command == 'runhooks' and self._options.force) | 593 run_scm = not (command == 'runhooks' and self._options.force) |
559 | 594 |
560 entries = {} | 595 entries = {} |
561 entries_deps_content = {} | 596 entries_deps_content = {} |
562 file_list = [] | 597 file_list = [] |
563 # Run on the base solutions first. | 598 # Run on the base solutions first. |
564 for solution in solutions: | 599 for solution in self.dependencies: |
565 name = solution["name"] | 600 name = solution.name |
566 deps_file = solution.get("deps_file", self.DEPS_FILE) | |
567 if '/' in deps_file or '\\' in deps_file: | |
568 raise gclient_utils.Error('deps_file name must not be a path, just a ' | |
569 'filename.') | |
570 if name in entries: | 601 if name in entries: |
571 raise gclient_utils.Error("solution %s specified more than once" % name) | 602 raise gclient_utils.Error("solution %s specified more than once" % name) |
572 url = solution["url"] | 603 url = solution.url |
573 entries[name] = url | 604 entries[name] = url |
574 if run_scm and url: | 605 if run_scm and url: |
575 self._options.revision = revision_overrides.get(name) | 606 self._options.revision = revision_overrides.get(name) |
576 scm = gclient_scm.CreateSCM(url, self.root_dir(), name) | 607 scm = gclient_scm.CreateSCM(url, self.root_dir(), name) |
577 scm.RunCommand(command, self._options, args, file_list) | 608 scm.RunCommand(command, self._options, args, file_list) |
578 file_list = [os.path.join(name, f.strip()) for f in file_list] | 609 file_list = [os.path.join(name, f.strip()) for f in file_list] |
579 self._options.revision = None | 610 self._options.revision = None |
580 try: | 611 try: |
581 deps_content = gclient_utils.FileRead( | 612 deps_content = gclient_utils.FileRead( |
582 os.path.join(self.root_dir(), name, deps_file)) | 613 os.path.join(self.root_dir(), name, solution.deps_file)) |
583 except IOError, e: | 614 except IOError, e: |
584 if e.errno != errno.ENOENT: | 615 if e.errno != errno.ENOENT: |
585 raise | 616 raise |
586 deps_content = "" | 617 deps_content = "" |
587 entries_deps_content[name] = deps_content | 618 entries_deps_content[name] = deps_content |
588 | 619 |
589 # Process the dependencies next (sort alphanumerically to ensure that | 620 # Process the dependencies next (sort alphanumerically to ensure that |
590 # containing directories get populated first and for readability) | 621 # containing directories get populated first and for readability) |
591 deps = self._ParseAllDeps(entries, entries_deps_content) | 622 deps = self._ParseAllDeps(entries, entries_deps_content) |
592 deps_to_process = deps.keys() | 623 deps_to_process = deps.keys() |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
696 """Output revision info mapping for the client and its dependencies. | 727 """Output revision info mapping for the client and its dependencies. |
697 | 728 |
698 This allows the capture of an overall "revision" for the source tree that | 729 This allows the capture of an overall "revision" for the source tree that |
699 can be used to reproduce the same tree in the future. It is only useful for | 730 can be used to reproduce the same tree in the future. It is only useful for |
700 "unpinned dependencies", i.e. DEPS/deps references without a svn revision | 731 "unpinned dependencies", i.e. DEPS/deps references without a svn revision |
701 number or a git hash. A git branch name isn't "pinned" since the actual | 732 number or a git hash. A git branch name isn't "pinned" since the actual |
702 commit can change. | 733 commit can change. |
703 | 734 |
704 The --snapshot option allows creating a .gclient file to reproduce the tree. | 735 The --snapshot option allows creating a .gclient file to reproduce the tree. |
705 """ | 736 """ |
706 solutions = self.GetVar("solutions") | 737 if not self.dependencies: |
707 if not solutions: | |
708 raise gclient_utils.Error("No solution specified") | 738 raise gclient_utils.Error("No solution specified") |
709 | 739 |
710 # Inner helper to generate base url and rev tuple | 740 # Inner helper to generate base url and rev tuple |
711 def GetURLAndRev(name, original_url): | 741 def GetURLAndRev(name, original_url): |
712 url, _ = gclient_utils.SplitUrlRevision(original_url) | 742 url, _ = gclient_utils.SplitUrlRevision(original_url) |
713 scm = gclient_scm.CreateSCM(original_url, self.root_dir(), name) | 743 scm = gclient_scm.CreateSCM(original_url, self.root_dir(), name) |
714 return (url, scm.revinfo(self._options, [], None)) | 744 return (url, scm.revinfo(self._options, [], None)) |
715 | 745 |
716 # text of the snapshot gclient file | 746 # text of the snapshot gclient file |
717 new_gclient = "" | 747 new_gclient = "" |
718 # Dictionary of { path : SCM url } to ensure no duplicate solutions | 748 # Dictionary of { path : SCM url } to ensure no duplicate solutions |
719 solution_names = {} | 749 solution_names = {} |
720 entries = {} | 750 entries = {} |
721 entries_deps_content = {} | 751 entries_deps_content = {} |
722 # Run on the base solutions first. | 752 # Run on the base solutions first. |
723 for solution in solutions: | 753 for solution in self.dependencies: |
724 # Dictionary of { path : SCM url } to describe the gclient checkout | 754 # Dictionary of { path : SCM url } to describe the gclient checkout |
725 name = solution["name"] | 755 name = solution.name |
726 if name in solution_names: | 756 if name in solution_names: |
727 raise gclient_utils.Error("solution %s specified more than once" % name) | 757 raise gclient_utils.Error("solution %s specified more than once" % name) |
728 (url, rev) = GetURLAndRev(name, solution["url"]) | 758 (url, rev) = GetURLAndRev(name, solution.url) |
729 entries[name] = "%s@%s" % (url, rev) | 759 entries[name] = "%s@%s" % (url, rev) |
730 solution_names[name] = "%s@%s" % (url, rev) | 760 solution_names[name] = "%s@%s" % (url, rev) |
731 deps_file = solution.get("deps_file", self.DEPS_FILE) | 761 deps_file = solution.deps_file |
732 if '/' in deps_file or '\\' in deps_file: | 762 if '/' in deps_file or '\\' in deps_file: |
733 raise gclient_utils.Error('deps_file name must not be a path, just a ' | 763 raise gclient_utils.Error('deps_file name must not be a path, just a ' |
734 'filename.') | 764 'filename.') |
735 try: | 765 try: |
736 deps_content = gclient_utils.FileRead( | 766 deps_content = gclient_utils.FileRead( |
737 os.path.join(self.root_dir(), name, deps_file)) | 767 os.path.join(self.root_dir(), name, deps_file)) |
738 except IOError, e: | 768 except IOError, e: |
739 if e.errno != errno.ENOENT: | 769 if e.errno != errno.ENOENT: |
740 raise | 770 raise |
741 deps_content = "" | 771 deps_content = "" |
(...skipping 399 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1141 return CMDhelp(parser, argv) | 1171 return CMDhelp(parser, argv) |
1142 except gclient_utils.Error, e: | 1172 except gclient_utils.Error, e: |
1143 print >> sys.stderr, 'Error: %s' % str(e) | 1173 print >> sys.stderr, 'Error: %s' % str(e) |
1144 return 1 | 1174 return 1 |
1145 | 1175 |
1146 | 1176 |
1147 if '__main__' == __name__: | 1177 if '__main__' == __name__: |
1148 sys.exit(Main(sys.argv[1:])) | 1178 sys.exit(Main(sys.argv[1:])) |
1149 | 1179 |
1150 # vim: ts=2:sw=2:tw=80:et: | 1180 # vim: ts=2:sw=2:tw=80:et: |
OLD | NEW |