blob: 2a8fd03cfb2eba8052b5aded19901fe890714102 [file] [log] [blame]
Jack Jansen813c9971998-07-31 09:42:35 +00001"""tools for BuildApplet and BuildApplication"""
2
3import sys
4import os
5import string
6import imp
7import marshal
8import macfs
9import Res
10import MACFS
11import MacOS
12import macostools
13import EasyDialogs
14
15
16BuildError = "BuildError"
17
18DEBUG=1
19
20
21# .pyc file (and 'PYC ' resource magic number)
22MAGIC = imp.get_magic()
23
24# Template file (searched on sys.path)
Jack Jansen3b805261999-02-14 23:12:06 +000025TEMPLATE = "PythonInterpreter"
Jack Jansen813c9971998-07-31 09:42:35 +000026
27# Specification of our resource
28RESTYPE = 'PYC '
29RESNAME = '__main__'
30
31# A resource with this name sets the "owner" (creator) of the destination
Jack Jansen81da9f11999-03-17 22:57:55 +000032# It should also have ID=0. Either of these alone is not enough.
Jack Jansen813c9971998-07-31 09:42:35 +000033OWNERNAME = "owner resource"
34
Jack Jansen81da9f11999-03-17 22:57:55 +000035# Default applet creator code
36DEFAULT_APPLET_CREATOR="Pyta"
37
Jack Jansen813c9971998-07-31 09:42:35 +000038# OpenResFile mode parameters
39READ = 1
40WRITE = 2
41
42
43def findtemplate():
44 """Locate the applet template along sys.path"""
45 for p in sys.path:
46 template = os.path.join(p, TEMPLATE)
47 try:
48 template, d1, d2 = macfs.ResolveAliasFile(template)
49 break
50 except (macfs.error, ValueError):
51 continue
52 else:
53 raise BuildError, "Template %s not found on sys.path" % `TEMPLATE`
54 template = template.as_pathname()
55 return template
56
57
58def process(template, filename, output, copy_codefragment):
59
60 if DEBUG:
61 progress = EasyDialogs.ProgressBar("Processing %s..."%os.path.split(filename)[1], 120)
62 progress.label("Compiling...")
63 else:
64 progress = None
65
66 # Read the source and compile it
67 # (there's no point overwriting the destination if it has a syntax error)
68
69 fp = open(filename)
70 text = fp.read()
71 fp.close()
72 try:
73 code = compile(text, filename, "exec")
74 except (SyntaxError, EOFError):
75 raise BuildError, "Syntax error in script %s" % `filename`
76
77 # Set the destination file name
78
79 if string.lower(filename[-3:]) == ".py":
80 destname = filename[:-3]
81 rsrcname = destname + '.rsrc'
82 else:
83 destname = filename + ".applet"
84 rsrcname = filename + '.rsrc'
85
86 if output:
87 destname = output
88
89 # Try removing the output file
90 try:
91 os.remove(destname)
92 except os.error:
93 pass
94 process_common(template, progress, code, rsrcname, destname, 0, copy_codefragment)
95
96
97def update(template, filename, output):
98 if DEBUG:
99 progress = EasyDialogs.ProgressBar("Updating %s..."%os.path.split(filename)[1], 120)
100 else:
101 progress = None
102 if not output:
103 output = filename + ' (updated)'
104
105 # Try removing the output file
106 try:
107 os.remove(output)
108 except os.error:
109 pass
110 process_common(template, progress, None, filename, output, 1, 1)
111
112
113def process_common(template, progress, code, rsrcname, destname, is_update, copy_codefragment):
114 # Create FSSpecs for the various files
115 template_fss = macfs.FSSpec(template)
116 template_fss, d1, d2 = macfs.ResolveAliasFile(template_fss)
117 dest_fss = macfs.FSSpec(destname)
118
119 # Copy data (not resources, yet) from the template
120 if DEBUG:
121 progress.label("Copy data fork...")
122 progress.set(10)
123
124 if copy_codefragment:
125 tmpl = open(template, "rb")
126 dest = open(destname, "wb")
127 data = tmpl.read()
128 if data:
129 dest.write(data)
130 dest.close()
131 tmpl.close()
132 del dest
133 del tmpl
134
135 # Open the output resource fork
136
137 if DEBUG:
138 progress.label("Copy resources...")
139 progress.set(20)
140 try:
141 output = Res.FSpOpenResFile(dest_fss, WRITE)
142 except MacOS.Error:
Jack Jansen01a2d9e2001-01-29 15:32:00 +0000143 Res.FSpCreateResFile(destname, '????', 'APPL', MACFS.smAllScripts)
Jack Jansen813c9971998-07-31 09:42:35 +0000144 output = Res.FSpOpenResFile(dest_fss, WRITE)
145
146 # Copy the resources from the target specific resource template, if any
147 typesfound, ownertype = [], None
148 try:
149 input = Res.FSpOpenResFile(rsrcname, READ)
150 except (MacOS.Error, ValueError):
151 pass
152 if DEBUG:
153 progress.inc(50)
154 else:
155 if is_update:
156 skip_oldfile = ['cfrg']
157 else:
158 skip_oldfile = []
159 typesfound, ownertype = copyres(input, output, skip_oldfile, 0, progress)
160 Res.CloseResFile(input)
161
162 # Check which resource-types we should not copy from the template
Jack Jansen81da9f11999-03-17 22:57:55 +0000163 skiptypes = []
164 if 'vers' in typesfound: skiptypes.append('vers')
Jack Jansen813c9971998-07-31 09:42:35 +0000165 if 'SIZE' in typesfound: skiptypes.append('SIZE')
166 if 'BNDL' in typesfound: skiptypes = skiptypes + ['BNDL', 'FREF', 'icl4',
167 'icl8', 'ics4', 'ics8', 'ICN#', 'ics#']
168 if not copy_codefragment:
169 skiptypes.append('cfrg')
Jack Jansen81da9f11999-03-17 22:57:55 +0000170## skipowner = (ownertype <> None)
Jack Jansen813c9971998-07-31 09:42:35 +0000171
172 # Copy the resources from the template
173
174 input = Res.FSpOpenResFile(template_fss, READ)
Jack Jansen81da9f11999-03-17 22:57:55 +0000175 dummy, tmplowner = copyres(input, output, skiptypes, 1, progress)
176
Jack Jansen813c9971998-07-31 09:42:35 +0000177 Res.CloseResFile(input)
Jack Jansen81da9f11999-03-17 22:57:55 +0000178## if ownertype == None:
179## raise BuildError, "No owner resource found in either resource file or template"
Jack Jansen813c9971998-07-31 09:42:35 +0000180 # Make sure we're manipulating the output resource file now
181
182 Res.UseResFile(output)
Jack Jansen81da9f11999-03-17 22:57:55 +0000183
184 if ownertype == None:
185 # No owner resource in the template. We have skipped the
186 # Python owner resource, so we have to add our own. The relevant
187 # bundle stuff is already included in the interpret/applet template.
188 newres = Res.Resource('\0')
189 newres.AddResource(DEFAULT_APPLET_CREATOR, 0, "Owner resource")
190 ownertype = DEFAULT_APPLET_CREATOR
Jack Jansen813c9971998-07-31 09:42:35 +0000191
192 if code:
193 # Delete any existing 'PYC ' resource named __main__
194
195 try:
196 res = Res.Get1NamedResource(RESTYPE, RESNAME)
197 res.RemoveResource()
198 except Res.Error:
199 pass
200
201 # Create the raw data for the resource from the code object
202 if DEBUG:
203 progress.label("Write PYC resource...")
204 progress.set(120)
205
206 data = marshal.dumps(code)
207 del code
208 data = (MAGIC + '\0\0\0\0') + data
209
210 # Create the resource and write it
211
212 id = 0
213 while id < 128:
214 id = Res.Unique1ID(RESTYPE)
215 res = Res.Resource(data)
216 res.AddResource(RESTYPE, id, RESNAME)
Just van Rossum874f87b1999-01-30 22:31:26 +0000217 attrs = res.GetResAttrs()
218 attrs = attrs | 0x04 # set preload
219 res.SetResAttrs(attrs)
Jack Jansen813c9971998-07-31 09:42:35 +0000220 res.WriteResource()
221 res.ReleaseResource()
222
223 # Close the output file
224
225 Res.CloseResFile(output)
226
227 # Now set the creator, type and bundle bit of the destination
228 dest_finfo = dest_fss.GetFInfo()
229 dest_finfo.Creator = ownertype
230 dest_finfo.Type = 'APPL'
Jack Jansenb70699b1999-12-03 23:38:05 +0000231 dest_finfo.Flags = dest_finfo.Flags | MACFS.kHasBundle | MACFS.kIsShared
Jack Jansen813c9971998-07-31 09:42:35 +0000232 dest_finfo.Flags = dest_finfo.Flags & ~MACFS.kHasBeenInited
233 dest_fss.SetFInfo(dest_finfo)
234
235 macostools.touched(dest_fss)
236 if DEBUG:
237 progress.label("Done.")
238
239
240# Copy resources between two resource file descriptors.
Jack Jansen81da9f11999-03-17 22:57:55 +0000241# skip a resource named '__main__' or (if skipowner is set) with ID zero.
Jack Jansen813c9971998-07-31 09:42:35 +0000242# Also skip resources with a type listed in skiptypes.
243#
244def copyres(input, output, skiptypes, skipowner, progress=None):
245 ctor = None
246 alltypes = []
247 Res.UseResFile(input)
248 ntypes = Res.Count1Types()
249 progress_type_inc = 50/ntypes
250 for itype in range(1, 1+ntypes):
251 type = Res.Get1IndType(itype)
252 if type in skiptypes:
253 continue
254 alltypes.append(type)
255 nresources = Res.Count1Resources(type)
256 progress_cur_inc = progress_type_inc/nresources
257 for ires in range(1, 1+nresources):
258 res = Res.Get1IndResource(type, ires)
259 id, type, name = res.GetResInfo()
260 lcname = string.lower(name)
Jack Jansen81da9f11999-03-17 22:57:55 +0000261
262 if lcname == OWNERNAME and id == 0:
Jack Jansen813c9971998-07-31 09:42:35 +0000263 if skipowner:
264 continue # Skip this one
265 else:
266 ctor = type
267 size = res.size
268 attrs = res.GetResAttrs()
269 if DEBUG and progress:
270 progress.label("Copy %s %d %s"%(type, id, name))
271 progress.inc(progress_cur_inc)
272 res.LoadResource()
273 res.DetachResource()
274 Res.UseResFile(output)
275 try:
276 res2 = Res.Get1Resource(type, id)
277 except MacOS.Error:
278 res2 = None
279 if res2:
280 if DEBUG and progress:
281 progress.label("Overwrite %s %d %s"%(type, id, name))
282 res2.RemoveResource()
283 res.AddResource(type, id, name)
284 res.WriteResource()
285 attrs = attrs | res.GetResAttrs()
286 res.SetResAttrs(attrs)
287 Res.UseResFile(input)
288 return alltypes, ctor
289
290