mirror of
https://github.com/kennethreitz-archive/pyinstaller.git
synced 2026-06-05 23:50:17 +00:00
48031ea8e2
to the binary files being packaged. mtime works well for binary files being compiled each time, but it is prone to be broken for other patterns of usages. For instance, switching between different versions of a third-party library (eg: wxPython) was totally broken without this fix, as only the newer binary file was being packaged, not the current one. git-svn-id: http://svn.pyinstaller.org/trunk@314 8dd32b29-ccff-0310-8a9a-9233e24343b1
876 lines
32 KiB
Python
Executable File
876 lines
32 KiB
Python
Executable File
#! /usr/bin/env python
|
|
# Build packages using spec files
|
|
# Copyright (C) 2005, Giovanni Bajo
|
|
# Based on previous work under copyright (c) 1999, 2002 McMillan Enterprises, Inc.
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
import sys, os, shutil, mf, archive, iu, carchive, pprint, time, py_compile, bindepend, tempfile, md5
|
|
|
|
STRINGTYPE = type('')
|
|
TUPLETYPE = type((None,))
|
|
|
|
HOMEPATH = os.path.dirname(sys.argv[0])
|
|
SPECPATH = None
|
|
BUILDPATH = None
|
|
WARNFILE = None
|
|
rthooks = {}
|
|
iswin = sys.platform[:3] == 'win'
|
|
cygwin = sys.platform == 'cygwin'
|
|
try:
|
|
config = eval(open(os.path.join(HOMEPATH, 'config.dat'), 'r').read())
|
|
except IOError:
|
|
print "You must run Configure.py before building!"
|
|
sys.exit(1)
|
|
|
|
if config['pythonVersion'] != sys.version:
|
|
print "The current version of Python is not the same with which PyInstaller was configured."
|
|
print "Please re-run Configure.py with this version."
|
|
sys.exit(1)
|
|
|
|
if config['hasRsrcUpdate']:
|
|
import icon, versionInfo
|
|
|
|
def setupUPXFlags():
|
|
f = os.environ.get("UPX", "")
|
|
is24 = hasattr(sys, "version_info") and sys.version_info[:2] >= (2,4)
|
|
if iswin and is24:
|
|
# Binaries built with Visual Studio 7.1 require --strip-loadconf
|
|
# or they won't compress. Configure.py makes sure that UPX is new
|
|
# enough to support --strip-loadconf.
|
|
f = "--strip-loadconf " + f
|
|
f = "--best " + f
|
|
os.environ["UPX"] = f
|
|
|
|
if config['hasUPX']:
|
|
setupUPXFlags()
|
|
|
|
def build(spec):
|
|
global SPECPATH, BUILDPATH, WARNFILE, rthooks
|
|
rthooks = eval(open(os.path.join(HOMEPATH, 'rthooks.dat'), 'r').read())
|
|
SPECPATH, specnm = os.path.split(spec)
|
|
specnm = os.path.splitext(specnm)[0]
|
|
if SPECPATH == '':
|
|
SPECPATH = os.getcwd()
|
|
WARNFILE = os.path.join(SPECPATH, 'warn%s.txt' % specnm)
|
|
BUILDPATH = os.path.join(SPECPATH, 'build%s' % specnm)
|
|
if '-o' in sys.argv:
|
|
bpath = sys.argv[sys.argv.index('-o')+1]
|
|
if os.path.isabs(bpath):
|
|
BUILDPATH = bpath
|
|
else:
|
|
BUILDPATH = os.path.join(SPECPATH, bpath)
|
|
if not os.path.exists(BUILDPATH):
|
|
os.mkdir(BUILDPATH)
|
|
exec open(spec, 'r').read()+'\n'
|
|
|
|
def mtime(fnm):
|
|
try:
|
|
return os.stat(fnm)[8]
|
|
except:
|
|
return 0
|
|
|
|
class Target:
|
|
invcnum = 0
|
|
def __init__(self):
|
|
self.invcnum = Target.invcnum
|
|
Target.invcnum = Target.invcnum + 1
|
|
self.out = os.path.join(BUILDPATH, 'out%d.toc' % self.invcnum)
|
|
self.dependencies = TOC()
|
|
def __postinit__(self):
|
|
print "checking %s" % (self.__class__.__name__,)
|
|
if self.check_guts(mtime(self.out)):
|
|
self.assemble()
|
|
|
|
class Analysis(Target):
|
|
def __init__(self, scripts=None, pathex=None, hookspath=None, excludes=None):
|
|
Target.__init__(self)
|
|
self.inputs = scripts
|
|
for script in scripts:
|
|
if not os.path.exists(script):
|
|
raise ValueError, "script '%s' not found" % script
|
|
self.pathex = []
|
|
if pathex:
|
|
for path in pathex:
|
|
self.pathex.append(os.path.abspath(os.path.normpath(path)))
|
|
self.hookspath = hookspath
|
|
self.excludes = excludes
|
|
self.scripts = TOC()
|
|
self.pure = TOC()
|
|
self.binaries = TOC()
|
|
self.__postinit__()
|
|
def check_guts(self, last_build):
|
|
outnm = os.path.basename(self.out)
|
|
if last_build == 0:
|
|
print "building %s because %s non existent" % (self.__class__.__name__, outnm)
|
|
return 1
|
|
for fnm in self.inputs:
|
|
if mtime(fnm) > last_build:
|
|
print "building because %s changed" % fnm
|
|
return 1
|
|
try:
|
|
inputs, pathex, hookspath, excludes, scripts, pure, binaries = eval(open(self.out, 'r').read())
|
|
except:
|
|
print "building because %s disappeared" % outnm
|
|
return 1
|
|
if inputs != self.inputs:
|
|
print "building %s because inputs changed" % outnm
|
|
return 1
|
|
if pathex != self.pathex:
|
|
print "building %s because pathex changed" % outnm
|
|
return 1
|
|
if hookspath != self.hookspath:
|
|
print "building %s because hookspath changed" % outnm
|
|
return 1
|
|
if excludes != self.excludes:
|
|
print "building %s because excludes changed" % outnm
|
|
return 1
|
|
for (nm, fnm, typ) in scripts:
|
|
if mtime(fnm) > last_build:
|
|
print "building because %s changed" % fnm
|
|
return 1
|
|
for (nm, fnm, typ) in pure:
|
|
if mtime(fnm) > last_build:
|
|
print "building because %s changed" % fnm
|
|
return 1
|
|
elif mtime(fnm[:-1]) > last_build:
|
|
print "building because %s changed" % fnm[:-1]
|
|
return 1
|
|
for (nm, fnm, typ) in binaries:
|
|
if mtime(fnm) > last_build:
|
|
print "building because %s changed" % fnm
|
|
return 1
|
|
self.scripts = TOC(scripts)
|
|
self.pure = TOC(pure)
|
|
self.binaries = TOC(binaries)
|
|
return 0
|
|
def assemble(self):
|
|
print "running Analysis", os.path.basename(self.out)
|
|
paths = self.pathex
|
|
for i in range(len(paths)):
|
|
paths[i] = os.path.abspath(os.path.normpath(paths[i]))
|
|
dirs = {}
|
|
pynms = []
|
|
for script in self.inputs:
|
|
if not os.path.exists(script):
|
|
print "Analysis: script %s not found!" % script
|
|
sys.exit(1)
|
|
d, base = os.path.split(script)
|
|
if not d:
|
|
d = os.getcwd()
|
|
d = os.path.abspath(os.path.normpath(d))
|
|
pynm, ext = os.path.splitext(base)
|
|
dirs[d] = 1
|
|
pynms.append(pynm)
|
|
analyzer = mf.ImportTracker(dirs.keys()+paths, self.hookspath, self.excludes)
|
|
#print analyzer.path
|
|
scripts = []
|
|
for i in range(len(self.inputs)):
|
|
script = self.inputs[i]
|
|
print "Analyzing:", script
|
|
analyzer.analyze_script(script)
|
|
scripts.append((pynms[i], script, 'PYSOURCE'))
|
|
pure = []
|
|
binaries = []
|
|
rthooks = []
|
|
for modnm, mod in analyzer.modules.items():
|
|
if mod is not None:
|
|
hooks = findRTHook(modnm) #XXX
|
|
if hooks:
|
|
rthooks.extend(hooks)
|
|
if isinstance(mod, mf.BuiltinModule):
|
|
pass
|
|
else:
|
|
fnm = mod.__file__
|
|
if isinstance(mod, mf.ExtensionModule):
|
|
binaries.append((mod.__name__, fnm, 'EXTENSION'))
|
|
elif modnm == '__main__':
|
|
pass
|
|
else:
|
|
pure.append((modnm, fnm, 'PYMODULE'))
|
|
binaries.extend(bindepend.Dependencies(binaries))
|
|
scripts[1:1] = rthooks
|
|
self.scripts = TOC(scripts)
|
|
self.pure = TOC(pure)
|
|
self.binaries = TOC(binaries)
|
|
try:
|
|
oldstuff = eval(open(self.out, 'r').read())
|
|
except:
|
|
oldstuff = None
|
|
if oldstuff != (self.inputs, self.pathex, self.hookspath, self.excludes, scripts, pure, binaries):
|
|
outf = open(self.out, 'w')
|
|
pprint.pprint(
|
|
(self.inputs, self.pathex, self.hookspath, self.excludes, self.scripts, self.pure, self.binaries),
|
|
outf)
|
|
outf.close()
|
|
wf = open(WARNFILE, 'w')
|
|
for ln in analyzer.getwarnings():
|
|
wf.write(ln+'\n')
|
|
wf.close()
|
|
print "Warnings written to %s" % WARNFILE
|
|
return 1
|
|
print self.out, "no change!"
|
|
return 0
|
|
|
|
def findRTHook(modnm):
|
|
hooklist = rthooks.get(modnm)
|
|
if hooklist:
|
|
rslt = []
|
|
for script in hooklist:
|
|
nm = os.path.basename(script)
|
|
nm = os.path.splitext(nm)[0]
|
|
if os.path.isabs(script):
|
|
path = script
|
|
else:
|
|
path = os.path.join(HOMEPATH, script)
|
|
rslt.append((nm, path, 'PYSOURCE'))
|
|
return rslt
|
|
return None
|
|
|
|
class PYZ(Target):
|
|
typ = 'PYZ'
|
|
def __init__(self, toc, name=None, level=9):
|
|
Target.__init__(self)
|
|
self.toc = toc
|
|
self.name = name
|
|
if name is None:
|
|
self.name = self.out[:-3] + 'pyz'
|
|
if config['useZLIB']:
|
|
self.level = level
|
|
else:
|
|
self.level = 0
|
|
self.dependencies = config['PYZ_dependencies']
|
|
self.__postinit__()
|
|
def check_guts(self, last_build):
|
|
outnm = os.path.basename(self.out)
|
|
if not os.path.exists(self.name):
|
|
print "rebuilding %s because %s is missing" % (outnm, os.path.basename(self.name))
|
|
return 1
|
|
try:
|
|
name, level, toc = eval(open(self.out, 'r').read())
|
|
except:
|
|
print "rebuilding %s because missing" % outnm
|
|
return 1
|
|
if name != self.name:
|
|
print "rebuilding %s because name changed" % outnm
|
|
return 1
|
|
if level != self.level:
|
|
print "rebuilding %s because level changed" % outnm
|
|
return 1
|
|
if toc != self.toc:
|
|
print "rebuilding %s because toc changed" % outnm
|
|
return 1
|
|
for (nm, fnm, typ) in toc:
|
|
if mtime(fnm) > last_build:
|
|
print "rebuilding %s because %s changed" % (outnm, fnm)
|
|
return 1
|
|
if fnm[-1] in ('c', 'o'):
|
|
if mtime(fnm[:-1]) > last_build:
|
|
print "rebuilding %s because %s changed" % (outnm, fnm[:-1])
|
|
return 1
|
|
return 0
|
|
def assemble(self):
|
|
print "building PYZ", os.path.basename(self.out)
|
|
pyz = archive.ZlibArchive(level=self.level)
|
|
toc = self.toc - config['PYZ_dependencies']
|
|
for (nm, fnm, typ) in toc:
|
|
if mtime(fnm[:-1]) > mtime(fnm):
|
|
py_compile.compile(fnm[:-1])
|
|
pyz.build(self.name, toc)
|
|
outf = open(self.out, 'w')
|
|
pprint.pprint((self.name, self.level, self.toc), outf)
|
|
outf.close()
|
|
return 1
|
|
|
|
def cacheDigest(fnm):
|
|
data = open(fnm, "rb").read()
|
|
digest = md5.new(data).digest()
|
|
return digest
|
|
|
|
def checkCache(fnm, strip, upx):
|
|
if not strip and not upx:
|
|
return fnm
|
|
if strip:
|
|
strip = 1
|
|
else:
|
|
strip = 0
|
|
if upx:
|
|
upx = 1
|
|
else:
|
|
upx = 0
|
|
|
|
# Load cache index
|
|
cachedir = os.path.join(HOMEPATH, 'bincache%d%d' % (strip, upx))
|
|
if not os.path.exists(cachedir):
|
|
os.makedirs(cachedir)
|
|
cacheindexfn = os.path.join(cachedir, "index.dat")
|
|
if os.path.exists(cacheindexfn):
|
|
cache_index = eval(open(cacheindexfn, "r").read())
|
|
else:
|
|
cache_index = {}
|
|
|
|
# Verify if the file we're looking for is present in the cache.
|
|
basenm = os.path.normcase(os.path.basename(fnm))
|
|
digest = cacheDigest(fnm)
|
|
cachedfile = os.path.join(cachedir, basenm)
|
|
if cache_index.has_key(basenm):
|
|
if digest != cache_index[basenm]:
|
|
os.remove(cachedfile)
|
|
else:
|
|
return cachedfile
|
|
if upx:
|
|
if strip:
|
|
fnm = checkCache(fnm, 1, 0)
|
|
cmd = "upx --best -q \"%s\"" % cachedfile
|
|
else:
|
|
cmd = "strip \"%s\"" % cachedfile
|
|
shutil.copy2(fnm, cachedfile)
|
|
os.chmod(cachedfile, 0755)
|
|
os.system(cmd)
|
|
|
|
# update cache index
|
|
cache_index[basenm] = digest
|
|
outf = open(cacheindexfn, 'w')
|
|
pprint.pprint(cache_index, outf)
|
|
outf.close()
|
|
|
|
return cachedfile
|
|
|
|
UNCOMPRESSED, COMPRESSED = range(2)
|
|
class PKG(Target):
|
|
typ = 'PKG'
|
|
xformdict = {'PYMODULE' : 'm',
|
|
'PYSOURCE' : 's',
|
|
'EXTENSION' : 'b',
|
|
'PYZ' : 'z',
|
|
'PKG' : 'a',
|
|
'DATA': 'x',
|
|
'BINARY': 'b',
|
|
'EXECUTABLE': 'b'}
|
|
def __init__(self, toc, name=None, cdict=None, exclude_binaries=0,
|
|
strip_binaries=0, upx_binaries=0):
|
|
Target.__init__(self)
|
|
self.toc = toc
|
|
self.cdict = cdict
|
|
self.name = name
|
|
self.exclude_binaries = exclude_binaries
|
|
self.strip_binaries = strip_binaries
|
|
self.upx_binaries = upx_binaries
|
|
if name is None:
|
|
self.name = self.out[:-3] + 'pkg'
|
|
if self.cdict is None:
|
|
if config['useZLIB']:
|
|
self.cdict = {'EXTENSION':COMPRESSED,
|
|
'DATA':COMPRESSED,
|
|
'BINARY':COMPRESSED,
|
|
'EXECUTABLE':COMPRESSED,
|
|
'PYSOURCE':COMPRESSED,
|
|
'PYMODULE':COMPRESSED }
|
|
else:
|
|
self.cdict = { 'PYSOURCE':UNCOMPRESSED }
|
|
self.__postinit__()
|
|
def check_guts(self, last_build):
|
|
outnm = os.path.basename(self.out)
|
|
if not os.path.exists(self.name):
|
|
print "rebuilding %s because %s is missing" % (outnm, os.path.basename(self.name))
|
|
return 1
|
|
try:
|
|
name, cdict, toc, exclude_binaries, strip_binaries, upx_binaries = eval(open(self.out, 'r').read())
|
|
except:
|
|
print "rebuilding %s because %s is missing" % (outnm, outnm)
|
|
return 1
|
|
if name != self.name:
|
|
print "rebuilding %s because name changed" % outnm
|
|
return 1
|
|
if cdict != self.cdict:
|
|
print "rebuilding %s because cdict changed" % outnm
|
|
return 1
|
|
if toc != self.toc:
|
|
print "rebuilding %s because toc changed" % outnm
|
|
return 1
|
|
if exclude_binaries != self.exclude_binaries:
|
|
print "rebuilding %s because exclude_binaries changed" % outnm
|
|
return 1
|
|
if strip_binaries != self.strip_binaries:
|
|
print "rebuilding %s because strip_binaries changed" % outnm
|
|
return 1
|
|
if upx_binaries != self.upx_binaries:
|
|
print "rebuilding %s because upx_binaries changed" % outnm
|
|
return 1
|
|
for (nm, fnm, typ) in toc:
|
|
if mtime(fnm) > last_build:
|
|
print "rebuilding %s because %s changed" % (outnm, fnm)
|
|
return 1
|
|
return 0
|
|
def assemble(self):
|
|
print "building PKG", os.path.basename(self.name)
|
|
trash = []
|
|
mytoc = []
|
|
toc = TOC()
|
|
for item in self.toc:
|
|
inm, fnm, typ = item
|
|
if typ == 'EXTENSION':
|
|
binext = os.path.splitext(fnm)[1]
|
|
if not os.path.splitext(inm)[1] == binext:
|
|
inm = inm + binext
|
|
toc.append((inm, fnm, typ))
|
|
seen = {}
|
|
for inm, fnm, typ in toc:
|
|
if typ in ('BINARY', 'EXTENSION'):
|
|
if self.exclude_binaries:
|
|
self.dependencies.append((inm, fnm, typ))
|
|
else:
|
|
fnm = checkCache(fnm, self.strip_binaries,
|
|
self.upx_binaries and ( iswin or cygwin )
|
|
and config['hasUPX'])
|
|
# Avoid importing the same binary extension twice. This might
|
|
# happen if they come from different sources (eg. once from
|
|
# binary dependence, and once from direct import).
|
|
if typ == 'BINARY' and seen.has_key(fnm):
|
|
continue
|
|
seen[fnm] = 1
|
|
mytoc.append((inm, fnm, self.cdict.get(typ,0),
|
|
self.xformdict.get(typ,'b')))
|
|
elif typ == 'OPTION':
|
|
mytoc.append((inm, '', 0, 'o'))
|
|
else:
|
|
mytoc.append((inm, fnm, self.cdict.get(typ,0), self.xformdict.get(typ,'b')))
|
|
archive = carchive.CArchive()
|
|
archive.build(self.name, mytoc)
|
|
outf = open(self.out, 'w')
|
|
pprint.pprint((self.name, self.cdict, self.toc, self.exclude_binaries, self.strip_binaries, self.upx_binaries), outf)
|
|
outf.close()
|
|
for item in trash:
|
|
os.remove(item)
|
|
return 1
|
|
|
|
class ELFEXE(Target):
|
|
typ = 'EXECUTABLE'
|
|
exclude_binaries = 0
|
|
def __init__(self, *args, **kws):
|
|
Target.__init__(self)
|
|
self.console = kws.get('console',1)
|
|
self.debug = kws.get('debug',0)
|
|
self.name = kws.get('name',None)
|
|
self.icon = kws.get('icon',None)
|
|
self.versrsrc = kws.get('version',None)
|
|
self.strip = kws.get('strip',None)
|
|
self.upx = kws.get('upx',None)
|
|
self.exclude_binaries = kws.get('exclude_binaries',0)
|
|
if self.name is None:
|
|
self.name = self.out[:-3] + 'exe'
|
|
if not os.path.isabs(self.name):
|
|
self.name = os.path.join(SPECPATH, self.name)
|
|
self.toc = TOC()
|
|
for arg in args:
|
|
if isinstance(arg, TOC):
|
|
self.toc.extend(arg)
|
|
elif isinstance(arg, Target):
|
|
self.toc.append((os.path.basename(arg.name), arg.name, arg.typ))
|
|
self.toc.extend(arg.dependencies)
|
|
else:
|
|
self.toc.extend(arg)
|
|
self.toc.extend(config['EXE_dependencies'])
|
|
self.pkg = PKG(self.toc, cdict=kws.get('cdict',None), exclude_binaries=self.exclude_binaries,
|
|
strip_binaries=self.strip, upx_binaries=self.upx)
|
|
self.dependencies = self.pkg.dependencies
|
|
self.__postinit__()
|
|
def check_guts(self, last_build):
|
|
outnm = os.path.basename(self.out)
|
|
if not os.path.exists(self.name):
|
|
print "rebuilding %s because %s missing" % (outnm, os.path.basename(self.name))
|
|
return 1
|
|
try:
|
|
name, console, debug, icon, versrsrc, strip, upx, mtm = eval(open(self.out, 'r').read())
|
|
except:
|
|
print "rebuilding %s because %s missing or bad" % (outnm, outnm)
|
|
return 1
|
|
if name != self.name:
|
|
print "rebuilding %s because name changed" % outnm
|
|
return 1
|
|
if console != self.console:
|
|
print "rebuilding %s because console option changed" % outnm
|
|
return 1
|
|
if debug != self.debug:
|
|
print "rebuilding %s because debug option changed" % outnm
|
|
return 1
|
|
if config['hasRsrcUpdate']:
|
|
if icon != self.icon:
|
|
print "rebuilding %s because icon option changed" % outnm
|
|
return 1
|
|
if versrsrc != self.versrsrc:
|
|
print "rebuilding %s because versrsrc option changed" % outnm
|
|
return 1
|
|
else:
|
|
if icon or versrsrc:
|
|
print "ignoring icon and version resources = platform not capable"
|
|
if strip != self.strip:
|
|
print "rebuilding %s because strip option changed" % outnm
|
|
return 1
|
|
if upx != self.upx:
|
|
print "rebuilding %s because upx option changed" % outnm
|
|
return 1
|
|
if mtm != mtime(self.name):
|
|
print "rebuilding %s because mtimes don't match" % outnm
|
|
return 1
|
|
if mtm < mtime(self.pkg.out):
|
|
print "rebuilding %s because pkg is more recent" % outnm
|
|
return 1
|
|
return 0
|
|
def _bootloader_postfix(self, exe):
|
|
if iswin:
|
|
exe = exe + "_"
|
|
is24 = hasattr(sys, "version_info") and sys.version_info[:2] >= (2,4)
|
|
exe = exe + "67"[is24]
|
|
exe = exe + "rd"[self.debug]
|
|
exe = exe + "wc"[self.console]
|
|
else:
|
|
if not self.console:
|
|
exe = exe + 'w'
|
|
if self.debug:
|
|
exe = exe + '_d'
|
|
return exe
|
|
def assemble(self):
|
|
print "building ELFEXE", os.path.basename(self.out)
|
|
trash = []
|
|
outf = open(self.name, 'wb')
|
|
exe = self._bootloader_postfix('support/loader/run')
|
|
exe = os.path.join(HOMEPATH, exe)
|
|
if iswin or cygwin:
|
|
exe = exe + '.exe'
|
|
if config['hasRsrcUpdate']:
|
|
if self.icon:
|
|
tmpnm = tempfile.mktemp()
|
|
shutil.copy2(exe, tmpnm)
|
|
os.chmod(tmpnm, 0755)
|
|
icon.CopyIcons(tmpnm, self.icon)
|
|
trash.append(tmpnm)
|
|
exe = tmpnm
|
|
if self.versrsrc:
|
|
tmpnm = tempfile.mktemp()
|
|
shutil.copy2(exe, tmpnm)
|
|
os.chmod(tmpnm, 0755)
|
|
versionInfo.SetVersion(tmpnm, self.versrsrc)
|
|
trash.append(tmpnm)
|
|
exe = tmpnm
|
|
exe = checkCache(exe, self.strip, self.upx and config['hasUPX'])
|
|
self.copy(exe, outf)
|
|
self.copy(self.pkg.name, outf)
|
|
outf.close()
|
|
os.chmod(self.name, 0755)
|
|
f = open(self.out, 'w')
|
|
pprint.pprint((self.name, self.console, self.debug, self.icon, self.versrsrc,
|
|
self.strip, self.upx, mtime(self.name)), f)
|
|
f.close()
|
|
for item in trash:
|
|
os.remove(item)
|
|
return 1
|
|
def copy(self, fnm, outf):
|
|
inf = open(fnm, 'rb')
|
|
while 1:
|
|
data = inf.read(64*1024)
|
|
if not data:
|
|
break
|
|
outf.write(data)
|
|
|
|
class DLL(ELFEXE):
|
|
def assemble(self):
|
|
print "building DLL", os.path.basename(self.out)
|
|
outf = open(self.name, 'wb')
|
|
dll = self._bootloader_postfix('support/loader/inprocsrvr')
|
|
dll = os.path.join(HOMEPATH, dll) + '.dll'
|
|
self.copy(dll, outf)
|
|
self.copy(self.pkg.name, outf)
|
|
outf.close()
|
|
os.chmod(self.name, 0755)
|
|
f = open(self.out, 'w')
|
|
pprint.pprint((self.name, self.console, self.debug, self.icon, self.versrsrc,
|
|
self.strip, self.upx, mtime(self.name)), f)
|
|
f.close()
|
|
return 1
|
|
|
|
class NonELFEXE(ELFEXE):
|
|
def assemble(self):
|
|
print "building NonELFEXE", os.path.basename(self.out)
|
|
trash = []
|
|
exe = 'support/loader/run'
|
|
if not self.console:
|
|
exe = exe + 'w'
|
|
if self.debug:
|
|
exe = exe + '_d'
|
|
exe = os.path.join(HOMEPATH, exe)
|
|
exe = checkCache(exe, self.strip, self.upx and config['hasUPX'])
|
|
shutil.copy2(exe, self.name)
|
|
os.chmod(self.name, 0755)
|
|
shutil.copy2(self.pkg.name, self.name+'.pkg')
|
|
f = open(self.out, 'w')
|
|
pprint.pprint((self.name, self.console, self.debug, self.icon, self.versrsrc,
|
|
self.strip, self.upx, mtime(self.name)), f)
|
|
f.close()
|
|
for fnm in trash:
|
|
os.remove(fnm)
|
|
return 1
|
|
|
|
if config['useELFEXE']:
|
|
EXE = ELFEXE
|
|
else:
|
|
EXE = NonELFEXE
|
|
|
|
class COLLECT(Target):
|
|
def __init__(self, *args, **kws):
|
|
Target.__init__(self)
|
|
self.name = kws.get('name',None)
|
|
if self.name is None:
|
|
self.name = 'dist_' + self.out[:-4]
|
|
self.strip_binaries = kws.get('strip',0)
|
|
self.upx_binaries = kws.get('upx',0)
|
|
if not os.path.isabs(self.name):
|
|
self.name = os.path.join(SPECPATH, self.name)
|
|
self.toc = TOC()
|
|
for arg in args:
|
|
if isinstance(arg, TOC):
|
|
self.toc.extend(arg)
|
|
elif isinstance(arg, Target):
|
|
self.toc.append((os.path.basename(arg.name), arg.name, arg.typ))
|
|
if isinstance(arg, NonELFEXE):
|
|
self.toc.append((os.path.basename(arg.name)+'.pkg', arg.name+'.pkg', 'PKG'))
|
|
self.toc.extend(arg.dependencies)
|
|
else:
|
|
self.toc.extend(arg)
|
|
self.__postinit__()
|
|
def check_guts(self, last_build):
|
|
outnm = os.path.basename(self.out)
|
|
try:
|
|
name, strip_binaries, upx_binaries, toc = eval(open(self.out, 'r').read())
|
|
except:
|
|
print "building %s because %s missing" % (outnm, outnm)
|
|
return 1
|
|
if name != self.name:
|
|
print "building %s because name changed" % outnm
|
|
return 1
|
|
if strip_binaries != self.strip_binaries:
|
|
print "building %s because strip_binaries option changed" % outnm
|
|
return 1
|
|
if upx_binaries != self.upx_binaries:
|
|
print "building %s because upx_binaries option changed" % outnm
|
|
return 1
|
|
if toc != self.toc:
|
|
print "building %s because toc changed" % outnm
|
|
return 1
|
|
for inm, fnm, typ in self.toc:
|
|
if typ == 'EXTENSION':
|
|
ext = os.path.splitext(fnm)[1]
|
|
test = os.path.join(self.name, inm+ext)
|
|
else:
|
|
test = os.path.join(self.name, os.path.basename(fnm))
|
|
if not os.path.exists(test):
|
|
print "building %s because %s is missing" % (outnm, test)
|
|
return 1
|
|
if mtime(fnm) > mtime(test):
|
|
print "building %s because %s is more recent" % (outnm, fnm)
|
|
return 1
|
|
return 0
|
|
def assemble(self):
|
|
print "building COLLECT", os.path.basename(self.out)
|
|
if not os.path.exists(self.name):
|
|
os.mkdir(self.name)
|
|
toc = TOC()
|
|
for inm, fnm, typ in self.toc:
|
|
if typ == 'EXTENSION':
|
|
binext = os.path.splitext(fnm)[1]
|
|
if not os.path.splitext(inm)[1] == binext:
|
|
inm = inm + binext
|
|
toc.append((inm, fnm, typ))
|
|
for inm, fnm, typ in toc:
|
|
tofnm = os.path.join(self.name, inm)
|
|
todir = os.path.dirname(tofnm)
|
|
if not os.path.exists(todir):
|
|
os.makedirs(todir)
|
|
if typ in ('EXTENSION', 'BINARY'):
|
|
fnm = checkCache(fnm, self.strip_binaries,
|
|
self.upx_binaries and ( iswin or cygwin )
|
|
and config['hasUPX'])
|
|
shutil.copy2(fnm, tofnm)
|
|
if typ in ('EXTENSION', 'BINARY'):
|
|
os.chmod(tofnm, 0755)
|
|
f = open(self.out, 'w')
|
|
pprint.pprint((self.name, self.strip_binaries, self.upx_binaries, self.toc), f)
|
|
f.close()
|
|
return 1
|
|
|
|
import UserList
|
|
class TOC(UserList.UserList):
|
|
def __init__(self, initlist=None):
|
|
UserList.UserList.__init__(self)
|
|
self.fltr = {}
|
|
if initlist:
|
|
for tpl in initlist:
|
|
self.append(tpl)
|
|
def append(self, tpl):
|
|
try:
|
|
fn = tpl[0]
|
|
if tpl[2] == "BINARY":
|
|
# Normalize the case for binary files only (to avoid duplicates
|
|
# for different cases under Windows). We can't do that for
|
|
# Python files because the import semantic (even at runtime)
|
|
# depends on the case.
|
|
fn = os.path.normcase(fn)
|
|
if not self.fltr.get(fn):
|
|
self.data.append(tpl)
|
|
self.fltr[fn] = 1
|
|
except TypeError:
|
|
print "TOC found a %s, not a tuple" % tpl
|
|
raise
|
|
def insert(self, pos, tpl):
|
|
fn = tpl[0]
|
|
if tpl[2] == "BINARY":
|
|
fn = os.path.normcase(fn)
|
|
if not self.fltr.get(fn):
|
|
self.data.insert(pos, tpl)
|
|
self.fltr[fn] = 1
|
|
def __add__(self, other):
|
|
rslt = TOC(self.data)
|
|
rslt.extend(other)
|
|
return rslt
|
|
def __radd__(self, other):
|
|
rslt = TOC(other)
|
|
rslt.extend(self.data)
|
|
return rslt
|
|
def extend(self, other):
|
|
for tpl in other:
|
|
self.append(tpl)
|
|
def __sub__(self, other):
|
|
fd = self.fltr.copy()
|
|
# remove from fd if it's in other
|
|
for tpl in other:
|
|
if fd.get(tpl[0],0):
|
|
del fd[tpl[0]]
|
|
rslt = TOC()
|
|
# return only those things still in fd (preserve order)
|
|
for tpl in self.data:
|
|
if fd.get(tpl[0],0):
|
|
rslt.append(tpl)
|
|
return rslt
|
|
def __rsub__(self, other):
|
|
rslt = TOC(other)
|
|
return rslt.__sub__(self)
|
|
def intersect(self, other):
|
|
rslt = TOC()
|
|
for tpl in other:
|
|
if self.fltr.get(tpl[0],0):
|
|
rslt.append(tpl)
|
|
return rslt
|
|
|
|
class Tree(Target, TOC):
|
|
def __init__(self, root=None, prefix=None, excludes=None):
|
|
Target.__init__(self)
|
|
TOC.__init__(self)
|
|
self.root = root
|
|
self.prefix = prefix
|
|
self.excludes = excludes
|
|
if excludes is None:
|
|
self.excludes = []
|
|
self.__postinit__()
|
|
def check_guts(self, last_build):
|
|
outnm = os.path.basename(self.out)
|
|
try:
|
|
root, prefix, excludes, toc = eval(open(self.out, 'r').read())
|
|
except:
|
|
print "building %s because %s is missing / bad" % (outnm, outnm)
|
|
return 1
|
|
if root != self.root:
|
|
print "building %s because root changed" % outnm
|
|
return 1
|
|
if prefix != self.prefix:
|
|
print "building %s because prefix changed" % outnm
|
|
return 1
|
|
if excludes != self.excludes:
|
|
print "building %s because excludes changed" % outnm
|
|
return 1
|
|
stack = [root]
|
|
while stack:
|
|
d = stack.pop()
|
|
if mtime(d) > last_build:
|
|
print "building %s because directory %s changed" % (outnm, d)
|
|
return 1
|
|
for nm in os.listdir(d):
|
|
path = os.path.join(d, nm)
|
|
if os.path.isdir(path):
|
|
stack.append(path)
|
|
self.data = toc
|
|
return 0
|
|
def assemble(self):
|
|
print "building Tree", os.path.basename(self.out)
|
|
stack = [(self.root, self.prefix)]
|
|
excludes = {}
|
|
xexcludes = {}
|
|
for nm in self.excludes:
|
|
if nm[0] == '*':
|
|
xexcludes[nm[1:]] = 1
|
|
else:
|
|
excludes[nm] = 1
|
|
rslt = []
|
|
while stack:
|
|
dir, prefix = stack.pop()
|
|
for fnm in os.listdir(dir):
|
|
if excludes.get(fnm, 0) == 0:
|
|
ext = os.path.splitext(fnm)[1]
|
|
if xexcludes.get(ext,0) == 0:
|
|
fullfnm = os.path.join(dir, fnm)
|
|
rfnm = prefix and os.path.join(prefix, fnm) or fnm
|
|
if os.path.isdir(fullfnm):
|
|
stack.append((fullfnm, rfnm))
|
|
else:
|
|
rslt.append((rfnm, fullfnm, 'DATA'))
|
|
try:
|
|
oldstuff = eval(open(self.out, 'r').read())
|
|
except:
|
|
oldstuff = None
|
|
if oldstuff != (self.root, self.prefix, self.excludes, rslt):
|
|
outf = open(self.out, 'w')
|
|
pprint.pprint((self.root, self.prefix, self.excludes, rslt), outf)
|
|
outf.close()
|
|
self.data = rslt
|
|
return 1
|
|
print self.out, "no change!"
|
|
return 0
|
|
|
|
def TkTree():
|
|
tclroot = config['TCL_root']
|
|
tclnm = os.path.join('_MEI', os.path.basename(tclroot))
|
|
tkroot = config['TK_root']
|
|
tknm = os.path.join('_MEI', os.path.basename(tkroot))
|
|
tcltree = Tree(tclroot, tclnm, excludes=['demos','encoding','*.lib'])
|
|
tktree = Tree(tkroot, tknm, excludes=['demos','encoding','*.lib'])
|
|
return tcltree + tktree
|
|
|
|
def TkPKG():
|
|
return PKG(TkTree(), name='tk.pkg')
|
|
|
|
usage = """\
|
|
Usage: python %s <specfile>
|
|
|
|
See doc/Tutorial.html for details.
|
|
"""
|
|
|
|
if __name__ == '__main__':
|
|
if len(sys.argv) < 2:
|
|
print usage % sys.argv[0]
|
|
else:
|
|
build(sys.argv[1])
|
|
|
|
|
|
|
|
|