On 5/7/13 2:26 PM, Jared Welch wrote:
This will not create any issues? I imagine the dependencies and shared code will all be available to each .exe, correct? For out app we are thinking of dumping everything from each package into one directory:

   \app
       ... EXEs ...
Here's a spec file for a very similar project that I've done. It resides in the main folder of the project; the sources for the various applications (including the "main" application) are subfolders; there's also a few non-program subfolders. The built applications go into a subfolder called Programs (which could be located somewhere else).

I originally had a subfolder within Programs for each app, plus another subfolder to collect the depedendencies common to all apps. This wound up causing two-pass execution, which slowed down application startup appreciably. After some expermentation, I wound up putting the programs and all dependencies in the same folder. This requires some care in naming application-specific auxiliary files and folders, but I considered it worth the care to get the faster startup.

# 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)


--

Don Dwiggins
Advanced Publishing Technology


--
You received this message because you are subscribed to the Google Groups 
"PyInstaller" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/pyinstaller?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to