1813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen"""tools for BuildApplet and BuildApplication"""
2813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
3236819310db3fe6f2fb22d48b780fa6ec253b6c7Benjamin Petersonimport warnings
4a6864e0d9f1fc06c50db36ed913ac48a3d2ddde5Benjamin Petersonwarnings.warnpy3k("the buildtools module is deprecated and is removed in 3.0",
5a6864e0d9f1fc06c50db36ed913ac48a3d2ddde5Benjamin Peterson              stacklevel=2)
6236819310db3fe6f2fb22d48b780fa6ec253b6c7Benjamin Peterson
7813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansenimport sys
8813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansenimport os
9813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansenimport string
10813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansenimport imp
11813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansenimport marshal
125a6fdcd3718927109592c6df692fe24a8fdaee31Jack Jansenfrom Carbon import Res
13cc94764c67f6fbb73caecec5afabef7327b15ac7Jack Jansenimport Carbon.Files
14cc94764c67f6fbb73caecec5afabef7327b15ac7Jack Jansenimport Carbon.File
15813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansenimport MacOS
16813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansenimport macostools
17b2e33fe285a1f998f477202e9379a39488ea518bJack Jansenimport macresource
182596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussorentry:
192596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussoren    import EasyDialogs
202596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussorenexcept ImportError:
212596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussoren    EasyDialogs = None
22b2e33fe285a1f998f477202e9379a39488ea518bJack Jansenimport shutil
23813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
24813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
25813c997b7663aaad296f6f221d2395b3fee4b422Jack JansenBuildError = "BuildError"
26813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
27813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen# .pyc file (and 'PYC ' resource magic number)
28813c997b7663aaad296f6f221d2395b3fee4b422Jack JansenMAGIC = imp.get_magic()
29813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
30813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen# Template file (searched on sys.path)
313b8052685c87e6564a3a4c37954ac2d96fcd6478Jack JansenTEMPLATE = "PythonInterpreter"
32813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
33813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen# Specification of our resource
34813c997b7663aaad296f6f221d2395b3fee4b422Jack JansenRESTYPE = 'PYC '
35813c997b7663aaad296f6f221d2395b3fee4b422Jack JansenRESNAME = '__main__'
36813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
37813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen# A resource with this name sets the "owner" (creator) of the destination
3881da9f146c2223bc486197c2df03feda5126dcd5Jack Jansen# It should also have ID=0. Either of these alone is not enough.
39813c997b7663aaad296f6f221d2395b3fee4b422Jack JansenOWNERNAME = "owner resource"
40813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
4181da9f146c2223bc486197c2df03feda5126dcd5Jack Jansen# Default applet creator code
4281da9f146c2223bc486197c2df03feda5126dcd5Jack JansenDEFAULT_APPLET_CREATOR="Pyta"
4381da9f146c2223bc486197c2df03feda5126dcd5Jack Jansen
44813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen# OpenResFile mode parameters
45813c997b7663aaad296f6f221d2395b3fee4b422Jack JansenREAD = 1
46813c997b7663aaad296f6f221d2395b3fee4b422Jack JansenWRITE = 2
47813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
48cc94764c67f6fbb73caecec5afabef7327b15ac7Jack Jansen# Parameter for FSOpenResourceFile
49cc94764c67f6fbb73caecec5afabef7327b15ac7Jack JansenRESOURCE_FORK_NAME=Carbon.File.FSGetResourceForkName()
50813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
51a4f8e580588b7ebd5fde61debab3fdb72f37cdecJack Jansendef findtemplate(template=None):
520ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    """Locate the applet template along sys.path"""
530ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if MacOS.runtimemodel == 'macho':
540ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        return None
550ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if not template:
560ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        template=TEMPLATE
570ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    for p in sys.path:
580ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        file = os.path.join(p, template)
590ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        try:
600ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            file, d1, d2 = Carbon.File.FSResolveAliasFile(file, 1)
610ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            break
620ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        except (Carbon.File.Error, ValueError):
630ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            continue
640ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    else:
6570a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald        raise BuildError, "Template %r not found on sys.path" % (template,)
660ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    file = file.as_pathname()
670ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    return file
68182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
69182b5aca27d376b08a2904bed42b751496f932f3Tim Petersdef process(template, filename, destname, copy_codefragment=0,
70c77f6dfd923e469cd645a0f825509cf0e9c490a0Jack Jansen        rsrcname=None, others=[], raw=0, progress="default", destroot=""):
71182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
720ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if progress == "default":
732596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussoren        if EasyDialogs is None:
742596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussoren            print "Compiling %s"%(os.path.split(filename)[1],)
752596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussoren            process = None
762596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussoren        else:
772596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussoren            progress = EasyDialogs.ProgressBar("Processing %s..."%os.path.split(filename)[1], 120)
782596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussoren            progress.label("Compiling...")
792596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussoren            progress.inc(0)
800ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # check for the script name being longer than 32 chars. This may trigger a bug
810ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # on OSX that can destroy your sourcefile.
820ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if '#' in os.path.split(filename)[1]:
830ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        raise BuildError, "BuildApplet could destroy your sourcefile on OSX, please rename: %s" % filename
840ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Read the source and compile it
850ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # (there's no point overwriting the destination if it has a syntax error)
86182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
870ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    fp = open(filename, 'rU')
880ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    text = fp.read()
890ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    fp.close()
900ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    try:
910ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        code = compile(text + '\n', filename, "exec")
920ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    except SyntaxError, arg:
930ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        raise BuildError, "Syntax error in script %s: %s" % (filename, arg)
940ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    except EOFError:
950ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        raise BuildError, "End-of-file in script %s" % (filename,)
96182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
970ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Set the destination file name. Note that basename
980ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # does contain the whole filepath, only a .py is stripped.
99182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
1000ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if string.lower(filename[-3:]) == ".py":
1010ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        basename = filename[:-3]
1020ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        if MacOS.runtimemodel != 'macho' and not destname:
1030ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            destname = basename
1040ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    else:
1050ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        basename = filename
106182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
1070ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if not destname:
1080ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        if MacOS.runtimemodel == 'macho':
1090ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            destname = basename + '.app'
1100ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        else:
1110ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            destname = basename + '.applet'
1120ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if not rsrcname:
1130ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        rsrcname = basename + '.rsrc'
114182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
1150ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Try removing the output file. This fails in MachO, but it should
1160ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # do any harm.
1170ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    try:
1180ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        os.remove(destname)
1190ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    except os.error:
1200ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        pass
121182b5aca27d376b08a2904bed42b751496f932f3Tim Peters    process_common(template, progress, code, rsrcname, destname, 0,
122c77f6dfd923e469cd645a0f825509cf0e9c490a0Jack Jansen        copy_codefragment, raw, others, filename, destroot)
123182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
124813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
125813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansendef update(template, filename, output):
1260ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if MacOS.runtimemodel == 'macho':
1270ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        raise BuildError, "No updating yet for MachO applets"
1280ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if progress:
1292596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussoren        if EasyDialogs is None:
1302596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussoren            print "Updating %s"%(os.path.split(filename)[1],)
1312596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussoren            progress = None
1322596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussoren        else:
1332596758cb42cb592f2e3c33ef77bc9b02c995510Ronald Oussoren            progress = EasyDialogs.ProgressBar("Updating %s..."%os.path.split(filename)[1], 120)
1340ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    else:
1350ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        progress = None
1360ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if not output:
1370ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        output = filename + ' (updated)'
138182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
1390ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Try removing the output file
1400ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    try:
1410ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        os.remove(output)
1420ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    except os.error:
1430ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        pass
1440ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    process_common(template, progress, None, filename, output, 1, 1)
145813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
146813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
147182b5aca27d376b08a2904bed42b751496f932f3Tim Petersdef process_common(template, progress, code, rsrcname, destname, is_update,
148c77f6dfd923e469cd645a0f825509cf0e9c490a0Jack Jansen        copy_codefragment, raw=0, others=[], filename=None, destroot=""):
1490ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if MacOS.runtimemodel == 'macho':
1500ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        return process_common_macho(template, progress, code, rsrcname, destname,
151c77f6dfd923e469cd645a0f825509cf0e9c490a0Jack Jansen            is_update, raw, others, filename, destroot)
1520ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if others:
1530ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        raise BuildError, "Extra files only allowed for MachoPython applets"
1540ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Create FSSpecs for the various files
1550ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    template_fsr, d1, d2 = Carbon.File.FSResolveAliasFile(template, 1)
1560ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    template = template_fsr.as_pathname()
157182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
1580ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Copy data (not resources, yet) from the template
1590ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if progress:
1600ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        progress.label("Copy data fork...")
1610ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        progress.set(10)
162182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
1630ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if copy_codefragment:
1640ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        tmpl = open(template, "rb")
1650ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        dest = open(destname, "wb")
1660ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        data = tmpl.read()
1670ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        if data:
1680ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            dest.write(data)
1690ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        dest.close()
1700ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        tmpl.close()
1710ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        del dest
1720ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        del tmpl
173182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
1740ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Open the output resource fork
175182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
1760ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if progress:
1770ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        progress.label("Copy resources...")
1780ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        progress.set(20)
1790ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    try:
1800ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        output = Res.FSOpenResourceFile(destname, RESOURCE_FORK_NAME, WRITE)
1810ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    except MacOS.Error:
1820ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        destdir, destfile = os.path.split(destname)
1830ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        Res.FSCreateResourceFile(destdir, unicode(destfile), RESOURCE_FORK_NAME)
1840ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        output = Res.FSOpenResourceFile(destname, RESOURCE_FORK_NAME, WRITE)
185182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
1860ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Copy the resources from the target specific resource template, if any
1870ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    typesfound, ownertype = [], None
1880ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    try:
1890ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        input = Res.FSOpenResourceFile(rsrcname, RESOURCE_FORK_NAME, READ)
1900ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    except (MacOS.Error, ValueError):
1910ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        pass
1920ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        if progress:
1930ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            progress.inc(50)
1940ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    else:
1950ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        if is_update:
1960ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            skip_oldfile = ['cfrg']
1970ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        else:
1980ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            skip_oldfile = []
1990ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        typesfound, ownertype = copyres(input, output, skip_oldfile, 0, progress)
2000ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        Res.CloseResFile(input)
201182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
2020ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Check which resource-types we should not copy from the template
2030ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    skiptypes = []
2040ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if 'vers' in typesfound: skiptypes.append('vers')
2050ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if 'SIZE' in typesfound: skiptypes.append('SIZE')
206182b5aca27d376b08a2904bed42b751496f932f3Tim Peters    if 'BNDL' in typesfound: skiptypes = skiptypes + ['BNDL', 'FREF', 'icl4',
2070ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            'icl8', 'ics4', 'ics8', 'ICN#', 'ics#']
2080ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if not copy_codefragment:
2090ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        skiptypes.append('cfrg')
210a3a505076efc19ec23669370778c5fa22d030ffaMark Dickinson##  skipowner = (ownertype != None)
211182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
2120ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Copy the resources from the template
213182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
2140ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    input = Res.FSOpenResourceFile(template, RESOURCE_FORK_NAME, READ)
2150ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    dummy, tmplowner = copyres(input, output, skiptypes, 1, progress)
216182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
2170ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    Res.CloseResFile(input)
2185b63acd31e0e40c1a9a9e9762905b0054ff37994Benjamin Peterson##  if ownertype is None:
2190ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen##      raise BuildError, "No owner resource found in either resource file or template"
2200ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Make sure we're manipulating the output resource file now
221182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
2220ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    Res.UseResFile(output)
22381da9f146c2223bc486197c2df03feda5126dcd5Jack Jansen
2245b63acd31e0e40c1a9a9e9762905b0054ff37994Benjamin Peterson    if ownertype is None:
2250ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        # No owner resource in the template. We have skipped the
2260ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        # Python owner resource, so we have to add our own. The relevant
2270ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        # bundle stuff is already included in the interpret/applet template.
2280ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        newres = Res.Resource('\0')
2290ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        newres.AddResource(DEFAULT_APPLET_CREATOR, 0, "Owner resource")
2300ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        ownertype = DEFAULT_APPLET_CREATOR
231182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
2320ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if code:
2330ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        # Delete any existing 'PYC ' resource named __main__
234182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
2350ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        try:
2360ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            res = Res.Get1NamedResource(RESTYPE, RESNAME)
2370ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            res.RemoveResource()
2380ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        except Res.Error:
2390ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            pass
240182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
2410ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        # Create the raw data for the resource from the code object
2420ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        if progress:
2430ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            progress.label("Write PYC resource...")
2440ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            progress.set(120)
245182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
2460ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        data = marshal.dumps(code)
2470ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        del code
2480ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        data = (MAGIC + '\0\0\0\0') + data
249182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
2500ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        # Create the resource and write it
251182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
2520ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        id = 0
2530ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        while id < 128:
2540ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            id = Res.Unique1ID(RESTYPE)
2550ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        res = Res.Resource(data)
2560ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        res.AddResource(RESTYPE, id, RESNAME)
2570ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        attrs = res.GetResAttrs()
2580ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        attrs = attrs | 0x04    # set preload
2590ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        res.SetResAttrs(attrs)
2600ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        res.WriteResource()
2610ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        res.ReleaseResource()
262182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
2630ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Close the output file
264182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
2650ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    Res.CloseResFile(output)
266182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
2670ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Now set the creator, type and bundle bit of the destination.
2680ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Done with FSSpec's, FSRef FInfo isn't good enough yet (2.3a1+)
2690ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    dest_fss = Carbon.File.FSSpec(destname)
2700ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    dest_finfo = dest_fss.FSpGetFInfo()
2710ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    dest_finfo.Creator = ownertype
2720ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    dest_finfo.Type = 'APPL'
2730ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    dest_finfo.Flags = dest_finfo.Flags | Carbon.Files.kHasBundle | Carbon.Files.kIsShared
2740ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    dest_finfo.Flags = dest_finfo.Flags & ~Carbon.Files.kHasBeenInited
2750ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    dest_fss.FSpSetFInfo(dest_finfo)
276182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
2770ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    macostools.touched(destname)
2780ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if progress:
2790ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        progress.label("Done.")
2800ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        progress.inc(0)
281813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
282182b5aca27d376b08a2904bed42b751496f932f3Tim Petersdef process_common_macho(template, progress, code, rsrcname, destname, is_update,
283c77f6dfd923e469cd645a0f825509cf0e9c490a0Jack Jansen        raw=0, others=[], filename=None, destroot=""):
2840ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Check that we have a filename
2850ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if filename is None:
2860ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        raise BuildError, "Need source filename on MacOSX"
2870ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # First make sure the name ends in ".app"
2880ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if destname[-4:] != '.app':
2890ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        destname = destname + '.app'
2900ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # Now deduce the short name
2910ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    destdir, shortname = os.path.split(destname)
2920ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if shortname[-4:] == '.app':
2930ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        # Strip the .app suffix
2940ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        shortname = shortname[:-4]
2950ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    # And deduce the .plist and .icns names
2960ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    plistname = None
2970ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    icnsname = None
2980ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if rsrcname and rsrcname[-5:] == '.rsrc':
2990ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        tmp = rsrcname[:-5]
3000ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        plistname = tmp + '.plist'
3010ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        if os.path.exists(plistname):
3020ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            icnsname = tmp + '.icns'
3030ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            if not os.path.exists(icnsname):
3040ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                icnsname = None
3050ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        else:
3060ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            plistname = None
307d69b74453192bed078946fbe0f2b06489a118121Jack Jansen    if not icnsname:
308d69b74453192bed078946fbe0f2b06489a118121Jack Jansen        dft_icnsname = os.path.join(sys.prefix, 'Resources/Python.app/Contents/Resources/PythonApplet.icns')
309d69b74453192bed078946fbe0f2b06489a118121Jack Jansen        if os.path.exists(dft_icnsname):
310d69b74453192bed078946fbe0f2b06489a118121Jack Jansen            icnsname = dft_icnsname
3110ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if not os.path.exists(rsrcname):
3120ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        rsrcname = None
3130ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if progress:
3140ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        progress.label('Creating bundle...')
3150ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    import bundlebuilder
3160ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    builder = bundlebuilder.AppBuilder(verbosity=0)
3170ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    builder.mainprogram = filename
3180ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    builder.builddir = destdir
3190ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    builder.name = shortname
320c77f6dfd923e469cd645a0f825509cf0e9c490a0Jack Jansen    builder.destroot = destroot
3210ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if rsrcname:
3220ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        realrsrcname = macresource.resource_pathname(rsrcname)
323182b5aca27d376b08a2904bed42b751496f932f3Tim Peters        builder.files.append((realrsrcname,
3240ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            os.path.join('Contents/Resources', os.path.basename(rsrcname))))
3250ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    for o in others:
3260ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        if type(o) == str:
3270ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            builder.resources.append(o)
3280ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        else:
3290ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            builder.files.append(o)
3300ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if plistname:
3310ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        import plistlib
3320ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        builder.plist = plistlib.Plist.fromFile(plistname)
3330ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if icnsname:
3340ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        builder.iconfile = icnsname
3350ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if not raw:
3360ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        builder.argv_emulation = 1
3370ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    builder.setup()
3380ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    builder.build()
339182b5aca27d376b08a2904bed42b751496f932f3Tim Peters    if progress:
3400ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        progress.label('Done.')
3410ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        progress.inc(0)
342182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
3430ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen##  macostools.touched(dest_fss)
344813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
345813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen# Copy resources between two resource file descriptors.
34681da9f146c2223bc486197c2df03feda5126dcd5Jack Jansen# skip a resource named '__main__' or (if skipowner is set) with ID zero.
347813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen# Also skip resources with a type listed in skiptypes.
348813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen#
349813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansendef copyres(input, output, skiptypes, skipowner, progress=None):
3500ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    ctor = None
3510ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    alltypes = []
3520ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    Res.UseResFile(input)
3530ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    ntypes = Res.Count1Types()
3540ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    progress_type_inc = 50/ntypes
3550ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    for itype in range(1, 1+ntypes):
3560ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        type = Res.Get1IndType(itype)
3570ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        if type in skiptypes:
3580ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            continue
3590ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        alltypes.append(type)
3600ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        nresources = Res.Count1Resources(type)
3610ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        progress_cur_inc = progress_type_inc/nresources
3620ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        for ires in range(1, 1+nresources):
3630ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            res = Res.Get1IndResource(type, ires)
3640ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            id, type, name = res.GetResInfo()
3650ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            lcname = string.lower(name)
36681da9f146c2223bc486197c2df03feda5126dcd5Jack Jansen
3670ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            if lcname == OWNERNAME and id == 0:
3680ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                if skipowner:
3690ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                    continue # Skip this one
3700ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                else:
3710ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                    ctor = type
3720ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            size = res.size
3730ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            attrs = res.GetResAttrs()
3740ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            if progress:
3750ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                progress.label("Copy %s %d %s"%(type, id, name))
3760ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                progress.inc(progress_cur_inc)
3770ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            res.LoadResource()
3780ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            res.DetachResource()
3790ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            Res.UseResFile(output)
3800ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            try:
3810ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                res2 = Res.Get1Resource(type, id)
3820ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            except MacOS.Error:
3830ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                res2 = None
3840ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            if res2:
3850ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                if progress:
3860ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                    progress.label("Overwrite %s %d %s"%(type, id, name))
3870ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                    progress.inc(0)
3880ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                res2.RemoveResource()
3890ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            res.AddResource(type, id, name)
3900ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            res.WriteResource()
3910ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            attrs = attrs | res.GetResAttrs()
3920ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            res.SetResAttrs(attrs)
3930ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            Res.UseResFile(input)
3940ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    return alltypes, ctor
395813c997b7663aaad296f6f221d2395b3fee4b422Jack Jansen
396388fbf3d4a624becfbdedf6ec1dd57b57a5642ecJack Jansendef copyapptree(srctree, dsttree, exceptlist=[], progress=None):
3970ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    names = []
3980ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    if os.path.exists(dsttree):
3990ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        shutil.rmtree(dsttree)
4000ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    os.mkdir(dsttree)
4010ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    todo = os.listdir(srctree)
4020ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    while todo:
4030ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        this, todo = todo[0], todo[1:]
4040ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        if this in exceptlist:
4050ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            continue
4060ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        thispath = os.path.join(srctree, this)
4070ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        if os.path.isdir(thispath):
4080ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            thiscontent = os.listdir(thispath)
4090ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            for t in thiscontent:
4100ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                todo.append(os.path.join(this, t))
4110ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        names.append(this)
4120ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    for this in names:
4130ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        srcpath = os.path.join(srctree, this)
4140ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        dstpath = os.path.join(dsttree, this)
4150ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        if os.path.isdir(srcpath):
4160ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            os.mkdir(dstpath)
4170ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        elif os.path.islink(srcpath):
4180ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            endpoint = os.readlink(srcpath)
4190ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            os.symlink(endpoint, dstpath)
4200ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen        else:
4210ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            if progress:
4220ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                progress.label('Copy '+this)
4230ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen                progress.inc(0)
4240ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen            shutil.copy2(srcpath, dstpath)
425182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
426b2e33fe285a1f998f477202e9379a39488ea518bJack Jansendef writepycfile(codeobject, cfile):
4270ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    import marshal
4280ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    fc = open(cfile, 'wb')
4290ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    fc.write('\0\0\0\0') # MAGIC placeholder, written later
4300ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    fc.write('\0\0\0\0') # Timestap placeholder, not needed
4310ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    marshal.dump(codeobject, fc)
4320ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    fc.flush()
4330ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    fc.seek(0, 0)
4340ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    fc.write(MAGIC)
4350ae3220736f9b71820b01aee1f540d0afcceb9a6Jack Jansen    fc.close()
436