PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/lib-python/2.7/plat-mac/buildtools.py

https://bitbucket.org/yrttyr/pypy
Python | 435 lines | 412 code | 13 blank | 10 comment | 11 complexity | 26da966afd8f80d48ec0292ecbd6f78e MD5 | raw file
  1. """tools for BuildApplet and BuildApplication"""
  2. import warnings
  3. warnings.warnpy3k("the buildtools module is deprecated and is removed in 3.0",
  4. stacklevel=2)
  5. import sys
  6. import os
  7. import string
  8. import imp
  9. import marshal
  10. from Carbon import Res
  11. import Carbon.Files
  12. import Carbon.File
  13. import MacOS
  14. import macostools
  15. import macresource
  16. try:
  17. import EasyDialogs
  18. except ImportError:
  19. EasyDialogs = None
  20. import shutil
  21. BuildError = "BuildError"
  22. # .pyc file (and 'PYC ' resource magic number)
  23. MAGIC = imp.get_magic()
  24. # Template file (searched on sys.path)
  25. TEMPLATE = "PythonInterpreter"
  26. # Specification of our resource
  27. RESTYPE = 'PYC '
  28. RESNAME = '__main__'
  29. # A resource with this name sets the "owner" (creator) of the destination
  30. # It should also have ID=0. Either of these alone is not enough.
  31. OWNERNAME = "owner resource"
  32. # Default applet creator code
  33. DEFAULT_APPLET_CREATOR="Pyta"
  34. # OpenResFile mode parameters
  35. READ = 1
  36. WRITE = 2
  37. # Parameter for FSOpenResourceFile
  38. RESOURCE_FORK_NAME=Carbon.File.FSGetResourceForkName()
  39. def findtemplate(template=None):
  40. """Locate the applet template along sys.path"""
  41. if MacOS.runtimemodel == 'macho':
  42. return None
  43. if not template:
  44. template=TEMPLATE
  45. for p in sys.path:
  46. file = os.path.join(p, template)
  47. try:
  48. file, d1, d2 = Carbon.File.FSResolveAliasFile(file, 1)
  49. break
  50. except (Carbon.File.Error, ValueError):
  51. continue
  52. else:
  53. raise BuildError, "Template %r not found on sys.path" % (template,)
  54. file = file.as_pathname()
  55. return file
  56. def process(template, filename, destname, copy_codefragment=0,
  57. rsrcname=None, others=[], raw=0, progress="default", destroot=""):
  58. if progress == "default":
  59. if EasyDialogs is None:
  60. print "Compiling %s"%(os.path.split(filename)[1],)
  61. process = None
  62. else:
  63. progress = EasyDialogs.ProgressBar("Processing %s..."%os.path.split(filename)[1], 120)
  64. progress.label("Compiling...")
  65. progress.inc(0)
  66. # check for the script name being longer than 32 chars. This may trigger a bug
  67. # on OSX that can destroy your sourcefile.
  68. if '#' in os.path.split(filename)[1]:
  69. raise BuildError, "BuildApplet could destroy your sourcefile on OSX, please rename: %s" % filename
  70. # Read the source and compile it
  71. # (there's no point overwriting the destination if it has a syntax error)
  72. fp = open(filename, 'rU')
  73. text = fp.read()
  74. fp.close()
  75. try:
  76. code = compile(text + '\n', filename, "exec")
  77. except SyntaxError, arg:
  78. raise BuildError, "Syntax error in script %s: %s" % (filename, arg)
  79. except EOFError:
  80. raise BuildError, "End-of-file in script %s" % (filename,)
  81. # Set the destination file name. Note that basename
  82. # does contain the whole filepath, only a .py is stripped.
  83. if string.lower(filename[-3:]) == ".py":
  84. basename = filename[:-3]
  85. if MacOS.runtimemodel != 'macho' and not destname:
  86. destname = basename
  87. else:
  88. basename = filename
  89. if not destname:
  90. if MacOS.runtimemodel == 'macho':
  91. destname = basename + '.app'
  92. else:
  93. destname = basename + '.applet'
  94. if not rsrcname:
  95. rsrcname = basename + '.rsrc'
  96. # Try removing the output file. This fails in MachO, but it should
  97. # do any harm.
  98. try:
  99. os.remove(destname)
  100. except os.error:
  101. pass
  102. process_common(template, progress, code, rsrcname, destname, 0,
  103. copy_codefragment, raw, others, filename, destroot)
  104. def update(template, filename, output):
  105. if MacOS.runtimemodel == 'macho':
  106. raise BuildError, "No updating yet for MachO applets"
  107. if progress:
  108. if EasyDialogs is None:
  109. print "Updating %s"%(os.path.split(filename)[1],)
  110. progress = None
  111. else:
  112. progress = EasyDialogs.ProgressBar("Updating %s..."%os.path.split(filename)[1], 120)
  113. else:
  114. progress = None
  115. if not output:
  116. output = filename + ' (updated)'
  117. # Try removing the output file
  118. try:
  119. os.remove(output)
  120. except os.error:
  121. pass
  122. process_common(template, progress, None, filename, output, 1, 1)
  123. def process_common(template, progress, code, rsrcname, destname, is_update,
  124. copy_codefragment, raw=0, others=[], filename=None, destroot=""):
  125. if MacOS.runtimemodel == 'macho':
  126. return process_common_macho(template, progress, code, rsrcname, destname,
  127. is_update, raw, others, filename, destroot)
  128. if others:
  129. raise BuildError, "Extra files only allowed for MachoPython applets"
  130. # Create FSSpecs for the various files
  131. template_fsr, d1, d2 = Carbon.File.FSResolveAliasFile(template, 1)
  132. template = template_fsr.as_pathname()
  133. # Copy data (not resources, yet) from the template
  134. if progress:
  135. progress.label("Copy data fork...")
  136. progress.set(10)
  137. if copy_codefragment:
  138. tmpl = open(template, "rb")
  139. dest = open(destname, "wb")
  140. data = tmpl.read()
  141. if data:
  142. dest.write(data)
  143. dest.close()
  144. tmpl.close()
  145. del dest
  146. del tmpl
  147. # Open the output resource fork
  148. if progress:
  149. progress.label("Copy resources...")
  150. progress.set(20)
  151. try:
  152. output = Res.FSOpenResourceFile(destname, RESOURCE_FORK_NAME, WRITE)
  153. except MacOS.Error:
  154. destdir, destfile = os.path.split(destname)
  155. Res.FSCreateResourceFile(destdir, unicode(destfile), RESOURCE_FORK_NAME)
  156. output = Res.FSOpenResourceFile(destname, RESOURCE_FORK_NAME, WRITE)
  157. # Copy the resources from the target specific resource template, if any
  158. typesfound, ownertype = [], None
  159. try:
  160. input = Res.FSOpenResourceFile(rsrcname, RESOURCE_FORK_NAME, READ)
  161. except (MacOS.Error, ValueError):
  162. pass
  163. if progress:
  164. progress.inc(50)
  165. else:
  166. if is_update:
  167. skip_oldfile = ['cfrg']
  168. else:
  169. skip_oldfile = []
  170. typesfound, ownertype = copyres(input, output, skip_oldfile, 0, progress)
  171. Res.CloseResFile(input)
  172. # Check which resource-types we should not copy from the template
  173. skiptypes = []
  174. if 'vers' in typesfound: skiptypes.append('vers')
  175. if 'SIZE' in typesfound: skiptypes.append('SIZE')
  176. if 'BNDL' in typesfound: skiptypes = skiptypes + ['BNDL', 'FREF', 'icl4',
  177. 'icl8', 'ics4', 'ics8', 'ICN#', 'ics#']
  178. if not copy_codefragment:
  179. skiptypes.append('cfrg')
  180. ## skipowner = (ownertype != None)
  181. # Copy the resources from the template
  182. input = Res.FSOpenResourceFile(template, RESOURCE_FORK_NAME, READ)
  183. dummy, tmplowner = copyres(input, output, skiptypes, 1, progress)
  184. Res.CloseResFile(input)
  185. ## if ownertype is None:
  186. ## raise BuildError, "No owner resource found in either resource file or template"
  187. # Make sure we're manipulating the output resource file now
  188. Res.UseResFile(output)
  189. if ownertype is None:
  190. # No owner resource in the template. We have skipped the
  191. # Python owner resource, so we have to add our own. The relevant
  192. # bundle stuff is already included in the interpret/applet template.
  193. newres = Res.Resource('\0')
  194. newres.AddResource(DEFAULT_APPLET_CREATOR, 0, "Owner resource")
  195. ownertype = DEFAULT_APPLET_CREATOR
  196. if code:
  197. # Delete any existing 'PYC ' resource named __main__
  198. try:
  199. res = Res.Get1NamedResource(RESTYPE, RESNAME)
  200. res.RemoveResource()
  201. except Res.Error:
  202. pass
  203. # Create the raw data for the resource from the code object
  204. if progress:
  205. progress.label("Write PYC resource...")
  206. progress.set(120)
  207. data = marshal.dumps(code)
  208. del code
  209. data = (MAGIC + '\0\0\0\0') + data
  210. # Create the resource and write it
  211. id = 0
  212. while id < 128:
  213. id = Res.Unique1ID(RESTYPE)
  214. res = Res.Resource(data)
  215. res.AddResource(RESTYPE, id, RESNAME)
  216. attrs = res.GetResAttrs()
  217. attrs = attrs | 0x04 # set preload
  218. res.SetResAttrs(attrs)
  219. res.WriteResource()
  220. res.ReleaseResource()
  221. # Close the output file
  222. Res.CloseResFile(output)
  223. # Now set the creator, type and bundle bit of the destination.
  224. # Done with FSSpec's, FSRef FInfo isn't good enough yet (2.3a1+)
  225. dest_fss = Carbon.File.FSSpec(destname)
  226. dest_finfo = dest_fss.FSpGetFInfo()
  227. dest_finfo.Creator = ownertype
  228. dest_finfo.Type = 'APPL'
  229. dest_finfo.Flags = dest_finfo.Flags | Carbon.Files.kHasBundle | Carbon.Files.kIsShared
  230. dest_finfo.Flags = dest_finfo.Flags & ~Carbon.Files.kHasBeenInited
  231. dest_fss.FSpSetFInfo(dest_finfo)
  232. macostools.touched(destname)
  233. if progress:
  234. progress.label("Done.")
  235. progress.inc(0)
  236. def process_common_macho(template, progress, code, rsrcname, destname, is_update,
  237. raw=0, others=[], filename=None, destroot=""):
  238. # Check that we have a filename
  239. if filename is None:
  240. raise BuildError, "Need source filename on MacOSX"
  241. # First make sure the name ends in ".app"
  242. if destname[-4:] != '.app':
  243. destname = destname + '.app'
  244. # Now deduce the short name
  245. destdir, shortname = os.path.split(destname)
  246. if shortname[-4:] == '.app':
  247. # Strip the .app suffix
  248. shortname = shortname[:-4]
  249. # And deduce the .plist and .icns names
  250. plistname = None
  251. icnsname = None
  252. if rsrcname and rsrcname[-5:] == '.rsrc':
  253. tmp = rsrcname[:-5]
  254. plistname = tmp + '.plist'
  255. if os.path.exists(plistname):
  256. icnsname = tmp + '.icns'
  257. if not os.path.exists(icnsname):
  258. icnsname = None
  259. else:
  260. plistname = None
  261. if not icnsname:
  262. dft_icnsname = os.path.join(sys.prefix, 'Resources/Python.app/Contents/Resources/PythonApplet.icns')
  263. if os.path.exists(dft_icnsname):
  264. icnsname = dft_icnsname
  265. if not os.path.exists(rsrcname):
  266. rsrcname = None
  267. if progress:
  268. progress.label('Creating bundle...')
  269. import bundlebuilder
  270. builder = bundlebuilder.AppBuilder(verbosity=0)
  271. builder.mainprogram = filename
  272. builder.builddir = destdir
  273. builder.name = shortname
  274. builder.destroot = destroot
  275. if rsrcname:
  276. realrsrcname = macresource.resource_pathname(rsrcname)
  277. builder.files.append((realrsrcname,
  278. os.path.join('Contents/Resources', os.path.basename(rsrcname))))
  279. for o in others:
  280. if type(o) == str:
  281. builder.resources.append(o)
  282. else:
  283. builder.files.append(o)
  284. if plistname:
  285. import plistlib
  286. builder.plist = plistlib.Plist.fromFile(plistname)
  287. if icnsname:
  288. builder.iconfile = icnsname
  289. if not raw:
  290. builder.argv_emulation = 1
  291. builder.setup()
  292. builder.build()
  293. if progress:
  294. progress.label('Done.')
  295. progress.inc(0)
  296. ## macostools.touched(dest_fss)
  297. # Copy resources between two resource file descriptors.
  298. # skip a resource named '__main__' or (if skipowner is set) with ID zero.
  299. # Also skip resources with a type listed in skiptypes.
  300. #
  301. def copyres(input, output, skiptypes, skipowner, progress=None):
  302. ctor = None
  303. alltypes = []
  304. Res.UseResFile(input)
  305. ntypes = Res.Count1Types()
  306. progress_type_inc = 50/ntypes
  307. for itype in range(1, 1+ntypes):
  308. type = Res.Get1IndType(itype)
  309. if type in skiptypes:
  310. continue
  311. alltypes.append(type)
  312. nresources = Res.Count1Resources(type)
  313. progress_cur_inc = progress_type_inc/nresources
  314. for ires in range(1, 1+nresources):
  315. res = Res.Get1IndResource(type, ires)
  316. id, type, name = res.GetResInfo()
  317. lcname = string.lower(name)
  318. if lcname == OWNERNAME and id == 0:
  319. if skipowner:
  320. continue # Skip this one
  321. else:
  322. ctor = type
  323. size = res.size
  324. attrs = res.GetResAttrs()
  325. if progress:
  326. progress.label("Copy %s %d %s"%(type, id, name))
  327. progress.inc(progress_cur_inc)
  328. res.LoadResource()
  329. res.DetachResource()
  330. Res.UseResFile(output)
  331. try:
  332. res2 = Res.Get1Resource(type, id)
  333. except MacOS.Error:
  334. res2 = None
  335. if res2:
  336. if progress:
  337. progress.label("Overwrite %s %d %s"%(type, id, name))
  338. progress.inc(0)
  339. res2.RemoveResource()
  340. res.AddResource(type, id, name)
  341. res.WriteResource()
  342. attrs = attrs | res.GetResAttrs()
  343. res.SetResAttrs(attrs)
  344. Res.UseResFile(input)
  345. return alltypes, ctor
  346. def copyapptree(srctree, dsttree, exceptlist=[], progress=None):
  347. names = []
  348. if os.path.exists(dsttree):
  349. shutil.rmtree(dsttree)
  350. os.mkdir(dsttree)
  351. todo = os.listdir(srctree)
  352. while todo:
  353. this, todo = todo[0], todo[1:]
  354. if this in exceptlist:
  355. continue
  356. thispath = os.path.join(srctree, this)
  357. if os.path.isdir(thispath):
  358. thiscontent = os.listdir(thispath)
  359. for t in thiscontent:
  360. todo.append(os.path.join(this, t))
  361. names.append(this)
  362. for this in names:
  363. srcpath = os.path.join(srctree, this)
  364. dstpath = os.path.join(dsttree, this)
  365. if os.path.isdir(srcpath):
  366. os.mkdir(dstpath)
  367. elif os.path.islink(srcpath):
  368. endpoint = os.readlink(srcpath)
  369. os.symlink(endpoint, dstpath)
  370. else:
  371. if progress:
  372. progress.label('Copy '+this)
  373. progress.inc(0)
  374. shutil.copy2(srcpath, dstpath)
  375. def writepycfile(codeobject, cfile):
  376. import marshal
  377. fc = open(cfile, 'wb')
  378. fc.write('\0\0\0\0') # MAGIC placeholder, written later
  379. fc.write('\0\0\0\0') # Timestap placeholder, not needed
  380. marshal.dump(codeobject, fc)
  381. fc.flush()
  382. fc.seek(0, 0)
  383. fc.write(MAGIC)
  384. fc.close()