PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/buildit.py

https://code.google.com/p/axe-gui-libraries/
Python | 416 lines | 397 code | 8 blank | 11 comment | 10 complexity | 878adad671816bb7d6617cb892464b24 MD5 | raw file
Possible License(s): GPL-3.0
  1. #!/usr/bin/env python
  2. # Axe GUI Library (InfinGUI?) Builder v1.0
  3. # (C) Copyright 2011 Albert Huang.
  4. #
  5. import tokenizer
  6. import os, sys
  7. import getopt
  8. import webbrowser
  9. import subprocess
  10. import datetime
  11. from cStringIO import StringIO
  12. # Version variable
  13. VERSION="1.0"
  14. # Core variables
  15. # Debugging?
  16. global DEBUG
  17. DEBUG=0
  18. # Where to get help?
  19. global webhelpurl
  20. webhelpurl='http://www.omnimaga.org/index.php?board=146.0'
  21. # Builder actions
  22. global actions
  23. actions=[]
  24. # What to build?
  25. global tobuild
  26. tobuild=[]
  27. # SPASM path
  28. global spasmpath
  29. spasmpath=""
  30. # What files need to be tokenized?
  31. # Format: [ 'source file', 'target 8xp file' ]
  32. # Separate each spec array with a comma.
  33. TOTOKEN = [
  34. ['AXEGUI.txt', 'AXEGUI.8xp'],
  35. ['GUIDEMO.txt', 'GUIDEMO.8xp']
  36. ]
  37. # What files need to be assembled?
  38. # Format: [ 'Z80 source file', 'target 8xp file' ]
  39. # Separate each spec array with a comma.
  40. TOASSEM = [
  41. ['DCSAxiom.z80', 'DCSAxiom.8xp']
  42. ]
  43. ##################################################
  44. # Core functions
  45. ##################################################
  46. # Hexidecimal to decimal converter
  47. def hex2dec(s):
  48. return int(str(s), 16)
  49. # Find element in array
  50. def findelement(string, thelist):
  51. """Return first item in sequence where f(item) == True."""
  52. dprint("[DEBUG] [FindElement] Search item is "+string)
  53. dprint("[DEBUG] [FindElement] List to look inside of is "+str(thelist))
  54. for item in thelist:
  55. dprint("[DEBUG] [FindElement] String is "+string+", item is "+item)
  56. if string == item:
  57. return True
  58. break
  59. return False
  60. ##################################################
  61. # CLI functions
  62. ##################################################
  63. # Help/usage
  64. def help_usage():
  65. print "Axe GUI Builder v"+VERSION+""
  66. bar=""
  67. for i in range(0,len("Axe GUI Builder v"+VERSION+"")):
  68. bar=bar+"="
  69. print bar
  70. print "Usage: "+sys.argv[0]+" [option(s)]\n"
  71. print "General options:"
  72. print "\t"+"-b, --build"+"\t\t"+"Build Axe GUI. (Default) This implies -n"
  73. print "\t\t\t\t"+"(building regular Axe GUI source code) and -s (building Axe"
  74. print "\t\t\t\t"+"GUI DCS edition/wrapper)."
  75. print "\t"+"-r, --reverse"+"\t\t"+"Detokenize Axe GUI source to raw text."
  76. print "\t\t\t\t"+"Useful for recovering the raw text source of Axe GUI."
  77. print "\t"+"-c, --clean"+"\t\t"+"Remove any built files."
  78. print "\t"+"-t, --test"+"\t\t"+"Test the built files - send them to WabbitEmu."
  79. print "Building choice options:"
  80. print "\t"+"-n, --normalbuild"+"\t"+"Build regular Axe GUI source code (the"
  81. print "\t\t\t\t"+"'original' Axe GUI)."
  82. print "\t"+"-s, --dcsbuild"+"\t\t"+"Build Axe GUI DCS edition/wrapper."
  83. print "Building options:"
  84. print "\t"+"-p, --spasmpath=[path]"+"\t"+"Specify path to the SPASM assembler."
  85. print "\t\t\t\t"+"This is usually automatically detected by the builder."
  86. print "\t"+"-d, --debug"+"\t"+"Enable builder debugging."
  87. print "Help/About:"
  88. print "\t"+"-h, --help"+"\t\t"+"Get help! :) (Shows this help)"
  89. print "\t"+"-w, --webhelp"+"\t\t"+"Go to PRIZM hacking site for more info/help! :)"
  90. #print "\t"+"-c, --credits"+"\t\t"+"Who made this awesome program? :D"
  91. print "\t\t\t\t"+"(This also shows the license of this program.)"
  92. print ""
  93. print "You may specify multiple commands to the builder, and the builder"
  94. print "will execute each based on the order of the commands (arguments)."
  95. # Credits/license
  96. def credits_license():
  97. print "Axe GUI Builder v"+VERSION+""
  98. bar=""
  99. for i in range(0,len("Axe GUI Builder v"+VERSION+"")):
  100. bar=bar+"="
  101. print bar
  102. print "(C) Copyright 2011 Albert Huang."
  103. print ""
  104. print "This program is under.... [insert license here, GPL perhaps?]"
  105. # Pretty printing?
  106. def tprint(str="\n"):
  107. if str=="\n":
  108. print ""
  109. else:
  110. print " == "+str
  111. # Debug printing - only prints if debugging is enabled, either from the
  112. # commandline or in the script.
  113. def dprint(str="\n"):
  114. global DEBUG
  115. if DEBUG==1:
  116. if str=="\n":
  117. print ""
  118. else:
  119. print " ++ "+str
  120. # Web help function
  121. def LaunchWebHelp():
  122. tprint("Opening online help...")
  123. webbrowser.open(webhelpurl)
  124. tprint("Online help launched. If nothing happens after a few")
  125. tprint("moments, please open the following URL:")
  126. tprint(webhelpurl)
  127. # Builder functions
  128. def doclean():
  129. tprint("Cleaning source tree...")
  130. for file in [ "src/AXEGUI_READY.txt", "src/AXEGUI.8xp", "src/dcs7axiom/DCSAxiom.8xp" ]:
  131. try:
  132. os.remove(file)
  133. tprint("Removed file "+file+"!")
  134. except:
  135. tprint("The file "+file+" doesn't exist, so skipping.")
  136. def dobuild():
  137. tprint("Building...")
  138. for build in tobuild:
  139. if build == 'normal':
  140. tprint("\tBuilding Axe GUI...")
  141. for src in TOTOKEN:
  142. fileexist = 0
  143. try:
  144. open(os.path.join("src", "axegui", src[1]), "r").close()
  145. fileexist = 1
  146. except:
  147. fileexist = 0
  148. if fileexist == 1:
  149. tprint("\t\tSkipping "+src[0]+", since it's already built ("+src[1]+" already exists)!")
  150. else:
  151. # Create the header!
  152. tprint("\t\tAdding header to "+src[0]+"...")
  153. # Read the original source file
  154. origsrcf = open(os.path.join("src", "axegui", src[0]), "r")
  155. origsrc = origsrcf.read()
  156. origsrcf.close()
  157. # Check name, and pad with null characters if necessary (\x00)
  158. # or trim it down.
  159. name = src[0].split('.')[0].upper()
  160. if len(name) < 8:
  161. remain = 8 - len(name)
  162. for n in range(0, remain):
  163. name = name + "\x00"
  164. if len(name) > 8:
  165. name = name[:8]
  166. # Create comment!
  167. # The length of the comment is 42, padded by null bytes if necessary.
  168. # Comment is "Prgm by AGLBldr v1.0 on xx/xx/xx xx:xx:xx"
  169. # Comment is "Single file dated Fri Mar 19 16:27:39 20 "
  170. datestr = datetime.datetime.now().strftime("%m/%d/%y %H:%M:%S")
  171. comment = "Prgm by AGLBldr v1.0 on "+datestr
  172. if len(comment) < 42:
  173. remain = 42 - len(comment)
  174. for n in range(0, remain):
  175. comment = comment + "\x00"
  176. if len(name) > 42:
  177. comment = comment[:42]
  178. # Is the program protected?
  179. protectedfield = "not protected"
  180. # Put it all together! :D
  181. finalsrc = name + "\n" + protectedfield + "\n" + comment + "\n" + origsrc
  182. newname = src[0].replace('.txt', '_READY.txt')
  183. tprint("\t\tFormatting "+src[0]+" to "+newname+"...")
  184. ftmp = open(os.path.join("src", "axegui", newname), "w")
  185. ftmp.write(finalsrc)
  186. ftmp.close()
  187. tprint("\t\tTokenizing "+src[0]+" (using formatted "+newname+")...")
  188. savedstdout = sys.stdout
  189. sys.stdout = compileout = StringIO()
  190. ret = tokenizer.recompile(os.path.join("src", "axegui", newname), os.path.join("src", "axegui", src[1]))
  191. sys.stdout = savedstdout
  192. print("\t\t\t"+compileout.getvalue().strip())
  193. if ret:
  194. tprint("\t\tTokenization successful! :D")
  195. else:
  196. print "Tokenization failed! :( See the above error messages for details."
  197. sys.exit(1)
  198. if build == 'dcs':
  199. tprint("\tBuilding Axe GUI, DCS edition/wrapper...")
  200. for src in TOASSEM:
  201. fileexist = 0
  202. try:
  203. open(os.path.join("src", "dcs7axiom", src[1]), "r").close()
  204. fileexist = 1
  205. except:
  206. fileexist = 0
  207. if fileexist == 1:
  208. tprint("\t\tSkipping "+src[0]+", since it's already built ("+src[1]+" already exists)!")
  209. else:
  210. tprint("\t\tAssembling "+src[0]+"...")
  211. olddir=os.getcwd()
  212. os.chdir(os.path.join("src", "dcs7axiom"))
  213. ASM_PROC = subprocess.Popen([spasmpath, '-N', src[0], src[1]], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
  214. ASM_OUTPUT = ASM_PROC.stdout.read()
  215. ASM_ERRCODE = ASM_PROC.wait()
  216. os.chdir(olddir)
  217. for line in ASM_OUTPUT.strip().split("\n"):
  218. tprint("\t\t\t"+line)
  219. if (ASM_ERRCODE != 0) and (ASM_ERRCODE != 1):
  220. print "SPASM returned error code: "+str(ASM_ERRCODE)
  221. print "Assembling failed! :( See the above error messages for details."
  222. sys.exit(1)
  223. def doreverse():
  224. tprint("Detokenizing files...")
  225. for src in TOTOKEN:
  226. fileexist = 0
  227. try:
  228. open(os.path.join("src", src[1]), "r").close()
  229. fileexist = 1
  230. except:
  231. fileexist = 0
  232. if fileexist == 1:
  233. tprint("\t\tDetokenizing "+src[1]+"...")
  234. savedstdout = sys.stdout
  235. sys.stdout = decompileout = StringIO()
  236. ret = tokenizer.decompile(os.path.join("src", src[1]), os.path.join("src", "rev_"+src[0]))
  237. sys.stdout = savedstdout
  238. print("\t\t\t"+decompileout.getvalue().strip())
  239. if ret:
  240. tprint("\t\tDetokenization successful! :D")
  241. else:
  242. print "Detokenization failed! :( See the above error messages for details."
  243. sys.exit(1)
  244. else:
  245. tprint("\t\tCouldn't find "+src[1]+", so not detokenizing...")
  246. # The main function!
  247. def main(argv=None):
  248. global actions, DEBUG, tobuild, spasmpath
  249. if argv is None:
  250. argv = sys.argv
  251. args = []
  252. try:
  253. # NOTE TO SELF: for short args, ":" will require argument,
  254. # "=" in long args to require an argument
  255. opts, args = getopt.gnu_getopt(argv[1:], "brctnsp:dhwl", ["build", "reverse", "clean", "test", "normalbuild", "dcsbuild", "spasmpath", "debug", "help", "webhelp", "credits"])
  256. except getopt.error, err:
  257. print "ERROR: "+str(err.msg)+"\n"
  258. help_usage()
  259. sys.exit(2)
  260. for opt, arg in opts:
  261. # Is the user asking for help?
  262. if opt in ("-h", "--help"):
  263. help_usage()
  264. sys.exit()
  265. # ...or do they like web help?
  266. if opt in ("-w", "--webhelp"):
  267. LaunchWebHelp()
  268. sys.exit()
  269. # Maybe they want to know who wrote this program!
  270. if opt in ("-l", "--credits"):
  271. credits_license()
  272. sys.exit()
  273. if opt in ("-b", "--build"):
  274. if (not(findelement("normal", tobuild))):
  275. tobuild.append("normal")
  276. if(not(findelement("dcs", tobuild))):
  277. tobuild.append("dcs")
  278. actions.append("build")
  279. if opt in ("-n", "--normalbuild"):
  280. if (not(findelement("normal", tobuild))):
  281. tobuild.append("normal")
  282. if(not(findelement("build", actions))):
  283. actions.append("build")
  284. if opt in ("-s", "--dcsbuild"):
  285. if(not(findelement("dcs", tobuild))):
  286. tobuild.append("dcs")
  287. if(not(findelement("build", actions))):
  288. actions.append("build")
  289. # Other tasks
  290. # Detokenize the compiled file?
  291. if opt in ("-r", "--reverse"):
  292. actions.append("reverse")
  293. # Clean the source tree?
  294. if opt in ("-c", "--clean"):
  295. actions.append("clean")
  296. # Grab assembler path, if specified
  297. if opt in ("-p", "--spasmpath"):
  298. spasmpath = arg
  299. # Does the user want to debug?
  300. if opt in ("-d", "--debug"):
  301. global DEBUG
  302. DEBUG=1
  303. if not len(sys.argv)>1:
  304. print "ERROR: No arguments specified!"
  305. help_usage()
  306. sys.exit(1)
  307. tprint("Doing sanity checks...")
  308. # Check for SPASM and a valid SPASM path.
  309. if spasmpath != '':
  310. tprint("Checking if SPASM path is correct...")
  311. try:
  312. open(spasmpath, "r").close()
  313. except:
  314. print "ERROR: The SPASM assembler path you have specified is invalid!"
  315. print "Check to make sure that the program exists, there are no"
  316. print "permission problems, and that the program is accessible, and"
  317. print "then try again."
  318. sys.exit(1)
  319. else:
  320. paths=os.environ["PATH"].split(":")
  321. found=[]
  322. finalpick=""
  323. tprint("Searching for spasm in PATH...")
  324. for path in paths:
  325. try:
  326. if sys.platform == 'win32':
  327. open(os.path.join(path, "spasm.exe")).close()
  328. found.append(os.path.join(path, "spasm.exe"))
  329. else:
  330. open(os.path.join(path, "spasm")).close()
  331. found.append(os.path.join(path, "spasm"))
  332. except:
  333. # Do nothing
  334. continue
  335. if len(found) == 0:
  336. print "ERROR: The SPASM assembler couldn't be found!"
  337. print "Check to make sure that the program exists, there are no"
  338. print "permission problems, and that the program is accessible, and"
  339. print "then try again. It's likely that you will need to install"
  340. print "SPASM assembler before you can run this again."
  341. sys.exit(1)
  342. if len(found) > 1:
  343. print "Multiple SPASM assemblers have been found!"
  344. n=1
  345. for pick in found:
  346. print str(n)+": "+pick
  347. n=n+1
  348. print "Enter the number of the SPASM assembler you want to use, then"
  349. print "press ENTER."
  350. choice = raw_input("Choose: ")
  351. if 1 < int(choice) < (len(found)):
  352. spasmpath = found[int(choice)-1]
  353. else:
  354. spasmpath = found[0]
  355. tprint("Using SPASM assembler found in "+spasmpath+"...")
  356. if len(actions) > 1:
  357. tprint("The builder will:")
  358. for n in range(0, len(actions)):
  359. if actions[n] == 'clean':
  360. desc = "clean up source tree"
  361. if actions[n] == 'build':
  362. desc = "build the source code"
  363. if actions[n] == 'reverse':
  364. desc = "detokenize files into source code"
  365. if n == (len(actions) - 1):
  366. tprint("\t"+"and "+desc+"!")
  367. else:
  368. tprint("\t"+desc+",")
  369. dprint("Friendly print debug: Actions array is "+str(actions))
  370. dprint("Friendly print debug: for loop range array is "+str(range(0, len(actions))))
  371. for act in actions:
  372. if act == 'clean':
  373. doclean()
  374. if act == 'build':
  375. dobuild()
  376. if act == 'reverse':
  377. doreverse()
  378. if __name__ == "__main__":
  379. sys.exit(main())