- Better commandline interface to BuildApplet, complete with options,

verbose output to the console, etc.
- Allow Cocoa applets to be built with BuildApplet.

No full testing has been done yet to ensure OS9 operation hasn't suffered.
This commit is contained in:
Jack Jansen 2002-06-09 22:08:52 +00:00
parent 2befa48926
commit 388fbf3d4a
3 changed files with 159 additions and 49 deletions

View File

@ -17,9 +17,6 @@ import shutil
BuildError = "BuildError"
DEBUG=1
# .pyc file (and 'PYC ' resource magic number)
MAGIC = imp.get_magic()
@ -70,13 +67,13 @@ def findtemplate_macho():
return '/'.join(execpath[:i])
def process(template, filename, output, copy_codefragment):
def process(template, filename, destname, copy_codefragment,
rsrcname=None, others=[], raw=0, progress="default"):
if DEBUG:
if progress == "default":
progress = EasyDialogs.ProgressBar("Processing %s..."%os.path.split(filename)[1], 120)
progress.label("Compiling...")
else:
progress = None
progress.inc(0)
# Read the source and compile it
# (there's no point overwriting the destination if it has a syntax error)
@ -89,34 +86,38 @@ def process(template, filename, output, copy_codefragment):
except (SyntaxError, EOFError):
raise BuildError, "Syntax error in script %s" % `filename`
# Set the destination file name
# Set the destination file name. Note that basename
# does contain the whole filepath, only a .py is stripped.
if string.lower(filename[-3:]) == ".py":
destname = filename[:-3]
rsrcname = destname + '.rsrc'
basename = filename[:-3]
if MacOS.runtimemodel != 'macho' and not destname:
destname = basename
else:
basename = filename
if not destname:
if MacOS.runtimemodel == 'macho':
destname = filename + '.app'
destname = basename + '.app'
else:
destname = filename + ".applet"
rsrcname = filename + '.rsrc'
if output:
destname = output
destname = basename + '.applet'
if not rsrcname:
rsrcname = basename + '.rsrc'
# Try removing the output file. This fails in MachO, but it should
# do any harm.
try:
os.remove(destname)
except os.error:
pass
process_common(template, progress, code, rsrcname, destname, 0, copy_codefragment)
process_common(template, progress, code, rsrcname, destname, 0,
copy_codefragment, raw, others)
def update(template, filename, output):
if MacOS.runtimemodel == 'macho':
raise BuildError, "No updating yet for MachO applets"
if DEBUG:
if progress:
progress = EasyDialogs.ProgressBar("Updating %s..."%os.path.split(filename)[1], 120)
else:
progress = None
@ -131,16 +132,20 @@ def update(template, filename, output):
process_common(template, progress, None, filename, output, 1, 1)
def process_common(template, progress, code, rsrcname, destname, is_update, copy_codefragment):
def process_common(template, progress, code, rsrcname, destname, is_update,
copy_codefragment, raw=0, others=[]):
if MacOS.runtimemodel == 'macho':
return process_common_macho(template, progress, code, rsrcname, destname, is_update)
return process_common_macho(template, progress, code, rsrcname, destname,
is_update, raw, others)
if others:
raise BuildError, "Extra files only allowed for MachoPython applets"
# Create FSSpecs for the various files
template_fss = macfs.FSSpec(template)
template_fss, d1, d2 = macfs.ResolveAliasFile(template_fss)
dest_fss = macfs.FSSpec(destname)
# Copy data (not resources, yet) from the template
if DEBUG:
if progress:
progress.label("Copy data fork...")
progress.set(10)
@ -157,7 +162,7 @@ def process_common(template, progress, code, rsrcname, destname, is_update, copy
# Open the output resource fork
if DEBUG:
if progress:
progress.label("Copy resources...")
progress.set(20)
try:
@ -172,7 +177,7 @@ def process_common(template, progress, code, rsrcname, destname, is_update, copy
input = Res.FSpOpenResFile(rsrcname, READ)
except (MacOS.Error, ValueError):
pass
if DEBUG:
if progress:
progress.inc(50)
else:
if is_update:
@ -222,7 +227,7 @@ def process_common(template, progress, code, rsrcname, destname, is_update, copy
pass
# Create the raw data for the resource from the code object
if DEBUG:
if progress:
progress.label("Write PYC resource...")
progress.set(120)
@ -256,10 +261,11 @@ def process_common(template, progress, code, rsrcname, destname, is_update, copy
dest_fss.SetFInfo(dest_finfo)
macostools.touched(dest_fss)
if DEBUG:
if progress:
progress.label("Done.")
progress.inc(0)
def process_common_macho(template, progress, code, rsrcname, destname, is_update):
def process_common_macho(template, progress, code, rsrcname, destname, is_update, raw=0, others=[]):
# First make sure the name ends in ".app"
if destname[-4:] != '.app':
destname = destname + '.app'
@ -286,14 +292,17 @@ def process_common_macho(template, progress, code, rsrcname, destname, is_update
"Contents/Resources/English.lproj/InfoPlist.strings",
"Contents/Resources/python.rsrc",
]
copyapptree(template, destname, exceptlist)
copyapptree(template, destname, exceptlist, progress)
# Now either use the .plist file or the default
if progress:
progress.label('Create info.plist')
progress.inc(0)
if plistname:
shutil.copy2(plistname, os.path.join(destname, 'Contents/Info.plist'))
shutil.copy2(plistname, os.path.join(destname, 'Contents', 'Info.plist'))
if icnsname:
icnsdest = os.path.split(icnsname)[1]
icnsdest = os.path.join(destname,
os.path.join('Contents/Resources', icnsdest))
os.path.join('Contents', 'Resources', icnsdest))
shutil.copy2(icnsname, icnsdest)
# XXXX Wrong. This should be parsed from plist file. Also a big hack:-)
if shortname == 'PythonIDE':
@ -302,31 +311,44 @@ def process_common_macho(template, progress, code, rsrcname, destname, is_update
ownertype = 'PytA'
# XXXX Should copy .icns file
else:
plistname = os.path.join(template, 'Contents/Resources/Applet-Info.plist')
cocoainfo = ''
for o in others:
if o[-4:] == '.nib':
nibname = os.path.split(o)[1][:-4]
cocoainfo = """
<key>NSMainNibFile</key>
<string>%s</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>""" % nibname
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')
plistdata = plistdata % {'appletname':shortname, 'cocoainfo':cocoainfo}
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')
if progress:
progress.label('Create PkgInfo')
progress.inc(0)
ofp = open(os.path.join(destname, 'Contents', 'PkgInfo'), 'wb')
ofp.write('APPL' + ownertype)
ofp.close()
if DEBUG:
if progress:
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),
os.path.join(destname, 'Contents', 'Resources', resfilename),
u'', WRITE)
except MacOS.Error:
fsr, dummy = Res.FSCreateResourceFile(
os.path.join(destname, 'Contents/Resources'),
os.path.join(destname, 'Contents', 'Resources'),
unicode(resfilename), '')
output = Res.FSOpenResourceFile(fsr, u'', WRITE)
@ -336,7 +358,7 @@ def process_common_macho(template, progress, code, rsrcname, destname, is_update
input = macresource.open_pathname(rsrcname)
except (MacOS.Error, ValueError):
pass
if DEBUG:
if progress:
progress.inc(50)
else:
typesfound, ownertype = copyres(input, output, [], 0, progress)
@ -355,8 +377,12 @@ def process_common_macho(template, progress, code, rsrcname, destname, is_update
# 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)
os.path.join(template, 'Contents', 'Resources', 'python.rsrc'), u'', READ)
if progress:
progress.label("Copy standard resources...")
progress.inc(0)
## dummy, tmplowner = copyres(input, output, skiptypes, 1, progress)
dummy, tmplowner = copyres(input, output, skiptypes, 1, None)
Res.CloseResFile(input)
## if ownertype == None:
@ -366,8 +392,29 @@ def process_common_macho(template, progress, code, rsrcname, destname, is_update
Res.CloseResFile(output)
if code:
outputfilename = os.path.join(destname, 'Contents/Resources/__main__.pyc')
if raw:
pycname = '__rawmain__.pyc'
else:
pycname = '__main__.pyc'
outputfilename = os.path.join(destname, 'Contents', 'Resources', pycname)
if progress:
progress.label('Creating '+pycname)
progress.inc(0)
writepycfile(code, outputfilename)
# Copy other files the user asked for
for osrc in others:
oname = os.path.split(osrc)[1]
odst = os.path.join(destname, 'Contents', 'Resources', oname)
if progress:
progress.label('Copy ' + oname)
progress.inc(0)
if os.path.isdir(osrc):
copyapptree(osrc, odst)
else:
shutil.copy2(osrc, odst)
if progress:
progress.label('Done.')
progress.inc(0)
## macostools.touched(dest_fss)
@ -400,7 +447,7 @@ def copyres(input, output, skiptypes, skipowner, progress=None):
ctor = type
size = res.size
attrs = res.GetResAttrs()
if DEBUG and progress:
if progress:
progress.label("Copy %s %d %s"%(type, id, name))
progress.inc(progress_cur_inc)
res.LoadResource()
@ -411,8 +458,9 @@ def copyres(input, output, skiptypes, skipowner, progress=None):
except MacOS.Error:
res2 = None
if res2:
if DEBUG and progress:
if progress:
progress.label("Overwrite %s %d %s"%(type, id, name))
progress.inc(0)
res2.RemoveResource()
res.AddResource(type, id, name)
res.WriteResource()
@ -421,7 +469,7 @@ def copyres(input, output, skiptypes, skipowner, progress=None):
Res.UseResFile(input)
return alltypes, ctor
def copyapptree(srctree, dsttree, exceptlist=[]):
def copyapptree(srctree, dsttree, exceptlist=[], progress=None):
names = []
if os.path.exists(dsttree):
shutil.rmtree(dsttree)
@ -443,6 +491,9 @@ def copyapptree(srctree, dsttree, exceptlist=[]):
if os.path.isdir(srcpath):
os.mkdir(dstpath)
else:
if progress:
progress.label('Copy '+this)
progress.inc(0)
shutil.copy2(srcpath, dstpath)
def writepycfile(codeobject, cfile):

View File

@ -22,6 +22,8 @@
<key>CFBundleGetInfoString</key>
<string>%(appletname)s, a Python applet</string>
%(cocoainfo)s
<key>CFBundleIconFile</key>
<string>PythonApplet.icns</string>

View File

@ -15,7 +15,7 @@ import macfs
import MacOS
import EasyDialogs
import buildtools
import getopt
def main():
try:
@ -54,14 +54,71 @@ def buildapplet():
buildtools.process(template, filename, dstfilename, 1)
else:
SHORTOPTS = "o:r:ne:v?"
LONGOPTS=("output=", "resource=", "noargv", "extra=", "verbose", "help")
try:
options, args = getopt.getopt(sys.argv[1:], SHORTOPTS, LONGOPTS)
except getopt.error:
usage()
if options and len(args) > 1:
sys.stderr.write("Cannot use options when specifying multiple input files")
sys.exit(1)
dstfilename = None
rsrcfilename = None
raw = 0
extras = []
verbose = None
for opt, arg in options:
if opt in ('-o', '--output'):
dstfilename = arg
elif opt in ('-r', '--resource'):
rsrcfilename = arg
elif opt in ('-n', '--noargv'):
raw = 1
elif opt in ('-e', '--extra'):
extras.append(arg)
elif opt in ('-v', '--verbose'):
verbose = Verbose()
elif opt in ('-?', '--help'):
usage()
# Loop over all files to be processed
for filename in sys.argv[1:]:
for filename in args:
cr, tp = MacOS.GetCreatorAndType(filename)
if tp == 'APPL':
buildtools.update(template, filename, '')
buildtools.update(template, filename, dstfilename)
else:
buildtools.process(template, filename, '', 1)
buildtools.process(template, filename, dstfilename, 1,
rsrcname=rsrcfilename, others=extras, raw=raw, progress=verbose)
def usage():
print "BuildApplet creates an application from a Python source file"
print "Usage:"
print " BuildApplet interactive, single file, no options"
print " BuildApplet src1.py src2.py ... non-interactive multiple file"
print " BuildApplet [options] src.py non-interactive single file"
print "Options:"
print " --output o Output file; default based on source filename, short -o"
print " --resource r Resource file; default based on source filename, short -r"
print " --noargv Build applet without drag-and-drop sys.argv emulation, short -n, OSX only"
print " --extra f Extra file to put in .app bundle, short -e, OSX only"
print " --verbose Verbose, short -v"
print " --help This message, short -?"
sys.exit(1)
class Verbose:
"""This class mimics EasyDialogs.ProgressBar but prints to stderr"""
def __init__(self, *args):
if args and args[0]:
self.label(args[0])
def set(self, *args):
pass
def inc(self, *args):
pass
def label(self, str):
sys.stderr.write(str+'\n')
if __name__ == '__main__':
main()