Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b2e33fe

Browse files
committed
Implemented buildtools for MachoPython .app bundles. The API is compatible
enough that IDE and BuildApplet can create applets, yeah!
1 parent 32f782c commit b2e33fe

1 file changed

Lines changed: 150 additions & 2 deletions

File tree

Mac/Lib/buildtools.py

Lines changed: 150 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
import MACFS
1111
import MacOS
1212
import macostools
13+
import macresource
1314
import EasyDialogs
15+
import shutil
1416

1517

1618
BuildError = "BuildError"
@@ -42,6 +44,10 @@
4244

4345
def 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

6073
def 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

99116
def 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

115134
def 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

Comments
 (0)