blob: a487b2a2a0fe3d4d8bdb9d55e14c63301f7a9c3a [file] [log] [blame]
# -*- coding: utf-8 -*-
"""
sphinx.setup_command
~~~~~~~~~~~~~~~~~~~~
Setuptools/distutils commands to assist the building of sphinx
documentation.
:author: Sebastian Wiesner
:contact: basti.wiesner@gmx.net
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
import os
import types
from StringIO import StringIO
from distutils.cmd import Command
from distutils.errors import DistutilsOptionError, DistutilsExecError
from sphinx.application import Sphinx
from sphinx.util.console import darkred, nocolor, color_terminal
from sphinx.util.osutil import abspath
class BuildDoc(Command):
"""
Distutils command to build Sphinx documentation.
The Sphinx build can then be triggered from distutils, and some Sphinx
options can be set in ``setup.py`` or ``setup.cfg`` instead of Sphinx own
configuration file.
For instance, from `setup.py`::
# this is only necessary when not using setuptools/distribute
from sphinx.setup_command import BuildDoc
cmdclass = {'build_sphinx': BuildDoc}
name = 'My project'
version = '1.2'
release = '1.2.0'
setup(
name=name,
author='Bernard Montgomery',
version=release,
cmdclass=cmdclass,
# these are optional and override conf.py settings
command_options={
'build_sphinx': {
'project': ('setup.py', name),
'version': ('setup.py', version),
'release': ('setup.py', release)}},
)
Or add this section in ``setup.cfg``::
[build_sphinx]
project = 'My project'
version = 1.2
release = 1.2.0
"""
description = 'Build Sphinx documentation'
user_options = [
('fresh-env', 'E', 'discard saved environment'),
('all-files', 'a', 'build all files'),
('source-dir=', 's', 'Source directory'),
('build-dir=', None, 'Build directory'),
('config-dir=', 'c', 'Location of the configuration directory'),
('builder=', 'b', 'The builder to use. Defaults to "html"'),
('project=', None, 'The documented project\'s name'),
('version=', None, 'The short X.Y version'),
('release=', None, 'The full version, including alpha/beta/rc tags'),
('today=', None, 'How to format the current date, used as the '
'replacement for |today|'),
('link-index', 'i', 'Link index.html to the master doc'),
]
boolean_options = ['fresh-env', 'all-files', 'link-index']
def initialize_options(self):
self.fresh_env = self.all_files = False
self.source_dir = self.build_dir = None
self.builder = 'html'
self.project = ''
self.version = ''
self.release = ''
self.today = ''
self.config_dir = None
self.link_index = False
def _guess_source_dir(self):
for guess in ('doc', 'docs'):
if not os.path.isdir(guess):
continue
for root, dirnames, filenames in os.walk(guess):
if 'conf.py' in filenames:
return root
return None
# Overriding distutils' Command._ensure_stringlike which doesn't support
# unicode, causing finalize_options to fail if invoked again. Workaround
# for http://bugs.python.org/issue19570
def _ensure_stringlike(self, option, what, default=None):
val = getattr(self, option)
if val is None:
setattr(self, option, default)
return default
elif not isinstance(val, types.StringTypes):
raise DistutilsOptionError("'%s' must be a %s (got `%s`)"
% (option, what, val))
return val
def finalize_options(self):
if self.source_dir is None:
self.source_dir = self._guess_source_dir()
self.announce('Using source directory %s' % self.source_dir)
self.ensure_dirname('source_dir')
if self.source_dir is None:
self.source_dir = os.curdir
self.source_dir = abspath(self.source_dir)
if self.config_dir is None:
self.config_dir = self.source_dir
self.config_dir = abspath(self.config_dir)
if self.build_dir is None:
build = self.get_finalized_command('build')
self.build_dir = os.path.join(abspath(build.build_base), 'sphinx')
self.mkpath(self.build_dir)
self.build_dir = abspath(self.build_dir)
self.doctree_dir = os.path.join(self.build_dir, 'doctrees')
self.mkpath(self.doctree_dir)
self.builder_target_dir = os.path.join(self.build_dir, self.builder)
self.mkpath(self.builder_target_dir)
def run(self):
if not color_terminal():
# Windows' poor cmd box doesn't understand ANSI sequences
nocolor()
if not self.verbose:
status_stream = StringIO()
else:
status_stream = sys.stdout
confoverrides = {}
if self.project:
confoverrides['project'] = self.project
if self.version:
confoverrides['version'] = self.version
if self.release:
confoverrides['release'] = self.release
if self.today:
confoverrides['today'] = self.today
app = Sphinx(self.source_dir, self.config_dir,
self.builder_target_dir, self.doctree_dir,
self.builder, confoverrides, status_stream,
freshenv=self.fresh_env)
try:
app.build(force_all=self.all_files)
if app.statuscode:
raise DistutilsExecError(
'caused by %s builder.' % app.builder.name)
except Exception, err:
from docutils.utils import SystemMessage
if isinstance(err, SystemMessage):
print >>sys.stderr, darkred('reST markup error:')
print >>sys.stderr, err.args[0].encode('ascii',
'backslashreplace')
else:
raise
if self.link_index:
src = app.config.master_doc + app.builder.out_suffix
dst = app.builder.get_outfilename('index')
os.symlink(src, dst)