Advertisement
ijontichy

packagepk3.py

Mar 4th, 2013
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.44 KB | None | 0 0
  1. #!/usr/bin/env python3
  2.  
  3. import sys, os, shutil, zipfile, tempfile
  4. import os.path
  5. from subprocess import Popen, PIPE
  6.  
  7. import which
  8. import ansicodes
  9. from pathwalker import pathwalker
  10. from decpreproc import *
  11.  
  12. WAD_DIR = os.environ["HOME"] + "/.pwads"
  13.  
  14. LINEPROCS   = [defineproc.DefineProc(), includeproc.IncludeDirProc()]
  15.  
  16. NUMBERS     = "0123456789"
  17. OKSTR       = ansicodes.mapColors("[ ok ]", "--22--")
  18. OKLEN       = len(ansicodes.stripCodes(OKSTR) )
  19.  
  20. MAKE_ACT    = "make"
  21. TEST_ACT    = "test"
  22. PACKAGE_ACT = "package"
  23. HELP_ACT    = "help"
  24.  
  25. USAGE = "usage: {} [{}] <args>".format(os.path.basename(sys.argv[0]), "|".join([MAKE_ACT, TEST_ACT, PACKAGE_ACT, HELP_ACT]))
  26.  
  27. USAGE_FULL = """
  28. - make: Builds a PK3 from the files in the pk3/ directory, with the name being
  29.  that of the current directory.
  30.  
  31. - test [port=zandronum] <args>: Runs 'make', and then runs 'port' with the PK3,
  32.  along with any arguments you choose.
  33.  
  34. - package [version]: Runs 'make', with 'version' at the end of the name, copies
  35.  it to a ZIP with the same name, and copies the PK3 to "{0}" if the
  36.  directory exists.
  37.  
  38. - help: Prints this.
  39.  
  40. Examples:
  41.  # PWD is /home/derp/derpPK3
  42.  packagepk3.py make  # derpPK3.pk3
  43.  packagepk3.py test zandronum -file nuts
  44.  packagepk3.py package 1_5  # derpPK3_1_5.pk3, derpPK3_1_5.zip,
  45.                             # /home/derp/.zdoom/derpPK3_1_5.pk3
  46.  # PWD is /home/derp/derpWAD
  47.  packagepk3.py package 1_5  # derpWAD1_5.pk3, derpWAD1_5.zip,
  48.                             # /home/derp/.zdoom/derpWAD1_5.pk3
  49. """.format(WAD_DIR)
  50.  
  51. DECORATE_EXTS   = ("dec",)
  52. ACS_EXTS        = ("c", "acs")
  53. ACS_OBJ_EXTS    = ("o",)
  54.  
  55. ACC_ERROR   = "**** ERROR ****"
  56.  
  57. DEFAULT_ARGS = ["-iwad doom2", "-warp 01"]
  58.  
  59. def warn(reason):
  60.     print("warning:", reason, file=sys.stderr)
  61.  
  62.  
  63. def errorExit(code = 1, reason = None):
  64.     if reason:
  65.         print("ERROR:", reason, file=sys.stderr)
  66.  
  67.     sys.exit(code)
  68.  
  69. def usageExit(code = 1, reason = None):
  70.     if reason:
  71.         print("error:", reason, file=sys.stderr)
  72.  
  73.     usage()
  74.     sys.exit(code)
  75.  
  76. def usage():
  77.     print(USAGE)
  78.  
  79. def usageFull():
  80.     usage()
  81.     print(USAGE_FULL)
  82.  
  83. NO_ARGS, NO_PK3, NO_ACC, NO_VERSION, ACC_FAIL, NO_BINARY, PREPROC_FAIL = range(4, 11)
  84.  
  85. PK3NAME = os.path.basename(os.path.realpath("."))
  86.  
  87. if not os.path.isdir(WAD_DIR):
  88.     warn("{} does not exist - no pk3 auto-installation will be done".format(WAD_DIR))
  89.     WAD_DIR = None
  90.  
  91. zdoomExe = "zdoom" + (".exe" if sys.platform[:3] == "win" else "")
  92. zandronumExe = "zandronum" + (".exe" if sys.platform[:3] == "win" else "")
  93.  
  94. if which.is_exe(zdoomExe):
  95.     ZDOOM = os.path.realpath(zdoomExe)
  96. else:
  97.     ZDOOM = which.which(zdoomExe)
  98.  
  99.     if ZDOOM is None:
  100.         warn("no zdoom in PATH or PWD")
  101.  
  102. if which.is_exe(zandronumExe):
  103.     ZANDRONUM = os.path.realpath(zandronumExe)
  104. else:
  105.     ZANDRONUM = which.which(zandronumExe)
  106.  
  107.     if ZANDRONUM is None:
  108.         warn("no zandronum in PATH or PWD")
  109.  
  110.  
  111. if which.is_exe("acc"):
  112.     ACC = os.path.realpath("acc")
  113. else:
  114.     ACC = which.which("acc")
  115.  
  116.     if ACC is None:
  117.         ACC_DIR = None
  118.         errorExit(NO_ACC, "no acc in PATH or PWD")
  119.     else:
  120.         ACC_DIR = ACC.rpartition("/")[0]
  121.  
  122.  
  123.  
  124. def playPK3(pk3, binary=ZANDRONUM, *args):
  125.     args = list(args)
  126.  
  127.     if binary is None:
  128.         errorExit(1, "no binary")
  129.  
  130.     if binary[0] in "+-":
  131.         args[:0] = [binary]
  132.         binary = ZANDRONUM
  133.  
  134.     pk3 = os.path.realpath(pk3)
  135.  
  136.     if not os.path.isfile(pk3):
  137.         errorExit(NO_BINARY, "somehow, {} doesn't exist".format(pk3))
  138.  
  139.     binPath = which.which(binary)
  140.  
  141.     if not binPath:
  142.         errorExit(NO_BINARY, "no such binary {}".format(binary))
  143.  
  144.     if not args:
  145.         args = DEFAULT_ARGS
  146.     elif isinstance(args, str):
  147.         args = [i for i in args.split()] or []
  148.     else:
  149.         args = list(args)
  150.  
  151.     pk3Game = Popen([binPath] + args + ["-file {}".format(pk3)])
  152.    
  153.     try:
  154.         pk3Game.wait()
  155.     except KeyboardInterrupt:
  156.         pk3Game.send_signal(2)   # SIGINT
  157.         sys.exit(2)
  158.  
  159.     return 0
  160.  
  161.  
  162. def compileACS(file):
  163.     file2 = file
  164.     file = os.path.realpath(file)
  165.  
  166.     if not os.path.isfile(file):
  167.         errorExit(ACC_FAIL, "must provide name of actual file")
  168.  
  169.     newFile = file.rpartition(".")[:2] + ("o",)
  170.     newFile = "".join(newFile)
  171.  
  172.     newFile2 = file2.rpartition(".")[:2] + ("o",)
  173.     newFile2 = "".join(newFile2)
  174.  
  175.     cmdline = [ACC, "-i", ACC_DIR, file, newFile]
  176.     accProc = Popen(cmdline, stdout=PIPE, stderr=PIPE)
  177.  
  178.     accOut, accErr = (i.decode().split("\n") for i in accProc.communicate())
  179.  
  180.     if ACC_ERROR in accErr:
  181.         reasonLines = accErr[accErr.index(ACC_ERROR)+1:]
  182.         reason = "\n".join(reasonLines)
  183.  
  184.         raise RuntimeError(reason)
  185.  
  186.     return newFile2
  187.  
  188. def statPrint(statStr):
  189.     statParts = statStr.split("\x1F")
  190.     statParts[0] = ansicodes.mapColors(statParts[0], "A6666A")
  191.     statParts[1:1] = [" "]
  192.     statColor = ansicodes.mapColors(statParts, "---")
  193.     print(statColor, end="")
  194.  
  195.  
  196. def makePK3(aArgs):
  197.     if not os.path.isdir("pk3"):
  198.         errorExit(NO_PK3, "no pk3/ directory")
  199.  
  200.     parseDecorate = "parse" in (i.lower() for i in aArgs)
  201.  
  202.     pk3Walk = pathwalker.PathWalker('pk3')
  203.  
  204.     oldDir = os.getcwd()
  205.     os.chdir("pk3")
  206.  
  207.     pk3Name     = "{}.pk3".format(PK3NAME)
  208.     pk3Name2    = os.path.realpath("../{}.pk3".format(PK3NAME) )
  209.     pk3Zip      = zipfile.ZipFile(pk3Name2, "w", zipfile.ZIP_DEFLATED)
  210.     pk3Files    = pk3Walk.walk(abs=1, fFilter=lambda x: not x.endswith("*.o")).flattenFiles(rel=1)
  211.     pk3Total    = len(pk3Files)
  212.     toBuild     = []
  213.     nLen        = 0
  214.     padLength   = max(len(i) for i in pk3Files)
  215.     conWidth    = os.environ.get("COLUMNS", 80)
  216.     builtFiles  = []
  217.  
  218.     try:
  219.         if os.path.isfile("pk3/buildFiles"):
  220.             toBuild = open("pk3/buildFiles", "r").readlines()
  221.  
  222.         for i, file in enumerate(pk3Files):
  223.             j = i + 1
  224.  
  225.             oLen = nLen
  226.             perc = "{}%".format(int( ( (j / pk3Total) * 100) ) )
  227.             nLen = len(perc)
  228.  
  229.             statStr = "[{:>4}]\x1F".format(perc)
  230.            
  231.             fPath, fName = os.path.realpath(file).rsplit("/", 1)
  232.  
  233.             try:
  234.                 fNameClean, fExt = fName.rsplit(".", 1)
  235.             except ValueError:
  236.                 fNameClean, fExt = fName, ""
  237.  
  238.             zipFName = file.lstrip("./")
  239.  
  240.             if fExt in DECORATE_EXTS and parseDecorate:  # DECORATE
  241.                 statStr += "preprocessing {}...".format(fName)
  242.                 statPrint(statStr)
  243.  
  244.                 prevDir = os.getcwd()
  245.  
  246.                 os.chdir(fPath)
  247.  
  248.                 dTmpName = tempfile.mkstemp(prefix="dec")[1]
  249.                 dTmp     = open(dTmpName, "w")
  250.                
  251.                 try:
  252.                     dTmp.write(fileproc.processFile(fName, LINEPROCS))
  253.                 except lineproc.LineProcError as exc:
  254.                     reason = exc.args[0]
  255.  
  256.                     print("\n\n !!! ERROR !!!", file=sys.stderr)
  257.                     print("  In file {}:".format(fPath), file=sys.stderr)
  258.                     print("    {}".format(reason), file=sys.stderr)
  259.                     sys.exit(PREPROC_FAIL)
  260.  
  261.                 finally:
  262.                     dTmp.close()
  263.                
  264.                 os.chdir(prevDir)
  265.  
  266.                 pk3Zip.write(dTmpName, zipFName)
  267.            
  268.             elif fExt in ACS_OBJ_EXTS:
  269.                 for acsExt in ACS_EXTS:
  270.                     if file in builtFiles:
  271.                         statStr += "already added {}...".format(fName)
  272.                         statPrint(statStr)
  273.                         break
  274.  
  275.                     possibleSource = fPath + os.sep + fNameClean + "." + acsExt
  276.  
  277.                     if os.path.exists(possibleSource):
  278.                         statStr += "ignore {}...".format(fName)
  279.                         statPrint(statStr)
  280.                         break
  281.                 else:
  282.                     statStr += "adding {}...".format(fName)
  283.                     statPrint(statStr)
  284.                     pk3Zip.write(file, zipFName)
  285.  
  286.             elif (fExt in ACS_EXTS and (not toBuild)) or (fName in toBuild):  # ACS
  287.                 statStr += "building {}...".format(fName)
  288.                 statPrint(statStr)
  289.                 objFile = compileACS(file)
  290.                 pk3Zip.write(objFile, objFile.lstrip("./"))
  291.                 pk3Zip.write(file, zipFName)
  292.  
  293.                 builtFiles.append(objFile)
  294.  
  295.             else:
  296.                 statStr += "adding {}...".format(fName)
  297.                 statPrint(statStr)
  298.                 pk3Zip.write(file, zipFName)
  299.  
  300.             statClean = ansicodes.stripCodes(statStr)
  301.             print(" " * (conWidth - len(statClean) - OKLEN) + OKSTR)
  302.  
  303.     except KeyboardInterrupt:
  304.         print()
  305.         os.remove(pk3Name2)
  306.         sys.exit(130)
  307.  
  308.     except RuntimeError as exc:
  309.         print()
  310.         errorExit(1, "\n" + exc.args[0])
  311.  
  312.     pk3Zip.close()
  313.     os.chdir(oldDir)
  314.  
  315.     return pk3Name
  316.  
  317. def testPK3(aArgs):
  318.     aArgs = list(aArgs)
  319.  
  320.     try:
  321.         parseIndex = [i.lower() for i in aArgs].index("parse")
  322.         aArgs.pop(parseIndex)
  323.         pk3 = makePK3(["parse"])
  324.     except ValueError:
  325.         pk3 = makePK3([])
  326.    
  327.     playPK3(pk3, *aArgs)
  328.  
  329.  
  330. def packagePK3(aArgs):
  331.  
  332.     if len(aArgs) < 1:
  333.         usageExit(NO_VERSION, "no version supplied")
  334.  
  335.     pk3 = makePK3(["parse"])
  336.  
  337.     print("renaming... ", end="")
  338.     sys.stdout.flush()
  339.  
  340.     pk3Name = os.path.basename(pk3)
  341.     pk3Base = pk3Name.rsplit(".", 1)[0]
  342.  
  343.     zipBase = [pk3Base, aArgs[0]]
  344.  
  345.     if pk3Base[-1] in NUMBERS:
  346.         zipBase = "_".join(zipBase)
  347.     else:
  348.         zipBase = "".join(zipBase)
  349.  
  350.     zipName  = "{}.zip".format(zipBase)
  351.     pk3Name2 = "{}.pk3".format(zipBase)
  352.  
  353.     shutil.move(pk3, pk3Name2)
  354.     print("done.")
  355.  
  356.     print("packaging... ", end="")
  357.     sys.stdout.flush()
  358.  
  359.  
  360.     pk3Zip = zipfile.ZipFile(zipName, "w")
  361.  
  362.     for file in (pk3Name2, "README", "README.txt", "CHANGELOG", "CHANGELOG.txt"):
  363.         if os.path.isfile(file):
  364.             pk3Zip.write(file, file)
  365.  
  366.     pk3Zip.close()
  367.  
  368.     print(zipName)
  369.  
  370.     if WAD_DIR is not None:
  371.         pk3Name3 = WAD_DIR + "/" + pk3Name2
  372.  
  373.         print("copying {} to {}... ".format(pk3Name2, pk3Name3), end="")
  374.         sys.stdout.flush()
  375.  
  376.         shutil.copy(pk3Name2, pk3Name3)
  377.         print("done.")
  378.  
  379. def printHelp(aArgs):
  380.     usageFull()
  381.  
  382.  
  383. choices = {MAKE_ACT: makePK3, TEST_ACT: testPK3, PACKAGE_ACT: packagePK3,
  384.             HELP_ACT: printHelp}
  385.  
  386. #######
  387. ##
  388. #   MAIN CODE
  389. ##
  390. ###
  391.  
  392. def main(*args, **kwargs):
  393.     args = list(args)
  394.  
  395.     if len(args) < 2:
  396.         args.extend(input("wat do? ").split())
  397.  
  398.     action = args[1].lower()
  399.     aArgs  = args[2:]
  400.  
  401.     if action in choices:
  402.         choices[action](aArgs)
  403.     else:
  404.         usageExit(127, "not a valid command")
  405.  
  406. if __name__ == "__main__":
  407.     main(*sys.argv)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement