1. SCons
  2. Core
  3. SCons

Commits

Steven Knight  committed 572f26c

Make all relevant builders of .tex and .ltx files consistent with respect to handling generating bibliographies and re-running LaTeX to resolve undefined references. (Joel B. Mohler) Add a $LATEXRETRIES to configure the number of undefined reference re-runs.

  • Participants
  • Parent commits c469185
  • Branches default

Comments (0)

Files changed (8)

File doc/man/scons.1

View file
 .IP LATEXFLAGS
 General options passed to the LaTeX structured formatter and typesetter.
 
+.IP LATEXRETRIES
+The maximum number of times that LaTeX
+will be re-run if the
+.B .log
+generated by the $LATEXCOM command
+indicates that there are undefined references.
+The default is to try to resolve undefined references
+by re-running LaTeX up to three times.
+
 .IP LATEXSUFFIXES
 The list of suffixes of files that will be scanned
 for LaTeX implicit dependencies

File src/CHANGES.txt

View file
   - Document the --debug=explain option in the man page.  (How did we
     miss this?)
 
+  - Add a $LATEXRETRIES variable to allow configuration of the number of
+    times LaTex can be re-called to try to resolve undefined references.
+
   From Chen Lee:
 
   - Handle Visual Studio project and solution files in Unicode.
     BCC compiler; some versions apparently require that the file name
     argument be concatenated with the option.
 
+  From Joel B. Mohler:
+
+  - Extend latex.py, pdflatex.py, pdftex.py and tex.py so that building
+    from both TeX and LaTeX files uses the same logic to call $BIBTEX
+    when it's necessary, to call $MAKEINDEX when it's necessary, and to
+    call $TEX or $LATEX multiple times to handle undefined references.
+
   From Elliot Murphy:
 
   - Enhance the tests to guarantee persistence of ListOption
     external environment, or settable internally) to put a shortened
     SCons execution line in the Visual Studio project file.
 
-  - Don't throw an exception if the configuration changes and a stored
-    implicit dependency now has a different type (File or Dir) than it
-    did last time.
-
   From Greg Ward:
 
   - Fix a misplaced line in the man page.

File src/engine/SCons/Tool/latex.py

View file
 import SCons.Scanner.LaTeX
 import SCons.Util
 import SCons.Tool
+import SCons.Tool.tex
 
 LaTeXAction = SCons.Action.Action('$LATEXCOM', '$LATEXCOMSTR')
 
+def LaTeXAuxFunction(target = None, source= None, env=None):
+    SCons.Tool.tex.InternalLaTeXAuxAction( LaTeXAction, target, source, env )
+
+LaTeXAuxAction = SCons.Action.Action(LaTeXAuxFunction, strfunction=None)
+
 def generate(env):
     """Add Builders and construction variables for LaTeX to an Environment."""
 
     except KeyError:
         bld = SCons.Defaults.DVI()
         env['BUILDERS']['DVI'] = bld
-        
-    for suffix in SCons.Tool.LaTeXSuffixes:
-        bld.add_action(suffix, LaTeXAction)
 
-    env['LATEX']      = 'latex'
-    env['LATEXFLAGS'] = SCons.Util.CLVar('')
-    env['LATEXCOM']   = '$LATEX $LATEXFLAGS $SOURCES'
+    bld.add_action('.ltx', LaTeXAuxAction)
+    bld.add_action('.latex', LaTeXAuxAction)
+
+    env['LATEX']        = 'latex'
+    env['LATEXFLAGS']   = SCons.Util.CLVar('')
+    env['LATEXCOM']     = '$LATEX $LATEXFLAGS $SOURCES'
+    env['LATEXRETRIES'] = 3
 
 def exists(env):
     return env.Detect('latex')

File src/engine/SCons/Tool/latex.xml

View file
 </summary>
 </cvar>
 
+<cvar name="LATEXRETRIES">
+<summary>
+The maximum number of times that LaTeX
+will be re-run if the
+<filename>.log</filename>
+generated by the &cv-LATEXCOM; command
+indicates that there are undefined references.
+The default is to try to resolve undefined references
+by re-running LaTeX up to three times.
+</summary>
+</cvar>
+
 <cvar name="TEXINPUTS">
 <summary>
 List of directories that the LaTeX programm will search

File src/engine/SCons/Tool/pdflatex.py

View file
 import SCons.Action
 import SCons.Defaults
 import SCons.Util
+import SCons.Tool.tex
 
 PDFLaTeXAction = SCons.Action.Action('$PDFLATEXCOM', '$PDFLATEXCOMSTR')
 
