def render_recursive_template(template_folder, info, out_folder, preserve=None, dry_run=False):
    """Copy a directory tree rendering all templates found within.

    This function inspects all of the files in template_folder recursively. If
    any file ends .tpl, it is rendered using render_template and the .tpl
    suffix is removed.  All other files are copied without modification.

    out_folder is not cleaned before rendering so you must delete its contents
    yourself if you want that behavior.

    If you just want to see all of the file paths that would be generated,
    call with dry_run=True.  This will not render anything but just inspect
    what would be generated.

    Args:
        template_folder (str): A relative path from config/templates with the
            folder that should be rendered recursively.
        info (dict): A dictionary of variables to be substituted into any
            templates found in template_folder.
        out_folder (str): The path to the output folder where the template will
            be generated.
        dry_run (bool): Whether to actually render output files or just return
            the files that would be generated.
        preserve (list of str): A list of file names relative to the start of the
            template folder that we are rendering that end in .tpl but should not
            be rendered and should not have their .tpl suffix removed.  This allows
            you to partially render a template so that you can render a specific
            file later.

    Returns:
        dict, list: The dict is map of output file path (relative to
            out_folder) to the absolute path of the input file that it depends
            on. This result is suitable for using in a dependency graph like
            SCons. The list is a list of all of the directories that would need
            to be created to hold these files (not including out_folder).
    """

    if isinstance(preserve, str):
        raise ArgumentError("You must pass a list of strings to preserve, not a string", preserve=preserve)

    if preserve is None:
        preserve = []

    preserve = set(preserve)

    template_dir = resource_path('templates', expect='folder')
    indir = os.path.join(template_dir, template_folder)

    if not os.path.exists(indir):
        raise ArgumentError("Input template folder for recursive template not found",
                            template_folder=template_folder, absolute_path=indir)
    elif not os.path.isdir(indir):
        raise ArgumentError("Input template folder is not a directory",
                            template_folder=template_folder, absolute_path=indir)

    create_dirs = []
    file_map = {}

    # Walk over all input files
    for dirpath, dirs, files in os.walk(indir):
        for file in files:
            in_abspath = os.path.abspath(os.path.join(dirpath, file))
            in_path = os.path.relpath(os.path.join(dirpath, file), start=indir)

            if file.endswith(".tpl") and not in_path in preserve:
                out_path = in_path[:-4]
            else:
                out_path = in_path

            file_map[out_path] = (in_path, in_abspath)

            for folder in dirs:
                dir_path = os.path.relpath(os.path.join(dirpath, folder), start=indir)
                create_dirs.append(dir_path)

    # Actually render / copy all files if we are not doing a dry run
    if not dry_run:
        for folder in create_dirs:
            out_path = os.path.join(out_folder, folder)
            if not os.path.isdir(out_path):
                os.makedirs(out_path)

        for out_rel, (in_path, in_abspath) in file_map.items():
            out_path = os.path.join(out_folder, out_rel)
            if in_path in preserve or not in_path.endswith(".tpl"):
                shutil.copyfile(in_abspath, out_path)
            else:
                # jinja needs to have unix path separators regardless of the platform and a relative path
                # from the templates base directory
                in_template_path = os.path.join(template_folder, in_path).replace(os.path.sep, '/')
                render_template(in_template_path, info, out_path=out_path)

    return file_map, create_dirs
Copy a directory tree rendering all templates found within.

    This function inspects all of the files in template_folder recursively. If
    any file ends .tpl, it is rendered using render_template and the .tpl
    suffix is removed.  All other files are copied without modification.

    out_folder is not cleaned before rendering so you must delete its contents
    yourself if you want that behavior.

    If you just want to see all of the file paths that would be generated,
    call with dry_run=True.  This will not render anything but just inspect
    what would be generated.

    Args:
        template_folder (str): A relative path from config/templates with the
            folder that should be rendered recursively.
        info (dict): A dictionary of variables to be substituted into any
            templates found in template_folder.
        out_folder (str): The path to the output folder where the template will
            be generated.
        dry_run (bool): Whether to actually render output files or just return
            the files that would be generated.
        preserve (list of str): A list of file names relative to the start of the
            template folder that we are rendering that end in .tpl but should not
            be rendered and should not have their .tpl suffix removed.  This allows
            you to partially render a template so that you can render a specific
            file later.

    Returns:
        dict, list: The dict is map of output file path (relative to
            out_folder) to the absolute path of the input file that it depends
            on. This result is suitable for using in a dependency graph like
            SCons. The list is a list of all of the directories that would need
            to be created to hold these files (not including out_folder).