+++ /dev/null
-#
-# Copyright 2012-2015 Bronto Software, Inc. and contributors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-from __future__ import print_function, unicode_literals
-
-try:
- import cPickle as pickle
-except ImportError:
- import pickle
-
-import hashlib
-import logging
-import sys
-import os
-import os.path
-
-from optparse import OptionParser
-
-import javalang
-
-import javasphinx.compiler as compiler
-import javasphinx.util as util
-
-def encode_output(s):
- if isinstance(s, str):
- return s
- return s.encode('utf-8')
-
-def find_source_files(input_path, excludes):
- """ Get a list of filenames for all Java source files within the given
- directory.
-
- """
-
- java_files = []
-
- input_path = os.path.normpath(os.path.abspath(input_path))
-
- for dirpath, dirnames, filenames in os.walk(input_path):
- if is_excluded(dirpath, excludes):
- del dirnames[:]
- continue
-
- for filename in filenames:
- if filename.endswith(".java"):
- java_files.append(os.path.join(dirpath, filename))
-
- return java_files
-
-def write_toc(packages, opts):
- doc = util.Document()
- doc.add_heading(opts.toc_title, '=')
-
- toc = util.Directive('toctree')
- toc.add_option('maxdepth', '2')
- doc.add_object(toc)
-
- for package in sorted(packages.keys()):
- toc.add_content("%s/package-index\n" % package.replace('.', '/'))
-
- filename = 'packages.' + opts.suffix
- fullpath = os.path.join(opts.destdir, filename)
-
- if os.path.exists(fullpath) and not (opts.force or opts.update):
- sys.stderr.write(fullpath + ' already exists. Use -f to overwrite.\n')
- sys.exit(1)
-
- f = open(fullpath, 'w')
- f.write(encode_output(doc.build()))
- f.close()
-
-def write_documents(packages, documents, sources, opts):
- package_contents = dict()
-
- # Write individual documents
- for fullname, (package, name, document) in documents.items():
- if is_package_info_doc(name):
- continue
-
- package_path = package.replace('.', os.sep)
- filebasename = name.replace('.', '-')
- filename = filebasename + '.' + opts.suffix
- dirpath = os.path.join(opts.destdir, package_path)
- fullpath = os.path.join(dirpath, filename)
-
- if not os.path.exists(dirpath):
- os.makedirs(dirpath)
- elif os.path.exists(fullpath) and not (opts.force or opts.update):
- sys.stderr.write(fullpath + ' already exists. Use -f to overwrite.\n')
- sys.exit(1)
-
- # Add to package indexes
- package_contents.setdefault(package, list()).append(filebasename)
-
- if opts.update and os.path.exists(fullpath):
- # If the destination file is newer than the source file than skip
- # writing it out
- source_mod_time = os.stat(sources[fullname]).st_mtime
- dest_mod_time = os.stat(fullpath).st_mtime
-
- if source_mod_time < dest_mod_time:
- continue
-
- f = open(fullpath, 'w')
- f.write(encode_output(document))
- f.close()
-
- # Write package-index for each package
- for package, classes in package_contents.items():
- doc = util.Document()
- doc.add_heading(package, '=')
-
- #Adds the package documentation (if any)
- if packages[package] != '':
- documentation = packages[package]
- doc.add_line("\n%s" % documentation)
-
- doc.add_object(util.Directive('java:package', package))
-
- toc = util.Directive('toctree')
- toc.add_option('maxdepth', '1')
-
- classes.sort()
- for filebasename in classes:
- toc.add_content(filebasename + '\n')
- doc.add_object(toc)
-
- package_path = package.replace('.', os.sep)
- filename = 'package-index.' + opts.suffix
- dirpath = os.path.join(opts.destdir, package_path)
- fullpath = os.path.join(dirpath, filename)
-
- if not os.path.exists(dirpath):
- os.makedirs(dirpath)
- elif os.path.exists(fullpath) and not (opts.force or opts.update):
- sys.stderr.write(fullpath + ' already exists. Use -f to overwrite.\n')
- sys.exit(1)
-
- f = open(fullpath, 'w')
- f.write(encode_output(doc.build()))
- f.close()
-
-def get_newer(a, b):
- if not os.path.exists(a):
- return b
-
- if not os.path.exists(b):
- return a
-
- a_mtime = int(os.stat(a).st_mtime)
- b_mtime = int(os.stat(b).st_mtime)
-
- if a_mtime < b_mtime:
- return b
-
- return a
-
-def format_syntax_error(e):
- rest = ""
- if e.at.position:
- value = e.at.value
- pos = e.at.position
- rest = ' at %s line %d, character %d' % (value, pos[0], pos[1])
- return e.description + rest
-
-def generate_from_source_file(doc_compiler, source_file, cache_dir):
- if cache_dir:
- fingerprint = hashlib.md5(source_file.encode()).hexdigest()
- cache_file = os.path.join(cache_dir, 'parsed-' + fingerprint + '.p')
-
- if get_newer(source_file, cache_file) == cache_file:
- return pickle.load(open(cache_file, 'rb'))
- else:
- cache_file = None
-
- f = open(source_file)
- source = f.read()
- f.close()
-
- try:
- ast = javalang.parse.parse(source)
- except javalang.parser.JavaSyntaxError as e:
- util.error('Syntax error in %s: %s', source_file, format_syntax_error(e))
- except Exception:
- util.unexpected('Unexpected exception while parsing %s', source_file)
-
- documents = {}
- try:
- if source_file.endswith("package-info.java"):
- if ast.package is not None:
- documentation = doc_compiler.compile_docblock(ast.package)
- documents[ast.package.name] = (ast.package.name, 'package-info', documentation)
- else:
- documents = doc_compiler.compile(ast)
- except Exception:
- util.unexpected('Unexpected exception while compiling %s', source_file)
-
- if cache_file:
- dump_file = open(cache_file, 'wb')
- pickle.dump(documents, dump_file)
- dump_file.close()
-
- return documents
-
-def generate_documents(source_files, cache_dir, verbose, member_headers, parser):
- documents = {}
- sources = {}
- doc_compiler = compiler.JavadocRestCompiler(None, member_headers, parser)
-
- for source_file in source_files:
- if verbose:
- print('Processing', source_file)
-
- this_file_documents = generate_from_source_file(doc_compiler, source_file, cache_dir)
- for fullname in this_file_documents:
- sources[fullname] = source_file
-
- documents.update(this_file_documents)
-
- #Existing packages dict, where each key is a package name
- #and each value is the package documentation (if any)
- packages = {}
-
- #Gets the name of the package where the document was declared
- #and adds it to the packages dict with no documentation.
- #Package documentation, if any, will be collected from package-info.java files.
- for package, name, _ in documents.values():
- packages[package] = ""
-
- #Gets packages documentation from package-info.java documents (if any).
- for package, name, content in documents.values():
- if is_package_info_doc(name):
- packages[package] = content
-
- return packages, documents, sources
-
-def normalize_excludes(rootpath, excludes):
- f_excludes = []
- for exclude in excludes:
- if not os.path.isabs(exclude) and not exclude.startswith(rootpath):
- exclude = os.path.join(rootpath, exclude)
- f_excludes.append(os.path.normpath(exclude) + os.path.sep)
- return f_excludes
-
-def is_excluded(root, excludes):
- sep = os.path.sep
- if not root.endswith(sep):
- root += sep
- for exclude in excludes:
- if root.startswith(exclude):
- return True
- return False
-
-def is_package_info_doc(document_name):
- ''' Checks if the name of a document represents a package-info.java file. '''
- return document_name == 'package-info'
-
-
-def main(argv=sys.argv):
- logging.basicConfig(level=logging.WARN)
-
- parser = OptionParser(
- usage="""\
-usage: %prog [options] -o <output_path> <input_path> [exclude_paths, ...]
-
-Look recursively in <input_path> for Java sources files and create reST files
-for all non-private classes, organized by package under <output_path>. A package
-index (package-index.<ext>) will be created for each package, and a top level
-table of contents will be generated named packages.<ext>.
-
-Paths matching any of the given exclude_paths (interpreted as regular
-expressions) will be skipped.
-
-Note: By default this script will not overwrite already created files.""")
-
- parser.add_option('-o', '--output-dir', action='store', dest='destdir',
- help='Directory to place all output', default='')
- parser.add_option('-f', '--force', action='store_true', dest='force',
- help='Overwrite all files')
- parser.add_option('-c', '--cache-dir', action='store', dest='cache_dir',
- help='Directory to stored cachable output')
- parser.add_option('-u', '--update', action='store_true', dest='update',
- help='Overwrite new and changed files', default=False)
- parser.add_option('-T', '--no-toc', action='store_true', dest='notoc',
- help='Don\'t create a table of contents file')
- parser.add_option('-t', '--title', dest='toc_title', default='Javadoc',
- help='Title to use on table of contents')
- parser.add_option('--no-member-headers', action='store_false', default=True, dest='member_headers',
- help='Don\'t generate headers for class members')
- parser.add_option('-s', '--suffix', action='store', dest='suffix',
- help='file suffix (default: rst)', default='rst')
- parser.add_option('-I', '--include', action='append', dest='includes',
- help='Additional input paths to scan', default=[])
- parser.add_option('-p', '--parser', dest='parser_lib', default='lxml',
- help='Beautiful Soup---html parser library option.')
- parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
- help='verbose output')
-
- (opts, args) = parser.parse_args(argv[1:])
-
- if not args:
- parser.error('A source path is required.')
-
- rootpath, excludes = args[0], args[1:]
-
- input_paths = opts.includes
- input_paths.append(rootpath)
-
- if not opts.destdir:
- parser.error('An output directory is required.')
-
- if opts.suffix.startswith('.'):
- opts.suffix = opts.suffix[1:]
-
- for input_path in input_paths:
- if not os.path.isdir(input_path):
- sys.stderr.write('%s is not a directory.\n' % (input_path,))
- sys.exit(1)
-
- if not os.path.isdir(opts.destdir):
- os.makedirs(opts.destdir)
-
- if opts.cache_dir and not os.path.isdir(opts.cache_dir):
- os.makedirs(opts.cache_dir)
-
- excludes = normalize_excludes(rootpath, excludes)
- source_files = []
-
- for input_path in input_paths:
- source_files.extend(find_source_files(input_path, excludes))
-
- packages, documents, sources = generate_documents(source_files, opts.cache_dir, opts.verbose,
- opts.member_headers, opts.parser_lib)
-
- write_documents(packages, documents, sources, opts)
-
- if not opts.notoc:
- write_toc(packages, opts)