Fix DVIPDF tests, refactor various TeX Tool modules.
[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
40 import SCons.Action
41 import SCons.Node
42 import SCons.Node.FS
43 import SCons.Util
44
45 # An Action sufficient to build any generic tex file.
46 TeXAction = None
47
48 # An action to build a latex file.  This action might be needed more
49 # than once if we are dealing with labels and bibtex.
50 LaTeXAction = None
51
52 # An action to run BibTeX on a file.
53 BibTeXAction = None
54
55 # An action to run MakeIndex on a file.
56 MakeIndexAction = None
57
58 def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None):
59     """A builder for LaTeX files that checks the output in the aux file
60     and decides how many times to use LaTeXAction, and BibTeXAction."""
61
62     basename, ext = SCons.Util.splitext(str(target[0]))
63
64     # Run LaTeX once to generate a new aux file.
65     XXXLaTeXAction(target,source,env)
66
67     # Decide if various things need to be run, or run again.  We check
68     # for the existence of files before opening them--even ones like the
69     # aux file that TeX always creates--to make it possible to write tests
70     # with stubs that don't necessarily generate all of the same files.
71
72     # Now decide if bibtex will need to be run.
73     auxfilename = basename + '.aux'
74     if os.path.exists(auxfilename):
75         content = open(auxfilename, "rb").read()
76         if string.find(content, "bibdata") != -1:
77             bibfile = env.fs.File(basename)
78             BibTeXAction(None,bibfile,env)
79
80     # Now decide if makeindex will need to be run.
81     idxfilename = basename + '.idx'
82     if os.path.exists(idxfilename):
83         idxfile = env.fs.File(basename)
84         # TODO: if ( idxfile has changed) ...
85         MakeIndexAction(None,idxfile,env)
86         LaTeXAction(target,source,env)
87
88     # Now decide if latex needs to be run yet again.
89     logfilename = basename + '.log'
90     for trial in range(int(env.subst('$LATEXRETRIES'))):
91         if not os.path.exists(logfilename):
92             break
93         content = open(logfilename, "rb").read()
94         if not re.search("^LaTeX Warning:.*Rerun",content,re.MULTILINE) and not re.search("^LaTeX Warning:.*undefined references",content,re.MULTILINE):
95             break
96         XXXLaTeXAction(target,source,env)
97     return 0
98
99 def LaTeXAuxAction(target = None, source= None, env=None):
100     InternalLaTeXAuxAction( LaTeXAction, target, source, env )
101
102 LaTeX_re = re.compile("\\\\document(style|class)")
103
104 def is_LaTeX(flist):
105     # Scan a file list to decide if it's TeX- or LaTeX-flavored.
106     for f in flist:
107         content = f.get_contents()
108         if LaTeX_re.search(content):
109             return 1
110     return 0
111
112 def TeXLaTeXFunction(target = None, source= None, env=None):
113     """A builder for TeX and LaTeX that scans the source file to
114     decide the "flavor" of the source and then executes the appropriate
115     program."""
116     if is_LaTeX(source):
117         LaTeXAuxAction(target,source,env)
118     else:
119         TeXAction(target,source,env)
120     return 0
121
122 def tex_emitter(target, source, env):
123     base = SCons.Util.splitext(str(source[0]))[0]
124     target.append(base + '.aux')
125     target.append(base + '.log')
126     return (target, source)
127
128 TeXLaTeXAction = None
129
130 def generate(env):
131     """Add Builders and construction variables for TeX to an Environment."""
132
133     # A generic tex file Action, sufficient for all tex files.
134     global TeXAction
135     if TeXAction is None:
136         TeXAction = SCons.Action.Action("$TEXCOM", "$TEXCOMSTR")
137
138     # An Action to build a latex file.  This might be needed more
139     # than once if we are dealing with labels and bibtex.
140     global LaTeXAction
141     if LaTeXAction is None:
142         LaTeXAction = SCons.Action.Action("$LATEXCOM", "$LATEXCOMSTR")
143
144     # Define an action to run BibTeX on a file.
145     global BibTeXAction
146     if BibTeXAction is None:
147         BibTeXAction = SCons.Action.Action("$BIBTEXCOM", "$BIBTEXCOMSTR")
148
149     # Define an action to run MakeIndex on a file.
150     global MakeIndexAction
151     if MakeIndexAction is None:
152         MakeIndexAction = SCons.Action.Action("$MAKEINDEXCOM", "$MAKEINDEXOMSTR")
153
154     global TeXLaTeXAction
155     if TeXLaTeXAction is None:
156         TeXLaTeXAction = SCons.Action.Action(TeXLaTeXFunction, strfunction=None)
157
158     import dvi
159     dvi.generate(env)
160
161     bld = env['BUILDERS']['DVI']
162     bld.add_action('.tex', TeXLaTeXAction)
163     bld.add_emitter('.tex', tex_emitter)
164
165     env['TEX']      = 'tex'
166     env['TEXFLAGS'] = SCons.Util.CLVar('')
167     env['TEXCOM']   = '$TEX $TEXFLAGS $SOURCE'
168
169     # Duplicate from latex.py.  If latex.py goes away, then this is still OK.
170     env['LATEX']        = 'latex'
171     env['LATEXFLAGS']   = SCons.Util.CLVar('')
172     env['LATEXCOM']     = '$LATEX $LATEXFLAGS $SOURCE'
173     env['LATEXRETRIES'] = 3
174
175     env['BIBTEX']      = 'bibtex'
176     env['BIBTEXFLAGS'] = SCons.Util.CLVar('')
177     env['BIBTEXCOM']   = '$BIBTEX $BIBTEXFLAGS $SOURCE'
178
179     env['MAKEINDEX']      = 'makeindex'
180     env['MAKEINDEXFLAGS'] = SCons.Util.CLVar('')
181     env['MAKEINDEXCOM']   = '$MAKEINDEX $MAKEINDEXFLAGS $SOURCES'
182
183 def exists(env):
184     return env.Detect('tex')