blob: 8da28d96453f688965d8d139680bf388b61180a7 [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 *
22
23# .pyc file (and 'PYC ' resource magic number)
24MAGIC = imp.get_magic()
25
26# Template file (searched on sys.path)
27TEMPLATE = "PythonApplet"
28
29# Specification of our resource
30RESTYPE = 'PYC '
31RESNAME = '__main__'
32
33# A resource with this name sets the "owner" (creator) of the destination
Jack Jansen3f14f4a1995-09-24 21:05:24 +000034# XXXX Should look for id=0
Jack Jansen7571f301995-07-29 13:48:41 +000035OWNERNAME = "owner resource"
36
37# OpenResFile mode parameters
38READ = 1
39WRITE = 2
40
Jack Jansen0f452fa1995-09-01 11:54:11 +000041def findtemplate():
42 """Locate the applet template along sys.path"""
Jack Jansen7571f301995-07-29 13:48:41 +000043 for p in sys.path:
44 template = os.path.join(p, TEMPLATE)
45 try:
Jack Jansen7c86b211995-08-31 13:48:43 +000046 template, d1, d2 = macfs.ResolveAliasFile(template)
Jack Jansen7571f301995-07-29 13:48:41 +000047 break
Jack Jansen7c86b211995-08-31 13:48:43 +000048 except (macfs.error, ValueError):
Jack Jansen7571f301995-07-29 13:48:41 +000049 continue
50 else:
Jack Jansen7c86b211995-08-31 13:48:43 +000051 die("Template %s not found on sys.path" % `TEMPLATE`)
Jack Jansen7571f301995-07-29 13:48:41 +000052 return
Jack Jansen7c86b211995-08-31 13:48:43 +000053 template = template.as_pathname()
Jack Jansen0f452fa1995-09-01 11:54:11 +000054 return template
55
56def main():
57 global DEBUG
58 DEBUG=1
59
60 # Find the template
61 # (there's no point in proceeding if we can't find it)
62
63 template = findtemplate()
Jack Jansen3f14f4a1995-09-24 21:05:24 +000064 if DEBUG:
65 print 'Using template', template
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 Jansen9062fa21995-08-14 12:21:12 +000070 srcfss, ok = macfs.PromptGetFile('Select Python source file:', 'TEXT')
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:
78 tf = tf + '.applet'
79 dstfss, ok = macfs.StandardPutFile('Save application as:', tf)
80 if not ok: return
81 process(template, filename, dstfss.as_pathname())
82 else:
83
84 # Loop over all files to be processed
85 for filename in sys.argv[1:]:
86 process(template, filename, '')
87
Jack Jansen7571f301995-07-29 13:48:41 +000088def process(template, filename, output):
89
Jack Jansen0f452fa1995-09-01 11:54:11 +000090 if DEBUG:
91 print "Processing", `filename`, "..."
Jack Jansen7571f301995-07-29 13:48:41 +000092
93 # Read the source and compile it
94 # (there's no point overwriting the destination if it has a syntax error)
95
96 fp = open(filename)
97 text = fp.read()
98 fp.close()
99 try:
100 code = compile(text, filename, "exec")
101 except (SyntaxError, EOFError):
102 die("Syntax error in script %s" % `filename`)
103 return
104
105 # Set the destination file name
106
107 if string.lower(filename[-3:]) == ".py":
108 destname = filename[:-3]
109 rsrcname = destname + '.rsrc'
110 else:
111 destname = filename + ".applet"
112 rsrcname = filename + '.rsrc'
113
114 if output:
115 destname = output
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000116
117 # Try removing the output file
118 try:
119 os.unlink(output)
120 except os.error:
121 pass
122
123
124 # Create FSSpecs for the various files
125
Jack Jansen7c86b211995-08-31 13:48:43 +0000126 template_fss = macfs.FSSpec(template)
127 template_fss, d1, d2 = macfs.ResolveAliasFile(template_fss)
128 dest_fss = macfs.FSSpec(destname)
129
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000130 # Copy data (not resources, yet) from the template
131
Jack Jansen7571f301995-07-29 13:48:41 +0000132 tmpl = open(template, "rb")
133 dest = open(destname, "wb")
134 data = tmpl.read()
135 if data:
136 dest.write(data)
137 dest.close()
138 tmpl.close()
139
Jack Jansen7571f301995-07-29 13:48:41 +0000140 # Open the output resource fork
141
142 try:
Jack Jansen7c86b211995-08-31 13:48:43 +0000143 output = FSpOpenResFile(dest_fss, WRITE)
Jack Jansen7571f301995-07-29 13:48:41 +0000144 except MacOS.Error:
Jack Jansen0f452fa1995-09-01 11:54:11 +0000145 if DEBUG:
146 print "Creating resource fork..."
Jack Jansen7571f301995-07-29 13:48:41 +0000147 CreateResFile(destname)
Jack Jansen7c86b211995-08-31 13:48:43 +0000148 output = FSpOpenResFile(dest_fss, WRITE)
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000149
150 # Copy the resources from the target specific resource template, if any
151 typesfound, ownertype = [], None
152 try:
153 input = FSpOpenResFile(rsrcname, READ)
154 except (MacOS.Error, ValueError):
155 pass
156 else:
157 typesfound, ownertype = copyres(input, output, [], 0)
158 CloseResFile(input)
159
160 # Check which resource-types we should not copy from the template
161 skiptypes = []
162 if 'SIZE' in typesfound: skiptypes.append('SIZE')
163 if 'BNDL' in typesfound: skiptypes = skiptypes + ['BNDL', 'FREF', 'icl4',
164 'icl8', 'ics4', 'ics8', 'ICN#', 'ics#']
165 skipowner = (ownertype <> None)
Jack Jansen7571f301995-07-29 13:48:41 +0000166
167 # Copy the resources from the template
168
Jack Jansen7c86b211995-08-31 13:48:43 +0000169 input = FSpOpenResFile(template_fss, READ)
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000170 dummy, tmplowner = copyres(input, output, skiptypes, skipowner)
171 if ownertype == None:
172 ownertype = tmplowner
Jack Jansen7571f301995-07-29 13:48:41 +0000173 CloseResFile(input)
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000174 if ownertype == None:
175 die("No owner resource found in either resource file or template")
Jack Jansen7571f301995-07-29 13:48:41 +0000176
Jack Jansen7c86b211995-08-31 13:48:43 +0000177 # Now set the creator, type and bundle bit of the destination
178 dest_finfo = dest_fss.GetFInfo()
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000179 dest_finfo.Creator = ownertype
180 dest_finfo.Type = 'APPL'
Jack Jansen7c86b211995-08-31 13:48:43 +0000181 dest_finfo.Flags = dest_finfo.Flags | MACFS.kHasBundle
182 dest_finfo.Flags = dest_finfo.Flags & ~MACFS.kHasBeenInited
183 dest_fss.SetFInfo(dest_finfo)
Jack Jansen7571f301995-07-29 13:48:41 +0000184
185 # Make sure we're manipulating the output resource file now
186
187 UseResFile(output)
188
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000189 # Delete any existing 'PYC ' resource named __main__
Jack Jansen7571f301995-07-29 13:48:41 +0000190
191 try:
192 res = Get1NamedResource(RESTYPE, RESNAME)
193 res.RemoveResource()
194 except Error:
195 pass
196
197 # Create the raw data for the resource from the code object
198
199 data = marshal.dumps(code)
200 del code
201 data = (MAGIC + '\0\0\0\0') + data
202
203 # Create the resource and write it
204
205 id = 0
206 while id < 128:
207 id = Unique1ID(RESTYPE)
208 res = Resource(data)
209 res.AddResource(RESTYPE, id, RESNAME)
210 res.WriteResource()
211 res.ReleaseResource()
212
213 # Close the output file
214
215 CloseResFile(output)
216
217 # Give positive feedback
218
219 message("Applet %s created." % `destname`)
220
221
222# Copy resources between two resource file descriptors.
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000223# skip a resource named '__main__' or (if skipowner is set) 'Owner resource'.
224# Also skip resources with a type listed in skiptypes.
225#
226def copyres(input, output, skiptypes, skipowner):
Jack Jansen7571f301995-07-29 13:48:41 +0000227 ctor = None
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000228 alltypes = []
Jack Jansen7571f301995-07-29 13:48:41 +0000229 UseResFile(input)
230 ntypes = Count1Types()
231 for itype in range(1, 1+ntypes):
232 type = Get1IndType(itype)
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000233 if type in skiptypes:
234 continue
235 alltypes.append(type)
Jack Jansen7571f301995-07-29 13:48:41 +0000236 nresources = Count1Resources(type)
237 for ires in range(1, 1+nresources):
238 res = Get1IndResource(type, ires)
239 id, type, name = res.GetResInfo()
240 lcname = string.lower(name)
241 if (type, lcname) == (RESTYPE, RESNAME):
242 continue # Don't copy __main__ from template
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000243 # XXXX should look for id=0
244 if lcname == OWNERNAME:
245 if skipowner:
246 continue # Skip this one
247 else:
248 ctor = type
Jack Jansen7571f301995-07-29 13:48:41 +0000249 size = res.size
250 attrs = res.GetResAttrs()
Jack Jansen0f452fa1995-09-01 11:54:11 +0000251 if DEBUG:
252 print id, type, name, size, hex(attrs)
Jack Jansen7571f301995-07-29 13:48:41 +0000253 res.LoadResource()
254 res.DetachResource()
255 UseResFile(output)
256 try:
257 res2 = Get1Resource(type, id)
258 except MacOS.Error:
259 res2 = None
260 if res2:
Jack Jansen0f452fa1995-09-01 11:54:11 +0000261 if DEBUG:
262 print "Overwriting..."
Jack Jansen7571f301995-07-29 13:48:41 +0000263 res2.RemoveResource()
264 res.AddResource(type, id, name)
265 res.WriteResource()
266 attrs = attrs | res.GetResAttrs()
Jack Jansen0f452fa1995-09-01 11:54:11 +0000267 if DEBUG:
268 print "New attrs =", hex(attrs)
Jack Jansen7571f301995-07-29 13:48:41 +0000269 res.SetResAttrs(attrs)
270 UseResFile(input)
Jack Jansen3f14f4a1995-09-24 21:05:24 +0000271 return alltypes, ctor
Jack Jansen7571f301995-07-29 13:48:41 +0000272
273
274# Show a message and exit
275
276def die(str):
277 message(str)
278 sys.exit(1)
279
280
281# Show a message
282
283def message(str, id = 256):
284 from Dlg import *
285 d = GetNewDialog(id, -1)
286 if not d:
287 print "Error:", `str`
288 print "DLOG id =", id, "not found."
289 return
290 tp, h, rect = d.GetDialogItem(2)
291 SetDialogItemText(h, str)
Jack Jansen6c6ad831996-01-22 14:55:52 +0000292 d.SetDialogDefaultItem(1)
Jack Jansen7571f301995-07-29 13:48:41 +0000293 while 1:
294 n = ModalDialog(None)
295 if n == 1: break
296 del d
297
298
299if __name__ == '__main__':
300 main()