blob: 77a5eda40848a51e6c8ae85809373e63891af6bd [file] [log] [blame]
Jack Jansen7571f301995-07-29 13:48:41 +00001"""Create an applet from a Python script.
2
3This puts up a dialog asking for a Python source file ('TEXT').
4The output is a file with the same name but its ".py" suffix dropped.
5It is created by copying an applet template and then adding a 'PYC '
6resource named __main__ containing the compiled, marshalled script.
7"""
8
Jack Jansen0f452fa1995-09-01 11:54:11 +00009DEBUG=0
10
Jack Jansen7571f301995-07-29 13:48:41 +000011import sys
12sys.stdout = sys.stderr
13
14import string
15import os
16import marshal
17import imp
18import macfs
Jack Jansen7c86b211995-08-31 13:48:43 +000019import MACFS
Jack Jansen7571f301995-07-29 13:48:41 +000020import MacOS
21from Res import *
Jack Jansen29a33551996-09-15 22:13:59 +000022import macostools
Jack Jansenaf647dd1997-05-13 15:42:26 +000023import EasyDialogs
Jack Jansen7571f301995-07-29 13:48:41 +000024
25# .pyc file (and 'PYC ' resource magic number)
26MAGIC = imp.get_magic()
27
28# Template file (searched on sys.path)
29TEMPLATE = "PythonApplet"
30
31# Specification of our resource
32RESTYPE = 'PYC '
33RESNAME = '__main__'
34
35# A resource with this name sets the "owner" (creator) of the destination
Jack Jansen3f14f4a1995-09-24 21:05:24 +000036# XXXX Should look for id=0
Jack Jansen7571f301995-07-29 13:48:41 +000037OWNERNAME = "owner resource"
38
39# OpenResFile mode parameters
40READ = 1
41WRITE = 2
42
Jack Jansen0f452fa1995-09-01 11:54:11 +000043def findtemplate():
44 """Locate the applet template along sys.path"""
Jack Jansen7571f301995-07-29 13:48:41 +000045 for p in sys.path:
46 template = os.path.join(p, TEMPLATE)
47 try:
Jack Jansen7c86b211995-08-31 13:48:43 +000048 template, d1, d2 = macfs.ResolveAliasFile(template)
Jack Jansen7571f301995-07-29 13:48:41 +000049 break
Jack Jansen7c86b211995-08-31 13:48:43 +000050 except (macfs.error, ValueError):
Jack Jansen7571f301995-07-29 13:48:41 +000051 continue
52 else:
Jack Jansen7c86b211995-08-31 13:48:43 +000053 die("Template %s not found on sys.path" % `TEMPLATE`)
Jack Jansen7571f301995-07-29 13:48:41 +000054 return
Jack Jansen7c86b211995-08-31 13:48:43 +000055 template = template.as_pathname()
Jack Jansen0f452fa1995-09-01 11:54:11 +000056 return template
57
58def main():
Jack Jansenaf647dd1997-05-13 15:42:26 +000059 global DEBUG
60 DEBUG=1
Jack Jansen0f452fa1995-09-01 11:54:11 +000061
62 # Find the template
63 # (there's no point in proceeding if we can't find it)
64
65 template = findtemplate()
Jack Jansen7c86b211995-08-31 13:48:43 +000066
Jack Jansen7571f301995-07-29 13:48:41 +000067 # Ask for source text if not specified in sys.argv[1:]
68
69 if not sys.argv[1:]:
Jack Jansen8554e301998-02-20 16:06:56 +000070 srcfss, ok = macfs.PromptGetFile('Select Python source or applet:', 'TEXT', 'APPL')
Jack Jansen7571f301995-07-29 13:48:41 +000071 if not ok:
72 return
73 filename = srcfss.as_pathname()
74 tp, tf = os.path.split(filename)
75 if tf[-3:] == '.py':
76 tf = tf[:-3]
77 else:
Jack Jansen8554e301998-02-20 16:06:56 +000078 tf = tf + '.out'
Jack Jansen7571f301995-07-29 13:48:41 +000079 dstfss, ok = macfs.StandardPutFile('Save application as:', tf)
80 if not ok: return
Jack Jansen8554e301998-02-20 16:06:56 +000081 dstfilename = dstfss.as_pathname()
82 cr, tp = MacOS.GetCreatorAndType(filename)
83 if tp == 'APPL':
84 update(template, filename, dstfilename)
85 else:
86 process(template, filename, dstfilename)
Jack Jansen7571f301995-07-29 13:48:41 +000087 else:
88
89 # Loop over all files to be processed
90 for filename in sys.argv[1:]:
Jack Jansen8554e301998-02-20 16:06:56 +000091 cr, tp = MacOS.GetCreatorAndType(filename)
92 if tp == 'APPL':
93 update(template, filename, '')
94 else:
95 process(template, filename, '')
Jack Jansen7571f301995-07-29 13:48:41 +000096
Jack Jansen7571f301995-07-29 13:48:41 +000097def process(template, filename, output):
98
Jack Jansen0f452fa1995-09-01 11:54:11 +000099 if DEBUG:
Jack Jansenaf647dd1997-05-13 15:42:26 +0000100 progress = EasyDialogs.ProgressBar("Processing %s..."%os.path.split(filename)[1], 120)
101 progress.label("Compiling...")
102 else:
103 progress = None
Jack Jansen7571f301995-07-29 13:48:41 +0000104
105 # Read the source and compile it
106 # (there's no point overwriting the destination if it has a syntax error)
107
108 fp = open(filename)
109 text = fp.read()
110 fp.close()
111 try:
112 code = compile(text, filename, "exec")
113 except (SyntaxError, EOFError):
114 die("Syntax error in script %s" % `filename`)
115 return
116
117 # Set the destination file name
118
119 if string.lower(filename[-3:]) == ".py":
120 destname = filename[:-3]
121 rsrcname = destname + '.rsrc'
122 else:
123 destname = filename + ".applet"
124 rsrcname = filename + '.rsrc'
125
126 if output:
127 destname = output
Jack Jansen8554e301998-02-20 16:06:56 +0000128
129 process_common(template, progress, code, rsrcname, destname, 0)
130
131def update(template, filename, output):
132 if DEBUG:
133 progress = EasyDialogs.ProgressBar("Updating %s..."%os.path.split(filename)[1], 120)
134 else:
135 progress = None
136 if not output:
137 output = filename + ' (updated)'
138 process_common(template, progress, None, filename, output, 1)
139
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000140
Jack Jansen8554e301998-02-20 16:06:56 +0000141def process_common(template, progress, code, rsrcname, destname, is_update):
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000142 # Try removing the output file
143 try:
Jack Jansen8554e301998-02-20 16:06:56 +0000144 os.unlink(destname)
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000145 except os.error:
146 pass
147
148
149 # Create FSSpecs for the various files
150
Jack Jansen7c86b211995-08-31 13:48:43 +0000151 template_fss = macfs.FSSpec(template)
152 template_fss, d1, d2 = macfs.ResolveAliasFile(template_fss)
153 dest_fss = macfs.FSSpec(destname)
154
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000155 # Copy data (not resources, yet) from the template
Jack Jansenaf647dd1997-05-13 15:42:26 +0000156 if DEBUG:
157 progress.label("Copy data fork...")
158 progress.set(10)
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000159
Jack Jansen7571f301995-07-29 13:48:41 +0000160 tmpl = open(template, "rb")
161 dest = open(destname, "wb")
162 data = tmpl.read()
163 if data:
164 dest.write(data)
165 dest.close()
166 tmpl.close()
167
Jack Jansen7571f301995-07-29 13:48:41 +0000168 # Open the output resource fork
169
Jack Jansenaf647dd1997-05-13 15:42:26 +0000170 if DEBUG:
171 progress.label("Copy resources...")
172 progress.set(20)
Jack Jansen7571f301995-07-29 13:48:41 +0000173 try:
Jack Jansen7c86b211995-08-31 13:48:43 +0000174 output = FSpOpenResFile(dest_fss, WRITE)
Jack Jansen7571f301995-07-29 13:48:41 +0000175 except MacOS.Error:
Jack Jansen7571f301995-07-29 13:48:41 +0000176 CreateResFile(destname)
Jack Jansen7c86b211995-08-31 13:48:43 +0000177 output = FSpOpenResFile(dest_fss, WRITE)
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000178
179 # Copy the resources from the target specific resource template, if any
180 typesfound, ownertype = [], None
181 try:
182 input = FSpOpenResFile(rsrcname, READ)
183 except (MacOS.Error, ValueError):
184 pass
Jack Jansenaf647dd1997-05-13 15:42:26 +0000185 if DEBUG:
186 progress.inc(50)
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000187 else:
Jack Jansen8554e301998-02-20 16:06:56 +0000188 if is_update:
189 skip_oldfile = ['cfrg']
190 else:
191 skip_oldfile = []
192 typesfound, ownertype = copyres(input, output, skip_oldfile, 0, progress)
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000193 CloseResFile(input)
194
195 # Check which resource-types we should not copy from the template
196 skiptypes = []
197 if 'SIZE' in typesfound: skiptypes.append('SIZE')
198 if 'BNDL' in typesfound: skiptypes = skiptypes + ['BNDL', 'FREF', 'icl4',
199 'icl8', 'ics4', 'ics8', 'ICN#', 'ics#']
200 skipowner = (ownertype <> None)
Jack Jansen7571f301995-07-29 13:48:41 +0000201
202 # Copy the resources from the template
203
Jack Jansen7c86b211995-08-31 13:48:43 +0000204 input = FSpOpenResFile(template_fss, READ)
Jack Jansenaf647dd1997-05-13 15:42:26 +0000205 dummy, tmplowner = copyres(input, output, skiptypes, skipowner, progress)
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000206 if ownertype == None:
207 ownertype = tmplowner
Jack Jansen7571f301995-07-29 13:48:41 +0000208 CloseResFile(input)
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000209 if ownertype == None:
210 die("No owner resource found in either resource file or template")
Jack Jansen7571f301995-07-29 13:48:41 +0000211
Jack Jansen7c86b211995-08-31 13:48:43 +0000212 # Now set the creator, type and bundle bit of the destination
213 dest_finfo = dest_fss.GetFInfo()
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000214 dest_finfo.Creator = ownertype
215 dest_finfo.Type = 'APPL'
Jack Jansen7c86b211995-08-31 13:48:43 +0000216 dest_finfo.Flags = dest_finfo.Flags | MACFS.kHasBundle
217 dest_finfo.Flags = dest_finfo.Flags & ~MACFS.kHasBeenInited
218 dest_fss.SetFInfo(dest_finfo)
Jack Jansen7571f301995-07-29 13:48:41 +0000219
220 # Make sure we're manipulating the output resource file now
221
222 UseResFile(output)
223
Jack Jansen8554e301998-02-20 16:06:56 +0000224 if code:
225 # Delete any existing 'PYC ' resource named __main__
226
227 try:
228 res = Get1NamedResource(RESTYPE, RESNAME)
229 res.RemoveResource()
230 except Error:
231 pass
Jack Jansen7571f301995-07-29 13:48:41 +0000232
Jack Jansen8554e301998-02-20 16:06:56 +0000233 # Create the raw data for the resource from the code object
234 if DEBUG:
235 progress.label("Write PYC resource...")
236 progress.set(120)
237
238 data = marshal.dumps(code)
239 del code
240 data = (MAGIC + '\0\0\0\0') + data
241
242 # Create the resource and write it
243
244 id = 0
245 while id < 128:
246 id = Unique1ID(RESTYPE)
247 res = Resource(data)
248 res.AddResource(RESTYPE, id, RESNAME)
249 res.WriteResource()
250 res.ReleaseResource()
Jack Jansen7571f301995-07-29 13:48:41 +0000251
252 # Close the output file
253
254 CloseResFile(output)
255
Jack Jansen29a33551996-09-15 22:13:59 +0000256 macostools.touched(dest_fss)
Jack Jansen055782a1996-08-28 14:16:39 +0000257 if DEBUG:
Jack Jansenaf647dd1997-05-13 15:42:26 +0000258 progress.label("Done.")
Jack Jansen7571f301995-07-29 13:48:41 +0000259
260
261# Copy resources between two resource file descriptors.
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000262# skip a resource named '__main__' or (if skipowner is set) 'Owner resource'.
263# Also skip resources with a type listed in skiptypes.
264#
Jack Jansenaf647dd1997-05-13 15:42:26 +0000265def copyres(input, output, skiptypes, skipowner, progress=None):
Jack Jansen7571f301995-07-29 13:48:41 +0000266 ctor = None
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000267 alltypes = []
Jack Jansen7571f301995-07-29 13:48:41 +0000268 UseResFile(input)
269 ntypes = Count1Types()
Jack Jansenaf647dd1997-05-13 15:42:26 +0000270 progress_type_inc = 50/ntypes
Jack Jansen7571f301995-07-29 13:48:41 +0000271 for itype in range(1, 1+ntypes):
272 type = Get1IndType(itype)
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000273 if type in skiptypes:
274 continue
275 alltypes.append(type)
Jack Jansen7571f301995-07-29 13:48:41 +0000276 nresources = Count1Resources(type)
Jack Jansenaf647dd1997-05-13 15:42:26 +0000277 progress_cur_inc = progress_type_inc/nresources
Jack Jansen7571f301995-07-29 13:48:41 +0000278 for ires in range(1, 1+nresources):
279 res = Get1IndResource(type, ires)
280 id, type, name = res.GetResInfo()
281 lcname = string.lower(name)
Jack Jansen8554e301998-02-20 16:06:56 +0000282## if (type, lcname) == (RESTYPE, RESNAME):
283## continue # Don't copy __main__ from template
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000284 # XXXX should look for id=0
285 if lcname == OWNERNAME:
286 if skipowner:
287 continue # Skip this one
288 else:
289 ctor = type
Jack Jansen7571f301995-07-29 13:48:41 +0000290 size = res.size
291 attrs = res.GetResAttrs()
Jack Jansenaf647dd1997-05-13 15:42:26 +0000292 if DEBUG and progress:
293 progress.label("Copy %s %d %s"%(type, id, name))
294 progress.inc(progress_cur_inc)
Jack Jansen7571f301995-07-29 13:48:41 +0000295 res.LoadResource()
296 res.DetachResource()
297 UseResFile(output)
298 try:
299 res2 = Get1Resource(type, id)
300 except MacOS.Error:
301 res2 = None
302 if res2:
Jack Jansenaf647dd1997-05-13 15:42:26 +0000303 if DEBUG and progress:
304 progress.label("Overwrite %s %d %s"%(type, id, name))
Jack Jansen7571f301995-07-29 13:48:41 +0000305 res2.RemoveResource()
306 res.AddResource(type, id, name)
307 res.WriteResource()
308 attrs = attrs | res.GetResAttrs()
Jack Jansen7571f301995-07-29 13:48:41 +0000309 res.SetResAttrs(attrs)
310 UseResFile(input)
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000311 return alltypes, ctor
Jack Jansen7571f301995-07-29 13:48:41 +0000312
313
314# Show a message and exit
315
316def die(str):
317 message(str)
318 sys.exit(1)
319
320
321# Show a message
322
323def message(str, id = 256):
324 from Dlg import *
325 d = GetNewDialog(id, -1)
326 if not d:
327 print "Error:", `str`
328 print "DLOG id =", id, "not found."
329 return
330 tp, h, rect = d.GetDialogItem(2)
331 SetDialogItemText(h, str)
Jack Jansen6c6ad831996-01-22 14:55:52 +0000332 d.SetDialogDefaultItem(1)
Jack Jansen7571f301995-07-29 13:48:41 +0000333 while 1:
334 n = ModalDialog(None)
335 if n == 1: break
336 del d
337
338
339if __name__ == '__main__':
340 main()