]> granicus.if.org Git - python/commitdiff
- Better commandline interface to BuildApplet, complete with options,
authorJack Jansen <jack.jansen@cwi.nl>
Sun, 9 Jun 2002 22:08:52 +0000 (22:08 +0000)
committerJack Jansen <jack.jansen@cwi.nl>
Sun, 9 Jun 2002 22:08:52 +0000 (22:08 +0000)
  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
Mac/OSXResources/app/Resources/Applet-Info.plist
Mac/scripts/BuildApplet.py

index 0eef5ac4e62ee0ae1eb6cfa005e82dfc9178cc61..d831610b96b543caa235e958a2771efa9ad69c3b 100644 (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):
index 8a61e6c3c81b234004123d615daa6beb1c0510b7..04409996f43d800c8848af0e2fd9406aafc017c4 100644 (file)
@@ -22,6 +22,8 @@
 
        <key>CFBundleGetInfoString</key>
        <string>%(appletname)s, a Python applet</string>
+       
+       %(cocoainfo)s
 
        <key>CFBundleIconFile</key>
        <string>PythonApplet.icns</string>
index b57c425b110cc27e50ee789f563321f42606bfa7..06798258c3c22fda299e944446fa5d3ba80099c8 100644 (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()