File indexing completed on 2024-05-12 03:53:40
0001 # -*- coding: utf-8 -*- 0002 # 0003 # SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org> 0004 # 0005 # SPDX-License-Identifier: BSD-2-Clause 0006 0007 import logging 0008 import itertools 0009 from functools import cmp_to_key 0010 0011 from kapidox.depdiagram.block import Block, quote 0012 from kapidox.depdiagram.framework import Framework 0013 from kapidox.depdiagram.frameworkdb import FrameworkDb 0014 0015 __all__ = ('generate',) 0016 0017 ROOT_NODE_ATTRS = dict(fontsize=12, shape="box") 0018 0019 GROUP_ATTRS = dict(style="filled", fillcolor="grey95", color="grey85") 0020 0021 # Blocks within groups 0022 OTHER_ATTRS = dict(style="filled", fillcolor="cornsilk", color="black") 0023 0024 QT_ATTRS = dict(style="filled", fillcolor="darkseagreen1", color="black") 0025 0026 FW_ATTRS = dict(style="filled", fillcolor="azure", color="black") 0027 0028 # Target blocks, used with --detailed 0029 FW_TARGET_ATTRS = dict(style="filled", fillcolor="paleturquoise") 0030 0031 # Highlight the wanted framework, used with --framework 0032 WANTED_FW_ATTRS = dict(penwidth=2) 0033 0034 class FrameworkCmp(object): 0035 def __init__(self, db): 0036 self.db = db 0037 self.src = set(db) 0038 self.dst = [] 0039 0040 def __call__(self, fw1, fw2): 0041 if self.depends_on(fw1, fw2): 0042 return 1 0043 if self.depends_on(fw2, fw1): 0044 return -1 0045 return 0 0046 0047 def depends_on(self, depender_fw, provider_fw): 0048 for dep_target in depender_fw.get_all_target_dependencies(): 0049 if provider_fw.has_target(dep_target): 0050 return True 0051 0052 try: 0053 dep_fw = self.db.get_framework_for_target(dep_target) 0054 except KeyError: 0055 # No framework for this target, must be an external dependency, 0056 # carry on 0057 continue 0058 if dep_fw != depender_fw and self.depends_on(dep_fw, provider_fw): 0059 return True 0060 0061 return False 0062 0063 0064 class DotWriter(Block): 0065 def __init__(self, db, out, wanted_fw=None, detailed=False): 0066 Block.__init__(self, out) 0067 self.db = db 0068 self.detailed = detailed 0069 self.wanted_fw = wanted_fw 0070 0071 def write(self): 0072 with self.curly_block("digraph Root") as root: 0073 root.write_list_attrs("node", **ROOT_NODE_ATTRS) 0074 0075 other_targets = self.db.find_external_targets() 0076 qt_targets = set([x for x in other_targets if x.startswith("Qt")]) 0077 other_targets.difference_update(qt_targets) 0078 0079 if qt_targets: 0080 with root.cluster_block("Qt", **GROUP_ATTRS) as b: 0081 b.write_list_attrs("node", **QT_ATTRS) 0082 b.write_nodes(qt_targets) 0083 0084 if other_targets: 0085 with root.cluster_block("Others", **GROUP_ATTRS) as b: 0086 b.write_list_attrs("node", **OTHER_ATTRS) 0087 b.write_nodes(other_targets) 0088 0089 lst = sorted([x for x in self.db], key=lambda x: x.tier) 0090 for tier, frameworks in itertools.groupby(lst, lambda x: x.tier): 0091 cluster_title = "Tier {}".format(tier) 0092 with root.cluster_block(cluster_title, **GROUP_ATTRS) as tier_block: 0093 tier_block.write_list_attrs("node", **FW_ATTRS) 0094 # Sort frameworks within the tier to ensure frameworks which 0095 # depend on other frameworks from that tier are listed after 0096 # their dependees. 0097 frameworks = list(frameworks) 0098 for fw in sorted(frameworks, key=cmp_to_key(FrameworkCmp(self.db))): 0099 if self.detailed: 0100 self.write_detailed_framework(tier_block, fw) 0101 else: 0102 self.write_framework(tier_block, fw) 0103 0104 def write_framework(self, tier_block, fw): 0105 if fw == self.wanted_fw: 0106 tier_block.write_list_attrs(quote(fw.name), **WANTED_FW_ATTRS) 0107 else: 0108 tier_block.write_nodes([fw.name]) 0109 edges = set([]) 0110 for target in fw.get_all_target_dependencies(): 0111 try: 0112 target_fw = self.db.get_framework_for_target(target) 0113 if fw == target_fw: 0114 continue 0115 dep = target_fw.name 0116 except KeyError: 0117 dep = target 0118 edges.add((fw.name, dep)) 0119 for dep_fw in fw.get_extra_frameworks(): 0120 edges.add((fw.name, dep_fw)) 0121 for edge in edges: 0122 tier_block.writeln('"{}" -> "{}";'.format(*edge)) 0123 0124 def write_detailed_framework(self, tier_block, fw): 0125 with tier_block.cluster_block(fw.name, **FW_ATTRS) as fw_block: 0126 if fw == self.wanted_fw: 0127 fw_block.write_attrs(**WANTED_FW_ATTRS) 0128 fw_block.write_list_attrs("node", **FW_TARGET_ATTRS) 0129 targets = sorted(fw.get_targets()) 0130 fw_block.write_nodes(targets) 0131 for target in targets: 0132 deps = fw.get_dependencies_for_target(target) 0133 for dep in sorted(deps): 0134 fw_block.writeln('"{}" -> "{}";'.format(target, dep)) 0135 0136 0137 def generate(out, dot_files, framework=None, with_qt=False, detailed=False): 0138 db = FrameworkDb() 0139 db.populate(dot_files, with_qt=with_qt) 0140 0141 if framework: 0142 wanted_fw = db.find_by_name(framework) 0143 if wanted_fw is None: 0144 logging.error("No framework named {}.".format(framework)) 0145 return False 0146 db.remove_unused_frameworks(wanted_fw) 0147 else: 0148 wanted_fw = None 0149 0150 writer = DotWriter(db, out, wanted_fw=wanted_fw, detailed=detailed) 0151 writer.write() 0152 0153 return True 0154 0155 # vi: ts=4 sw=4 et