Skip to content

Commit 9faeca8

Browse files
author
Arran Ireland
committed
Add bst graph command
Adds a new command to bst called 'graph' which reimplements the same API and functionality as contrib/bst-graph. The implementation details are within _stream.py as another function called 'graph'. Part of #1915.
1 parent 82e562c commit 9faeca8

4 files changed

Lines changed: 62 additions & 0 deletions

File tree

requirements/requirements.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
Click >= 7.0
2+
graphviz
23
grpcio
34
Jinja2 >= 2.10
45
pluginbase

requirements/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
click==8.1.7
2+
graphviz==0.20.3
23
grpcio==1.65.1
34
Jinja2==3.1.4
45
pluginbase==1.0.1

src/buildstream/_frontend/cli.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,3 +1654,29 @@ def artifact_delete(app, artifacts, deps):
16541654
"""Remove artifacts from the local cache"""
16551655
with app.initialized():
16561656
app.stream.artifact_delete(artifacts, selection=deps)
1657+
1658+
1659+
###################################################################
1660+
# Graph Command #
1661+
###################################################################
1662+
@cli.command(short_help="Render pipeline dependency graph.")
1663+
@click.option(
1664+
"--format",
1665+
"-f",
1666+
"format_",
1667+
metavar="FORMAT",
1668+
default="dot",
1669+
type=click.STRING,
1670+
help="Render format: e.g. `dot`, `pdf`, `png`, `svg`, etc",
1671+
)
1672+
@click.option(
1673+
"--view",
1674+
is_flag=True,
1675+
help="Open the rendered graph with the default application",
1676+
)
1677+
@click.argument("element", nargs=1, required=True, type=click.Path())
1678+
@click.pass_obj
1679+
def graph(app, element, format_, view):
1680+
"""Render dependency graph."""
1681+
with app.initialized():
1682+
app.stream.graph(element, format_, view)

src/buildstream/_stream.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from contextlib import contextmanager, suppress
2828
from collections import deque
2929
from typing import List, Tuple, Optional, Iterable, Callable
30+
from graphviz import Digraph
3031

3132
from ._artifactelement import verify_artifact_ref, ArtifactElement
3233
from ._artifactproject import ArtifactProject
@@ -1228,6 +1229,39 @@ def redirect_element_names(self, elements):
12281229

12291230
return list(output_elements)
12301231

1232+
# graph()
1233+
#
1234+
# Renders a dependency graph for the target element, in the given format and optionally opens it.
1235+
#
1236+
# Args:
1237+
# target (str): The target element from which to build a dependency graph.
1238+
#
1239+
def graph(self, target, format_, view):
1240+
graph_ = Digraph()
1241+
1242+
for e in self.load_selection([target], selection=_PipelineSelection.ALL, need_state=False):
1243+
name = e._get_full_name()
1244+
build_deps = set(dep._get_full_name() for dep in e._dependencies(_Scope.BUILD, recurse=False) if dep)
1245+
runtime_deps = set(dep._get_full_name() for dep in e._dependencies(_Scope.RUN, recurse=False) if dep)
1246+
1247+
graph_.node(name)
1248+
for dep in build_deps:
1249+
graph_.edge(name, dep, label='build-dep')
1250+
for dep in runtime_deps:
1251+
graph_.edge(name, dep, label='runtime-dep')
1252+
1253+
graph_name = os.path.basename(target)
1254+
graph_name, _ = os.path.splitext(graph_name)
1255+
graph_path = graph_.render(cleanup=True,
1256+
filename=graph_name,
1257+
format=format_,
1258+
view=view)
1259+
1260+
if graph_path:
1261+
self._context.messenger.info(f"Rendered dependency graph: {graph_path}")
1262+
else:
1263+
self._context.messenger.warn("Failed to render graph")
1264+
12311265
# get_state()
12321266
#
12331267
# Get the State object owned by Stream

0 commit comments

Comments
 (0)