mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			419 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			419 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""tools for BuildApplet and BuildApplication"""
 | 
						|
 | 
						|
import sys
 | 
						|
import os
 | 
						|
import string
 | 
						|
import imp
 | 
						|
import marshal
 | 
						|
from Carbon import Res
 | 
						|
import Carbon.Files
 | 
						|
import Carbon.File
 | 
						|
import MacOS
 | 
						|
import macostools
 | 
						|
import macresource
 | 
						|
import EasyDialogs
 | 
						|
import shutil
 | 
						|
 | 
						|
 | 
						|
BuildError = "BuildError"
 | 
						|
 | 
						|
# .pyc file (and 'PYC ' resource magic number)
 | 
						|
MAGIC = imp.get_magic()
 | 
						|
 | 
						|
# Template file (searched on sys.path)
 | 
						|
TEMPLATE = "PythonInterpreter"
 | 
						|
 | 
						|
# Specification of our resource
 | 
						|
RESTYPE = 'PYC '
 | 
						|
RESNAME = '__main__'
 | 
						|
 | 
						|
# A resource with this name sets the "owner" (creator) of the destination
 | 
						|
# It should also have ID=0. Either of these alone is not enough.
 | 
						|
OWNERNAME = "owner resource"
 | 
						|
 | 
						|
# Default applet creator code
 | 
						|
DEFAULT_APPLET_CREATOR="Pyta"
 | 
						|
 | 
						|
# OpenResFile mode parameters
 | 
						|
READ = 1
 | 
						|
WRITE = 2
 | 
						|
 | 
						|
# Parameter for FSOpenResourceFile
 | 
						|
RESOURCE_FORK_NAME=Carbon.File.FSGetResourceForkName()
 | 
						|
 | 
						|
def findtemplate(template=None):
 | 
						|
    """Locate the applet template along sys.path"""
 | 
						|
    if MacOS.runtimemodel == 'macho':
 | 
						|
        return None
 | 
						|
    if not template:
 | 
						|
        template=TEMPLATE
 | 
						|
    for p in sys.path:
 | 
						|
        file = os.path.join(p, template)
 | 
						|
        try:
 | 
						|
            file, d1, d2 = Carbon.File.FSResolveAliasFile(file, 1)
 | 
						|
            break
 | 
						|
        except (Carbon.File.Error, ValueError):
 | 
						|
            continue
 | 
						|
    else:
 | 
						|
        raise BuildError, "Template %r not found on sys.path" % (template,)
 | 
						|
    file = file.as_pathname()
 | 
						|
    return file
 | 
						|
 | 
						|
def process(template, filename, destname, copy_codefragment=0,
 | 
						|
        rsrcname=None, others=[], raw=0, progress="default"):
 | 
						|
 | 
						|
    if progress == "default":
 | 
						|
        progress = EasyDialogs.ProgressBar("Processing %s..."%os.path.split(filename)[1], 120)
 | 
						|
        progress.label("Compiling...")
 | 
						|
        progress.inc(0)
 | 
						|
    # check for the script name being longer than 32 chars. This may trigger a bug
 | 
						|
    # on OSX that can destroy your sourcefile.
 | 
						|
    if '#' in os.path.split(filename)[1]:
 | 
						|
        raise BuildError, "BuildApplet could destroy your sourcefile on OSX, please rename: %s" % filename
 | 
						|
    # Read the source and compile it
 | 
						|
    # (there's no point overwriting the destination if it has a syntax error)
 | 
						|
 | 
						|
    fp = open(filename, 'rU')
 | 
						|
    text = fp.read()
 | 
						|
    fp.close()
 | 
						|
    try:
 | 
						|
        code = compile(text + '\n', filename, "exec")
 | 
						|
    except SyntaxError, arg:
 | 
						|
        raise BuildError, "Syntax error in script %s: %s" % (filename, arg)
 | 
						|
    except EOFError:
 | 
						|
        raise BuildError, "End-of-file in script %s" % (filename,)
 | 
						|
 | 
						|
    # Set the destination file name. Note that basename
 | 
						|
    # does contain the whole filepath, only a .py is stripped.
 | 
						|
 | 
						|
    if string.lower(filename[-3:]) == ".py":
 | 
						|
        basename = filename[:-3]
 | 
						|
        if MacOS.runtimemodel != 'macho' and not destname:
 | 
						|
            destname = basename
 | 
						|
    else:
 | 
						|
        basename = filename
 | 
						|
 | 
						|
    if not destname:
 | 
						|
        if MacOS.runtimemodel == 'macho':
 | 
						|
            destname = basename + '.app'
 | 
						|
        else:
 | 
						|
            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, raw, others, filename)
 | 
						|
 | 
						|
 | 
						|
def update(template, filename, output):
 | 
						|
    if MacOS.runtimemodel == 'macho':
 | 
						|
        raise BuildError, "No updating yet for MachO applets"
 | 
						|
    if progress:
 | 
						|
        progress = EasyDialogs.ProgressBar("Updating %s..."%os.path.split(filename)[1], 120)
 | 
						|
    else:
 | 
						|
        progress = None
 | 
						|
    if not output:
 | 
						|
        output = filename + ' (updated)'
 | 
						|
 | 
						|
    # Try removing the output file
 | 
						|
    try:
 | 
						|
        os.remove(output)
 | 
						|
    except os.error:
 | 
						|
        pass
 | 
						|
    process_common(template, progress, None, filename, output, 1, 1)
 | 
						|
 | 
						|
 | 
						|
def process_common(template, progress, code, rsrcname, destname, is_update,
 | 
						|
        copy_codefragment, raw=0, others=[], filename=None):
 | 
						|
    if MacOS.runtimemodel == 'macho':
 | 
						|
        return process_common_macho(template, progress, code, rsrcname, destname,
 | 
						|
            is_update, raw, others, filename)
 | 
						|
    if others:
 | 
						|
        raise BuildError, "Extra files only allowed for MachoPython applets"
 | 
						|
    # Create FSSpecs for the various files
 | 
						|
    template_fsr, d1, d2 = Carbon.File.FSResolveAliasFile(template, 1)
 | 
						|
    template = template_fsr.as_pathname()
 | 
						|
 | 
						|
    # Copy data (not resources, yet) from the template
 | 
						|
    if progress:
 | 
						|
        progress.label("Copy data fork...")
 | 
						|
        progress.set(10)
 | 
						|
 | 
						|
    if copy_codefragment:
 | 
						|
        tmpl = open(template, "rb")
 | 
						|
        dest = open(destname, "wb")
 | 
						|
        data = tmpl.read()
 | 
						|
        if data:
 | 
						|
            dest.write(data)
 | 
						|
        dest.close()
 | 
						|
        tmpl.close()
 | 
						|
        del dest
 | 
						|
        del tmpl
 | 
						|
 | 
						|
    # Open the output resource fork
 | 
						|
 | 
						|
    if progress:
 | 
						|
        progress.label("Copy resources...")
 | 
						|
        progress.set(20)
 | 
						|
    try:
 | 
						|
        output = Res.FSOpenResourceFile(destname, RESOURCE_FORK_NAME, WRITE)
 | 
						|
    except MacOS.Error:
 | 
						|
        destdir, destfile = os.path.split(destname)
 | 
						|
        Res.FSCreateResourceFile(destdir, unicode(destfile), RESOURCE_FORK_NAME)
 | 
						|
        output = Res.FSOpenResourceFile(destname, RESOURCE_FORK_NAME, WRITE)
 | 
						|
 | 
						|
    # Copy the resources from the target specific resource template, if any
 | 
						|
    typesfound, ownertype = [], None
 | 
						|
    try:
 | 
						|
        input = Res.FSOpenResourceFile(rsrcname, RESOURCE_FORK_NAME, READ)
 | 
						|
    except (MacOS.Error, ValueError):
 | 
						|
        pass
 | 
						|
        if progress:
 | 
						|
            progress.inc(50)
 | 
						|
    else:
 | 
						|
        if is_update:
 | 
						|
            skip_oldfile = ['cfrg']
 | 
						|
        else:
 | 
						|
            skip_oldfile = []
 | 
						|
        typesfound, ownertype = copyres(input, output, skip_oldfile, 0, progress)
 | 
						|
        Res.CloseResFile(input)
 | 
						|
 | 
						|
    # Check which resource-types we should not copy from the template
 | 
						|
    skiptypes = []
 | 
						|
    if 'vers' in typesfound: skiptypes.append('vers')
 | 
						|
    if 'SIZE' in typesfound: skiptypes.append('SIZE')
 | 
						|
    if 'BNDL' in typesfound: skiptypes = skiptypes + ['BNDL', 'FREF', 'icl4',
 | 
						|
            'icl8', 'ics4', 'ics8', 'ICN#', 'ics#']
 | 
						|
    if not copy_codefragment:
 | 
						|
        skiptypes.append('cfrg')
 | 
						|
##  skipowner = (ownertype <> None)
 | 
						|
 | 
						|
    # Copy the resources from the template
 | 
						|
 | 
						|
    input = Res.FSOpenResourceFile(template, RESOURCE_FORK_NAME, READ)
 | 
						|
    dummy, tmplowner = copyres(input, output, skiptypes, 1, progress)
 | 
						|
 | 
						|
    Res.CloseResFile(input)
 | 
						|
##  if ownertype == None:
 | 
						|
##      raise BuildError, "No owner resource found in either resource file or template"
 | 
						|
    # Make sure we're manipulating the output resource file now
 | 
						|
 | 
						|
    Res.UseResFile(output)
 | 
						|
 | 
						|
    if ownertype == None:
 | 
						|
        # No owner resource in the template. We have skipped the
 | 
						|
        # Python owner resource, so we have to add our own. The relevant
 | 
						|
        # bundle stuff is already included in the interpret/applet template.
 | 
						|
        newres = Res.Resource('\0')
 | 
						|
        newres.AddResource(DEFAULT_APPLET_CREATOR, 0, "Owner resource")
 | 
						|
        ownertype = DEFAULT_APPLET_CREATOR
 | 
						|
 | 
						|
    if code:
 | 
						|
        # Delete any existing 'PYC ' resource named __main__
 | 
						|
 | 
						|
        try:
 | 
						|
            res = Res.Get1NamedResource(RESTYPE, RESNAME)
 | 
						|
            res.RemoveResource()
 | 
						|
        except Res.Error:
 | 
						|
            pass
 | 
						|
 | 
						|
        # Create the raw data for the resource from the code object
 | 
						|
        if progress:
 | 
						|
            progress.label("Write PYC resource...")
 | 
						|
            progress.set(120)
 | 
						|
 | 
						|
        data = marshal.dumps(code)
 | 
						|
        del code
 | 
						|
        data = (MAGIC + '\0\0\0\0') + data
 | 
						|
 | 
						|
        # Create the resource and write it
 | 
						|
 | 
						|
        id = 0
 | 
						|
        while id < 128:
 | 
						|
            id = Res.Unique1ID(RESTYPE)
 | 
						|
        res = Res.Resource(data)
 | 
						|
        res.AddResource(RESTYPE, id, RESNAME)
 | 
						|
        attrs = res.GetResAttrs()
 | 
						|
        attrs = attrs | 0x04    # set preload
 | 
						|
        res.SetResAttrs(attrs)
 | 
						|
        res.WriteResource()
 | 
						|
        res.ReleaseResource()
 | 
						|
 | 
						|
    # Close the output file
 | 
						|
 | 
						|
    Res.CloseResFile(output)
 | 
						|
 | 
						|
    # Now set the creator, type and bundle bit of the destination.
 | 
						|
    # Done with FSSpec's, FSRef FInfo isn't good enough yet (2.3a1+)
 | 
						|
    dest_fss = Carbon.File.FSSpec(destname)
 | 
						|
    dest_finfo = dest_fss.FSpGetFInfo()
 | 
						|
    dest_finfo.Creator = ownertype
 | 
						|
    dest_finfo.Type = 'APPL'
 | 
						|
    dest_finfo.Flags = dest_finfo.Flags | Carbon.Files.kHasBundle | Carbon.Files.kIsShared
 | 
						|
    dest_finfo.Flags = dest_finfo.Flags & ~Carbon.Files.kHasBeenInited
 | 
						|
    dest_fss.FSpSetFInfo(dest_finfo)
 | 
						|
 | 
						|
    macostools.touched(destname)
 | 
						|
    if progress:
 | 
						|
        progress.label("Done.")
 | 
						|
        progress.inc(0)
 | 
						|
 | 
						|
def process_common_macho(template, progress, code, rsrcname, destname, is_update,
 | 
						|
        raw=0, others=[], filename=None):
 | 
						|
    # Check that we have a filename
 | 
						|
    if filename is None:
 | 
						|
        raise BuildError, "Need source filename on MacOSX"
 | 
						|
    # First make sure the name ends in ".app"
 | 
						|
    if destname[-4:] != '.app':
 | 
						|
        destname = destname + '.app'
 | 
						|
    # Now deduce the short name
 | 
						|
    destdir, shortname = os.path.split(destname)
 | 
						|
    if shortname[-4:] == '.app':
 | 
						|
        # Strip the .app suffix
 | 
						|
        shortname = shortname[:-4]
 | 
						|
    # And deduce the .plist and .icns names
 | 
						|
    plistname = None
 | 
						|
    icnsname = None
 | 
						|
    if rsrcname and rsrcname[-5:] == '.rsrc':
 | 
						|
        tmp = rsrcname[:-5]
 | 
						|
        plistname = tmp + '.plist'
 | 
						|
        if os.path.exists(plistname):
 | 
						|
            icnsname = tmp + '.icns'
 | 
						|
            if not os.path.exists(icnsname):
 | 
						|
                icnsname = None
 | 
						|
        else:
 | 
						|
            plistname = None
 | 
						|
    if not icnsname:
 | 
						|
        dft_icnsname = os.path.join(sys.prefix, 'Resources/Python.app/Contents/Resources/PythonApplet.icns')
 | 
						|
        if os.path.exists(dft_icnsname):
 | 
						|
            icnsname = dft_icnsname
 | 
						|
    if not os.path.exists(rsrcname):
 | 
						|
        rsrcname = None
 | 
						|
    if progress:
 | 
						|
        progress.label('Creating bundle...')
 | 
						|
    import bundlebuilder
 | 
						|
    builder = bundlebuilder.AppBuilder(verbosity=0)
 | 
						|
    builder.mainprogram = filename
 | 
						|
    builder.builddir = destdir
 | 
						|
    builder.name = shortname
 | 
						|
    if rsrcname:
 | 
						|
        realrsrcname = macresource.resource_pathname(rsrcname)
 | 
						|
        builder.files.append((realrsrcname,
 | 
						|
            os.path.join('Contents/Resources', os.path.basename(rsrcname))))
 | 
						|
    for o in others:
 | 
						|
        if type(o) == str:
 | 
						|
            builder.resources.append(o)
 | 
						|
        else:
 | 
						|
            builder.files.append(o)
 | 
						|
    if plistname:
 | 
						|
        import plistlib
 | 
						|
        builder.plist = plistlib.Plist.fromFile(plistname)
 | 
						|
    if icnsname:
 | 
						|
        builder.iconfile = icnsname
 | 
						|
    if not raw:
 | 
						|
        builder.argv_emulation = 1
 | 
						|
    builder.setup()
 | 
						|
    builder.build()
 | 
						|
    if progress:
 | 
						|
        progress.label('Done.')
 | 
						|
        progress.inc(0)
 | 
						|
 | 
						|
##  macostools.touched(dest_fss)
 | 
						|
 | 
						|
# Copy resources between two resource file descriptors.
 | 
						|
# skip a resource named '__main__' or (if skipowner is set) with ID zero.
 | 
						|
# Also skip resources with a type listed in skiptypes.
 | 
						|
#
 | 
						|
def copyres(input, output, skiptypes, skipowner, progress=None):
 | 
						|
    ctor = None
 | 
						|
    alltypes = []
 | 
						|
    Res.UseResFile(input)
 | 
						|
    ntypes = Res.Count1Types()
 | 
						|
    progress_type_inc = 50/ntypes
 | 
						|
    for itype in range(1, 1+ntypes):
 | 
						|
        type = Res.Get1IndType(itype)
 | 
						|
        if type in skiptypes:
 | 
						|
            continue
 | 
						|
        alltypes.append(type)
 | 
						|
        nresources = Res.Count1Resources(type)
 | 
						|
        progress_cur_inc = progress_type_inc/nresources
 | 
						|
        for ires in range(1, 1+nresources):
 | 
						|
            res = Res.Get1IndResource(type, ires)
 | 
						|
            id, type, name = res.GetResInfo()
 | 
						|
            lcname = string.lower(name)
 | 
						|
 | 
						|
            if lcname == OWNERNAME and id == 0:
 | 
						|
                if skipowner:
 | 
						|
                    continue # Skip this one
 | 
						|
                else:
 | 
						|
                    ctor = type
 | 
						|
            size = res.size
 | 
						|
            attrs = res.GetResAttrs()
 | 
						|
            if progress:
 | 
						|
                progress.label("Copy %s %d %s"%(type, id, name))
 | 
						|
                progress.inc(progress_cur_inc)
 | 
						|
            res.LoadResource()
 | 
						|
            res.DetachResource()
 | 
						|
            Res.UseResFile(output)
 | 
						|
            try:
 | 
						|
                res2 = Res.Get1Resource(type, id)
 | 
						|
            except MacOS.Error:
 | 
						|
                res2 = None
 | 
						|
            if res2:
 | 
						|
                if progress:
 | 
						|
                    progress.label("Overwrite %s %d %s"%(type, id, name))
 | 
						|
                    progress.inc(0)
 | 
						|
                res2.RemoveResource()
 | 
						|
            res.AddResource(type, id, name)
 | 
						|
            res.WriteResource()
 | 
						|
            attrs = attrs | res.GetResAttrs()
 | 
						|
            res.SetResAttrs(attrs)
 | 
						|
            Res.UseResFile(input)
 | 
						|
    return alltypes, ctor
 | 
						|
 | 
						|
def copyapptree(srctree, dsttree, exceptlist=[], progress=None):
 | 
						|
    names = []
 | 
						|
    if os.path.exists(dsttree):
 | 
						|
        shutil.rmtree(dsttree)
 | 
						|
    os.mkdir(dsttree)
 | 
						|
    todo = os.listdir(srctree)
 | 
						|
    while todo:
 | 
						|
        this, todo = todo[0], todo[1:]
 | 
						|
        if this in exceptlist:
 | 
						|
            continue
 | 
						|
        thispath = os.path.join(srctree, this)
 | 
						|
        if os.path.isdir(thispath):
 | 
						|
            thiscontent = os.listdir(thispath)
 | 
						|
            for t in thiscontent:
 | 
						|
                todo.append(os.path.join(this, t))
 | 
						|
        names.append(this)
 | 
						|
    for this in names:
 | 
						|
        srcpath = os.path.join(srctree, this)
 | 
						|
        dstpath = os.path.join(dsttree, this)
 | 
						|
        if os.path.isdir(srcpath):
 | 
						|
            os.mkdir(dstpath)
 | 
						|
        elif os.path.islink(srcpath):
 | 
						|
            endpoint = os.readlink(srcpath)
 | 
						|
            os.symlink(endpoint, dstpath)
 | 
						|
        else:
 | 
						|
            if progress:
 | 
						|
                progress.label('Copy '+this)
 | 
						|
                progress.inc(0)
 | 
						|
            shutil.copy2(srcpath, dstpath)
 | 
						|
 | 
						|
def writepycfile(codeobject, cfile):
 | 
						|
    import marshal
 | 
						|
    fc = open(cfile, 'wb')
 | 
						|
    fc.write('\0\0\0\0') # MAGIC placeholder, written later
 | 
						|
    fc.write('\0\0\0\0') # Timestap placeholder, not needed
 | 
						|
    marshal.dump(codeobject, fc)
 | 
						|
    fc.flush()
 | 
						|
    fc.seek(0, 0)
 | 
						|
    fc.write(MAGIC)
 | 
						|
    fc.close()
 |