summaryrefslogtreecommitdiff
path: root/waflib/Tools/tex.py
diff options
context:
space:
mode:
Diffstat (limited to 'waflib/Tools/tex.py')
-rw-r--r--waflib/Tools/tex.py327
1 files changed, 327 insertions, 0 deletions
diff --git a/waflib/Tools/tex.py b/waflib/Tools/tex.py
new file mode 100644
index 0000000..3a208d8
--- /dev/null
+++ b/waflib/Tools/tex.py
@@ -0,0 +1,327 @@
+#! /usr/bin/env python
+# encoding: utf-8
+# WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
+
+import os,re
+from waflib import Utils,Task,Errors,Logs,Node
+from waflib.TaskGen import feature,before_method
+re_bibunit=re.compile(r'\\(?P<type>putbib)\[(?P<file>[^\[\]]*)\]',re.M)
+def bibunitscan(self):
+ node=self.inputs[0]
+ nodes=[]
+ if not node:
+ return nodes
+ code=node.read()
+ for match in re_bibunit.finditer(code):
+ path=match.group('file')
+ if path:
+ found=None
+ for k in('','.bib'):
+ Logs.debug('tex: trying %s%s',path,k)
+ fi=node.parent.find_resource(path+k)
+ if fi:
+ found=True
+ nodes.append(fi)
+ if not found:
+ Logs.debug('tex: could not find %s',path)
+ Logs.debug('tex: found the following bibunit files: %s',nodes)
+ return nodes
+exts_deps_tex=['','.ltx','.tex','.bib','.pdf','.png','.eps','.ps','.sty']
+exts_tex=['.ltx','.tex']
+re_tex=re.compile(r'\\(?P<type>usepackage|RequirePackage|include|bibliography([^\[\]{}]*)|putbib|includegraphics|input|import|bringin|lstinputlisting)(\[[^\[\]]*\])?{(?P<file>[^{}]*)}',re.M)
+g_bibtex_re=re.compile('bibdata',re.M)
+g_glossaries_re=re.compile('\\@newglossary',re.M)
+class tex(Task.Task):
+ bibtex_fun,_=Task.compile_fun('${BIBTEX} ${BIBTEXFLAGS} ${SRCFILE}',shell=False)
+ bibtex_fun.__doc__="""
+ Execute the program **bibtex**
+ """
+ makeindex_fun,_=Task.compile_fun('${MAKEINDEX} ${MAKEINDEXFLAGS} ${SRCFILE}',shell=False)
+ makeindex_fun.__doc__="""
+ Execute the program **makeindex**
+ """
+ makeglossaries_fun,_=Task.compile_fun('${MAKEGLOSSARIES} ${SRCFILE}',shell=False)
+ makeglossaries_fun.__doc__="""
+ Execute the program **makeglossaries**
+ """
+ def exec_command(self,cmd,**kw):
+ if self.env.PROMPT_LATEX:
+ kw['stdout']=kw['stderr']=None
+ return super(tex,self).exec_command(cmd,**kw)
+ def scan_aux(self,node):
+ nodes=[node]
+ re_aux=re.compile(r'\\@input{(?P<file>[^{}]*)}',re.M)
+ def parse_node(node):
+ code=node.read()
+ for match in re_aux.finditer(code):
+ path=match.group('file')
+ found=node.parent.find_or_declare(path)
+ if found and found not in nodes:
+ Logs.debug('tex: found aux node %r',found)
+ nodes.append(found)
+ parse_node(found)
+ parse_node(node)
+ return nodes
+ def scan(self):
+ node=self.inputs[0]
+ nodes=[]
+ names=[]
+ seen=[]
+ if not node:
+ return(nodes,names)
+ def parse_node(node):
+ if node in seen:
+ return
+ seen.append(node)
+ code=node.read()
+ for match in re_tex.finditer(code):
+ multibib=match.group('type')
+ if multibib and multibib.startswith('bibliography'):
+ multibib=multibib[len('bibliography'):]
+ if multibib.startswith('style'):
+ continue
+ else:
+ multibib=None
+ for path in match.group('file').split(','):
+ if path:
+ add_name=True
+ found=None
+ for k in exts_deps_tex:
+ for up in self.texinputs_nodes:
+ Logs.debug('tex: trying %s%s',path,k)
+ found=up.find_resource(path+k)
+ if found:
+ break
+ for tsk in self.generator.tasks:
+ if not found or found in tsk.outputs:
+ break
+ else:
+ nodes.append(found)
+ add_name=False
+ for ext in exts_tex:
+ if found.name.endswith(ext):
+ parse_node(found)
+ break
+ if found and multibib and found.name.endswith('.bib'):
+ try:
+ self.multibibs.append(found)
+ except AttributeError:
+ self.multibibs=[found]
+ if add_name:
+ names.append(path)
+ parse_node(node)
+ for x in nodes:
+ x.parent.get_bld().mkdir()
+ Logs.debug("tex: found the following : %s and names %s",nodes,names)
+ return(nodes,names)
+ def check_status(self,msg,retcode):
+ if retcode!=0:
+ raise Errors.WafError('%r command exit status %r'%(msg,retcode))
+ def info(self,*k,**kw):
+ try:
+ info=self.generator.bld.conf.logger.info
+ except AttributeError:
+ info=Logs.info
+ info(*k,**kw)
+ def bibfile(self):
+ for aux_node in self.aux_nodes:
+ try:
+ ct=aux_node.read()
+ except EnvironmentError:
+ Logs.error('Error reading %s: %r',aux_node.abspath())
+ continue
+ if g_bibtex_re.findall(ct):
+ self.info('calling bibtex')
+ self.env.env={}
+ self.env.env.update(os.environ)
+ self.env.env.update({'BIBINPUTS':self.texinputs(),'BSTINPUTS':self.texinputs()})
+ self.env.SRCFILE=aux_node.name[:-4]
+ self.check_status('error when calling bibtex',self.bibtex_fun())
+ for node in getattr(self,'multibibs',[]):
+ self.env.env={}
+ self.env.env.update(os.environ)
+ self.env.env.update({'BIBINPUTS':self.texinputs(),'BSTINPUTS':self.texinputs()})
+ self.env.SRCFILE=node.name[:-4]
+ self.check_status('error when calling bibtex',self.bibtex_fun())
+ def bibunits(self):
+ try:
+ bibunits=bibunitscan(self)
+ except OSError:
+ Logs.error('error bibunitscan')
+ else:
+ if bibunits:
+ fn=['bu'+str(i)for i in range(1,len(bibunits)+1)]
+ if fn:
+ self.info('calling bibtex on bibunits')
+ for f in fn:
+ self.env.env={'BIBINPUTS':self.texinputs(),'BSTINPUTS':self.texinputs()}
+ self.env.SRCFILE=f
+ self.check_status('error when calling bibtex',self.bibtex_fun())
+ def makeindex(self):
+ self.idx_node=self.inputs[0].change_ext('.idx')
+ try:
+ idx_path=self.idx_node.abspath()
+ os.stat(idx_path)
+ except OSError:
+ self.info('index file %s absent, not calling makeindex',idx_path)
+ else:
+ self.info('calling makeindex')
+ self.env.SRCFILE=self.idx_node.name
+ self.env.env={}
+ self.check_status('error when calling makeindex %s'%idx_path,self.makeindex_fun())
+ def bibtopic(self):
+ p=self.inputs[0].parent.get_bld()
+ if os.path.exists(os.path.join(p.abspath(),'btaux.aux')):
+ self.aux_nodes+=p.ant_glob('*[0-9].aux')
+ def makeglossaries(self):
+ src_file=self.inputs[0].abspath()
+ base_file=os.path.basename(src_file)
+ base,_=os.path.splitext(base_file)
+ for aux_node in self.aux_nodes:
+ try:
+ ct=aux_node.read()
+ except EnvironmentError:
+ Logs.error('Error reading %s: %r',aux_node.abspath())
+ continue
+ if g_glossaries_re.findall(ct):
+ if not self.env.MAKEGLOSSARIES:
+ raise Errors.WafError("The program 'makeglossaries' is missing!")
+ Logs.warn('calling makeglossaries')
+ self.env.SRCFILE=base
+ self.check_status('error when calling makeglossaries %s'%base,self.makeglossaries_fun())
+ return
+ def texinputs(self):
+ return os.pathsep.join([k.abspath()for k in self.texinputs_nodes])+os.pathsep
+ def run(self):
+ env=self.env
+ if not env.PROMPT_LATEX:
+ env.append_value('LATEXFLAGS','-interaction=batchmode')
+ env.append_value('PDFLATEXFLAGS','-interaction=batchmode')
+ env.append_value('XELATEXFLAGS','-interaction=batchmode')
+ self.cwd=self.inputs[0].parent.get_bld()
+ self.info('first pass on %s',self.__class__.__name__)
+ cur_hash=self.hash_aux_nodes()
+ self.call_latex()
+ self.hash_aux_nodes()
+ self.bibtopic()
+ self.bibfile()
+ self.bibunits()
+ self.makeindex()
+ self.makeglossaries()
+ for i in range(10):
+ prev_hash=cur_hash
+ cur_hash=self.hash_aux_nodes()
+ if not cur_hash:
+ Logs.error('No aux.h to process')
+ if cur_hash and cur_hash==prev_hash:
+ break
+ self.info('calling %s',self.__class__.__name__)
+ self.call_latex()
+ def hash_aux_nodes(self):
+ try:
+ self.aux_nodes
+ except AttributeError:
+ try:
+ self.aux_nodes=self.scan_aux(self.inputs[0].change_ext('.aux'))
+ except IOError:
+ return None
+ return Utils.h_list([Utils.h_file(x.abspath())for x in self.aux_nodes])
+ def call_latex(self):
+ self.env.env={}
+ self.env.env.update(os.environ)
+ self.env.env.update({'TEXINPUTS':self.texinputs()})
+ self.env.SRCFILE=self.inputs[0].abspath()
+ self.check_status('error when calling latex',self.texfun())
+class latex(tex):
+ texfun,vars=Task.compile_fun('${LATEX} ${LATEXFLAGS} ${SRCFILE}',shell=False)
+class pdflatex(tex):
+ texfun,vars=Task.compile_fun('${PDFLATEX} ${PDFLATEXFLAGS} ${SRCFILE}',shell=False)
+class xelatex(tex):
+ texfun,vars=Task.compile_fun('${XELATEX} ${XELATEXFLAGS} ${SRCFILE}',shell=False)
+class dvips(Task.Task):
+ run_str='${DVIPS} ${DVIPSFLAGS} ${SRC} -o ${TGT}'
+ color='BLUE'
+ after=['latex','pdflatex','xelatex']
+class dvipdf(Task.Task):
+ run_str='${DVIPDF} ${DVIPDFFLAGS} ${SRC} ${TGT}'
+ color='BLUE'
+ after=['latex','pdflatex','xelatex']
+class pdf2ps(Task.Task):
+ run_str='${PDF2PS} ${PDF2PSFLAGS} ${SRC} ${TGT}'
+ color='BLUE'
+ after=['latex','pdflatex','xelatex']
+@feature('tex')
+@before_method('process_source')
+def apply_tex(self):
+ if not getattr(self,'type',None)in('latex','pdflatex','xelatex'):
+ self.type='pdflatex'
+ outs=Utils.to_list(getattr(self,'outs',[]))
+ try:
+ self.generator.bld.conf
+ except AttributeError:
+ default_prompt=False
+ else:
+ default_prompt=True
+ self.env.PROMPT_LATEX=getattr(self,'prompt',default_prompt)
+ deps_lst=[]
+ if getattr(self,'deps',None):
+ deps=self.to_list(self.deps)
+ for dep in deps:
+ if isinstance(dep,str):
+ n=self.path.find_resource(dep)
+ if not n:
+ self.bld.fatal('Could not find %r for %r'%(dep,self))
+ if not n in deps_lst:
+ deps_lst.append(n)
+ elif isinstance(dep,Node.Node):
+ deps_lst.append(dep)
+ for node in self.to_nodes(self.source):
+ if self.type=='latex':
+ task=self.create_task('latex',node,node.change_ext('.dvi'))
+ elif self.type=='pdflatex':
+ task=self.create_task('pdflatex',node,node.change_ext('.pdf'))
+ elif self.type=='xelatex':
+ task=self.create_task('xelatex',node,node.change_ext('.pdf'))
+ task.env=self.env
+ if deps_lst:
+ for n in deps_lst:
+ if not n in task.dep_nodes:
+ task.dep_nodes.append(n)
+ if hasattr(self,'texinputs_nodes'):
+ task.texinputs_nodes=self.texinputs_nodes
+ else:
+ task.texinputs_nodes=[node.parent,node.parent.get_bld(),self.path,self.path.get_bld()]
+ lst=os.environ.get('TEXINPUTS','')
+ if self.env.TEXINPUTS:
+ lst+=os.pathsep+self.env.TEXINPUTS
+ if lst:
+ lst=lst.split(os.pathsep)
+ for x in lst:
+ if x:
+ if os.path.isabs(x):
+ p=self.bld.root.find_node(x)
+ if p:
+ task.texinputs_nodes.append(p)
+ else:
+ Logs.error('Invalid TEXINPUTS folder %s',x)
+ else:
+ Logs.error('Cannot resolve relative paths in TEXINPUTS %s',x)
+ if self.type=='latex':
+ if'ps'in outs:
+ tsk=self.create_task('dvips',task.outputs,node.change_ext('.ps'))
+ tsk.env.env=dict(os.environ)
+ if'pdf'in outs:
+ tsk=self.create_task('dvipdf',task.outputs,node.change_ext('.pdf'))
+ tsk.env.env=dict(os.environ)
+ elif self.type=='pdflatex':
+ if'ps'in outs:
+ self.create_task('pdf2ps',task.outputs,node.change_ext('.ps'))
+ self.source=[]
+def configure(self):
+ v=self.env
+ for p in'tex latex pdflatex xelatex bibtex dvips dvipdf ps2pdf makeindex pdf2ps makeglossaries'.split():
+ try:
+ self.find_program(p,var=p.upper())
+ except self.errors.ConfigurationError:
+ pass
+ v.DVIPSFLAGS='-Ppdf'