Merge pull request #2774 from tk0miya/latex_engine

Add `latex_engine` to switch the LaTeX engine by conf.py
This commit is contained in:
Takeshi KOMIYA
2016-07-21 01:05:51 +09:00
committed by GitHub
10 changed files with 82 additions and 240 deletions

View File

@@ -35,7 +35,6 @@ Features added
-------------- --------------
* Add ``:caption:`` option for sphinx.ext.inheritance_diagram. * Add ``:caption:`` option for sphinx.ext.inheritance_diagram.
* #894: Add ``lualatexpdf`` and ``xelatexpdf`` as a make target to build PDF using lualatex or xelatex
* #2471: Add config variable for default doctest flags. * #2471: Add config variable for default doctest flags.
* Convert linkcheck builder to requests for better encoding handling * Convert linkcheck builder to requests for better encoding handling
* #2463, #2516: Add keywords of "meta" directive to search index * #2463, #2516: Add keywords of "meta" directive to search index
@@ -77,7 +76,7 @@ Features added
entries in 3rd party extensions. entries in 3rd party extensions.
* Python domain signature parser now uses the xref mixin for 'exceptions', * Python domain signature parser now uses the xref mixin for 'exceptions',
allowing exception classes to be autolinked. allowing exception classes to be autolinked.
* #2513: Add `latex_engine` to switch the LaTeX engine by conf.py
Bugs fixed Bugs fixed
---------- ----------

View File

@@ -1469,6 +1469,16 @@ Options for LaTeX output
These options influence LaTeX output. See further :doc:`latex`. These options influence LaTeX output. See further :doc:`latex`.
.. confval:: latex_engine
The LaTeX engine to build the docs. The setting can have the following
values:
* pdflatex -- PDFLaTeX (default)
* xelatex -- XeLaTeX
* lualatex -- LuaLaTeX
* platex -- pLaTeX (default if `language` is 'ja')
.. confval:: latex_documents .. confval:: latex_documents
This value determines how to group the document tree into LaTeX source files. This value determines how to group the document tree into LaTeX source files.

View File

