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