From 0bafd9ee21eee9f2f800a79062dd6590a2a51575 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 3 Jul 2016 18:14:19 +0900 Subject: [PATCH 1/5] Support multiple LaTeX engines through `latex_engine` --- CHANGES | 1 - sphinx/builders/latex.py | 25 ++++++++++++----------- sphinx/make_mode.py | 14 ------------- sphinx/quickstart.py | 16 --------------- sphinx/texinputs/{Makefile => Makefile_t} | 2 +- 5 files changed, 14 insertions(+), 44 deletions(-) rename sphinx/texinputs/{Makefile => Makefile_t} (98%) diff --git a/CHANGES b/CHANGES index 64ba439db..995370a77 100644 --- a/CHANGES +++ b/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 diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py index 5c41a8799..6b9c5b138 100644 --- a/sphinx/builders/latex.py +++ b/sphinx/builders/latex.py @@ -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), - path.join(self.outdir, dest)) + 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,8 @@ def setup(app): app.add_builder(LaTeXBuilder) app.connect('builder-inited', validate_config_values) + app.add_config_value('latex_engine', 'pdflatex', None, + ENUM('pdflatex', 'xelatex', 'lualatex')) app.add_config_value('latex_documents', lambda self: [(self.master_doc, make_filename(self.project) + '.tex', self.project, '', 'manual')], diff --git a/sphinx/make_mode.py b/sphinx/make_mode.py index 9a17895bd..3b7c29383 100644 --- a/sphinx/make_mode.py +++ b/sphinx/make_mode.py @@ -42,8 +42,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"), @@ -173,18 +171,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 diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 5aa47cb61..ae4d90ad3 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -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 diff --git a/sphinx/texinputs/Makefile b/sphinx/texinputs/Makefile_t similarity index 98% rename from sphinx/texinputs/Makefile rename to sphinx/texinputs/Makefile_t index c0676e3ee..ca66b554d 100644 --- a/sphinx/texinputs/Makefile +++ b/sphinx/texinputs/Makefile_t @@ -13,7 +13,7 @@ LATEXOPTS = FMT = pdf LATEX = latex -PDFLATEX = pdflatex +PDFLATEX = {{ latex_engine }} MAKEINDEX = makeindex all: $(ALLPDF) From b20b248913768532e42ff5c25a731377891104fe Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 13 Jul 2016 13:02:05 +0900 Subject: [PATCH 2/5] Use platex by default if language is "ja" --- sphinx/builders/latex.py | 6 ++++-- sphinx/texinputs/Makefile_t | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py index 6b9c5b138..34ac6d0c8 100644 --- a/sphinx/builders/latex.py +++ b/sphinx/builders/latex.py @@ -263,8 +263,10 @@ def setup(app): app.add_builder(LaTeXBuilder) app.connect('builder-inited', validate_config_values) - app.add_config_value('latex_engine', 'pdflatex', None, - ENUM('pdflatex', 'xelatex', 'lualatex')) + 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')], diff --git a/sphinx/texinputs/Makefile_t b/sphinx/texinputs/Makefile_t index ca66b554d..ffec3662c 100644 --- a/sphinx/texinputs/Makefile_t +++ b/sphinx/texinputs/Makefile_t @@ -16,8 +16,13 @@ LATEX = latex 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) From 1a575f4666b061e13a0f3b0a1e8698e2ba301b41 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 13 Jul 2016 13:25:12 +0900 Subject: [PATCH 3/5] Update CHANGES and docs --- CHANGES | 2 +- doc/config.rst | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 995370a77..597dd3ef1 100644 --- a/CHANGES +++ b/CHANGES @@ -76,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 ---------- diff --git a/doc/config.rst b/doc/config.rst index a71555974..a6cf19dce 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -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. From 6399f8703bcdfbf7b070505641acf2dfa00f4450 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 13 Jul 2016 21:15:16 +0900 Subject: [PATCH 4/5] Invoke latex building tests using latex_engine --- tests/test_build_latex.py | 141 +++++++++++++------------------------- 1 file changed, 47 insertions(+), 94 deletions(-) diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 67f945e4d..54ba5811b 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -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,70 +41,58 @@ 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) + 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 + return None + else: + p.communicate() + if p.returncode != 0: + # not found + return False + # found + return True - 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') -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 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 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() From ead753eaad7e0f73f106c4bda918f916b6d3638c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 13 Jul 2016 21:15:24 +0900 Subject: [PATCH 5/5] Replace iftex by latex_engine --- sphinx/templates/latex/content.tex_t | 1 - sphinx/texinputs/iftex.sty | 97 ---------------------------- sphinx/writers/latex.py | 6 +- 3 files changed, 3 insertions(+), 101 deletions(-) delete mode 100755 sphinx/texinputs/iftex.sty diff --git a/sphinx/templates/latex/content.tex_t b/sphinx/templates/latex/content.tex_t index fb5b1decd..cac910945 100644 --- a/sphinx/templates/latex/content.tex_t +++ b/sphinx/templates/latex/content.tex_t @@ -5,7 +5,6 @@ \ifdefined\pdfpxdimen \let\sphinxpxdimen\pdfpxdimen\else\newdimen\sphinxpxdimen \fi \sphinxpxdimen=<%= pxunit %>\relax -\usepackage{iftex} <%= passoptionstopackages %> <%= inputenc %> <%= utf8extra %> diff --git a/sphinx/texinputs/iftex.sty b/sphinx/texinputs/iftex.sty deleted file mode 100755 index 765146644..000000000 --- a/sphinx/texinputs/iftex.sty +++ /dev/null @@ -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 -%% -%% 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'. diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 85014ef2a..19eb3e195 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -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: