| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 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 # Files | 7 # Files |
| 8 # .gclient : Current client configuration, written by 'config' command. | 8 # .gclient : Current client configuration, written by 'config' command. |
| 9 # Format is a Python script defining 'solutions', a list whose | 9 # Format is a Python script defining 'solutions', a list whose |
| 10 # entries each are maps binding the strings "name" and "url" | 10 # entries each are maps binding the strings "name" and "url" |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 66 # If the "target_os_only" key is also present and true, then *only* the | 66 # If the "target_os_only" key is also present and true, then *only* the |
| 67 # operating systems listed in "target_os" will be used. | 67 # operating systems listed in "target_os" will be used. |
| 68 # | 68 # |
| 69 # Example: | 69 # Example: |
| 70 # target_os = [ "ios" ] | 70 # target_os = [ "ios" ] |
| 71 # target_os_only = True | 71 # target_os_only = True |
| 72 | 72 |
| 73 __version__ = '0.7' | 73 __version__ = '0.7' |
| 74 | 74 |
| 75 import copy | 75 import copy |
| 76 import json |
| 76 import logging | 77 import logging |
| 77 import optparse | 78 import optparse |
| 78 import os | 79 import os |
| 79 import platform | 80 import platform |
| 80 import posixpath | 81 import posixpath |
| 81 import pprint | 82 import pprint |
| 82 import re | 83 import re |
| 83 import sys | 84 import sys |
| 84 import time | 85 import time |
| 85 import urllib | 86 import urllib |
| (...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 294 # If it is not set to True, the dependency wasn't processed for its child | 295 # If it is not set to True, the dependency wasn't processed for its child |
| 295 # dependency, i.e. its DEPS wasn't read. | 296 # dependency, i.e. its DEPS wasn't read. |
| 296 self._deps_parsed = False | 297 self._deps_parsed = False |
| 297 # This dependency has been processed, i.e. checked out | 298 # This dependency has been processed, i.e. checked out |
| 298 self._processed = False | 299 self._processed = False |
| 299 # This dependency had its hook run | 300 # This dependency had its hook run |
| 300 self._hooks_ran = False | 301 self._hooks_ran = False |
| 301 # This is the scm used to checkout self.url. It may be used by dependencies | 302 # This is the scm used to checkout self.url. It may be used by dependencies |
| 302 # to get the datetime of the revision we checked out. | 303 # to get the datetime of the revision we checked out. |
| 303 self._used_scm = None | 304 self._used_scm = None |
| 305 # The actual revision we ended up getting, or None if that information is |
| 306 # unavailable |
| 307 self._got_revision = None |
| 304 | 308 |
| 305 if not self.name and self.parent: | 309 if not self.name and self.parent: |
| 306 raise gclient_utils.Error('Dependency without name') | 310 raise gclient_utils.Error('Dependency without name') |
| 307 | 311 |
| 308 @property | 312 @property |
| 309 def requirements(self): | 313 def requirements(self): |
| 310 """Calculate the list of requirements.""" | 314 """Calculate the list of requirements.""" |
| 311 requirements = set() | 315 requirements = set() |
| 312 # self.parent is implicitly a requirement. This will be recursive by | 316 # self.parent is implicitly a requirement. This will be recursive by |
| 313 # definition. | 317 # definition. |
| (...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 617 self._used_scm.RunCommand('updatesingle', | 621 self._used_scm.RunCommand('updatesingle', |
| 618 options, args + [parsed_url.GetFilename()], file_list) | 622 options, args + [parsed_url.GetFilename()], file_list) |
| 619 else: | 623 else: |
| 620 # Create a shallow copy to mutate revision. | 624 # Create a shallow copy to mutate revision. |
| 621 options = copy.copy(options) | 625 options = copy.copy(options) |
| 622 options.revision = revision_overrides.get(self.name) | 626 options.revision = revision_overrides.get(self.name) |
| 623 self.maybeGetParentRevision( | 627 self.maybeGetParentRevision( |
| 624 command, options, parsed_url, self.parent.name, revision_overrides) | 628 command, options, parsed_url, self.parent.name, revision_overrides) |
| 625 self._used_scm = gclient_scm.CreateSCM( | 629 self._used_scm = gclient_scm.CreateSCM( |
| 626 parsed_url, self.root.root_dir, self.name) | 630 parsed_url, self.root.root_dir, self.name) |
| 627 self._used_scm.RunCommand(command, options, args, file_list) | 631 self._got_revision = self._used_scm.RunCommand(command, options, args, |
| 632 file_list) |
| 628 if file_list: | 633 if file_list: |
| 629 file_list = [os.path.join(self.name, f.strip()) for f in file_list] | 634 file_list = [os.path.join(self.name, f.strip()) for f in file_list] |
| 630 | 635 |
| 631 # TODO(phajdan.jr): We should know exactly when the paths are absolute. | 636 # TODO(phajdan.jr): We should know exactly when the paths are absolute. |
| 632 # Convert all absolute paths to relative. | 637 # Convert all absolute paths to relative. |
| 633 for i in range(len(file_list or [])): | 638 for i in range(len(file_list or [])): |
| 634 # It depends on the command being executed (like runhooks vs sync). | 639 # It depends on the command being executed (like runhooks vs sync). |
| 635 if not os.path.isabs(file_list[i]): | 640 if not os.path.isabs(file_list[i]): |
| 636 continue | 641 continue |
| 637 prefix = os.path.commonprefix( | 642 prefix = os.path.commonprefix( |
| (...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 848 @gclient_utils.lockedmethod | 853 @gclient_utils.lockedmethod |
| 849 def file_list(self): | 854 def file_list(self): |
| 850 return tuple(self._file_list) | 855 return tuple(self._file_list) |
| 851 | 856 |
| 852 @property | 857 @property |
| 853 def used_scm(self): | 858 def used_scm(self): |
| 854 """SCMWrapper instance for this dependency or None if not processed yet.""" | 859 """SCMWrapper instance for this dependency or None if not processed yet.""" |
| 855 return self._used_scm | 860 return self._used_scm |
| 856 | 861 |
| 857 @property | 862 @property |
| 863 @gclient_utils.lockedmethod |
| 864 def got_revision(self): |
| 865 return self._got_revision |
| 866 |
| 867 @property |
| 858 def file_list_and_children(self): | 868 def file_list_and_children(self): |
| 859 result = list(self.file_list) | 869 result = list(self.file_list) |
| 860 for d in self.dependencies: | 870 for d in self.dependencies: |
| 861 result.extend(d.file_list_and_children) | 871 result.extend(d.file_list_and_children) |
| 862 return tuple(result) | 872 return tuple(result) |
| 863 | 873 |
| 864 def __str__(self): | 874 def __str__(self): |
| 865 out = [] | 875 out = [] |
| 866 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps', | 876 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps', |
| 867 'custom_vars', 'deps_hooks', 'file_list', 'should_process', | 877 'custom_vars', 'deps_hooks', 'file_list', 'should_process', |
| (...skipping 648 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1516 | 1526 |
| 1517 @subcommand.epilog("""Examples: | 1527 @subcommand.epilog("""Examples: |
| 1518 gclient sync | 1528 gclient sync |
| 1519 update files from SCM according to current configuration, | 1529 update files from SCM according to current configuration, |
| 1520 *for modules which have changed since last update or sync* | 1530 *for modules which have changed since last update or sync* |
| 1521 gclient sync --force | 1531 gclient sync --force |
| 1522 update files from SCM according to current configuration, for | 1532 update files from SCM according to current configuration, for |
| 1523 all modules (useful for recovering files deleted from local copy) | 1533 all modules (useful for recovering files deleted from local copy) |
| 1524 gclient sync --revision src@31000 | 1534 gclient sync --revision src@31000 |
| 1525 update src directory to r31000 | 1535 update src directory to r31000 |
| 1536 |
| 1537 JSON output format: |
| 1538 If the --output-json option is specified, the following document structure will |
| 1539 be emitted to the provided file. 'null' entries may occur for subprojects which |
| 1540 are present in the gclient solution, but were not processed (due to custom_deps, |
| 1541 os_deps, etc.) |
| 1542 |
| 1543 { |
| 1544 "solutions" : { |
| 1545 "<name>": { # <name> is the posix-normalized path to the solution. |
| 1546 "revision": [<svn rev int>|<git id hex string>|null], |
| 1547 "scm": ["svn"|"git"|null], |
| 1548 } |
| 1549 } |
| 1550 } |
| 1526 """) | 1551 """) |
| 1527 def CMDsync(parser, args): | 1552 def CMDsync(parser, args): |
| 1528 """Checkout/update all modules.""" | 1553 """Checkout/update all modules.""" |
| 1529 parser.add_option('-f', '--force', action='store_true', | 1554 parser.add_option('-f', '--force', action='store_true', |
| 1530 help='force update even for unchanged modules') | 1555 help='force update even for unchanged modules') |
| 1531 parser.add_option('-n', '--nohooks', action='store_true', | 1556 parser.add_option('-n', '--nohooks', action='store_true', |
| 1532 help='don\'t run hooks after the update is complete') | 1557 help='don\'t run hooks after the update is complete') |
| 1533 parser.add_option('-r', '--revision', action='append', | 1558 parser.add_option('-r', '--revision', action='append', |
| 1534 dest='revisions', metavar='REV', default=[], | 1559 dest='revisions', metavar='REV', default=[], |
| 1535 help='Enforces revision/hash for the solutions with the ' | 1560 help='Enforces revision/hash for the solutions with the ' |
| (...skipping 30 matching lines...) Expand all Loading... |
| 1566 'fast-forward or rebase') | 1591 'fast-forward or rebase') |
| 1567 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', | 1592 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
| 1568 help='override deps for the specified (comma-separated) ' | 1593 help='override deps for the specified (comma-separated) ' |
| 1569 'platform(s); \'all\' will process all deps_os ' | 1594 'platform(s); \'all\' will process all deps_os ' |
| 1570 'references') | 1595 'references') |
| 1571 parser.add_option('-m', '--manually_grab_svn_rev', action='store_true', | 1596 parser.add_option('-m', '--manually_grab_svn_rev', action='store_true', |
| 1572 help='Skip svn up whenever possible by requesting ' | 1597 help='Skip svn up whenever possible by requesting ' |
| 1573 'actual HEAD revision from the repository') | 1598 'actual HEAD revision from the repository') |
| 1574 parser.add_option('--upstream', action='store_true', | 1599 parser.add_option('--upstream', action='store_true', |
| 1575 help='Make repo state match upstream branch.') | 1600 help='Make repo state match upstream branch.') |
| 1601 parser.add_option('--output-json', |
| 1602 help='Output a json document to this path containing ' |
| 1603 'summary information about the sync.') |
| 1576 (options, args) = parser.parse_args(args) | 1604 (options, args) = parser.parse_args(args) |
| 1577 client = GClient.LoadCurrentConfig(options) | 1605 client = GClient.LoadCurrentConfig(options) |
| 1578 | 1606 |
| 1579 if not client: | 1607 if not client: |
| 1580 raise gclient_utils.Error('client not configured; see \'gclient config\'') | 1608 raise gclient_utils.Error('client not configured; see \'gclient config\'') |
| 1581 | 1609 |
| 1582 if options.revisions and options.head: | 1610 if options.revisions and options.head: |
| 1583 # TODO(maruel): Make it a parser.error if it doesn't break any builder. | 1611 # TODO(maruel): Make it a parser.error if it doesn't break any builder. |
| 1584 print('Warning: you cannot use both --head and --revision') | 1612 print('Warning: you cannot use both --head and --revision') |
| 1585 | 1613 |
| 1586 if options.verbose: | 1614 if options.verbose: |
| 1587 # Print out the .gclient file. This is longer than if we just printed the | 1615 # Print out the .gclient file. This is longer than if we just printed the |
| 1588 # client dict, but more legible, and it might contain helpful comments. | 1616 # client dict, but more legible, and it might contain helpful comments. |
| 1589 print(client.config_content) | 1617 print(client.config_content) |
| 1590 return client.RunOnDeps('update', args) | 1618 ret = client.RunOnDeps('update', args) |
| 1619 if options.output_json: |
| 1620 slns = {} |
| 1621 for d in client.subtree(True): |
| 1622 normed = d.name.replace('\\', '/').rstrip('/') + '/' |
| 1623 slns[normed] = { |
| 1624 'revision': d.got_revision, |
| 1625 'scm': d.used_scm.name if d.used_scm else None, |
| 1626 } |
| 1627 with open(options.output_json, 'wb') as f: |
| 1628 json.dump({'solutions': slns}, f) |
| 1629 return ret |
| 1591 | 1630 |
| 1592 | 1631 |
| 1593 CMDupdate = CMDsync | 1632 CMDupdate = CMDsync |
| 1594 | 1633 |
| 1595 | 1634 |
| 1596 def CMDdiff(parser, args): | 1635 def CMDdiff(parser, args): |
| 1597 """Displays local diff for every dependencies.""" | 1636 """Displays local diff for every dependencies.""" |
| 1598 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', | 1637 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
| 1599 help='override deps for the specified (comma-separated) ' | 1638 help='override deps for the specified (comma-separated) ' |
| 1600 'platform(s); \'all\' will process all deps_os ' | 1639 'platform(s); \'all\' will process all deps_os ' |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1797 raise | 1836 raise |
| 1798 except (gclient_utils.Error, subprocess2.CalledProcessError), e: | 1837 except (gclient_utils.Error, subprocess2.CalledProcessError), e: |
| 1799 print >> sys.stderr, 'Error: %s' % str(e) | 1838 print >> sys.stderr, 'Error: %s' % str(e) |
| 1800 return 1 | 1839 return 1 |
| 1801 | 1840 |
| 1802 | 1841 |
| 1803 if '__main__' == __name__: | 1842 if '__main__' == __name__: |
| 1804 sys.exit(Main(sys.argv[1:])) | 1843 sys.exit(Main(sys.argv[1:])) |
| 1805 | 1844 |
| 1806 # vim: ts=2:sw=2:tw=80:et: | 1845 # vim: ts=2:sw=2:tw=80:et: |
| OLD | NEW |