mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
289 lines
11 KiB
Python
289 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
sphinx.cmdline
|
|
~~~~~~~~~~~~~~
|
|
|
|
sphinx-build command-line handling.
|
|
|
|
:copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
|
|
:license: BSD, see LICENSE for details.
|
|
"""
|
|
from __future__ import print_function
|
|
|
|
import os
|
|
import sys
|
|
import optparse
|
|
import traceback
|
|
from os import path
|
|
|
|
from six import text_type, binary_type
|
|
from docutils.utils import SystemMessage
|
|
|
|
from sphinx import __version__
|
|
from sphinx.errors import SphinxError
|
|
from sphinx.application import Sphinx
|
|
from sphinx.util import Tee, format_exception_cut_frames, save_traceback
|
|
from sphinx.util.console import red, nocolor, color_terminal
|
|
from sphinx.util.osutil import abspath, fs_encoding
|
|
from sphinx.util.pycompat import terminal_safe
|
|
|
|
|
|
def usage(argv, msg=None):
|
|
if msg:
|
|
print(msg, file=sys.stderr)
|
|
print(file=sys.stderr)
|
|
|
|
USAGE = """\
|
|
Sphinx v%s
|
|
Usage: %%prog [options] sourcedir outdir [filenames...]
|
|
|
|
Filename arguments:
|
|
without -a and without filenames, write new and changed files.
|
|
with -a, write all files.
|
|
with filenames, write these.
|
|
""" % __version__
|
|
|
|
EPILOG = """\
|
|
For more information, visit <http://sphinx-doc.org/>.
|
|
"""
|
|
|
|
|
|
class MyFormatter(optparse.IndentedHelpFormatter):
|
|
def format_usage(self, usage):
|
|
return usage
|
|
|
|
def format_help(self, formatter):
|
|
result = []
|
|
if self.description:
|
|
result.append(self.format_description(formatter))
|
|
if self.option_list:
|
|
result.append(self.format_option_help(formatter))
|
|
return "\n".join(result)
|
|
|
|
|
|
def main(argv):
|
|
if not color_terminal():
|
|
nocolor()
|
|
|
|
parser = optparse.OptionParser(USAGE, epilog=EPILOG, formatter=MyFormatter())
|
|
parser.add_option('--version', action='store_true', dest='version',
|
|
help='show version information and exit')
|
|
|
|
group = parser.add_option_group('General options')
|
|
group.add_option('-b', metavar='BUILDER', dest='builder', default='html',
|
|
help='builder to use; default is html')
|
|
group.add_option('-a', action='store_true', dest='force_all',
|
|
help='write all files; default is to only write new and '
|
|
'changed files')
|
|
group.add_option('-E', action='store_true', dest='freshenv',
|
|
help='don\'t use a saved environment, always read '
|
|
'all files')
|
|
group.add_option('-d', metavar='PATH', default=None, dest='doctreedir',
|
|
help='path for the cached environment and doctree files '
|
|
'(default: outdir/.doctrees)')
|
|
group.add_option('-j', metavar='N', default=1, type='int', dest='jobs',
|
|
help='build in parallel with N processes where possible')
|
|
# this option never gets through to this point (it is intercepted earlier)
|
|
# group.add_option('-M', metavar='BUILDER', dest='make_mode',
|
|
# help='"make" mode -- as used by Makefile, like '
|
|
# '"sphinx-build -M html"')
|
|
|
|
group = parser.add_option_group('Build configuration options')
|
|
group.add_option('-c', metavar='PATH', dest='confdir',
|
|
help='path where configuration file (conf.py) is located '
|
|
'(default: same as sourcedir)')
|
|
group.add_option('-C', action='store_true', dest='noconfig',
|
|
help='use no config file at all, only -D options')
|
|
group.add_option('-D', metavar='setting=value', action='append',
|
|
dest='define', default=[],
|
|
help='override a setting in configuration file')
|
|
group.add_option('-A', metavar='name=value', action='append',
|
|
dest='htmldefine', default=[],
|
|
help='pass a value into HTML templates')
|
|
group.add_option('-t', metavar='TAG', action='append',
|
|
dest='tags', default=[],
|
|
help='define tag: include "only" blocks with TAG')
|
|
group.add_option('-n', action='store_true', dest='nitpicky',
|
|
help='nit-picky mode, warn about all missing references')
|
|
|
|
group = parser.add_option_group('Console output options')
|
|
group.add_option('-v', action='count', dest='verbosity', default=0,
|
|
help='increase verbosity (can be repeated)')
|
|
group.add_option('-q', action='store_true', dest='quiet',
|
|
help='no output on stdout, just warnings on stderr')
|
|
group.add_option('-Q', action='store_true', dest='really_quiet',
|
|
help='no output at all, not even warnings')
|
|
group.add_option('-N', action='store_true', dest='nocolor',
|
|
help='do not emit colored output')
|
|
group.add_option('-w', metavar='FILE', dest='warnfile',
|
|
help='write warnings (and errors) to given file')
|
|
group.add_option('-W', action='store_true', dest='warningiserror',
|
|
help='turn warnings into errors')
|
|
group.add_option('-T', action='store_true', dest='traceback',
|
|
help='show full traceback on exception')
|
|
group.add_option('-P', action='store_true', dest='pdb',
|
|
help='run Pdb on exception')
|
|
|
|
# parse options
|
|
try:
|
|
opts, args = parser.parse_args(argv[1:])
|
|
except SystemExit as err:
|
|
return err.code
|
|
|
|
# handle basic options
|
|
if opts.version:
|
|
print('Sphinx (sphinx-build) %s' % __version__)
|
|
return 0
|
|
|
|
# get paths (first and second positional argument)
|
|
try:
|
|
srcdir = abspath(args[0])
|
|
confdir = abspath(opts.confdir or srcdir)
|
|
if opts.noconfig:
|
|
confdir = None
|
|
if not path.isdir(srcdir):
|
|
print('Error: Cannot find source directory `%s\'.' % srcdir,
|
|
file=sys.stderr)
|
|
return 1
|
|
if not opts.noconfig and not path.isfile(path.join(confdir, 'conf.py')):
|
|
print('Error: Config directory doesn\'t contain a conf.py file.',
|
|
file=sys.stderr)
|
|
return 1
|
|
outdir = abspath(args[1])
|
|
except IndexError:
|
|
usage(argv, 'Error: Insufficient arguments.')
|
|
return 1
|
|
except UnicodeError:
|
|
print(
|
|
'Error: Multibyte filename not supported on this filesystem '
|
|
'encoding (%r).' % fs_encoding, file=sys.stderr)
|
|
return 1
|
|
|
|
# handle remaining filename arguments
|
|
filenames = args[2:]
|
|
err = 0
|
|
for filename in filenames:
|
|
if not path.isfile(filename):
|
|
print('Error: Cannot find file %r.' % filename, file=sys.stderr)
|
|
err = 1
|
|
if err:
|
|
return 1
|
|
|
|
# likely encoding used for command-line arguments
|
|
try:
|
|
locale = __import__('locale') # due to submodule of the same name
|
|
likely_encoding = locale.getpreferredencoding()
|
|
except Exception:
|
|
likely_encoding = None
|
|
|
|
if opts.force_all and filenames:
|
|
print('Error: Cannot combine -a option and filenames.', file=sys.stderr)
|
|
return 1
|
|
|
|
if opts.nocolor:
|
|
nocolor()
|
|
|
|
doctreedir = abspath(opts.doctreedir or path.join(outdir, '.doctrees'))
|
|
|
|
status = sys.stdout
|
|
warning = sys.stderr
|
|
error = sys.stderr
|
|
|
|
if opts.quiet:
|
|
status = None
|
|
if opts.really_quiet:
|
|
status = warning = None
|
|
if warning and opts.warnfile:
|
|
try:
|
|
warnfp = open(opts.warnfile, 'w')
|
|
except Exception as exc:
|
|
print('Error: Cannot open warning file %r: %s' %
|
|
(opts.warnfile, exc), file=sys.stderr)
|
|
sys.exit(1)
|
|
warning = Tee(warning, warnfp)
|
|
error = warning
|
|
|
|
confoverrides = {}
|
|
for val in opts.define:
|
|
try:
|
|
key, val = val.split('=')
|
|
except ValueError:
|
|
print('Error: -D option argument must be in the form name=value.',
|
|
file=sys.stderr)
|
|
return 1
|
|
if likely_encoding and isinstance(val, binary_type):
|
|
try:
|
|
val = val.decode(likely_encoding)
|
|
except UnicodeError:
|
|
pass
|
|
confoverrides[key] = val
|
|
|
|
for val in opts.htmldefine:
|
|
try:
|
|
key, val = val.split('=')
|
|
except ValueError:
|
|
print('Error: -A option argument must be in the form name=value.',
|
|
file=sys.stderr)
|
|
return 1
|
|
try:
|
|
val = int(val)
|
|
except ValueError:
|
|
if likely_encoding and isinstance(val, binary_type):
|
|
try:
|
|
val = val.decode(likely_encoding)
|
|
except UnicodeError:
|
|
pass
|
|
confoverrides['html_context.%s' % key] = val
|
|
|
|
if opts.nitpicky:
|
|
confoverrides['nitpicky'] = True
|
|
|
|
app = None
|
|
try:
|
|
app = Sphinx(srcdir, confdir, outdir, doctreedir, opts.builder,
|
|
confoverrides, status, warning, opts.freshenv,
|
|
opts.warningiserror, opts.tags, opts.verbosity, opts.jobs)
|
|
app.build(opts.force_all, filenames)
|
|
return app.statuscode
|
|
except (Exception, KeyboardInterrupt) as err:
|
|
if opts.pdb:
|
|
import pdb
|
|
print(red('Exception occurred while building, starting debugger:'),
|
|
file=error)
|
|
traceback.print_exc()
|
|
pdb.post_mortem(sys.exc_info()[2])
|
|
else:
|
|
print(file=error)
|
|
if opts.verbosity or opts.traceback:
|
|
traceback.print_exc(None, error)
|
|
print(file=error)
|
|
if isinstance(err, KeyboardInterrupt):
|
|
print('interrupted!', file=error)
|
|
elif isinstance(err, SystemMessage):
|
|
print(red('reST markup error:'), file=error)
|
|
print(terminal_safe(err.args[0]), file=error)
|
|
elif isinstance(err, SphinxError):
|
|
print(red('%s:' % err.category), file=error)
|
|
print(terminal_safe(text_type(err)), file=error)
|
|
elif isinstance(err, UnicodeError):
|
|
print(red('Encoding error:'), file=error)
|
|
print(terminal_safe(text_type(err)), file=error)
|
|
tbpath = save_traceback(app)
|
|
print(red('The full traceback has been saved in %s, if you want '
|
|
'to report the issue to the developers.' % tbpath),
|
|
file=error)
|
|
else:
|
|
print(red('Exception occurred:'), file=error)
|
|
print(format_exception_cut_frames().rstrip(), file=error)
|
|
tbpath = save_traceback(app)
|
|
print(red('The full traceback has been saved in %s, if you '
|
|
'want to report the issue to the developers.' % tbpath),
|
|
file=error)
|
|
print('Please also report this if it was a user error, so '
|
|
'that a better error message can be provided next time.',
|
|
file=error)
|
|
print('A bug report can be filed in the tracker at '
|
|
'<https://github.com/sphinx-doc/sphinx/issues>. Thanks!',
|
|
file=error)
|
|
return 1
|