Exemplo n.º 1
0
def setup():
    """
    Setup fixtures
    """
    log = logging.get_logger()
    log.info("Generating test project fixture...")
    
    test_project = os.path.join(os.path.dirname(__file__), "fixtures", "test_project")
    from miki import structure
    docs_directory = structure.get_docs_directory(test_project)
    build_directory = structure.get_build_directory(docs_directory)
    
    if os.path.exists(build_directory):
        log.warn("Clearing existing build directory at %s" % build_directory)
        shutil.rmtree(build_directory)
    structure.create(test_project, force=True)
Exemplo n.º 2
0
def setup():
    """
    Setup fixtures
    """
    log = logging.get_logger()
    log.info("Generating test project fixture...")

    test_project = os.path.join(os.path.dirname(__file__), "fixtures",
                                "test_project")
    from miki import structure
    docs_directory = structure.get_docs_directory(test_project)
    build_directory = structure.get_build_directory(docs_directory)

    if os.path.exists(build_directory):
        log.warn("Clearing existing build directory at %s" % build_directory)
        shutil.rmtree(build_directory)
    structure.create(test_project, force=True)
Exemplo n.º 3
0
def main(arguments=None):
    """
    Miki main entry point.
    
    :param arguments: if specified denotes list of arguments to use, else retrieves argument list from sys.argv
    
    """
    from rodin import logging
    from rodin.parsers.argument import ArgumentParser
    
    from miki import builder, structure, autodoc, errors
    
    if arguments is None:
        arguments = sys.argv[1:]
    
    log = logging.get_logger()
    return_code = 0
    
    # handle arguments
    parser = ArgumentParser(description="Documentation Assistant")
    
    common = ArgumentParser(add_help=False)
    common.add_argument("-p", "--project-directory", help="Project directory. ", default=os.path.abspath("."))
    common.add_argument("-s", "--source-directory", help="Source directory. Can be relative to project directory, default is 'sources'.", action="append")
    common.add_argument("-l", "--loud", help="Output more information during operations.", default=False, action="store_true")
    
    subparsers = parser.add_subparsers(dest='mode', help="Use mode -h/--help for mode specific help")
        
    build_parser = subparsers.add_parser("build", help="Build documentation output.", parents=[common])
    build_parser.add_argument("-n", "--project-name", help="Project name.", default=None)
    build_parser.add_argument("-r", "--project-release", help="Project release string, such as 2.0.3.", default=None)
    build_parser.add_argument("-v", "--project-version", help="Project version string, such as 2.0.", default=None)
    
    build_parser.add_argument("-d", "--doxyfile", help="Location of doxyfile. Can be relative to project directory.", default="Doxyfile")
    build_parser.add_argument("-c", "--clean", help="Remove existing target output before build.", default=False, action="store_true")
    build_parser.add_argument("-t", "--target", action="append", choices=builder.VALID_TARGETS, help="A target to build. Can be specified multiple times.")
    
    structure_parser = subparsers.add_parser("structure", help="Manage documentation structure.", parents=[common])
    structure_group = structure_parser.add_mutually_exclusive_group(required=True)
    structure_group.add_argument("-c", "--create", help="Create documentation structure from template.", default=False, action="store_true")
    structure_group.add_argument("-u", "--update", help="Update existing structure to match template. Will overwrite, but not remove existing files.", default=False, action="store_true")
    structure_group.add_argument("-v", "--validate", help="Validate existing structure.", default=False, action="store_true")
    
    create_parser = subparsers.add_parser("autodoc", help="Automatically generate reference files for all sources.", parents=[common])
    create_parser.add_argument("-d", "--output-directory", help="Directory to output generated files in. Can be relative to docs directory.", default='technical')
    create_parser.add_argument("-u", "--update", help="Overwrite existing files.", default=False, action="store_true")
    
    args = parser.parse_args(arguments)

    # sort out common arguments
    # are we in docs directory by accident
    if os.path.basename(args.project_directory) ==  "docs":
        log.warn("Project directory appears to be a documentation directory - switching up a level.")
        args.project_directory = os.path.dirname(args.project_directory)

    if not args.source_directory:
        args.source_directory = ["sources"]
    
    log.info("Project directory is: %s" % args.project_directory)
    docs_directory = structure.get_docs_directory(args.project_directory)

    # now specialise
    if args.mode == "structure":
        if args.create:
            structure.generate(args.project_directory, update=False)
        elif args.update:
            structure.generate(args.project_directory, update=True)
        elif args.validate:
            structure.validate(args.project_directory)
            
    elif args.mode == "autodoc":
        output_directory_root = os.path.join(docs_directory, args.output_directory)
        for source_directory in args.source_directory:
            source_directory = os.path.join(args.project_directory, source_directory)
            output_directory = output_directory_root
            if len(args.source_directory) > 1:
                output_directory = os.path.join(output_directory, os.path.dirname(source_directory))
            autodoc.generate(output_directory, source_directory, update=args.update)
    
    elif args.mode == "build":
        
        if not os.path.isdir(docs_directory):
            log.error("Docs directory does not exist - cannot build documentation! (Expected location: %s)" % docs_directory)
            return 1
                
        defaults = {}
        
        # guess project name if not set
        if args.project_name:
            defaults["project"] = args.project_name
        else:
            defaults["project"] = os.path.basename(args.project_directory)
            # Check for release build which has project name one level higher with an intermediate release directory
            match = re.search("v\d\d_\d\d_\d\d", defaults["project"])
            if match:
                defaults["project"] = os.path.basename(os.path.dirname(args.project_directory))

        log.info("Project name is: %s" % defaults["project"])
            
        if args.project_release:
            defaults["release"] = args.project_release
        else:
            # guess
            release_folder = os.path.basename(args.project_directory)
            match = re.search("v\d\d_\d\d_\d\d", release_folder)
            if match:
                defaults["release"] = release_folder[1:]
            
        if args.project_version:
            defaults["version"] = args.project_version
            
        # auto add discovered Python packages to PYTHONPATH
        defaults["modindex_common_prefix"] = []
        for source_directory in args.source_directory:
            source_directory = os.path.join(args.project_directory, source_directory)
                
            if not os.path.isdir(source_directory):
                log.warn("Source path is not a valid directory: %s" % source_directory )
            else:
                log.info("Scanning source directory: %s" % source_directory)
                python_package_paths = []
                doxyfile = None
                for path, dirs, files in os.walk(source_directory, topdown=True):
                    if "__init__.py" in files:
                        python_package_paths.append(path)
                        # don't descend any further
                        del dirs[:]
                            
                log.info("Looking for Python packages...")
                python_package_paths.sort()
                for path in python_package_paths:
                    path, entry = os.path.split(path)
                    defaults["modindex_common_prefix"].append("%s." % entry)
                    if not path in sys.path:
                        sys.path.insert(0, path)
                        log.info("Added source path to PYTHONPATH: %s" % path)
        
        for entry in defaults["modindex_common_prefix"]:
            log.info("Added package name '%s' to common prefixes." % entry)
        
        # Check for doxyfile
        if not "/" in args.doxyfile:
            args.doxyfile = os.path.join(args.project_directory, args.doxyfile)
        args.doxyfile = os.path.abspath(args.doxyfile)
        if os.path.isfile(args.doxyfile):
            log.info("Found Doxyfile: %s" % args.doxyfile)
        else:
            args.doxyfile = None
            
        # set default target to html
        if not args.target:
            args.target = ["html"]
              
        # Build docs
        build_directory = structure.get_build_directory(docs_directory)
        
        try:
            builder.build(docs_directory, 
                          build_directory, 
                          defaults=defaults, 
                          targets=args.target,
                          verbose=args.loud,
                          doxyfile=args.doxyfile,
                          clean=args.clean)
        except errors.BuilderError, error:
            log.error("Build failed: %s" % error)
            return_code = 1
        except Exception, error:
            log.exception("Build failed")
            return_code = 1
