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