ยปCore Development>Code coverage>Lib/plat-mac/buildtools.py

Python code coverage for Lib/plat-mac/buildtools.py

#countcontent
1n/a"""tools for BuildApplet and BuildApplication"""
2n/a
3n/aimport warnings
4n/awarnings.warnpy3k("the buildtools module is deprecated and is removed in 3.0",
5n/a stacklevel=2)
6n/a
7n/aimport sys
8n/aimport os
9n/aimport string
10n/aimport imp
11n/aimport marshal
12n/afrom Carbon import Res
13n/aimport Carbon.Files
14n/aimport Carbon.File
15n/aimport MacOS
16n/aimport macostools
17n/aimport macresource
18n/atry:
19n/a import EasyDialogs
20n/aexcept ImportError:
21n/a EasyDialogs = None
22n/aimport shutil
23n/a
24n/a
25n/aBuildError = "BuildError"
26n/a
27n/a# .pyc file (and 'PYC ' resource magic number)
28n/aMAGIC = imp.get_magic()
29n/a
30n/a# Template file (searched on sys.path)
31n/aTEMPLATE = "PythonInterpreter"
32n/a
33n/a# Specification of our resource
34n/aRESTYPE = 'PYC '
35n/aRESNAME = '__main__'
36n/a
37n/a# A resource with this name sets the "owner" (creator) of the destination
38n/a# It should also have ID=0. Either of these alone is not enough.
39n/aOWNERNAME = "owner resource"
40n/a
41n/a# Default applet creator code
42n/aDEFAULT_APPLET_CREATOR="Pyta"
43n/a
44n/a# OpenResFile mode parameters
45n/aREAD = 1
46n/aWRITE = 2
47n/a
48n/a# Parameter for FSOpenResourceFile
49n/aRESOURCE_FORK_NAME=Carbon.File.FSGetResourceForkName()
50n/a
51n/adef findtemplate(template=None):
52n/a """Locate the applet template along sys.path"""
53n/a if MacOS.runtimemodel == 'macho':
54n/a return None
55n/a if not template:
56n/a template=TEMPLATE
57n/a for p in sys.path:
58n/a file = os.path.join(p, template)
59n/a try:
60n/a file, d1, d2 = Carbon.File.FSResolveAliasFile(file, 1)
61n/a break
62n/a except (Carbon.File.Error, ValueError):
63n/a continue
64n/a else:
65n/a raise BuildError, "Template %r not found on sys.path" % (template,)
66n/a file = file.as_pathname()
67n/a return file
68n/a
69n/adef process(template, filename, destname, copy_codefragment=0,
70n/a rsrcname=None, others=[], raw=0, progress="default", destroot=""):
71n/a
72n/a if progress == "default":
73n/a if EasyDialogs is None:
74n/a print "Compiling %s"%(os.path.split(filename)[1],)
75n/a process = None
76n/a else:
77n/a progress = EasyDialogs.ProgressBar("Processing %s..."%os.path.split(filename)[1], 120)
78n/a progress.label("Compiling...")
79n/a progress.inc(0)
80n/a # check for the script name being longer than 32 chars. This may trigger a bug
81n/a # on OSX that can destroy your sourcefile.
82n/a if '#' in os.path.split(filename)[1]:
83n/a raise BuildError, "BuildApplet could destroy your sourcefile on OSX, please rename: %s" % filename
84n/a # Read the source and compile it
85n/a # (there's no point overwriting the destination if it has a syntax error)
86n/a
87n/a fp = open(filename, 'rU')
88n/a text = fp.read()
89n/a fp.close()
90n/a try:
91n/a code = compile(text + '\n', filename, "exec")
92n/a except SyntaxError, arg:
93n/a raise BuildError, "Syntax error in script %s: %s" % (filename, arg)
94n/a except EOFError:
95n/a raise BuildError, "End-of-file in script %s" % (filename,)
96n/a
97n/a # Set the destination file name. Note that basename
98n/a # does contain the whole filepath, only a .py is stripped.
99n/a
100n/a if string.lower(filename[-3:]) == ".py":
101n/a basename = filename[:-3]
102n/a if MacOS.runtimemodel != 'macho' and not destname:
103n/a destname = basename
104n/a else:
105n/a basename = filename
106n/a
107n/a if not destname:
108n/a if MacOS.runtimemodel == 'macho':
109n/a destname = basename + '.app'
110n/a else:
111n/a destname = basename + '.applet'
112n/a if not rsrcname:
113n/a rsrcname = basename + '.rsrc'
114n/a
115n/a # Try removing the output file. This fails in MachO, but it should
116n/a # do any harm.
117n/a try:
118n/a os.remove(destname)
119n/a except os.error:
120n/a pass
121n/a process_common(template, progress, code, rsrcname, destname, 0,
122n/a copy_codefragment, raw, others, filename, destroot)
123n/a
124n/a
125n/adef update(template, filename, output):
126n/a if MacOS.runtimemodel == 'macho':
127n/a raise BuildError, "No updating yet for MachO applets"
128n/a if progress:
129n/a if EasyDialogs is None:
130n/a print "Updating %s"%(os.path.split(filename)[1],)
131n/a progress = None
132n/a else:
133n/a progress = EasyDialogs.ProgressBar("Updating %s..."%os.path.split(filename)[1], 120)
134n/a else:
135n/a progress = None
136n/a if not output:
137n/a output = filename + ' (updated)'
138n/a
139n/a # Try removing the output file
140n/a try:
141n/a os.remove(output)
142n/a except os.error:
143n/a pass
144n/a process_common(template, progress, None, filename, output, 1, 1)
145n/a
146n/a
147n/adef process_common(template, progress, code, rsrcname, destname, is_update,
148n/a copy_codefragment, raw=0, others=[], filename=None, destroot=""):
149n/a if MacOS.runtimemodel == 'macho':
150n/a return process_common_macho(template, progress, code, rsrcname, destname,
151n/a is_update, raw, others, filename, destroot)
152n/a if others:
153n/a raise BuildError, "Extra files only allowed for MachoPython applets"
154n/a # Create FSSpecs for the various files
155n/a template_fsr, d1, d2 = Carbon.File.FSResolveAliasFile(template, 1)
156n/a template = template_fsr.as_pathname()
157n/a
158n/a # Copy data (not resources, yet) from the template
159n/a if progress:
160n/a progress.label("Copy data fork...")
161n/a progress.set(10)
162n/a
163n/a if copy_codefragment:
164n/a tmpl = open(template, "rb")
165n/a dest = open(destname, "wb")
166n/a data = tmpl.read()
167n/a if data:
168n/a dest.write(data)
169n/a dest.close()
170n/a tmpl.close()
171n/a del dest
172n/a del tmpl
173n/a
174n/a # Open the output resource fork
175n/a
176n/a if progress:
177n/a progress.label("Copy resources...")
178n/a progress.set(20)
179n/a try:
180n/a output = Res.FSOpenResourceFile(destname, RESOURCE_FORK_NAME, WRITE)
181n/a except MacOS.Error:
182n/a destdir, destfile = os.path.split(destname)
183n/a Res.FSCreateResourceFile(destdir, unicode(destfile), RESOURCE_FORK_NAME)
184n/a output = Res.FSOpenResourceFile(destname, RESOURCE_FORK_NAME, WRITE)
185n/a
186n/a # Copy the resources from the target specific resource template, if any
187n/a typesfound, ownertype = [], None
188n/a try:
189n/a input = Res.FSOpenResourceFile(rsrcname, RESOURCE_FORK_NAME, READ)
190n/a except (MacOS.Error, ValueError):
191n/a pass
192n/a if progress:
193n/a progress.inc(50)
194n/a else:
195n/a if is_update:
196n/a skip_oldfile = ['cfrg']
197n/a else:
198n/a skip_oldfile = []
199n/a typesfound, ownertype = copyres(input, output, skip_oldfile, 0, progress)
200n/a Res.CloseResFile(input)
201n/a
202n/a # Check which resource-types we should not copy from the template
203n/a skiptypes = []
204n/a if 'vers' in typesfound: skiptypes.append('vers')
205n/a if 'SIZE' in typesfound: skiptypes.append('SIZE')
206n/a if 'BNDL' in typesfound: skiptypes = skiptypes + ['BNDL', 'FREF', 'icl4',
207n/a 'icl8', 'ics4', 'ics8', 'ICN#', 'ics#']
208n/a if not copy_codefragment:
209n/a skiptypes.append('cfrg')
210n/a## skipowner = (ownertype != None)
211n/a
212n/a # Copy the resources from the template
213n/a
214n/a input = Res.FSOpenResourceFile(template, RESOURCE_FORK_NAME, READ)
215n/a dummy, tmplowner = copyres(input, output, skiptypes, 1, progress)
216n/a
217n/a Res.CloseResFile(input)
218n/a## if ownertype is None:
219n/a## raise BuildError, "No owner resource found in either resource file or template"
220n/a # Make sure we're manipulating the output resource file now
221n/a
222n/a Res.UseResFile(output)
223n/a
224n/a if ownertype is None:
225n/a # No owner resource in the template. We have skipped the
226n/a # Python owner resource, so we have to add our own. The relevant
227n/a # bundle stuff is already included in the interpret/applet template.
228n/a newres = Res.Resource('\0')
229n/a newres.AddResource(DEFAULT_APPLET_CREATOR, 0, "Owner resource")
230n/a ownertype = DEFAULT_APPLET_CREATOR
231n/a
232n/a if code:
233n/a # Delete any existing 'PYC ' resource named __main__
234n/a
235n/a try:
236n/a res = Res.Get1NamedResource(RESTYPE, RESNAME)
237n/a res.RemoveResource()
238n/a except Res.Error:
239n/a pass
240n/a
241n/a # Create the raw data for the resource from the code object
242n/a if progress:
243n/a progress.label("Write PYC resource...")
244n/a progress.set(120)
245n/a
246n/a data = marshal.dumps(code)
247n/a del code
248n/a data = (MAGIC + '\0\0\0\0') + data
249n/a
250n/a # Create the resource and write it
251n/a
252n/a id = 0
253n/a while id < 128:
254n/a id = Res.Unique1ID(RESTYPE)
255n/a res = Res.Resource(data)
256n/a res.AddResource(RESTYPE, id, RESNAME)
257n/a attrs = res.GetResAttrs()
258n/a attrs = attrs | 0x04 # set preload
259n/a res.SetResAttrs(attrs)
260n/a res.WriteResource()
261n/a res.ReleaseResource()
262n/a
263n/a # Close the output file
264n/a
265n/a Res.CloseResFile(output)
266n/a
267n/a # Now set the creator, type and bundle bit of the destination.
268n/a # Done with FSSpec's, FSRef FInfo isn't good enough yet (2.3a1+)
269n/a dest_fss = Carbon.File.FSSpec(destname)
270n/a dest_finfo = dest_fss.FSpGetFInfo()
271n/a dest_finfo.Creator = ownertype
272n/a dest_finfo.Type = 'APPL'
273n/a dest_finfo.Flags = dest_finfo.Flags | Carbon.Files.kHasBundle | Carbon.Files.kIsShared
274n/a dest_finfo.Flags = dest_finfo.Flags & ~Carbon.Files.kHasBeenInited
275n/a dest_fss.FSpSetFInfo(dest_finfo)
276n/a
277n/a macostools.touched(destname)
278n/a if progress:
279n/a progress.label("Done.")
280n/a progress.inc(0)
281n/a
282n/adef process_common_macho(template, progress, code, rsrcname, destname, is_update,
283n/a raw=0, others=[], filename=None, destroot=""):
284n/a # Check that we have a filename
285n/a if filename is None:
286n/a raise BuildError, "Need source filename on MacOSX"
287n/a # First make sure the name ends in ".app"
288n/a if destname[-4:] != '.app':
289n/a destname = destname + '.app'
290n/a # Now deduce the short name
291n/a destdir, shortname = os.path.split(destname)
292n/a if shortname[-4:] == '.app':
293n/a # Strip the .app suffix
294n/a shortname = shortname[:-4]
295n/a # And deduce the .plist and .icns names
296n/a plistname = None
297n/a icnsname = None
298n/a if rsrcname and rsrcname[-5:] == '.rsrc':
299n/a tmp = rsrcname[:-5]
300n/a plistname = tmp + '.plist'
301n/a if os.path.exists(plistname):
302n/a icnsname = tmp + '.icns'
303n/a if not os.path.exists(icnsname):
304n/a icnsname = None
305n/a else:
306n/a plistname = None
307n/a if not icnsname:
308n/a dft_icnsname = os.path.join(sys.prefix, 'Resources/Python.app/Contents/Resources/PythonApplet.icns')
309n/a if os.path.exists(dft_icnsname):
310n/a icnsname = dft_icnsname
311n/a if not os.path.exists(rsrcname):
312n/a rsrcname = None
313n/a if progress:
314n/a progress.label('Creating bundle...')
315n/a import bundlebuilder
316n/a builder = bundlebuilder.AppBuilder(verbosity=0)
317n/a builder.mainprogram = filename
318n/a builder.builddir = destdir
319n/a builder.name = shortname
320n/a builder.destroot = destroot
321n/a if rsrcname:
322n/a realrsrcname = macresource.resource_pathname(rsrcname)
323n/a builder.files.append((realrsrcname,
324n/a os.path.join('Contents/Resources', os.path.basename(rsrcname))))
325n/a for o in others:
326n/a if type(o) == str:
327n/a builder.resources.append(o)
328n/a else:
329n/a builder.files.append(o)
330n/a if plistname:
331n/a import plistlib
332n/a builder.plist = plistlib.Plist.fromFile(plistname)
333n/a if icnsname:
334n/a builder.iconfile = icnsname
335n/a if not raw:
336n/a builder.argv_emulation = 1
337n/a builder.setup()
338n/a builder.build()
339n/a if progress:
340n/a progress.label('Done.')
341n/a progress.inc(0)
342n/a
343n/a## macostools.touched(dest_fss)
344n/a
345n/a# Copy resources between two resource file descriptors.
346n/a# skip a resource named '__main__' or (if skipowner is set) with ID zero.
347n/a# Also skip resources with a type listed in skiptypes.
348n/a#
349n/adef copyres(input, output, skiptypes, skipowner, progress=None):
350n/a ctor = None
351n/a alltypes = []
352n/a Res.UseResFile(input)
353n/a ntypes = Res.Count1Types()
354n/a progress_type_inc = 50/ntypes
355n/a for itype in range(1, 1+ntypes):
356n/a type = Res.Get1IndType(itype)
357n/a if type in skiptypes:
358n/a continue
359n/a alltypes.append(type)
360n/a nresources = Res.Count1Resources(type)
361n/a progress_cur_inc = progress_type_inc/nresources
362n/a for ires in range(1, 1+nresources):
363n/a res = Res.Get1IndResource(type, ires)
364n/a id, type, name = res.GetResInfo()
365n/a lcname = string.lower(name)
366n/a
367n/a if lcname == OWNERNAME and id == 0:
368n/a if skipowner:
369n/a continue # Skip this one
370n/a else:
371n/a ctor = type
372n/a size = res.size
373n/a attrs = res.GetResAttrs()
374n/a if progress:
375n/a progress.label("Copy %s %d %s"%(type, id, name))
376n/a progress.inc(progress_cur_inc)
377n/a res.LoadResource()
378n/a res.DetachResource()
379n/a Res.UseResFile(output)
380n/a try:
381n/a res2 = Res.Get1Resource(type, id)
382n/a except MacOS.Error:
383n/a res2 = None
384n/a if res2:
385n/a if progress:
386n/a progress.label("Overwrite %s %d %s"%(type, id, name))
387n/a progress.inc(0)
388n/a res2.RemoveResource()
389n/a res.AddResource(type, id, name)
390n/a res.WriteResource()
391n/a attrs = attrs | res.GetResAttrs()
392n/a res.SetResAttrs(attrs)
393n/a Res.UseResFile(input)
394n/a return alltypes, ctor
395n/a
396n/adef copyapptree(srctree, dsttree, exceptlist=[], progress=None):
397n/a names = []
398n/a if os.path.exists(dsttree):
399n/a shutil.rmtree(dsttree)
400n/a os.mkdir(dsttree)
401n/a todo = os.listdir(srctree)
402n/a while todo:
403n/a this, todo = todo[0], todo[1:]
404n/a if this in exceptlist:
405n/a continue
406n/a thispath = os.path.join(srctree, this)
407n/a if os.path.isdir(thispath):
408n/a thiscontent = os.listdir(thispath)
409n/a for t in thiscontent:
410n/a todo.append(os.path.join(this, t))
411n/a names.append(this)
412n/a for this in names:
413n/a srcpath = os.path.join(srctree, this)
414n/a dstpath = os.path.join(dsttree, this)
415n/a if os.path.isdir(srcpath):
416n/a os.mkdir(dstpath)
417n/a elif os.path.islink(srcpath):
418n/a endpoint = os.readlink(srcpath)
419n/a os.symlink(endpoint, dstpath)
420n/a else:
421n/a if progress:
422n/a progress.label('Copy '+this)
423n/a progress.inc(0)
424n/a shutil.copy2(srcpath, dstpath)
425n/a
426n/adef writepycfile(codeobject, cfile):
427n/a import marshal
428n/a fc = open(cfile, 'wb')
429n/a fc.write('\0\0\0\0') # MAGIC placeholder, written later
430n/a fc.write('\0\0\0\0') # Timestap placeholder, not needed
431n/a marshal.dump(codeobject, fc)
432n/a fc.flush()
433n/a fc.seek(0, 0)
434n/a fc.write(MAGIC)
435n/a fc.close()