+def PDFLaTeXAuxFunction(target = None, source= None, env=None):
+    SCons.Tool.tex.InternalLaTeXAuxAction( PDFLaTeXAction, target, source, env )
+
+PDFLaTeXAuxAction = SCons.Action.Action(PDFLaTeXAuxFunction, strfunction=None)
+
 def generate(env):
     """Add Builders and construction variables for pdflatex to an Environment."""
     try:
         bld = SCons.Defaults.PDF()
         env['BUILDERS']['PDF'] = bld
 
-    bld.add_action('.ltx', PDFLaTeXAction)
-    bld.add_action('.latex', PDFLaTeXAction)
+    bld.add_action('.ltx', PDFLaTeXAuxAction)
+    bld.add_action('.latex', PDFLaTeXAuxAction)
 
     env['PDFLATEX']      = 'pdflatex'
     env['PDFLATEXFLAGS'] = SCons.Util.CLVar('')
     env['PDFLATEXCOM']   = '$PDFLATEX $PDFLATEXFLAGS $SOURCE'
+    env['LATEXRETRIES']  = 3
 
 def exists(env):
     return env.Detect('pdflatex')

File src/engine/SCons/Tool/pdftex.py

View file
 import SCons.Action
 import SCons.Defaults
 import SCons.Util
+import SCons.Tool.tex
 
 PDFTeXAction = SCons.Action.Action('$PDFTEXCOM', '$PDFTEXCOMSTR')
 
+# Define an action to build a latex file.  This action might be needed more
+# than once if we are dealing with labels and bibtex
+PDFLaTeXAction = SCons.Action.Action("$PDFLATEXCOM", "$PDFLATEXCOMSTR")
+
+def PDFLaTeXAuxAction(target = None, source= None, env=None):
+    SCons.Tool.tex.InternalLaTeXAuxAction( PDFLaTeXAction, target, source, env )
+
+def PDFTeXLaTeXFunction(target = None, source= None, env=None):
+    """A builder for TeX and LaTeX that scans the source file to
+    decide the "flavor" of the source and then executes the appropriate
+    program."""
+    if SCons.Tool.tex.is_LaTeX(source):
+        PDFLaTeXAuxAction(target,source,env)
+    else:
+        PDFTeXAction(target,source,env)
+    return 0
+
+PDFTeXLaTeXAction = SCons.Action.Action(PDFTeXLaTeXFunction,
+                                     strfunction=None)
+
 def generate(env):
     """Add Builders and construction variables for pdftex to an Environment."""
     try:
         bld = SCons.Defaults.PDF()
         env['BUILDERS']['PDF'] = bld
 
-    bld.add_action('.tex', PDFTeXAction)
+    bld.add_action('.tex', PDFTeXLaTeXAction)
 
     env['PDFTEX']      = 'pdftex'
     env['PDFTEXFLAGS'] = SCons.Util.CLVar('')
     env['PDFTEXCOM']   = '$PDFTEX $PDFTEXFLAGS $SOURCE'
 
+    # Duplicate from latex.py.  If latex.py goes away, then this is still OK.
+    env['PDFLATEX']      = 'pdflatex'
+    env['PDFLATEXFLAGS'] = SCons.Util.CLVar('')
+    env['PDFLATEXCOM']   = '$PDFLATEX $PDFLATEXFLAGS $SOURCES'
+    env['LATEXRETRIES']  = 3
+
+    env['BIBTEX']      = 'bibtex'
+    env['BIBTEXFLAGS'] = SCons.Util.CLVar('')
+    env['BIBTEXCOM']   = '$BIBTEX $BIBTEXFLAGS $SOURCES'
+
 def exists(env):
     return env.Detect('pdftex')

File src/engine/SCons/Tool/tex.py

View file
 import SCons.Node.FS
 import SCons.Util
 
-# Define an action to build a generic tex file.  This is sufficient for all 
+# Define an action to build a generic tex file.  This is sufficient for all
 # tex files.
 TeXAction = SCons.Action.Action("$TEXCOM", "$TEXCOMSTR")
 
 # Define an action to run MakeIndex on a file.
 MakeIndexAction = SCons.Action.Action("$MAKEINDEXCOM", "$MAKEINDEXOMSTR")
 
