Improved some error messages for command line processing.
[python/dscho.git] / Mac / Lib / buildtools.py
blob62fc38e19ddc8a7951e5bef10441e535636ea463
1 """tools for BuildApplet and BuildApplication"""
3 import sys
4 import os
5 import string
6 import imp
7 import marshal
8 import macfs
9 import Res
10 import MACFS
11 import MacOS
12 import macostools
13 import EasyDialogs
16 BuildError = "BuildError"
18 DEBUG=1
21 # .pyc file (and 'PYC ' resource magic number)
22 MAGIC = imp.get_magic()
24 # Template file (searched on sys.path)
25 TEMPLATE = "PythonApplet"
27 # Specification of our resource
28 RESTYPE = 'PYC '
29 RESNAME = '__main__'
31 # A resource with this name sets the "owner" (creator) of the destination
32 # XXXX Should look for id=0
33 OWNERNAME = "owner resource"
35 # OpenResFile mode parameters
36 READ = 1
37 WRITE = 2
40 def 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
55 def process(template, filename, output, copy_codefragment):
57 if DEBUG:
58 progress = EasyDialogs.ProgressBar("Processing %s..."%os.path.split(filename)[1], 120)
59 progress.label("Compiling...")
60 else:
61 progress = None
63 # Read the source and compile it
64 # (there's no point overwriting the destination if it has a syntax error)
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`
74 # Set the destination file name
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'
83 if output:
84 destname = output
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)
94 def 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)'
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)
110 def 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)
116 # Copy data (not resources, yet) from the template
117 if DEBUG:
118 progress.label("Copy data fork...")
119 progress.set(10)
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
132 # Open the output resource fork
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)
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)
159 # Check which resource-types we should not copy from the template
160 skiptypes = []
161 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)
168 # Copy the resources from the template
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"
178 # Make sure we're manipulating the output resource file now
180 Res.UseResFile(output)
182 if code:
183 # Delete any existing 'PYC ' resource named __main__
185 try:
186 res = Res.Get1NamedResource(RESTYPE, RESNAME)
187 res.RemoveResource()
188 except Res.Error:
189 pass
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)
196 data = marshal.dumps(code)
197 del code
198 data = (MAGIC + '\0\0\0\0') + data
200 # Create the resource and write it
202 id = 0
203 while id < 128:
204 id = Res.Unique1ID(RESTYPE)
205 res = Res.Resource(data)
206 res.AddResource(RESTYPE, id, RESNAME)
207 res.WriteResource()
208 res.ReleaseResource()
210 # Close the output file
212 Res.CloseResFile(output)
214 # Now set the creator, type and bundle bit of the destination
215 dest_finfo = dest_fss.GetFInfo()
216 dest_finfo.Creator = ownertype
217 dest_finfo.Type = 'APPL'
218 dest_finfo.Flags = dest_finfo.Flags | MACFS.kHasBundle
219 dest_finfo.Flags = dest_finfo.Flags & ~MACFS.kHasBeenInited
220 dest_fss.SetFInfo(dest_finfo)
222 macostools.touched(dest_fss)
223 if DEBUG:
224 progress.label("Done.")
227 # Copy resources between two resource file descriptors.
228 # skip a resource named '__main__' or (if skipowner is set) 'Owner resource'.
229 # Also skip resources with a type listed in skiptypes.
231 def copyres(input, output, skiptypes, skipowner, progress=None):
232 ctor = None
233 alltypes = []
234 Res.UseResFile(input)
235 ntypes = Res.Count1Types()
236 progress_type_inc = 50/ntypes
237 for itype in range(1, 1+ntypes):
238 type = Res.Get1IndType(itype)
239 if type in skiptypes:
240 continue
241 alltypes.append(type)
242 nresources = Res.Count1Resources(type)
243 progress_cur_inc = progress_type_inc/nresources
244 for ires in range(1, 1+nresources):
245 res = Res.Get1IndResource(type, ires)
246 id, type, name = res.GetResInfo()
247 lcname = string.lower(name)
248 ## if (type, lcname) == (RESTYPE, RESNAME):
249 ## continue # Don't copy __main__ from template
250 # XXXX should look for id=0
251 if lcname == OWNERNAME:
252 if skipowner:
253 continue # Skip this one
254 else:
255 ctor = type
256 size = res.size
257 attrs = res.GetResAttrs()
258 if DEBUG and progress:
259 progress.label("Copy %s %d %s"%(type, id, name))
260 progress.inc(progress_cur_inc)
261 res.LoadResource()
262 res.DetachResource()
263 Res.UseResFile(output)
264 try:
265 res2 = Res.Get1Resource(type, id)
266 except MacOS.Error:
267 res2 = None
268 if res2:
269 if DEBUG and progress:
270 progress.label("Overwrite %s %d %s"%(type, id, name))
271 res2.RemoveResource()
272 res.AddResource(type, id, name)
273 res.WriteResource()
274 attrs = attrs | res.GetResAttrs()
275 res.SetResAttrs(attrs)
276 Res.UseResFile(input)
277 return alltypes, ctor