@@ -20,13 +20,14 @@ from docutils.frontend import OptionParser
from sphinx import package_dir, addnodes, highlighting from sphinx import package_dir, addnodes, highlighting
from sphinx.util import texescape from sphinx.util import texescape
from sphinx.config import string_classes from sphinx.config import string_classes, ENUM
from sphinx.errors import SphinxError from sphinx.errors import SphinxError
from sphinx.locale import _ from sphinx.locale import _
from sphinx.builders import Builder from sphinx.builders import Builder
from sphinx.environment import NoUri from sphinx.environment import NoUri
from sphinx.util.nodes import inline_all_toctrees from sphinx.util.nodes import inline_all_toctrees
from sphinx.util.osutil import SEP, copyfile, make_filename from sphinx.util.fileutil import copy_asset_file
from sphinx.util.osutil import SEP, make_filename
from sphinx.util.console import bold, darkgreen from sphinx.util.console import bold, darkgreen
from sphinx.writers.latex import LaTeXWriter from sphinx.writers.latex import LaTeXWriter
@@ -188,35 +189,33 @@ class LaTeXBuilder(Builder):
self.info(bold('copying images...'), nonl=1) self.info(bold('copying images...'), nonl=1)
for src, dest in iteritems(self.images): for src, dest in iteritems(self.images):
self.info(' '+src, nonl=1) self.info(' '+src, nonl=1)
copyfile(path.join(self.srcdir, src), copy_asset_file(path.join(self.srcdir, src),
path.join(self.outdir, dest)) path.join(self.outdir, dest))
self.info() self.info()
# copy TeX support files from texinputs # copy TeX support files from texinputs
context = {'latex_engine': self.config.latex_engine}
self.info(bold('copying TeX support files...')) self.info(bold('copying TeX support files...'))
staticdirname = path.join(package_dir, 'texinputs') staticdirname = path.join(package_dir, 'texinputs')
for filename in os.listdir(staticdirname): for filename in os.listdir(staticdirname):
if not filename.startswith('.'): if not filename.startswith('.'):
copyfile(path.join(staticdirname, filename), copy_asset_file(path.join(staticdirname, filename),
path.join(self.outdir, filename)) self.outdir, context=context)
# copy additional files # copy additional files
if self.config.latex_additional_files: if self.config.latex_additional_files:
self.info(bold('copying additional files...'), nonl=1) self.info(bold('copying additional files...'), nonl=1)
for filename in self.config.latex_additional_files: for filename in self.config.latex_additional_files:
self.info(' '+filename, nonl=1) self.info(' '+filename, nonl=1)
copyfile(path.join(self.confdir, filename), copy_asset_file(path.join(self.confdir, filename), self.outdir)
path.join(self.outdir, path.basename(filename)))
self.info() self.info()
# the logo is handled differently # the logo is handled differently
if self.config.latex_logo: if self.config.latex_logo:
logobase = path.basename(self.config.latex_logo)
logotarget = path.join(self.outdir, logobase)
if not path.isfile(path.join(self.confdir, self.config.latex_logo)): if not path.isfile(path.join(self.confdir, self.config.latex_logo)):
raise SphinxError('logo file %r does not exist' % self.config.latex_logo) raise SphinxError('logo file %r does not exist' % self.config.latex_logo)
elif not path.isfile(logotarget): else:
copyfile(path.join(self.confdir, self.config.latex_logo), logotarget) copy_asset_file(path.join(self.confdir, self.config.latex_logo), self.outdir)
self.info('done') self.info('done')
@@ -264,6 +263,10 @@ def setup(app):
app.add_builder(LaTeXBuilder) app.add_builder(LaTeXBuilder)
app.connect('builder-inited', validate_config_values) app.connect('builder-inited', validate_config_values)
app.add_config_value('latex_engine',
lambda self: 'pdflatex' if self.language != 'ja' else 'platex',
None,
ENUM('pdflatex', 'xelatex', 'lualatex', 'platex'))
app.add_config_value('latex_documents', app.add_config_value('latex_documents',
lambda self: [(self.master_doc, make_filename(self.project) + '.tex', lambda self: [(self.master_doc, make_filename(self.project) + '.tex',
self.project, '', 'manual')], self.project, '', 'manual')],

View File

@@ -41,8 +41,6 @@ BUILDERS = [
("", "latex", "to make LaTeX files, you can set PAPER=a4 or PAPER=letter"), ("", "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", "latexpdf", "to make LaTeX files and run them through pdflatex"),
("posix", "latexpdfja", "to make LaTeX files and run them through platex/dvipdfmx"), ("posix", "latexpdfja", "to make LaTeX files and run them through platex/dvipdfmx"),
("posix", "lualatexpdf", "to make LaTeX files and run them through lualatex"),
("posix", "xelatexpdf", "to make LaTeX files and run them through xelatex"),
("", "text", "to make text files"), ("", "text", "to make text files"),
("", "man", "to make manual pages"), ("", "man", "to make manual pages"),
("", "texinfo", "to make Texinfo files"), ("", "texinfo", "to make Texinfo files"),
@@ -172,18 +170,6 @@ class Make(object):
with cd(self.builddir_join('latex')): with cd(self.builddir_join('latex')):
os.system('make all-pdf-ja') os.system('make all-pdf-ja')
def build_lualatexpdf(self):
if self.run_generic_build('latex') > 0:
return 1
with cd(self.builddir_join('latex')):
os.system('make PDFLATEX=lualatex all-pdf')
def build_xelatexpdf(self):
if self.run_generic_build('latex') > 0:
return 1
with cd(self.builddir_join('latex')):
os.system('make PDFLATEX=xelatex all-pdf')
def build_text(self): def build_text(self):
if self.run_generic_build('text') > 0: if self.run_generic_build('text') > 0:
return 1 return 1

View File

@@ -565,8 +565,6 @@ help:
\t@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" \t@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
\t@echo " latexpdf to make LaTeX files and run them through pdflatex" \t@echo " latexpdf to make LaTeX files and run them through pdflatex"
\t@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" \t@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
\t@echo " lualatexpdf to make LaTeX files and run them through lualatex"
\t@echo " xelatexpdf to make LaTeX files and run them through xelatex"
\t@echo " text to make text files" \t@echo " text to make text files"
\t@echo " man to make manual pages" \t@echo " man to make manual pages"
\t@echo " texinfo to make Texinfo files" \t@echo " texinfo to make Texinfo files"
@@ -686,20 +684,6 @@ latexpdfja:
\t$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja \t$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
\t@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." \t@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
.PHONY: lualatexpdf
lualatexpdf:
\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
\t@echo "Running LaTeX files through lualatex..."
\t$(MAKE) PDFLATEX=lualatex -C $(BUILDDIR)/latex all-pdf
\t@echo "lualatex finished; the PDF files are in $(BUILDDIR)/latex."
.PHONY: xelatexpdf
xelatexpdf:
\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
\t@echo "Running LaTeX files through xelatex..."
\t$(MAKE) PDFLATEX=xelatex -C $(BUILDDIR)/latex all-pdf
\t@echo "xelatex finished; the PDF files are in $(BUILDDIR)/latex."
.PHONY: text .PHONY: text
text: text:
\t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text \t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text

View File

@@ -5,7 +5,6 @@
\ifdefined\pdfpxdimen \ifdefined\pdfpxdimen
\let\sphinxpxdimen\pdfpxdimen\else\newdimen\sphinxpxdimen \let\sphinxpxdimen\pdfpxdimen\else\newdimen\sphinxpxdimen
\fi \sphinxpxdimen=<%= pxunit %>\relax \fi \sphinxpxdimen=<%= pxunit %>\relax
\usepackage{iftex}
<%= passoptionstopackages %> <%= passoptionstopackages %>
<%= inputenc %> <%= inputenc %>
<%= utf8extra %> <%= utf8extra %>

View File

@@ -13,11 +13,16 @@ LATEXOPTS =
FMT = pdf FMT = pdf
LATEX = latex LATEX = latex
PDFLATEX = pdflatex PDFLATEX = {{ latex_engine }}
MAKEINDEX = makeindex MAKEINDEX = makeindex
{% if latex_engine == 'platex' %}
all: all-pdf-ja
all-pdf: all-pdf-ja
{% else %}
all: $(ALLPDF) all: $(ALLPDF)
all-pdf: $(ALLPDF) all-pdf: $(ALLPDF)
{% endif -%}
all-dvi: $(ALLDVI) all-dvi: $(ALLDVI)
all-ps: $(ALLPS) all-ps: $(ALLPS)

View File

@@ -1,97 +0,0 @@
%%
%% This is file `iftex.sty',
%%
%% __________________________________
%% Copyright © 20102013 Persian TeX Group
%%
%% License information appended.
%%
%%
\csname iftexloaded\endcsname
\let\iftexloaded\endinput
\expandafter\ifx\csname ProvidesPackage\endcsname\relax\else
\ProvidesPackage{iftex}
[2013/04/04 v0.2 Provides if(tex) conditional for PDFTeX, XeTeX, and LuaTeX]
\fi
\def\RequirePDFTeX{%
\ifPDFTeX\else
\begingroup
\errorcontextlines=-1\relax
\newlinechar=10\relax
\errmessage{^^J
********************************************^^J
* PDFTeX is required to compile this document.^^J
* Sorry!^^J
********************************************}%
\endgroup
\fi}
\def\RequireXeTeX{%
\ifXeTeX\else
\begingroup
\errorcontextlines=-1\relax
\newlinechar=10\relax
\errmessage{^^J
********************************************^^J
* XeTeX is required to compile this document.^^J
* Sorry!^^J
********************************************}%
\endgroup
\fi}
\def\RequireLuaTeX{%
\ifLuaTeX\else
\begingroup
\errorcontextlines=-1\relax
\newlinechar=10\relax
\errmessage{^^J
********************************************^^J
* LuaTeX is required to compile this document.^^J
* Sorry!^^J
********************************************}%
\endgroup
\fi}
\expandafter\ifx\csname ifPDFTeX\endcsname\relax\else
\expandafter\endinput
\fi
\expandafter\ifx\csname ifXeTeX\endcsname\relax\else
\expandafter\endinput
\fi
\expandafter\ifx\csname ifLuaTeX\endcsname\relax\else
\expandafter\endinput
\fi
\newif\ifPDFTeX
\begingroup\expandafter\expandafter\expandafter\endgroup
\expandafter\ifx\csname pdfmatch\endcsname\relax
\PDFTeXfalse
\else
\PDFTeXtrue
\fi
\newif\ifXeTeX
\begingroup\expandafter\expandafter\expandafter\endgroup
\expandafter\ifx\csname XeTeXinterchartoks\endcsname\relax
\XeTeXfalse
\else
\XeTeXtrue
\fi
\newif\ifLuaTeX
\begingroup\expandafter\expandafter\expandafter\endgroup
\expandafter\ifx\csname directlua\endcsname\relax
\LuaTeXfalse
\else
\LuaTeXtrue
\fi
%%
%% Copyright © 20102013 by Persian TeX Group <persian-tex@tug.org>
%%
%% Distributable under the LaTeX Project Public License,
%% version 1.3c or higher (your choice). The latest version of
%% this license is at: http://www.latex-project.org/lppl.txt
%%
%% This work is "maintained" (as per LPPL maintenance status)
%% by Persian TeX Group.
%%
%%
%%
%%
%%
%% End of file `iftex.sty'.

View File

@@ -265,9 +265,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
'classoptions': '', 'classoptions': '',
'extraclassoptions': '', 'extraclassoptions': '',
'passoptionstopackages': '', 'passoptionstopackages': '',
'inputenc': ('\\ifPDFTeX\n' 'inputenc': '',
' \\usepackage[utf8]{inputenc}\n'
'\\fi'),
'utf8extra': ('\\ifdefined\\DeclareUnicodeCharacter\n' 'utf8extra': ('\\ifdefined\\DeclareUnicodeCharacter\n'
' \\DeclareUnicodeCharacter{00A0}{\\nobreakspace}\n' ' \\DeclareUnicodeCharacter{00A0}{\\nobreakspace}\n'
'\\fi'), '\\fi'),
@@ -358,6 +356,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
else: else:
docclass = builder.config.latex_docclass.get('manual', 'report') docclass = builder.config.latex_docclass.get('manual', 'report')
self.elements['docclass'] = docclass self.elements['docclass'] = docclass
if builder.config.latex_engine == 'pdflatex':
self.elements['inputenc'] = '\\usepackage[utf8]{inputenc}'
if builder.config.today: if builder.config.today:
self.elements['date'] = builder.config.today self.elements['date'] = builder.config.today
else: else:

View File

@@ -12,17 +12,24 @@ from __future__ import print_function
import os import os
import re import re
from itertools import product
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from six import PY3 from six import PY3
from sphinx.errors import SphinxError from sphinx.errors import SphinxError
from sphinx.util.osutil import cd, ensuredir
from sphinx.writers.latex import LaTeXTranslator from sphinx.writers.latex import LaTeXTranslator
from util import SkipTest, remove_unicode_literals, with_app, strip_escseq from util import SkipTest, remove_unicode_literals, with_app, strip_escseq
from test_build_html import ENV_WARNINGS from test_build_html import ENV_WARNINGS
LATEX_ENGINES = ['pdflatex', 'lualatex', 'xelatex']
DOCCLASSES = ['howto', 'manual']
STYLEFILES = ['article.sty', 'fancyhdr.sty', 'fancybox.sty', 'titlesec.sty', 'amsmath.sty',
'framed.sty', 'color.sty', 'fancyvrb.sty', 'threeparttable.sty']
LATEX_WARNINGS = ENV_WARNINGS + """\ LATEX_WARNINGS = ENV_WARNINGS + """\
%(root)s/index.rst:\\d+: WARNING: unknown option: &option %(root)s/index.rst:\\d+: WARNING: unknown option: &option
%(root)s/index.rst:\\d+: WARNING: citation not found: missing %(root)s/index.rst:\\d+: WARNING: citation not found: missing
@@ -34,70 +41,58 @@ if PY3:
LATEX_WARNINGS = remove_unicode_literals(LATEX_WARNINGS) LATEX_WARNINGS = remove_unicode_literals(LATEX_WARNINGS)
def run_latex(outdir): # only run latex if all needed packages are there
"""Run pdflatex, xelatex, and lualatex in the outdir""" def kpsetest(*filenames):
cwd = os.getcwd()
os.chdir(outdir)
try: try:
latexes = ('pdflatex', 'xelatex', 'lualatex') p = Popen(['kpsewhich'] + list(filenames), stdout=PIPE)
available_latexes = len(latexes) except OSError:
for latex in latexes: # no kpsewhich... either no tex distribution is installed or it is
try: # a "strange" one -- don't bother running latex
os.mkdir(latex) return None
p = Popen([latex, '--interaction=nonstopmode', else:
'-output-directory=%s' % latex, 'SphinxTests.tex'], p.communicate()
stdout=PIPE, stderr=PIPE) if p.returncode != 0:
except OSError: # most likely the latex executable was not found # not found
available_latexes -= 1 return False
else: # found
stdout, stderr = p.communicate() return True
if p.returncode != 0:
print(stdout)
print(stderr)
assert False, '%s exited with return code %s' % (
latex, p.returncode)
finally:
os.chdir(cwd)
if available_latexes == 0: # no latex is available, skip the test
raise SkipTest def test_latex():
if kpsetest(*STYLEFILES) is False:
raise SkipTest('not running latex, the required styles doesn\'t seem to be installed')
for engine, docclass in product(LATEX_ENGINES, DOCCLASSES):
yield build_latex_doc, engine, docclass
@with_app(buildername='latex') @with_app(buildername='latex')
def test_latex(app, status, warning): def build_latex_doc(app, status, warning, engine, docclass):
app.config.latex_engine = engine
app.config.latex_documents[0] = app.config.latex_documents[0][:4] + (docclass,)
LaTeXTranslator.ignore_missing_images = True LaTeXTranslator.ignore_missing_images = True
app.builder.build_all() app.builder.build_all()
# file from latex_additional_files # file from latex_additional_files
assert (app.outdir / 'svgimg.svg').isfile() assert (app.outdir / 'svgimg.svg').isfile()
# only run latex if all needed packages are there
def kpsetest(filename):
try:
p = Popen(['kpsewhich', filename], stdout=PIPE)
except OSError:
# no kpsewhich... either no tex distribution is installed or it is
# a "strange" one -- don't bother running latex
return None
else:
p.communicate()
if p.returncode != 0:
# not found
return False
# found
return True
if kpsetest('article.sty') is None:
raise SkipTest('not running latex, it doesn\'t seem to be installed')
for filename in ['fancyhdr.sty', 'fancybox.sty', 'titlesec.sty',
'amsmath.sty', 'framed.sty', 'color.sty', 'fancyvrb.sty',
'threeparttable.sty']:
if not kpsetest(filename):
raise SkipTest('not running latex, the %s package doesn\'t '
'seem to be installed' % filename)
# now, try to run latex over it # now, try to run latex over it
run_latex(app.outdir) with cd(app.outdir):
try:
ensuredir(engine)
p = Popen([engine, '--interaction=nonstopmode',
'-output-directory=%s' % engine, 'SphinxTests.tex'],
stdout=PIPE, stderr=PIPE)
except OSError: # most likely the latex executable was not found
raise SkipTest
else:
stdout, stderr = p.communicate()
if p.returncode != 0:
print(stdout)
print(stderr)
assert False, '%s exited with return code %s' % (
engine, p.returncode)
@with_app(buildername='latex') @with_app(buildername='latex')
@@ -126,48 +121,6 @@ def test_writer(app, status, warning):
'\\end{wrapfigure}' in result) '\\end{wrapfigure}' in result)
@with_app(buildername='latex',
confoverrides={'latex_documents': [
('contents', 'SphinxTests.tex', 'Sphinx Tests Documentation',
'Georg Brandl \\and someone else', 'howto'),
]},
srcdir='latex_howto')
def test_latex_howto(app, status, warning):
LaTeXTranslator.ignore_missing_images = True
app.builder.build_all()
# file from latex_additional_files
assert (app.outdir / 'svgimg.svg').isfile()
# only run latex if all needed packages are there
def kpsetest(filename):
try:
p = Popen(['kpsewhich', filename], stdout=PIPE)
except OSError:
# no kpsewhich... either no tex distribution is installed or it is
# a "strange" one -- don't bother running latex
return None
else:
p.communicate()
if p.returncode != 0:
# not found
return False
# found
return True
if kpsetest('article.sty') is None:
raise SkipTest('not running latex, it doesn\'t seem to be installed')
for filename in ['fancyhdr.sty', 'fancybox.sty', 'titlesec.sty',
'amsmath.sty', 'framed.sty', 'color.sty', 'fancyvrb.sty',
'threeparttable.sty']:
if not kpsetest(filename):
raise SkipTest('not running latex, the %s package doesn\'t '
'seem to be installed' % filename)
# now, try to run latex over it
run_latex(app.outdir)
@with_app(buildername='latex', testroot='warnings', freshenv=True) @with_app(buildername='latex', testroot='warnings', freshenv=True)
def test_latex_warnings(app, status, warning): def test_latex_warnings(app, status, warning):
app.builder.build_all() app.builder.build_all()