.B DVI
builder method will also examine the contents
of the
-.B .aux file
-and invoke the $BIBTEX command line
+.B .aux
+file and invoke the $BIBTEX command line
if the string
.B bibdata
is found,
+start $MAKEINDEX to generate an index if a
+.B .ind
+file is found
and will examine the contents
.B .log
file and re-run the $LATEXCOM command
.IP LATEXFLAGS
General options passed to the LaTeX structured formatter and typesetter.
+.IP LATEXSUFFIXES
+The list of suffixes of files that will be scanned
+for LaTeX implicit dependencies
+(\\include or \\import files).
+The default list is:
+
+.ES
+[".tex", ".ltx", ".latex"]
+.EE
+
.IP LDMODULE
The linker for building loadable modules.
By default, this is the same as $SHLINK.
a file is passed through the M4 macro preprocessor.
If this is not set, then $M4COM (the command line) is displayed.
+.IP MAKEINDEX
+The makeindex generator for the TeX formatter and typesetter and the
+LaTeX structured formatter and typesetter.
+
+.IP MAKEINDEXCOM
+The command line used to call the makeindex generator for the
+TeX formatter and typesetter and the LaTeX structured formatter and
+typesetter.
+
+.IP MAKEINDEXCOMSTR
+The string displayed when calling the makeindex generator for the
+TeX formatter and typesetter
+and the LaTeX structured formatter and typesetter.
+If this is not set, then $MAKEINDEXCOM (the command line) is displayed.
+
+.IP MAKEINDEXFLAGS
+General options passed to the makeindex generator for the TeX formatter
+and typesetter and the LaTeX structured formatter and typesetter.
+
.IP MAXLINELENGTH
The maximum number of characters allowed on an external command line.
On Win32 systems,
.IP TEXFLAGS
General options passed to the TeX formatter and typesetter.
+.IP TEXINPUTS
+List of directories that the LaTeX programm will search
+for include directories.
+The LaTeX implicit dependency scanner will search these
+directories for \include and \import files.
+
.IP TOOLS
A list of the names of the Tool specifications
that are part of this construction environment.
- Remove unneceesary (and incorrect) SCons.Util strings on some function
calls in SCons.Util.
+ From August Hörandl:
+
+ - Add a scanner for \include and \import files, with support for
+ searching a directory list in $TEXINPUTS (imported from the external
+ environment).
+
+ - Support $MAKEINDEX, $MAKEINDEXCOM, $MAKEINDEXCOMSTR and
+ $MAKEINDEXFLAGS for generating indices from .idx files.
+
From Stephen Kennedy:
- Speed up writing the .sconsign file at the end of a run by only
From Sanjoy Mahajan:
+ - Correct TeX-related command lines to just $SOURCE, not $SOURCES
+
- Fix a bad use of Copy() in an example in the man page, and a
bad regular expression example in the man page and User's Guide.
SCons/Scanner/Dir.py
SCons/Scanner/Fortran.py
SCons/Scanner/IDL.py
+SCons/Scanner/LaTeX.py
SCons/Scanner/Prog.py
SCons/SConf.py
SCons/SConsign.py
# transition period.
CScan = SCons.Tool.CScanner
DScan = SCons.Tool.DScanner
+LaTeXScan = SCons.Tool.LaTeXScanner
ObjSourceScan = SCons.Tool.SourceFileScanner
ProgScan = SCons.Tool.ProgramScanner
def DVI():
"""Common function to generate a DVI file Builder."""
return SCons.Builder.Builder(action = {},
+ source_scanner = LaTeXScan,
# The suffix is not configurable via a
# construction variable like $DVISUFFIX
# because the output file name is
def PDF():
"""A function for generating the PDF Builder."""
return SCons.Builder.Builder(action = { },
+ source_scanner = LaTeXScan,
prefix = '$PDFPREFIX',
suffix = '$PDFSUFFIX')
'CPPSUFFIXES' : SCons.Tool.CSuffixes,
'DSUFFIXES' : SCons.Tool.DSuffixes,
'IDLSUFFIXES' : SCons.Tool.IDLSuffixes,
+ 'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes,
'PDFPREFIX' : '',
'PDFSUFFIX' : '.pdf',
'PSPREFIX' : '',
builder method will also examine the contents
of the
<filename>.aux</filename>
-file
-and invoke the &cv-BIBTEX; command line
+file and invoke the &cv-BIBTEX; command line
if the string
<literal>bibdata</literal>
is found,
+start &cv-MAKEINDEX; to generate an index if a
+<filename>.ind</filename>
+file is found
and will examine the contents
<filename>.log</filename>
file and re-run the &cv-LATEXCOM; command
</summary>
</cvar>
+<cvar name="LATEXSUFFIXES">
+<summary>
+The list of suffixes of files that will be scanned
+for LaTeX implicit dependencies
+(<literal>\include</literal> or <literal>\import</literal> files).
+The default list is:
+
+<example>
+[".tex", ".ltx", ".latex"]
+</example>
+</summary>
+</cvar>
+
<cvar name="_LIBDIRFLAGS">
<summary>
An automatically-generated construction variable
--- /dev/null
+"""SCons.Scanner.LaTeX
+
+This module implements the dependency scanner for LaTeX code.
+
+"""
+
+#
+# Copyright (c) 2005 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = ""
+
+
+import SCons.Scanner
+
+def LaTeXScanner(fs = SCons.Node.FS.default_fs):
+ """Return a prototype Scanner instance for scanning LaTeX source files"""
+ ds = LaTeX(name = "LaTeXScanner",
+ suffixes = '$LATEXSUFFIXES',
+ path_variable = 'TEXINPUTS',
+ regex = '\\\\(include|input){([^}]*)}',
+ recursive = 0)
+ return ds
+
+class LaTeX(SCons.Scanner.Classic):
+ def find_include(self, include, source_dir, path):
+ if callable(path): path=path()
+ # find (2nd result reg expr) + extension
+ # print 'looking for latex includes: ' + include[1]
+ i = SCons.Node.FS.find_file(include[1] + '.tex',
+ (source_dir,) + path)
+ return i, include
--- /dev/null
+#
+# Copyright (c) 2005 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = ""
+
+import os.path
+import string
+import sys
+import types
+import unittest
+import UserDict
+
+import TestCmd
+import SCons.Node.FS
+import SCons.Scanner.LaTeX
+
+test = TestCmd.TestCmd(workdir = '')
+
+test.write('test1.latex',"""
+\include{inc1}
+\input{inc2}
+""")
+test.write('test2.latex',"""
+\include{inc1}
+\include{inc3}
+""")
+
+test.subdir('subdir')
+
+test.write('inc1.tex',"\n")
+test.write('inc2.tex',"\n")
+test.write([ 'subdir', 'inc3.tex'], "\n")
+
+# define some helpers:
+# copied from CTest.py
+class DummyEnvironment(UserDict.UserDict):
+ def __init__(self, **kw):
+ UserDict.UserDict.__init__(self)
+ self.data.update(kw)
+ self.fs = SCons.Node.FS.FS(test.workpath(''))
+
+ def Dictionary(self, *args):
+ return self.data
+
+ def subst(self, strSubst):
+ if strSubst[0] == '$':
+ return self.data[strSubst[1:]]
+ return strSubst
+
+ def subst_list(self, strSubst):
+ if strSubst[0] == '$':
+ return [self.data[strSubst[1:]]]
+ return [[strSubst]]
+
+ def subst_path(self, path, target=None, source=None):
+ if type(path) != type([]):
+ path = [path]
+ return map(self.subst, path)
+
+ def get_calculator(self):
+ return None
+
+ def get_factory(self, factory):
+ return factory or self.fs.File
+
+ def Dir(self, filename):
+ return self.fs.Dir(filename)
+
+ def File(self, filename):
+ return self.fs.File(filename)
+
+if os.path.normcase('foo') == os.path.normcase('FOO'):
+ my_normpath = os.path.normcase
+else:
+ my_normpath = os.path.normpath
+
+def deps_match(self, deps, headers):
+ global my_normpath
+ scanned = map(my_normpath, map(str, deps))
+ expect = map(my_normpath, headers)
+ self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned))
+
+
+class LaTeXScannerTestCase1(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment()
+ s = SCons.Scanner.LaTeX.LaTeXScanner()
+ path = s.path(env)
+ deps = s(env.File('test1.latex'), env, path)
+ headers = ['inc1.tex', 'inc2.tex']
+ deps_match(self, deps, headers)
+
+class LaTeXScannerTestCase2(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(TEXINPUTS=[test.workpath("subdir")])
+ s = SCons.Scanner.LaTeX.LaTeXScanner()
+ path = s.path(env)
+ deps = s(env.File('test2.latex'), env, path)
+ headers = ['inc1.tex', 'subdir/inc3.tex']
+ deps_match(self, deps, headers)
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(LaTeXScannerTestCase1())
+ suite.addTest(LaTeXScannerTestCase2())
+ return suite
+
+if __name__ == "__main__":
+ runner = unittest.TextTestRunner()
+ result = runner.run(suite())
+ if not result.wasSuccessful():
+ sys.exit(1)
+
import SCons.Scanner
import SCons.Scanner.C
import SCons.Scanner.D
+import SCons.Scanner.LaTeX
import SCons.Scanner.Prog
CScanner = SCons.Scanner.C.CScanner()
DScanner = SCons.Scanner.D.DScanner()
+LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner()
ProgramScanner = SCons.Scanner.Prog.ProgramScanner()
SourceFileScanner = SCons.Scanner.Scanner({}, name='SourceFileScanner')
IDLSuffixes = [".idl", ".IDL"]
+LaTeXSuffixes = [".tex", ".ltx", ".latex"]
+
for suffix in CSuffixes:
SourceFileScanner.add_scanner(suffix, CScanner)
for suffix in DSuffixes:
SourceFileScanner.add_scanner(suffix, DScanner)
+for suffix in LaTeXSuffixes:
+ SourceFileScanner.add_scanner(suffix, LaTeXScanner)
+
class Tool:
def __init__(self, name, toolpath=[], **kw):
self.name = name
env['DVIPS'] = 'dvips'
env['DVIPSFLAGS'] = SCons.Util.CLVar('')
- env['PSCOM'] = '$DVIPS $DVIPSFLAGS -o $TARGET $SOURCES'
+ env['PSCOM'] = '$DVIPS $DVIPSFLAGS -o $TARGET $SOURCE'
def exists(env):
return env.Detect('dvips')
import SCons.Action
import SCons.Defaults
+import SCons.Scanner.LaTeX
import SCons.Util
+import SCons.Tool
LaTeXAction = SCons.Action.Action('$LATEXCOM', '$LATEXCOMSTR')
bld = SCons.Defaults.DVI()
env['BUILDERS']['DVI'] = bld
- bld.add_action('.ltx', LaTeXAction)
- bld.add_action('.latex', LaTeXAction)
+ for suffix in SCons.Tool.LaTeXSuffixes:
+ bld.add_action(suffix, LaTeXAction)
env['LATEX'] = 'latex'
env['LATEXFLAGS'] = SCons.Util.CLVar('')
General options passed to the LaTeX structured formatter and typesetter.
</summary>
</cvar>
+
+<cvar name="TEXINPUTS">
+<summary>
+List of directories that the LaTeX programm will search
+for include directories.
+The LaTeX implicit dependency scanner will search these
+directories for \include and \import files.
+</summary>
+</cvar>
+
env['PDFLATEX'] = 'pdflatex'
env['PDFLATEXFLAGS'] = SCons.Util.CLVar('')
- env['PDFLATEXCOM'] = '$PDFLATEX $PDFLATEXFLAGS $SOURCES $TARGET'
+ env['PDFLATEXCOM'] = '$PDFLATEX $PDFLATEXFLAGS $SOURCE'
def exists(env):
return env.Detect('pdflatex')
env['PDFTEX'] = 'pdftex'
env['PDFTEXFLAGS'] = SCons.Util.CLVar('')
- env['PDFTEXCOM'] = '$PDFTEX $PDFTEXFLAGS $SOURCES $TARGET'
+ env['PDFTEXCOM'] = '$PDFTEX $PDFTEXFLAGS $SOURCE'
def exists(env):
return env.Detect('pdftex')
# Define an action to run BibTeX on a file.
BibTeXAction = SCons.Action.Action("$BIBTEXCOM", "$BIBTEXCOMSTR")
+# Define an action to run MakeIndex on a file.
+MakeIndexAction = SCons.Action.Action("$MAKEINDEXCOM", "$MAKEINDEXOMSTR")
+
def LaTeXAuxAction(target = None, source= None, env=None):
"""A builder for LaTeX files that checks the output in the aux file
and decides how many times to use LaTeXAction, and BibTeXAction."""
if string.find(content, "bibdata") != -1:
bibfile = env.fs.File(basename)
BibTeXAction(None,bibfile,env)
+ # Now if makeindex will need to be run.
+ idxfilename = basename + ".idx"
+ if os.path.exists(idxfilename):
+ idxfile = env.fs.File(basename)
+ # TODO: if ( idxfile has changed) ...
+ MakeIndexAction(None,idxfile,env)
+ LaTeXAction(target,source,env)
+
# Now check if latex needs to be run yet again.
for trial in range(3):
content = open(basename + ".log","rb").read()
env['TEX'] = 'tex'
env['TEXFLAGS'] = SCons.Util.CLVar('')
- env['TEXCOM'] = '$TEX $TEXFLAGS $SOURCES'
+ env['TEXCOM'] = '$TEX $TEXFLAGS $SOURCE'
# Duplicate from latex.py. If latex.py goes away, then this is still OK.
env['LATEX'] = 'latex'
env['LATEXFLAGS'] = SCons.Util.CLVar('')
- env['LATEXCOM'] = '$LATEX $LATEXFLAGS $SOURCES'
+ env['LATEXCOM'] = '$LATEX $LATEXFLAGS $SOURCE'
env['BIBTEX'] = 'bibtex'
env['BIBTEXFLAGS'] = SCons.Util.CLVar('')
- env['BIBTEXCOM'] = '$BIBTEX $BIBTEXFLAGS $SOURCES'
+ env['BIBTEXCOM'] = '$BIBTEX $BIBTEXFLAGS $SOURCE'
+ env['MAKEINDEX'] = 'makeindex'
+ env['MAKEINDEXFLAGS'] = SCons.Util.CLVar('')
+ env['MAKEINDEXCOM'] = '$MAKEINDEX $MAKEINDEXFLAGS $SOURCES'
+
def exists(env):
return env.Detect('tex')
</summary>
</cvar>
+<cvar name="MAKEINDEX">
+<summary>
+The makeindex generator for the TeX formatter and typesetter and the
+LaTeX structured formatter and typesetter.
+</summary>
+</cvar>
+
+<cvar name="MAKEINDEXCOM">
+<summary>
+The command line used to call the makeindex generator for the
+TeX formatter and typesetter and the LaTeX structured formatter and
+typesetter.
+</summary>
+</cvar>
+
+<cvar name="MAKEINDEXCOMSTR">
+<summary>
+The string displayed when calling the makeindex generator for the
+TeX formatter and typesetter
+and the LaTeX structured formatter and typesetter.
+If this is not set, then &cv-MAKEINDEXCOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="MAKEINDEXFLAGS">
+<summary>
+General options passed to the makeindex generator for the TeX formatter
+and typesetter and the LaTeX structured formatter and typesetter.
+</summary>
+</cvar>
+
<cvar name="TEX">
<summary>
The TeX formatter and typesetter.
test.write('SConstruct', """
import os
-ENV = { 'PATH' : os.environ['PATH'] }
+ENV = { 'PATH' : os.environ['PATH'],
+ 'TEXINPUTS' : [ 'subdir', os.environ.get('TEXINPUTS', '') ] }
foo = Environment(ENV = ENV)
latex = foo.Dictionary('LATEX')
-bar = Environment(ENV = ENV, LATEX = r'%s wrapper.py ' + latex)
+makeindex = foo.Dictionary('MAKEINDEX')
+bar = Environment(ENV = ENV,
+ LATEX = r'%s wrapper.py ' + latex,
+ MAKEINDEX = r' wrapper.py ' + makeindex)
foo.DVI(target = 'foo.dvi', source = 'foo.ltx')
bar.DVI(target = 'bar', source = 'bar.latex')
+
+bar.DVI(target = 'makeindex', source = 'makeindex.tex')
+foo.DVI(target = 'latexi', source = 'latexi.tex')
""" % python)
latex = r"""
\begin{document}
This is the %s LaTeX file.
\end{document}
+"""
+
+ makeindex = r"""
+\documentclass{letter}
+\usepackage{makeidx}
+\makeindex
+\begin{document}
+\index{info}
+This is the %s LaTeX file.
+\printindex{}
+\end{document}
+"""
+
+ latex1 = r"""
+\documentclass{letter}
+\usepackage{makeidx}
+\makeindex
+\begin{document}
+\index{info}
+This is the %s LaTeX file.
+
+It has an Index and includes another file.
+\include{latexincludefile}
+\end{document}
+"""
+
+ latex2 = r"""
+\index{include}
+This is the include file.
+\printindex{}
"""
test.write('foo.ltx', latex % 'foo.ltx')
test.write('bar.latex', latex % 'bar.latex')
- test.run(arguments = 'foo.dvi', stderr = None)
+ test.write('makeindex.tex', makeindex % 'makeindex.tex');
+ test.write('makeindex.idx', '');
- test.fail_test(os.path.exists(test.workpath('wrapper.out')))
+ test.subdir('subdir')
+ test.write('latexi.tex', latex1 % 'latexi.tex');
+ test.write([ 'subdir', 'latexincludefile.tex'], latex2)
+ test.run(arguments = 'foo.dvi', stderr = None)
+ test.fail_test(os.path.exists(test.workpath('wrapper.out')))
test.fail_test(not os.path.exists(test.workpath('foo.dvi')))
test.run(arguments = 'bar.dvi', stderr = None)
+ test.fail_test(test.read('wrapper.out') != "wrapper.py\n")
+ test.fail_test(not os.path.exists(test.workpath('bar.dvi')))
+ test.run(arguments = 'makeindex.dvi', stderr = None)
test.fail_test(test.read('wrapper.out') != "wrapper.py\n")
- test.fail_test(not os.path.exists(test.workpath('bar.dvi')))
+ test.run(arguments = 'latexi.dvi', stderr = None)
+ test.fail_test(not os.path.exists(test.workpath('latexi.dvi')))
+ test.fail_test(not os.path.exists(test.workpath('latexi.ind')))
+
+
test.pass_test()