blob: c8b099be7f2ed45c46a75edb40e527f5f03d1754 [file] [log] [blame]
#!/usr/bin/env python3.8
# Copyright 2021 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
'''Produces a representation of the dependencies between Banjo libraries.'''
import argparse
import copy
import json
import re
import sys
_LIBRARY_LABEL = r'^//sdk/banjo/([^:]+):\1(\.\.\.)?$'
_DUMMY_LIBRARIES = set([
'zircon.hw.pci',
])
def _extract_dependencies(label, base_depth, deps, result):
index = 0
current_label = None
dep_labels = []
while index < len(deps):
depth = deps[index].index('//')
if depth <= base_depth:
break
elif depth == base_depth + 2:
current_label = re.sub(r'\.\.\.', '', deps[index]).strip()
dep_labels.append(current_label)
index += 1
else:
index += _extract_dependencies(
current_label, base_depth + 2, deps[index:], result)
result[label] = dep_labels
return index
def extract_dependencies(deps):
result = {}
_extract_dependencies('main', -2, deps, result)
return result
def filter_banjo_libraries(deps):
# This filters dep lists to retain only Banjo libraries.
normalize_deps = lambda l: list(
filter(lambda i: i, map(lambda c: get_library_label(c), l)))
# Retain only Banjo libraries at the top level and normalize their dep lists.
result = dict(
map(
lambda r: (r[0], normalize_deps(r[1])),
filter(
lambda t: t[0],
map(lambda p: (get_library_label(p[0]), p[1]), deps.items()))))
# Add all standalone libraries.
for lib in normalize_deps(deps['main']):
if lib not in result:
result[lib] = []
return result
def get_library_label(label):
match = re.match(_LIBRARY_LABEL, label)
return match.group(1) if match else None
def remove_library(name, deps):
if name in deps:
del deps[name]
for v in deps.values():
if name in v:
v.remove(name)
def add_back_edges(deps):
result = copy.deepcopy(deps)
for lib, lib_deps in deps.items():
for d in lib_deps:
result[d].append(lib)
return result
def find_connected_components(deps):
def find_component(library, component):
connections = deps.pop(library)
component.add(library)
for c in connections:
if c not in deps:
continue
find_component(c, component)
return component
result = []
while deps:
result.append(find_component(list(deps.keys())[0], set()))
return result
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'--gn-deps',
help='Path to the JSON-formatted files with dependency info',
required=True)
args = parser.parse_args()
with open(args.gn_deps, 'r') as deps_file:
deps = json.load(deps_file)
all_deps = extract_dependencies(deps['//sdk/banjo:banjo']['deps'])
banjo_deps = filter_banjo_libraries(all_deps)
remove_library('zx', banjo_deps)
remove_library('fuchsia.hardware.composite', banjo_deps)
banjo_graph = add_back_edges(banjo_deps)
components = find_connected_components(banjo_graph)
blocked = filter(lambda g: g & _DUMMY_LIBRARIES, components)
if blocked:
print()
print('Blocked by dummy libraries')
all_blocked = set()
for group in blocked:
all_blocked |= group
for library in sorted(all_blocked - _DUMMY_LIBRARIES):
print(' - ' + library)
standalones = filter(
lambda s: len(s) == 1 and not s & _DUMMY_LIBRARIES, components)
if standalones:
print()
print('Standalone:')
for singleton in standalones:
print(' - ' + next(iter(singleton)))
groups = map(
lambda t: t[1],
sorted(
map(
lambda s: (len(s), s),
filter(
lambda g: len(g) > 1 and not g & _DUMMY_LIBRARIES,
components))))
if groups:
print()
print('Groups:')
for index, group in enumerate(groups):
print('[' + str(index) + ']')
for library in sorted(group):
print(' - ' + library)
return 0
if __name__ == '__main__':
sys.exit(main())