Issue 2401: Fix usage of comparison with None, patch from Jared Grubb
[scons.git] / src / engine / SCons / Tool / tex.py
1 """SCons.Tool.tex
2
3 Tool-specific initialization for TeX.
4
5 There normally shouldn't be any need to import this module directly.
6 It will usually be imported through the generic SCons.Tool.Tool()
7 selection method.
8
9 """
10
11 #
12 # __COPYRIGHT__
13 #
14 # Permission is hereby granted, free of charge, to any person obtaining
15 # a copy of this software and associated documentation files (the
16 # "Software"), to deal in the Software without restriction, including
17 # without limitation the rights to use, copy, modify, merge, publish,
18 # distribute, sublicense, and/or sell copies of the Software, and to
19 # permit persons to whom the Software is furnished to do so, subject to
20 # the following conditions:
21 #
22 # The above copyright notice and this permission notice shall be included
23 # in all copies or substantial portions of the Software.
24 #
25 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
26 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
27 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 #
33
34 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
35
36 import os.path
37 import re
38 import string
39 import shutil
40
41 import SCons.Action
42 import SCons.Node
43 import SCons.Node.FS
44 import SCons.Util
45 import SCons.Scanner.LaTeX
46
47 Verbose = False
48
49 must_rerun_latex = True
50
51 # these are files that just need to be checked for changes and then rerun latex
52 check_suffixes = ['.toc', '.lof', '.lot', '.out', '.nav', '.snm']
53
54 # these are files that require bibtex or makeindex to be run when they change
55 all_suffixes = check_suffixes + ['.bbl', '.idx', '.nlo', '.glo']
56
57 #
58 # regular expressions used to search for Latex features
59 # or outputs that require rerunning latex
60 #
61 # search for all .aux files opened by latex (recorded in the .log file)
62 openout_aux_re = re.compile(r"\\openout.*`(.*\.aux)'")
63
64 #printindex_re = re.compile(r"^[^%]*\\printindex", re.MULTILINE)
65 #printnomenclature_re = re.compile(r"^[^%]*\\printnomenclature", re.MULTILINE)
66 #printglossary_re = re.compile(r"^[^%]*\\printglossary", re.MULTILINE)
67
68 # search to find rerun warnings
69 warning_rerun_str = '(^LaTeX Warning:.*Rerun)|(^Package \w+ Warning:.*Rerun)'
70 warning_rerun_re = re.compile(warning_rerun_str, re.MULTILINE)
71
72 # search to find citation rerun warnings
73 rerun_citations_str = "^LaTeX Warning:.*\n.*Rerun to get citations correct"
74 rerun_citations_re = re.compile(rerun_citations_str, re.MULTILINE)
75
76 # search to find undefined references or citations warnings
77 undefined_references_str = '(^LaTeX Warning:.*undefined references)|(^Package \w+ Warning:.*undefined citations)'
78 undefined_references_re = re.compile(undefined_references_str, re.MULTILINE)
79
80 # used by the emitter
81 auxfile_re = re.compile(r".", re.MULTILINE)
82 tableofcontents_re = re.compile(r"^[^%\n]*\\tableofcontents", re.MULTILINE)
83 makeindex_re = re.compile(r"^[^%\n]*\\makeindex", re.MULTILINE)
84 bibliography_re = re.compile(r"^[^%\n]*\\bibliography", re.MULTILINE)
85 listoffigures_re = re.compile(r"^[^%\n]*\\listoffigures", re.MULTILINE)
86 listoftables_re = re.compile(r"^[^%\n]*\\listoftables", re.MULTILINE)
87 hyperref_re = re.compile(r"^[^%\n]*\\usepackage.*\{hyperref\}", re.MULTILINE)
88 makenomenclature_re = re.compile(r"^[^%\n]*\\makenomenclature", re.MULTILINE)
89 makeglossary_re = re.compile(r"^[^%\n]*\\makeglossary", re.MULTILINE)
90 beamer_re = re.compile(r"^[^%\n]*\\documentclass\{beamer\}", re.MULTILINE)
91
92 # search to find all files included by Latex
93 include_re = re.compile(r'^[^%\n]*\\(?:include|input){([^}]*)}', re.MULTILINE)
94
95 # search to find all graphics files included by Latex
96 includegraphics_re = re.compile(r'^[^%\n]*\\(?:includegraphics(?:\[[^\]]+\])?){([^}]*)}', re.MULTILINE)
97
98 # search to find all files opened by Latex (recorded in .log file)
99 openout_re = re.compile(r"\\openout.*`(.*)'")
100
101 # list of graphics file extensions for TeX and LaTeX
102 TexGraphics   = SCons.Scanner.LaTeX.TexGraphics
103 LatexGraphics = SCons.Scanner.LaTeX.LatexGraphics
104
105 # An Action sufficient to build any generic tex file.
106 TeXAction = None
107
108 # An action to build a latex file.  This action might be needed more
109 # than once if we are dealing with labels and bibtex.
110 LaTeXAction = None
111
112 # An action to run BibTeX on a file.
113 BibTeXAction = None
114
115 # An action to run MakeIndex on a file.
116 MakeIndexAction = None
117
118 # An action to run MakeIndex (for nomencl) on a file.
119 MakeNclAction = None
120
121 # An action to run MakeIndex (for glossary) on a file.
122 MakeGlossaryAction = None
123
124 # Used as a return value of modify_env_var if the variable is not set.
125 _null = SCons.Scanner.LaTeX._null
126
127 modify_env_var = SCons.Scanner.LaTeX.modify_env_var
128
129 def FindFile(name,suffixes,paths,env,requireExt=False):
130     if requireExt:
131         name,ext = SCons.Util.splitext(name)
132         # if the user gave an extension use it.
133         if ext:
134             name = name + ext
135     if Verbose:
136         print " searching for '%s' with extensions: " % name,suffixes
137
138     for path in paths:
139         testName = os.path.join(path,name)
140         if Verbose:
141             print " look for '%s'" % testName
142         if os.path.exists(testName):
143             if Verbose:
144                 print " found '%s'" % testName
145             return env.fs.File(testName)
146         else:
147             name_ext = SCons.Util.splitext(testName)[1]
148             if name_ext:
149                 continue
150
151             # if no suffix try adding those passed in
152             for suffix in suffixes:
153                 testNameExt = testName + suffix
154                 if Verbose:
155                     print " look for '%s'" % testNameExt
156
157                 if os.path.exists(testNameExt):
158                     if Verbose:
159                         print " found '%s'" % testNameExt
160                     return env.fs.File(testNameExt)
161     if Verbose:
162         print " did not find '%s'" % name
163     return None
164
165 def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None):
166     """A builder for LaTeX files that checks the output in the aux file
167     and decides how many times to use LaTeXAction, and BibTeXAction."""
168
169     global must_rerun_latex
170
171     # This routine is called with two actions. In this file for DVI builds
172     # with LaTeXAction and from the pdflatex.py with PDFLaTeXAction
173     # set this up now for the case where the user requests a different extension
174     # for the target filename
175     if (XXXLaTeXAction == LaTeXAction):
176        callerSuffix = ".dvi"
177     else:
178        callerSuffix = env['PDFSUFFIX']
179
180     basename = SCons.Util.splitext(str(source[0]))[0]
181     basedir = os.path.split(str(source[0]))[0]
182     basefile = os.path.split(str(basename))[1]
183     abspath = os.path.abspath(basedir)
184
185     targetext = os.path.splitext(str(target[0]))[1]
186     targetdir = os.path.split(str(target[0]))[0]
187
188     saved_env = {}
189     for var in SCons.Scanner.LaTeX.LaTeX.env_variables:
190         saved_env[var] = modify_env_var(env, var, abspath)
191
192     # Create base file names with the target directory since the auxiliary files
193     # will be made there.   That's because the *COM variables have the cd
194     # command in the prolog. We check
195     # for the existence of files before opening them--even ones like the
196     # aux file that TeX always creates--to make it possible to write tests
197     # with stubs that don't necessarily generate all of the same files.
198
199     targetbase = os.path.join(targetdir, basefile)
200
201     # if there is a \makeindex there will be a .idx and thus
202     # we have to run makeindex at least once to keep the build
203     # happy even if there is no index.
204     # Same for glossaries and nomenclature
205     src_content = source[0].get_text_contents()
206     run_makeindex = makeindex_re.search(src_content) and not os.path.exists(targetbase + '.idx')
207     run_nomenclature = makenomenclature_re.search(src_content) and not os.path.exists(targetbase + '.nlo')
208     run_glossary = makeglossary_re.search(src_content) and not os.path.exists(targetbase + '.glo')
209
210     saved_hashes = {}
211     suffix_nodes = {}
212
213     for suffix in all_suffixes:
214         theNode = env.fs.File(targetbase + suffix)
215         suffix_nodes[suffix] = theNode
216         saved_hashes[suffix] = theNode.get_csig()
217
218     if Verbose:
219         print "hashes: ",saved_hashes
220
221     must_rerun_latex = True
222
223     #
224     # routine to update MD5 hash and compare
225     #
226     # TODO(1.5):  nested scopes
227     def check_MD5(filenode, suffix, saved_hashes=saved_hashes, targetbase=targetbase):
228         global must_rerun_latex
229         # two calls to clear old csig
230         filenode.clear_memoized_values()
231         filenode.ninfo = filenode.new_ninfo()
232         new_md5 = filenode.get_csig()
233
234         if saved_hashes[suffix] == new_md5:
235             if Verbose:
236                 print "file %s not changed" % (targetbase+suffix)
237             return False        # unchanged
238         saved_hashes[suffix] = new_md5
239         must_rerun_latex = True
240         if Verbose:
241             print "file %s changed, rerunning Latex, new hash = " % (targetbase+suffix), new_md5
242         return True     # changed
243
244     # generate the file name that latex will generate
245     resultfilename = targetbase + callerSuffix
246
247     count = 0
248
249     while (must_rerun_latex and count < int(env.subst('$LATEXRETRIES'))) :
250         result = XXXLaTeXAction(target, source, env)
251         if result != 0:
252             return result
253
254         count = count + 1
255
256         must_rerun_latex = False
257         # Decide if various things need to be run, or run again.
258
259         # Read the log file to find all .aux files
260         logfilename = targetbase + '.log'
261         logContent = ''
262         auxfiles = []
263         if os.path.exists(logfilename):
264             logContent = open(logfilename, "rb").read()
265             auxfiles = openout_aux_re.findall(logContent)
266
267         # Now decide if bibtex will need to be run.
268         # The information that bibtex reads from the .aux file is
269         # pass-independent. If we find (below) that the .bbl file is unchanged,
270         # then the last latex saw a correct bibliography.
271         # Therefore only do this on the first pass
272         if count == 1:
273             for auxfilename in auxfiles:
274                 target_aux = os.path.join(targetdir, auxfilename)
275                 if os.path.exists(target_aux):
276                     content = open(target_aux, "rb").read()
277                     if string.find(content, "bibdata") != -1:
278                         if Verbose:
279                             print "Need to run bibtex"
280                         bibfile = env.fs.File(targetbase)
281                         result = BibTeXAction(bibfile, bibfile, env)
282                         if result != 0:
283                             return result
284                         must_rerun_latex = check_MD5(suffix_nodes['.bbl'],'.bbl')
285                         break
286
287         # Now decide if latex will need to be run again due to index.
288         if check_MD5(suffix_nodes['.idx'],'.idx') or (count == 1 and run_makeindex):
289             # We must run makeindex
290             if Verbose:
291                 print "Need to run makeindex"
292             idxfile = suffix_nodes['.idx']
293             result = MakeIndexAction(idxfile, idxfile, env)
294             if result != 0:
295                 return result
296
297         # TO-DO: need to add a way for the user to extend this list for whatever
298         # auxiliary files they create in other (or their own) packages
299         # Harder is case is where an action needs to be called -- that should be rare (I hope?)
300
301         for index in check_suffixes:
302             check_MD5(suffix_nodes[index],index)
303
304         # Now decide if latex will need to be run again due to nomenclature.
305         if check_MD5(suffix_nodes['.nlo'],'.nlo') or (count == 1 and run_nomenclature):
306             # We must run makeindex
307             if Verbose:
308                 print "Need to run makeindex for nomenclature"
309             nclfile = suffix_nodes['.nlo']
310             result = MakeNclAction(nclfile, nclfile, env)
311             if result != 0:
312                 return result
313
314         # Now decide if latex will need to be run again due to glossary.
315         if check_MD5(suffix_nodes['.glo'],'.glo') or (count == 1 and run_glossary):
316             # We must run makeindex
317             if Verbose:
318                 print "Need to run makeindex for glossary"
319             glofile = suffix_nodes['.glo']
320             result = MakeGlossaryAction(glofile, glofile, env)
321             if result != 0:
322                 return result
323
324         # Now decide if latex needs to be run yet again to resolve warnings.
325         if warning_rerun_re.search(logContent):
326             must_rerun_latex = True
327             if Verbose:
328                 print "rerun Latex due to latex or package rerun warning"
329
330         if rerun_citations_re.search(logContent):
331             must_rerun_latex = True
332             if Verbose:
333                 print "rerun Latex due to 'Rerun to get citations correct' warning"
334
335         if undefined_references_re.search(logContent):
336             must_rerun_latex = True
337             if Verbose:
338                 print "rerun Latex due to undefined references or citations"
339
340         if (count >= int(env.subst('$LATEXRETRIES')) and must_rerun_latex):
341             print "reached max number of retries on Latex ,",int(env.subst('$LATEXRETRIES'))
342 # end of while loop
343
344     # rename Latex's output to what the target name is
345     if not (str(target[0]) == resultfilename  and  os.path.exists(resultfilename)):
346         if os.path.exists(resultfilename):
347             print "move %s to %s" % (resultfilename, str(target[0]), )
348             shutil.move(resultfilename,str(target[0]))
349
350     # Original comment (when TEXPICTS was not restored):
351     # The TEXPICTS enviroment variable is needed by a dvi -> pdf step
352     # later on Mac OSX so leave it
353     #
354     # It is also used when searching for pictures (implicit dependencies).
355     # Why not set the variable again in the respective builder instead
356     # of leaving local modifications in the environment? What if multiple
357     # latex builds in different directories need different TEXPICTS?
358     for var in SCons.Scanner.LaTeX.LaTeX.env_variables:
359         if var == 'TEXPICTS':
360             continue
361         if saved_env[var] is _null:
362             try:
363                 del env['ENV'][var]
364             except KeyError:
365                 pass # was never set
366         else:
367             env['ENV'][var] = saved_env[var]
368
369     return result
370
371 def LaTeXAuxAction(target = None, source= None, env=None):
372     result = InternalLaTeXAuxAction( LaTeXAction, target, source, env )
373     return result
374
375 LaTeX_re = re.compile("\\\\document(style|class)")
376
377 def is_LaTeX(flist):
378     # Scan a file list to decide if it's TeX- or LaTeX-flavored.
379     for f in flist:
380         content = f.get_text_contents()
381         if LaTeX_re.search(content):
382             return 1
383     return 0
384
385 def TeXLaTeXFunction(target = None, source= None, env=None):
386     """A builder for TeX and LaTeX that scans the source file to
387     decide the "flavor" of the source and then executes the appropriate
388     program."""
389     if is_LaTeX(source):
390         result = LaTeXAuxAction(target,source,env)
391     else:
392         result = TeXAction(target,source,env)
393     return result
394
395 def TeXLaTeXStrFunction(target = None, source= None, env=None):
396     """A strfunction for TeX and LaTeX that scans the source file to
397     decide the "flavor" of the source and then returns the appropriate
398     command string."""
399     if env.GetOption("no_exec"):
400         if is_LaTeX(source):
401             result = env.subst('$LATEXCOM',0,target,source)+" ..."
402         else:
403             result = env.subst("$TEXCOM",0,target,source)+" ..."
404     else:
405         result = ''
406     return result
407
408 def tex_eps_emitter(target, source, env):
409     """An emitter for TeX and LaTeX sources when
410     executing tex or latex. It will accept .ps and .eps
411     graphics files
412     """
413     (target, source) = tex_emitter_core(target, source, env, TexGraphics)
414
415     return (target, source)
416
417 def tex_pdf_emitter(target, source, env):
418     """An emitter for TeX and LaTeX sources when
419     executing pdftex or pdflatex. It will accept graphics
420     files of types .pdf, .jpg, .png, .gif, and .tif
421     """
422     (target, source) = tex_emitter_core(target, source, env, LatexGraphics)
423
424     return (target, source)
425
426 def ScanFiles(theFile, target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir):
427     # for theFile (a Node) update any file_tests and search for graphics files
428     # then find all included files and call ScanFiles for each of them
429     content = theFile.get_text_contents()
430     if Verbose:
431         print " scanning ",str(theFile)
432
433     for i in range(len(file_tests_search)):
434         if file_tests[i][0] is None:
435             file_tests[i][0] = file_tests_search[i].search(content)
436
437     # recursively call this on each of the included files
438     inc_files = [ ]
439     inc_files.extend( include_re.findall(content) )
440     if Verbose:
441         print "files included by '%s': "%str(theFile),inc_files
442     # inc_files is list of file names as given. need to find them
443     # using TEXINPUTS paths.
444
445     for src in inc_files:
446         srcNode = srcNode = FindFile(src,['.tex','.ltx','.latex'],paths,env,requireExt=False)
447         if srcNode is not None:
448             file_test = ScanFiles(srcNode, target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir)
449     if Verbose:
450         print " done scanning ",str(theFile)
451     return file_tests
452
453 def tex_emitter_core(target, source, env, graphics_extensions):
454     """An emitter for TeX and LaTeX sources.
455     For LaTeX sources we try and find the common created files that
456     are needed on subsequent runs of latex to finish tables of contents,
457     bibliographies, indices, lists of figures, and hyperlink references.
458     """
459     targetbase = SCons.Util.splitext(str(target[0]))[0]
460     basename = SCons.Util.splitext(str(source[0]))[0]
461     basefile = os.path.split(str(basename))[1]
462
463     basedir = os.path.split(str(source[0]))[0]
464     targetdir = os.path.split(str(target[0]))[0]
465     abspath = os.path.abspath(basedir)
466     target[0].attributes.path = abspath
467
468     #
469     # file names we will make use of in searching the sources and log file
470     #
471     emit_suffixes = ['.aux', '.log', '.ilg', '.blg', '.nls', '.nlg', '.gls', '.glg'] + all_suffixes
472     auxfilename = targetbase + '.aux'
473     logfilename = targetbase + '.log'
474
475     env.SideEffect(auxfilename,target[0])
476     env.SideEffect(logfilename,target[0])
477     env.Clean(target[0],auxfilename)
478     env.Clean(target[0],logfilename)
479
480     content = source[0].get_text_contents()
481
482     idx_exists = os.path.exists(targetbase + '.idx')
483     nlo_exists = os.path.exists(targetbase + '.nlo')
484     glo_exists = os.path.exists(targetbase + '.glo')
485
486     # set up list with the regular expressions
487     # we use to find features used
488     file_tests_search = [auxfile_re,
489                          makeindex_re,
490                          bibliography_re,
491                          tableofcontents_re,
492                          listoffigures_re,
493                          listoftables_re,
494                          hyperref_re,
495                          makenomenclature_re,
496                          makeglossary_re,
497                          beamer_re ]
498     # set up list with the file suffixes that need emitting
499     # when a feature is found
500     file_tests_suff = [['.aux'],
501                   ['.idx', '.ind', '.ilg'],
502                   ['.bbl', '.blg'],
503                   ['.toc'],
504                   ['.lof'],
505                   ['.lot'],
506                   ['.out'],
507                   ['.nlo', '.nls', '.nlg'],
508                   ['.glo', '.gls', '.glg'],
509                   ['.nav', '.snm', '.out', '.toc'] ]
510     # build the list of lists
511     file_tests = []
512     for i in range(len(file_tests_search)):
513         file_tests.append( [None, file_tests_suff[i]] )
514
515     # TO-DO: need to add a way for the user to extend this list for whatever
516     # auxiliary files they create in other (or their own) packages
517
518     # get path list from both env['TEXINPUTS'] and env['ENV']['TEXINPUTS']
519     savedpath = modify_env_var(env, 'TEXINPUTS', abspath)
520     paths = env['ENV']['TEXINPUTS']
521     if SCons.Util.is_List(paths):
522         pass
523     else:
524         # Split at os.pathsep to convert into absolute path
525         # TODO(1.5)
526         #paths = paths.split(os.pathsep)
527         paths = string.split(paths, os.pathsep)
528
529     # now that we have the path list restore the env
530     if savedpath is _null:
531         try:
532             del env['ENV']['TEXINPUTS']
533         except KeyError:
534             pass # was never set
535     else:
536         env['ENV']['TEXINPUTS'] = savedpath
537     if Verbose:
538         print "search path ",paths
539
540     file_tests = ScanFiles(source[0], target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir)
541
542     for (theSearch,suffix_list) in file_tests:
543         if theSearch:
544             for suffix in suffix_list:
545                 env.SideEffect(targetbase + suffix,target[0])
546                 env.Clean(target[0],targetbase + suffix)
547
548     # read log file to get all other files that latex creates and will read on the next pass
549     if os.path.exists(logfilename):
550         content = open(logfilename, "rb").read()
551         out_files = openout_re.findall(content)
552         env.SideEffect(out_files,target[0])
553         env.Clean(target[0],out_files)
554
555     return (target, source)
556
557
558 TeXLaTeXAction = None
559
560 def generate(env):
561     """Add Builders and construction variables for TeX to an Environment."""
562
563     # A generic tex file Action, sufficient for all tex files.
564     global TeXAction
565     if TeXAction is None:
566         TeXAction = SCons.Action.Action("$TEXCOM", "$TEXCOMSTR")
567
568     # An Action to build a latex file.  This might be needed more
569     # than once if we are dealing with labels and bibtex.
570     global LaTeXAction
571     if LaTeXAction is None:
572         LaTeXAction = SCons.Action.Action("$LATEXCOM", "$LATEXCOMSTR")
573
574     # Define an action to run BibTeX on a file.
575     global BibTeXAction
576     if BibTeXAction is None:
577         BibTeXAction = SCons.Action.Action("$BIBTEXCOM", "$BIBTEXCOMSTR")
578
579     # Define an action to run MakeIndex on a file.
580     global MakeIndexAction
581     if MakeIndexAction is None:
582         MakeIndexAction = SCons.Action.Action("$MAKEINDEXCOM", "$MAKEINDEXCOMSTR")
583
584     # Define an action to run MakeIndex on a file for nomenclatures.
585     global MakeNclAction
586     if MakeNclAction is None:
587         MakeNclAction = SCons.Action.Action("$MAKENCLCOM", "$MAKENCLCOMSTR")
588
589     # Define an action to run MakeIndex on a file for glossaries.
590     global MakeGlossaryAction
591     if MakeGlossaryAction is None:
592         MakeGlossaryAction = SCons.Action.Action("$MAKEGLOSSARYCOM", "$MAKEGLOSSARYCOMSTR")
593
594     global TeXLaTeXAction
595     if TeXLaTeXAction is None:
596         TeXLaTeXAction = SCons.Action.Action(TeXLaTeXFunction,
597                               strfunction=TeXLaTeXStrFunction)
598
599     import dvi
600     dvi.generate(env)
601
602     bld = env['BUILDERS']['DVI']
603     bld.add_action('.tex', TeXLaTeXAction)
604     bld.add_emitter('.tex', tex_eps_emitter)
605
606     env['TEX']      = 'tex'
607     env['TEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode')
608     env['TEXCOM']   = 'cd ${TARGET.dir} && $TEX $TEXFLAGS ${SOURCE.file}'
609
610     # Duplicate from latex.py.  If latex.py goes away, then this is still OK.
611     env['LATEX']        = 'latex'
612     env['LATEXFLAGS']   = SCons.Util.CLVar('-interaction=nonstopmode')
613     env['LATEXCOM']     = 'cd ${TARGET.dir} && $LATEX $LATEXFLAGS ${SOURCE.file}'
614     env['LATEXRETRIES'] = 3
615
616     env['BIBTEX']      = 'bibtex'
617     env['BIBTEXFLAGS'] = SCons.Util.CLVar('')
618     env['BIBTEXCOM']   = 'cd ${TARGET.dir} && $BIBTEX $BIBTEXFLAGS ${SOURCE.filebase}'
619
620     env['MAKEINDEX']      = 'makeindex'
621     env['MAKEINDEXFLAGS'] = SCons.Util.CLVar('')
622     env['MAKEINDEXCOM']   = 'cd ${TARGET.dir} && $MAKEINDEX $MAKEINDEXFLAGS ${SOURCE.file}'
623
624     env['MAKEGLOSSARY']      = 'makeindex'
625     env['MAKEGLOSSARYSTYLE'] = '${SOURCE.filebase}.ist'
626     env['MAKEGLOSSARYFLAGS'] = SCons.Util.CLVar('-s ${MAKEGLOSSARYSTYLE} -t ${SOURCE.filebase}.glg')
627     env['MAKEGLOSSARYCOM']   = 'cd ${TARGET.dir} && $MAKEGLOSSARY ${SOURCE.filebase}.glo $MAKEGLOSSARYFLAGS -o ${SOURCE.filebase}.gls'
628
629     env['MAKENCL']      = 'makeindex'
630     env['MAKENCLSTYLE'] = '$nomencl.ist'
631     env['MAKENCLFLAGS'] = '-s ${MAKENCLSTYLE} -t ${SOURCE.filebase}.nlg'
632     env['MAKENCLCOM']   = 'cd ${TARGET.dir} && $MAKENCL ${SOURCE.filebase}.nlo $MAKENCLFLAGS -o ${SOURCE.filebase}.nls'
633
634     # Duplicate from pdflatex.py.  If latex.py goes away, then this is still OK.
635     env['PDFLATEX']      = 'pdflatex'
636     env['PDFLATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode')
637     env['PDFLATEXCOM']   = 'cd ${TARGET.dir} && $PDFLATEX $PDFLATEXFLAGS ${SOURCE.file}'
638
639 def exists(env):
640     return env.Detect('tex')
641
642 # Local Variables:
643 # tab-width:4
644 # indent-tabs-mode:nil
645 # End:
646 # vim: set expandtab tabstop=4 shiftwidth=4: