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