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 |