From 388fbf3d4a624becfbdedf6ec1dd57b57a5642ec Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Sun, 9 Jun 2002 22:08:52 +0000 Subject: - 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. --- Mac/Lib/buildtools.py | 141 +++++++++++++++-------- Mac/OSXResources/app/Resources/Applet-Info.plist | 2 + Mac/scripts/BuildApplet.py | 65 ++++++++++- 3 files changed, 159 insertions(+), 49 deletions(-) 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 @@ 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 = """ + NSMainNibFile + %s + NSPrincipalClass + NSApplication""" % 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): 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 @@ CFBundleGetInfoString %(appletname)s, a Python applet + + %(cocoainfo)s CFBundleIconFile PythonApplet.icns 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 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() -- cgit v0.12