| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 1 | #!/usr/bin/env python3 | 
|  | 2 | # | 
|  | 3 | # Copyright (C) 2018 Wind River Systems, Inc. | 
|  | 4 | # | 
|  | 5 | # This program is free software; you can redistribute it and/or modify | 
|  | 6 | # it under the terms of the GNU General Public License version 2 as | 
|  | 7 | # published by the Free Software Foundation. | 
|  | 8 | # | 
|  | 9 | # This program is distributed in the hope that it will be useful, | 
|  | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | 
|  | 12 | # See the GNU General Public License for more details. | 
|  | 13 | # | 
|  | 14 | # You should have received a copy of the GNU General Public License | 
|  | 15 | # along with this program; if not, write to the Free Software | 
|  | 16 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 
|  | 17 |  | 
|  | 18 | import os | 
|  | 19 | import sys | 
|  | 20 | import argparse | 
|  | 21 | import logging | 
|  | 22 | import re | 
|  | 23 |  | 
|  | 24 | class Dot(object): | 
|  | 25 | def __init__(self): | 
|  | 26 | parser = argparse.ArgumentParser( | 
|  | 27 | description="Analyse recipe-depends.dot generated by bitbake -g", | 
|  | 28 | epilog="Use %(prog)s --help to get help") | 
|  | 29 | parser.add_argument("dotfile", | 
|  | 30 | help = "Specify the dotfile", nargs = 1, action='store', default='') | 
|  | 31 | parser.add_argument("-k", "--key", | 
|  | 32 | help = "Specify the key, e.g., recipe name", | 
|  | 33 | action="store", default='') | 
|  | 34 | parser.add_argument("-d", "--depends", | 
|  | 35 | help = "Print the key's dependencies", | 
|  | 36 | action="store_true", default=False) | 
|  | 37 | parser.add_argument("-w", "--why", | 
|  | 38 | help = "Print why the key is built", | 
|  | 39 | action="store_true", default=False) | 
|  | 40 | parser.add_argument("-r", "--remove", | 
|  | 41 | help = "Remove duplicated dependencies to reduce the size of the dot files." | 
|  | 42 | " For example, A->B, B->C, A->C, then A->C can be removed.", | 
|  | 43 | action="store_true", default=False) | 
|  | 44 |  | 
|  | 45 | self.args = parser.parse_args() | 
|  | 46 |  | 
|  | 47 | if len(sys.argv) != 3 and len(sys.argv) < 5: | 
|  | 48 | print('ERROR: Not enough args, see --help for usage') | 
|  | 49 |  | 
|  | 50 | def main(self): | 
|  | 51 | #print(self.args.dotfile[0]) | 
|  | 52 | # The format is {key: depends} | 
|  | 53 | depends = {} | 
|  | 54 | with open(self.args.dotfile[0], 'r') as f: | 
|  | 55 | for line in f.readlines(): | 
|  | 56 | if ' -> ' not in line: | 
|  | 57 | continue | 
|  | 58 | line_no_quotes = line.replace('"', '') | 
|  | 59 | m = re.match("(.*) -> (.*)", line_no_quotes) | 
|  | 60 | if not m: | 
|  | 61 | print('WARNING: Found unexpected line: %s' % line) | 
|  | 62 | continue | 
|  | 63 | key = m.group(1) | 
|  | 64 | if key == "meta-world-pkgdata": | 
|  | 65 | continue | 
|  | 66 | dep = m.group(2) | 
|  | 67 | if key in depends: | 
|  | 68 | if not key in depends[key]: | 
|  | 69 | depends[key].add(dep) | 
|  | 70 | else: | 
|  | 71 | print('WARNING: Fonud duplicated line: %s' % line) | 
|  | 72 | else: | 
|  | 73 | depends[key] = set() | 
|  | 74 | depends[key].add(dep) | 
|  | 75 |  | 
|  | 76 | if self.args.remove: | 
|  | 77 | reduced_depends = {} | 
|  | 78 | for k, deps in depends.items(): | 
|  | 79 | child_deps = set() | 
|  | 80 | added = set() | 
|  | 81 | # Both direct and indirect depends are already in the dict, so | 
|  | 82 | # we don't have to do this recursively. | 
|  | 83 | for dep in deps: | 
|  | 84 | if dep in depends: | 
|  | 85 | child_deps |= depends[dep] | 
|  | 86 |  | 
|  | 87 | reduced_depends[k] = deps - child_deps | 
|  | 88 | outfile= '%s-reduced%s' % (self.args.dotfile[0][:-4], self.args.dotfile[0][-4:]) | 
|  | 89 | with open(outfile, 'w') as f: | 
|  | 90 | print('Saving reduced dot file to %s' % outfile) | 
|  | 91 | f.write('digraph depends {\n') | 
|  | 92 | for k, v in reduced_depends.items(): | 
|  | 93 | for dep in v: | 
|  | 94 | f.write('"%s" -> "%s"\n' % (k, dep)) | 
|  | 95 | f.write('}\n') | 
|  | 96 | sys.exit(0) | 
|  | 97 |  | 
|  | 98 | if self.args.key not in depends: | 
|  | 99 | print("ERROR: Can't find key %s in %s" % (self.args.key, self.args.dotfile[0])) | 
|  | 100 | sys.exit(1) | 
|  | 101 |  | 
|  | 102 | if self.args.depends: | 
|  | 103 | if self.args.key in depends: | 
|  | 104 | print('Depends: %s' % ' '.join(depends[self.args.key])) | 
|  | 105 |  | 
|  | 106 | reverse_deps = [] | 
|  | 107 | if self.args.why: | 
|  | 108 | for k, v in depends.items(): | 
|  | 109 | if self.args.key in v and not k in reverse_deps: | 
|  | 110 | reverse_deps.append(k) | 
|  | 111 | print('Because: %s' % ' '.join(reverse_deps)) | 
|  | 112 |  | 
|  | 113 | if __name__ == "__main__": | 
|  | 114 | try: | 
|  | 115 | dot = Dot() | 
|  | 116 | ret = dot.main() | 
|  | 117 | except Exception as esc: | 
|  | 118 | ret = 1 | 
|  | 119 | import traceback | 
|  | 120 | traceback.print_exc() | 
|  | 121 | sys.exit(ret) |