diff options
Diffstat (limited to 'scripts/oe-depends-dot')
-rwxr-xr-x | scripts/oe-depends-dot | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/scripts/oe-depends-dot b/scripts/oe-depends-dot new file mode 100755 index 0000000000..d02ee455f6 --- /dev/null +++ b/scripts/oe-depends-dot @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2018 Wind River Systems, Inc. +# +# SPDX-License-Identifier: GPL-2.0-only +# + +import os +import sys +import argparse +import logging +import re + +class Dot(object): + def __init__(self): + parser = argparse.ArgumentParser( + description="Analyse task-depends.dot generated by bitbake -g", + formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument("dotfile", + help = "Specify the dotfile", nargs = 1, action='store', default='') + parser.add_argument("-k", "--key", + help = "Specify the key, e.g., recipe name", + action="store", default='') + parser.add_argument("-d", "--depends", + help = "Print the key's dependencies", + action="store_true", default=False) + parser.add_argument("-w", "--why", + help = "Print why the key is built", + action="store_true", default=False) + parser.add_argument("-r", "--remove", + help = "Remove duplicated dependencies to reduce the size of the dot files." + " For example, A->B, B->C, A->C, then A->C can be removed.", + action="store_true", default=False) + + parser.epilog = """ +Examples: +First generate the .dot file: + bitbake -g core-image-minimal + +To find out why a package is being built: + %(prog)s -k <package> -w ./task-depends.dot + +To find out what a package depends on: + %(prog)s -k <package> -d ./task-depends.dot + +Reduce the .dot file packages only, no tasks: + %(prog)s -r ./task-depends.dot +""" + + self.args = parser.parse_args() + + if len(sys.argv) != 3 and len(sys.argv) < 5: + print('ERROR: Not enough args, see --help for usage') + + @staticmethod + def insert_dep_chain(chain, rdeps, alldeps): + """ + insert elements to chain from rdeps, according to alldeps + """ + # chain should at least contain one element + if len(chain) == 0: + raise + + inserted_elements = [] + for rdep in rdeps: + if rdep in chain: + continue + else: + for i in range(0, len(chain)-1): + if chain[i] in alldeps[rdep] and rdep in alldeps[chain[i+1]]: + chain.insert(i+1, rdep) + inserted_elements.append(rdep) + break + if chain[-1] in alldeps[rdep] and rdep not in chain: + chain.append(rdep) + inserted_elements.append(rdep) + return inserted_elements + + @staticmethod + def print_dep_chains(key, rdeps, alldeps): + rlist = rdeps.copy() + chain = [] + removed_rdeps = [] # hold rdeps removed from rlist + + chain.append(key) + while (len(rlist) != 0): + # insert chain from rlist + inserted_elements = Dot.insert_dep_chain(chain, rlist, alldeps) + if not inserted_elements: + if chain[-1] in rlist: + rlist.remove(chain[-1]) + removed_rdeps.append(chain[-1]) + chain.pop() + continue + else: + # insert chain from removed_rdeps + Dot.insert_dep_chain(chain, removed_rdeps, alldeps) + print(' -> '.join(list(reversed(chain)))) + + def main(self): + #print(self.args.dotfile[0]) + # The format is {key: depends} + depends = {} + with open(self.args.dotfile[0], 'r') as f: + for line in f.readlines(): + if ' -> ' not in line: + continue + line_no_quotes = line.replace('"', '') + m = re.match("(.*) -> (.*)", line_no_quotes) + if not m: + print('WARNING: Found unexpected line: %s' % line) + continue + key = m.group(1) + if key == "meta-world-pkgdata": + continue + dep = m.group(2) + key = key.split('.')[0] + dep = dep.split('.')[0] + if key == dep: + continue + if key in depends: + if not key in depends[key]: + depends[key].add(dep) + else: + print('WARNING: Fonud duplicated line: %s' % line) + else: + depends[key] = set() + depends[key].add(dep) + + if self.args.remove: + reduced_depends = {} + for k, deps in depends.items(): + child_deps = set() + added = set() + # Both direct and indirect depends are already in the dict, so + # we don't have to do this recursively. + for dep in deps: + if dep in depends: + child_deps |= depends[dep] + + reduced_depends[k] = deps - child_deps + outfile= '%s-reduced%s' % (self.args.dotfile[0][:-4], self.args.dotfile[0][-4:]) + with open(outfile, 'w') as f: + print('Saving reduced dot file to %s' % outfile) + f.write('digraph depends {\n') + for k, v in reduced_depends.items(): + for dep in v: + f.write('"%s" -> "%s"\n' % (k, dep)) + f.write('}\n') + sys.exit(0) + + if self.args.key not in depends: + print("ERROR: Can't find key %s in %s" % (self.args.key, self.args.dotfile[0])) + sys.exit(1) + + if self.args.depends: + if self.args.key in depends: + print('Depends: %s' % ' '.join(depends[self.args.key])) + + reverse_deps = [] + if self.args.why: + key_list = [self.args.key] + current_key = self.args.key + while (len(key_list) != 0): + current_key = key_list.pop() + for k, v in depends.items(): + if current_key in v and not k in reverse_deps: + reverse_deps.append(k) + key_list.append(k) + print('Because: %s' % ' '.join(reverse_deps)) + Dot.print_dep_chains(self.args.key, reverse_deps, depends) + +if __name__ == "__main__": + try: + dot = Dot() + ret = dot.main() + except Exception as esc: + ret = 1 + import traceback + traceback.print_exc() + sys.exit(ret) |