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