| # -*- 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) |