1010import MACFS
1111import MacOS
1212import macostools
13+ import macresource
1314import EasyDialogs
15+ import shutil
1416
1517
1618BuildError = "BuildError"
4244
4345def findtemplate (template = None ):
4446 """Locate the applet template along sys.path"""
47+ if MacOS .runtimemodel == 'macho' :
48+ if template :
49+ return template
50+ return findtemplate_macho ()
4551 if not template :
4652 template = TEMPLATE
4753 for p in sys .path :
@@ -55,6 +61,13 @@ def findtemplate(template=None):
5561 raise BuildError , "Template %s not found on sys.path" % `template`
5662 file = file .as_pathname ()
5763 return file
64+
65+ def findtemplate_macho ():
66+ execpath = sys .executable .split ('/' )
67+ if not 'Contents' in execpath :
68+ raise BuildError , "Not running from a .app bundle: %s" % sys .executable
69+ i = execpath .index ('Contents' )
70+ return '/' .join (execpath [:i ])
5871
5972
6073def process (template , filename , output , copy_codefragment ):
@@ -82,13 +95,17 @@ def process(template, filename, output, copy_codefragment):
8295 destname = filename [:- 3 ]
8396 rsrcname = destname + '.rsrc'
8497 else :
85- destname = filename + ".applet"
98+ if MacOS .runtimemodel == 'macho' :
99+ destname = filename + '.app'
100+ else :
101+ destname = filename + ".applet"
86102 rsrcname = filename + '.rsrc'
87103
88104 if output :
89105 destname = output
90106
91- # Try removing the output file
107+ # Try removing the output file. This fails in MachO, but it should
108+ # do any harm.
92109 try :
93110 os .remove (destname )
94111 except os .error :
@@ -97,6 +114,8 @@ def process(template, filename, output, copy_codefragment):
97114
98115
99116def update (template , filename , output ):
117+ if MacOS .runtimemodel == 'macho' :
118+ raise BuildError , "No updating yet for MachO applets"
100119 if DEBUG :
101120 progress = EasyDialogs .ProgressBar ("Updating %s..." % os .path .split (filename )[1 ], 120 )
102121 else :
@@ -113,6 +132,8 @@ def update(template, filename, output):
113132
114133
115134def process_common (template , progress , code , rsrcname , destname , is_update , copy_codefragment ):
135+ if MacOS .runtimemodel == 'macho' :
136+ return process_common_macho (template , progress , code , rsrcname , destname , is_update )
116137 # Create FSSpecs for the various files
117138 template_fss = macfs .FSSpec (template )
118139 template_fss , d1 , d2 = macfs .ResolveAliasFile (template_fss )
@@ -238,6 +259,99 @@ def process_common(template, progress, code, rsrcname, destname, is_update, copy
238259 if DEBUG :
239260 progress .label ("Done." )
240261
262+ def process_common_macho (template , progress , code , rsrcname , destname , is_update ):
263+ # First make sure the name ends in ".app"
264+ if destname [- 4 :] != '.app' :
265+ destname = destname + '.app'
266+ # Now deduce the short name
267+ shortname = os .path .split (destname )[1 ]
268+ if shortname [- 4 :] == '.app' :
269+ # Strip the .app suffix
270+ shortname = shortname [:- 4 ]
271+ plistname = shortname + '.plist'
272+ # Start with copying the .app framework
273+ if not is_update :
274+ exceptlist = ["Contents/Info.plist" ,
275+ "Contents/Resources/English.lproj/InfoPlist.strings" ,
276+ "Contents/Resources/python.rsrc" ,
277+ ]
278+ copyapptree (template , destname , exceptlist )
279+ # Now either use the .plist file or the default
280+ if plistname and os .path .exists (plistname ):
281+ shutil .copy2 (plistname , os .path .join (destname , 'Contents/Info.plist' ))
282+ # XXXX Wrong. This should be parsed from plist file
283+ # icnsname = 'PythonApplet.icns'
284+ ownertype = 'PytA'
285+ # XXXX Should copy .icns file
286+ else :
287+ plistname = os .path .join (template , 'Contents/Resources/Applet-Info.plist' )
288+ plistdata = open (plistname ).read ()
289+ plistdata = plistdata % {'appletname' :shortname }
290+ ofp = open (os .path .join (destname , 'Contents/Info.plist' ), 'w' )
291+ ofp .write (plistdata )
292+ ofp .close ()
293+ ownertype = 'PytA'
294+ # Create the PkgInfo file
295+ ofp = open (os .path .join (destname , 'Contents/PkgInfo' ), 'wb' )
296+ ofp .write ('APPL' + ownertype )
297+ ofp .close ()
298+
299+
300+ if DEBUG :
301+ progress .label ("Copy resources..." )
302+ progress .set (20 )
303+ resfilename = '%s.rsrc' % shortname
304+ respartialpathname = 'Contents/Resources/%s' % resfilename
305+ try :
306+ output = Res .FSOpenResourceFile (
307+ os .path .join (destname , respartialpathname ),
308+ u'' , WRITE )
309+ except MacOS .Error :
310+ fsr , dummy = Res .FSCreateResourceFile (
311+ os .path .join (destname , 'Contents/Resources' ),
312+ unicode (resfilename ), '' )
313+ output = Res .FSOpenResourceFile (fsr , u'' , WRITE )
314+
315+ # Copy the resources from the target specific resource template, if any
316+ typesfound , ownertype = [], None
317+ try :
318+ input = macresource .open_pathname (rsrcname )
319+ except (MacOS .Error , ValueError ):
320+ pass
321+ if DEBUG :
322+ progress .inc (50 )
323+ else :
324+ typesfound , ownertype = copyres (input , output , [], 0 , progress )
325+ Res .CloseResFile (input )
326+
327+ # Check which resource-types we should not copy from the template
328+ skiptypes = []
329+ ## if 'vers' in typesfound: skiptypes.append('vers')
330+ ## if 'SIZE' in typesfound: skiptypes.append('SIZE')
331+ ## if 'BNDL' in typesfound: skiptypes = skiptypes + ['BNDL', 'FREF', 'icl4',
332+ ## 'icl8', 'ics4', 'ics8', 'ICN#', 'ics#']
333+ ## if not copy_codefragment:
334+ ## skiptypes.append('cfrg')
335+ ## skipowner = (ownertype <> None)
336+
337+ # Copy the resources from the template
338+
339+ input = Res .FSOpenResourceFile (
340+ os .path .join (template , 'Contents/Resources/python.rsrc' ), u'' , READ )
341+ dummy , tmplowner = copyres (input , output , skiptypes , 1 , progress )
342+
343+ Res .CloseResFile (input )
344+ ## if ownertype == None:
345+ ## raise BuildError, "No owner resource found in either resource file or template"
346+ # Make sure we're manipulating the output resource file now
347+
348+ Res .CloseResFile (output )
349+
350+ if code :
351+ outputfilename = os .path .join (destname , 'Contents/Resources/__main__.pyc' )
352+ writepycfile (code , outputfilename )
353+
354+ ## macostools.touched(dest_fss)
241355
242356# Copy resources between two resource file descriptors.
243357# skip a resource named '__main__' or (if skipowner is set) with ID zero.
@@ -289,4 +403,38 @@ def copyres(input, output, skiptypes, skipowner, progress=None):
289403 Res .UseResFile (input )
290404 return alltypes , ctor
291405
406+ def copyapptree (srctree , dsttree , exceptlist = []):
407+ names = []
408+ if os .path .exists (dsttree ):
409+ shutil .rmtree (dsttree )
410+ os .mkdir (dsttree )
411+ todo = os .listdir (srctree )
412+ while todo :
413+ this , todo = todo [0 ], todo [1 :]
414+ if this in exceptlist :
415+ continue
416+ thispath = os .path .join (srctree , this )
417+ if os .path .isdir (thispath ):
418+ thiscontent = os .listdir (thispath )
419+ for t in thiscontent :
420+ todo .append (os .path .join (this , t ))
421+ names .append (this )
422+ for this in names :
423+ srcpath = os .path .join (srctree , this )
424+ dstpath = os .path .join (dsttree , this )
425+ if os .path .isdir (srcpath ):
426+ os .mkdir (dstpath )
427+ else :
428+ shutil .copy2 (srcpath , dstpath )
429+
430+ def writepycfile (codeobject , cfile ):
431+ import marshal
432+ fc = open (cfile , 'wb' )
433+ fc .write ('\0 \0 \0 \0 ' ) # MAGIC placeholder, written later
434+ fc .write ('\0 \0 \0 \0 ' ) # Timestap placeholder, not needed
435+ marshal .dump (codeobject , fc )
436+ fc .flush ()
437+ fc .seek (0 , 0 )
438+ fc .write (MAGIC )
439+ fc .close ()
292440
0 commit comments