- 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.
diff --git a/Mac/Lib/buildtools.py b/Mac/Lib/buildtools.py
index 0eef5ac..d831610 100644
--- a/Mac/Lib/buildtools.py
+++ b/Mac/Lib/buildtools.py
@@ -17,9 +17,6 @@
BuildError = "BuildError"
-DEBUG=1
-
-
# .pyc file (and 'PYC ' resource magic number)
MAGIC = imp.get_magic()
@@ -70,13 +67,13 @@
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 @@
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 @@
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 @@
# Open the output resource fork
- if DEBUG:
+ if progress:
progress.label("Copy resources...")
progress.set(20)
try:
@@ -172,7 +177,7 @@
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 @@
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 @@
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 @@
"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 @@
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 @@
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 @@
# 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 @@
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 @@
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 @@
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 @@
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 @@
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):
diff --git a/Mac/OSXResources/app/Resources/Applet-Info.plist b/Mac/OSXResources/app/Resources/Applet-Info.plist
index 8a61e6c..0440999 100644
--- a/Mac/OSXResources/app/Resources/Applet-Info.plist
+++ b/Mac/OSXResources/app/Resources/Applet-Info.plist
@@ -22,6 +22,8 @@
<key>CFBundleGetInfoString</key>
<string>%(appletname)s, a Python applet</string>
+
+ %(cocoainfo)s
<key>CFBundleIconFile</key>
<string>PythonApplet.icns</string>
diff --git a/Mac/scripts/BuildApplet.py b/Mac/scripts/BuildApplet.py
index b57c425..0679825 100644
--- a/Mac/scripts/BuildApplet.py
+++ b/Mac/scripts/BuildApplet.py
@@ -15,7 +15,7 @@
import MacOS
import EasyDialogs
import buildtools
-
+import getopt
def main():
try:
@@ -54,14 +54,71 @@
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()