예제 #1
0
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')
예제 #2
0
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))
예제 #3
0
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()
예제 #6
0
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})')