Commit 2702bcde authored by iannucci@chromium.org's avatar iannucci@chromium.org

Add json output for gclient.

This is in order to support 'features' like got_revision for build systems
unwilling to scrape the human-readable output of gclient in order to extract
basic information :)

R=agable@chromium.org, maruel@chromium.org
BUG=265618

Review URL: https://chromiumcodereview.appspot.com/23753008

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@225046 0039d316-1c4b-4281-b951-d872f2087c98
parent c7a9efad
......@@ -73,6 +73,7 @@
__version__ = '0.7'
import copy
import json
import logging
import optparse
import os
......@@ -301,6 +302,9 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
# This is the scm used to checkout self.url. It may be used by dependencies
# to get the datetime of the revision we checked out.
self._used_scm = None
# The actual revision we ended up getting, or None if that information is
# unavailable
self._got_revision = None
if not self.name and self.parent:
raise gclient_utils.Error('Dependency without name')
......@@ -624,7 +628,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
command, options, parsed_url, self.parent.name, revision_overrides)
self._used_scm = gclient_scm.CreateSCM(
parsed_url, self.root.root_dir, self.name)
self._used_scm.RunCommand(command, options, args, file_list)
self._got_revision = self._used_scm.RunCommand(command, options, args,
file_list)
if file_list:
file_list = [os.path.join(self.name, f.strip()) for f in file_list]
......@@ -854,6 +859,11 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
"""SCMWrapper instance for this dependency or None if not processed yet."""
return self._used_scm
@property
@gclient_utils.lockedmethod
def got_revision(self):
return self._got_revision
@property
def file_list_and_children(self):
result = list(self.file_list)
......@@ -1523,6 +1533,21 @@ def CMDstatus(parser, args):
all modules (useful for recovering files deleted from local copy)
gclient sync --revision src@31000
update src directory to r31000
JSON output format:
If the --output-json option is specified, the following document structure will
be emitted to the provided file. 'null' entries may occur for subprojects which
are present in the gclient solution, but were not processed (due to custom_deps,
os_deps, etc.)
{
"solutions" : {
"<name>": { # <name> is the posix-normalized path to the solution.
"revision": [<svn rev int>|<git id hex string>|null],
"scm": ["svn"|"git"|null],
}
}
}
""")
def CMDsync(parser, args):
"""Checkout/update all modules."""
......@@ -1573,6 +1598,9 @@ def CMDsync(parser, args):
'actual HEAD revision from the repository')
parser.add_option('--upstream', action='store_true',
help='Make repo state match upstream branch.')
parser.add_option('--output-json',
help='Output a json document to this path containing '
'summary information about the sync.')
(options, args) = parser.parse_args(args)
client = GClient.LoadCurrentConfig(options)
......@@ -1587,7 +1615,18 @@ def CMDsync(parser, args):
# Print out the .gclient file. This is longer than if we just printed the
# client dict, but more legible, and it might contain helpful comments.
print(client.config_content)
return client.RunOnDeps('update', args)
ret = client.RunOnDeps('update', args)
if options.output_json:
slns = {}
for d in client.subtree(True):
normed = d.name.replace('\\', '/').rstrip('/') + '/'
slns[normed] = {
'revision': d.got_revision,
'scm': d.used_scm.name if d.used_scm else None,
}
with open(options.output_json, 'wb') as f:
json.dump({'solutions': slns}, f)
return ret
CMDupdate = CMDsync
......
......@@ -190,6 +190,7 @@ class GitFilter(object):
class GitWrapper(SCMWrapper):
"""Wrapper for Git"""
name = 'git'
cache_dir = None
# If a given cache is used in a solution more than once, prevent multiple
......@@ -363,13 +364,13 @@ class GitWrapper(SCMWrapper):
# Make the output a little prettier. It's nice to have some whitespace
# between projects when cloning.
print('')
return
return self._Capture(['rev-parse', '--verify', 'HEAD'])
if not managed:
self._UpdateBranchHeads(options, fetch=False)
self.UpdateSubmoduleConfig()
print ('________ unmanaged solution; skipping %s' % self.relpath)
return
return self._Capture(['rev-parse', '--verify', 'HEAD'])
if not os.path.exists(os.path.join(self.checkout_path, '.git')):
raise gclient_utils.Error('\n____ %s%s\n'
......@@ -406,7 +407,7 @@ class GitWrapper(SCMWrapper):
self._PossiblySwitchCache(url, options)
if return_early:
return
return self._Capture(['rev-parse', '--verify', 'HEAD'])
cur_branch = self._GetCurrentBranch()
......@@ -595,6 +596,8 @@ class GitWrapper(SCMWrapper):
print('\n_____ removing unversioned directory %s' % path)
gclient_utils.rmtree(full_path)
return self._Capture(['rev-parse', '--verify', 'HEAD'])
def revert(self, options, _args, file_list):
"""Reverts local modifications.
......@@ -1088,6 +1091,7 @@ class GitWrapper(SCMWrapper):
class SVNWrapper(SCMWrapper):
""" Wrapper for SVN """
name = 'svn'
@staticmethod
def BinaryExists():
......@@ -1202,11 +1206,11 @@ class SVNWrapper(SCMWrapper):
command = ['checkout', url, self.checkout_path]
command = self._AddAdditionalUpdateFlags(command, options, revision)
self._RunAndGetFileList(command, options, file_list, self._root_dir)
return
return self.Svnversion()
if not managed:
print ('________ unmanaged solution; skipping %s' % self.relpath)
return
return self.Svnversion()
if 'URL' not in from_info:
raise gclient_utils.Error(
......@@ -1294,7 +1298,7 @@ class SVNWrapper(SCMWrapper):
command = ['checkout', url, self.checkout_path]
command = self._AddAdditionalUpdateFlags(command, options, revision)
self._RunAndGetFileList(command, options, file_list, self._root_dir)
return
return self.Svnversion()
# If the provided url has a revision number that matches the revision
# number of the existing directory, then we don't need to bother updating.
......@@ -1316,6 +1320,7 @@ class SVNWrapper(SCMWrapper):
and not os.path.islink(full_path)):
print('\n_____ removing unversioned directory %s' % status[1])
gclient_utils.rmtree(full_path)
return self.Svnversion()
def updatesingle(self, options, args, file_list):
filename = args.pop()
......@@ -1442,6 +1447,11 @@ class SVNWrapper(SCMWrapper):
gclient_utils.CheckCallAndFilterAndHeader(['svn'] + args,
always=options.verbose, **kwargs)
def Svnversion(self):
"""Runs the lowest checked out revision in the current project."""
info = scm.SVN.CaptureLocalInfo([], os.path.join(self.checkout_path, '.'))
return info['Revision']
def _RunAndGetFileList(self, args, options, file_list, cwd=None):
"""Runs a commands that goes to stdout and grabs the file listed."""
cwd = cwd or self.checkout_path
......
......@@ -104,9 +104,11 @@ class SVNWrapperTestCase(BaseTestCase):
'GetCheckoutRoot',
'GetRevisionDate',
'GetUsableRev',
'Svnversion',
'RunCommand',
'cleanup',
'diff',
'name',
'nag_max',
'nag_timer',
'pack',
......@@ -198,6 +200,9 @@ class SVNWrapperTestCase(BaseTestCase):
cwd=self.root_dir,
file_list=files_list)
gclient_scm.scm.SVN._CaptureInfo([], self.base_path+'/.'
).AndReturn({'Revision': 100})
self.mox.ReplayAll()
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
relpath=self.relpath)
......@@ -228,6 +233,8 @@ class SVNWrapperTestCase(BaseTestCase):
cwd=self.root_dir,
file_list=files_list)
gclient_scm.gclient_utils.rmtree(self.base_path)
gclient_scm.scm.SVN._CaptureInfo([], self.base_path+'/.'
).AndReturn({'Revision': 100})
self.mox.ReplayAll()
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
relpath=self.relpath)
......@@ -352,6 +359,8 @@ class SVNWrapperTestCase(BaseTestCase):
['checkout', self.url, self.base_path, '--force', '--ignore-externals'],
cwd=self.root_dir,
file_list=files_list)
gclient_scm.scm.SVN._CaptureInfo([], self.base_path+'/.'
).AndReturn({'Revision': 100})
self.mox.ReplayAll()
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
relpath=self.relpath)
......@@ -386,6 +395,9 @@ class SVNWrapperTestCase(BaseTestCase):
gclient_scm.scm.SVN.Capture(['--version', '--quiet'], None
).AndReturn('1.5.1')
gclient_scm.scm.SVN._CaptureInfo([], self.base_path+'/.'
).AndReturn({'Revision': 100})
additional_args = []
if options.manually_grab_svn_rev:
additional_args = ['--revision', str(file_info['Revision'])]
......@@ -425,6 +437,9 @@ class SVNWrapperTestCase(BaseTestCase):
gclient_scm.scm.SVN._CaptureInfo([file_info['URL']], None
).AndReturn(file_info)
gclient_scm.scm.SVN._CaptureInfo([], self.base_path+'/.'
).AndReturn({'Revision': 100})
self.mox.ReplayAll()
files_list = []
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
......@@ -466,6 +481,9 @@ class SVNWrapperTestCase(BaseTestCase):
gclient_scm.os.path.islink(join(self.base_path, 'dir')).AndReturn(False)
gclient_scm.gclient_utils.rmtree(join(self.base_path, 'dir'))
gclient_scm.scm.SVN._CaptureInfo([], self.base_path+'/.'
).AndReturn({'Revision': 100})
self.mox.ReplayAll()
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
relpath=self.relpath)
......@@ -515,6 +533,9 @@ class SVNWrapperTestCase(BaseTestCase):
gclient_scm.scm.SVN._CaptureInfo([file_info['URL']], None
).AndReturn(file_info)
gclient_scm.scm.SVN._CaptureInfo([], self.base_path+'/.'
).AndReturn({'Revision': 100})
self.mox.ReplayAll()
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
relpath=self.relpath)
......@@ -585,6 +606,9 @@ class SVNWrapperTestCase(BaseTestCase):
gclient_scm.scm.SVN._CaptureInfo([file_info['URL']], None
).AndReturn(file_info)
gclient_scm.scm.SVN._CaptureInfo([], self.base_path+'/.'
).AndReturn({'Revision': 100})
self.mox.ReplayAll()
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
relpath=self.relpath)
......@@ -618,6 +642,9 @@ class SVNWrapperTestCase(BaseTestCase):
gclient_scm.scm.SVN._CaptureInfo([file_info['URL']], None
).AndReturn(file_info)
gclient_scm.scm.SVN._CaptureInfo([], self.base_path+'/.'
).AndReturn({'Revision': 100})
self.mox.ReplayAll()
scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir,
relpath=self.relpath)
......@@ -799,6 +826,7 @@ class ManagedGitWrapperTestCase(BaseGitWrapperTestCase):
'cache_locks',
'cleanup',
'diff',
'name',
'nag_max',
'nag_timer',
'pack',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment