1#! /usr/bin/env python 2# encoding: utf-8 3# WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file 4 5import os,re 6from waflib import Utils,Task,Errors,Logs,Node 7from waflib.TaskGen import feature,before_method 8re_bibunit=re.compile(r'\\(?P<type>putbib)\[(?P<file>[^\[\]]*)\]',re.M) 9def bibunitscan(self): 10 node=self.inputs[0] 11 nodes=[] 12 if not node:return nodes 13 code=node.read() 14 for match in re_bibunit.finditer(code): 15 path=match.group('file') 16 if path: 17 for k in('','.bib'): 18 Logs.debug('tex: trying %s%s',path,k) 19 fi=node.parent.find_resource(path+k) 20 if fi: 21 nodes.append(fi) 22 else: 23 Logs.debug('tex: could not find %s',path) 24 Logs.debug('tex: found the following bibunit files: %s',nodes) 25 return nodes 26exts_deps_tex=['','.ltx','.tex','.bib','.pdf','.png','.eps','.ps','.sty'] 27exts_tex=['.ltx','.tex'] 28re_tex=re.compile(r'\\(?P<type>usepackage|RequirePackage|include|bibliography([^\[\]{}]*)|putbib|includegraphics|input|import|bringin|lstinputlisting)(\[[^\[\]]*\])?{(?P<file>[^{}]*)}',re.M) 29g_bibtex_re=re.compile('bibdata',re.M) 30g_glossaries_re=re.compile('\\@newglossary',re.M) 31class tex(Task.Task): 32 bibtex_fun,_=Task.compile_fun('${BIBTEX} ${BIBTEXFLAGS} ${SRCFILE}',shell=False) 33 bibtex_fun.__doc__=""" 34 Execute the program **bibtex** 35 """ 36 makeindex_fun,_=Task.compile_fun('${MAKEINDEX} ${MAKEINDEXFLAGS} ${SRCFILE}',shell=False) 37 makeindex_fun.__doc__=""" 38 Execute the program **makeindex** 39 """ 40 makeglossaries_fun,_=Task.compile_fun('${MAKEGLOSSARIES} ${SRCFILE}',shell=False) 41 makeglossaries_fun.__doc__=""" 42 Execute the program **makeglossaries** 43 """ 44 def exec_command(self,cmd,**kw): 45 kw['stdout']=kw['stderr']=None 46 return super(tex,self).exec_command(cmd,**kw) 47 def scan_aux(self,node): 48 nodes=[node] 49 re_aux=re.compile(r'\\@input{(?P<file>[^{}]*)}',re.M) 50 def parse_node(node): 51 code=node.read() 52 for match in re_aux.finditer(code): 53 path=match.group('file') 54 found=node.parent.find_or_declare(path) 55 if found and found not in nodes: 56 Logs.debug('tex: found aux node %r',found) 57 nodes.append(found) 58 parse_node(found) 59 parse_node(node) 60 return nodes 61 def scan(self): 62 node=self.inputs[0] 63 nodes=[] 64 names=[] 65 seen=[] 66 if not node:return(nodes,names) 67 def parse_node(node): 68 if node in seen: 69 return 70 seen.append(node) 71 code=node.read() 72 global re_tex 73 for match in re_tex.finditer(code): 74 multibib=match.group('type') 75 if multibib and multibib.startswith('bibliography'): 76 multibib=multibib[len('bibliography'):] 77 if multibib.startswith('style'): 78 continue 79 else: 80 multibib=None 81 for path in match.group('file').split(','): 82 if path: 83 add_name=True 84 found=None 85 for k in exts_deps_tex: 86 for up in self.texinputs_nodes: 87 Logs.debug('tex: trying %s%s',path,k) 88 found=up.find_resource(path+k) 89 if found: 90 break 91 for tsk in self.generator.tasks: 92 if not found or found in tsk.outputs: 93 break 94 else: 95 nodes.append(found) 96 add_name=False 97 for ext in exts_tex: 98 if found.name.endswith(ext): 99 parse_node(found) 100 break 101 if found and multibib and found.name.endswith('.bib'): 102 try: 103 self.multibibs.append(found) 104 except AttributeError: 105 self.multibibs=[found] 106 if add_name: 107 names.append(path) 108 parse_node(node) 109 for x in nodes: 110 x.parent.get_bld().mkdir() 111 Logs.debug("tex: found the following : %s and names %s",nodes,names) 112 return(nodes,names) 113 def check_status(self,msg,retcode): 114 if retcode!=0: 115 raise Errors.WafError('%r command exit status %r'%(msg,retcode)) 116 def bibfile(self): 117 for aux_node in self.aux_nodes: 118 try: 119 ct=aux_node.read() 120 except EnvironmentError: 121 Logs.error('Error reading %s: %r',aux_node.abspath()) 122 continue 123 if g_bibtex_re.findall(ct): 124 Logs.info('calling bibtex') 125 self.env.env={} 126 self.env.env.update(os.environ) 127 self.env.env.update({'BIBINPUTS':self.texinputs(),'BSTINPUTS':self.texinputs()}) 128 self.env.SRCFILE=aux_node.name[:-4] 129 self.check_status('error when calling bibtex',self.bibtex_fun()) 130 for node in getattr(self,'multibibs',[]): 131 self.env.env={} 132 self.env.env.update(os.environ) 133 self.env.env.update({'BIBINPUTS':self.texinputs(),'BSTINPUTS':self.texinputs()}) 134 self.env.SRCFILE=node.name[:-4] 135 self.check_status('error when calling bibtex',self.bibtex_fun()) 136 def bibunits(self): 137 try: 138 bibunits=bibunitscan(self) 139 except OSError: 140 Logs.error('error bibunitscan') 141 else: 142 if bibunits: 143 fn=['bu'+str(i)for i in range(1,len(bibunits)+1)] 144 if fn: 145 Logs.info('calling bibtex on bibunits') 146 for f in fn: 147 self.env.env={'BIBINPUTS':self.texinputs(),'BSTINPUTS':self.texinputs()} 148 self.env.SRCFILE=f 149 self.check_status('error when calling bibtex',self.bibtex_fun()) 150 def makeindex(self): 151 self.idx_node=self.inputs[0].change_ext('.idx') 152 try: 153 idx_path=self.idx_node.abspath() 154 os.stat(idx_path) 155 except OSError: 156 Logs.info('index file %s absent, not calling makeindex',idx_path) 157 else: 158 Logs.info('calling makeindex') 159 self.env.SRCFILE=self.idx_node.name 160 self.env.env={} 161 self.check_status('error when calling makeindex %s'%idx_path,self.makeindex_fun()) 162 def bibtopic(self): 163 p=self.inputs[0].parent.get_bld() 164 if os.path.exists(os.path.join(p.abspath(),'btaux.aux')): 165 self.aux_nodes+=p.ant_glob('*[0-9].aux') 166 def makeglossaries(self): 167 src_file=self.inputs[0].abspath() 168 base_file=os.path.basename(src_file) 169 base,_=os.path.splitext(base_file) 170 for aux_node in self.aux_nodes: 171 try: 172 ct=aux_node.read() 173 except EnvironmentError: 174 Logs.error('Error reading %s: %r',aux_node.abspath()) 175 continue 176 if g_glossaries_re.findall(ct): 177 if not self.env.MAKEGLOSSARIES: 178 raise Errors.WafError("The program 'makeglossaries' is missing!") 179 Logs.warn('calling makeglossaries') 180 self.env.SRCFILE=base 181 self.check_status('error when calling makeglossaries %s'%base,self.makeglossaries_fun()) 182 return 183 def texinputs(self): 184 return os.pathsep.join([k.abspath()for k in self.texinputs_nodes])+os.pathsep 185 def run(self): 186 env=self.env 187 if not env.PROMPT_LATEX: 188 env.append_value('LATEXFLAGS','-interaction=batchmode') 189 env.append_value('PDFLATEXFLAGS','-interaction=batchmode') 190 env.append_value('XELATEXFLAGS','-interaction=batchmode') 191 self.cwd=self.inputs[0].parent.get_bld() 192 Logs.info('first pass on %s',self.__class__.__name__) 193 cur_hash=self.hash_aux_nodes() 194 self.call_latex() 195 self.hash_aux_nodes() 196 self.bibtopic() 197 self.bibfile() 198 self.bibunits() 199 self.makeindex() 200 self.makeglossaries() 201 for i in range(10): 202 prev_hash=cur_hash 203 cur_hash=self.hash_aux_nodes() 204 if not cur_hash: 205 Logs.error('No aux.h to process') 206 if cur_hash and cur_hash==prev_hash: 207 break 208 Logs.info('calling %s',self.__class__.__name__) 209 self.call_latex() 210 def hash_aux_nodes(self): 211 try: 212 self.aux_nodes 213 except AttributeError: 214 try: 215 self.aux_nodes=self.scan_aux(self.inputs[0].change_ext('.aux')) 216 except IOError: 217 return None 218 return Utils.h_list([Utils.h_file(x.abspath())for x in self.aux_nodes]) 219 def call_latex(self): 220 self.env.env={} 221 self.env.env.update(os.environ) 222 self.env.env.update({'TEXINPUTS':self.texinputs()}) 223 self.env.SRCFILE=self.inputs[0].abspath() 224 self.check_status('error when calling latex',self.texfun()) 225class latex(tex): 226 texfun,vars=Task.compile_fun('${LATEX} ${LATEXFLAGS} ${SRCFILE}',shell=False) 227class pdflatex(tex): 228 texfun,vars=Task.compile_fun('${PDFLATEX} ${PDFLATEXFLAGS} ${SRCFILE}',shell=False) 229class xelatex(tex): 230 texfun,vars=Task.compile_fun('${XELATEX} ${XELATEXFLAGS} ${SRCFILE}',shell=False) 231class dvips(Task.Task): 232 run_str='${DVIPS} ${DVIPSFLAGS} ${SRC} -o ${TGT}' 233 color='BLUE' 234 after=['latex','pdflatex','xelatex'] 235class dvipdf(Task.Task): 236 run_str='${DVIPDF} ${DVIPDFFLAGS} ${SRC} ${TGT}' 237 color='BLUE' 238 after=['latex','pdflatex','xelatex'] 239class pdf2ps(Task.Task): 240 run_str='${PDF2PS} ${PDF2PSFLAGS} ${SRC} ${TGT}' 241 color='BLUE' 242 after=['latex','pdflatex','xelatex'] 243@feature('tex') 244@before_method('process_source') 245def apply_tex(self): 246 if not getattr(self,'type',None)in('latex','pdflatex','xelatex'): 247 self.type='pdflatex' 248 outs=Utils.to_list(getattr(self,'outs',[])) 249 self.env.PROMPT_LATEX=getattr(self,'prompt',1) 250 deps_lst=[] 251 if getattr(self,'deps',None): 252 deps=self.to_list(self.deps) 253 for dep in deps: 254 if isinstance(dep,str): 255 n=self.path.find_resource(dep) 256 if not n: 257 self.bld.fatal('Could not find %r for %r'%(dep,self)) 258 if not n in deps_lst: 259 deps_lst.append(n) 260 elif isinstance(dep,Node.Node): 261 deps_lst.append(dep) 262 for node in self.to_nodes(self.source): 263 if self.type=='latex': 264 task=self.create_task('latex',node,node.change_ext('.dvi')) 265 elif self.type=='pdflatex': 266 task=self.create_task('pdflatex',node,node.change_ext('.pdf')) 267 elif self.type=='xelatex': 268 task=self.create_task('xelatex',node,node.change_ext('.pdf')) 269 task.env=self.env 270 if deps_lst: 271 for n in deps_lst: 272 if not n in task.dep_nodes: 273 task.dep_nodes.append(n) 274 if hasattr(self,'texinputs_nodes'): 275 task.texinputs_nodes=self.texinputs_nodes 276 else: 277 task.texinputs_nodes=[node.parent,node.parent.get_bld(),self.path,self.path.get_bld()] 278 lst=os.environ.get('TEXINPUTS','') 279 if self.env.TEXINPUTS: 280 lst+=os.pathsep+self.env.TEXINPUTS 281 if lst: 282 lst=lst.split(os.pathsep) 283 for x in lst: 284 if x: 285 if os.path.isabs(x): 286 p=self.bld.root.find_node(x) 287 if p: 288 task.texinputs_nodes.append(p) 289 else: 290 Logs.error('Invalid TEXINPUTS folder %s',x) 291 else: 292 Logs.error('Cannot resolve relative paths in TEXINPUTS %s',x) 293 if self.type=='latex': 294 if'ps'in outs: 295 tsk=self.create_task('dvips',task.outputs,node.change_ext('.ps')) 296 tsk.env.env=dict(os.environ) 297 if'pdf'in outs: 298 tsk=self.create_task('dvipdf',task.outputs,node.change_ext('.pdf')) 299 tsk.env.env=dict(os.environ) 300 elif self.type=='pdflatex': 301 if'ps'in outs: 302 self.create_task('pdf2ps',task.outputs,node.change_ext('.ps')) 303 self.source=[] 304def configure(self): 305 v=self.env 306 for p in'tex latex pdflatex xelatex bibtex dvips dvipdf ps2pdf makeindex pdf2ps makeglossaries'.split(): 307 try: 308 self.find_program(p,var=p.upper()) 309 except self.errors.ConfigurationError: 310 pass 311 v.DVIPSFLAGS='-Ppdf' 312