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 """Meta checkout manager supporting both Subversion and GIT. | 6 """Meta checkout manager supporting both Subversion and GIT. |
7 | 7 |
8 Files | 8 Files |
9 .gclient : Current client configuration, written by 'config' command. | 9 .gclient : Current client configuration, written by 'config' command. |
10 Format is a Python script defining 'solutions', a list whose | 10 Format is a Python script defining 'solutions', a list whose |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
42 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 |
43 by the list of matching files. | 43 by the list of matching files. |
44 | 44 |
45 Example: | 45 Example: |
46 hooks = [ | 46 hooks = [ |
47 { "pattern": "\\.(gif|jpe?g|pr0n|png)$", | 47 { "pattern": "\\.(gif|jpe?g|pr0n|png)$", |
48 "action": ["python", "image_indexer.py", "--all"]}, | 48 "action": ["python", "image_indexer.py", "--all"]}, |
49 ] | 49 ] |
50 """ | 50 """ |
51 | 51 |
52 __version__ = "0.5.1" | 52 __version__ = "0.5.2" |
53 | 53 |
54 import logging | 54 import logging |
55 import optparse | 55 import optparse |
56 import os | 56 import os |
57 import posixpath | 57 import posixpath |
58 import pprint | 58 import pprint |
59 import re | 59 import re |
60 import subprocess | 60 import subprocess |
61 import sys | 61 import sys |
62 import threading | 62 import threading |
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
237 elif var_name in self._local_scope.get("vars", {}): | 237 elif var_name in self._local_scope.get("vars", {}): |
238 return self._local_scope["vars"][var_name] | 238 return self._local_scope["vars"][var_name] |
239 raise gclient_utils.Error("Var is not defined: %s" % var_name) | 239 raise gclient_utils.Error("Var is not defined: %s" % var_name) |
240 | 240 |
241 | 241 |
242 class Dependency(GClientKeywords, WorkItem): | 242 class Dependency(GClientKeywords, WorkItem): |
243 """Object that represents a dependency checkout.""" | 243 """Object that represents a dependency checkout.""" |
244 DEPS_FILE = 'DEPS' | 244 DEPS_FILE = 'DEPS' |
245 | 245 |
246 def __init__(self, parent, name, url, safesync_url, custom_deps, | 246 def __init__(self, parent, name, url, safesync_url, custom_deps, |
247 custom_vars, deps_file): | 247 custom_vars, deps_file, should_process): |
248 GClientKeywords.__init__(self) | 248 GClientKeywords.__init__(self) |
249 self.parent = parent | 249 self.parent = parent |
250 self.name = name | 250 self.name = name |
251 self.url = url | 251 self.url = url |
252 self.parsed_url = None | 252 self.parsed_url = None |
253 # These 2 are only set in .gclient and not in DEPS files. | 253 # These 2 are only set in .gclient and not in DEPS files. |
254 self.safesync_url = safesync_url | 254 self.safesync_url = safesync_url |
255 self.custom_vars = custom_vars or {} | 255 self.custom_vars = custom_vars or {} |
256 self.custom_deps = custom_deps or {} | 256 self.custom_deps = custom_deps or {} |
257 self.deps_hooks = [] | 257 self.deps_hooks = [] |
258 self.dependencies = [] | 258 self.dependencies = [] |
259 self.deps_file = deps_file or self.DEPS_FILE | 259 self.deps_file = deps_file or self.DEPS_FILE |
260 # A cache of the files affected by the current operation, necessary for | 260 # A cache of the files affected by the current operation, necessary for |
261 # hooks. | 261 # hooks. |
262 self._file_list = [] | 262 self._file_list = [] |
263 # If it is not set to True, the dependency wasn't processed for its child | 263 # If it is not set to True, the dependency wasn't processed for its child |
264 # dependency, i.e. its DEPS wasn't read. | 264 # dependency, i.e. its DEPS wasn't read. |
265 self.deps_parsed = False | 265 self.deps_parsed = False |
266 # A direct reference is dependency that is referenced by a deps, deps_os or | 266 # This dependency should be processed, i.e. checked out |
267 # solution. A indirect one is one that was loaded with From() or that | 267 self.should_process = should_process |
268 # exceeded recursion limit. | |
269 self.direct_reference = False | |
270 # This dependency has been processed, i.e. checked out | 268 # This dependency has been processed, i.e. checked out |
271 self.processed = False | 269 self.processed = False |
272 # This dependency had its hook run | 270 # This dependency had its hook run |
273 self.hooks_ran = False | 271 self.hooks_ran = False |
274 # Required dependencies to run before running this one: | 272 # Required dependencies to run before running this one: |
275 self.requirements = [] | 273 self.requirements = [] |
276 if self.parent and self.parent.name: | 274 if self.parent and self.parent.name: |
277 self.requirements.append(self.parent.name) | 275 self.requirements.append(self.parent.name) |
278 if isinstance(self.url, self.FromImpl): | 276 if isinstance(self.url, self.FromImpl): |
279 self.requirements.append(self.url.module_name) | 277 self.requirements.append(self.url.module_name) |
280 | 278 |
281 # Sanity checks | 279 # Sanity checks |
282 if not self.name and self.parent: | 280 if not self.name and self.parent: |
283 raise gclient_utils.Error('Dependency without name') | 281 raise gclient_utils.Error('Dependency without name') |
284 if not isinstance(self.url, | 282 if not isinstance(self.url, |
285 (basestring, self.FromImpl, self.FileImpl, None.__class__)): | 283 (basestring, self.FromImpl, self.FileImpl, None.__class__)): |
286 raise gclient_utils.Error('dependency url must be either a string, None, ' | 284 raise gclient_utils.Error('dependency url must be either a string, None, ' |
287 'File() or From() instead of %s' % | 285 'File() or From() instead of %s' % |
288 self.url.__class__.__name__) | 286 self.url.__class__.__name__) |
289 if '/' in self.deps_file or '\\' in self.deps_file: | 287 if '/' in self.deps_file or '\\' in self.deps_file: |
290 raise gclient_utils.Error('deps_file name must not be a path, just a ' | 288 raise gclient_utils.Error('deps_file name must not be a path, just a ' |
291 'filename. %s' % self.deps_file) | 289 'filename. %s' % self.deps_file) |
292 | 290 |
293 def LateOverride(self, url): | 291 def LateOverride(self, url): |
294 """Resolves the parsed url from url. | 292 """Resolves the parsed url from url. |
295 | 293 |
296 Manages From() keyword accordingly. Do not touch self.parsed_url nor | 294 Manages From() keyword accordingly. Do not touch self.parsed_url nor |
297 self.url because it may called with other urls due to From().""" | 295 self.url because it may called with other urls due to From().""" |
| 296 assert self.parsed_url == None or not self.should_process, self.parsed_url |
298 overriden_url = self.get_custom_deps(self.name, url) | 297 overriden_url = self.get_custom_deps(self.name, url) |
299 if overriden_url != url: | 298 if overriden_url != url: |
300 logging.info('%s, %s was overriden to %s' % (self.name, url, | 299 logging.info('%s, %s was overriden to %s' % (self.name, url, |
301 overriden_url)) | 300 overriden_url)) |
302 return overriden_url | 301 return overriden_url |
303 elif isinstance(url, self.FromImpl): | 302 elif isinstance(url, self.FromImpl): |
304 ref = [dep for dep in self.tree(True) if url.module_name == dep.name] | 303 ref = [dep for dep in self.tree(True) if url.module_name == dep.name] |
305 if not ref: | 304 if not ref: |
306 raise gclient_utils.Error('Failed to find one reference to %s. %s' % ( | 305 raise gclient_utils.Error('Failed to find one reference to %s. %s' % ( |
307 url.module_name, ref)) | 306 url.module_name, ref)) |
308 # It may happen that len(ref) > 1 but it's no big deal. | 307 # It may happen that len(ref) > 1 but it's no big deal. |
309 ref = ref[0] | 308 ref = ref[0] |
310 sub_target = url.sub_target_name or self.name | 309 sub_target = url.sub_target_name or self.name |
311 # Make sure the referenced dependency DEPS file is loaded and file the | 310 # Make sure the referenced dependency DEPS file is loaded and file the |
312 # inner referenced dependency. | 311 # inner referenced dependency. |
313 ref.ParseDepsFile(False) | 312 ref.ParseDepsFile() |
314 found_dep = None | 313 found_dep = None |
315 for d in ref.dependencies: | 314 for d in ref.dependencies: |
316 if d.name == sub_target: | 315 if d.name == sub_target: |
317 found_dep = d | 316 found_dep = d |
318 break | 317 break |
319 if not found_dep: | 318 if not found_dep: |
320 raise gclient_utils.Error( | 319 raise gclient_utils.Error( |
321 'Couldn\'t find %s in %s, referenced by %s\n%s' % ( | 320 'Couldn\'t find %s in %s, referenced by %s\n%s' % ( |
322 sub_target, ref.name, self.name, str(self.root_parent()))) | 321 sub_target, ref.name, self.name, str(self.root_parent()))) |
323 # Call LateOverride() again. | 322 # Call LateOverride() again. |
(...skipping 20 matching lines...) Expand all Loading... |
344 return parsed_url | 343 return parsed_url |
345 elif isinstance(url, self.FileImpl): | 344 elif isinstance(url, self.FileImpl): |
346 parsed_url = url | 345 parsed_url = url |
347 logging.info('%s, %s -> %s (File)' % (self.name, url, parsed_url)) | 346 logging.info('%s, %s -> %s (File)' % (self.name, url, parsed_url)) |
348 return parsed_url | 347 return parsed_url |
349 elif url is None: | 348 elif url is None: |
350 return None | 349 return None |
351 else: | 350 else: |
352 raise gclient_utils.Error('Unkown url type') | 351 raise gclient_utils.Error('Unkown url type') |
353 | 352 |
354 def ParseDepsFile(self, direct_reference): | 353 def ParseDepsFile(self): |
355 """Parses the DEPS file for this dependency.""" | 354 """Parses the DEPS file for this dependency.""" |
356 if direct_reference: | 355 assert self.processed == True |
357 # Maybe it was referenced earlier by a From() keyword but it's now | |
358 # directly referenced. | |
359 self.direct_reference = direct_reference | |
360 if self.deps_parsed: | 356 if self.deps_parsed: |
361 logging.debug('%s was already parsed' % self.name) | 357 logging.debug('%s was already parsed' % self.name) |
362 return | 358 return |
363 self.deps_parsed = True | 359 self.deps_parsed = True |
364 filepath = os.path.join(self.root_dir(), self.name, self.deps_file) | 360 filepath = os.path.join(self.root_dir(), self.name, self.deps_file) |
365 if not os.path.isfile(filepath): | 361 if not os.path.isfile(filepath): |
366 logging.info('%s: No DEPS file found at %s' % (self.name, filepath)) | 362 logging.info('%s: No DEPS file found at %s' % (self.name, filepath)) |
367 return | 363 return |
368 deps_content = gclient_utils.FileRead(filepath) | 364 deps_content = gclient_utils.FileRead(filepath) |
369 logging.debug(deps_content) | 365 logging.debug(deps_content) |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
416 # dependency local path. | 412 # dependency local path. |
417 rel_deps[os.path.normpath(os.path.join(self.name, d))] = url | 413 rel_deps[os.path.normpath(os.path.join(self.name, d))] = url |
418 deps = rel_deps | 414 deps = rel_deps |
419 | 415 |
420 # Convert the deps into real Dependency. | 416 # Convert the deps into real Dependency. |
421 for name, url in deps.iteritems(): | 417 for name, url in deps.iteritems(): |
422 if name in [s.name for s in self.dependencies]: | 418 if name in [s.name for s in self.dependencies]: |
423 raise gclient_utils.Error( | 419 raise gclient_utils.Error( |
424 'The same name "%s" appears multiple times in the deps section' % | 420 'The same name "%s" appears multiple times in the deps section' % |
425 name) | 421 name) |
| 422 should_process = self.recursion_limit() and self.should_process |
| 423 if should_process: |
| 424 tree = dict((d.name, d) for d in self.tree(False)) |
| 425 if name in tree: |
| 426 if url == tree[name].url: |
| 427 logging.info('Won\'t process duplicate dependency %s' % tree[name]) |
| 428 # In theory we could keep it as a shadow of the other one. In |
| 429 # practice, simply ignore it. |
| 430 #should_process = False |
| 431 continue |
| 432 else: |
| 433 raise gclient_utils.Error( |
| 434 'Dependency %s specified more than once:\n %s\nvs\n %s' % |
| 435 (name, tree[name].hierarchy(), self.hierarchy())) |
426 self.dependencies.append(Dependency(self, name, url, None, None, None, | 436 self.dependencies.append(Dependency(self, name, url, None, None, None, |
427 None)) | 437 None, should_process)) |
428 logging.debug('Loaded: %s' % str(self)) | 438 logging.debug('Loaded: %s' % str(self)) |
429 | 439 |
430 def run(self, options, revision_overrides, command, args, work_queue): | 440 def run(self, options, revision_overrides, command, args, work_queue): |
431 """Runs 'command' before parsing the DEPS in case it's a initial checkout | 441 """Runs 'command' before parsing the DEPS in case it's a initial checkout |
432 or a revert.""" | 442 or a revert.""" |
433 assert self._file_list == [] | 443 assert self._file_list == [] |
| 444 if not self.should_process: |
| 445 return |
434 # When running runhooks, there's no need to consult the SCM. | 446 # When running runhooks, there's no need to consult the SCM. |
435 # All known hooks are expected to run unconditionally regardless of working | 447 # All known hooks are expected to run unconditionally regardless of working |
436 # copy state, so skip the SCM status check. | 448 # copy state, so skip the SCM status check. |
437 run_scm = command not in ('runhooks', None) | 449 run_scm = command not in ('runhooks', None) |
438 self.parsed_url = self.LateOverride(self.url) | 450 self.parsed_url = self.LateOverride(self.url) |
439 if run_scm and self.parsed_url: | 451 if run_scm and self.parsed_url: |
440 if isinstance(self.parsed_url, self.FileImpl): | 452 if isinstance(self.parsed_url, self.FileImpl): |
441 # Special support for single-file checkout. | 453 # Special support for single-file checkout. |
442 if not command in (None, 'cleanup', 'diff', 'pack', 'status'): | 454 if not command in (None, 'cleanup', 'diff', 'pack', 'status'): |
443 options.revision = self.parsed_url.GetRevision() | 455 options.revision = self.parsed_url.GetRevision() |
444 scm = gclient_scm.SVNWrapper(self.parsed_url.GetPath(), | 456 scm = gclient_scm.SVNWrapper(self.parsed_url.GetPath(), |
445 self.root_dir(), | 457 self.root_dir(), |
446 self.name) | 458 self.name) |
447 scm.RunCommand('updatesingle', options, | 459 scm.RunCommand('updatesingle', options, |
448 args + [self.parsed_url.GetFilename()], | 460 args + [self.parsed_url.GetFilename()], |
449 self._file_list) | 461 self._file_list) |
450 else: | 462 else: |
451 options.revision = revision_overrides.get(self.name) | 463 options.revision = revision_overrides.get(self.name) |
452 scm = gclient_scm.CreateSCM(self.parsed_url, self.root_dir(), self.name) | 464 scm = gclient_scm.CreateSCM(self.parsed_url, self.root_dir(), self.name) |
453 scm.RunCommand(command, options, args, self._file_list) | 465 scm.RunCommand(command, options, args, self._file_list) |
454 self._file_list = [os.path.join(self.name, f.strip()) | 466 self._file_list = [os.path.join(self.name, f.strip()) |
455 for f in self._file_list] | 467 for f in self._file_list] |
456 options.revision = None | 468 options.revision = None |
457 self.processed = True | 469 self.processed = True |
458 if self.recursion_limit(): | 470 if self.recursion_limit(): |
459 # Then we can parse the DEPS file. | 471 # Then we can parse the DEPS file. |
460 self.ParseDepsFile(True) | 472 self.ParseDepsFile() |
461 # Adjust the implicit dependency requirement; e.g. if a DEPS file contains | 473 # Adjust the implicit dependency requirement; e.g. if a DEPS file contains |
462 # both src/foo and src/foo/bar, src/foo/bar is implicitly dependent of | 474 # both src/foo and src/foo/bar, src/foo/bar is implicitly dependent of |
463 # src/foo. Yes, it's O(n^2)... It's important to do that before | 475 # src/foo. Yes, it's O(n^2)... It's important to do that before |
464 # enqueueing them. | 476 # enqueueing them. |
465 for s in self.dependencies: | 477 for s in self.dependencies: |
466 for s2 in self.dependencies: | 478 for s2 in self.dependencies: |
467 if s is s2: | 479 if s is s2: |
468 continue | 480 continue |
469 if s.name.startswith(posixpath.join(s2.name, '')): | 481 if s.name.startswith(posixpath.join(s2.name, '')): |
470 s.requirements.append(s2.name) | 482 s.requirements.append(s2.name) |
471 | 483 |
472 # Parse the dependencies of this dependency. | 484 # Parse the dependencies of this dependency. |
473 for s in self.dependencies: | 485 for s in self.dependencies: |
474 work_queue.enqueue(s) | 486 work_queue.enqueue(s) |
475 | 487 |
476 def RunHooksRecursively(self, options): | 488 def RunHooksRecursively(self, options): |
477 """Evaluates all hooks, running actions as needed. run() | 489 """Evaluates all hooks, running actions as needed. run() |
478 must have been called before to load the DEPS.""" | 490 must have been called before to load the DEPS.""" |
| 491 assert self.hooks_ran == False |
| 492 if not self.should_process: |
| 493 return |
479 # If "--force" was specified, run all hooks regardless of what files have | 494 # If "--force" was specified, run all hooks regardless of what files have |
480 # changed. | 495 # changed. |
481 if self.deps_hooks and self.direct_reference: | 496 if self.deps_hooks: |
482 # TODO(maruel): If the user is using git or git-svn, then we don't know | 497 # TODO(maruel): If the user is using git or git-svn, then we don't know |
483 # what files have changed so we always run all hooks. It'd be nice to fix | 498 # what files have changed so we always run all hooks. It'd be nice to fix |
484 # that. | 499 # that. |
485 if (options.force or | 500 if (options.force or |
486 isinstance(self.parsed_url, self.FileImpl) or | 501 isinstance(self.parsed_url, self.FileImpl) or |
487 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or | 502 gclient_scm.GetScmName(self.parsed_url) in ('git', None) or |
488 os.path.isdir(os.path.join(self.root_dir(), self.name, '.git'))): | 503 os.path.isdir(os.path.join(self.root_dir(), self.name, '.git'))): |
489 for hook_dict in self.deps_hooks: | 504 for hook_dict in self.deps_hooks: |
490 self._RunHookAction(hook_dict, []) | 505 self._RunHookAction(hook_dict, []) |
491 else: | 506 else: |
(...skipping 14 matching lines...) Expand all Loading... |
506 file_list[i].startswith('/')): | 521 file_list[i].startswith('/')): |
507 file_list[i] = file_list[i][1:] | 522 file_list[i] = file_list[i][1:] |
508 | 523 |
509 # Run hooks on the basis of whether the files from the gclient operation | 524 # Run hooks on the basis of whether the files from the gclient operation |
510 # match each hook's pattern. | 525 # match each hook's pattern. |
511 for hook_dict in self.deps_hooks: | 526 for hook_dict in self.deps_hooks: |
512 pattern = re.compile(hook_dict['pattern']) | 527 pattern = re.compile(hook_dict['pattern']) |
513 matching_file_list = [f for f in file_list if pattern.search(f)] | 528 matching_file_list = [f for f in file_list if pattern.search(f)] |
514 if matching_file_list: | 529 if matching_file_list: |
515 self._RunHookAction(hook_dict, matching_file_list) | 530 self._RunHookAction(hook_dict, matching_file_list) |
516 if self.recursion_limit(): | 531 for s in self.dependencies: |
517 for s in self.dependencies: | 532 s.RunHooksRecursively(options) |
518 s.RunHooksRecursively(options) | |
519 | 533 |
520 def _RunHookAction(self, hook_dict, matching_file_list): | 534 def _RunHookAction(self, hook_dict, matching_file_list): |
521 """Runs the action from a single hook.""" | 535 """Runs the action from a single hook.""" |
522 # A single DEPS file can specify multiple hooks so this function can be | 536 # A single DEPS file can specify multiple hooks so this function can be |
523 # called multiple times on a single Dependency. | 537 # called multiple times on a single Dependency. |
524 #assert self.hooks_ran == False | 538 #assert self.hooks_ran == False |
525 self.hooks_ran = True | 539 self.hooks_ran = True |
526 logging.debug(hook_dict) | 540 logging.debug(hook_dict) |
527 logging.debug(matching_file_list) | 541 logging.debug(matching_file_list) |
528 command = hook_dict['action'][:] | 542 command = hook_dict['action'][:] |
(...skipping 18 matching lines...) Expand all Loading... |
547 def enforced_os(self): | 561 def enforced_os(self): |
548 return self.parent.enforced_os() | 562 return self.parent.enforced_os() |
549 | 563 |
550 def recursion_limit(self): | 564 def recursion_limit(self): |
551 return self.parent.recursion_limit() - 1 | 565 return self.parent.recursion_limit() - 1 |
552 | 566 |
553 def tree(self, include_all): | 567 def tree(self, include_all): |
554 return self.parent.tree(include_all) | 568 return self.parent.tree(include_all) |
555 | 569 |
556 def subtree(self, include_all): | 570 def subtree(self, include_all): |
| 571 """Breadth first""" |
557 result = [] | 572 result = [] |
558 # Add breadth-first. | 573 for d in self.dependencies: |
559 if self.direct_reference or include_all: | 574 if d.should_process or include_all: |
560 for d in self.dependencies: | |
561 result.append(d) | 575 result.append(d) |
562 for d in self.dependencies: | 576 for d in self.dependencies: |
563 result.extend(d.subtree(include_all)) | 577 result.extend(d.subtree(include_all)) |
564 return result | 578 return result |
565 | 579 |
566 def get_custom_deps(self, name, url): | 580 def get_custom_deps(self, name, url): |
567 """Returns a custom deps if applicable.""" | 581 """Returns a custom deps if applicable.""" |
568 if self.parent: | 582 if self.parent: |
569 url = self.parent.get_custom_deps(name, url) | 583 url = self.parent.get_custom_deps(name, url) |
570 # None is a valid return value to disable a dependency. | 584 # None is a valid return value to disable a dependency. |
571 return self.custom_deps.get(name, url) | 585 return self.custom_deps.get(name, url) |
572 | 586 |
573 def file_list(self): | 587 def file_list(self): |
574 result = self._file_list[:] | 588 result = self._file_list[:] |
575 for d in self.dependencies: | 589 for d in self.dependencies: |
576 result.extend(d.file_list()) | 590 result.extend(d.file_list()) |
577 return result | 591 return result |
578 | 592 |
579 def __str__(self): | 593 def __str__(self): |
580 out = [] | 594 out = [] |
581 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps', | 595 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps', |
582 'custom_vars', 'deps_hooks', '_file_list', 'processed', | 596 'custom_vars', 'deps_hooks', '_file_list', 'should_process', |
583 'hooks_ran', 'deps_parsed', 'requirements', 'direct_reference'): | 597 'processed', 'hooks_ran', 'deps_parsed', 'requirements'): |
584 # 'deps_file' | 598 # 'deps_file' |
585 if self.__dict__[i]: | 599 if self.__dict__[i]: |
586 out.append('%s: %s' % (i, self.__dict__[i])) | 600 out.append('%s: %s' % (i, self.__dict__[i])) |
587 | 601 |
588 for d in self.dependencies: | 602 for d in self.dependencies: |
589 out.extend([' ' + x for x in str(d).splitlines()]) | 603 out.extend([' ' + x for x in str(d).splitlines()]) |
590 out.append('') | 604 out.append('') |
591 return '\n'.join(out) | 605 return '\n'.join(out) |
592 | 606 |
593 def __repr__(self): | 607 def __repr__(self): |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
648 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\ | 662 DEFAULT_SNAPSHOT_FILE_TEXT = ("""\ |
649 # Snapshot generated with gclient revinfo --snapshot | 663 # Snapshot generated with gclient revinfo --snapshot |
650 solutions = [ | 664 solutions = [ |
651 %(solution_list)s] | 665 %(solution_list)s] |
652 """) | 666 """) |
653 | 667 |
654 def __init__(self, root_dir, options): | 668 def __init__(self, root_dir, options): |
655 # Do not change previous behavior. Only solution level and immediate DEPS | 669 # Do not change previous behavior. Only solution level and immediate DEPS |
656 # are processed. | 670 # are processed. |
657 self._recursion_limit = 2 | 671 self._recursion_limit = 2 |
658 Dependency.__init__(self, None, None, None, None, None, None, None) | 672 Dependency.__init__(self, None, None, None, None, None, None, None, True) |
659 self._options = options | 673 self._options = options |
660 if options.deps_os: | 674 if options.deps_os: |
661 enforced_os = options.deps_os.split(',') | 675 enforced_os = options.deps_os.split(',') |
662 else: | 676 else: |
663 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')] | 677 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')] |
664 if 'all' in enforced_os: | 678 if 'all' in enforced_os: |
665 enforced_os = self.DEPS_OS_CHOICES.itervalues() | 679 enforced_os = self.DEPS_OS_CHOICES.itervalues() |
666 self._enforced_os = list(set(enforced_os)) | 680 self._enforced_os = list(set(enforced_os)) |
667 self._root_dir = root_dir | 681 self._root_dir = root_dir |
668 self.config_content = None | 682 self.config_content = None |
669 | 683 |
670 def SetConfig(self, content): | 684 def SetConfig(self, content): |
671 assert self.dependencies == [] | 685 assert self.dependencies == [] |
672 config_dict = {} | 686 config_dict = {} |
673 self.config_content = content | 687 self.config_content = content |
674 try: | 688 try: |
675 exec(content, config_dict) | 689 exec(content, config_dict) |
676 except SyntaxError, e: | 690 except SyntaxError, e: |
677 gclient_utils.SyntaxErrorToError('.gclient', e) | 691 gclient_utils.SyntaxErrorToError('.gclient', e) |
678 for s in config_dict.get('solutions', []): | 692 for s in config_dict.get('solutions', []): |
679 try: | 693 try: |
| 694 tree = dict((d.name, d) for d in self.tree(False)) |
| 695 if s['name'] in tree: |
| 696 raise gclient_utils.Error( |
| 697 'Dependency %s specified more than once in .gclient' % s['name']) |
680 self.dependencies.append(Dependency( | 698 self.dependencies.append(Dependency( |
681 self, s['name'], s['url'], | 699 self, s['name'], s['url'], |
682 s.get('safesync_url', None), | 700 s.get('safesync_url', None), |
683 s.get('custom_deps', {}), | 701 s.get('custom_deps', {}), |
684 s.get('custom_vars', {}), | 702 s.get('custom_vars', {}), |
685 None)) | 703 None, |
| 704 True)) |
686 except KeyError: | 705 except KeyError: |
687 raise gclient_utils.Error('Invalid .gclient file. Solution is ' | 706 raise gclient_utils.Error('Invalid .gclient file. Solution is ' |
688 'incomplete: %s' % s) | 707 'incomplete: %s' % s) |
689 # .gclient can have hooks. | 708 # .gclient can have hooks. |
690 self.deps_hooks = config_dict.get('hooks', []) | 709 self.deps_hooks = config_dict.get('hooks', []) |
691 self.direct_reference = True | 710 self.direct_reference = True |
692 self.deps_parsed = True | 711 self.deps_parsed = True |
693 | 712 |
694 def SaveConfig(self): | 713 def SaveConfig(self): |
695 gclient_utils.FileWrite(os.path.join(self.root_dir(), | 714 gclient_utils.FileWrite(os.path.join(self.root_dir(), |
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
887 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient}) | 906 print(self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient}) |
888 else: | 907 else: |
889 entries = {} | 908 entries = {} |
890 for d in self.tree(False): | 909 for d in self.tree(False): |
891 if self._options.actual: | 910 if self._options.actual: |
892 entries[d.name] = GetURLAndRev(d) | 911 entries[d.name] = GetURLAndRev(d) |
893 else: | 912 else: |
894 entries[d.name] = d.parsed_url | 913 entries[d.name] = d.parsed_url |
895 keys = sorted(entries.keys()) | 914 keys = sorted(entries.keys()) |
896 for x in keys: | 915 for x in keys: |
897 line = '%s: %s' % (x, entries[x]) | 916 print('%s: %s' % (x, entries[x])) |
898 if x is not keys[-1]: | |
899 line += ';' | |
900 print line | |
901 logging.info(str(self)) | 917 logging.info(str(self)) |
902 | 918 |
903 def ParseDepsFile(self, direct_reference): | 919 def ParseDepsFile(self): |
904 """No DEPS to parse for a .gclient file.""" | 920 """No DEPS to parse for a .gclient file.""" |
905 raise gclient_utils.Error('Internal error') | 921 raise gclient_utils.Error('Internal error') |
906 | 922 |
907 def root_dir(self): | 923 def root_dir(self): |
908 """Root directory of gclient checkout.""" | 924 """Root directory of gclient checkout.""" |
909 return self._root_dir | 925 return self._root_dir |
910 | 926 |
911 def enforced_os(self): | 927 def enforced_os(self): |
912 """What deps_os entries that are to be parsed.""" | 928 """What deps_os entries that are to be parsed.""" |
913 return self._enforced_os | 929 return self._enforced_os |
(...skipping 390 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1304 return CMDhelp(parser, argv) | 1320 return CMDhelp(parser, argv) |
1305 except gclient_utils.Error, e: | 1321 except gclient_utils.Error, e: |
1306 print >> sys.stderr, 'Error: %s' % str(e) | 1322 print >> sys.stderr, 'Error: %s' % str(e) |
1307 return 1 | 1323 return 1 |
1308 | 1324 |
1309 | 1325 |
1310 if '__main__' == __name__: | 1326 if '__main__' == __name__: |
1311 sys.exit(Main(sys.argv[1:])) | 1327 sys.exit(Main(sys.argv[1:])) |
1312 | 1328 |
1313 # vim: ts=2:sw=2:tw=80:et: | 1329 # vim: ts=2:sw=2:tw=80:et: |
OLD | NEW |