poky/scripts/contrib/graph-tool
Paul Eggleton b87f0746b0 graph-tool: add filter subcommand
Add a filter subcommand to filter a task-depends.dot graph produced by
bitbake -g down to just a subset of targets/tasks.

(From OE-Core rev: a14b274b56676ff0ba55a4048169ad60c9514994)

Signed-off-by: Paul Eggleton <paul.eggleton@linux.microsoft.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2020-06-16 23:39:28 +01:00

4.2 KiB
Executable File

#!/usr/bin/env python3

Simple graph query utility

useful for getting answers from .dot files produced by bitbake -g

Written by: Paul Eggleton paul.eggleton@linux.intel.com

Copyright 2013 Intel Corporation

SPDX-License-Identifier: GPL-2.0-only

import sys import os import argparse

scripts_lib_path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(file)), '..', 'lib')) sys.path.insert(0, scripts_lib_path) import argparse_oe

def get_path_networkx(dotfile, fromnode, tonode): try: import networkx except ImportError: print('ERROR: Please install the networkx python module') sys.exit(1)

graph = networkx.DiGraph(networkx.nx_pydot.read_dot(dotfile))
def node_missing(node):
    import difflib
    close_matches = difflib.get_close_matches(node, graph.nodes(), cutoff=0.7)
    if close_matches:
        print('ERROR: no node "%s" in graph. Close matches:\n  %s' % (node, '\n  '.join(close_matches)))
    sys.exit(1)

if not fromnode in graph:
    node_missing(fromnode)
if not tonode in graph:
    node_missing(tonode)
return networkx.all_simple_paths(graph, source=fromnode, target=tonode)

def find_paths(args): path = None for path in get_path_networkx(args.dotfile, args.fromnode, args.tonode): print(" -> ".join(map(str, path))) if not path: print("ERROR: no path from %s to %s in graph" % (args.fromnode, args.tonode)) return 1

def filter_graph(args): import fnmatch

exclude_tasks = []
if args.exclude_tasks:
    for task in args.exclude_tasks.split(','):
        if not task.startswith('do_'):
            task = 'do_%s' % task
        exclude_tasks.append(task)

def checkref(strval):
    strval = strval.strip().strip('"')
    target, taskname = strval.rsplit('.', 1)
    if exclude_tasks:
        for extask in exclude_tasks:
            if fnmatch.fnmatch(taskname, extask):
                return False
    if strval in args.ref or target in args.ref:
        return True
    return False

with open(args.infile, 'r') as f:
    for line in f:
        line = line.rstrip()
        if line.startswith(('digraph', '}')):
            print(line)
        elif '->' in line:
            linesplit = line.split('->')
            if checkref(linesplit[0]) and checkref(linesplit[1]):
                print(line)
        elif (not args.no_nodes) and checkref(line.split()[0]):
            print(line)

def main(): parser = argparse_oe.ArgumentParser(description='Small utility for working with .dot graph files')

subparsers = parser.add_subparsers(title='subcommands', metavar='<subcommand>')
subparsers.required = True

parser_find_paths = subparsers.add_parser('find-paths',
                                          help='Find all of the paths between two nodes in a dot graph',
                                          description='Finds all of the paths between two nodes in a dot graph')
parser_find_paths.add_argument('dotfile', help='.dot graph to search in')
parser_find_paths.add_argument('fromnode', help='starting node name')
parser_find_paths.add_argument('tonode', help='ending node name')
parser_find_paths.set_defaults(func=find_paths)

parser_filter = subparsers.add_parser('filter',
                                       help='Pare down a task graph to contain only the specified references',
                                       description='Pares down a task-depends.dot graph produced by bitbake -g to contain only the specified references')
parser_filter.add_argument('infile', help='Input file')
parser_filter.add_argument('ref', nargs='+', help='Reference to include (either recipe/target name or full target.taskname specification)')
parser_filter.add_argument('-n', '--no-nodes', action='store_true', help='Skip node formatting lines')
parser_filter.add_argument('-x', '--exclude-tasks', help='Comma-separated list of tasks to exclude (do_ prefix optional, wildcards allowed)')
parser_filter.set_defaults(func=filter_graph)

args = parser.parse_args()

ret = args.func(args)
return ret

if name == "main": ret = main() sys.exit(ret)