blob: da43d096d95b2219e446ff6aa3eb4fa4e3583b00 [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
Jack Jansen5a6fdcd2001-08-25 12:15:04 +00009from Carbon import Res
Jack Jansen813c9971998-07-31 09:42:35 +000010import 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
Jack Jansena4f8e582001-02-17 23:30:19 +000043def findtemplate(template=None):
Jack Jansen813c9971998-07-31 09:42:35 +000044 """Locate the applet template along sys.path"""
Jack Jansena4f8e582001-02-17 23:30:19 +000045 if not template:
46 template=TEMPLATE
Jack Jansen813c9971998-07-31 09:42:35 +000047 for p in sys.path:
Jack Jansena4f8e582001-02-17 23:30:19 +000048 file = os.path.join(p, template)
Jack Jansen813c9971998-07-31 09:42:35 +000049 try:
Jack Jansena4f8e582001-02-17 23:30:19 +000050 file, d1, d2 = macfs.ResolveAliasFile(file)
Jack Jansen813c9971998-07-31 09:42:35 +000051 break
52 except (macfs.error, ValueError):
53 continue
54 else:
Jack Jansena4f8e582001-02-17 23:30:19 +000055 raise BuildError, "Template %s not found on sys.path" % `template`
56 file = file.as_pathname()
57 return file
Jack Jansen813c9971998-07-31 09:42:35 +000058
59
60def process(template, filename, output, copy_codefragment):
61
62 if DEBUG:
63 progress = EasyDialogs.ProgressBar("Processing %s..."%os.path.split(filename)[1], 120)
64 progress.label("Compiling...")
65 else:
66 progress = None
67
68 # Read the source and compile it
69 # (there's no point overwriting the destination if it has a syntax error)
70
71 fp = open(filename)
72 text = fp.read()
73 fp.close()
74 try:
75 code = compile(text, filename, "exec")
76 except (SyntaxError, EOFError):
77 raise BuildError, "Syntax error in script %s" % `filename`
78
79 # Set the destination file name
80
81 if string.lower(filename[-3:]) == ".py":
82 destname = filename[:-3]
83 rsrcname = destname + '.rsrc'
84 else:
85 destname = filename + ".applet"
86 rsrcname = filename + '.rsrc'
87
88 if output:
89 destname = output
90
91 # Try removing the output file
92 try:
93 os.remove(destname)
94 except os.error:
95 pass
96 process_common(template, progress, code, rsrcname, destname, 0, copy_codefragment)
97
98
99def update(template, filename, output):
100 if DEBUG:
101 progress = EasyDialogs.ProgressBar("Updating %s..."%os.path.split(filename)[1], 120)
102 else:
103 progress = None
104 if not output:
105 output = filename + ' (updated)'
106
107 # Try removing the output file
108 try:
109 os.remove(output)
110 except os.error:
111 pass
112 process_common(template, progress, None, filename, output, 1, 1)
113
114
115def process_common(template, progress, code, rsrcname, destname, is_update, copy_codefragment):
116 # Create FSSpecs for the various files
117 template_fss = macfs.FSSpec(template)
118 template_fss, d1, d2 = macfs.ResolveAliasFile(template_fss)
119 dest_fss = macfs.FSSpec(destname)
120
121 # Copy data (not resources, yet) from the template
122 if DEBUG:
123 progress.label("Copy data fork...")
124 progress.set(10)
125
126 if copy_codefragment:
127 tmpl = open(template, "rb")
128 dest = open(destname, "wb")
129 data = tmpl.read()
130 if data:
131 dest.write(data)
132 dest.close()
133 tmpl.close()
134 del dest
135 del tmpl
136
137 # Open the output resource fork
138
139 if DEBUG:
140 progress.label("Copy resources...")
141 progress.set(20)
142 try:
143 output = Res.FSpOpenResFile(dest_fss, WRITE)
144 except MacOS.Error:
Jack Jansen01a2d9e2001-01-29 15:32:00 +0000145 Res.FSpCreateResFile(destname, '????', 'APPL', MACFS.smAllScripts)
Jack Jansen813c9971998-07-31 09:42:35 +0000146 output = Res.FSpOpenResFile(dest_fss, WRITE)
147
148 # Copy the resources from the target specific resource template, if any
149 typesfound, ownertype = [], None
150 try:
151 input = Res.FSpOpenResFile(rsrcname, READ)
152 except (MacOS.Error, ValueError):
153 pass
154 if DEBUG:
155 progress.inc(50)
156 else:
157 if is_update:
158 skip_oldfile = ['cfrg']
159 else:
160 skip_oldfile = []
161 typesfound, ownertype = copyres(input, output, skip_oldfile, 0, progress)
162 Res.CloseResFile(input)
163
164 # Check which resource-types we should not copy from the template
Jack Jansen81da9f11999-03-17 22:57:55 +0000165 skiptypes = []
166 if 'vers' in typesfound: skiptypes.append('vers')
Jack Jansen813c9971998-07-31 09:42:35 +0000167 if 'SIZE' in typesfound: skiptypes.append('SIZE')
168 if 'BNDL' in typesfound: skiptypes = skiptypes + ['BNDL', 'FREF', 'icl4',
169 'icl8', 'ics4', 'ics8', 'ICN#', 'ics#']
170 if not copy_codefragment:
171 skiptypes.append('cfrg')
Jack Jansen81da9f11999-03-17 22:57:55 +0000172## skipowner = (ownertype <> None)
Jack Jansen813c9971998-07-31 09:42:35 +0000173
174 # Copy the resources from the template
175
176 input = Res.FSpOpenResFile(template_fss, READ)
Jack Jansen81da9f11999-03-17 22:57:55 +0000177 dummy, tmplowner = copyres(input, output, skiptypes, 1, progress)
178
Jack Jansen813c9971998-07-31 09:42:35 +0000179 Res.CloseResFile(input)
Jack Jansen81da9f11999-03-17 22:57:55 +0000180## if ownertype == None:
181## raise BuildError, "No owner resource found in either resource file or template"
Jack Jansen813c9971998-07-31 09:42:35 +0000182 # Make sure we're manipulating the output resource file now
183
184 Res.UseResFile(output)
Jack Jansen81da9f11999-03-17 22:57:55 +0000185
186 if ownertype == None:
187 # No owner resource in the template. We have skipped the
188 # Python owner resource, so we have to add our own. The relevant
189 # bundle stuff is already included in the interpret/applet template.
190 newres = Res.Resource('\0')
191 newres.AddResource(DEFAULT_APPLET_CREATOR, 0, "Owner resource")
192 ownertype = DEFAULT_APPLET_CREATOR
Jack Jansen813c9971998-07-31 09:42:35 +0000193
194 if code:
195 # Delete any existing 'PYC ' resource named __main__
196
197 try:
198 res = Res.Get1NamedResource(RESTYPE, RESNAME)
199 res.RemoveResource()
200 except Res.Error:
201 pass
202
203 # Create the raw data for the resource from the code object
204 if DEBUG:
205 progress.label("Write PYC resource...")
206 progress.set(120)
207
208 data = marshal.dumps(code)
209 del code
210 data = (MAGIC + '\0\0\0\0') + data
211
212 # Create the resource and write it
213
214 id = 0
215 while id < 128:
216 id = Res.Unique1ID(RESTYPE)
217 res = Res.Resource(data)
218 res.AddResource(RESTYPE, id, RESNAME)
Just van Rossum874f87b1999-01-30 22:31:26 +0000219 attrs = res.GetResAttrs()
220 attrs = attrs | 0x04 # set preload
221 res.SetResAttrs(attrs)
Jack Jansen813c9971998-07-31 09:42:35 +0000222 res.WriteResource()
223 res.ReleaseResource()
224
225 # Close the output file
226
227 Res.CloseResFile(output)
228
229 # Now set the creator, type and bundle bit of the destination
230 dest_finfo = dest_fss.GetFInfo()
231 dest_finfo.Creator = ownertype
232 dest_finfo.Type = 'APPL'
Jack Jansenb70699b1999-12-03 23:38:05 +0000233 dest_finfo.Flags = dest_finfo.Flags | MACFS.kHasBundle | MACFS.kIsShared
Jack Jansen813c9971998-07-31 09:42:35 +0000234 dest_finfo.Flags = dest_finfo.Flags & ~MACFS.kHasBeenInited
235 dest_fss.SetFInfo(dest_finfo)
236
237 macostools.touched(dest_fss)
238 if DEBUG:
239 progress.label("Done.")
240
241
242# Copy resources between two resource file descriptors.
Jack Jansen81da9f11999-03-17 22:57:55 +0000243# skip a resource named '__main__' or (if skipowner is set) with ID zero.
Jack Jansen813c9971998-07-31 09:42:35 +0000244# Also skip resources with a type listed in skiptypes.
245#
246def copyres(input, output, skiptypes, skipowner, progress=None):
247 ctor = None
248 alltypes = []
249 Res.UseResFile(input)
250 ntypes = Res.Count1Types()
251 progress_type_inc = 50/ntypes
252 for itype in range(1, 1+ntypes):
253 type = Res.Get1IndType(itype)
254 if type in skiptypes:
255 continue
256 alltypes.append(type)
257 nresources = Res.Count1Resources(type)
258 progress_cur_inc = progress_type_inc/nresources
259 for ires in range(1, 1+nresources):
260 res = Res.Get1IndResource(type, ires)
261 id, type, name = res.GetResInfo()
262 lcname = string.lower(name)
Jack Jansen81da9f11999-03-17 22:57:55 +0000263
264 if lcname == OWNERNAME and id == 0:
Jack Jansen813c9971998-07-31 09:42:35 +0000265 if skipowner:
266 continue # Skip this one
267 else:
268 ctor = type
269 size = res.size
270 attrs = res.GetResAttrs()
271 if DEBUG and progress:
272 progress.label("Copy %s %d %s"%(type, id, name))
273 progress.inc(progress_cur_inc)
274 res.LoadResource()
275 res.DetachResource()
276 Res.UseResFile(output)
277 try:
278 res2 = Res.Get1Resource(type, id)
279 except MacOS.Error:
280 res2 = None
281 if res2:
282 if DEBUG and progress:
283 progress.label("Overwrite %s %d %s"%(type, id, name))
284 res2.RemoveResource()
285 res.AddResource(type, id, name)
286 res.WriteResource()
287 attrs = attrs | res.GetResAttrs()
288 res.SetResAttrs(attrs)
289 Res.UseResFile(input)
290 return alltypes, ctor
291
292