| 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 |