def main(): arg_parser = argparse.ArgumentParser( description='Given two JSON dependency graphs, output the differences ' 'between them.') required_arg_group = arg_parser.add_argument_group('required arguments') required_arg_group.add_argument( '-b', '--before', required=True, help='Path to the JSON file containing the "before" dependency graph. ' 'See the README on how to generate this file.') required_arg_group.add_argument( '-a', '--after', required=True, help='Path to the JSON file containing the "after" dependency graph.') arguments = arg_parser.parse_args() class_graph_before, package_graph_before, _ = \ serialization.load_class_and_package_graphs_from_file(arguments.before) class_graph_after, package_graph_after, _ = \ serialization.load_class_and_package_graphs_from_file(arguments.after) diff_num_graph_nodes(class_graph_before, class_graph_after, 'Total Java class count') diff_num_graph_nodes(package_graph_before, package_graph_after, 'Total Java package count') print() diff_node_list(class_graph_before, class_graph_after, 'Java classes') print() diff_node_list(package_graph_before, package_graph_after, 'Java packages')
def main(): arg_parser = argparse.ArgumentParser( description='Given a JSON dependency graph, output a JSON with a ' 'number of metrics to track progress of modularization.') required_arg_group = arg_parser.add_argument_group('required arguments') required_arg_group.add_argument( '-f', '--file', required=True, help='Path to the JSON file containing the dependency graph. ' 'See the README on how to generate this file.') arg_parser.add_argument( '-o', '--output', help='File to write the result json to. In not specified, outputs to ' 'stdout.') arguments = arg_parser.parse_args() class_graph, package_graph = \ serialization.load_class_and_package_graphs_from_file(arguments.file) stats = {} stats.update(_generate_graph_sizes(class_graph, package_graph)) stats.update(_generate_inbound_stats(class_graph, CLASSES_TO_COUNT_INBOUND)) stats.update(_generate_package_cycle_stats(package_graph)) if arguments.output: with open(arguments.output, 'w') as f: json.dump(stats, f, sort_keys=True) else: print(json.dumps(stats, sort_keys=True))
def main(): """Enumerates the cycles within a certain length in a graph.""" arg_parser = argparse.ArgumentParser( description='Given a JSON dependency graph, count the number of cycles ' 'in the package graph.') required_arg_group = arg_parser.add_argument_group('required arguments') required_arg_group.add_argument( '-f', '--file', required=True, help='Path to the JSON file containing the dependency graph. ' 'See the README on how to generate this file.') required_arg_group.add_argument( '-l', '--cycle-length', type=int, required=True, help='The maximum length of cycles to find, at most 5 or 6 to keep the ' 'script runtime low.') arg_parser.add_argument( '-o', '--output', type=argparse.FileType('w'), help='Path to the file to write the list of cycles to.') args = arg_parser.parse_args() _, package_graph, _ = serialization.load_class_and_package_graphs_from_file( args.file) all_cycles = find_cycles(package_graph, args.cycle_length) # There are no cycles of length 0 or 1 (since self-loops are disallowed). nonzero_cycles = all_cycles[2:] print(f'Found {sum(len(cycles) for cycles in nonzero_cycles)} cycles.') for cycle_length, cycles in enumerate(nonzero_cycles, 2): print(f'Found {len(cycles)} cycles of length {cycle_length}.') if args.output is not None: print(f'Dumping cycles to {args.output.name}.') with args.output as output_file: for cycle_length, cycles in enumerate(nonzero_cycles, 2): output_file.write(f'Cycles of length {cycle_length}:\n') cycle_texts = [] for cycle in cycles: cycle_texts.append(' > '.join(cycle_node.name for cycle_node in cycle)) output_file.write('\n'.join(sorted(cycle_texts))) output_file.write('\n')
def main(): """Prints package-level dependencies for an input package.""" arg_parser = argparse.ArgumentParser( description='Given a JSON dependency graph, output the package-level ' 'dependencies for a given package and the ' 'class dependencies comprising those dependencies') required_arg_group = arg_parser.add_argument_group('required arguments') required_arg_group.add_argument( '-f', '--file', required=True, help='Path to the JSON file containing the dependency graph. ' 'See the README on how to generate this file.') required_arg_group.add_argument( '-p', '--package', required=True, help='Case-insensitive name of the package to print dependencies for. ' 'Matches names of the form ...input, for example ' '`browser` matches `org.chromium.browser`.') optional_arg_group = arg_parser.add_argument_group('optional arguments') optional_arg_group.add_argument( '-s', '--ignore-subpackages', action='store_true', help='If present, this tool will ignore dependencies between the ' 'given package and subpackages. For example, if given ' 'browser.customtabs, it won\'t print a dependency between ' 'browser.customtabs and browser.customtabs.content.') arguments = arg_parser.parse_args() _, package_graph, _ = serialization.load_class_and_package_graphs_from_file( arguments.file) package_graph_keys = [node.name for node in package_graph.nodes] valid_keys = print_dependencies_helper.get_valid_package_keys_matching( package_graph_keys, arguments.package) if len(valid_keys) == 0: print(f'No package found by the name {arguments.package}.') elif len(valid_keys) > 1: print(f'Multiple valid keys found for the name {arguments.package}, ' 'please disambiguate between one of the following options:') for valid_key in valid_keys: print(f'\t{valid_key}') else: print(f'Printing package dependencies for {valid_keys[0]}:') print_package_dependencies_for_key(package_graph, valid_keys[0], arguments.ignore_subpackages)
def main(): """Prints class-level dependencies for one or more input classes.""" arg_parser = argparse.ArgumentParser( description='Given a JSON dependency graph, output ' 'the class-level dependencies for a given list of classes.') required_arg_group = arg_parser.add_argument_group('required arguments') required_arg_group.add_argument( '-f', '--file', required=True, help='Path to the JSON file containing the dependency graph. ' 'See the README on how to generate this file.') required_arg_group_either = arg_parser.add_argument_group( 'required arguments (at least one)') required_arg_group_either.add_argument( '-c', '--classes', dest='class_names', help='Case-sensitive name of the classes to print dependencies for. ' 'Matches either the simple class name without package or the fully ' 'qualified class name. For example, `AppHooks` matches ' '`org.chromium.browser.AppHooks`. Specify multiple classes with a ' 'comma-separated list, for example ' '`ChromeActivity,ChromeTabbedActivity`') required_arg_group_either.add_argument( '-p', '--packages', dest='package_names', help='Case-sensitive name of the packages to print dependencies for, ' 'such as `org.chromium.browser`. Specify multiple packages with a ' 'comma-separated list.`') direction_arg_group = arg_parser.add_mutually_exclusive_group() direction_arg_group.add_argument('--inbound', dest='inbound_only', action='store_true', help='Print inbound dependencies only.') direction_arg_group.add_argument('--outbound', dest='outbound_only', action='store_true', help='Print outbound dependencies only.') arg_parser.add_argument('--fully-qualified', action='store_true', help='Use fully qualified class names instead of ' 'shortened ones.') arg_parser.add_argument('--ignore-modularized', action='store_true', help='Do not print outbound dependencies on ' 'allowed (modules, components, base, etc.) ' 'dependencies.') arg_parser.add_argument('--ignore-audited-here', action='store_true', help='Do not print outbound dependencies on ' 'other classes being audited in this run.') arg_parser.add_argument('--ignore-same-package', action='store_true', help='Do not print outbound dependencies on ' 'classes in the same package.') arguments = arg_parser.parse_args() if not arguments.class_names and not arguments.package_names: raise ValueError('Either -c/--classes or -p/--packages need to be ' 'specified.') print_mode = PrintMode(inbound=not arguments.outbound_only, outbound=not arguments.inbound_only, ignore_modularized=arguments.ignore_modularized, ignore_audited_here=arguments.ignore_audited_here, ignore_same_package=arguments.ignore_same_package, fully_qualified=arguments.fully_qualified) class_graph, package_graph, _ = \ serialization.load_class_and_package_graphs_from_file(arguments.file) valid_class_names = [] if arguments.class_names: valid_class_names.extend( print_dependencies_helper.get_valid_classes_from_class_input( class_graph, arguments.class_names)) if arguments.package_names: valid_class_names.extend( print_dependencies_helper.get_valid_classes_from_package_input( package_graph, arguments.package_names)) target_dependencies = TargetDependencies() for i, fully_qualified_class_name in enumerate(valid_class_names): if i > 0: print() new_target_deps = print_class_dependencies_for_key( class_graph, fully_qualified_class_name, print_mode, set(valid_class_names)) target_dependencies.merge(new_target_deps) target_dependencies.print()
def main(): arg_parser = argparse.ArgumentParser( description='Given two JSON dependency graphs, output the differences ' 'between them. By default, outputs the differences in the sets of ' 'class and package nodes.') required_arg_group = arg_parser.add_argument_group('required arguments') required_arg_group.add_argument( '-b', '--before', required=True, help='Path to the JSON file containing the "before" dependency graph. ' 'See the README on how to generate this file.') required_arg_group.add_argument( '-a', '--after', required=True, help='Path to the JSON file containing the "after" dependency graph.') arg_parser.add_argument('-e', '--edges', action='store_true', help='Also diff the set of graph edges.') arg_parser.add_argument( '--package-cycles', type=int, help='Also diff the set of package cycles up to the specified size.') arguments = arg_parser.parse_args() class_graph_before, package_graph_before, _ = \ serialization.load_class_and_package_graphs_from_file(arguments.before) class_graph_after, package_graph_after, _ = \ serialization.load_class_and_package_graphs_from_file(arguments.after) _print_diff_num_nodes(class_graph_before, class_graph_after, 'Total Java class count') _print_diff_num_nodes(package_graph_before, package_graph_after, 'Total Java package count') print() _print_diff_node_list(class_graph_before, class_graph_after, 'Java classes') print() _print_diff_node_list(package_graph_before, package_graph_after, 'Java packages') if arguments.edges: print() _print_diff_num_edges(class_graph_before, class_graph_after, 'Total Java class edge count') _print_diff_num_edges(package_graph_before, package_graph_after, 'Total Java package edge count') print() _print_diff_edge_list(class_graph_before, class_graph_after, 'Java class edges') print() _print_diff_edge_list(package_graph_before, package_graph_after, 'Java package edges') if arguments.package_cycles: cycles_before = _cycle_set(package_graph_before, arguments.package_cycles) cycles_after = _cycle_set(package_graph_after, arguments.package_cycles) print() _print_diff_metric( len(cycles_before), len(cycles_after), 'Total Java package cycle count (up to size ' f'{arguments.package_cycles})') print() _print_diff_cycle_list( cycles_before, cycles_after, f'Java package cycles (up to size {arguments.package_cycles})')