Print dependency information, grouping components based on their position
    in the dependency graph.  Components with no dependnecies will be in layer
    0, components that only depend on layer 0 will be in layer 1, and so on.

    If there's a circular dependency, those nodes and their dependencies will
    be colored red.

    Arguments
    targets - the targets explicitly requested
    components - full configuration for all components in a project

def _print_layers(targets, components, tasks):
    """
    Print dependency information, grouping components based on their position
    in the dependency graph.  Components with no dependnecies will be in layer
    0, components that only depend on layer 0 will be in layer 1, and so on.

    If there's a circular dependency, those nodes and their dependencies will
    be colored red.

    Arguments
    targets - the targets explicitly requested
    components - full configuration for all components in a project
    """
    layer = 0
    expected_count = len(tasks)
    counts = {}

    def _add_layer(resolved, dep_fn):
        nonlocal layer
        nonlocal counts
        nonlocal expected_count

        really_resolved = []
        for resolved_task in resolved:
            resolved_component_tasks = counts.get(resolved_task[0], [])
            resolved_component_tasks.append(resolved_task)
            if len(resolved_component_tasks) == expected_count:
                really_resolved.extend(resolved_component_tasks)
                del counts[resolved_task[0]]
            else:
                counts[resolved_task[0]] = resolved_component_tasks

        if really_resolved:
            indentation = " " * 4
            print("{}subgraph cluster_{} {{".format(indentation, layer))
            print('{}label="Layer {}"'.format(indentation * 2, layer))
            dep_fn(indentation * 2, really_resolved)
            print("{}}}".format(indentation))
            layer += 1

    _do_dot(targets, components, tasks, _add_layer)