Poodletooth-iLand/panda/direct/pyinst/Builder.py

505 lines
19 KiB
Python
Raw Normal View History

2015-03-03 16:10:12 -06:00
import string
import pprint
import sys
import os
import ConfigParser
import pprint
import shutil
import tempfile
import ltoc
import tocfilter
import resource
import archive
import archivebuilder
import carchive
logfile = None
autopath = []
built = {}
copyFile = None
class Target:
def __init__(self, cfg, sectnm, cnvrts):
self.children = []
self._dependencies = ltoc.lTOC() # the stuff an outer package will need to use me
self.cfg = cfg
self.__name__ = 'joe'
for optnm in cfg.options(sectnm):
cnvrt = cnvrts.get(optnm, 'getstringlist')
if cnvrt:
f = getattr(self, cnvrt, None)
if f:
self.__dict__[optnm] = f(cfg.get(sectnm, optnm))
if not hasattr(self, 'name'):
self.name = self.__name__
print "Initializing", self.__name__
self.pathprefix = autopath + self.pathprefix
self.pathprefix.append(os.path.join(pyinsthome, 'support'))
for z in self.zlib:
if z in self.cfg.sections():
self.children.append(z)
else:
raise ValueError, "%s - zlib '%s' does not refer to a sections" \
% (self.name, z)
for i in range(len(self.misc)):
x = self.misc[i]
if x in self.cfg.sections():
if self.cfg.get(x, "type") == 'PYZ':
self.zlib.append(x)
self.misc[i] = None
self.children.append(x)
self.misc = filter(None, self.misc)
self.edit()
self.toc = ltoc.lTOC()
for thingie in self.excludes:
try:
fltr = tocfilter.makefilter(thingie, self.pathprefix)
except ValueError:
print "Warning: '%s' not found - no filter created" % thingie
else:
self.toc.addFilter(fltr)
if self.exstdlib:
self.toc.addFilter(tocfilter.StdLibFilter())
if self.extypes:
self.toc.addFilter(tocfilter.ExtFilter(self.extypes))
if self.expatterns:
self.toc.addFilter(tocfilter.PatternFilter(self.expatterns))
##------utilities------##
def dump(self):
logfile.write("---- %s: %s -----\n" % (self.__class__.__name__, self.name))
pprint.pprint(self.__dict__, logfile)
def getstringlist(self, opt):
tmp = string.split(opt, ',')
return filter(None, map(string.strip, tmp))
def getstring(self, opt):
return opt
def getbool(self, opt):
if opt in ('0','f','F','n','N'):
return 0
return 1
##-----framework-----##
def build(self):
print "Gathering components of %s" % self.name
self.gather()
logfile.write("Final Table of Contents for %s:\n" % self.name)
pprint.pprint(self.toc.toList(), logfile)
print "Creating %s" % self.name
self.assemble()
##-----overrideables-----##
def edit(self):
pass
def gather(self):
pass
def assemble(self):
pass
class PYZTarget(Target):
def __init__(self, cfg, sectnm, cnvrts):
Target.__init__(self, cfg, sectnm, cnvrts)
# to use a PYZTarget, you'll need imputil and archive
archivebuilder.GetCompiled([os.path.join(pyinsthome, 'imputil.py')])
print "pyinsthome:", pyinsthome
imputil = resource.makeresource('imputil.py', [pyinsthome])
self._dependencies.append(imputil)
archivebuilder.GetCompiled([os.path.join(pyinsthome, 'archive_rt.py')])
archmodule = resource.makeresource('archive_rt.py', [pyinsthome])
self._dependencies.merge(archmodule.dependencies())
self._dependencies.append(archmodule)
self.toc.addFilter(archmodule)
self.toc.addFilter(imputil)
for mod in archmodule.modules:
self.toc.addFilter(mod)
def edit(self):
if self.extypes:
print "PYZ target %s ignoring extypes = %s" % (self.__name__, self.extypes)
def gather(self):
for script in self.dependencies:
rsrc = resource.makeresource(script, self.pathprefix)
if not isinstance(rsrc, resource.scriptresource):
print "Bug alert - Made %s from %s!" % (rsrc, script)
self.toc.merge(rsrc.modules)
logfile.write("lTOC after expanding 'depends':\n")
pprint.pprint(self.toc.toList(), logfile)
for thingie in self.includes + self.directories + self.packages:
rsrc = resource.makeresource(thingie, self.pathprefix)
## if not isinstance(rsrc, resource.pythonresource):
## print "PYZ target %s ignoring include %s" % (self.name, thingie)
## else:
self.toc.merge(rsrc.contents())
logfile.write("lTOC after includes, dir, pkgs:\n")
pprint.pprint(self.toc.toList(), logfile)
self.toc.addFilter(tocfilter.ExtFilter(['.py', '.pyc', '.pyo'], 1))
logfile.write("Applying the following filters:\n")
pprint.pprint(self.toc.filters, logfile)
self.toc.filter()
def assemble(self):
contents = self.toc.toList()
if contents:
lib = archive.ZlibArchive()
lib.build(self.name, archivebuilder.GetCompiled(self.toc.toList()))
class CollectTarget(Target):
def __init__(self, cfg, sectnm, cnvrts):
Target.__init__(self, cfg, sectnm, cnvrts)
_rsrcdict = {'COLLECT': resource.dirresource, 'PYZ': resource.zlibresource, 'CARCHIVE': resource.archiveresource}
def gather(self):
if self.support:
# the bare minimum
self.toc.merge([resource.makeresource('python20.dll')])
self.toc.merge([resource.makeresource('exceptions.pyc').asBinary()])
# zlib, bindepends, misc, trees, destdir
for i in range(len(self.zlib)):
# z refers to the section name
z = self.zlib[i]
nm = self.cfg.get(z, 'name')
try:
self.toc.merge([resource.makeresource(nm, ['.'])])
except ValueError:
# zlibs aren't written if they turn out to be empty
self.zlib[i] = None
self.zlib = filter(None, self.zlib)
if self.zlib:
target = built.get(self.zlib[0], None)
if target:
self.toc.merge(target._dependencies)
for script in self.bindepends:
rsrc = resource.makeresource(script, self.pathprefix)
self.toc.merge(rsrc.binaries)
logfile.write('ltoc after bindepends:\n')
pprint.pprint(self.toc.toList(), logfile)
for thingie in self.misc:
if thingie in self.cfg.sections():
name = self.cfg.get(thingie, "name")
typ = self.cfg.get(thingie, "type")
klass = self._rsrcdict.get(typ, resource.dataresource)
rsrc = apply(klass, (name, name))
#now make sure we have the stuff the resource requires
target = built.get(thingie, None)
if target:
self.toc.merge(target._dependencies)
else:
rsrc = resource.makeresource(thingie, self.pathprefix)
self.toc.merge(rsrc.contents())
logfile.write('ltoc after misc:\n')
pprint.pprint(self.toc.toList(), logfile)
for script in self.script:
if string.find(script, '.') == -1:
script = script + '.py'
rsrc = resource.makeresource(script, self.pathprefix)
if rsrc.typ == 'm':
rsrc.typ = 's'
self.toc.merge([rsrc])
logfile.write('ltoc after scripts:\n')
pprint.pprint(self.toc.toList(), logfile)
for tree in self.trees:
try:
rsrc = resource.treeresource('.', tree)
except ValueError:
print "tree %s not found" % tree
else:
self.toc.merge(rsrc.contents())
logfile.write('ltoc after trees:\n')
pprint.pprint(self.toc.toList(), logfile)
self.toc.addFilter(tocfilter.TypeFilter(['d']))
logfile.write("Applying the following filters:\n")
pprint.pprint(self.toc.filters, logfile)
self.toc.filter()
#don't dupe stuff in a zlib that's part of this target
if self.zlib:
ztoc = ltoc.lTOC()
for zlibnm in self.zlib:
target = built.get(zlibnm, None)
if target:
ztoc.merge(target.toc)
for i in range(len(self.toc)-1, -1, -1):
rsrc = self.toc[i]
if isinstance(rsrc, resource.moduleresource) and rsrc in ztoc:
del self.toc[i]
def assemble(self):
if os.path.exists(self.name):
if os.path.isdir(self.name):
for fnm in os.listdir(self.name):
try:
os.remove(os.path.join(self.name, fnm))
except:
print "Could not delete file %s" % os.path.join(self.name, fnm)
else:
os.makedirs(self.name)
mysite = []
for nm, path, typ in self.toc.toList():
shutil.copy2(path, self.name)
if typ == 'z':
mysite.append('imputil.FuncImporter(archive.ZlibArchive("%s", 0).get_code).install()' % nm)
if mysite:
mysite.insert(0, 'import archive, imputil')
open(os.path.join(self.name, 'site.py'),'w').write(string.join(mysite, '\n'))
class ArchiveTarget(CollectTarget):
usefullname = 1
def __init__(self, cfg, sectnm, cnvrts):
CollectTarget.__init__(self, cfg, sectnm, cnvrts)
archivebuilder.GetCompiled([os.path.join(pyinsthome, 'carchive_rt.py')])
carchmodule = resource.makeresource('carchive_rt.py', [pyinsthome])
self._dependencies.merge(carchmodule.dependencies())
self._dependencies.append(carchmodule)
def edit(self):
if self.destdir:
print "Warning 'destdir = %s' ignored for %s" % (self.destdir, self.name)
def gather(self):
CollectTarget.gather(self)
_cdict = {'s':2,'m':1,'b':1,'x':1,'a':0,'z':0, 'p':1}
def assemble(self, pkgnm=None):
if pkgnm is None:
pkgnm = self.name
arch = carchive.CArchive()
toc = []
pytoc = []
for nm, path, typ in self.toc.toList():
compress = self._cdict[typ]
if typ == 'b' or (self.usefullname and typ in 'ms'):
nm = os.path.basename(path)
if typ == 'm':
pytoc.append((nm, path, compress, typ))
else:
toc.append((nm, path, compress, typ))
toc = toc + archivebuilder.GetCompiled(pytoc)
arch.build(pkgnm, toc)
return arch
class FullExeTarget(ArchiveTarget):
usefullname = 0
def __init__(self, cfg, sectnm, cnvrts):
ArchiveTarget.__init__(self, cfg, sectnm, cnvrts)
def gather(self):
for script in self.script:
#print "FullExeTarget.gather: script is", repr(script)
rsrc = resource.makeresource(script, self.pathprefix)
rsrc = resource.scriptresource(rsrc.name, rsrc.path)
#print " resource is", repr(rsrc)
self.toc.merge(rsrc.binaries)
ArchiveTarget.gather(self)
if not self.zlib:
self.toc.merge(rsrc.modules)
self._dependencies = ltoc.lTOC()
_cdict = {'s':2,'m':0,'b':1,'x':0,'a':0,'z':0}
_edict = { (1, 1):'Runw_d.exe', (1, 0):'Runw.exe', (0, 1):'Run_d.exe', (0, 0):'Run.exe'}
def assemble(self):
pkgname = tempfile.mktemp()
arch = ArchiveTarget.assemble(self, pkgname)
exe = self._edict[(self.userunw, self.debug)]
exe = os.path.normpath(os.path.join(pyinsthome, 'support', exe))
## copyFile([exe, pkgname], self.name)
## os.remove(pkgname)
# Thomas Heller's icon code
# my version
if self.icon:
myexe = tempfile.mktemp()
copyFile (exe, myexe)
try:
from icon import CopyIcons
CopyIcons(myexe, self.icon)
except ImportError:
print "win32api is required for updating icons"
print "You should have win32api.pyd and PyWinTypes20.dll"
print "in the installation directory."
print "Please copy them to Python's DLLS subdirectory"
print "(or install Mark Hammond's Win32 extensions)."
## iconfile = None
## for name in self.cfg.sections():
## if self.cfg.get (name, "type") == "STANDALONE":
## try:
## iconfile = self.cfg.get (name, "iconfile")
## except:
## pass
## if iconfile:
## from icon import CopyIcons
## CopyIcons (myexe, iconfile)
copyFile ([myexe, pkgname], self.name)
os.remove(myexe)
else:
copyFile([exe, pkgname], self.name)
#os.remove(pkgname)
class ExeTarget(FullExeTarget):
def __init__(self, cfg, sectnm, cnvrts):
FullExeTarget.__init__(self, cfg, sectnm, cnvrts)
def edit(self):
if not self.script:
raise ValueError, "EXE target %s requires 'script= <script>'" % self.__name__
def gather(self):
FullExeTarget.gather(self)
for i in range(len(self.toc)-1, -1, -1):
rsrc = self.toc[i]
if rsrc.typ == 'b':
self._dependencies.append(rsrc)
del self.toc[i]
installpreamble = """\
import sys, os
import installutils
import carchive_rt
idir = installutils.getinstalldir()
me = sys.argv[0]
if me[:-4] != '.exe':
me = me + '.exe'
this = carchive_rt.CArchive(sys.argv[0])
here = sys.path[0]
"""
mvfile = "installutils.copyFile(os.path.join(here, '%s'), os.path.join(idir, '%s'))\n"
extractfile = "open(os.path.join(idir, '%s'), 'wb').write(this.extract('%s')[1])\n"
sitepreamble = """\
import archive_rt
import imputil
import sys
"""
importzlib = "imputil.FuncImporter(archive_rt.ZlibArchive(sys.path[0]+'/%s').get_code).install()\n"
class InstallTarget(FullExeTarget):
def __init__(self, cfg, sectnm, cnvrts):
FullExeTarget.__init__(self, cfg, sectnm, cnvrts)
def edit(self):
if not self.script:
open('gen_install.py', 'w').write(installpreamble)
self.script = ['gen_install.py']
def gather(self):
FullExeTarget.gather(self)
if self.script[0] == 'gen_install.py':
f = open(self.script[0], 'a')
for rsrc in self.toc:
if isinstance(rsrc, resource.binaryresource):
nm = os.path.basename(rsrc.path)
f.write(mvfile % (nm, nm))
elif isinstance(rsrc, resource.pythonresource):
pass
elif isinstance(rsrc, resource.zlibresource):
pass
else:
f.write(extractfile % (rsrc.name, rsrc.name))
if isinstance(rsrc, resource.archiveresource):
#did it come with an install script?
target = built.get(rsrc.name, None)
if target:
if hasattr(target, "installscript"):
for script in target.installscript:
s = resource.makeresource(script, self.pathprefix)
txt = open(s.path, 'r').read()
f.write(txt)
f.close()
dispatch = {
'PYZ': PYZTarget,
'CARCHIVE': ArchiveTarget,
'COLLECT': CollectTarget,
'STANDALONE': ExeTarget,
'INSTALL': InstallTarget,
'FULLEXE': FullExeTarget,
}
def makeTarget(cfg, section):
return dispatch[cfg.get(section, 'type')](cfg, section, optcnvrts)
optdefaults = { 'type':'PYZ',
'script':'', # INSTALL (opt) & STANDALONE (required)
'zlib':'', # INSTALL, STANDALONE, COLLECT
'bindepends':'', # INSTALL, COLLECT
'misc':'', # INSTALL. COLLECT
'includetk': '0', # INSTALL, COLLECT
'userunw': '0', # STANDALONE
'dependencies':'', # PYZ
'directories':'', # PYZ
'excludes':'', # PYZ, INSTALL, COLLECT
'expatterns': '',
'exstdlib': '0',
'extypes': '',
'includes':'', # PYZ
'packages':'', # PYZ
'destdir':'', # COLLECT
'pathprefix': '',
'trees': '',
'debug': '0',
'support': '1', # include python20.dll & exceptons.pyc at a minimum
'icon': '',
}
optcnvrts = { 'type':'',
'name': 'getstring',
'exstdlib': 'getbool',
'console': 'getbool',
'analyze': 'getbool',
'debug': 'getbool',
'includetk': 'getbool',
'userunw': 'getbool',
'destdir': 'getstring',
'support': 'getbool',
'__name__': 'getstring',
'icon': 'getstring',
}
def main(opts, args):
global pyinsthome
global copyFile
pyinsthome = os.path.abspath(os.path.dirname(sys.argv[0]))
# sys.path.insert(0, os.path.join(pyinsthome, 'support'))
import installutils
copyFile = installutils.copyFile
global logfile
logfile = open('Builder.log','w')
targets = []
xref = {}
cfg = ConfigParser.ConfigParser(optdefaults)
for arg in args:
dirnm = os.path.dirname(arg)
if dirnm == '':
dirnm = '.'
autopath.append(os.path.abspath(dirnm))
cfg.read(args)
for section in cfg.sections():
target = makeTarget(cfg, section)
targets.append(target)
xref[section] = target
while targets:
for i in range(len(targets)):
target = targets[i]
for child in target.children:
if xref[child] in targets:
break
else: #no break - ready to build
target.dump()
target.build()
built[target.__name__] = target
built[target.name] = target
targets[i] = None
break
else: #no break - couldn't find anything to build
names = map(lambda x: getattr(x, 'name'), targets)
raise RuntimeError, "circular dependencies in %s" % repr(names)
targets = filter(None, targets)
def run(file):
main ([], file)
if __name__ == '__main__':
import getopt
(opts, args) = getopt.getopt(sys.argv[1:], 'dv')
print "opts:", opts
print "args:", args
main(opts, args)