-def LaTeXAuxAction(target = None, source= None, env=None):
+def InternalLaTeXAuxAction(XXXLaTeXAction, 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."""
     # Get the base name of the target
     basename, ext = os.path.splitext(str(target[0]))
+
     # Run LaTeX once to generate a new aux file.
-    LaTeXAction(target,source,env)
-    # Now if bibtex will need to be run.
-    content = open(basename + ".aux","rb").read()
-    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"
+    XXXLaTeXAction(target,source,env)
+
+    # Decide if various things need to be run, or run again.  We check
+    # for the existence of files before opening them--even ones like the
+    # aux file that TeX always creates--to make it possible to write tests
+    # with stubs that don't necessarily generate all of the same files.
+
+    # Now decide if bibtex will need to be run.
+    auxfilename = basename + '.aux'
+    if os.path.exists(auxfilename):
+        content = open(auxfilename, "rb").read()
+        if string.find(content, "bibdata") != -1:
+            bibfile = env.fs.File(basename)
+            BibTeXAction(None,bibfile,env)
+
+    # Now decide 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()
-        if not re.search("^LaTeX Warning:.*Rerun",content,re.MULTILINE):
+
+    # Now decide if latex needs to be run yet again.
+    logfilename = basename + '.log'
+    for trial in range(int(env.subst('$LATEXRETRIES'))):
+        if not os.path.exists(logfilename):
             break
-        LaTeXAction(target,source,env)
+        content = open(logfilename, "rb").read()
+        if not re.search("^LaTeX Warning:.*Rerun",content,re.MULTILINE) and not re.search("^LaTeX Warning:.*undefined references",content,re.MULTILINE):
+            break
+        XXXLaTeXAction(target,source,env)
     return 0
 
+def LaTeXAuxAction(target = None, source= None, env=None):
+    InternalLaTeXAuxAction( LaTeXAction, target, source, env )
+
 LaTeX_re = re.compile("\\\\document(style|class)")
 
 def is_LaTeX(flist):
         TeXAction(target,source,env)
     return 0
 
-TeXLaTeXAction = SCons.Action.Action(TeXLaTeXFunction,
-                                     strfunction=None)
+TeXLaTeXAction = SCons.Action.Action(TeXLaTeXFunction, strfunction=None)
 
 def generate(env):
     """Add Builders and construction variables for TeX to an Environment."""
     except KeyError:
         bld = SCons.Defaults.DVI()
         env['BUILDERS']['DVI'] = bld
-        
+
     bld.add_action('.tex', TeXLaTeXAction)
 
     env['TEX']      = 'tex'
     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 $SOURCE'
+    env['LATEX']        = 'latex'
+    env['LATEXFLAGS']   = SCons.Util.CLVar('')
+    env['LATEXCOM']     = '$LATEX $LATEXFLAGS $SOURCE'
+    env['LATEXRETRIES'] = 3
 
     env['BIBTEX']      = 'bibtex'
     env['BIBTEXFLAGS'] = SCons.Util.CLVar('')
     env['MAKEINDEXFLAGS'] = SCons.Util.CLVar('')
     env['MAKEINDEXCOM']   = '$MAKEINDEX $MAKEINDEXFLAGS $SOURCES'
 
-    
 def exists(env):
     return env.Detect('tex')

File test/TEX/multi-run.py

View file
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Validate that both .tex and .ltx files can handle a LaTeX-style
+bibliography (by calling $BIBTEX to generate a .bbl file) and
+correctly re-run to resolve undefined references.
+"""
+
+import string
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+tex = test.where_is('tex')
+latex = test.where_is('latex')
+
+if not tex and not latex:
+    test.skip_test("Could not find tex or latex; skipping test(s).\n")
+
+test.subdir('work1', 'work2')
+
+
+input_file = r"""
+\documentclass{article}
+
+\begin{document}
+As stated in \cite{X}, this is a bug-a-boo.
+\bibliography{fooref}
+\bibliographystyle{plain}
+\end{document}
+"""
+
+bibfile = r"""
+@Article{X,
+  author = 	 "Mr. X",
+  title = 	 "A determination of bug-a-boo-ness",
+  journal =	 "Journal of B.a.B.",
+  year = 	 1920,
+  volume =	 62,
+  pages =	 291
+}
+"""
+
+if tex:
+
+    test.write(['work1', 'SConstruct'], """\
+DVI( "foo.tex" )
+PDF( "foo.tex" )
+""")
+
+    test.write(['work1', 'foo.tex'], input_file)
+    test.write(['work1', 'fooref.bib'], bibfile)
+
+    test.run(chdir = 'work1', arguments = '.')
+
+    test.must_exist(['work1', 'foo.bbl'])
+
+    foo_log = test.read(['work1', 'foo.log'])
+    if string.find(foo_log, 'undefined references') != -1:
+        print 'foo.log contains "undefined references":'
+        print foo_log
+        test.fail_test(1)
+
+if latex:
+
+    test.write(['work2', 'SConstruct'], """\
+DVI( "foo.ltx" )
+PDF( "foo.ltx" )
+""")
+
+    test.write(['work2', 'foo.ltx'], input_file)
+    test.write(['work2', 'fooref.bib'], bibfile)
+
+    test.run(chdir = 'work2', arguments = '.')
+
+    test.must_exist(['work2', 'foo.bbl'])
+
+    foo_log = test.read(['work2', 'foo.log'])
+    if string.find(foo_log, 'undefined references') != -1:
+        print 'foo.log contains "undefined references":'
+        print foo_log
+        test.fail_test(1)
+
+test.pass_test()