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 |