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