Implemented buildtools for MachoPython .app bundles. The API is compatible
enough that IDE and BuildApplet can create applets, yeah!
diff --git a/Mac/Lib/buildtools.py b/Mac/Lib/buildtools.py
index da43d09..25f77e6 100644
--- a/Mac/Lib/buildtools.py
+++ b/Mac/Lib/buildtools.py
@@ -10,7 +10,9 @@
 import MACFS
 import MacOS
 import macostools
+import macresource
 import EasyDialogs
+import shutil
 
 
 BuildError = "BuildError"
@@ -42,6 +44,10 @@
 
 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 @@
 		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 @@
 		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 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 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 @@
 	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 @@
 			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()