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
25 BuildError
= "BuildError"
27 # .pyc file (and 'PYC ' resource magic number)
28 MAGIC
= imp
.get_magic()
30 # Template file (searched on sys.path)
31 TEMPLATE
= "PythonInterpreter"
33 # Specification of our resource
37 # A resource with this name sets the "owner" (creator) of the destination
38 # It should also have ID=0. Either of these alone is not enough.
39 OWNERNAME
= "owner resource"
41 # Default applet creator code
42 DEFAULT_APPLET_CREATOR
="Pyta"
44 # OpenResFile mode parameters
48 # Parameter for FSOpenResourceFile
49 RESOURCE_FORK_NAME
=Carbon
.File
.FSGetResourceForkName()
51 def findtemplate(template
=None):
52 """Locate the applet template along sys.path"""
53 if MacOS
.runtimemodel
== 'macho':
58 file = os
.path
.join(p
, template
)
60 file, d1
, d2
= Carbon
.File
.FSResolveAliasFile(file, 1)
62 except (Carbon
.File
.Error
, ValueError):
65 raise BuildError
, "Template %r not found on sys.path" % (template
,)
66 file = file.as_pathname()
69 def process(template
, filename
, destname
, copy_codefragment
=0,
70 rsrcname
=None, others
=[], raw
=0, progress
="default", destroot
=""):
72 if progress
== "default":
73 if EasyDialogs
is None:
74 print "Compiling %s"%(os
.path
.split(filename
)[1],)
77 progress
= EasyDialogs
.ProgressBar("Processing %s..."%os.path
.split(filename
)[1], 120)
78 progress
.label("Compiling...")
80 # check for the script name being longer than 32 chars. This may trigger a bug
81 # on OSX that can destroy your sourcefile.
82 if '#' in os
.path
.split(filename
)[1]:
83 raise BuildError
, "BuildApplet could destroy your sourcefile on OSX, please rename: %s" % filename
84 # Read the source and compile it
85 # (there's no point overwriting the destination if it has a syntax error)
87 fp
= open(filename
, 'rU')
91 code
= compile(text
+ '\n', filename
, "exec")
92 except SyntaxError, arg
:
93 raise BuildError
, "Syntax error in script %s: %s" % (filename
, arg
)
95 raise BuildError
, "End-of-file in script %s" % (filename
,)
97 # Set the destination file name. Note that basename
98 # does contain the whole filepath, only a .py is stripped.
100 if string
.lower(filename
[-3:]) == ".py":
101 basename
= filename
[:-3]
102 if MacOS
.runtimemodel
!= 'macho' and not destname
:
108 if MacOS
.runtimemodel
== 'macho':
109 destname
= basename
+ '.app'
111 destname
= basename
+ '.applet'
113 rsrcname
= basename
+ '.rsrc'
115 # Try removing the output file. This fails in MachO, but it should
121 process_common(template
, progress
, code
, rsrcname
, destname
, 0,
122 copy_codefragment
, raw
, others
, filename
, destroot
)
125 def update(template
, filename
, output
):
126 if MacOS
.runtimemodel
== 'macho':
127 raise BuildError
, "No updating yet for MachO applets"
129 if EasyDialogs
is None:
130 print "Updating %s"%(os
.path
.split(filename
)[1],)
133 progress
= EasyDialogs
.ProgressBar("Updating %s..."%os.path
.split(filename
)[1], 120)
137 output
= filename
+ ' (updated)'
139 # Try removing the output file
144 process_common(template
, progress
, None, filename
, output
, 1, 1)
147 def process_common(template
, progress
, code
, rsrcname
, destname
, is_update
,
148 copy_codefragment
, raw
=0, others
=[], filename
=None, destroot
=""):
149 if MacOS
.runtimemodel
== 'macho':
150 return process_common_macho(template
, progress
, code
, rsrcname
, destname
,
151 is_update
, raw
, others
, filename
, destroot
)
153 raise BuildError
, "Extra files only allowed for MachoPython applets"
154 # Create FSSpecs for the various files
155 template_fsr
, d1
, d2
= Carbon
.File
.FSResolveAliasFile(template
, 1)
156 template
= template_fsr
.as_pathname()
158 # Copy data (not resources, yet) from the template
160 progress
.label("Copy data fork...")
163 if copy_codefragment
:
164 tmpl
= open(template
, "rb")
165 dest
= open(destname
, "wb")
174 # Open the output resource fork
177 progress
.label("Copy resources...")
180 output
= Res
.FSOpenResourceFile(destname
, RESOURCE_FORK_NAME
, WRITE
)
182 destdir
, destfile
= os
.path
.split(destname
)
183 Res
.FSCreateResourceFile(destdir
, unicode(destfile
), RESOURCE_FORK_NAME
)
184 output
= Res
.FSOpenResourceFile(destname
, RESOURCE_FORK_NAME
, WRITE
)
186 # Copy the resources from the target specific resource template, if any
187 typesfound
, ownertype
= [], None
189 input = Res
.FSOpenResourceFile(rsrcname
, RESOURCE_FORK_NAME
, READ
)
190 except (MacOS
.Error
, ValueError):
196 skip_oldfile
= ['cfrg']
199 typesfound
, ownertype
= copyres(input, output
, skip_oldfile
, 0, progress
)
200 Res
.CloseResFile(input)
202 # Check which resource-types we should not copy from the template
204 if 'vers' in typesfound
: skiptypes
.append('vers')
205 if 'SIZE' in typesfound
: skiptypes
.append('SIZE')
206 if 'BNDL' in typesfound
: skiptypes
= skiptypes
+ ['BNDL', 'FREF', 'icl4',
207 'icl8', 'ics4', 'ics8', 'ICN#', 'ics#']
208 if not copy_codefragment
:
209 skiptypes
.append('cfrg')
210 ## skipowner = (ownertype <> None)
212 # Copy the resources from the template
214 input = Res
.FSOpenResourceFile(template
, RESOURCE_FORK_NAME
, READ
)
215 dummy
, tmplowner
= copyres(input, output
, skiptypes
, 1, progress
)
217 Res
.CloseResFile(input)
218 ## if ownertype is None:
219 ## raise BuildError, "No owner resource found in either resource file or template"
220 # Make sure we're manipulating the output resource file now
222 Res
.UseResFile(output
)
224 if ownertype
is None:
225 # No owner resource in the template. We have skipped the
226 # Python owner resource, so we have to add our own. The relevant
227 # bundle stuff is already included in the interpret/applet template.
228 newres
= Res
.Resource('\0')
229 newres
.AddResource(DEFAULT_APPLET_CREATOR
, 0, "Owner resource")
230 ownertype
= DEFAULT_APPLET_CREATOR
233 # Delete any existing 'PYC ' resource named __main__
236 res
= Res
.Get1NamedResource(RESTYPE
, RESNAME
)
241 # Create the raw data for the resource from the code object
243 progress
.label("Write PYC resource...")
246 data
= marshal
.dumps(code
)
248 data
= (MAGIC
+ '\0\0\0\0') + data
250 # Create the resource and write it
254 id = Res
.Unique1ID(RESTYPE
)
255 res
= Res
.Resource(data
)
256 res
.AddResource(RESTYPE
, id, RESNAME
)
257 attrs
= res
.GetResAttrs()
258 attrs
= attrs |
0x04 # set preload
259 res
.SetResAttrs(attrs
)
261 res
.ReleaseResource()
263 # Close the output file
265 Res
.CloseResFile(output
)
267 # Now set the creator, type and bundle bit of the destination.
268 # Done with FSSpec's, FSRef FInfo isn't good enough yet (2.3a1+)
269 dest_fss
= Carbon
.File
.FSSpec(destname
)
270 dest_finfo
= dest_fss
.FSpGetFInfo()
271 dest_finfo
.Creator
= ownertype
272 dest_finfo
.Type
= 'APPL'
273 dest_finfo
.Flags
= dest_finfo
.Flags | Carbon
.Files
.kHasBundle | Carbon
.Files
.kIsShared
274 dest_finfo
.Flags
= dest_finfo
.Flags
& ~Carbon
.Files
.kHasBeenInited
275 dest_fss
.FSpSetFInfo(dest_finfo
)
277 macostools
.touched(destname
)
279 progress
.label("Done.")
282 def process_common_macho(template
, progress
, code
, rsrcname
, destname
, is_update
,
283 raw
=0, others
=[], filename
=None, destroot
=""):
284 # Check that we have a filename
286 raise BuildError
, "Need source filename on MacOSX"
287 # First make sure the name ends in ".app"
288 if destname
[-4:] != '.app':
289 destname
= destname
+ '.app'
290 # Now deduce the short name
291 destdir
, shortname
= os
.path
.split(destname
)
292 if shortname
[-4:] == '.app':
293 # Strip the .app suffix
294 shortname
= shortname
[:-4]
295 # And deduce the .plist and .icns names
298 if rsrcname
and rsrcname
[-5:] == '.rsrc':
300 plistname
= tmp
+ '.plist'
301 if os
.path
.exists(plistname
):
302 icnsname
= tmp
+ '.icns'
303 if not os
.path
.exists(icnsname
):
308 dft_icnsname
= os
.path
.join(sys
.prefix
, 'Resources/Python.app/Contents/Resources/PythonApplet.icns')
309 if os
.path
.exists(dft_icnsname
):
310 icnsname
= dft_icnsname
311 if not os
.path
.exists(rsrcname
):
314 progress
.label('Creating bundle...')
316 builder
= bundlebuilder
.AppBuilder(verbosity
=0)
317 builder
.mainprogram
= filename
318 builder
.builddir
= destdir
319 builder
.name
= shortname
320 builder
.destroot
= destroot
322 realrsrcname
= macresource
.resource_pathname(rsrcname
)
323 builder
.files
.append((realrsrcname
,
324 os
.path
.join('Contents/Resources', os
.path
.basename(rsrcname
))))
327 builder
.resources
.append(o
)
329 builder
.files
.append(o
)
332 builder
.plist
= plistlib
.Plist
.fromFile(plistname
)
334 builder
.iconfile
= icnsname
336 builder
.argv_emulation
= 1
340 progress
.label('Done.')
343 ## macostools.touched(dest_fss)
345 # Copy resources between two resource file descriptors.
346 # skip a resource named '__main__' or (if skipowner is set) with ID zero.
347 # Also skip resources with a type listed in skiptypes.
349 def copyres(input, output
, skiptypes
, skipowner
, progress
=None):
352 Res
.UseResFile(input)
353 ntypes
= Res
.Count1Types()
354 progress_type_inc
= 50/ntypes
355 for itype
in range(1, 1+ntypes
):
356 type = Res
.Get1IndType(itype
)
357 if type in skiptypes
:
359 alltypes
.append(type)
360 nresources
= Res
.Count1Resources(type)
361 progress_cur_inc
= progress_type_inc
/nresources
362 for ires
in range(1, 1+nresources
):
363 res
= Res
.Get1IndResource(type, ires
)
364 id, type, name
= res
.GetResInfo()
365 lcname
= string
.lower(name
)
367 if lcname
== OWNERNAME
and id == 0:
369 continue # Skip this one
373 attrs
= res
.GetResAttrs()
375 progress
.label("Copy %s %d %s"%(type, id, name
))
376 progress
.inc(progress_cur_inc
)
379 Res
.UseResFile(output
)
381 res2
= Res
.Get1Resource(type, id)
386 progress
.label("Overwrite %s %d %s"%(type, id, name
))
388 res2
.RemoveResource()
389 res
.AddResource(type, id, name
)
391 attrs
= attrs | res
.GetResAttrs()
392 res
.SetResAttrs(attrs
)
393 Res
.UseResFile(input)
394 return alltypes
, ctor
396 def copyapptree(srctree
, dsttree
, exceptlist
=[], progress
=None):
398 if os
.path
.exists(dsttree
):
399 shutil
.rmtree(dsttree
)
401 todo
= os
.listdir(srctree
)
403 this
, todo
= todo
[0], todo
[1:]
404 if this
in exceptlist
:
406 thispath
= os
.path
.join(srctree
, this
)
407 if os
.path
.isdir(thispath
):
408 thiscontent
= os
.listdir(thispath
)
409 for t
in thiscontent
:
410 todo
.append(os
.path
.join(this
, t
))
413 srcpath
= os
.path
.join(srctree
, this
)
414 dstpath
= os
.path
.join(dsttree
, this
)
415 if os
.path
.isdir(srcpath
):
417 elif os
.path
.islink(srcpath
):
418 endpoint
= os
.readlink(srcpath
)
419 os
.symlink(endpoint
, dstpath
)
422 progress
.label('Copy '+this
)
424 shutil
.copy2(srcpath
, dstpath
)
426 def writepycfile(codeobject
, cfile
):
428 fc
= open(cfile
, 'wb')
429 fc
.write('\0\0\0\0') # MAGIC placeholder, written later
430 fc
.write('\0\0\0\0') # Timestap placeholder, not needed
431 marshal
.dump(codeobject
, fc
)