1 """tools for BuildApplet and BuildApplication"""
4 warnings
.warnpy3k("the buildtools module is deprecated and is removed in 3.0",
12 from Carbon
import Res
22 BuildError
= "BuildError"
24 # .pyc file (and 'PYC ' resource magic number)
25 MAGIC
= imp
.get_magic()
27 # Template file (searched on sys.path)
28 TEMPLATE
= "PythonInterpreter"
30 # Specification of our resource
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"
38 # Default applet creator code
39 DEFAULT_APPLET_CREATOR
="Pyta"
41 # OpenResFile mode parameters
45 # Parameter for FSOpenResourceFile
46 RESOURCE_FORK_NAME
=Carbon
.File
.FSGetResourceForkName()
48 def findtemplate(template
=None):
49 """Locate the applet template along sys.path"""
50 if MacOS
.runtimemodel
== 'macho':
55 file = os
.path
.join(p
, template
)
57 file, d1
, d2
= Carbon
.File
.FSResolveAliasFile(file, 1)
59 except (Carbon
.File
.Error
, ValueError):
62 raise BuildError
, "Template %r not found on sys.path" % (template
,)
63 file = file.as_pathname()
66 def process(template
, filename
, destname
, copy_codefragment
=0,
67 rsrcname
=None, others
=[], raw
=0, progress
="default", destroot
=""):
69 if progress
== "default":
70 progress
= EasyDialogs
.ProgressBar("Processing %s..."%os.path
.split(filename
)[1], 120)
71 progress
.label("Compiling...")
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)
80 fp
= open(filename
, 'rU')
84 code
= compile(text
+ '\n', filename
, "exec")
85 except SyntaxError, arg
:
86 raise BuildError
, "Syntax error in script %s: %s" % (filename
, arg
)
88 raise BuildError
, "End-of-file in script %s" % (filename
,)
90 # Set the destination file name. Note that basename
91 # does contain the whole filepath, only a .py is stripped.
93 if string
.lower(filename
[-3:]) == ".py":
94 basename
= filename
[:-3]
95 if MacOS
.runtimemodel
!= 'macho' and not destname
:
101 if MacOS
.runtimemodel
== 'macho':
102 destname
= basename
+ '.app'
104 destname
= basename
+ '.applet'
106 rsrcname
= basename
+ '.rsrc'
108 # Try removing the output file. This fails in MachO, but it should
114 process_common(template
, progress
, code
, rsrcname
, destname
, 0,
115 copy_codefragment
, raw
, others
, filename
, destroot
)
118 def update(template
, filename
, output
):
119 if MacOS
.runtimemodel
== 'macho':
120 raise BuildError
, "No updating yet for MachO applets"
122 progress
= EasyDialogs
.ProgressBar("Updating %s..."%os.path
.split(filename
)[1], 120)
126 output
= filename
+ ' (updated)'
128 # Try removing the output file
133 process_common(template
, progress
, None, filename
, output
, 1, 1)
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
)
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()
147 # Copy data (not resources, yet) from the template
149 progress
.label("Copy data fork...")
152 if copy_codefragment
:
153 tmpl
= open(template
, "rb")
154 dest
= open(destname
, "wb")
163 # Open the output resource fork
166 progress
.label("Copy resources...")
169 output
= Res
.FSOpenResourceFile(destname
, RESOURCE_FORK_NAME
, WRITE
)
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
)
175 # Copy the resources from the target specific resource template, if any
176 typesfound
, ownertype
= [], None
178 input = Res
.FSOpenResourceFile(rsrcname
, RESOURCE_FORK_NAME
, READ
)
179 except (MacOS
.Error
, ValueError):
185 skip_oldfile
= ['cfrg']
188 typesfound
, ownertype
= copyres(input, output
, skip_oldfile
, 0, progress
)
189 Res
.CloseResFile(input)
191 # Check which resource-types we should not copy from the template
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)
201 # Copy the resources from the template
203 input = Res
.FSOpenResourceFile(template
, RESOURCE_FORK_NAME
, READ
)
204 dummy
, tmplowner
= copyres(input, output
, skiptypes
, 1, progress
)
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
211 Res
.UseResFile(output
)
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
222 # Delete any existing 'PYC ' resource named __main__
225 res
= Res
.Get1NamedResource(RESTYPE
, RESNAME
)
230 # Create the raw data for the resource from the code object
232 progress
.label("Write PYC resource...")
235 data
= marshal
.dumps(code
)
237 data
= (MAGIC
+ '\0\0\0\0') + data
239 # Create the resource and write it
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
)
250 res
.ReleaseResource()
252 # Close the output file
254 Res
.CloseResFile(output
)
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
)
266 macostools
.touched(destname
)
268 progress
.label("Done.")
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
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
287 if rsrcname
and rsrcname
[-5:] == '.rsrc':
289 plistname
= tmp
+ '.plist'
290 if os
.path
.exists(plistname
):
291 icnsname
= tmp
+ '.icns'
292 if not os
.path
.exists(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
):
303 progress
.label('Creating bundle...')
305 builder
= bundlebuilder
.AppBuilder(verbosity
=0)
306 builder
.mainprogram
= filename
307 builder
.builddir
= destdir
308 builder
.name
= shortname
309 builder
.destroot
= destroot
311 realrsrcname
= macresource
.resource_pathname(rsrcname
)
312 builder
.files
.append((realrsrcname
,
313 os
.path
.join('Contents/Resources', os
.path
.basename(rsrcname
))))
316 builder
.resources
.append(o
)
318 builder
.files
.append(o
)
321 builder
.plist
= plistlib
.Plist
.fromFile(plistname
)
323 builder
.iconfile
= icnsname
325 builder
.argv_emulation
= 1
329 progress
.label('Done.')
332 ## macostools.touched(dest_fss)
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.
338 def copyres(input, output
, skiptypes
, skipowner
, progress
=None):
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
:
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
)
356 if lcname
== OWNERNAME
and id == 0:
358 continue # Skip this one
362 attrs
= res
.GetResAttrs()
364 progress
.label("Copy %s %d %s"%(type, id, name
))
365 progress
.inc(progress_cur_inc
)
368 Res
.UseResFile(output
)
370 res2
= Res
.Get1Resource(type, id)
375 progress
.label("Overwrite %s %d %s"%(type, id, name
))
377 res2
.RemoveResource()
378 res
.AddResource(type, id, name
)
380 attrs
= attrs | res
.GetResAttrs()
381 res
.SetResAttrs(attrs
)
382 Res
.UseResFile(input)
383 return alltypes
, ctor
385 def copyapptree(srctree
, dsttree
, exceptlist
=[], progress
=None):
387 if os
.path
.exists(dsttree
):
388 shutil
.rmtree(dsttree
)
390 todo
= os
.listdir(srctree
)
392 this
, todo
= todo
[0], todo
[1:]
393 if this
in exceptlist
:
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
))
402 srcpath
= os
.path
.join(srctree
, this
)
403 dstpath
= os
.path
.join(dsttree
, this
)
404 if os
.path
.isdir(srcpath
):
406 elif os
.path
.islink(srcpath
):
407 endpoint
= os
.readlink(srcpath
)
408 os
.symlink(endpoint
, dstpath
)
411 progress
.label('Copy '+this
)
413 shutil
.copy2(srcpath
, dstpath
)
415 def writepycfile(codeobject
, cfile
):
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
)