Mac/scripts/BuildApplet.py
"""Create an applet from a Python script.

This puts up a dialog asking for a Python source file ('TEXT').
The output is a file with the same name but its ".py" suffix dropped.
It is created by copying an applet template and then adding a 'PYC '
resource named __main__ containing the compiled, marshalled script.
"""


import sys
sys.stdout = sys.stderr

import os
import MacOS
try:
    import EasyDialogs
except ImportError:
    EasyDialogs = None
import buildtools
import getopt

if not sys.executable.startswith(sys.exec_prefix):
    # Oh, the joys of using a python script to bootstrap applicatin bundles
    # sys.executable points inside the current application bundle. Because this
    # path contains blanks (two of them actually) this path isn't usable on
    # #! lines. Reset sys.executable to point to the embedded python interpreter
    sys.executable = os.path.join(sys.prefix,
            'Resources/Python.app/Contents/MacOS/Python')

    # Just in case we're not in a framework:
    if not os.path.exists(sys.executable):
        sys.executable = os.path.join(sys.exec_prefix,  'bin/python')

def main():
    try:
        buildapplet()
    except buildtools.BuildError, detail:
        if EasyDialogs is None:
            print detail
        else:
            EasyDialogs.Message(detail)


def buildapplet():
    buildtools.DEBUG=1

    # Find the template
    # (there's no point in proceeding if we can't find it)

    template = buildtools.findtemplate()

    # Ask for source text if not specified in sys.argv[1:]

    if not sys.argv[1:]:
        if EasyDialogs is None:
            usage()
            sys.exit(1)

        filename = EasyDialogs.AskFileForOpen(message='Select Python source or applet:',
                typeList=('TEXT', 'APPL'))
        if not filename:
            return
        tp, tf = os.path.split(filename)
        if tf[-3:] == '.py':
            tf = tf[:-3]
        else:
            tf = tf + '.applet'
        dstfilename = EasyDialogs.AskFileForSave(message='Save application as:',
                savedFileName=tf)
        if not dstfilename: return
        cr, tp = MacOS.GetCreatorAndType(filename)
        if tp == 'APPL':
            buildtools.update(template, filename, dstfilename)
        else:
            buildtools.process(template, filename, dstfilename, 1)
    else:

        SHORTOPTS = "o:r:ne:v?PR"
        LONGOPTS=("output=", "resource=", "noargv", "extra=", "verbose", "help", "python=", "destroot=")
        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
        destroot = ''
        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'):
                if ':' in arg:
                    arg = arg.split(':')
                extras.append(arg)
            elif opt in ('-P', '--python'):
                # This is a very dirty trick. We set sys.executable
                # so that bundlebuilder will use this in the #! line
                # for the applet bootstrap.
                sys.executable = arg
            elif opt in ('-v', '--verbose'):
                verbose = Verbose()
            elif opt in ('-?', '--help'):
                usage()
            elif opt in ('-d', '--destroot'):
                destroot = arg
        # Loop over all files to be processed
        for filename in args:
            cr, tp = MacOS.GetCreatorAndType(filename)
            if tp == 'APPL':
                buildtools.update(template, filename, dstfilename)
            else:
                buildtools.process(template, filename, dstfilename, 1,
                        rsrcname=rsrcfilename, others=extras, raw=raw,
                        progress=verbose, destroot=destroot)

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 src[:dst] 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()