symbian-qemu-0.9.1-12/python-2.6.1/Lib/plat-mac/buildtools.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 """tools for BuildApplet and BuildApplication"""
       
     2 
       
     3 import warnings
       
     4 warnings.warnpy3k("the buildtools module is deprecated and is removed in 3.0",
       
     5               stacklevel=2)
       
     6 
       
     7 import sys
       
     8 import os
       
     9 import string
       
    10 import imp
       
    11 import marshal
       
    12 from Carbon import Res
       
    13 import Carbon.Files
       
    14 import Carbon.File
       
    15 import MacOS
       
    16 import macostools
       
    17 import macresource
       
    18 import EasyDialogs
       
    19 import shutil
       
    20 
       
    21 
       
    22 BuildError = "BuildError"
       
    23 
       
    24 # .pyc file (and 'PYC ' resource magic number)
       
    25 MAGIC = imp.get_magic()
       
    26 
       
    27 # Template file (searched on sys.path)
       
    28 TEMPLATE = "PythonInterpreter"
       
    29 
       
    30 # Specification of our resource
       
    31 RESTYPE = 'PYC '
       
    32 RESNAME = '__main__'
       
    33 
       
    34 # A resource with this name sets the "owner" (creator) of the destination
       
    35 # It should also have ID=0. Either of these alone is not enough.
       
    36 OWNERNAME = "owner resource"
       
    37 
       
    38 # Default applet creator code
       
    39 DEFAULT_APPLET_CREATOR="Pyta"
       
    40 
       
    41 # OpenResFile mode parameters
       
    42 READ = 1
       
    43 WRITE = 2
       
    44 
       
    45 # Parameter for FSOpenResourceFile
       
    46 RESOURCE_FORK_NAME=Carbon.File.FSGetResourceForkName()
       
    47 
       
    48 def findtemplate(template=None):
       
    49     """Locate the applet template along sys.path"""
       
    50     if MacOS.runtimemodel == 'macho':
       
    51         return None
       
    52     if not template:
       
    53         template=TEMPLATE
       
    54     for p in sys.path:
       
    55         file = os.path.join(p, template)
       
    56         try:
       
    57             file, d1, d2 = Carbon.File.FSResolveAliasFile(file, 1)
       
    58             break
       
    59         except (Carbon.File.Error, ValueError):
       
    60             continue
       
    61     else:
       
    62         raise BuildError, "Template %r not found on sys.path" % (template,)
       
    63     file = file.as_pathname()
       
    64     return file
       
    65 
       
    66 def process(template, filename, destname, copy_codefragment=0,
       
    67         rsrcname=None, others=[], raw=0, progress="default", destroot=""):
       
    68 
       
    69     if progress == "default":
       
    70         progress = EasyDialogs.ProgressBar("Processing %s..."%os.path.split(filename)[1], 120)
       
    71         progress.label("Compiling...")
       
    72         progress.inc(0)
       
    73     # check for the script name being longer than 32 chars. This may trigger a bug
       
    74     # on OSX that can destroy your sourcefile.
       
    75     if '#' in os.path.split(filename)[1]:
       
    76         raise BuildError, "BuildApplet could destroy your sourcefile on OSX, please rename: %s" % filename
       
    77     # Read the source and compile it
       
    78     # (there's no point overwriting the destination if it has a syntax error)
       
    79 
       
    80     fp = open(filename, 'rU')
       
    81     text = fp.read()
       
    82     fp.close()
       
    83     try:
       
    84         code = compile(text + '\n', filename, "exec")
       
    85     except SyntaxError, arg:
       
    86         raise BuildError, "Syntax error in script %s: %s" % (filename, arg)
       
    87     except EOFError:
       
    88         raise BuildError, "End-of-file in script %s" % (filename,)
       
    89 
       
    90     # Set the destination file name. Note that basename
       
    91     # does contain the whole filepath, only a .py is stripped.
       
    92 
       
    93     if string.lower(filename[-3:]) == ".py":
       
    94         basename = filename[:-3]
       
    95         if MacOS.runtimemodel != 'macho' and not destname:
       
    96             destname = basename
       
    97     else:
       
    98         basename = filename
       
    99 
       
   100     if not destname:
       
   101         if MacOS.runtimemodel == 'macho':
       
   102             destname = basename + '.app'
       
   103         else:
       
   104             destname = basename + '.applet'
       
   105     if not rsrcname:
       
   106         rsrcname = basename + '.rsrc'
       
   107 
       
   108     # Try removing the output file. This fails in MachO, but it should
       
   109     # do any harm.
       
   110     try:
       
   111         os.remove(destname)
       
   112     except os.error:
       
   113         pass
       
   114     process_common(template, progress, code, rsrcname, destname, 0,
       
   115         copy_codefragment, raw, others, filename, destroot)
       
   116 
       
   117 
       
   118 def update(template, filename, output):
       
   119     if MacOS.runtimemodel == 'macho':
       
   120         raise BuildError, "No updating yet for MachO applets"
       
   121     if progress:
       
   122         progress = EasyDialogs.ProgressBar("Updating %s..."%os.path.split(filename)[1], 120)
       
   123     else:
       
   124         progress = None
       
   125     if not output:
       
   126         output = filename + ' (updated)'
       
   127 
       
   128     # Try removing the output file
       
   129     try:
       
   130         os.remove(output)
       
   131     except os.error:
       
   132         pass
       
   133     process_common(template, progress, None, filename, output, 1, 1)
       
   134 
       
   135 
       
   136 def process_common(template, progress, code, rsrcname, destname, is_update,
       
   137         copy_codefragment, raw=0, others=[], filename=None, destroot=""):
       
   138     if MacOS.runtimemodel == 'macho':
       
   139         return process_common_macho(template, progress, code, rsrcname, destname,
       
   140             is_update, raw, others, filename, destroot)
       
   141     if others:
       
   142         raise BuildError, "Extra files only allowed for MachoPython applets"
       
   143     # Create FSSpecs for the various files
       
   144     template_fsr, d1, d2 = Carbon.File.FSResolveAliasFile(template, 1)
       
   145     template = template_fsr.as_pathname()
       
   146 
       
   147     # Copy data (not resources, yet) from the template
       
   148     if progress:
       
   149         progress.label("Copy data fork...")
       
   150         progress.set(10)
       
   151 
       
   152     if copy_codefragment:
       
   153         tmpl = open(template, "rb")
       
   154         dest = open(destname, "wb")
       
   155         data = tmpl.read()
       
   156         if data:
       
   157             dest.write(data)
       
   158         dest.close()
       
   159         tmpl.close()
       
   160         del dest
       
   161         del tmpl
       
   162 
       
   163     # Open the output resource fork
       
   164 
       
   165     if progress:
       
   166         progress.label("Copy resources...")
       
   167         progress.set(20)
       
   168     try:
       
   169         output = Res.FSOpenResourceFile(destname, RESOURCE_FORK_NAME, WRITE)
       
   170     except MacOS.Error:
       
   171         destdir, destfile = os.path.split(destname)
       
   172         Res.FSCreateResourceFile(destdir, unicode(destfile), RESOURCE_FORK_NAME)
       
   173         output = Res.FSOpenResourceFile(destname, RESOURCE_FORK_NAME, WRITE)
       
   174 
       
   175     # Copy the resources from the target specific resource template, if any
       
   176     typesfound, ownertype = [], None
       
   177     try:
       
   178         input = Res.FSOpenResourceFile(rsrcname, RESOURCE_FORK_NAME, READ)
       
   179     except (MacOS.Error, ValueError):
       
   180         pass
       
   181         if progress:
       
   182             progress.inc(50)
       
   183     else:
       
   184         if is_update:
       
   185             skip_oldfile = ['cfrg']
       
   186         else:
       
   187             skip_oldfile = []
       
   188         typesfound, ownertype = copyres(input, output, skip_oldfile, 0, progress)
       
   189         Res.CloseResFile(input)
       
   190 
       
   191     # Check which resource-types we should not copy from the template
       
   192     skiptypes = []
       
   193     if 'vers' in typesfound: skiptypes.append('vers')
       
   194     if 'SIZE' in typesfound: skiptypes.append('SIZE')
       
   195     if 'BNDL' in typesfound: skiptypes = skiptypes + ['BNDL', 'FREF', 'icl4',
       
   196             'icl8', 'ics4', 'ics8', 'ICN#', 'ics#']
       
   197     if not copy_codefragment:
       
   198         skiptypes.append('cfrg')
       
   199 ##  skipowner = (ownertype <> None)
       
   200 
       
   201     # Copy the resources from the template
       
   202 
       
   203     input = Res.FSOpenResourceFile(template, RESOURCE_FORK_NAME, READ)
       
   204     dummy, tmplowner = copyres(input, output, skiptypes, 1, progress)
       
   205 
       
   206     Res.CloseResFile(input)
       
   207 ##  if ownertype is None:
       
   208 ##      raise BuildError, "No owner resource found in either resource file or template"
       
   209     # Make sure we're manipulating the output resource file now
       
   210 
       
   211     Res.UseResFile(output)
       
   212 
       
   213     if ownertype is None:
       
   214         # No owner resource in the template. We have skipped the
       
   215         # Python owner resource, so we have to add our own. The relevant
       
   216         # bundle stuff is already included in the interpret/applet template.
       
   217         newres = Res.Resource('\0')
       
   218         newres.AddResource(DEFAULT_APPLET_CREATOR, 0, "Owner resource")
       
   219         ownertype = DEFAULT_APPLET_CREATOR
       
   220 
       
   221     if code:
       
   222         # Delete any existing 'PYC ' resource named __main__
       
   223 
       
   224         try:
       
   225             res = Res.Get1NamedResource(RESTYPE, RESNAME)
       
   226             res.RemoveResource()
       
   227         except Res.Error:
       
   228             pass
       
   229 
       
   230         # Create the raw data for the resource from the code object
       
   231         if progress:
       
   232             progress.label("Write PYC resource...")
       
   233             progress.set(120)
       
   234 
       
   235         data = marshal.dumps(code)
       
   236         del code
       
   237         data = (MAGIC + '\0\0\0\0') + data
       
   238 
       
   239         # Create the resource and write it
       
   240 
       
   241         id = 0
       
   242         while id < 128:
       
   243             id = Res.Unique1ID(RESTYPE)
       
   244         res = Res.Resource(data)
       
   245         res.AddResource(RESTYPE, id, RESNAME)
       
   246         attrs = res.GetResAttrs()
       
   247         attrs = attrs | 0x04    # set preload
       
   248         res.SetResAttrs(attrs)
       
   249         res.WriteResource()
       
   250         res.ReleaseResource()
       
   251 
       
   252     # Close the output file
       
   253 
       
   254     Res.CloseResFile(output)
       
   255 
       
   256     # Now set the creator, type and bundle bit of the destination.
       
   257     # Done with FSSpec's, FSRef FInfo isn't good enough yet (2.3a1+)
       
   258     dest_fss = Carbon.File.FSSpec(destname)
       
   259     dest_finfo = dest_fss.FSpGetFInfo()
       
   260     dest_finfo.Creator = ownertype
       
   261     dest_finfo.Type = 'APPL'
       
   262     dest_finfo.Flags = dest_finfo.Flags | Carbon.Files.kHasBundle | Carbon.Files.kIsShared
       
   263     dest_finfo.Flags = dest_finfo.Flags & ~Carbon.Files.kHasBeenInited
       
   264     dest_fss.FSpSetFInfo(dest_finfo)
       
   265 
       
   266     macostools.touched(destname)
       
   267     if progress:
       
   268         progress.label("Done.")
       
   269         progress.inc(0)
       
   270 
       
   271 def process_common_macho(template, progress, code, rsrcname, destname, is_update,
       
   272         raw=0, others=[], filename=None, destroot=""):
       
   273     # Check that we have a filename
       
   274     if filename is None:
       
   275         raise BuildError, "Need source filename on MacOSX"
       
   276     # First make sure the name ends in ".app"
       
   277     if destname[-4:] != '.app':
       
   278         destname = destname + '.app'
       
   279     # Now deduce the short name
       
   280     destdir, shortname = os.path.split(destname)
       
   281     if shortname[-4:] == '.app':
       
   282         # Strip the .app suffix
       
   283         shortname = shortname[:-4]
       
   284     # And deduce the .plist and .icns names
       
   285     plistname = None
       
   286     icnsname = None
       
   287     if rsrcname and rsrcname[-5:] == '.rsrc':
       
   288         tmp = rsrcname[:-5]
       
   289         plistname = tmp + '.plist'
       
   290         if os.path.exists(plistname):
       
   291             icnsname = tmp + '.icns'
       
   292             if not os.path.exists(icnsname):
       
   293                 icnsname = None
       
   294         else:
       
   295             plistname = None
       
   296     if not icnsname:
       
   297         dft_icnsname = os.path.join(sys.prefix, 'Resources/Python.app/Contents/Resources/PythonApplet.icns')
       
   298         if os.path.exists(dft_icnsname):
       
   299             icnsname = dft_icnsname
       
   300     if not os.path.exists(rsrcname):
       
   301         rsrcname = None
       
   302     if progress:
       
   303         progress.label('Creating bundle...')
       
   304     import bundlebuilder
       
   305     builder = bundlebuilder.AppBuilder(verbosity=0)
       
   306     builder.mainprogram = filename
       
   307     builder.builddir = destdir
       
   308     builder.name = shortname
       
   309     builder.destroot = destroot
       
   310     if rsrcname:
       
   311         realrsrcname = macresource.resource_pathname(rsrcname)
       
   312         builder.files.append((realrsrcname,
       
   313             os.path.join('Contents/Resources', os.path.basename(rsrcname))))
       
   314     for o in others:
       
   315         if type(o) == str:
       
   316             builder.resources.append(o)
       
   317         else:
       
   318             builder.files.append(o)
       
   319     if plistname:
       
   320         import plistlib
       
   321         builder.plist = plistlib.Plist.fromFile(plistname)
       
   322     if icnsname:
       
   323         builder.iconfile = icnsname
       
   324     if not raw:
       
   325         builder.argv_emulation = 1
       
   326     builder.setup()
       
   327     builder.build()
       
   328     if progress:
       
   329         progress.label('Done.')
       
   330         progress.inc(0)
       
   331 
       
   332 ##  macostools.touched(dest_fss)
       
   333 
       
   334 # Copy resources between two resource file descriptors.
       
   335 # skip a resource named '__main__' or (if skipowner is set) with ID zero.
       
   336 # Also skip resources with a type listed in skiptypes.
       
   337 #
       
   338 def copyres(input, output, skiptypes, skipowner, progress=None):
       
   339     ctor = None
       
   340     alltypes = []
       
   341     Res.UseResFile(input)
       
   342     ntypes = Res.Count1Types()
       
   343     progress_type_inc = 50/ntypes
       
   344     for itype in range(1, 1+ntypes):
       
   345         type = Res.Get1IndType(itype)
       
   346         if type in skiptypes:
       
   347             continue
       
   348         alltypes.append(type)
       
   349         nresources = Res.Count1Resources(type)
       
   350         progress_cur_inc = progress_type_inc/nresources
       
   351         for ires in range(1, 1+nresources):
       
   352             res = Res.Get1IndResource(type, ires)
       
   353             id, type, name = res.GetResInfo()
       
   354             lcname = string.lower(name)
       
   355 
       
   356             if lcname == OWNERNAME and id == 0:
       
   357                 if skipowner:
       
   358                     continue # Skip this one
       
   359                 else:
       
   360                     ctor = type
       
   361             size = res.size
       
   362             attrs = res.GetResAttrs()
       
   363             if progress:
       
   364                 progress.label("Copy %s %d %s"%(type, id, name))
       
   365                 progress.inc(progress_cur_inc)
       
   366             res.LoadResource()
       
   367             res.DetachResource()
       
   368             Res.UseResFile(output)
       
   369             try:
       
   370                 res2 = Res.Get1Resource(type, id)
       
   371             except MacOS.Error:
       
   372                 res2 = None
       
   373             if res2:
       
   374                 if progress:
       
   375                     progress.label("Overwrite %s %d %s"%(type, id, name))
       
   376                     progress.inc(0)
       
   377                 res2.RemoveResource()
       
   378             res.AddResource(type, id, name)
       
   379             res.WriteResource()
       
   380             attrs = attrs | res.GetResAttrs()
       
   381             res.SetResAttrs(attrs)
       
   382             Res.UseResFile(input)
       
   383     return alltypes, ctor
       
   384 
       
   385 def copyapptree(srctree, dsttree, exceptlist=[], progress=None):
       
   386     names = []
       
   387     if os.path.exists(dsttree):
       
   388         shutil.rmtree(dsttree)
       
   389     os.mkdir(dsttree)
       
   390     todo = os.listdir(srctree)
       
   391     while todo:
       
   392         this, todo = todo[0], todo[1:]
       
   393         if this in exceptlist:
       
   394             continue
       
   395         thispath = os.path.join(srctree, this)
       
   396         if os.path.isdir(thispath):
       
   397             thiscontent = os.listdir(thispath)
       
   398             for t in thiscontent:
       
   399                 todo.append(os.path.join(this, t))
       
   400         names.append(this)
       
   401     for this in names:
       
   402         srcpath = os.path.join(srctree, this)
       
   403         dstpath = os.path.join(dsttree, this)
       
   404         if os.path.isdir(srcpath):
       
   405             os.mkdir(dstpath)
       
   406         elif os.path.islink(srcpath):
       
   407             endpoint = os.readlink(srcpath)
       
   408             os.symlink(endpoint, dstpath)
       
   409         else:
       
   410             if progress:
       
   411                 progress.label('Copy '+this)
       
   412                 progress.inc(0)
       
   413             shutil.copy2(srcpath, dstpath)
       
   414 
       
   415 def writepycfile(codeobject, cfile):
       
   416     import marshal
       
   417     fc = open(cfile, 'wb')
       
   418     fc.write('\0\0\0\0') # MAGIC placeholder, written later
       
   419     fc.write('\0\0\0\0') # Timestap placeholder, not needed
       
   420     marshal.dump(codeobject, fc)
       
   421     fc.flush()
       
   422     fc.seek(0, 0)
       
   423     fc.write(MAGIC)
       
   424     fc.close()