Commit b2e33fe2 authored by Jack Jansen's avatar Jack Jansen

Implemented buildtools for MachoPython .app bundles. The API is compatible

enough that IDE and BuildApplet can create applets, yeah!
parent 32f782c0
......@@ -10,7 +10,9 @@ from Carbon import Res
import MACFS
import MacOS
import macostools
import macresource
import EasyDialogs
import shutil
BuildError = "BuildError"
......@@ -42,6 +44,10 @@ WRITE = 2
def findtemplate(template=None):
"""Locate the applet template along sys.path"""
if MacOS.runtimemodel == 'macho':
if template:
return template
return findtemplate_macho()
if not template:
template=TEMPLATE
for p in sys.path:
......@@ -55,6 +61,13 @@ def findtemplate(template=None):
raise BuildError, "Template %s not found on sys.path" % `template`
file = file.as_pathname()
return file
def findtemplate_macho():
execpath = sys.executable.split('/')
if not 'Contents' in execpath:
raise BuildError, "Not running from a .app bundle: %s" % sys.executable
i = execpath.index('Contents')
return '/'.join(execpath[:i])
def process(template, filename, output, copy_codefragment):
......@@ -82,13 +95,17 @@ def process(template, filename, output, copy_codefragment):
destname = filename[:-3]
rsrcname = destname + '.rsrc'
else:
destname = filename + ".applet"
if MacOS.runtimemodel == 'macho':
destname = filename + '.app'
else:
destname = filename + ".applet"
rsrcname = filename + '.rsrc'
if output:
destname = output
# Try removing the output file
# Try removing the output file. This fails in MachO, but it should
# do any harm.
try:
os.remove(destname)
except os.error:
......@@ -97,6 +114,8 @@ def process(template, filename, output, copy_codefragment):
def update(template, filename, output):
if MacOS.runtimemodel == 'macho':
raise BuildError, "No updating yet for MachO applets"
if DEBUG:
progress = EasyDialogs.ProgressBar("Updating %s..."%os.path.split(filename)[1], 120)
else:
......@@ -113,6 +132,8 @@ def update(template, filename, output):
def process_common(template, progress, code, rsrcname, destname, is_update, copy_codefragment):
if MacOS.runtimemodel == 'macho':
return process_common_macho(template, progress, code, rsrcname, destname, is_update)
# Create FSSpecs for the various files
template_fss = macfs.FSSpec(template)
template_fss, d1, d2 = macfs.ResolveAliasFile(template_fss)
......@@ -238,6 +259,99 @@ def process_common(template, progress, code, rsrcname, destname, is_update, copy
if DEBUG:
progress.label("Done.")
def process_common_macho(template, progress, code, rsrcname, destname, is_update):
# First make sure the name ends in ".app"
if destname[-4:] != '.app':
destname = destname + '.app'
# Now deduce the short name
shortname = os.path.split(destname)[1]
if shortname[-4:] == '.app':
# Strip the .app suffix
shortname = shortname[:-4]
plistname = shortname + '.plist'
# Start with copying the .app framework
if not is_update:
exceptlist = ["Contents/Info.plist",
"Contents/Resources/English.lproj/InfoPlist.strings",
"Contents/Resources/python.rsrc",
]
copyapptree(template, destname, exceptlist)
# Now either use the .plist file or the default
if plistname and os.path.exists(plistname):
shutil.copy2(plistname, os.path.join(destname, 'Contents/Info.plist'))
# XXXX Wrong. This should be parsed from plist file
# icnsname = 'PythonApplet.icns'
ownertype = 'PytA'
# XXXX Should copy .icns file
else:
plistname = os.path.join(template, 'Contents/Resources/Applet-Info.plist')
plistdata = open(plistname).read()
plistdata = plistdata % {'appletname':shortname}
ofp = open(os.path.join(destname, 'Contents/Info.plist'), 'w')
ofp.write(plistdata)
ofp.close()
ownertype = 'PytA'
# Create the PkgInfo file
ofp = open(os.path.join(destname, 'Contents/PkgInfo'), 'wb')
ofp.write('APPL' + ownertype)
ofp.close()
if DEBUG:
progress.label("Copy resources...")
progress.set(20)
resfilename = '%s.rsrc' % shortname
respartialpathname = 'Contents/Resources/%s' % resfilename
try:
output = Res.FSOpenResourceFile(
os.path.join(destname, respartialpathname),
u'', WRITE)
except MacOS.Error:
fsr, dummy = Res.FSCreateResourceFile(
os.path.join(destname, 'Contents/Resources'),
unicode(resfilename), '')
output = Res.FSOpenResourceFile(fsr, u'', WRITE)
# Copy the resources from the target specific resource template, if any
typesfound, ownertype = [], None
try:
input = macresource.open_pathname(rsrcname)
except (MacOS.Error, ValueError):
pass
if DEBUG:
progress.inc(50)
else:
typesfound, ownertype = copyres(input, output, [], 0, progress)
Res.CloseResFile(input)
# Check which resource-types we should not copy from the template
skiptypes = []
## if 'vers' in typesfound: skiptypes.append('vers')
## if 'SIZE' in typesfound: skiptypes.append('SIZE')
## if 'BNDL' in typesfound: skiptypes = skiptypes + ['BNDL', 'FREF', 'icl4',
## 'icl8', 'ics4', 'ics8', 'ICN#', 'ics#']
## if not copy_codefragment:
## skiptypes.append('cfrg')
## skipowner = (ownertype <> None)
# Copy the resources from the template
input = Res.FSOpenResourceFile(
os.path.join(template, 'Contents/Resources/python.rsrc'), u'', READ)
dummy, tmplowner = copyres(input, output, skiptypes, 1, progress)
Res.CloseResFile(input)
## if ownertype == None:
## raise BuildError, "No owner resource found in either resource file or template"
# Make sure we're manipulating the output resource file now
Res.CloseResFile(output)
if code:
outputfilename = os.path.join(destname, 'Contents/Resources/__main__.pyc')
writepycfile(code, outputfilename)
## macostools.touched(dest_fss)
# Copy resources between two resource file descriptors.
# 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):
Res.UseResFile(input)
return alltypes, ctor
def copyapptree(srctree, dsttree, exceptlist=[]):
names = []
if os.path.exists(dsttree):
shutil.rmtree(dsttree)
os.mkdir(dsttree)
todo = os.listdir(srctree)
while todo:
this, todo = todo[0], todo[1:]
if this in exceptlist:
continue
thispath = os.path.join(srctree, this)
if os.path.isdir(thispath):
thiscontent = os.listdir(thispath)
for t in thiscontent:
todo.append(os.path.join(this, t))
names.append(this)
for this in names:
srcpath = os.path.join(srctree, this)
dstpath = os.path.join(dsttree, this)
if os.path.isdir(srcpath):
os.mkdir(dstpath)
else:
shutil.copy2(srcpath, dstpath)
def writepycfile(codeobject, cfile):
import marshal
fc = open(cfile, 'wb')
fc.write('\0\0\0\0') # MAGIC placeholder, written later
fc.write('\0\0\0\0') # Timestap placeholder, not needed
marshal.dump(codeobject, fc)
fc.flush()
fc.seek(0, 0)
fc.write(MAGIC)
fc.close()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment