# -*- 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. """ import os import sys import getopt import traceback from os import path 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, bytes def usage(argv, msg=None): if msg: print >>sys.stderr, msg print >>sys.stderr print >>sys.stderr, """\ Sphinx v%s Usage: %s [options] sourcedir outdir [filenames...] General options ^^^^^^^^^^^^^^^ -b builder to use; default is html -a write all files; default is to only write new and changed files -E don't use a saved environment, always read all files -d path for the cached environment and doctree files (default: outdir/.doctrees) -j build in parallel with N processes where possible -M "make" mode -- used by Makefile, like "sphinx-build -M html" Build configuration options ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -c path where configuration file (conf.py) is located (default: same as sourcedir) -C use no config file at all, only -D options -D override a setting in configuration file -t define tag: include "only" blocks with -A pass a value into the templates, for HTML builder -n nit-picky mode, warn about all missing references Console output options ^^^^^^^^^^^^^^^^^^^^^^ -v increase verbosity (can be repeated) -q no output on stdout, just warnings on stderr -Q no output at all, not even warnings -w write warnings (and errors) to given file -W turn warnings into errors -T show full traceback on exception -N do not emit colored output -P run Pdb on exception Filename arguments ^^^^^^^^^^^^^^^^^^ * without -a and without filenames, write new and changed files. * with -a, write all files. * with filenames, write these. Standard options ^^^^^^^^^^^^^^^^ -h, --help show this help and exit --version show version information and exit """ % (__version__, argv[0]) def main(argv): if not color_terminal(): # Windows' poor cmd box doesn't understand ANSI sequences nocolor() # parse options try: opts, args = getopt.getopt(argv[1:], 'ab:t:d:c:CD:A:nNEqQWw:PThvj:', ['help', 'version']) except getopt.error, err: usage(argv, 'Error: %s' % err) return 1 # handle basic options allopts = set(opt[0] for opt in opts) # help and version options if '-h' in allopts or '--help' in allopts: usage(argv) print >>sys.stderr print >>sys.stderr, 'For more information, see .' return 0 if '--version' in allopts: print 'Sphinx (sphinx-build) %s' % __version__ return 0 # get paths (first and second positional argument) try: srcdir = confdir = abspath(args[0]) if not path.isdir(srcdir): print >>sys.stderr, 'Error: Cannot find source directory `%s\'.' % ( srcdir,) return 1 if not path.isfile(path.join(srcdir, 'conf.py')) and \ '-c' not in allopts and '-C' not in allopts: print >>sys.stderr, ('Error: Source directory doesn\'t ' 'contain a conf.py file.') return 1 outdir = abspath(args[1]) except IndexError: usage(argv, 'Error: Insufficient arguments.') return 1 except UnicodeError: print >>sys.stderr, ( 'Error: Multibyte filename not supported on this filesystem ' 'encoding (%r).' % fs_encoding) return 1 # handle remaining filename arguments filenames = args[2:] err = 0 for filename in filenames: if not path.isfile(filename): print >>sys.stderr, 'Error: Cannot find file %r.' % filename 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 buildername = None force_all = freshenv = warningiserror = use_pdb = False show_traceback = False verbosity = 0 parallel = 0 status = sys.stdout warning = sys.stderr error = sys.stderr warnfile = None confoverrides = {} tags = [] doctreedir = path.join(outdir, '.doctrees') for opt, val in opts: if opt == '-b': buildername = val elif opt == '-a': if filenames: usage(argv, 'Error: Cannot combine -a option and filenames.') return 1 force_all = True elif opt == '-t': tags.append(val) elif opt == '-d': doctreedir = abspath(val) elif opt == '-c': confdir = abspath(val) if not path.isfile(path.join(confdir, 'conf.py')): print >>sys.stderr, ('Error: Configuration directory ' 'doesn\'t contain conf.py file.') return 1 elif opt == '-C': confdir = None elif opt == '-D': try: key, val = val.split('=') except ValueError: print >>sys.stderr, ('Error: -D option argument must be ' 'in the form name=value.') return 1 try: val = int(val) except ValueError: if likely_encoding and isinstance(val, bytes): try: val = val.decode(likely_encoding) except UnicodeError: pass confoverrides[key] = val elif opt == '-A': try: key, val = val.split('=') except ValueError: print >>sys.stderr, ('Error: -A option argument must be ' 'in the form name=value.') return 1 try: val = int(val) except ValueError: if likely_encoding and isinstance(val, bytes): try: val = val.decode(likely_encoding) except UnicodeError: pass confoverrides['html_context.%s' % key] = val elif opt == '-n': confoverrides['nitpicky'] = True elif opt == '-N': nocolor() elif opt == '-E': freshenv = True elif opt == '-q': status = None elif opt == '-Q': status = None warning = None elif opt == '-W': warningiserror = True elif opt == '-w': warnfile = val elif opt == '-P': use_pdb = True elif opt == '-T': show_traceback = True elif opt == '-v': verbosity += 1 show_traceback = True elif opt == '-j': try: parallel = int(val) except ValueError: print >>sys.stderr, ('Error: -j option argument must be an ' 'integer.') return 1 if warning and warnfile: warnfp = open(warnfile, 'w') warning = Tee(warning, warnfp) error = warning if not path.isdir(outdir): if status: print >>status, 'Making output directory...' os.makedirs(outdir) app = None try: app = Sphinx(srcdir, confdir, outdir, doctreedir, buildername, confoverrides, status, warning, freshenv, warningiserror, tags, verbosity, parallel) app.build(force_all, filenames) return app.statuscode except (Exception, KeyboardInterrupt), err: if use_pdb: import pdb print >>error, red('Exception occurred while building, ' 'starting debugger:') traceback.print_exc() pdb.post_mortem(sys.exc_info()[2]) else: print >>error if show_traceback: traceback.print_exc(None, error) print >>error if isinstance(err, KeyboardInterrupt): print >>error, 'interrupted!' elif isinstance(err, SystemMessage): print >>error, red('reST markup error:') print >>error, terminal_safe(err.args[0]) elif isinstance(err, SphinxError): print >>error, red('%s:' % err.category) print >>error, terminal_safe(unicode(err)) elif isinstance(err, UnicodeError): print >>error, red('Encoding error:') print >>error, terminal_safe(unicode(err)) tbpath = save_traceback(app) print >>error, red('The full traceback has been saved ' 'in %s, if you want to report the ' 'issue to the developers.' % tbpath) else: print >>error, red('Exception occurred:') print >>error, format_exception_cut_frames().rstrip() tbpath = save_traceback(app) print >>error, red('The full traceback has been saved ' 'in %s, if you want to report the ' 'issue to the developers.' % tbpath) print >>error, ('Please also report this if it was a user ' 'error, so that a better error message ' 'can be provided next time.') print >>error, ( 'A bug report can be filed in the tracker at ' '. Thanks!') return 1