blob: badd8a1f6653413bebe4ad843d16982f8f328167 [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
32# XXXX Should look for id=0
33OWNERNAME = "owner resource"
34
35# OpenResFile mode parameters
36READ = 1
37WRITE = 2
38
39
40def findtemplate():
41 """Locate the applet template along sys.path"""
42 for p in sys.path:
43 template = os.path.join(p, TEMPLATE)
44 try:
45 template, d1, d2 = macfs.ResolveAliasFile(template)
46 break
47 except (macfs.error, ValueError):
48 continue
49 else:
50 raise BuildError, "Template %s not found on sys.path" % `TEMPLATE`
51 template = template.as_pathname()
52 return template
53
54
55def process(template, filename, output, copy_codefragment):
56
57 if DEBUG:
58 progress = EasyDialogs.ProgressBar("Processing %s..."%os.path.split(filename)[1], 120)
59 progress.label("Compiling...")
60 else:
61 progress = None
62
63 # Read the source and compile it
64 # (there's no point overwriting the destination if it has a syntax error)
65
66 fp = open(filename)
67 text = fp.read()
68 fp.close()
69 try:
70 code = compile(text, filename, "exec")
71 except (SyntaxError, EOFError):
72 raise BuildError, "Syntax error in script %s" % `filename`
73
74 # Set the destination file name
75
76 if string.lower(filename[-3:]) == ".py":
77 destname = filename[:-3]
78 rsrcname = destname + '.rsrc'
79 else:
80 destname = filename + ".applet"
81 rsrcname = filename + '.rsrc'
82
83 if output:
84 destname = output
85
86 # Try removing the output file
87 try:
88 os.remove(destname)
89 except os.error:
90 pass
91 process_common(template, progress, code, rsrcname, destname, 0, copy_codefragment)
92
93
94def update(template, filename, output):
95 if DEBUG:
96 progress = EasyDialogs.ProgressBar("Updating %s..."%os.path.split(filename)[1], 120)
97 else:
98 progress = None
99 if not output:
100 output = filename + ' (updated)'
101
102 # Try removing the output file
103 try:
104 os.remove(output)
105 except os.error:
106 pass
107 process_common(template, progress, None, filename, output, 1, 1)
108
109
110def process_common(template, progress, code, rsrcname, destname, is_update, copy_codefragment):
111 # Create FSSpecs for the various files
112 template_fss = macfs.FSSpec(template)
113 template_fss, d1, d2 = macfs.ResolveAliasFile(template_fss)
114 dest_fss = macfs.FSSpec(destname)
115
116 # Copy data (not resources, yet) from the template
117 if DEBUG:
118 progress.label("Copy data fork...")
119 progress.set(10)
120
121 if copy_codefragment:
122 tmpl = open(template, "rb")
123 dest = open(destname, "wb")
124 data = tmpl.read()
125 if data:
126 dest.write(data)
127 dest.close()
128 tmpl.close()
129 del dest
130 del tmpl
131
132 # Open the output resource fork
133
134 if DEBUG:
135 progress.label("Copy resources...")
136 progress.set(20)
137 try:
138 output = Res.FSpOpenResFile(dest_fss, WRITE)
139 except MacOS.Error:
140 Res.CreateResFile(destname)
141 output = Res.FSpOpenResFile(dest_fss, WRITE)
142
143 # Copy the resources from the target specific resource template, if any
144 typesfound, ownertype = [], None
145 try:
146 input = Res.FSpOpenResFile(rsrcname, READ)
147 except (MacOS.Error, ValueError):
148 pass
149 if DEBUG:
150 progress.inc(50)
151 else:
152 if is_update:
153 skip_oldfile = ['cfrg']
154 else:
155 skip_oldfile = []
156 typesfound, ownertype = copyres(input, output, skip_oldfile, 0, progress)
157 Res.CloseResFile(input)
158
159 # Check which resource-types we should not copy from the template
Just van Rossum1cea8041999-01-30 21:20:10 +0000160 skiptypes = ['vers']
Jack Jansen813c9971998-07-31 09:42:35 +0000161 if 'SIZE' in typesfound: skiptypes.append('SIZE')
162 if 'BNDL' in typesfound: skiptypes = skiptypes + ['BNDL', 'FREF', 'icl4',
163 'icl8', 'ics4', 'ics8', 'ICN#', 'ics#']
164 if not copy_codefragment:
165 skiptypes.append('cfrg')
166 skipowner = (ownertype <> None)
167
168 # Copy the resources from the template
169
170 input = Res.FSpOpenResFile(template_fss, READ)
171 dummy, tmplowner = copyres(input, output, skiptypes, skipowner, progress)
172 if ownertype == None:
173 ownertype = tmplowner
174 Res.CloseResFile(input)
175 if ownertype == None:
176 raise BuildError, "No owner resource found in either resource file or template"
177
178 # Make sure we're manipulating the output resource file now
179
180 Res.UseResFile(output)
181
182 if code:
183 # Delete any existing 'PYC ' resource named __main__
184
185 try:
186 res = Res.Get1NamedResource(RESTYPE, RESNAME)
187 res.RemoveResource()
188 except Res.Error:
189 pass
190
191 # Create the raw data for the resource from the code object
192 if DEBUG:
193 progress.label("Write PYC resource...")
194 progress.set(120)
195
196 data = marshal.dumps(code)
197 del code
198 data = (MAGIC + '\0\0\0\0') + data
199
200 # Create the resource and write it
201
202 id = 0
203 while id < 128:
204 id = Res.Unique1ID(RESTYPE)
205 res = Res.Resource(data)
206 res.AddResource(RESTYPE, id, RESNAME)
Just van Rossum874f87b1999-01-30 22:31:26 +0000207 attrs = res.GetResAttrs()
208 attrs = attrs | 0x04 # set preload
209 res.SetResAttrs(attrs)
Jack Jansen813c9971998-07-31 09:42:35 +0000210 res.WriteResource()
211 res.ReleaseResource()
212
213 # Close the output file
214
215 Res.CloseResFile(output)
216
217 # Now set the creator, type and bundle bit of the destination
218 dest_finfo = dest_fss.GetFInfo()
219 dest_finfo.Creator = ownertype
220 dest_finfo.Type = 'APPL'
221 dest_finfo.Flags = dest_finfo.Flags | MACFS.kHasBundle
222 dest_finfo.Flags = dest_finfo.Flags & ~MACFS.kHasBeenInited
223 dest_fss.SetFInfo(dest_finfo)
224
225 macostools.touched(dest_fss)
226 if DEBUG:
227 progress.label("Done.")
228
229
230# Copy resources between two resource file descriptors.
231# skip a resource named '__main__' or (if skipowner is set) 'Owner resource'.
232# Also skip resources with a type listed in skiptypes.
233#
234def copyres(input, output, skiptypes, skipowner, progress=None):
235 ctor = None
236 alltypes = []
237 Res.UseResFile(input)
238 ntypes = Res.Count1Types()
239 progress_type_inc = 50/ntypes
240 for itype in range(1, 1+ntypes):
241 type = Res.Get1IndType(itype)
242 if type in skiptypes:
243 continue
244 alltypes.append(type)
245 nresources = Res.Count1Resources(type)
246 progress_cur_inc = progress_type_inc/nresources
247 for ires in range(1, 1+nresources):
248 res = Res.Get1IndResource(type, ires)
249 id, type, name = res.GetResInfo()
250 lcname = string.lower(name)
251## if (type, lcname) == (RESTYPE, RESNAME):
252## continue # Don't copy __main__ from template
253 # XXXX should look for id=0
254 if lcname == OWNERNAME:
255 if skipowner:
256 continue # Skip this one
257 else:
258 ctor = type
259 size = res.size
260 attrs = res.GetResAttrs()
261 if DEBUG and progress:
262 progress.label("Copy %s %d %s"%(type, id, name))
263 progress.inc(progress_cur_inc)
264 res.LoadResource()
265 res.DetachResource()
266 Res.UseResFile(output)
267 try:
268 res2 = Res.Get1Resource(type, id)
269 except MacOS.Error:
270 res2 = None
271 if res2:
272 if DEBUG and progress:
273 progress.label("Overwrite %s %d %s"%(type, id, name))
274 res2.RemoveResource()
275 res.AddResource(type, id, name)
276 res.WriteResource()
277 attrs = attrs | res.GetResAttrs()
278 res.SetResAttrs(attrs)
279 Res.UseResFile(input)
280 return alltypes, ctor
281
282