| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """A wrapper script to manage a set of client modules in different SCM. | 6 """A wrapper script to manage a set of client modules in different SCM. |
| 7 | 7 |
| 8 This script is intended to be used to help basic management of client | 8 This script is intended to be used to help basic management of client |
| 9 program sources residing in one or more Subversion modules and Git | 9 program sources residing in one or more Subversion modules and Git |
| 10 repositories, along with other modules it depends on, also in Subversion or Git, | 10 repositories, along with other modules it depends on, also in Subversion or Git, |
| (...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 210 self._config_dict = {} | 210 self._config_dict = {} |
| 211 self._deps_hooks = [] | 211 self._deps_hooks = [] |
| 212 | 212 |
| 213 def SetConfig(self, content): | 213 def SetConfig(self, content): |
| 214 self._config_dict = {} | 214 self._config_dict = {} |
| 215 self.config_content = content | 215 self.config_content = content |
| 216 try: | 216 try: |
| 217 exec(content, self._config_dict) | 217 exec(content, self._config_dict) |
| 218 except SyntaxError, e: | 218 except SyntaxError, e: |
| 219 try: | 219 try: |
| 220 __pychecker__ = 'no-objattrs' | |
| 221 # Try to construct a human readable error message | 220 # Try to construct a human readable error message |
| 222 error_message = [ | 221 error_message = [ |
| 223 'There is a syntax error in your configuration file.', | 222 'There is a syntax error in your configuration file.', |
| 224 'Line #%s, character %s:' % (e.lineno, e.offset), | 223 'Line #%s, character %s:' % (e.lineno, e.offset), |
| 225 '"%s"' % re.sub(r'[\r\n]*$', '', e.text) ] | 224 '"%s"' % re.sub(r'[\r\n]*$', '', e.text) ] |
| 226 except: | 225 except: |
| 227 # Something went wrong, re-raise the original exception | 226 # Something went wrong, re-raise the original exception |
| 228 raise e | 227 raise e |
| 229 else: | 228 else: |
| 230 # Raise a new exception with the human readable message: | 229 # Raise a new exception with the human readable message: |
| 231 raise gclient_utils.Error('\n'.join(error_message)) | 230 raise gclient_utils.Error('\n'.join(error_message)) |
| 232 | 231 |
| 233 def SaveConfig(self): | 232 def SaveConfig(self): |
| 234 gclient_utils.FileWrite(os.path.join(self._root_dir, | 233 gclient_utils.FileWrite(os.path.join(self.root_dir(), |
| 235 self._options.config_filename), | 234 self._options.config_filename), |
| 236 self.config_content) | 235 self.config_content) |
| 237 | 236 |
| 238 def _LoadConfig(self): | 237 def _LoadConfig(self): |
| 239 client_source = gclient_utils.FileRead( | 238 client_source = gclient_utils.FileRead( |
| 240 os.path.join(self._root_dir, self._options.config_filename)) | 239 os.path.join(self.root_dir(), self._options.config_filename)) |
| 241 self.SetConfig(client_source) | 240 self.SetConfig(client_source) |
| 242 | 241 |
| 243 def GetVar(self, key, default=None): | 242 def GetVar(self, key, default=None): |
| 244 return self._config_dict.get(key, default) | 243 return self._config_dict.get(key, default) |
| 245 | 244 |
| 246 @staticmethod | 245 @staticmethod |
| 247 def LoadCurrentConfig(options, from_dir=None): | 246 def LoadCurrentConfig(options, from_dir=None): |
| 248 """Searches for and loads a .gclient file relative to the current working | 247 """Searches for and loads a .gclient file relative to the current working |
| 249 dir. | 248 dir. |
| 250 | 249 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 278 | 277 |
| 279 Args: | 278 Args: |
| 280 entries: A sequence of solution names. | 279 entries: A sequence of solution names. |
| 281 """ | 280 """ |
| 282 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It | 281 # Sometimes pprint.pformat will use {', sometimes it'll use { ' ... It |
| 283 # makes testing a bit too fun. | 282 # makes testing a bit too fun. |
| 284 result = pprint.pformat(entries, 2) | 283 result = pprint.pformat(entries, 2) |
| 285 if result.startswith('{\''): | 284 if result.startswith('{\''): |
| 286 result = '{ \'' + result[2:] | 285 result = '{ \'' + result[2:] |
| 287 text = "entries = \\\n" + result + '\n' | 286 text = "entries = \\\n" + result + '\n' |
| 288 file_path = os.path.join(self._root_dir, self._options.entries_filename) | 287 file_path = os.path.join(self.root_dir(), self._options.entries_filename) |
| 289 gclient_utils.FileWrite(file_path, text) | 288 gclient_utils.FileWrite(file_path, text) |
| 290 | 289 |
| 291 def _ReadEntries(self): | 290 def _ReadEntries(self): |
| 292 """Read the .gclient_entries file for the given client. | 291 """Read the .gclient_entries file for the given client. |
| 293 | 292 |
| 294 Args: | 293 Args: |
| 295 client: The client for which the entries file should be read. | 294 client: The client for which the entries file should be read. |
| 296 | 295 |
| 297 Returns: | 296 Returns: |
| 298 A sequence of solution names, which will be empty if there is the | 297 A sequence of solution names, which will be empty if there is the |
| 299 entries file hasn't been created yet. | 298 entries file hasn't been created yet. |
| 300 """ | 299 """ |
| 301 scope = {} | 300 scope = {} |
| 302 filename = os.path.join(self._root_dir, self._options.entries_filename) | 301 filename = os.path.join(self.root_dir(), self._options.entries_filename) |
| 303 if not os.path.exists(filename): | 302 if not os.path.exists(filename): |
| 304 return [] | 303 return [] |
| 305 exec(gclient_utils.FileRead(filename), scope) | 304 exec(gclient_utils.FileRead(filename), scope) |
| 306 return scope["entries"] | 305 return scope["entries"] |
| 307 | 306 |
| 308 def _ParseSolutionDeps(self, solution_name, solution_deps_content, | 307 def _ParseSolutionDeps(self, solution_name, solution_deps_content, |
| 309 custom_vars, parse_hooks): | 308 custom_vars, parse_hooks): |
| 310 """Parses the DEPS file for the specified solution. | 309 """Parses the DEPS file for the specified solution. |
| 311 | 310 |
| 312 Args: | 311 Args: |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 428 elif isinstance(url, str): | 427 elif isinstance(url, str): |
| 429 parsed_url = urlparse.urlparse(url) | 428 parsed_url = urlparse.urlparse(url) |
| 430 scheme = parsed_url[0] | 429 scheme = parsed_url[0] |
| 431 if not scheme: | 430 if not scheme: |
| 432 # A relative url. Fetch the real base. | 431 # A relative url. Fetch the real base. |
| 433 path = parsed_url[2] | 432 path = parsed_url[2] |
| 434 if path[0] != "/": | 433 if path[0] != "/": |
| 435 raise gclient_utils.Error( | 434 raise gclient_utils.Error( |
| 436 "relative DEPS entry \"%s\" must begin with a slash" % d) | 435 "relative DEPS entry \"%s\" must begin with a slash" % d) |
| 437 # Create a scm just to query the full url. | 436 # Create a scm just to query the full url. |
| 438 scm = gclient_scm.CreateSCM(solution["url"], self._root_dir, | 437 scm = gclient_scm.CreateSCM(solution["url"], self.root_dir(), |
| 439 None) | 438 None) |
| 440 url = scm.FullUrlForRelativeUrl(url) | 439 url = scm.FullUrlForRelativeUrl(url) |
| 441 if d in deps and deps[d] != url: | 440 if d in deps and deps[d] != url: |
| 442 raise gclient_utils.Error( | 441 raise gclient_utils.Error( |
| 443 "Solutions have conflicting versions of dependency \"%s\"" % d) | 442 "Solutions have conflicting versions of dependency \"%s\"" % d) |
| 444 if d in solution_urls and solution_urls[d] != url: | 443 if d in solution_urls and solution_urls[d] != url: |
| 445 raise gclient_utils.Error( | 444 raise gclient_utils.Error( |
| 446 "Dependency \"%s\" conflicts with specified solution" % d) | 445 "Dependency \"%s\" conflicts with specified solution" % d) |
| 447 # Grab the dependency. | 446 # Grab the dependency. |
| 448 deps[d] = url | 447 deps[d] = url |
| (...skipping 11 matching lines...) Expand all Loading... |
| 460 # interpreter. | 459 # interpreter. |
| 461 command[0] = sys.executable | 460 command[0] = sys.executable |
| 462 | 461 |
| 463 if '$matching_files' in command: | 462 if '$matching_files' in command: |
| 464 splice_index = command.index('$matching_files') | 463 splice_index = command.index('$matching_files') |
| 465 command[splice_index:splice_index + 1] = matching_file_list | 464 command[splice_index:splice_index + 1] = matching_file_list |
| 466 | 465 |
| 467 # Use a discrete exit status code of 2 to indicate that a hook action | 466 # Use a discrete exit status code of 2 to indicate that a hook action |
| 468 # failed. Users of this script may wish to treat hook action failures | 467 # failed. Users of this script may wish to treat hook action failures |
| 469 # differently from VC failures. | 468 # differently from VC failures. |
| 470 gclient_utils.SubprocessCall(command, self._root_dir, fail_status=2) | 469 gclient_utils.SubprocessCall(command, self.root_dir(), fail_status=2) |
| 471 | 470 |
| 472 def _RunHooks(self, command, file_list, is_using_git): | 471 def _RunHooks(self, command, file_list, is_using_git): |
| 473 """Evaluates all hooks, running actions as needed. | 472 """Evaluates all hooks, running actions as needed. |
| 474 """ | 473 """ |
| 475 # Hooks only run for these command types. | 474 # Hooks only run for these command types. |
| 476 if not command in ('update', 'revert', 'runhooks'): | 475 if not command in ('update', 'revert', 'runhooks'): |
| 477 return | 476 return |
| 478 | 477 |
| 479 # Hooks only run when --nohooks is not specified | 478 # Hooks only run when --nohooks is not specified |
| 480 if self._options.nohooks: | 479 if self._options.nohooks: |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 567 deps_file = solution.get("deps_file", self.DEPS_FILE) | 566 deps_file = solution.get("deps_file", self.DEPS_FILE) |
| 568 if '/' in deps_file or '\\' in deps_file: | 567 if '/' in deps_file or '\\' in deps_file: |
| 569 raise gclient_utils.Error('deps_file name must not be a path, just a ' | 568 raise gclient_utils.Error('deps_file name must not be a path, just a ' |
| 570 'filename.') | 569 'filename.') |
| 571 if name in entries: | 570 if name in entries: |
| 572 raise gclient_utils.Error("solution %s specified more than once" % name) | 571 raise gclient_utils.Error("solution %s specified more than once" % name) |
| 573 url = solution["url"] | 572 url = solution["url"] |
| 574 entries[name] = url | 573 entries[name] = url |
| 575 if run_scm and url: | 574 if run_scm and url: |
| 576 self._options.revision = revision_overrides.get(name) | 575 self._options.revision = revision_overrides.get(name) |
| 577 scm = gclient_scm.CreateSCM(url, self._root_dir, name) | 576 scm = gclient_scm.CreateSCM(url, self.root_dir(), name) |
| 578 scm.RunCommand(command, self._options, args, file_list) | 577 scm.RunCommand(command, self._options, args, file_list) |
| 579 file_list = [os.path.join(name, f.strip()) for f in file_list] | 578 file_list = [os.path.join(name, f.strip()) for f in file_list] |
| 580 self._options.revision = None | 579 self._options.revision = None |
| 581 try: | 580 try: |
| 582 deps_content = gclient_utils.FileRead( | 581 deps_content = gclient_utils.FileRead( |
| 583 os.path.join(self._root_dir, name, deps_file)) | 582 os.path.join(self.root_dir(), name, deps_file)) |
| 584 except IOError, e: | 583 except IOError, e: |
| 585 if e.errno != errno.ENOENT: | 584 if e.errno != errno.ENOENT: |
| 586 raise | 585 raise |
| 587 deps_content = "" | 586 deps_content = "" |
| 588 entries_deps_content[name] = deps_content | 587 entries_deps_content[name] = deps_content |
| 589 | 588 |
| 590 # Process the dependencies next (sort alphanumerically to ensure that | 589 # Process the dependencies next (sort alphanumerically to ensure that |
| 591 # containing directories get populated first and for readability) | 590 # containing directories get populated first and for readability) |
| 592 deps = self._ParseAllDeps(entries, entries_deps_content) | 591 deps = self._ParseAllDeps(entries, entries_deps_content) |
| 593 deps_to_process = deps.keys() | 592 deps_to_process = deps.keys() |
| 594 deps_to_process.sort() | 593 deps_to_process.sort() |
| 595 | 594 |
| 596 # First pass for direct dependencies. | 595 # First pass for direct dependencies. |
| 597 if command == 'update' and not self._options.verbose: | 596 if command == 'update' and not self._options.verbose: |
| 598 pm = Progress('Syncing projects', len(deps_to_process)) | 597 pm = Progress('Syncing projects', len(deps_to_process)) |
| 599 for d in deps_to_process: | 598 for d in deps_to_process: |
| 600 if command == 'update' and not self._options.verbose: | 599 if command == 'update' and not self._options.verbose: |
| 601 pm.update() | 600 pm.update() |
| 602 if type(deps[d]) == str: | 601 if type(deps[d]) == str: |
| 603 url = deps[d] | 602 url = deps[d] |
| 604 entries[d] = url | 603 entries[d] = url |
| 605 if run_scm: | 604 if run_scm: |
| 606 self._options.revision = revision_overrides.get(d) | 605 self._options.revision = revision_overrides.get(d) |
| 607 scm = gclient_scm.CreateSCM(url, self._root_dir, d) | 606 scm = gclient_scm.CreateSCM(url, self.root_dir(), d) |
| 608 scm.RunCommand(command, self._options, args, file_list) | 607 scm.RunCommand(command, self._options, args, file_list) |
| 609 self._options.revision = None | 608 self._options.revision = None |
| 610 elif isinstance(deps[d], self.FileImpl): | 609 elif isinstance(deps[d], self.FileImpl): |
| 611 file_dep = deps[d] | 610 file_dep = deps[d] |
| 612 self._options.revision = file_dep.GetRevision() | 611 self._options.revision = file_dep.GetRevision() |
| 613 if run_scm: | 612 if run_scm: |
| 614 scm = gclient_scm.CreateSCM(file_dep.GetPath(), self._root_dir, d) | 613 scm = gclient_scm.CreateSCM(file_dep.GetPath(), self.root_dir(), d) |
| 615 scm.RunCommand("updatesingle", self._options, | 614 scm.RunCommand("updatesingle", self._options, |
| 616 args + [file_dep.GetFilename()], file_list) | 615 args + [file_dep.GetFilename()], file_list) |
| 617 | 616 |
| 618 if command == 'update' and not self._options.verbose: | 617 if command == 'update' and not self._options.verbose: |
| 619 pm.end() | 618 pm.end() |
| 620 | 619 |
| 621 # Second pass for inherited deps (via the From keyword) | 620 # Second pass for inherited deps (via the From keyword) |
| 622 for d in deps_to_process: | 621 for d in deps_to_process: |
| 623 if isinstance(deps[d], self.FromImpl): | 622 if isinstance(deps[d], self.FromImpl): |
| 624 filename = os.path.join(self._root_dir, | 623 filename = os.path.join(self.root_dir(), |
| 625 deps[d].module_name, | 624 deps[d].module_name, |
| 626 self.DEPS_FILE) | 625 self.DEPS_FILE) |
| 627 content = gclient_utils.FileRead(filename) | 626 content = gclient_utils.FileRead(filename) |
| 628 sub_deps = self._ParseSolutionDeps(deps[d].module_name, content, {}, | 627 sub_deps = self._ParseSolutionDeps(deps[d].module_name, content, {}, |
| 629 False) | 628 False) |
| 630 # Getting the URL from the sub_deps file can involve having to resolve | 629 # Getting the URL from the sub_deps file can involve having to resolve |
| 631 # a File() or having to resolve a relative URL. To resolve relative | 630 # a File() or having to resolve a relative URL. To resolve relative |
| 632 # URLs, we need to pass in the orignal sub deps URL. | 631 # URLs, we need to pass in the orignal sub deps URL. |
| 633 sub_deps_base_url = deps[deps[d].module_name] | 632 sub_deps_base_url = deps[deps[d].module_name] |
| 634 url = deps[d].GetUrl(d, sub_deps_base_url, self._root_dir, sub_deps) | 633 url = deps[d].GetUrl(d, sub_deps_base_url, self.root_dir(), sub_deps) |
| 635 entries[d] = url | 634 entries[d] = url |
| 636 if run_scm: | 635 if run_scm: |
| 637 self._options.revision = revision_overrides.get(d) | 636 self._options.revision = revision_overrides.get(d) |
| 638 scm = gclient_scm.CreateSCM(url, self._root_dir, d) | 637 scm = gclient_scm.CreateSCM(url, self.root_dir(), d) |
| 639 scm.RunCommand(command, self._options, args, file_list) | 638 scm.RunCommand(command, self._options, args, file_list) |
| 640 self._options.revision = None | 639 self._options.revision = None |
| 641 | 640 |
| 642 # Convert all absolute paths to relative. | 641 # Convert all absolute paths to relative. |
| 643 for i in range(len(file_list)): | 642 for i in range(len(file_list)): |
| 644 # TODO(phajdan.jr): We should know exactly when the paths are absolute. | 643 # TODO(phajdan.jr): We should know exactly when the paths are absolute. |
| 645 # It depends on the command being executed (like runhooks vs sync). | 644 # It depends on the command being executed (like runhooks vs sync). |
| 646 if not os.path.isabs(file_list[i]): | 645 if not os.path.isabs(file_list[i]): |
| 647 continue | 646 continue |
| 648 | 647 |
| 649 prefix = os.path.commonprefix([self._root_dir.lower(), | 648 prefix = os.path.commonprefix([self.root_dir().lower(), |
| 650 file_list[i].lower()]) | 649 file_list[i].lower()]) |
| 651 file_list[i] = file_list[i][len(prefix):] | 650 file_list[i] = file_list[i][len(prefix):] |
| 652 | 651 |
| 653 # Strip any leading path separators. | 652 # Strip any leading path separators. |
| 654 while file_list[i].startswith('\\') or file_list[i].startswith('/'): | 653 while file_list[i].startswith('\\') or file_list[i].startswith('/'): |
| 655 file_list[i] = file_list[i][1:] | 654 file_list[i] = file_list[i][1:] |
| 656 | 655 |
| 657 is_using_git = gclient_utils.IsUsingGit(self._root_dir, entries.keys()) | 656 is_using_git = gclient_utils.IsUsingGit(self.root_dir(), entries.keys()) |
| 658 self._RunHooks(command, file_list, is_using_git) | 657 self._RunHooks(command, file_list, is_using_git) |
| 659 | 658 |
| 660 if command == 'update': | 659 if command == 'update': |
| 661 # Notify the user if there is an orphaned entry in their working copy. | 660 # Notify the user if there is an orphaned entry in their working copy. |
| 662 # Only delete the directory if there are no changes in it, and | 661 # Only delete the directory if there are no changes in it, and |
| 663 # delete_unversioned_trees is set to true. | 662 # delete_unversioned_trees is set to true. |
| 664 prev_entries = self._ReadEntries() | 663 prev_entries = self._ReadEntries() |
| 665 for entry in prev_entries: | 664 for entry in prev_entries: |
| 666 # Fix path separator on Windows. | 665 # Fix path separator on Windows. |
| 667 entry_fixed = entry.replace('/', os.path.sep) | 666 entry_fixed = entry.replace('/', os.path.sep) |
| 668 e_dir = os.path.join(self._root_dir, entry_fixed) | 667 e_dir = os.path.join(self.root_dir(), entry_fixed) |
| 669 # Use entry and not entry_fixed there. | 668 # Use entry and not entry_fixed there. |
| 670 if entry not in entries and os.path.exists(e_dir): | 669 if entry not in entries and os.path.exists(e_dir): |
| 671 modified_files = False | 670 modified_files = False |
| 672 if isinstance(prev_entries, list): | 671 if isinstance(prev_entries, list): |
| 673 # old .gclient_entries format was list, now dict | 672 # old .gclient_entries format was list, now dict |
| 674 modified_files = gclient_scm.scm.SVN.CaptureStatus(e_dir) | 673 modified_files = gclient_scm.scm.SVN.CaptureStatus(e_dir) |
| 675 else: | 674 else: |
| 676 file_list = [] | 675 file_list = [] |
| 677 scm = gclient_scm.CreateSCM(prev_entries[entry], self._root_dir, | 676 scm = gclient_scm.CreateSCM(prev_entries[entry], self.root_dir(), |
| 678 entry_fixed) | 677 entry_fixed) |
| 679 scm.status(self._options, [], file_list) | 678 scm.status(self._options, [], file_list) |
| 680 modified_files = file_list != [] | 679 modified_files = file_list != [] |
| 681 if not self._options.delete_unversioned_trees or modified_files: | 680 if not self._options.delete_unversioned_trees or modified_files: |
| 682 # There are modified files in this entry. Keep warning until | 681 # There are modified files in this entry. Keep warning until |
| 683 # removed. | 682 # removed. |
| 684 print(("\nWARNING: \"%s\" is no longer part of this client. " | 683 print(("\nWARNING: \"%s\" is no longer part of this client. " |
| 685 "It is recommended that you manually remove it.\n") % | 684 "It is recommended that you manually remove it.\n") % |
| 686 entry_fixed) | 685 entry_fixed) |
| 687 else: | 686 else: |
| 688 # Delete the entry | 687 # Delete the entry |
| 689 print("\n________ deleting \'%s\' " + | 688 print("\n________ deleting \'%s\' " + |
| 690 "in \'%s\'") % (entry_fixed, self._root_dir) | 689 "in \'%s\'") % (entry_fixed, self.root_dir()) |
| 691 gclient_utils.RemoveDirectory(e_dir) | 690 gclient_utils.RemoveDirectory(e_dir) |
| 692 # record the current list of entries for next time | 691 # record the current list of entries for next time |
| 693 self._SaveEntries(entries) | 692 self._SaveEntries(entries) |
| 694 return 0 | 693 return 0 |
| 695 | 694 |
| 696 def PrintRevInfo(self): | 695 def PrintRevInfo(self): |
| 697 """Output revision info mapping for the client and its dependencies. | 696 """Output revision info mapping for the client and its dependencies. |
| 698 | 697 |
| 699 This allows the capture of an overall "revision" for the source tree that | 698 This allows the capture of an overall "revision" for the source tree that |
| 700 can be used to reproduce the same tree in the future. It is only useful for | 699 can be used to reproduce the same tree in the future. It is only useful for |
| 701 "unpinned dependencies", i.e. DEPS/deps references without a svn revision | 700 "unpinned dependencies", i.e. DEPS/deps references without a svn revision |
| 702 number or a git hash. A git branch name isn't "pinned" since the actual | 701 number or a git hash. A git branch name isn't "pinned" since the actual |
| 703 commit can change. | 702 commit can change. |
| 704 | 703 |
| 705 The --snapshot option allows creating a .gclient file to reproduce the tree. | 704 The --snapshot option allows creating a .gclient file to reproduce the tree. |
| 706 """ | 705 """ |
| 707 solutions = self.GetVar("solutions") | 706 solutions = self.GetVar("solutions") |
| 708 if not solutions: | 707 if not solutions: |
| 709 raise gclient_utils.Error("No solution specified") | 708 raise gclient_utils.Error("No solution specified") |
| 710 | 709 |
| 711 # Inner helper to generate base url and rev tuple | 710 # Inner helper to generate base url and rev tuple |
| 712 def GetURLAndRev(name, original_url): | 711 def GetURLAndRev(name, original_url): |
| 713 url, _ = gclient_utils.SplitUrlRevision(original_url) | 712 url, _ = gclient_utils.SplitUrlRevision(original_url) |
| 714 scm = gclient_scm.CreateSCM(original_url, self._root_dir, name) | 713 scm = gclient_scm.CreateSCM(original_url, self.root_dir(), name) |
| 715 return (url, scm.revinfo(self._options, [], None)) | 714 return (url, scm.revinfo(self._options, [], None)) |
| 716 | 715 |
| 717 # text of the snapshot gclient file | 716 # text of the snapshot gclient file |
| 718 new_gclient = "" | 717 new_gclient = "" |
| 719 # Dictionary of { path : SCM url } to ensure no duplicate solutions | 718 # Dictionary of { path : SCM url } to ensure no duplicate solutions |
| 720 solution_names = {} | 719 solution_names = {} |
| 721 entries = {} | 720 entries = {} |
| 722 entries_deps_content = {} | 721 entries_deps_content = {} |
| 723 # Run on the base solutions first. | 722 # Run on the base solutions first. |
| 724 for solution in solutions: | 723 for solution in solutions: |
| 725 # Dictionary of { path : SCM url } to describe the gclient checkout | 724 # Dictionary of { path : SCM url } to describe the gclient checkout |
| 726 name = solution["name"] | 725 name = solution["name"] |
| 727 if name in solution_names: | 726 if name in solution_names: |
| 728 raise gclient_utils.Error("solution %s specified more than once" % name) | 727 raise gclient_utils.Error("solution %s specified more than once" % name) |
| 729 (url, rev) = GetURLAndRev(name, solution["url"]) | 728 (url, rev) = GetURLAndRev(name, solution["url"]) |
| 730 entries[name] = "%s@%s" % (url, rev) | 729 entries[name] = "%s@%s" % (url, rev) |
| 731 solution_names[name] = "%s@%s" % (url, rev) | 730 solution_names[name] = "%s@%s" % (url, rev) |
| 732 deps_file = solution.get("deps_file", self.DEPS_FILE) | 731 deps_file = solution.get("deps_file", self.DEPS_FILE) |
| 733 if '/' in deps_file or '\\' in deps_file: | 732 if '/' in deps_file or '\\' in deps_file: |
| 734 raise gclient_utils.Error('deps_file name must not be a path, just a ' | 733 raise gclient_utils.Error('deps_file name must not be a path, just a ' |
| 735 'filename.') | 734 'filename.') |
| 736 try: | 735 try: |
| 737 deps_content = gclient_utils.FileRead( | 736 deps_content = gclient_utils.FileRead( |
| 738 os.path.join(self._root_dir, name, deps_file)) | 737 os.path.join(self.root_dir(), name, deps_file)) |
| 739 except IOError, e: | 738 except IOError, e: |
| 740 if e.errno != errno.ENOENT: | 739 if e.errno != errno.ENOENT: |
| 741 raise | 740 raise |
| 742 deps_content = "" | 741 deps_content = "" |
| 743 entries_deps_content[name] = deps_content | 742 entries_deps_content[name] = deps_content |
| 744 | 743 |
| 745 # Process the dependencies next (sort alphanumerically to ensure that | 744 # Process the dependencies next (sort alphanumerically to ensure that |
| 746 # containing directories get populated first and for readability) | 745 # containing directories get populated first and for readability) |
| 747 deps = self._ParseAllDeps(entries, entries_deps_content) | 746 deps = self._ParseAllDeps(entries, entries_deps_content) |
| 748 deps_to_process = deps.keys() | 747 deps_to_process = deps.keys() |
| 749 deps_to_process.sort() | 748 deps_to_process.sort() |
| 750 | 749 |
| 751 # First pass for direct dependencies. | 750 # First pass for direct dependencies. |
| 752 for d in deps_to_process: | 751 for d in deps_to_process: |
| 753 if type(deps[d]) == str: | 752 if type(deps[d]) == str: |
| 754 (url, rev) = GetURLAndRev(d, deps[d]) | 753 (url, rev) = GetURLAndRev(d, deps[d]) |
| 755 entries[d] = "%s@%s" % (url, rev) | 754 entries[d] = "%s@%s" % (url, rev) |
| 756 | 755 |
| 757 # Second pass for inherited deps (via the From keyword) | 756 # Second pass for inherited deps (via the From keyword) |
| 758 for d in deps_to_process: | 757 for d in deps_to_process: |
| 759 if isinstance(deps[d], self.FromImpl): | 758 if isinstance(deps[d], self.FromImpl): |
| 760 deps_parent_url = entries[deps[d].module_name] | 759 deps_parent_url = entries[deps[d].module_name] |
| 761 if deps_parent_url.find("@") < 0: | 760 if deps_parent_url.find("@") < 0: |
| 762 raise gclient_utils.Error("From %s missing revisioned url" % | 761 raise gclient_utils.Error("From %s missing revisioned url" % |
| 763 deps[d].module_name) | 762 deps[d].module_name) |
| 764 content = gclient_utils.FileRead(os.path.join( | 763 content = gclient_utils.FileRead(os.path.join( |
| 765 self._root_dir, | 764 self.root_dir(), |
| 766 deps[d].module_name, | 765 deps[d].module_name, |
| 767 self.DEPS_FILE)) | 766 self.DEPS_FILE)) |
| 768 sub_deps = self._ParseSolutionDeps(deps[d].module_name, content, {}, | 767 sub_deps = self._ParseSolutionDeps(deps[d].module_name, content, {}, |
| 769 False) | 768 False) |
| 770 (url, rev) = GetURLAndRev(d, sub_deps[d]) | 769 (url, rev) = GetURLAndRev(d, sub_deps[d]) |
| 771 entries[d] = "%s@%s" % (url, rev) | 770 entries[d] = "%s@%s" % (url, rev) |
| 772 | 771 |
| 773 # Build the snapshot configuration string | 772 # Build the snapshot configuration string |
| 774 if self._options.snapshot: | 773 if self._options.snapshot: |
| 775 url = entries.pop(name) | 774 url = entries.pop(name) |
| 776 custom_deps = ",\n ".join(["\"%s\": \"%s\"" % (x, entries[x]) | 775 custom_deps = ",\n ".join(["\"%s\": \"%s\"" % (x, entries[x]) |
| 777 for x in sorted(entries.keys())]) | 776 for x in sorted(entries.keys())]) |
| 778 | 777 |
| 779 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % { | 778 new_gclient += self.DEFAULT_SNAPSHOT_SOLUTION_TEXT % { |
| 780 'solution_name': name, | 779 'solution_name': name, |
| 781 'solution_url': url, | 780 'solution_url': url, |
| 782 'safesync_url' : "", | 781 'safesync_url' : "", |
| 783 'solution_deps': custom_deps, | 782 'solution_deps': custom_deps, |
| 784 } | 783 } |
| 785 else: | 784 else: |
| 786 print(";\n".join(["%s: %s" % (x, entries[x]) | 785 print(";\n".join(["%s: %s" % (x, entries[x]) |
| 787 for x in sorted(entries.keys())])) | 786 for x in sorted(entries.keys())])) |
| 788 | 787 |
| 789 # Print the snapshot configuration file | 788 # Print the snapshot configuration file |
| 790 if self._options.snapshot: | 789 if self._options.snapshot: |
| 791 config = self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient} | 790 config = self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient} |
| 792 snapclient = GClient(self._root_dir, self._options) | 791 snapclient = GClient(self.root_dir(), self._options) |
| 793 snapclient.SetConfig(config) | 792 snapclient.SetConfig(config) |
| 794 print(snapclient.config_content) | 793 print(snapclient.config_content) |
| 795 | 794 |
| 795 def root_dir(self): |
| 796 return self._root_dir |
| 797 |
| 796 | 798 |
| 797 #### gclient commands. | 799 #### gclient commands. |
| 798 | 800 |
| 799 | 801 |
| 800 def CMDcleanup(parser, args): | 802 def CMDcleanup(parser, args): |
| 801 """Cleans up all working copies. | 803 """Cleans up all working copies. |
| 802 | 804 |
| 803 Mostly svn-specific. Simply runs 'svn cleanup' for each module. | 805 Mostly svn-specific. Simply runs 'svn cleanup' for each module. |
| 804 """ | 806 """ |
| 805 parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", | 807 parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", |
| (...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1139 return CMDhelp(parser, argv) | 1141 return CMDhelp(parser, argv) |
| 1140 except gclient_utils.Error, e: | 1142 except gclient_utils.Error, e: |
| 1141 print >> sys.stderr, 'Error: %s' % str(e) | 1143 print >> sys.stderr, 'Error: %s' % str(e) |
| 1142 return 1 | 1144 return 1 |
| 1143 | 1145 |
| 1144 | 1146 |
| 1145 if '__main__' == __name__: | 1147 if '__main__' == __name__: |
| 1146 sys.exit(Main(sys.argv[1:])) | 1148 sys.exit(Main(sys.argv[1:])) |
| 1147 | 1149 |
| 1148 # vim: ts=2:sw=2:tw=80:et: | 1150 # vim: ts=2:sw=2:tw=80:et: |
| OLD | NEW |