From 91d92be8adae2e44e1e23e1c07b7088c35261abf Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 11 Jan 2014 19:36:05 +0100 Subject: [PATCH] Add "make mode" to sphinx-build, invoked by the -M flag. This is intended to do almost all of what the quickstart-generated Makefile and make.bat did, but within Sphinx. The advantages are: * no duplication between Unix and Windows files * updates and fixes are propagated (the generated makefiles never update) * more Python code, less shell code! --- doc/Makefile | 164 +++-------------------------------- sphinx-build.py | 7 +- sphinx/__init__.py | 6 ++ sphinx/cmdline.py | 36 +++++--- sphinx/make_mode.py | 202 +++++++++++++++++++++++++++++++++++++++++++ sphinx/quickstart.py | 66 ++++++++++++++ 6 files changed, 315 insertions(+), 166 deletions(-) create mode 100644 sphinx/make_mode.py diff --git a/doc/Makefile b/doc/Makefile index 831c12c52..d799c3658 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -4,157 +4,21 @@ # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = python ../sphinx-build.py -PAPER = +SPHINXPROJ = sphinx +BUILDDIR = _build -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) \ - $(SPHINXOPTS) $(O) . -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(O) . - -.PHONY: help clean html dirhtml singlehtml text man pickle json htmlhelp \ - qthelp devhelp epub latex latexpdf changes linkcheck doctest xml \ - pseudoxml +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error \ +The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx \ +installed, then set the SPHINXBUILD environment variable to point \ +to the full path of the '$(SPHINXBUILD)' executable. Alternatively you \ +can add the directory with the executable to your PATH. \ +If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files called index.html in directories" - @echo " singlehtml to make one big HTML file" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " pickle to make pickle files" - @echo " json to make json files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make Qt help files and project" - @echo " devhelp to make Devhelp files and project" - @echo " epub to make an epub file" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run pdflatex" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview over all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" + @$(SPHINXBUILD) -M help "$(BUILDDIR)" $(SPHINXOPTS) $(O) -clean: - rm -rf _build/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html - @echo - @echo "Build finished. The HTML pages are in _build/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) _build/dirhtml - @echo - @echo "Build finished. The HTML pages are in _build/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) _build/singlehtml - @echo - @echo "Build finished. The HTML page is in _build/singlehtml." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) _build/text - @echo - @echo "Build finished." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) _build/man - @echo - @echo "Build finished." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle - @echo - @echo "Build finished." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json - @echo - @echo "Build finished." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in _build/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) _build/qthelp - @echo - @echo "Build finished; now you can run qcollectiongenerator with the" \ - ".qhcp project file in build/qthelp." - @echo "# qcollectiongenerator _build/qthelp/Sphinx.qhcp" - @echo "To view the help collection:" - @echo "# assistant -collectionFile _build/qthelp/Sphinx.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) _build/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/sphinx" - @echo "# ln -s _build/devhelp $$HOME/.local/share/devhelp/sphinx" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) _build/epub - @echo - @echo "Build finished. The epub file is in _build/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex - @echo - @echo "Build finished; the LaTeX files are in _build/latex." - @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ - "run these through (pdf)latex." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex - @echo "Running LaTeX files through pdflatex..." - make -C _build/latex all-pdf - @echo "pdflatex finished; the PDF files are in _build/latex." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) _build/locale - @echo - @echo "Build finished. The message catalogs are in _build/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes - @echo - @echo "The overview file is in _build/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in _build/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) _build/doctest - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) _build/texinfo - @echo - @echo "Build finished. The Texinfo files are in _build/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) _build/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C _build/texinfo info - @echo "makeinfo finished; the Info files are in _build/texinfo." - -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) _build/xml - @echo - @echo "Build finished. The XML files are in _build/XML." - -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) _build/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in _build/pseudoxml." +%: + @$(SPHINXBUILD) -M $@ "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/sphinx-build.py b/sphinx-build.py index 6737d0729..54bdb5293 100755 --- a/sphinx-build.py +++ b/sphinx-build.py @@ -11,5 +11,8 @@ import sys if __name__ == '__main__': - from sphinx import main - sys.exit(main(sys.argv)) + from sphinx import main, make_main + if sys.argv[1:2] == ['-M']: + sys.exit(make_main(sys.argv)) + else: + sys.exit(main(sys.argv)) diff --git a/sphinx/__init__.py b/sphinx/__init__.py index d41d4424a..8a1b97f70 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -80,5 +80,11 @@ def main(argv=sys.argv): return cmdline.main(argv) +def make_main(argv=sys.argv): + """Sphinx build "make mode" entry.""" + from sphinx import make_mode + return make_mode.run_make_mode(argv[2:]) + + if __name__ == '__main__': sys.exit(main(sys.argv)) diff --git a/sphinx/cmdline.py b/sphinx/cmdline.py index e5ad3ab5b..f4a09e161 100644 --- a/sphinx/cmdline.py +++ b/sphinx/cmdline.py @@ -42,6 +42,7 @@ General options -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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -82,19 +83,28 @@ def main(argv): # 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']) - allopts = set(opt[0] for opt in opts) - 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 + 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\'.' % ( @@ -103,12 +113,9 @@ def main(argv): 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 conf.py file.') + 'contain a conf.py file.') return 1 outdir = abspath(args[1]) - except getopt.error, err: - usage(argv, 'Error: %s' % err) - return 1 except IndexError: usage(argv, 'Error: Insufficient arguments.') return 1 @@ -118,6 +125,7 @@ def main(argv): 'encoding (%r).' % fs_encoding) return 1 + # handle remaining filename arguments filenames = args[2:] err = 0 for filename in filenames: diff --git a/sphinx/make_mode.py b/sphinx/make_mode.py new file mode 100644 index 000000000..5ef065a5d --- /dev/null +++ b/sphinx/make_mode.py @@ -0,0 +1,202 @@ +# -*- coding: utf-8 -*- +""" + sphinx.make_mode + ~~~~~~~~~~~~~~~~ + + sphinx-build -M command-line handling. + + This replaces the old, platform-dependent and once-generated content + of Makefile / make.bat. + + This is in its own module so that importing it is fast. It should not + import the main Sphinx modules (like sphinx.applications, sphinx.builders). + + :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import sys +import shutil +from os import path +from subprocess import call + +import sphinx +from sphinx.util.console import bold, blue + +proj_name = os.getenv('SPHINXPROJ', '') + +def build_clean(builddir, opts): + if not path.exists(builddir): + return + elif not path.isdir(builddir): + print "Error: %r is not a directory!" % builddir + return 1 + print "removing everything under %r..." % builddir + for item in os.listdir(builddir): + shutil.rmtree(path.join(builddir, item)) + +BUILDERS = [ + ("", "html", "to make standalone HTML files"), + ("", "dirhtml", "to make HTML files named index.html in directories"), + ("", "singlehtml","to make a single large HTML file"), + ("", "pickle", "to make pickle files"), + ("", "json", "to make JSON files"), + ("", "htmlhelp", "to make HTML files and a HTML help project"), + ("", "qthelp", "to make HTML files and a qthelp project"), + ("", "devhelp", "to make HTML files and a Devhelp project"), + ("", "epub", "to make an epub"), + ("", "latex", "to make LaTeX files, you can set PAPER=a4 or PAPER=letter"), + ("posix", "latexpdf", "to make LaTeX files and run them through pdflatex"), + ("posix", "latexpdfja","to make LaTeX files and run them through platex/dvipdfmx"), + ("", "text", "to make text files"), + ("", "man", "to make manual pages"), + ("", "texinfo", "to make Texinfo files"), + ("posix", "info", "to make Texinfo files and run them through makeinfo"), + ("", "gettext", "to make PO message catalogs"), + ("", "changes", "to make an overview of all changed/added/deprecated items"), + ("", "xml", "to make Docutils-native XML files"), + ("", "pseudoxml", "to make pseudoxml-XML files for display purposes"), + ("", "linkcheck", "to check all external links for integrity"), + ("", "doctest", "to run all doctests embedded in the documentation (if enabled)"), +] + +def build_help(builddir, opts): + print bold("Sphinx v%s" % sphinx.__version__) + print "Please use `make %s' where %s is one of" % ((blue('target'),)*2) + for osname, bname, description in BUILDERS: + if not osname or os.name == osname: + print ' %s %s' % (blue(bname.ljust(10)), description) + + +def build_html(builddir, opts): + if run_generic_build('html', builddir, opts) > 0: + return 1 + print + print 'Build finished. The HTML pages are in %s.' % path.join(builddir, 'html') + +def build_dirhtml(builddir, opts): + if run_generic_build('dirhtml', builddir, opts) > 0: + return 1 + print + print 'Build finished. The HTML pages are in %s.' % path.join(builddir, 'dirhtml') + +def build_singlehtml(builddir, opts): + if run_generic_build('singlehtml', builddir, opts) > 0: + return 1 + print + print 'Build finished. The HTML page is in %s.' % path.join(builddir, 'singlehtml') + +def build_pickle(builddir, opts): + if run_generic_build('pickle', builddir, opts) > 0: + return 1 + print + print 'Build finished; now you can process the pickle files.' + +def build_json(builddir, opts): + if run_generic_build('json', builddir, opts) > 0: + return 1 + print + print 'Build finished; now you can process the JSON files.' + +def build_htmlhelp(builddir, opts): + if run_generic_build('htmlhelp', builddir, opts) > 0: + return 1 + print + print ('Build finished; now you can run HTML Help Workshop with the ' + '.hhp project file in %s.') % path.join(builddir, 'htmlhelp') + +def build_qthelp(builddir, opts): + if run_generic_build('qthelp', builddir, opts) > 0: + return 1 + print + print ('Build finished; now you can run "qcollectiongenerator" with the ' + '.qhcp project file in %s, like this:') % path.join(builddir, 'qthelp') + print '$ qcollectiongenerator %s.qhcp' % path.join(builddir, 'qthelp', proj_name) + print 'To view the help file:' + print '$ assistant -collectionFile %s.qhc' % path.join(builddir, 'qthelp', proj_name) + +def build_devhelp(builddir, opts): + if run_generic_build('devhelp', builddir, opts) > 0: + return 1 + print + print "Build finished." + print "To view the help file:" + print "$ mkdir -p $HOME/.local/share/devhelp/" + proj_name + print "$ ln -s %s $HOME/.local/share/devhelp/%s" % \ + (path.join(builddir, 'devhelp'), proj_name) + print "$ devhelp" + +def build_epub(builddir, opts): + if run_generic_build('epub', builddir, opts) > 0: + return 1 + print + print 'Build finished. The ePub file is in %s.' % path.join(builddir, 'epub') + + +# latex +# latexpdf +# latexpdfja + +def build_text(builddir, opts): + if run_generic_build('text', builddir, opts) > 0: + return 1 + print + print 'Build finished. The text files are in %s.' % path.join(builddir, 'text') + +# texinfo +# info + +def build_gettext(builddir, opts): + dtdir = path.join(builddir, 'gettext', '.doctrees') + if run_generic_build('gettext', builddir, opts, doctreedir=dtdir) > 0: + return 1 + print + print 'Build finished. The message catalogs are in %s.' % path.join(builddir, 'gettext') + +def build_changes(builddir, opts): + if run_generic_build('changes', builddir, opts) > 0: + return 1 + print + print 'Build finished. The overview file is in %s.' % path.join(builddir, 'changes') + +def build_linkcheck(builddir, opts): + res = run_generic_build('linkcheck', builddir, opts) + print + print ('Link check complete; look for any errors in the above output ' + 'or in %s.') % path.join(builddir, 'linkcheck', 'output.txt') + return res + +def build_xml(builddir, opts): + if run_generic_build('xml', builddir, opts) > 0: + return 1 + print + print 'Build finished. The XML files are in %s.' % path.join(builddir, 'xml') + +def build_pseudoxml(builddir, opts): + if run_generic_build('pseudoxml', builddir, opts) > 0: + return 1 + print + print 'Build finished. The pseudo-XML files are in %s.' % path.join(builddir, 'pseudoxml') + + +def run_generic_build(builder, builddir, opts, doctreedir=None): + # compatibility with old Makefile + papersize = os.getenv('PAPER', '') + if papersize in ('a4', 'letter'): + opts.extend(['-D', 'latex_paper_size=' + papersize]) + if doctreedir is None: + doctreedir = path.join(builddir, 'doctrees') + return call([sys.executable, sys.argv[0], '-b', builder, + '-d', doctreedir, '.', path.join(builddir, builder)] + opts) + + +def run_make_mode(args): + if len(args) < 2: + print >>sys.stderr, ('Error: at least two arguments (builder, build ' + 'dir) are required.') + return 1 + run_method = 'build_' + args[0] + if run_method in globals(): + return globals()[run_method](args[1], args[2:]) + return run_generic_build(args[0], args[1], args[2:]) diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index f77e2f22b..7ce3ad848 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -837,6 +837,72 @@ if "%%1" == "pseudoxml" ( :end ''' +# This will become the Makefile template for Sphinx 1.5. +MAKEFILE_NEW = u'''\ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = %(project_fn)s +BUILDDIR = %(rbuilddir)s + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error \ +The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx \ +installed, then set the SPHINXBUILD environment variable to point \ +to the full path of the '$(SPHINXBUILD)' executable. Alternatively you \ +can add the directory with the executable to your PATH. \ +If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Has to be explicit, otherwise we don't get "make" without targets right. +help: +\t@$(SPHINXBUILD) -M help "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +# Catch-all target using the new "make mode" option. +%: +\t@$(SPHINXBUILD) -M $@ "$(BUILDDIR)" $(SPHINXOPTS) $(O) +''' + +# This will become the make.bat template for Sphinx 1.5. +BATCHFILE_NEW = u'''\ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%%SPHINXBUILD%%" == "" ( +\tset SPHINXBUILD=sphinx-build +) +set BUILDDIR=%(rbuilddir)s +set SPHINXPROJ=%(project_fn)s + +if "%%1" == "" goto help + +%%SPHINXBUILD%% 2> nul +if errorlevel 9009 ( +\techo. +\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx +\techo.installed, then set the SPHINXBUILD environment variable to point +\techo.to the full path of the 'sphinx-build' executable. Alternatively you +\techo.may add the Sphinx directory to PATH. +\techo. +\techo.If you don't have Sphinx installed, grab it from +\techo.http://sphinx-doc.org/ +\texit /b 1 +) + +%%SPHINXBUILD%% -M %%1 %%BUILDDIR%% %%SPHINXOPTS%% +goto end + +:help +%%SPHINXBUILD%% -M help %%BUILDDIR%% %%SPHINXOPTS%% + +:end +''' + def mkdir_p(dir): if path.isdir(dir):