# -*- coding: utf-8 -*- """ sphinx.cmdline ~~~~~~~~~~~~~~ sphinx-build command-line handling. :copyright: Copyright 2007-2014 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 . """ 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 ' '. Thanks!', file=error) return 1