gclient: Create graphviz file in flatten command.

Change-Id: I57808ecb43df51a007945874339400fee2342807
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/4229571
Reviewed-by: Josip Sokcevic <sokcevic@chromium.org>
Commit-Queue: Joanna Wang <jojwang@chromium.org>
changes/71/4229571/9
Joanna Wang 3 years ago committed by LUCI CQ
parent b54d0f013b
commit 9144b67c7f

@ -544,6 +544,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
self.set_url(url)
def ToLines(self):
# () -> Sequence[str]
"""Returns strings representing the deps (info, graphviz line)"""
s = []
condition_part = ([' "condition": %r,' % self.condition]
if self.condition else [])
@ -1322,16 +1324,20 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
def __repr__(self):
return '%s: %s' % (self.name, self.url)
def hierarchy(self, include_url=True):
def hierarchy(self, include_url=True, graphviz=False):
"""Returns a human-readable hierarchical reference to a Dependency."""
def format_name(d):
if include_url:
return '%s(%s)' % (d.name, d.url)
return d.name
return '"%s"' % d.name # quotes required for graph dot file.
out = format_name(self)
i = self.parent
while i and i.name:
out = '%s -> %s' % (format_name(i), out)
if graphviz:
# for graphviz we just need each parent->child relationship listed once.
return out
i = i.parent
return out
@ -2275,10 +2281,13 @@ class CipdDependency(Dependency):
self.url, self.root.root_dir, self.name, self.outbuf, out_cb,
root=self._cipd_root, package=self._cipd_package)
def hierarchy(self, include_url=False):
def hierarchy(self, include_url=False, graphviz=False):
if graphviz:
return '' # graphviz lines not implemented for cipd deps.
return self.parent.hierarchy(include_url) + ' -> ' + self._cipd_subdir
def ToLines(self):
# () -> Sequence[str]
"""Return a list of lines representing this in a DEPS file."""
def escape_cipd_var(package):
return package.replace('{', '{{').replace('}', '}}')
@ -2393,6 +2402,7 @@ class Flattener(object):
self._client = client
self._deps_string = None
self._deps_graph_lines = None
self._deps_files = set()
self._allowed_hosts = set()
@ -2408,6 +2418,11 @@ class Flattener(object):
assert self._deps_string is not None
return self._deps_string
@property
def deps_graph_lines(self):
assert self._deps_graph_lines is not None
return self._deps_graph_lines
@property
def deps_files(self):
return self._deps_files
@ -2464,16 +2479,17 @@ class Flattener(object):
gn_args_dep = self._deps.get(self._client.dependencies[0]._gn_args_from,
self._client.dependencies[0])
self._deps_graph_lines = _DepsToDotGraphLines(self._deps)
self._deps_string = '\n'.join(
_GNSettingsToLines(gn_args_dep._gn_args_file, gn_args_dep._gn_args) +
_AllowedHostsToLines(self._allowed_hosts) +
_DepsToLines(self._deps) +
_AllowedHostsToLines(self._allowed_hosts) + _DepsToLines(self._deps) +
_HooksToLines('hooks', self._hooks) +
_HooksToLines('pre_deps_hooks', self._pre_deps_hooks) +
_VarsToLines(self._vars) +
['# %s, %s' % (url, deps_file)
for url, deps_file, _ in sorted(self._deps_files)] +
['']) # Ensure newline at end of file.
_VarsToLines(self._vars) + [
'# %s, %s' % (url, deps_file)
for url, deps_file, _ in sorted(self._deps_files)
] + ['']) # Ensure newline at end of file.
def _add_dep(self, dep):
"""Helper to add a dependency to flattened DEPS.
@ -2546,6 +2562,8 @@ def CMDflatten(parser, args):
'--pin-all-deps', action='store_true',
help=('Pin all deps, even if not pinned in DEPS. CAVEAT: only does so '
'for checked out deps, NOT deps_os.'))
parser.add_option('--deps-graph-file',
help='Provide a path for the output graph file')
options, args = parser.parse_args(args)
options.nohooks = True
@ -2568,6 +2586,10 @@ def CMDflatten(parser, args):
else:
print(flattener.deps_string)
if options.deps_graph_file:
with open(options.deps_graph_file, 'w') as f:
f.write('\n'.join(flattener.deps_graph_lines))
deps_files = [{'url': d[0], 'deps_file': d[1], 'hierarchy': d[2]}
for d in sorted(flattener.deps_files)]
if options.output_deps_files:
@ -2599,6 +2621,7 @@ def _AllowedHostsToLines(allowed_hosts):
def _DepsToLines(deps):
# type: (Mapping[str, Dependency]) -> Sequence[str]
"""Converts |deps| dict to list of lines for output."""
if not deps:
return []
@ -2609,6 +2632,20 @@ def _DepsToLines(deps):
return s
def _DepsToDotGraphLines(deps):
# type: (Mapping[str, Dependency]) -> Sequence[str]
"""Converts |deps| dict to list of lines for dot graphs"""
if not deps:
return []
graph_lines = ["digraph {\n\trankdir=\"LR\";"]
for _, dep in sorted(deps.items()):
line = dep.hierarchy(include_url=False, graphviz=True)
if line:
graph_lines.append("\t%s" % line)
graph_lines.append("}")
return graph_lines
def _DepsOsToLines(deps_os):
"""Converts |deps_os| dict to list of lines for output."""
if not deps_os:

@ -725,70 +725,70 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
self.assertEqual([
'gclient_gn_args_file = "src/repo2/gclient.args"',
'gclient_gn_args = [\'false_var\', \'false_str_var\', \'true_var\', '
'\'true_str_var\', \'str_var\', \'cond_var\']',
'\'true_str_var\', \'str_var\', \'cond_var\']',
'allowed_hosts = [',
' "' + self.git_base + '",',
']',
'',
'deps = {',
' # src -> src/repo2 -> foo/bar',
' # "src" -> "src/repo2" -> "foo/bar"',
' "foo/bar": {',
' "url": "' + self.git_base + 'repo_3",',
' "condition": \'(repo2_false_var) and (true_str_var)\',',
' },',
'',
' # src',
' # "src"',
' "src": {',
' "url": "' + self.git_base + 'repo_6",',
' },',
'',
' # src -> src/mac_repo',
' # "src" -> "src/mac_repo"',
' "src/mac_repo": {',
' "url": "' + self.git_base + 'repo_5",',
' "condition": \'checkout_mac\',',
' },',
'',
' # src -> src/repo8 -> src/recursed_os_repo',
' # "src" -> "src/repo8" -> "src/recursed_os_repo"',
' "src/recursed_os_repo": {',
' "url": "' + self.git_base + 'repo_5",',
' "condition": \'(checkout_linux) or (checkout_mac)\',',
' },',
'',
' # src -> src/repo15',
' # "src" -> "src/repo15"',
' "src/repo15": {',
' "url": "' + self.git_base + 'repo_15",',
' },',
'',
' # src -> src/repo16',
' # "src" -> "src/repo16"',
' "src/repo16": {',
' "url": "' + self.git_base + 'repo_16",',
' },',
'',
' # src -> src/repo2',
' # "src" -> "src/repo2"',
' "src/repo2": {',
' "url": "' + self.git_base + 'repo_2@%s",' % (
self.githash('repo_2', 1)[:7]),
' "url": "' + self.git_base + 'repo_2@%s",' %
(self.githash('repo_2', 1)[:7]),
' "condition": \'true_str_var\',',
' },',
'',
' # src -> src/repo4',
' # "src" -> "src/repo4"',
' "src/repo4": {',
' "url": "' + self.git_base + 'repo_4",',
' "condition": \'False\',',
' },',
'',
' # src -> src/repo8',
' # "src" -> "src/repo8"',
' "src/repo8": {',
' "url": "' + self.git_base + 'repo_8",',
' },',
'',
' # src -> src/unix_repo',
' # "src" -> "src/unix_repo"',
' "src/unix_repo": {',
' "url": "' + self.git_base + 'repo_5",',
' "condition": \'checkout_linux\',',
' },',
'',
' # src -> src/win_repo',
' # "src" -> "src/win_repo"',
' "src/win_repo": {',
' "url": "' + self.git_base + 'repo_5",',
' "condition": \'checkout_win\',',
@ -797,7 +797,7 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
'}',
'',
'hooks = [',
' # src',
' # "src"',
' {',
' "pattern": ".",',
' "condition": \'True\',',
@ -806,11 +806,11 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
' "python",',
' "-c",',
' "open(\'src/git_hooked1\', \'w\')'
'.write(\'git_hooked1\')",',
'.write(\'git_hooked1\')",',
' ]',
' },',
'',
' # src',
' # "src"',
' {',
' "pattern": "nonexistent",',
' "cwd": ".",',
@ -821,7 +821,7 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
' ]',
' },',
'',
' # src',
' # "src"',
' {',
' "pattern": ".",',
' "condition": \'checkout_mac\',',
@ -830,11 +830,11 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
' "python",',
' "-c",',
' "open(\'src/git_hooked_mac\', \'w\').write('
'\'git_hooked_mac\')",',
'\'git_hooked_mac\')",',
' ]',
' },',
'',
' # src -> src/repo15',
' # "src" -> "src/repo15"',
' {',
' "name": "absolute_cwd",',
' "pattern": ".",',
@ -846,7 +846,7 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
' ]',
' },',
'',
' # src -> src/repo16',
' # "src" -> "src/repo16"',
' {',
' "name": "relative_cwd",',
' "pattern": ".",',
@ -860,45 +860,45 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
']',
'',
'vars = {',
' # src',
' # "src"',
' "DummyVariable": \'repo\',',
'',
' # src',
' # "src"',
' "cond_var": \'false_str_var and true_var\',',
'',
' # src',
' # "src"',
' "false_str_var": \'False\',',
'',
' # src',
' # "src"',
' "false_var": False,',
'',
' # src',
' # "src"',
' "git_base": \'' + self.git_base + '\',',
'',
' # src',
' # "src"',
' "hook1_contents": \'git_hooked1\',',
'',
' # src -> src/repo2',
' # "src" -> "src/repo2"',
' "repo2_false_var": \'False\',',
'',
' # src',
' # "src"',
' "repo5_var": \'/repo_5\',',
'',
' # src',
' # "src"',
' "str_var": \'abc\',',
'',
' # src',
' # "src"',
' "true_str_var": \'True\',',
'',
' # src [custom_var override]',
' # "src" [custom_var override]',
' "true_var": \'False\',',
'',
'}',
'',
'# ' + self.git_base + 'repo_15, DEPS',
'# ' + self.git_base + 'repo_16, DEPS',
'# ' + self.git_base + 'repo_2@%s, DEPS' % (
self.githash('repo_2', 1)[:7]),
'# ' + self.git_base + 'repo_2@%s, DEPS' %
(self.githash('repo_2', 1)[:7]),
'# ' + self.git_base + 'repo_6, DEPS',
'# ' + self.git_base + 'repo_8, DEPS',
], deps_contents.splitlines())
@ -920,89 +920,89 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
self.assertEqual([
'gclient_gn_args_file = "src/repo2/gclient.args"',
'gclient_gn_args = [\'false_var\', \'false_str_var\', \'true_var\', '
'\'true_str_var\', \'str_var\', \'cond_var\']',
'\'true_str_var\', \'str_var\', \'cond_var\']',
'allowed_hosts = [',
' "' + self.git_base + '",',
']',
'',
'deps = {',
' # src -> src/repo2 -> foo/bar',
' # "src" -> "src/repo2" -> "foo/bar"',
' "foo/bar": {',
' "url": "' + self.git_base + 'repo_3@%s",' % (
self.githash('repo_3', 2)),
' "url": "' + self.git_base + 'repo_3@%s",' %
(self.githash('repo_3', 2)),
' "condition": \'(repo2_false_var) and (true_str_var)\',',
' },',
'',
' # src',
' # "src"',
' "src": {',
' "url": "' + self.git_base + 'repo_6@%s",' % (
self.githash('repo_6', 1)),
' "url": "' + self.git_base + 'repo_6@%s",' %
(self.githash('repo_6', 1)),
' },',
'',
' # src -> src/mac_repo',
' # "src" -> "src/mac_repo"',
' "src/mac_repo": {',
' "url": "' + self.git_base + 'repo_5@%s",' % (
self.githash('repo_5', 3)),
' "url": "' + self.git_base + 'repo_5@%s",' %
(self.githash('repo_5', 3)),
' "condition": \'checkout_mac\',',
' },',
'',
' # src -> src/repo8 -> src/recursed_os_repo',
' # "src" -> "src/repo8" -> "src/recursed_os_repo"',
' "src/recursed_os_repo": {',
' "url": "' + self.git_base + 'repo_5@%s",' % (
self.githash('repo_5', 3)),
' "url": "' + self.git_base + 'repo_5@%s",' %
(self.githash('repo_5', 3)),
' "condition": \'(checkout_linux) or (checkout_mac)\',',
' },',
'',
' # src -> src/repo15',
' # "src" -> "src/repo15"',
' "src/repo15": {',
' "url": "' + self.git_base + 'repo_15@%s",' % (
self.githash('repo_15', 1)),
' "url": "' + self.git_base + 'repo_15@%s",' %
(self.githash('repo_15', 1)),
' },',
'',
' # src -> src/repo16',
' # "src" -> "src/repo16"',
' "src/repo16": {',
' "url": "' + self.git_base + 'repo_16@%s",' % (
self.githash('repo_16', 1)),
' "url": "' + self.git_base + 'repo_16@%s",' %
(self.githash('repo_16', 1)),
' },',
'',
' # src -> src/repo2',
' # "src" -> "src/repo2"',
' "src/repo2": {',
' "url": "' + self.git_base + 'repo_2@%s",' % (
self.githash('repo_2', 1)),
' "url": "' + self.git_base + 'repo_2@%s",' %
(self.githash('repo_2', 1)),
' "condition": \'true_str_var\',',
' },',
'',
' # src -> src/repo4',
' # "src" -> "src/repo4"',
' "src/repo4": {',
' "url": "' + self.git_base + 'repo_4@%s",' % (
self.githash('repo_4', 2)),
' "url": "' + self.git_base + 'repo_4@%s",' %
(self.githash('repo_4', 2)),
' "condition": \'False\',',
' },',
'',
' # src -> src/repo8',
' # "src" -> "src/repo8"',
' "src/repo8": {',
' "url": "' + self.git_base + 'repo_8@%s",' % (
self.githash('repo_8', 1)),
' "url": "' + self.git_base + 'repo_8@%s",' %
(self.githash('repo_8', 1)),
' },',
'',
' # src -> src/unix_repo',
' # "src" -> "src/unix_repo"',
' "src/unix_repo": {',
' "url": "' + self.git_base + 'repo_5@%s",' % (
self.githash('repo_5', 3)),
' "url": "' + self.git_base + 'repo_5@%s",' %
(self.githash('repo_5', 3)),
' "condition": \'checkout_linux\',',
' },',
'',
' # src -> src/win_repo',
' # "src" -> "src/win_repo"',
' "src/win_repo": {',
' "url": "' + self.git_base + 'repo_5@%s",' % (
self.githash('repo_5', 3)),
' "url": "' + self.git_base + 'repo_5@%s",' %
(self.githash('repo_5', 3)),
' "condition": \'checkout_win\',',
' },',
'',
'}',
'',
'hooks = [',
' # src',
' # "src"',
' {',
' "pattern": ".",',
' "condition": \'True\',',
@ -1011,11 +1011,11 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
' "python",',
' "-c",',
' "open(\'src/git_hooked1\', \'w\')'
'.write(\'git_hooked1\')",',
'.write(\'git_hooked1\')",',
' ]',
' },',
'',
' # src',
' # "src"',
' {',
' "pattern": "nonexistent",',
' "cwd": ".",',
@ -1026,7 +1026,7 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
' ]',
' },',
'',
' # src',
' # "src"',
' {',
' "pattern": ".",',
' "condition": \'checkout_mac\',',
@ -1035,11 +1035,11 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
' "python",',
' "-c",',
' "open(\'src/git_hooked_mac\', \'w\').write('
'\'git_hooked_mac\')",',
'\'git_hooked_mac\')",',
' ]',
' },',
'',
' # src -> src/repo15',
' # "src" -> "src/repo15"',
' {',
' "name": "absolute_cwd",',
' "pattern": ".",',
@ -1051,7 +1051,7 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
' ]',
' },',
'',
' # src -> src/repo16',
' # "src" -> "src/repo16"',
' {',
' "name": "relative_cwd",',
' "pattern": ".",',
@ -1065,51 +1065,48 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
']',
'',
'vars = {',
' # src',
' # "src"',
' "DummyVariable": \'repo\',',
'',
' # src',
' # "src"',
' "cond_var": \'false_str_var and true_var\',',
'',
' # src',
' # "src"',
' "false_str_var": \'False\',',
'',
' # src',
' # "src"',
' "false_var": False,',
'',
' # src',
' # "src"',
' "git_base": \'' + self.git_base + '\',',
'',
' # src',
' # "src"',
' "hook1_contents": \'git_hooked1\',',
'',
' # src -> src/repo2',
' # "src" -> "src/repo2"',
' "repo2_false_var": \'False\',',
'',
' # src',
' # "src"',
' "repo5_var": \'/repo_5\',',
'',
' # src',
' # "src"',
' "str_var": \'abc\',',
'',
' # src',
' # "src"',
' "true_str_var": \'True\',',
'',
' # src',
' # "src"',
' "true_var": True,',
'',
'}',
'',
'# ' + self.git_base + 'repo_15@%s, DEPS' % (
self.githash('repo_15', 1)),
'# ' + self.git_base + 'repo_16@%s, DEPS' % (
self.githash('repo_16', 1)),
'# ' + self.git_base + 'repo_2@%s, DEPS' % (
self.githash('repo_2', 1)),
'# ' + self.git_base + 'repo_6@%s, DEPS' % (
self.githash('repo_6', 1)),
'# ' + self.git_base + 'repo_8@%s, DEPS' % (
self.githash('repo_8', 1)),
'# ' + self.git_base + 'repo_15@%s, DEPS' %
(self.githash('repo_15', 1)),
'# ' + self.git_base + 'repo_16@%s, DEPS' %
(self.githash('repo_16', 1)),
'# ' + self.git_base + 'repo_2@%s, DEPS' % (self.githash('repo_2', 1)),
'# ' + self.git_base + 'repo_6@%s, DEPS' % (self.githash('repo_6', 1)),
'# ' + self.git_base + 'repo_8@%s, DEPS' % (self.githash('repo_8', 1)),
], deps_contents.splitlines())
# TODO(crbug.com/1024683): Enable for windows.
@ -1134,51 +1131,51 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
'gclient_gn_args_file = "src/repo8/gclient.args"',
"gclient_gn_args = ['str_var']",
'deps = {',
' # src',
' # "src"',
' "src": {',
' "url": "' + self.git_base + 'repo_10",',
' },',
'',
' # src -> src/repo9 -> src/repo8 -> src/recursed_os_repo',
' # "src" -> "src/repo9" -> "src/repo8" -> "src/recursed_os_repo"',
' "src/recursed_os_repo": {',
' "url": "' + self.git_base + 'repo_5",',
' "condition": \'(checkout_linux) or (checkout_mac)\',',
' },',
'',
' # src -> src/repo11',
' # "src" -> "src/repo11"',
' "src/repo11": {',
' "url": "' + self.git_base + 'repo_11",',
' "condition": \'(checkout_ios) or (checkout_mac)\',',
' },',
'',
' # src -> src/repo11 -> src/repo12',
' # "src" -> "src/repo11" -> "src/repo12"',
' "src/repo12": {',
' "url": "' + self.git_base + 'repo_12",',
' "condition": \'(checkout_ios) or (checkout_mac)\',',
' },',
'',
' # src -> src/repo9 -> src/repo4',
' # "src" -> "src/repo9" -> "src/repo4"',
' "src/repo4": {',
' "url": "' + self.git_base + 'repo_4",',
' "condition": \'checkout_android\',',
' },',
'',
' # src -> src/repo6',
' # "src" -> "src/repo6"',
' "src/repo6": {',
' "url": "' + self.git_base + 'repo_6",',
' },',
'',
' # src -> src/repo9 -> src/repo7',
' # "src" -> "src/repo9" -> "src/repo7"',
' "src/repo7": {',
' "url": "' + self.git_base + 'repo_7",',
' },',
'',
' # src -> src/repo9 -> src/repo8',
' # "src" -> "src/repo9" -> "src/repo8"',
' "src/repo8": {',
' "url": "' + self.git_base + 'repo_8",',
' },',
'',
' # src -> src/repo9',
' # "src" -> "src/repo9"',
' "src/repo9": {',
' "url": "' + self.git_base + 'repo_9",',
' },',
@ -1186,7 +1183,7 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
'}',
'',
'vars = {',
' # src -> src/repo9',
' # "src" -> "src/repo9"',
' "str_var": \'xyz\',',
'',
'}',
@ -1230,12 +1227,12 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
self.assertEqual([
'deps = {',
' # src',
' # "src"',
' "src": {',
' "url": "' + self.git_base + 'repo_14",',
' },',
'',
' # src -> src/another_cipd_dep',
' # "src" -> src/another_cipd_dep',
' "src/another_cipd_dep": {',
' "packages": [',
' {',
@ -1250,7 +1247,7 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
' "dep_type": "cipd",',
' },',
'',
' # src -> src/cipd_dep',
' # "src" -> src/cipd_dep',
' "src/cipd_dep": {',
' "packages": [',
' {',
@ -1261,7 +1258,7 @@ class GClientSmokeGIT(gclient_smoketest_base.GClientSmokeBase):
' "dep_type": "cipd",',
' },',
'',
' # src -> src/cipd_dep_with_cipd_variable',
' # "src" -> src/cipd_dep_with_cipd_variable',
' "src/cipd_dep_with_cipd_variable": {',
' "packages": [',
' {',

Loading…
Cancel
Save