| 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 """Meta checkout manager supporting both Subversion and GIT. |
| 7 | |
| 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 | |
| 10 repositories, along with other modules it depends on, also in Subversion or Git, | |
| 11 but possibly on multiple respositories, making a wrapper system apparently | |
| 12 necessary. | |
| 13 | 7 |
| 14 Files | 8 Files |
| 15 .gclient : Current client configuration, written by 'config' command. | 9 .gclient : Current client configuration, written by 'config' command. |
| 16 Format is a Python script defining 'solutions', a list whose | 10 Format is a Python script defining 'solutions', a list whose |
| 17 entries each are maps binding the strings "name" and "url" | 11 entries each are maps binding the strings "name" and "url" |
| 18 to strings specifying the name and location of the client | 12 to strings specifying the name and location of the client |
| 19 module, as well as "custom_deps" to a map similar to the DEPS | 13 module, as well as "custom_deps" to a map similar to the DEPS |
| 20 file below. | 14 file below. |
| 21 .gclient_entries : A cache constructed by 'update' command. Format is a | 15 .gclient_entries : A cache constructed by 'update' command. Format is a |
| 22 Python script defining 'entries', a list of the names | 16 Python script defining 'entries', a list of the names |
| 23 of all modules in the client | 17 of all modules in the client |
| 24 <module>/DEPS : Python script defining var 'deps' as a map from each requisite | 18 <module>/DEPS : Python script defining var 'deps' as a map from each requisite |
| 25 submodule name to a URL where it can be found (via one SCM) | 19 submodule name to a URL where it can be found (via one SCM) |
| 26 | 20 |
| 27 Hooks | 21 Hooks |
| 28 .gclient and DEPS files may optionally contain a list named "hooks" to | 22 .gclient and DEPS files may optionally contain a list named "hooks" to |
| 29 allow custom actions to be performed based on files that have changed in the | 23 allow custom actions to be performed based on files that have changed in the |
| 30 working copy as a result of a "sync"/"update" or "revert" operation. This | 24 working copy as a result of a "sync"/"update" or "revert" operation. This |
| 31 could be prevented by using --nohooks (hooks run by default). Hooks can also | 25 can be prevented by using --nohooks (hooks run by default). Hooks can also |
| 32 be forced to run with the "runhooks" operation. If "sync" is run with | 26 be forced to run with the "runhooks" operation. If "sync" is run with |
| 33 --force, all known hooks will run regardless of the state of the working | 27 --force, all known hooks will run regardless of the state of the working |
| 34 copy. | 28 copy. |
| 35 | 29 |
| 36 Each item in a "hooks" list is a dict, containing these two keys: | 30 Each item in a "hooks" list is a dict, containing these two keys: |
| 37 "pattern" The associated value is a string containing a regular | 31 "pattern" The associated value is a string containing a regular |
| 38 expression. When a file whose pathname matches the expression | 32 expression. When a file whose pathname matches the expression |
| 39 is checked out, updated, or reverted, the hook's "action" will | 33 is checked out, updated, or reverted, the hook's "action" will |
| 40 run. | 34 run. |
| 41 "action" A list describing a command to run along with its arguments, if | 35 "action" A list describing a command to run along with its arguments, if |
| 42 any. An action command will run at most one time per gclient | 36 any. An action command will run at most one time per gclient |
| 43 invocation, regardless of how many files matched the pattern. | 37 invocation, regardless of how many files matched the pattern. |
| 44 The action is executed in the same directory as the .gclient | 38 The action is executed in the same directory as the .gclient |
| 45 file. If the first item in the list is the string "python", | 39 file. If the first item in the list is the string "python", |
| 46 the current Python interpreter (sys.executable) will be used | 40 the current Python interpreter (sys.executable) will be used |
| 47 to run the command. If the list contains string "$matching_files" | 41 to run the command. If the list contains string "$matching_files" |
| 48 it will be removed from the list and the list will be extended | 42 it will be removed from the list and the list will be extended |
| 49 by the list of matching files. | 43 by the list of matching files. |
| 50 | 44 |
| 51 Example: | 45 Example: |
| 52 hooks = [ | 46 hooks = [ |
| 53 { "pattern": "\\.(gif|jpe?g|pr0n|png)$", | 47 { "pattern": "\\.(gif|jpe?g|pr0n|png)$", |
| 54 "action": ["python", "image_indexer.py", "--all"]}, | 48 "action": ["python", "image_indexer.py", "--all"]}, |
| 55 ] | 49 ] |
| 56 """ | 50 """ |
| 57 | 51 |
| 58 __version__ = "0.4" | 52 __version__ = "0.4.1" |
| 59 | 53 |
| 60 import errno | 54 import errno |
| 61 import logging | 55 import logging |
| 62 import optparse | 56 import optparse |
| 63 import os | 57 import os |
| 64 import pprint | 58 import pprint |
| 65 import re | 59 import re |
| 66 import sys | 60 import sys |
| 67 import urlparse | 61 import urlparse |
| 68 import urllib | 62 import urllib |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 151 if var_name in self._custom_vars: | 145 if var_name in self._custom_vars: |
| 152 return self._custom_vars[var_name] | 146 return self._custom_vars[var_name] |
| 153 elif var_name in self._local_scope.get("vars", {}): | 147 elif var_name in self._local_scope.get("vars", {}): |
| 154 return self._local_scope["vars"][var_name] | 148 return self._local_scope["vars"][var_name] |
| 155 raise gclient_utils.Error("Var is not defined: %s" % var_name) | 149 raise gclient_utils.Error("Var is not defined: %s" % var_name) |
| 156 | 150 |
| 157 | 151 |
| 158 class Dependency(GClientKeywords): | 152 class Dependency(GClientKeywords): |
| 159 """Object that represents a dependency checkout.""" | 153 """Object that represents a dependency checkout.""" |
| 160 DEPS_FILE = 'DEPS' | 154 DEPS_FILE = 'DEPS' |
| 155 |
| 161 def __init__(self, parent, name, url, safesync_url=None, custom_deps=None, | 156 def __init__(self, parent, name, url, safesync_url=None, custom_deps=None, |
| 162 custom_vars=None, deps_file=None): | 157 custom_vars=None, deps_file=None): |
| 163 GClientKeywords.__init__(self) | 158 GClientKeywords.__init__(self) |
| 164 self.parent = parent | 159 self.parent = parent |
| 165 self.name = name | 160 self.name = name |
| 166 self.url = url | 161 self.url = url |
| 167 # These 2 are only set in .gclient and not in DEPS files. | 162 # These 2 are only set in .gclient and not in DEPS files. |
| 168 self.safesync_url = safesync_url | 163 self.safesync_url = safesync_url |
| 169 self.custom_vars = custom_vars or {} | 164 self.custom_vars = custom_vars or {} |
| 170 self.custom_deps = custom_deps or {} | 165 self.custom_deps = custom_deps or {} |
| 166 self.deps_hooks = [] |
| 171 self.dependencies = [] | 167 self.dependencies = [] |
| 172 self.deps_file = deps_file or self.DEPS_FILE | 168 self.deps_file = deps_file or self.DEPS_FILE |
| 173 self._deps_hooks = [] | |
| 174 | 169 |
| 175 # Sanity checks | 170 # Sanity checks |
| 176 if not self.name and self.parent: | 171 if not self.name and self.parent: |
| 177 raise gclient_utils.Error('Dependency without name') | 172 raise gclient_utils.Error('Dependency without name') |
| 178 if not isinstance(self.url, | 173 if not isinstance(self.url, |
| 179 (basestring, self.FromImpl, self.FileImpl, None.__class__)): | 174 (basestring, self.FromImpl, self.FileImpl, None.__class__)): |
| 180 raise gclient_utils.Error('dependency url must be either a string, None, ' | 175 raise gclient_utils.Error('dependency url must be either a string, None, ' |
| 181 'File() or From() instead of %s' % | 176 'File() or From() instead of %s' % |
| 182 self.url.__class__.__name__) | 177 self.url.__class__.__name__) |
| 183 if '/' in self.deps_file or '\\' in self.deps_file: | 178 if '/' in self.deps_file or '\\' in self.deps_file: |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 231 ] | 226 ] |
| 232 """) | 227 """) |
| 233 | 228 |
| 234 def __init__(self, root_dir, options): | 229 def __init__(self, root_dir, options): |
| 235 Dependency.__init__(self, None, None, None) | 230 Dependency.__init__(self, None, None, None) |
| 236 self._root_dir = root_dir | 231 self._root_dir = root_dir |
| 237 self._options = options | 232 self._options = options |
| 238 self.config_content = None | 233 self.config_content = None |
| 239 | 234 |
| 240 def SetConfig(self, content): | 235 def SetConfig(self, content): |
| 236 assert self.dependencies == [] |
| 241 config_dict = {} | 237 config_dict = {} |
| 242 self.config_content = content | 238 self.config_content = content |
| 243 try: | 239 try: |
| 244 exec(content, config_dict) | 240 exec(content, config_dict) |
| 245 except SyntaxError, e: | 241 except SyntaxError, e: |
| 246 try: | 242 try: |
| 247 # Try to construct a human readable error message | 243 # Try to construct a human readable error message |
| 248 error_message = [ | 244 error_message = [ |
| 249 'There is a syntax error in your configuration file.', | 245 'There is a syntax error in your configuration file.', |
| 250 'Line #%s, character %s:' % (e.lineno, e.offset), | 246 'Line #%s, character %s:' % (e.lineno, e.offset), |
| 251 '"%s"' % re.sub(r'[\r\n]*$', '', e.text) ] | 247 '"%s"' % re.sub(r'[\r\n]*$', '', e.text) ] |
| 252 except: | 248 except: |
| 253 # Something went wrong, re-raise the original exception | 249 # Something went wrong, re-raise the original exception |
| 254 raise e | 250 raise e |
| 255 else: | 251 else: |
| 256 # Raise a new exception with the human readable message: | 252 # Raise a new exception with the human readable message: |
| 257 raise gclient_utils.Error('\n'.join(error_message)) | 253 raise gclient_utils.Error('\n'.join(error_message)) |
| 258 for s in config_dict.get('solutions', []): | 254 for s in config_dict.get('solutions', []): |
| 259 self.dependencies.append(Dependency( | 255 self.dependencies.append(Dependency( |
| 260 self, s['name'], s['url'], | 256 self, s['name'], s['url'], |
| 261 s.get('safesync_url', None), | 257 s.get('safesync_url', None), |
| 262 s.get('custom_deps', {}), | 258 s.get('custom_deps', {}), |
| 263 s.get('custom_vars', {}))) | 259 s.get('custom_vars', {}))) |
| 264 # .gclient can have hooks. | 260 # .gclient can have hooks. |
| 265 self._deps_hooks = config_dict.get('hooks', []) | 261 self.deps_hooks = config_dict.get('hooks', []) |
| 266 | 262 |
| 267 def SaveConfig(self): | 263 def SaveConfig(self): |
| 268 gclient_utils.FileWrite(os.path.join(self.root_dir(), | 264 gclient_utils.FileWrite(os.path.join(self.root_dir(), |
| 269 self._options.config_filename), | 265 self._options.config_filename), |
| 270 self.config_content) | 266 self.config_content) |
| 271 | 267 |
| 272 def _LoadConfig(self): | |
| 273 client_source = gclient_utils.FileRead( | |
| 274 os.path.join(self.root_dir(), self._options.config_filename)) | |
| 275 self.SetConfig(client_source) | |
| 276 | |
| 277 @staticmethod | 268 @staticmethod |
| 278 def LoadCurrentConfig(options, from_dir=None): | 269 def LoadCurrentConfig(options): |
| 279 """Searches for and loads a .gclient file relative to the current working | 270 """Searches for and loads a .gclient file relative to the current working |
| 280 dir. | 271 dir. Returns a GClient object.""" |
| 281 | 272 path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename) |
| 282 Returns: | 273 if not path: |
| 283 A dict representing the contents of the .gclient file or an empty dict if | 274 return None |
| 284 the .gclient file doesn't exist. | |
| 285 """ | |
| 286 if not from_dir: | |
| 287 from_dir = os.curdir | |
| 288 path = os.path.realpath(from_dir) | |
| 289 while not os.path.exists(os.path.join(path, options.config_filename)): | |
| 290 split_path = os.path.split(path) | |
| 291 if not split_path[1]: | |
| 292 return None | |
| 293 path = split_path[0] | |
| 294 client = GClient(path, options) | 275 client = GClient(path, options) |
| 295 client._LoadConfig() | 276 client.SetConfig(gclient_utils.FileRead( |
| 277 os.path.join(path, options.config_filename))) |
| 296 return client | 278 return client |
| 297 | 279 |
| 298 def SetDefaultConfig(self, solution_name, solution_url, safesync_url): | 280 def SetDefaultConfig(self, solution_name, solution_url, safesync_url): |
| 299 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % { | 281 self.SetConfig(self.DEFAULT_CLIENT_FILE_TEXT % { |
| 300 'solution_name': solution_name, | 282 'solution_name': solution_name, |
| 301 'solution_url': solution_url, | 283 'solution_url': solution_url, |
| 302 'safesync_url' : safesync_url, | 284 'safesync_url' : safesync_url, |
| 303 }) | 285 }) |
| 304 | 286 |
| 305 def _SaveEntries(self, entries): | 287 def _SaveEntries(self, entries): |
| 306 """Creates a .gclient_entries file to record the list of unique checkouts. | 288 """Creates a .gclient_entries file to record the list of unique checkouts. |
| 307 | 289 |
| 308 The .gclient_entries file lives in the same directory as .gclient. | 290 The .gclient_entries file lives in the same directory as .gclient. |
| 309 | 291 |
| 310 Args: | 292 Args: |
| 311 entries: A sequence of solution names. | 293 entries: A sequence of solution names. |
| 312 """ | 294 """ |
| 313 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It | 295 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It |
| 314 # makes testing a bit too fun. | 296 # makes testing a bit too fun. |
| 315 result = pprint.pformat(entries, 2) | 297 result = pprint.pformat(entries, 2) |
| 316 if result.startswith('{\''): | 298 if result.startswith('{\''): |
| 317 result = '{ \'' + result[2:] | 299 result = '{ \'' + result[2:] |
| 318 text = "entries = \\\n" + result + '\n' | 300 text = "entries = \\\n" + result + '\n' |
| 319 file_path = os.path.join(self.root_dir(), self._options.entries_filename) | 301 file_path = os.path.join(self.root_dir(), self._options.entries_filename) |
| 320 gclient_utils.FileWrite(file_path, text) | 302 gclient_utils.FileWrite(file_path, text) |
| 321 | 303 |
| 322 def _ReadEntries(self): | 304 def _ReadEntries(self): |
| 323 """Read the .gclient_entries file for the given client. | 305 """Read the .gclient_entries file for the given client. |
| 324 | 306 |
| 325 Args: | |
| 326 client: The client for which the entries file should be read. | |
| 327 | |
| 328 Returns: | 307 Returns: |
| 329 A sequence of solution names, which will be empty if there is the | 308 A sequence of solution names, which will be empty if there is the |
| 330 entries file hasn't been created yet. | 309 entries file hasn't been created yet. |
| 331 """ | 310 """ |
| 332 scope = {} | 311 scope = {} |
| 333 filename = os.path.join(self.root_dir(), self._options.entries_filename) | 312 filename = os.path.join(self.root_dir(), self._options.entries_filename) |
| 334 if not os.path.exists(filename): | 313 if not os.path.exists(filename): |
| 335 return [] | 314 return [] |
| 336 exec(gclient_utils.FileRead(filename), scope) | 315 exec(gclient_utils.FileRead(filename), scope) |
| 337 return scope["entries"] | 316 return scope['entries'] |
| 338 | 317 |
| 339 def _ParseSolutionDeps(self, solution_name, solution_deps_content, | 318 def _ParseSolutionDeps(self, solution_name, solution_deps_content, |
| 340 custom_vars, parse_hooks): | 319 custom_vars, parse_hooks): |
| 341 """Parses the DEPS file for the specified solution. | 320 """Parses the DEPS file for the specified solution. |
| 342 | 321 |
| 343 Args: | 322 Args: |
| 344 solution_name: The name of the solution to query. | 323 solution_name: The name of the solution to query. |
| 345 solution_deps_content: Content of the DEPS file for the solution | 324 solution_deps_content: Content of the DEPS file for the solution |
| 346 custom_vars: A dict of vars to override any vars defined in the DEPS file. | 325 custom_vars: A dict of vars to override any vars defined in the DEPS file. |
| 347 | 326 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 384 # platform, but this is the best we can do. | 363 # platform, but this is the best we can do. |
| 385 deps.update([x for x in os_deps.items() if not x[0] in deps]) | 364 deps.update([x for x in os_deps.items() if not x[0] in deps]) |
| 386 else: | 365 else: |
| 387 deps.update(os_deps) | 366 deps.update(os_deps) |
| 388 | 367 |
| 389 if 'hooks' in local_scope and parse_hooks: | 368 if 'hooks' in local_scope and parse_hooks: |
| 390 # TODO(maruel): Temporary Hack. Since this function is misplaced, find the | 369 # TODO(maruel): Temporary Hack. Since this function is misplaced, find the |
| 391 # right 'self' to add the hooks. | 370 # right 'self' to add the hooks. |
| 392 for d in self.dependencies: | 371 for d in self.dependencies: |
| 393 if d.name == solution_name: | 372 if d.name == solution_name: |
| 394 d._deps_hooks.extend(local_scope['hooks']) | 373 d.deps_hooks.extend(local_scope['hooks']) |
| 395 break | 374 break |
| 396 | 375 |
| 397 # If use_relative_paths is set in the DEPS file, regenerate | 376 # If use_relative_paths is set in the DEPS file, regenerate |
| 398 # the dictionary using paths relative to the directory containing | 377 # the dictionary using paths relative to the directory containing |
| 399 # the DEPS file. | 378 # the DEPS file. |
| 400 if local_scope.get('use_relative_paths'): | 379 if local_scope.get('use_relative_paths'): |
| 401 rel_deps = {} | 380 rel_deps = {} |
| 402 for d, url in deps.items(): | 381 for d, url in deps.items(): |
| 403 # normpath is required to allow DEPS to use .. in their | 382 # normpath is required to allow DEPS to use .. in their |
| 404 # dependency local path. | 383 # dependency local path. |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 508 """ | 487 """ |
| 509 # Hooks only run for these command types. | 488 # Hooks only run for these command types. |
| 510 if not command in ('update', 'revert', 'runhooks'): | 489 if not command in ('update', 'revert', 'runhooks'): |
| 511 return | 490 return |
| 512 | 491 |
| 513 # Hooks only run when --nohooks is not specified | 492 # Hooks only run when --nohooks is not specified |
| 514 if self._options.nohooks: | 493 if self._options.nohooks: |
| 515 return | 494 return |
| 516 | 495 |
| 517 # Get any hooks from the .gclient file. | 496 # Get any hooks from the .gclient file. |
| 518 hooks = self._deps_hooks[:] | 497 hooks = self.deps_hooks[:] |
| 519 # Add any hooks found in DEPS files. | 498 # Add any hooks found in DEPS files. |
| 520 for d in self.dependencies: | 499 for d in self.dependencies: |
| 521 hooks.extend(d._deps_hooks) | 500 hooks.extend(d.deps_hooks) |
| 522 | 501 |
| 523 # If "--force" was specified, run all hooks regardless of what files have | 502 # If "--force" was specified, run all hooks regardless of what files have |
| 524 # changed. If the user is using git, then we don't know what files have | 503 # changed. If the user is using git, then we don't know what files have |
| 525 # changed so we always run all hooks. | 504 # changed so we always run all hooks. |
| 526 if self._options.force or is_using_git: | 505 if self._options.force or is_using_git: |
| 527 for hook_dict in hooks: | 506 for hook_dict in hooks: |
| 528 self._RunHookAction(hook_dict, []) | 507 self._RunHookAction(hook_dict, []) |
| 529 return | 508 return |
| 530 | 509 |
| 531 # Run hooks on the basis of whether the files from the gclient operation | 510 # Run hooks on the basis of whether the files from the gclient operation |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 564 print >> sys.stderr, ('Please fix your script, having invalid ' | 543 print >> sys.stderr, ('Please fix your script, having invalid ' |
| 565 '--revision flags will soon considered an error.') | 544 '--revision flags will soon considered an error.') |
| 566 else: | 545 else: |
| 567 revision_overrides[sol] = rev | 546 revision_overrides[sol] = rev |
| 568 index += 1 | 547 index += 1 |
| 569 return revision_overrides | 548 return revision_overrides |
| 570 | 549 |
| 571 def RunOnDeps(self, command, args): | 550 def RunOnDeps(self, command, args): |
| 572 """Runs a command on each dependency in a client and its dependencies. | 551 """Runs a command on each dependency in a client and its dependencies. |
| 573 | 552 |
| 574 The module's dependencies are specified in its top-level DEPS files. | |
| 575 | |
| 576 Args: | 553 Args: |
| 577 command: The command to use (e.g., 'status' or 'diff') | 554 command: The command to use (e.g., 'status' or 'diff') |
| 578 args: list of str - extra arguments to add to the command line. | 555 args: list of str - extra arguments to add to the command line. |
| 579 | |
| 580 Raises: | |
| 581 Error: If the client has conflicting entries. | |
| 582 """ | 556 """ |
| 583 if not command in self.SUPPORTED_COMMANDS: | 557 if not command in self.SUPPORTED_COMMANDS: |
| 584 raise gclient_utils.Error("'%s' is an unsupported command" % command) | 558 raise gclient_utils.Error("'%s' is an unsupported command" % command) |
| 585 | 559 |
| 586 if not self.dependencies: | 560 if not self.dependencies: |
| 587 raise gclient_utils.Error("No solution specified") | 561 raise gclient_utils.Error("No solution specified") |
| 588 revision_overrides = self._EnforceRevisions() | 562 revision_overrides = self._EnforceRevisions() |
| 589 | 563 |
| 590 # When running runhooks --force, there's no need to consult the SCM. | 564 # When running runhooks --force, there's no need to consult the SCM. |
| 591 # All known hooks are expected to run unconditionally regardless of working | 565 # All known hooks are expected to run unconditionally regardless of working |
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 827 | 801 |
| 828 | 802 |
| 829 #### gclient commands. | 803 #### gclient commands. |
| 830 | 804 |
| 831 | 805 |
| 832 def CMDcleanup(parser, args): | 806 def CMDcleanup(parser, args): |
| 833 """Cleans up all working copies. | 807 """Cleans up all working copies. |
| 834 | 808 |
| 835 Mostly svn-specific. Simply runs 'svn cleanup' for each module. | 809 Mostly svn-specific. Simply runs 'svn cleanup' for each module. |
| 836 """ | 810 """ |
| 837 parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", | 811 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
| 838 help="override deps for the specified (comma-separated) " | 812 help='override deps for the specified (comma-separated) ' |
| 839 "platform(s); 'all' will process all deps_os " | 813 'platform(s); \'all\' will process all deps_os ' |
| 840 "references") | 814 'references') |
| 841 (options, args) = parser.parse_args(args) | 815 (options, args) = parser.parse_args(args) |
| 842 client = GClient.LoadCurrentConfig(options) | 816 client = GClient.LoadCurrentConfig(options) |
| 843 if not client: | 817 if not client: |
| 844 raise gclient_utils.Error("client not configured; see 'gclient config'") | 818 raise gclient_utils.Error('client not configured; see \'gclient config\'') |
| 845 if options.verbose: | 819 if options.verbose: |
| 846 # Print out the .gclient file. This is longer than if we just printed the | 820 # Print out the .gclient file. This is longer than if we just printed the |
| 847 # client dict, but more legible, and it might contain helpful comments. | 821 # client dict, but more legible, and it might contain helpful comments. |
| 848 print(client.config_content) | 822 print(client.config_content) |
| 849 return client.RunOnDeps('cleanup', args) | 823 return client.RunOnDeps('cleanup', args) |
| 850 | 824 |
| 851 | 825 |
| 852 @attr('usage', '[url] [safesync url]') | 826 @attr('usage', '[url] [safesync url]') |
| 853 def CMDconfig(parser, args): | 827 def CMDconfig(parser, args): |
| 854 """Create a .gclient file in the current directory. | 828 """Create a .gclient file in the current directory. |
| 855 | 829 |
| 856 This specifies the configuration for further commands. After update/sync, | 830 This specifies the configuration for further commands. After update/sync, |
| 857 top-level DEPS files in each module are read to determine dependent | 831 top-level DEPS files in each module are read to determine dependent |
| 858 modules to operate on as well. If optional [url] parameter is | 832 modules to operate on as well. If optional [url] parameter is |
| 859 provided, then configuration is read from a specified Subversion server | 833 provided, then configuration is read from a specified Subversion server |
| 860 URL. | 834 URL. |
| 861 """ | 835 """ |
| 862 parser.add_option("--spec", | 836 parser.add_option('--spec', |
| 863 help="create a gclient file containing the provided " | 837 help='create a gclient file containing the provided ' |
| 864 "string. Due to Cygwin/Python brokenness, it " | 838 'string. Due to Cygwin/Python brokenness, it ' |
| 865 "probably can't contain any newlines.") | 839 'probably can\'t contain any newlines.') |
| 866 parser.add_option("--name", | 840 parser.add_option('--name', |
| 867 help="overrides the default name for the solution") | 841 help='overrides the default name for the solution') |
| 868 (options, args) = parser.parse_args(args) | 842 (options, args) = parser.parse_args(args) |
| 869 if ((options.spec and args) or len(args) > 2 or | 843 if ((options.spec and args) or len(args) > 2 or |
| 870 (not options.spec and not args)): | 844 (not options.spec and not args)): |
| 871 parser.error('Inconsistent arguments. Use either --spec or one or 2 args') | 845 parser.error('Inconsistent arguments. Use either --spec or one or 2 args') |
| 872 | 846 |
| 873 if os.path.exists(options.config_filename): | 847 if os.path.exists(options.config_filename): |
| 874 raise gclient_utils.Error("%s file already exists in the current directory" | 848 raise gclient_utils.Error('%s file already exists in the current directory' |
| 875 % options.config_filename) | 849 % options.config_filename) |
| 876 client = GClient('.', options) | 850 client = GClient('.', options) |
| 877 if options.spec: | 851 if options.spec: |
| 878 client.SetConfig(options.spec) | 852 client.SetConfig(options.spec) |
| 879 else: | 853 else: |
| 880 base_url = args[0].rstrip('/') | 854 base_url = args[0].rstrip('/') |
| 881 if not options.name: | 855 if not options.name: |
| 882 name = base_url.split("/")[-1] | 856 name = base_url.split('/')[-1] |
| 883 else: | 857 else: |
| 884 # specify an alternate relpath for the given URL. | 858 # specify an alternate relpath for the given URL. |
| 885 name = options.name | 859 name = options.name |
| 886 safesync_url = "" | 860 safesync_url = '' |
| 887 if len(args) > 1: | 861 if len(args) > 1: |
| 888 safesync_url = args[1] | 862 safesync_url = args[1] |
| 889 client.SetDefaultConfig(name, base_url, safesync_url) | 863 client.SetDefaultConfig(name, base_url, safesync_url) |
| 890 client.SaveConfig() | 864 client.SaveConfig() |
| 891 return 0 | 865 return 0 |
| 892 | 866 |
| 893 | 867 |
| 894 def CMDexport(parser, args): | 868 def CMDexport(parser, args): |
| 895 """Wrapper for svn export for all managed directories.""" | 869 """Wrapper for svn export for all managed directories.""" |
| 896 parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", | 870 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
| 897 help="override deps for the specified (comma-separated) " | 871 help='override deps for the specified (comma-separated) ' |
| 898 "platform(s); 'all' will process all deps_os " | 872 'platform(s); \'all\' will process all deps_os ' |
| 899 "references") | 873 'references') |
| 900 (options, args) = parser.parse_args(args) | 874 (options, args) = parser.parse_args(args) |
| 901 if len(args) != 1: | 875 if len(args) != 1: |
| 902 raise gclient_utils.Error("Need directory name") | 876 raise gclient_utils.Error('Need directory name') |
| 903 client = GClient.LoadCurrentConfig(options) | 877 client = GClient.LoadCurrentConfig(options) |
| 904 | 878 |
| 905 if not client: | 879 if not client: |
| 906 raise gclient_utils.Error("client not configured; see 'gclient config'") | 880 raise gclient_utils.Error('client not configured; see \'gclient config\'') |
| 907 | 881 |
| 908 if options.verbose: | 882 if options.verbose: |
| 909 # Print out the .gclient file. This is longer than if we just printed the | 883 # Print out the .gclient file. This is longer than if we just printed the |
| 910 # client dict, but more legible, and it might contain helpful comments. | 884 # client dict, but more legible, and it might contain helpful comments. |
| 911 print(client.config_content) | 885 print(client.config_content) |
| 912 return client.RunOnDeps('export', args) | 886 return client.RunOnDeps('export', args) |
| 913 | 887 |
| 914 | 888 |
| 915 @attr('epilog', """Example: | 889 @attr('epilog', """Example: |
| 916 gclient pack > patch.txt | 890 gclient pack > patch.txt |
| 917 generate simple patch for configured client and dependences | 891 generate simple patch for configured client and dependences |
| 918 """) | 892 """) |
| 919 def CMDpack(parser, args): | 893 def CMDpack(parser, args): |
| 920 """Generate a patch which can be applied at the root of the tree. | 894 """Generate a patch which can be applied at the root of the tree. |
| 921 | 895 |
| 922 Internally, runs 'svn diff'/'git diff' on each checked out module and | 896 Internally, runs 'svn diff'/'git diff' on each checked out module and |
| 923 dependencies, and performs minimal postprocessing of the output. The | 897 dependencies, and performs minimal postprocessing of the output. The |
| 924 resulting patch is printed to stdout and can be applied to a freshly | 898 resulting patch is printed to stdout and can be applied to a freshly |
| 925 checked out tree via 'patch -p0 < patchfile'. | 899 checked out tree via 'patch -p0 < patchfile'. |
| 926 """ | 900 """ |
| 927 parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", | 901 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
| 928 help="override deps for the specified (comma-separated) " | 902 help='override deps for the specified (comma-separated) ' |
| 929 "platform(s); 'all' will process all deps_os " | 903 'platform(s); \'all\' will process all deps_os ' |
| 930 "references") | 904 'references') |
| 931 (options, args) = parser.parse_args(args) | 905 (options, args) = parser.parse_args(args) |
| 932 client = GClient.LoadCurrentConfig(options) | 906 client = GClient.LoadCurrentConfig(options) |
| 933 if not client: | 907 if not client: |
| 934 raise gclient_utils.Error("client not configured; see 'gclient config'") | 908 raise gclient_utils.Error('client not configured; see \'gclient config\'') |
| 935 if options.verbose: | 909 if options.verbose: |
| 936 # Print out the .gclient file. This is longer than if we just printed the | 910 # Print out the .gclient file. This is longer than if we just printed the |
| 937 # client dict, but more legible, and it might contain helpful comments. | 911 # client dict, but more legible, and it might contain helpful comments. |
| 938 print(client.config_content) | 912 print(client.config_content) |
| 939 return client.RunOnDeps('pack', args) | 913 return client.RunOnDeps('pack', args) |
| 940 | 914 |
| 941 | 915 |
| 942 def CMDstatus(parser, args): | 916 def CMDstatus(parser, args): |
| 943 """Show modification status for every dependencies.""" | 917 """Show modification status for every dependencies.""" |
| 944 parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", | 918 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
| 945 help="override deps for the specified (comma-separated) " | 919 help='override deps for the specified (comma-separated) ' |
| 946 "platform(s); 'all' will process all deps_os " | 920 'platform(s); \'all\' will process all deps_os ' |
| 947 "references") | 921 'references') |
| 948 (options, args) = parser.parse_args(args) | 922 (options, args) = parser.parse_args(args) |
| 949 client = GClient.LoadCurrentConfig(options) | 923 client = GClient.LoadCurrentConfig(options) |
| 950 if not client: | 924 if not client: |
| 951 raise gclient_utils.Error("client not configured; see 'gclient config'") | 925 raise gclient_utils.Error('client not configured; see \'gclient config\'') |
| 952 if options.verbose: | 926 if options.verbose: |
| 953 # Print out the .gclient file. This is longer than if we just printed the | 927 # Print out the .gclient file. This is longer than if we just printed the |
| 954 # client dict, but more legible, and it might contain helpful comments. | 928 # client dict, but more legible, and it might contain helpful comments. |
| 955 print(client.config_content) | 929 print(client.config_content) |
| 956 return client.RunOnDeps('status', args) | 930 return client.RunOnDeps('status', args) |
| 957 | 931 |
| 958 | 932 |
| 959 @attr('epilog', """Examples: | 933 @attr('epilog', """Examples: |
| 960 gclient sync | 934 gclient sync |
| 961 update files from SCM according to current configuration, | 935 update files from SCM according to current configuration, |
| 962 *for modules which have changed since last update or sync* | 936 *for modules which have changed since last update or sync* |
| 963 gclient sync --force | 937 gclient sync --force |
| 964 update files from SCM according to current configuration, for | 938 update files from SCM according to current configuration, for |
| 965 all modules (useful for recovering files deleted from local copy) | 939 all modules (useful for recovering files deleted from local copy) |
| 966 gclient sync --revision src@31000 | 940 gclient sync --revision src@31000 |
| 967 update src directory to r31000 | 941 update src directory to r31000 |
| 968 """) | 942 """) |
| 969 def CMDsync(parser, args): | 943 def CMDsync(parser, args): |
| 970 """Checkout/update all modules.""" | 944 """Checkout/update all modules.""" |
| 971 parser.add_option("-f", "--force", action="store_true", | 945 parser.add_option('-f', '--force', action='store_true', |
| 972 help="force update even for unchanged modules") | 946 help='force update even for unchanged modules') |
| 973 parser.add_option("-n", "--nohooks", action="store_true", | 947 parser.add_option('-n', '--nohooks', action='store_true', |
| 974 help="don't run hooks after the update is complete") | 948 help='don\'t run hooks after the update is complete') |
| 975 parser.add_option("-r", "--revision", action="append", | 949 parser.add_option('-r', '--revision', action='append', |
| 976 dest="revisions", metavar="REV", default=[], | 950 dest='revisions', metavar='REV', default=[], |
| 977 help="Enforces revision/hash for the solutions with the " | 951 help='Enforces revision/hash for the solutions with the ' |
| 978 "format src@rev. The src@ part is optional and can be " | 952 'format src@rev. The src@ part is optional and can be ' |
| 979 "skipped. -r can be used multiple times when .gclient " | 953 'skipped. -r can be used multiple times when .gclient ' |
| 980 "has multiple solutions configured and will work even " | 954 'has multiple solutions configured and will work even ' |
| 981 "if the src@ part is skipped.") | 955 'if the src@ part is skipped.') |
| 982 parser.add_option("-H", "--head", action="store_true", | 956 parser.add_option('-H', '--head', action='store_true', |
| 983 help="skips any safesync_urls specified in " | 957 help='skips any safesync_urls specified in ' |
| 984 "configured solutions and sync to head instead") | 958 'configured solutions and sync to head instead') |
| 985 parser.add_option("-D", "--delete_unversioned_trees", action="store_true", | 959 parser.add_option('-D', '--delete_unversioned_trees', action='store_true', |
| 986 help="delete any unexpected unversioned trees " | 960 help='delete any unexpected unversioned trees ' |
| 987 "that are in the checkout") | 961 'that are in the checkout') |
| 988 parser.add_option("-R", "--reset", action="store_true", | 962 parser.add_option('-R', '--reset', action='store_true', |
| 989 help="resets any local changes before updating (git only)") | 963 help='resets any local changes before updating (git only)') |
| 990 parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", | 964 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
| 991 help="override deps for the specified (comma-separated) " | 965 help='override deps for the specified (comma-separated) ' |
| 992 "platform(s); 'all' will process all deps_os " | 966 'platform(s); \'all\' will process all deps_os ' |
| 993 "references") | 967 'references') |
| 994 parser.add_option("-m", "--manually_grab_svn_rev", action="store_true", | 968 parser.add_option('-m', '--manually_grab_svn_rev', action='store_true', |
| 995 help="Skip svn up whenever possible by requesting " | 969 help='Skip svn up whenever possible by requesting ' |
| 996 "actual HEAD revision from the repository") | 970 'actual HEAD revision from the repository') |
| 997 (options, args) = parser.parse_args(args) | 971 (options, args) = parser.parse_args(args) |
| 998 client = GClient.LoadCurrentConfig(options) | 972 client = GClient.LoadCurrentConfig(options) |
| 999 | 973 |
| 1000 if not client: | 974 if not client: |
| 1001 raise gclient_utils.Error("client not configured; see 'gclient config'") | 975 raise gclient_utils.Error('client not configured; see \'gclient config\'') |
| 1002 | 976 |
| 1003 if options.revisions and options.head: | 977 if options.revisions and options.head: |
| 1004 # TODO(maruel): Make it a parser.error if it doesn't break any builder. | 978 # TODO(maruel): Make it a parser.error if it doesn't break any builder. |
| 1005 print("Warning: you cannot use both --head and --revision") | 979 print('Warning: you cannot use both --head and --revision') |
| 1006 | 980 |
| 1007 if options.verbose: | 981 if options.verbose: |
| 1008 # Print out the .gclient file. This is longer than if we just printed the | 982 # Print out the .gclient file. This is longer than if we just printed the |
| 1009 # client dict, but more legible, and it might contain helpful comments. | 983 # client dict, but more legible, and it might contain helpful comments. |
| 1010 print(client.config_content) | 984 print(client.config_content) |
| 1011 return client.RunOnDeps('update', args) | 985 return client.RunOnDeps('update', args) |
| 1012 | 986 |
| 1013 | 987 |
| 1014 def CMDupdate(parser, args): | 988 def CMDupdate(parser, args): |
| 1015 """Alias for the sync command. Deprecated.""" | 989 """Alias for the sync command. Deprecated.""" |
| 1016 return CMDsync(parser, args) | 990 return CMDsync(parser, args) |
| 1017 | 991 |
| 1018 def CMDdiff(parser, args): | 992 def CMDdiff(parser, args): |
| 1019 """Displays local diff for every dependencies.""" | 993 """Displays local diff for every dependencies.""" |
| 1020 parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", | 994 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
| 1021 help="override deps for the specified (comma-separated) " | 995 help='override deps for the specified (comma-separated) ' |
| 1022 "platform(s); 'all' will process all deps_os " | 996 'platform(s); \'all\' will process all deps_os ' |
| 1023 "references") | 997 'references') |
| 1024 (options, args) = parser.parse_args(args) | 998 (options, args) = parser.parse_args(args) |
| 1025 client = GClient.LoadCurrentConfig(options) | 999 client = GClient.LoadCurrentConfig(options) |
| 1026 if not client: | 1000 if not client: |
| 1027 raise gclient_utils.Error("client not configured; see 'gclient config'") | 1001 raise gclient_utils.Error('client not configured; see \'gclient config\'') |
| 1028 if options.verbose: | 1002 if options.verbose: |
| 1029 # Print out the .gclient file. This is longer than if we just printed the | 1003 # Print out the .gclient file. This is longer than if we just printed the |
| 1030 # client dict, but more legible, and it might contain helpful comments. | 1004 # client dict, but more legible, and it might contain helpful comments. |
| 1031 print(client.config_content) | 1005 print(client.config_content) |
| 1032 return client.RunOnDeps('diff', args) | 1006 return client.RunOnDeps('diff', args) |
| 1033 | 1007 |
| 1034 | 1008 |
| 1035 def CMDrevert(parser, args): | 1009 def CMDrevert(parser, args): |
| 1036 """Revert all modifications in every dependencies.""" | 1010 """Revert all modifications in every dependencies.""" |
| 1037 parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", | 1011 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
| 1038 help="override deps for the specified (comma-separated) " | 1012 help='override deps for the specified (comma-separated) ' |
| 1039 "platform(s); 'all' will process all deps_os " | 1013 'platform(s); \'all\' will process all deps_os ' |
| 1040 "references") | 1014 'references') |
| 1041 parser.add_option("-n", "--nohooks", action="store_true", | 1015 parser.add_option('-n', '--nohooks', action='store_true', |
| 1042 help="don't run hooks after the revert is complete") | 1016 help='don\'t run hooks after the revert is complete') |
| 1043 (options, args) = parser.parse_args(args) | 1017 (options, args) = parser.parse_args(args) |
| 1044 # --force is implied. | 1018 # --force is implied. |
| 1045 options.force = True | 1019 options.force = True |
| 1046 client = GClient.LoadCurrentConfig(options) | 1020 client = GClient.LoadCurrentConfig(options) |
| 1047 if not client: | 1021 if not client: |
| 1048 raise gclient_utils.Error("client not configured; see 'gclient config'") | 1022 raise gclient_utils.Error('client not configured; see \'gclient config\'') |
| 1049 return client.RunOnDeps('revert', args) | 1023 return client.RunOnDeps('revert', args) |
| 1050 | 1024 |
| 1051 | 1025 |
| 1052 def CMDrunhooks(parser, args): | 1026 def CMDrunhooks(parser, args): |
| 1053 """Runs hooks for files that have been modified in the local working copy.""" | 1027 """Runs hooks for files that have been modified in the local working copy.""" |
| 1054 parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", | 1028 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
| 1055 help="override deps for the specified (comma-separated) " | 1029 help='override deps for the specified (comma-separated) ' |
| 1056 "platform(s); 'all' will process all deps_os " | 1030 'platform(s); \'all\' will process all deps_os ' |
| 1057 "references") | 1031 'references') |
| 1058 parser.add_option("-f", "--force", action="store_true", default=True, | 1032 parser.add_option('-f', '--force', action='store_true', default=True, |
| 1059 help="Deprecated. No effect.") | 1033 help='Deprecated. No effect.') |
| 1060 (options, args) = parser.parse_args(args) | 1034 (options, args) = parser.parse_args(args) |
| 1061 client = GClient.LoadCurrentConfig(options) | 1035 client = GClient.LoadCurrentConfig(options) |
| 1062 if not client: | 1036 if not client: |
| 1063 raise gclient_utils.Error("client not configured; see 'gclient config'") | 1037 raise gclient_utils.Error('client not configured; see \'gclient config\'') |
| 1064 if options.verbose: | 1038 if options.verbose: |
| 1065 # Print out the .gclient file. This is longer than if we just printed the | 1039 # Print out the .gclient file. This is longer than if we just printed the |
| 1066 # client dict, but more legible, and it might contain helpful comments. | 1040 # client dict, but more legible, and it might contain helpful comments. |
| 1067 print(client.config_content) | 1041 print(client.config_content) |
| 1068 options.force = True | 1042 options.force = True |
| 1069 options.nohooks = False | 1043 options.nohooks = False |
| 1070 return client.RunOnDeps('runhooks', args) | 1044 return client.RunOnDeps('runhooks', args) |
| 1071 | 1045 |
| 1072 | 1046 |
| 1073 def CMDrevinfo(parser, args): | 1047 def CMDrevinfo(parser, args): |
| 1074 """Output revision info mapping for the client and its dependencies. | 1048 """Output revision info mapping for the client and its dependencies. |
| 1075 | 1049 |
| 1076 This allows the capture of an overall "revision" for the source tree that | 1050 This allows the capture of an overall 'revision' for the source tree that |
| 1077 can be used to reproduce the same tree in the future. It is only useful for | 1051 can be used to reproduce the same tree in the future. It is only useful for |
| 1078 "unpinned dependencies", i.e. DEPS/deps references without a svn revision | 1052 'unpinned dependencies', i.e. DEPS/deps references without a svn revision |
| 1079 number or a git hash. A git branch name isn't "pinned" since the actual | 1053 number or a git hash. A git branch name isn't 'pinned' since the actual |
| 1080 commit can change. | 1054 commit can change. |
| 1081 """ | 1055 """ |
| 1082 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', | 1056 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
| 1083 help='override deps for the specified (comma-separated) ' | 1057 help='override deps for the specified (comma-separated) ' |
| 1084 'platform(s); \'all\' will process all deps_os ' | 1058 'platform(s); \'all\' will process all deps_os ' |
| 1085 'references') | 1059 'references') |
| 1086 parser.add_option('-s', '--snapshot', action='store_true', | 1060 parser.add_option('-s', '--snapshot', action='store_true', |
| 1087 help='creates a snapshot .gclient file of the current ' | 1061 help='creates a snapshot .gclient file of the current ' |
| 1088 'version of all repositories to reproduce the tree, ' | 1062 'version of all repositories to reproduce the tree, ' |
| 1089 'implies -a') | 1063 'implies -a') |
| 1090 (options, args) = parser.parse_args(args) | 1064 (options, args) = parser.parse_args(args) |
| 1091 client = GClient.LoadCurrentConfig(options) | 1065 client = GClient.LoadCurrentConfig(options) |
| 1092 if not client: | 1066 if not client: |
| 1093 raise gclient_utils.Error("client not configured; see 'gclient config'") | 1067 raise gclient_utils.Error('client not configured; see \'gclient config\'') |
| 1094 client.PrintRevInfo() | 1068 client.PrintRevInfo() |
| 1095 return 0 | 1069 return 0 |
| 1096 | 1070 |
| 1097 | 1071 |
| 1098 def Command(name): | 1072 def Command(name): |
| 1099 return getattr(sys.modules[__name__], 'CMD' + name, None) | 1073 return getattr(sys.modules[__name__], 'CMD' + name, None) |
| 1100 | 1074 |
| 1101 | 1075 |
| 1102 def CMDhelp(parser, args): | 1076 def CMDhelp(parser, args): |
| 1103 """Prints list of commands or help for a specific command.""" | 1077 """Prints list of commands or help for a specific command.""" |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1171 return CMDhelp(parser, argv) | 1145 return CMDhelp(parser, argv) |
| 1172 except gclient_utils.Error, e: | 1146 except gclient_utils.Error, e: |
| 1173 print >> sys.stderr, 'Error: %s' % str(e) | 1147 print >> sys.stderr, 'Error: %s' % str(e) |
| 1174 return 1 | 1148 return 1 |
| 1175 | 1149 |
| 1176 | 1150 |
| 1177 if '__main__' == __name__: | 1151 if '__main__' == __name__: |
| 1178 sys.exit(Main(sys.argv[1:])) | 1152 sys.exit(Main(sys.argv[1:])) |
| 1179 | 1153 |
| 1180 # vim: ts=2:sw=2:tw=80:et: | 1154 # vim: ts=2:sw=2:tw=80:et: |
| OLD | NEW |