mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #2774 from tk0miya/latex_engine
Add `latex_engine` to switch the LaTeX engine by conf.py
This commit is contained in:
3
CHANGES
3
CHANGES
@@ -35,7 +35,6 @@ Features added
|
||||
--------------
|
||||
|
||||
* 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.
|
||||
* Convert linkcheck builder to requests for better encoding handling
|
||||
* #2463, #2516: Add keywords of "meta" directive to search index
|
||||
@@ -77,7 +76,7 @@ Features added
|
||||
entries in 3rd party extensions.
|
||||
* Python domain signature parser now uses the xref mixin for 'exceptions',
|
||||
allowing exception classes to be autolinked.
|
||||
|
||||
* #2513: Add `latex_engine` to switch the LaTeX engine by conf.py
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
@@ -1469,6 +1469,16 @@ Options for LaTeX output
|
||||
|
||||
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
|
||||
|
||||
This value determines how to group the document tree into LaTeX source files.
|
||||
|
||||
@@ -20,13 +20,14 @@ from docutils.frontend import OptionParser
|
||||
|
||||
from sphinx import package_dir, addnodes, highlighting
|
||||
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.locale import _
|
||||
from sphinx.builders import Builder
|
||||
from sphinx.environment import NoUri
|
||||
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.writers.latex import LaTeXWriter
|
||||
|
||||
@@ -188,35 +189,33 @@ class LaTeXBuilder(Builder):
|
||||
self.info(bold('copying images...'), nonl=1)
|
||||
for src, dest in iteritems(self.images):
|
||||
self.info(' '+src, nonl=1)
|
||||
copyfile(path.join(self.srcdir, src),
|
||||
copy_asset_file(path.join(self.srcdir, src),
|
||||
path.join(self.outdir, dest))
|
||||
self.info()
|
||||
|
||||
# copy TeX support files from texinputs
|
||||
context = {'latex_engine': self.config.latex_engine}
|
||||
self.info(bold('copying TeX support files...'))
|
||||
staticdirname = path.join(package_dir, 'texinputs')
|
||||
for filename in os.listdir(staticdirname):
|
||||
if not filename.startswith('.'):
|
||||
copyfile(path.join(staticdirname, filename),
|
||||
path.join(self.outdir, filename))
|
||||
copy_asset_file(path.join(staticdirname, filename),
|
||||
self.outdir, context=context)
|
||||
|
||||
# copy additional files
|
||||
if self.config.latex_additional_files:
|
||||
self.info(bold('copying additional files...'), nonl=1)
|
||||
for filename in self.config.latex_additional_files:
|
||||
self.info(' '+filename, nonl=1)
|
||||
copyfile(path.join(self.confdir, filename),
|
||||
path.join(self.outdir, path.basename(filename)))
|
||||
copy_asset_file(path.join(self.confdir, filename), self.outdir)
|
||||
self.info()
|
||||
|
||||
# the logo is handled differently
|
||||
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)):
|
||||
raise SphinxError('logo file %r does not exist' % self.config.latex_logo)
|
||||
elif not path.isfile(logotarget):
|
||||
copyfile(path.join(self.confdir, self.config.latex_logo), logotarget)
|
||||
else:
|
||||
copy_asset_file(path.join(self.confdir, self.config.latex_logo), self.outdir)
|
||||
self.info('done')
|
||||
|
||||
|
||||
@@ -264,6 +263,10 @@ def setup(app):
|
||||
app.add_builder(LaTeXBuilder)
|
||||
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',
|
||||
lambda self: [(self.master_doc, make_filename(self.project) + '.tex',
|
||||
self.project, '', 'manual')],
|
||||
|
||||
@@ -41,8 +41,6 @@ BUILDERS = [
|
||||
("", "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"),
|
||||
("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"),
|
||||
("", "man", "to make manual pages"),
|
||||
("", "texinfo", "to make Texinfo files"),
|
||||
@@ -172,18 +170,6 @@ class Make(object):
|
||||
with cd(self.builddir_join('latex')):
|
||||
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):
|
||||
if self.run_generic_build('text') > 0:
|
||||
return 1
|
||||
|
||||
@@ -565,8 +565,6 @@ help:
|
||||
\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 " 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 " man to make manual pages"
|
||||
\t@echo " texinfo to make Texinfo files"
|
||||
@@ -686,20 +684,6 @@ latexpdfja:
|
||||
\t$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
||||
\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
|
||||
text:
|
||||
\t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
\ifdefined\pdfpxdimen
|
||||
\let\sphinxpxdimen\pdfpxdimen\else\newdimen\sphinxpxdimen
|
||||
\fi \sphinxpxdimen=<%= pxunit %>\relax
|
||||
\usepackage{iftex}
|
||||
<%= passoptionstopackages %>
|
||||
<%= inputenc %>
|
||||
<%= utf8extra %>
|
||||
|
||||
@@ -13,11 +13,16 @@ LATEXOPTS =
|
||||
FMT = pdf
|
||||
|
||||
LATEX = latex
|
||||
PDFLATEX = pdflatex
|
||||
PDFLATEX = {{ latex_engine }}
|
||||
MAKEINDEX = makeindex
|
||||
|
||||
{% if latex_engine == 'platex' %}
|
||||
all: all-pdf-ja
|
||||
all-pdf: all-pdf-ja
|
||||
{% else %}
|
||||
all: $(ALLPDF)
|
||||
all-pdf: $(ALLPDF)
|
||||
{% endif -%}
|
||||
all-dvi: $(ALLDVI)
|
||||
all-ps: $(ALLPS)
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
%%
|
||||
%% This is file `iftex.sty',
|
||||
|
||||
%%
|
||||
%% __________________________________
|
||||
%% Copyright © 2010–2013 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 © 2010–2013 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'.
|
||||
@@ -265,9 +265,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
'classoptions': '',
|
||||
'extraclassoptions': '',
|
||||
'passoptionstopackages': '',
|
||||
'inputenc': ('\\ifPDFTeX\n'
|
||||
' \\usepackage[utf8]{inputenc}\n'
|
||||
'\\fi'),
|
||||
'inputenc': '',
|
||||
'utf8extra': ('\\ifdefined\\DeclareUnicodeCharacter\n'
|
||||
' \\DeclareUnicodeCharacter{00A0}{\\nobreakspace}\n'
|
||||
'\\fi'),
|
||||
@@ -358,6 +356,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
else:
|
||||
docclass = builder.config.latex_docclass.get('manual', 'report')
|
||||
self.elements['docclass'] = docclass
|
||||
if builder.config.latex_engine == 'pdflatex':
|
||||
self.elements['inputenc'] = '\\usepackage[utf8]{inputenc}'
|
||||
if builder.config.today:
|
||||
self.elements['date'] = builder.config.today
|
||||
else:
|
||||
|
||||
@@ -12,17 +12,24 @@ from __future__ import print_function
|
||||
|
||||
import os
|
||||
import re
|
||||
from itertools import product
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
from six import PY3
|
||||
|
||||
from sphinx.errors import SphinxError
|
||||
from sphinx.util.osutil import cd, ensuredir
|
||||
from sphinx.writers.latex import LaTeXTranslator
|
||||
|
||||
from util import SkipTest, remove_unicode_literals, with_app, strip_escseq
|
||||
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 + """\
|
||||
%(root)s/index.rst:\\d+: WARNING: unknown option: &option
|
||||
%(root)s/index.rst:\\d+: WARNING: citation not found: missing
|
||||
@@ -34,47 +41,10 @@ if PY3:
|
||||
LATEX_WARNINGS = remove_unicode_literals(LATEX_WARNINGS)
|
||||
|
||||
|
||||
def run_latex(outdir):
|
||||
"""Run pdflatex, xelatex, and lualatex in the outdir"""
|
||||
cwd = os.getcwd()
|
||||
os.chdir(outdir)
|
||||
# only run latex if all needed packages are there
|
||||
def kpsetest(*filenames):
|
||||
try:
|
||||
latexes = ('pdflatex', 'xelatex', 'lualatex')
|
||||
available_latexes = len(latexes)
|
||||
for latex in latexes:
|
||||
try:
|
||||
os.mkdir(latex)
|
||||
p = Popen([latex, '--interaction=nonstopmode',
|
||||
'-output-directory=%s' % latex, 'SphinxTests.tex'],
|
||||
stdout=PIPE, stderr=PIPE)
|
||||
except OSError: # most likely the latex executable was not found
|
||||
available_latexes -= 1
|
||||
else:
|
||||
stdout, stderr = p.communicate()
|
||||
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
|
||||
|
||||
|
||||
@with_app(buildername='latex')
|
||||
def test_latex(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)
|
||||
p = Popen(['kpsewhich'] + list(filenames), stdout=PIPE)
|
||||
except OSError:
|
||||
# no kpsewhich... either no tex distribution is installed or it is
|
||||
# a "strange" one -- don't bother running latex
|
||||
@@ -87,17 +57,42 @@ def test_latex(app, status, warning):
|
||||
# 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)
|
||||
|
||||
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')
|
||||
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
|
||||
app.builder.build_all()
|
||||
|
||||
# file from latex_additional_files
|
||||
assert (app.outdir / 'svgimg.svg').isfile()
|
||||
|
||||
# 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')
|
||||
@@ -126,48 +121,6 @@ def test_writer(app, status, warning):
|
||||
'\\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)
|
||||
def test_latex_warnings(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
Reference in New Issue
Block a user