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