# Build all Python Falcon applications, using the PyInstaller
# multipackage support. This will create an executable, plus required
# files and folders, for each app in the Programs folder.
from shutil import copytree, copy2, rmtree
from os.path import join
import stat
## The product version, used in the EXE step
## Note: when changing this, also delete */Version.txt
Version = (8, 1, 1)
## Where the application folders will be built
TargetDir = os.path.abspath(join('.','Programs'))
## Structure for Windows version info data
VersionInfo = """VSVersionInfo(
ffi=FixedFileInfo(
filevers=(%(1)d, %(2)d, 0, %(3)d),
prodvers=(%(1)d, %(2)d, 0, %(3)d),
mask=0x0,
flags=0x0,
OS=0x4,
fileType=0x1,
subtype=0x0,
date=(0, 0)
),
kids=[
VarFileInfo([VarStruct('Translation', [1033, 1200])]),
StringFileInfo(
[
StringTable(
'040904B0',
[StringStruct('CompanyName', 'Advanced Publishing Technology,
Inc.'),
StringStruct('ProductName', '%(appname)s'),
StringStruct('FileVersion', '%(1)d.%(2)02d.%(3)04d'),
StringStruct('ProductVersion', '%(1)d.%(2)02d.%(3)04d'),
StringStruct('InternalName', '%(appname)s'),
StringStruct('OriginalFilename', '%(appname)s.exe')])
])
]
)
"""
## The application names
AppNames = [d for d in os.listdir(os.getcwd())
if os.path.isdir(d)
and d[0]!='.'
and d[0:6]!='Common'
and d not in ('Programs','User
Manual','build','dummy','ConfigEdit')
]
## Build MERGE arguments (analysis object, script base name, final exe
path)
### Commented out, since we've learned that we don't need the merge or
the dummy,
### and it works better without it (the merge forces two-pass execution)
# Start with the dummy package
# print 'Analyzing app dummy...'
# Analyses = [(Analysis([join('dummy','dummy.py')]),
# 'dummy', join('dummy','dummy.exe'))
# ]
Analyses = []
# NOTE: this assumes that the main script in each is appname.pyw in
the appname folder
for appname in AppNames:
print 'Analyzing app %s' % appname
Analyses.append((Analysis([join(appname, appname + '.pyw')]),
appname, join(appname,appname+'.exe')))
## Merge all the dependencies
##print 'Merging dependencies...'
##MERGE(*Analyses)
## Build each app
collectexes = []
collectanals = []
for anal, basename, exename in Analyses:
print 'Building app %s ...' % basename
versionpath = join(basename,'Version.txt')
if not os.path.exists(versionpath):
vdict = {'appname': basename, '1':Version[0], '2':Version[1],
'3':Version[2]}
vstring = VersionInfo % vdict
f = open(versionpath,'w')
f.write(vstring)
f.close()
pyz = PYZ(anal.pure)
exe = EXE(pyz,
anal.scripts,
##anal.scripts + [('v', '', 'OPTION')], ## For debugging
anal.dependencies,
exclude_binaries=1,
name=exename,
version=versionpath,
debug=False,
strip=False,
upx=False, # Only saves ~50Kb/app, about doubles
startup time
console=False )
# Lists for collection
collectexes.append(exe)
collectanals.append(anal.binaries)
# Since PyI insists on a "clean" output folder, make it so
outfolder = TargetDir
if os.path.exists(outfolder):
# Use walk rather than os.path.rmtree so we can handle read-only files
for root, dirs, files in os.walk(outfolder, topdown=False):
for name in files:
f = join(root, name)
# Needed on windows, since remove doesn't handle read-only
files
os.chmod(f, stat.S_IWUSR)
os.remove(f)
for name in dirs:
d = join(root, name)
# Needed on windows, since rmdir doesn't handle read-only
folders
os.chmod(d, stat.S_IWUSR)
os.rmdir(d)
print 'Collecting files for apps...'
# Flatten the lists of exes, anals, and options
dist = COLLECT(*(collectexes + collectanals),
**{'strip': False,
'upx': False,
'name': outfolder}
)
## Copy the app-specific non-code folders and files into the target
app folders
# (Currently, I can't think of anything cleverer than this)
# Dict keyed by appname, value (folderlist, filelist) per app
AddDict = {'Alert':(['Alert_aux_files','mpl-data'],[]),
## Note: the inclusion of the mpl-data folder here is a hack.
## I copied it from
C:\ProgramFiles\Python25\Lib\site-packages\matplotlib into Alert.
## (This means that it'll need to be copied again if
matplotlib is updated.)
## It should be dealt with in PyI, but isn't. See
##
http://www.sofastatistics.com/blog/making-better-installer-for-sofa-using-pyinstaller/
and
## http://comments.gmane.org/gmane.comp.python.pyinstaller/4161
## for some information.
## Maybe the hook file could take care of it?
'Administration':(['Administration_aux_files'],[]),
'AdZoneEntry':(['AdZoneEntry_aux_files'],[]),
'CustomerService':(['CustomerService_aux_files'],[]),
'FalconApp':(['FalconApp_aux_files'],[]),
'FinancialEntry':(['FinancialEntry_aux_files'],[]),
'GeneralLedger':(['GeneralLedger_aux_files'],[]),
'PreprintManagement':(['PreprintManagement_aux_files'],[]),
'Reports':(['Reports_aux_files'],[]),
'RouteDelivery':(['RouteDelivery_aux_files'],[]),
'Setup':(['Setup_aux_files'],[]),
'SingleCopy':(['SingleCopy_aux_files'],[]),
'SubscriberLabel':(['SubscriberLabel_aux_files'],[]),
'Management':(['Management_aux_files'],[]),
'TotalMarketCoverage':(['TotalMarketCoverage_aux_files'],[]),
'UserAccess':(['UserAccess_aux_files'],[]),
'Utilities':(['Utilities_aux_files'],[]),
}
for app in AppNames:
print "Copying data folders and files for " + app
if AddDict.has_key(app):
folders = AddDict[app][0]
files = AddDict[app][1]
for folder in folders:
wherefrom = join(app,folder)
whereto = join(TargetDir, folder)
for srcdir, dirs, files in os.walk(wherefrom):
if '.svn' in dirs:
dirs.remove('.svn')
destdir = srcdir.replace(wherefrom,whereto)
if not os.path.exists(destdir):
os.mkdir(destdir)
for file_ in files:
src_file = os.path.join(srcdir, file_)
dst_file = os.path.join(destdir, file_)
copy2(src_file, dst_file)