Advertisement
Guest User

texliveonfly.py

a guest
Sep 18th, 2011
157
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.56 KB | None | 0 0
  1. #!/usr/bin/env python3
  2.  
  3. # texliveonfly.py (formerly lualatexonfly.py) - "Downloading on the fly"
  4. #     (similar to miktex) for texlive.
  5. #
  6. # Given a .tex file, runs lualatex (by default) repeatedly, using error messages
  7. #     to install missing packages.
  8. #
  9. #
  10. # September 19, 2011 Release
  11. #
  12. # Written on Ubuntu 10.04 with TexLive 2011
  13. # Other systems may have not been tested.
  14. #
  15. # Copyright (C) 2011 Saitulaa Naranong
  16. #
  17. # This program is free software; you can redistribute it and/or modify
  18. # it under the terms of the GNU General Public License as published by
  19. # the Free Software Foundation; either version 3 of the License, or
  20. # (at your option) any later version.
  21. #
  22. # This program is distributed in the hope that it will be useful,
  23. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  24. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  25. # GNU General Public License for more details.
  26. #
  27. # You should have received a copy of the GNU General Public License
  28. # along with this program; if not, see <http://www.gnu.org/copyleft/gpl.html>.
  29.  
  30. import re, subprocess, os, time,  optparse, sys
  31.  
  32. #sets up temp directory and paths
  33. tempDirectory =  os.path.join(os.getenv("HOME"), ".texliveonfly")
  34. lockfilePath = os.path.join(tempDirectory,  "newterminal_lock")
  35.  
  36. #makes sure the temp directory exists
  37. try:
  38.     os.mkdir(tempDirectory)
  39. except OSError:
  40.     print("Our temp directory " + tempDirectory +  " already exists; good.")
  41.  
  42. checkedForUpdates = False   #have we checked for updates yet?
  43.  
  44. #NOTE: double-escaping \\ is neccessary for a slash to appear in the bash command
  45. def spawnInNewTerminal(bashCommand):
  46.     #creates lock file
  47.     lockfile = open(lockfilePath, 'w')
  48.     lockfile.write("texliveonfly currently performing task in separate terminal.")
  49.     lockfile.close()
  50.  
  51.     #adds line to remove lock at end of command
  52.     bashCommand += '; rm \\"' + lockfilePath + '\\"'
  53.  
  54.     #runs the bash command in a new terminal
  55.     process = subprocess.Popen (
  56.         ['x-terminal-emulator', '-e',  'sh -c "{0}"'.format(bashCommand) ]
  57.             , stdout=subprocess.PIPE )
  58.     process.wait()
  59.  
  60.     #doesn't let us proceed until the lock file has been removed by the bash command
  61.     while os.path.exists(lockfilePath):
  62.         time.sleep(0.1)
  63.  
  64. def updateTLMGR():
  65.     global checkedForUpdates
  66.     if not checkedForUpdates:
  67.         spawnInNewTerminal('''echo \\"Updating tlmgr prior to installing packages\n(this is necessary to avoid complaints from itself).\\n\\" ; sudo tlmgr update --self''')
  68.         checkedForUpdates = True
  69.  
  70. #strictmatch requires an entire /file match in the search results
  71. def getSearchResults(preamble, term, strictMatch):
  72.     output = subprocess.getoutput("tlmgr search --global --file " + term)
  73.     outList = output.split("\n")
  74.  
  75.     results = ["latex"]    #latex entry for removal later
  76.  
  77.     for line in outList:
  78.         line = line.strip()
  79.         if line.startswith(preamble) and (not strictMatch or line.endswith("/" + term)):
  80.             #filters out the package in:
  81.             #   texmf-dist/.../package/file
  82.             #and adds it to packages
  83.             results.append(line.split("/")[-2].strip())
  84.             results.append(line.split("/")[-3].strip()) #occasionally the package is one more slash before
  85.  
  86.     results = list(set(results))    #removes duplicates
  87.     results.remove("latex")     #removes most common fake result
  88.     return results
  89.  
  90. def getFilePackage(file):
  91.     return " ".join( getSearchResults("texmf-dist/", file, True) )
  92.  
  93. def getFontPackage(font):
  94.     font = re.sub(r"\((.*)\)", "", font)    #gets rid of parentheses
  95.     results = getSearchResults("texmf-dist/fonts/", font , False)
  96.  
  97.     #allow for possibility of lowercase
  98.     if len(results) == 0:
  99.         return "" if font.islower() else getFontPackage(font.lower())
  100.     else:
  101.         return " ".join(results)
  102.  
  103. #string can contain more than one package
  104. def installPackages(packagesString):
  105.     updateTLMGR()  #avoids complaints about tlmgr not being updated
  106.  
  107.     #New terminal is required: we're not guaranteed user can input sudo password into editor
  108.     print("Attempting to install LaTex package(s): " + packagesString )
  109.     print("A new terminal will open and you may be prompted for your sudo password.")
  110.  
  111.     #bash command to download and remove lock
  112.     bashCommand='''echo \\"Attempting to install LaTeX package(s): {0} \\"
  113. echo \\"(Some of them might not be real.)\\n\\"
  114. sudo tlmgr install {0}'''.format(packagesString)
  115.  
  116.     spawnInNewTerminal(bashCommand)
  117.  
  118. ### MAIN PROGRAM ###
  119. licenseinfo = """texliveonfly.py Copyright (C) 2011 Saitulaa Naranong
  120. This program comes with ABSOLUTELY NO WARRANTY;
  121. See the GNU General Public License v3 for more info."""
  122.  
  123. defaultArgs = "-synctex=1 -interaction=nonstopmode"
  124.  
  125. if __name__ == '__main__':
  126.     # Parse command line
  127.     parser = optparse.OptionParser(
  128.         usage="\n\n\t%prog [options] file.tex\n\nUse option --help for more info.\n\n" + licenseinfo ,
  129.         version='2011.20.9',
  130.         conflict_handler='resolve'
  131.     )
  132.  
  133.     parser.add_option('-h', '--help',
  134.         action='help', help='print this help text and exit')
  135.     parser.add_option('-e', '--engine',
  136.         dest='engine', metavar='ENGINE', help='your LaTeX compiler; defaults to lualatex', default="lualatex")
  137.     parser.add_option('-a', '--arguments',
  138.         dest='arguments', metavar='ARGS', help='arguments to send to engine; default is: "{0}"'.format(defaultArgs) , default=defaultArgs)
  139.     parser.add_option('-f', '--fail_silently', action = "store_true" ,
  140.         dest='fail_silently', help="If tlmgr cannot be found, compile document anyway.", default=False)
  141.  
  142.     (options, args) = parser.parse_args()
  143.  
  144.     if len(args) == 0:
  145.         parser.error("You must specify a .tex file to compile.")
  146.  
  147.     latexDocName = args[0]
  148.  
  149.     if "not found" in subprocess.getoutput("tlmgr"):
  150.         if options.fail_silently:
  151.             subprocess.getoutput( options.engine + ' ' + options.arguments + ' "' + latexDocName + '"')
  152.             sys.exit(0)
  153.         else:
  154.             parser.error("It appears tlmgr is not installed.  Are you sure you have TeX Live 2010 or later?")
  155.  
  156.     #loop constraints
  157.     done = False
  158.     previousFile = ""
  159.     previousFontFile = ""
  160.     previousFont =""
  161.  
  162.     #keeps running until all missing font/file errors are gone, or the same ones persist in all categories
  163.     while not done:
  164.         output = subprocess.getoutput( options.engine + ' ' + options.arguments + ' "' + latexDocName + '"')
  165.  
  166.         #most reliable: searches for missing file
  167.         filesSearch = re.findall(r"! LaTeX Error: File `([^`']*)' not found" , output) + re.findall(r"! I can't find file `([^`']*)'." , output)
  168.         #next most reliable: infers filename from font error
  169.         fontsFileSearch = [ name + ".tfm" for name in re.findall(r"! Font \\[^=]*=([^\s]*)\s", output) ]
  170.         #brute force search for font name in files
  171.         fontsSearch =  re.findall(r"! Font [^\n]*file\:([^\:\n]*)\:", output) + re.findall(r"! Font \\[^/]*/([^/]*)/", output)
  172.  
  173.         if len(filesSearch) > 0 and filesSearch[0] != previousFile:
  174.             installPackages(getFilePackage(filesSearch[0]))
  175.             previousFile = filesSearch[0]
  176.         elif len(fontsFileSearch) > 0 and fontsFileSearch[0] != previousFontFile:
  177.             installPackages(getFilePackage(fontsFileSearch[0]))
  178.             previousFontFile = fontsFileSearch[0]
  179.         elif len(fontsSearch) > 0 and fontsSearch[0] != previousFont:
  180.             installPackages(getFontPackage(fontsSearch[0]))
  181.             previousFont = fontsSearch[0]
  182.         else:
  183.             done = True
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement