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