Exemplo n.º 4
0
def main(arguments=None):
    """
    Miki main entry point.
    
    :param arguments: if specified denotes list of arguments to use, else retrieves argument list from sys.argv
    
    """
    from rodin import logging
    from rodin.parsers.argument import ArgumentParser

    from miki import builder, structure, autodoc, errors

    if arguments is None:
        arguments = sys.argv[1:]

    log = logging.get_logger()
    return_code = 0

    # handle arguments
    parser = ArgumentParser(description="Documentation Assistant")

    common = ArgumentParser(add_help=False)
    common.add_argument("-p",
                        "--project-directory",
                        help="Project directory. ",
                        default=os.path.abspath("."))
    common.add_argument(
        "-s",
        "--source-directory",
        help=
        "Source directory. Can be relative to project directory, default is 'sources'.",
        action="append")
    common.add_argument("-l",
                        "--loud",
                        help="Output more information during operations.",
                        default=False,
                        action="store_true")

    subparsers = parser.add_subparsers(
        dest='mode', help="Use mode -h/--help for mode specific help")

    build_parser = subparsers.add_parser("build",
                                         help="Build documentation output.",
                                         parents=[common])
    build_parser.add_argument("-n",
                              "--project-name",
                              help="Project name.",
                              default=None)
    build_parser.add_argument("-r",
                              "--project-release",
                              help="Project release string, such as 2.0.3.",
                              default=None)
    build_parser.add_argument("-v",
                              "--project-version",
                              help="Project version string, such as 2.0.",
                              default=None)

    build_parser.add_argument(
        "-d",
        "--doxyfile",
        help="Location of doxyfile. Can be relative to project directory.",
        default="Doxyfile")
    build_parser.add_argument(
        "-c",
        "--clean",
        help="Remove existing target output before build.",
        default=False,
        action="store_true")
    build_parser.add_argument(
        "-t",
        "--target",
        action="append",
        choices=builder.VALID_TARGETS,
        help="A target to build. Can be specified multiple times.")

    structure_parser = subparsers.add_parser(
        "structure", help="Manage documentation structure.", parents=[common])
    structure_group = structure_parser.add_mutually_exclusive_group(
        required=True)
    structure_group.add_argument(
        "-c",
        "--create",
        help="Create documentation structure from template.",
        default=False,
        action="store_true")
    structure_group.add_argument(
        "-u",
        "--update",
        help=
        "Update existing structure to match template. Will overwrite, but not remove existing files.",
        default=False,
        action="store_true")
    structure_group.add_argument("-v",
                                 "--validate",
                                 help="Validate existing structure.",
                                 default=False,
                                 action="store_true")

    create_parser = subparsers.add_parser(
        "autodoc",
        help="Automatically generate reference files for all sources.",
        parents=[common])
    create_parser.add_argument(
        "-d",
        "--output-directory",
        help=
        "Directory to output generated files in. Can be relative to docs directory.",
        default='technical')
    create_parser.add_argument("-u",
                               "--update",
                               help="Overwrite existing files.",
                               default=False,
                               action="store_true")

    args = parser.parse_args(arguments)

    # sort out common arguments
    # are we in docs directory by accident
    if os.path.basename(args.project_directory) == "docs":
        log.warn(
            "Project directory appears to be a documentation directory - switching up a level."
        )
        args.project_directory = os.path.dirname(args.project_directory)

    if not args.source_directory:
        args.source_directory = ["sources"]

    log.info("Project directory is: %s" % args.project_directory)
    docs_directory = structure.get_docs_directory(args.project_directory)

    # now specialise
    if args.mode == "structure":
        if args.create:
            structure.generate(args.project_directory, update=False)
        elif args.update:
            structure.generate(args.project_directory, update=True)
        elif args.validate:
            structure.validate(args.project_directory)

    elif args.mode == "autodoc":
        output_directory_root = os.path.join(docs_directory,
                                             args.output_directory)
        for source_directory in args.source_directory:
            source_directory = os.path.join(args.project_directory,
                                            source_directory)
            output_directory = output_directory_root
            if len(args.source_directory) > 1:
                output_directory = os.path.join(
                    output_directory, os.path.dirname(source_directory))
            autodoc.generate(output_directory,
                             source_directory,
                             update=args.update)

    elif args.mode == "build":

        if not os.path.isdir(docs_directory):
            log.error(
                "Docs directory does not exist - cannot build documentation! (Expected location: %s)"
                % docs_directory)
            return 1

        defaults = {}

        # guess project name if not set
        if args.project_name:
            defaults["project"] = args.project_name
        else:
            defaults["project"] = os.path.basename(args.project_directory)
            # Check for release build which has project name one level higher with an intermediate release directory
            match = re.search("v\d\d_\d\d_\d\d", defaults["project"])
            if match:
                defaults["project"] = os.path.basename(
                    os.path.dirname(args.project_directory))

        log.info("Project name is: %s" % defaults["project"])

        if args.project_release:
            defaults["release"] = args.project_release
        else:
            # guess
            release_folder = os.path.basename(args.project_directory)
            match = re.search("v\d\d_\d\d_\d\d", release_folder)
            if match:
                defaults["release"] = release_folder[1:]

        if args.project_version:
            defaults["version"] = args.project_version

        # auto add discovered Python packages to PYTHONPATH
        defaults["modindex_common_prefix"] = []
        for source_directory in args.source_directory:
            source_directory = os.path.join(args.project_directory,
                                            source_directory)

            if not os.path.isdir(source_directory):
                log.warn("Source path is not a valid directory: %s" %
                         source_directory)
            else:
                log.info("Scanning source directory: %s" % source_directory)
                python_package_paths = []
                doxyfile = None
                for path, dirs, files in os.walk(source_directory,
                                                 topdown=True):
                    if "__init__.py" in files:
                        python_package_paths.append(path)
                        # don't descend any further
                        del dirs[:]

                log.info("Looking for Python packages...")
                python_package_paths.sort()
                for path in python_package_paths:
                    path, entry = os.path.split(path)
                    defaults["modindex_common_prefix"].append("%s." % entry)
                    if not path in sys.path:
                        sys.path.insert(0, path)
                        log.info("Added source path to PYTHONPATH: %s" % path)

        for entry in defaults["modindex_common_prefix"]:
            log.info("Added package name '%s' to common prefixes." % entry)

        # Check for doxyfile
        if not "/" in args.doxyfile:
            args.doxyfile = os.path.join(args.project_directory, args.doxyfile)
        args.doxyfile = os.path.abspath(args.doxyfile)
        if os.path.isfile(args.doxyfile):
            log.info("Found Doxyfile: %s" % args.doxyfile)
        else:
            args.doxyfile = None

        # set default target to html
        if not args.target:
            args.target = ["html"]

        # Build docs
        build_directory = structure.get_build_directory(docs_directory)

        try:
            builder.build(docs_directory,
                          build_directory,
                          defaults=defaults,
                          targets=args.target,
                          verbose=args.loud,
                          doxyfile=args.doxyfile,
                          clean=args.clean)
        except errors.BuilderError, error:
            log.error("Build failed: %s" % error)
            return_code = 1
        except Exception, error:
            log.exception("Build failed")
            return_code = 1