Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 11 additions & 20 deletions contrib/bst-graph
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ installed.
import argparse
import subprocess
import re
import json
import urllib.parse

from graphviz import Digraph
from ruamel.yaml import YAML

def parse_args():
'''Handle parsing of command line arguments.
Expand Down Expand Up @@ -72,34 +72,25 @@ def unique_node_name(s):
return urllib.parse.quote_plus(s)


def parse_graph(lines):
def parse_graph(output):
'''Return nodes and edges of the parsed grpah.

Args:
lines: List of lines in format 'NAME|BUILD-DEPS|RUNTIME-DEPS'
output: json output

Returns:
Tuple of format (nodes,build_deps,runtime_deps)
Each member of build_deps and runtime_deps is also a tuple.
'''
parser = YAML(typ="safe")
project = json.loads(output)
nodes = set()
build_deps = set()
runtime_deps = set()
for line in lines:
line = line.strip()
if not line:
continue
# It is safe to split on '|' as it is not a valid character for
# element names.
name, build_dep, runtime_dep = line.split('|')

build_dep = parser.load(build_dep)
runtime_dep = parser.load(runtime_dep)

for element in project["elements"]:
name = element["name"]
nodes.add(name)
[build_deps.add((name, dep)) for dep in build_dep if dep]
[runtime_deps.add((name, dep)) for dep in runtime_dep if dep]
[build_deps.add((name, dep)) for dep in element.get("build_dependencies", [])]
[runtime_deps.add((name, dep)) for dep in element.get("runtime_dependencies", [])]

return nodes, build_deps, runtime_deps

Expand Down Expand Up @@ -127,13 +118,13 @@ def generate_graph(nodes, build_deps, runtime_deps):

def main():
args = parse_args()
cmd = ['bst', 'show', '--format', '%{name}|%{build-deps}|%{runtime-deps}||']
cmd = ['bst', 'inspect']
if 'element' in args:
cmd += args.element
graph_lines = subprocess.check_output(cmd, universal_newlines=True)
json_output = subprocess.check_output(cmd, universal_newlines=True)
# NOTE: We generate nodes and edges before giving them to graphviz as
# the library does not de-deuplicate them.
nodes, build_deps, runtime_deps = parse_graph(re.split(r"\|\|", graph_lines))
nodes, build_deps, runtime_deps = parse_graph(json_output)
graph = generate_graph(nodes, build_deps, runtime_deps)

print(graph.source)
Expand Down
69 changes: 69 additions & 0 deletions src/buildstream/_frontend/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from ..types import _CacheBuildTrees, _SchedulerErrorAction, _PipelineSelection, _HostMount, _Scope
from .._remotespec import RemoteSpec, RemoteSpecPurpose
from ..utils import UtilError
from .inspect import Inspector


##################################################################
Expand Down Expand Up @@ -531,6 +532,74 @@ def build(
)


##################################################################
# Inspect Command #
##################################################################
@cli.command(name="inspect", short_help="Inspect Project Information")
@click.option(
"--except", "except_", multiple=True, type=click.Path(readable=False), help="Except certain dependencies"
)
@click.option(
"--deps",
"-d",
default=_PipelineSelection.ALL,
show_default=True,
type=FastEnumType(
_PipelineSelection,
[
_PipelineSelection.NONE,
_PipelineSelection.RUN,
_PipelineSelection.BUILD,
_PipelineSelection.ALL,
],
),
help="The dependencies to show",
)
@click.argument("elements", nargs=-1, type=click.Path(readable=False))
@click.pass_obj
def inspect(app, except_, elements, deps):
"""Access structured data about a given buildstream project and it's computed elements.

Specifying no elements will result in showing the default targets
of the project. If no default targets are configured, all project
elements will be shown.

When this command is executed from a workspace directory, the default
is to show the workspace element.

By default this will show all of the dependencies of the
specified target element.

Specify ``--deps`` to control which elements to show:

\b
none: No dependencies, just the element itself
run: Runtime dependencies, including the element itself
build: Build time dependencies, excluding the element itself
all: All dependencies

Examples:

# A specific target (glob pattern)
\n
bst inspect public/*.bst


# With a dependency target
\n
bst inspect -d run


# Show each remote file source (with the help of jq)
\n
bst inspect -d all | jq '.elements.[].sources | select( . != null ) | .[] | select( .medium == "remote-file")

"""
with app.initialized():
inspector = Inspector(app.stream, app.project, app.context)
inspector.dump_to_stdout(elements, except_=except_, selection=deps)


##################################################################
# Show Command #
##################################################################
Expand Down
Loading