Commits

yanchuan sim committed e96ac2c Merge

Merge branch 'develop'

Comments (0)

Files changed (3)

-# yc-make-latex is a simple Python script that handles compiling of LaTeX and related files (like gnuplot, etc).
+yc-make-latex is a simple Python script that handles compiling of LaTeX and related files.
+
+Version 0.1
+- Supports compiling LaTeX files and running BibTeX alternatively until there are no citation errors (naive implementation).
+- Supports running .plt scripts with gnuplot and detecting the eps files that are being produced.
+- Supports converting .eps files to .pdf

ycmakelatex.py

-#!/usr/bin/env python
-
-import sys, subprocess, os, time, re
-import argparse
-import termcolor
-
-def main():
-  parser = argparse.ArgumentParser(description='Compiles LaTeX/related file given on the command line. A sequence of files maybe given and it will be compiled in order. Currently supports .tex, .plt')
-  # parser.add_argument('-m', '--show-members', metavar='N', type=int, const=8, nargs='?', default=8, help='Show <N> top members of factions (default=10).')
-  # parser.add_argument('-M', '--hide-members', action='store_true', default=False, help='Hide top members of factions.')
-  # parser.add_argument('-c', '--show-uncertain', metavar='N', type=int, const=32, nargs='?', default=32, help='Show <N> top members whose factions distribution are most uninformative (default=10).')
-  # parser.add_argument('-u', '--show-unidirectional', metavar='N', const=8, type=int, nargs='?', default=8, help='Hide top citation words unidirectionally.')
-  parser.add_argument('files', metavar='FILES', nargs='+', type=file, help='List of files to compile. They can be .tex, .plt.')
-  parser.add_argument('-f', '--force', action='store_true', default=False, help='Force compile all the files given on the command line without checking to see if it has been modified since last compilation.')
-
-  parser.add_argument('-s', '--skylander', default=False, action='store_true', help='Skylander')
-
-  A = parser.parse_args()
-
-  my_path = os.path.realpath(__file__)
-
-  # get the last time my script was run
-  my_last_time = os.path.getmtime(my_path)
-
-  # update the mtime, which is when the script was run
-  os.utime(my_path, None)
-
-  for f in A.files:
-    # check if file has been modified since the last time we ran this script
-    if A.force or os.path.getmtime(f.name) > my_last_time:
-      try:
-        process_files(f)
-      except MyError as e:
-        print_error('{}: {}'.format(os.path.basename(__file__), e.msg))
-        sys.exit(-1)
-      #end try
-    #end if
-  #end for
-#end def
-
-def process_files(f):
-  (root, ext) = os.path.splitext(f.name)
-
-  try:
-    PROCESS_EXT[ext.lower()](f)
-  except KeyError:
-    raise MyError('Extension {} is not supported!'.format(ext))
-  #end try
-#end def
-
-def process_tex(f):
-  # start compiling
-  command = ['pdflatex', '-halt-on-error', '-interaction=nonstopmode', f.name]
-  try:
-    output = subprocess.check_output(command)
-
-    retval = parse_latex_output(output)
-    if retval == 'need_citations': process_bibtex(f)
-
-  except subprocess.CalledProcessError as e:
-    parse_latex_output(e.output)
-    return False
-  #end try
-
-  return True
-#end def
-
-def process_bibtex(f):
-  noext, ext = os.path.splitext(f.name)
-  command = ['bibtex', noext]
-  try:
-    output = subprocess.check_output(command)
-    # look for warnings here --
-  except subprocess.CalledProcessError as e:
-    # look for errors here ---
-    return False
-  #end try
-  
-#end def
-
-def parse_latex_output(output, report_warning=False):
-  lines = output.split('\n')
-  errors = []
-  warnings = []
-  cur = ''
-
-  re_error = re.compile(r'LaTeX Error\: (.+\.)$')
-  re_warning = re.compile(r'LaTeX Warning\: (.+\.)$')
-  re_citation = re.compile(r'Citation \`(.+)\' on page (\d+) undefined on input line (\d+).')
-  re_reference = re.compile(r'Reference \`(.+)\' on page (\d+) undefined on input line (\d+).')
-  for i, line in enumerate(lines):
-    new_error = False
-
-    if line.startswith('!'):
-      cur = line[2:]
-      new_error = True
-    elif line.startswith('LaTeX Warning'):
-      cur = line
-      new_error = True
-    #end if
-
-    if cur:
-      if not new_error: cur += line
-      if re_error.match(cur):
-        errors.append(cur)
-        cur = ''
-      elif re_warning.match(cur):
-        warnings.append(cur)
-        cur = ''
-      #end if
-    #end if
-  #end for
-  if cur:
-    if re_error.match(cur): errors.append(cur)
-    elif re_warning.match(cur): warnings.append(cur)
-  #end if
-
-  if errors:
-    for msg in errors:
-      m = re_error.match(msg)
-      print_error('Error: {}'.format(m.group(1)))
-    #end for
-    return 'compile_error'
-  #end if
-
-  # check for missing citations/references
-  need_citations = False
-  need_references = False
-  for msg in warnings:
-    m = re_warning.match(msg)
-    msg = m.group(1)
-
-    if re_citation.match(msg): need_citations = True
-    elif re_reference.match(msg): need_references = True
-  #end for
-
-  if need_citations: return 'need_citations'
-
-  return 'ok'
-#end def
-
-def print_error(msg): termcolor.cprint(msg, 'red', attrs=['bold'], file=sys.stderr)
-
-class MyError(Exception):
-  def __init__(self, msg): self.msg = msg
-  def __str__(self): return repr(self.msg)
-
-#end class
-if __name__ == "__main__":
-  PROCESS_EXT = {'.tex': process_tex}
-
-  main()
-#end if
+#!/usr/bin/env python
+
+import sys, subprocess, os, time, re
+import argparse
+import termcolor
+
+VERSION = 0.1
+
+parser = argparse.ArgumentParser(description='Compiles LaTeX/related file given on the command line. A sequence of files maybe given and it will be compiled in order. Currently supports .tex, .plt')
+parser.add_argument('files', metavar='file', nargs='+', type=file, help='List of files to compile. They can be .tex, .plt.')
+parser.add_argument('-f', '--force', action='store_true', default=False, help='Force compile all the files given on the command line without checking to see if it has been modified since last compilation.')
+parser.add_argument('-c', '--clean', action='store_true', default=False, help='Clean up all intermediate files on success.')
+parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Print info messages.')
+parser.add_argument('-V', '--version', action='store_true', default=False, help='Print version information.')
+
+OUTPUT_EXT = {'.tex': '.pdf', '.eps': '.pdf'}
+DELETE_EXT = {'.tex': ['.aux', '.bbl', '.blg', '.log']}
+MAX_LATEX_RUNS = 3
+
+def main():
+  if A.version: parser.exit(message='ycmakelatex.py version {} by yanchuan <yanchuan@yanchuan.sg>\n'.format(VERSION))
+
+  my_path = os.path.realpath(__file__)
+
+  # get the last time my script was run
+  my_last_time = os.path.getmtime(my_path)
+
+  # update the mtime, which is when the script was run
+  os.utime(my_path, None)
+
+  processed = []
+
+  for f in A.files:
+    # check if file has been modified since the last time we ran this script
+    if A.force or os.path.getmtime(f.name) > my_last_time or output_not_found(f):
+      processed.append(f.name)
+      try:
+        process_files(f)
+      except MyError as e:
+        print_error('{}: {}'.format(os.path.basename(__file__), e.msg))
+        sys.exit(-1)
+      #end try
+    #end if
+    
+    if A.clean: cleanup_files(f)
+  #end for
+
+  if not processed: print_message('There was nothing to do.')
+  else: print_message('The following files were processed: {}'.format(', '.join(processed)))
+  os.utime(my_path, None)
+#end def
+
+def process_files(f):
+  (root, ext) = os.path.splitext(f.name)
+
+  try:
+    PROCESS_EXT[ext.lower()](f)
+
+  except KeyError:
+    raise MyError('Extension {} is not supported!'.format(ext))
+  #end try
+
+  return True
+#end def
+
+def output_not_found(f):
+  (root, ext) = os.path.splitext(f.name)
+
+  if ext not in OUTPUT_EXT: return True
+  if os.access(root + OUTPUT_EXT[ext], os.F_OK): return False
+
+  return True
+#end def
+
+def cleanup_files(f):
+  (root, ext) = os.path.splitext(f.name)
+
+  if ext not in DELETE_EXT: return False
+
+  for del_ext in DELETE_EXT[ext]:
+    try:
+      os.unlink(root+del_ext)
+    except:
+      pass
+  #end for
+#end def
+
+def process_tex(f, run_count=0):
+  # start compiling
+  command = ['pdflatex', '-halt-on-error', '-interaction=nonstopmode', f.name]
+  try:
+    print_info('pdfLaTeX {}'.format(f.name))
+    output = subprocess.check_output(command)
+    retval = parse_latex_output(output, report_warning=True if run_count == MAX_LATEX_RUNS else False)
+
+    if retval == 'need_citations':
+      if run_count == 0:
+        process_bibtex(f)
+        process_tex(f, run_count=run_count+1)
+
+      elif run_count > 0 and run_count < MAX_LATEX_RUNS:
+        if run_count > 1 and not process_bibtex(f):
+          run_count = MAX_LATEX_RUNS - 1
+
+        process_tex(f, run_count=run_count+1)
+
+      elif run_count == MAX_LATEX_RUNS:
+        print_message('There are still missing citations.')
+        return False
+      #end if
+    elif retval == 'compile_error': raise MyError('LaTeX failed!')
+    elif retval == 'ok+warning': parse_latex_output(output, report_warning=True)
+    #end if
+
+  except subprocess.CalledProcessError as e:
+    parse_latex_output(e.output)
+    raise MyError('LaTeX failed!')
+  #end try
+
+  return True
+#end def
+
+def process_bibtex(f):
+  noext, ext = os.path.splitext(f.name)
+  command = ['bibtex', noext]
+
+  try:
+    print_info('BibTeX {}'.format(f.name))
+    output = subprocess.check_output(command)
+    retval = parse_bibtex_output(output)
+
+  except subprocess.CalledProcessError as e:
+    parse_bibtex_output(output)
+
+    raise MyError('BibTeX failed!')
+  #end try
+
+  return retval
+#end def
+
+def process_plt(f):
+  command = ['gnuplot', f.name]
+  time_now = time.time() - 1
+  try:
+    output = subprocess.check_output(command)
+    
+    for file in os.listdir('.'):
+      if file.endswith('.eps') and os.path.getmtime(file) >= time_now: 
+        retval = process_eps(open(file))
+        if retval: 
+          try: os.unlink(file)
+          except: pass
+        #end if
+      #end if
+    #end for
+  except subprocess.CalledProcessError as e:
+    raise MyError('gnuplot {} failed!'.format(f.name))
+  #end try
+#end def
+
+def process_eps(f):
+  command = ['epstopdf', f.name]
+  try:
+    print_info('epstopdf {}'.format(f.name))
+    output = subprocess.check_output(command)
+  except subprocess.CalledProcessError as e:
+    raise MyError('epstopdf {} failed!'.format(f.name))
+  #end try
+  
+  return True
+#end def
+
+def parse_bibtex_output(output):
+  lines = output.split('\n')
+
+  have_warning = False
+  for line in lines:
+    if '---' in line:
+      print_error('BibTeX: {}'.format(line))
+      return False
+    #end if
+
+    if '--' in line:
+      print_warning('BibTeX: {}'.format(line))
+      have_warning = True
+    #end if
+  #end for
+
+  return not have_warning
+#end def
+
+def parse_latex_output(output, report_warning=False):
+  lines = output.split('\n')
+  errors = []
+  warnings = []
+  cur = ''
+
+  re_error = re.compile(r'LaTeX Error\: (.+\.)$')
+  re_warning = re.compile(r'LaTeX.* Warning\: (.+\.)$')
+  re_citation = re.compile(r'Citation \`(.+)\' on page (\d+) undefined on input line (\d+).')
+  re_reference = re.compile(r'Reference \`(.+)\' on page (\d+) undefined on input line (\d+).')
+  for i, line in enumerate(lines):
+    new_error = False
+
+    if line.startswith('!'):
+      cur = line[2:]
+      new_error = True
+    elif line.startswith('LaTeX Warning'):
+      cur = line
+      new_error = True
+    #end if
+
+    if cur:
+      if not new_error: cur += line
+      if re_error.match(cur):
+        errors.append(cur)
+        cur = ''
+      elif re_warning.match(cur):
+        warnings.append(cur)
+        cur = ''
+      #end if
+    #end if
+  #end for
+  if cur:
+    if re_error.match(cur): errors.append(cur)
+    elif re_warning.match(cur): warnings.append(cur)
+  #end if
+
+  if errors:
+    for msg in errors:
+      m = re_error.match(msg)
+      print_error('LaTeX: {}'.format(m.group(1)))
+    #end for
+    return 'compile_error'
+  #end if
+
+  # check for missing citations/references
+  need_citations = False
+  need_references = False
+  for msg in warnings:
+    m = re_warning.match(msg)
+    msg = m.group(1)
+
+    if re_citation.match(msg): need_citations = True
+    elif re_reference.match(msg): need_references = True
+
+    if report_warning: print_warning('LaTeX: {}'.format(msg))
+  #end for
+
+  if need_citations: return 'need_citations'
+
+  if warnings: return 'ok+warnings'
+
+  return 'ok'
+#end def
+
+def print_error(msg): termcolor.cprint(msg, 'red', attrs=['bold'], file=sys.stderr)
+def print_warning(msg): termcolor.cprint(msg, 'green', attrs=['bold'], file=sys.stderr)
+def print_info(msg):
+  if A.verbose: termcolor.cprint(msg, 'cyan', attrs=['bold'], file=sys.stderr)
+#end def
+def print_message(msg): termcolor.cprint('{}: {}'.format(os.path.basename(__file__), msg), 'magenta', attrs=['bold'], file=sys.stderr)
+
+class MyError(Exception):
+  def __init__(self, msg): self.msg = msg
+  def __str__(self): return repr(self.msg)
+
+#end class
+if __name__ == "__main__":
+  PROCESS_EXT = {'.tex': process_tex, '.plt': process_plt, '.eps': process_eps}
+
+  A = parser.parse_args()
+
+  main()
+#end if
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.