diff options
Diffstat (limited to 'lib/python2.7/plat-mac/buildtools.py')
-rw-r--r-- | lib/python2.7/plat-mac/buildtools.py | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/lib/python2.7/plat-mac/buildtools.py b/lib/python2.7/plat-mac/buildtools.py new file mode 100644 index 0000000..bbb37ab --- /dev/null +++ b/lib/python2.7/plat-mac/buildtools.py @@ -0,0 +1,435 @@ +"""tools for BuildApplet and BuildApplication""" + +import warnings +warnings.warnpy3k("the buildtools module is deprecated and is removed in 3.0", + stacklevel=2) + +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 +try: + import EasyDialogs +except ImportError: + EasyDialogs = None +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", destroot=""): + + if progress == "default": + if EasyDialogs is None: + print "Compiling %s"%(os.path.split(filename)[1],) + process = None + else: + 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, destroot) + + +def update(template, filename, output): + if MacOS.runtimemodel == 'macho': + raise BuildError, "No updating yet for MachO applets" + if progress: + if EasyDialogs is None: + print "Updating %s"%(os.path.split(filename)[1],) + progress = None + else: + 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, destroot=""): + if MacOS.runtimemodel == 'macho': + return process_common_macho(template, progress, code, rsrcname, destname, + is_update, raw, others, filename, destroot) + 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 is 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 is 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, destroot=""): + # 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 + builder.destroot = destroot + 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() |