Home | History | Annotate | Download | only in freeze
      1 #! /usr/bin/env python
      2 
      3 """Freeze a Python script into a binary.
      4 
      5 usage: freeze [options...] script [module]...
      6 
      7 Options:
      8 -p prefix:    This is the prefix used when you ran ``make install''
      9               in the Python build directory.
     10               (If you never ran this, freeze won't work.)
     11               The default is whatever sys.prefix evaluates to.
     12               It can also be the top directory of the Python source
     13               tree; then -P must point to the build tree.
     14 
     15 -P exec_prefix: Like -p but this is the 'exec_prefix', used to
     16                 install objects etc.  The default is whatever sys.exec_prefix
     17                 evaluates to, or the -p argument if given.
     18                 If -p points to the Python source tree, -P must point
     19                 to the build tree, if different.
     20 
     21 -e extension: A directory containing additional .o files that
     22               may be used to resolve modules.  This directory
     23               should also have a Setup file describing the .o files.
     24               On Windows, the name of a .INI file describing one
     25               or more extensions is passed.
     26               More than one -e option may be given.
     27 
     28 -o dir:       Directory where the output files are created; default '.'.
     29 
     30 -m:           Additional arguments are module names instead of filenames.
     31 
     32 -a package=dir: Additional directories to be added to the package's
     33                 __path__.  Used to simulate directories added by the
     34                 package at runtime (eg, by OpenGL and win32com).
     35                 More than one -a option may be given for each package.
     36 
     37 -l file:      Pass the file to the linker (windows only)
     38 
     39 -d:           Debugging mode for the module finder.
     40 
     41 -q:           Make the module finder totally quiet.
     42 
     43 -h:           Print this help message.
     44 
     45 -x module     Exclude the specified module. It will still be imported
     46               by the frozen binary if it exists on the host system.
     47 
     48 -X module     Like -x, except the module can never be imported by
     49               the frozen binary.
     50 
     51 -E:           Freeze will fail if any modules can't be found (that
     52               were not excluded using -x or -X).
     53 
     54 -i filename:  Include a file with additional command line options.  Used
     55               to prevent command lines growing beyond the capabilities of
     56               the shell/OS.  All arguments specified in filename
     57               are read and the -i option replaced with the parsed
     58               params (note - quoting args in this file is NOT supported)
     59 
     60 -s subsystem: Specify the subsystem (For Windows only.);
     61               'console' (default), 'windows', 'service' or 'com_dll'
     62 
     63 -w:           Toggle Windows (NT or 95) behavior.
     64               (For debugging only -- on a win32 platform, win32 behavior
     65               is automatic.)
     66 
     67 -r prefix=f:  Replace path prefix.
     68               Replace prefix with f in the source path references
     69               contained in the resulting binary.
     70 
     71 Arguments:
     72 
     73 script:       The Python script to be executed by the resulting binary.
     74 
     75 module ...:   Additional Python modules (referenced by pathname)
     76               that will be included in the resulting binary.  These
     77               may be .py or .pyc files.  If -m is specified, these are
     78               module names that are search in the path instead.
     79 
     80 NOTES:
     81 
     82 In order to use freeze successfully, you must have built Python and
     83 installed it ("make install").
     84 
     85 The script should not use modules provided only as shared libraries;
     86 if it does, the resulting binary is not self-contained.
     87 """
     88 
     89 
     90 # Import standard modules
     91 
     92 import modulefinder
     93 import getopt
     94 import os
     95 import sys
     96 
     97 
     98 # Import the freeze-private modules
     99 
    100 import checkextensions
    101 import makeconfig
    102 import makefreeze
    103 import makemakefile
    104 import parsesetup
    105 import bkfile
    106 
    107 
    108 # Main program
    109 
    110 def main():
    111     # overridable context
    112     prefix = None                       # settable with -p option
    113     exec_prefix = None                  # settable with -P option
    114     extensions = []
    115     exclude = []                        # settable with -x option
    116     addn_link = []      # settable with -l, but only honored under Windows.
    117     path = sys.path[:]
    118     modargs = 0
    119     debug = 1
    120     odir = ''
    121     win = sys.platform[:3] == 'win'
    122     replace_paths = []                  # settable with -r option
    123     error_if_any_missing = 0
    124 
    125     # default the exclude list for each platform
    126     if win: exclude = exclude + [
    127         'dos', 'dospath', 'mac', 'macpath', 'macfs', 'MACFS', 'posix',
    128         'os2', 'ce', 'riscos', 'riscosenviron', 'riscospath',
    129         ]
    130 
    131     fail_import = exclude[:]
    132 
    133     # output files
    134     frozen_c = 'frozen.c'
    135     config_c = 'config.c'
    136     target = 'a.out'                    # normally derived from script name
    137     makefile = 'Makefile'
    138     subsystem = 'console'
    139 
    140     # parse command line by first replacing any "-i" options with the
    141     # file contents.
    142     pos = 1
    143     while pos < len(sys.argv)-1:
    144         # last option can not be "-i", so this ensures "pos+1" is in range!
    145         if sys.argv[pos] == '-i':
    146             try:
    147                 options = open(sys.argv[pos+1]).read().split()
    148             except IOError, why:
    149                 usage("File name '%s' specified with the -i option "
    150                       "can not be read - %s" % (sys.argv[pos+1], why) )
    151             # Replace the '-i' and the filename with the read params.
    152             sys.argv[pos:pos+2] = options
    153             pos = pos + len(options) - 1 # Skip the name and the included args.
    154         pos = pos + 1
    155 
    156     # Now parse the command line with the extras inserted.
    157     try:
    158         opts, args = getopt.getopt(sys.argv[1:], 'r:a:dEe:hmo:p:P:qs:wX:x:l:')
    159     except getopt.error, msg:
    160         usage('getopt error: ' + str(msg))
    161 
    162     # process option arguments
    163     for o, a in opts:
    164         if o == '-h':
    165             print __doc__
    166             return
    167         if o == '-d':
    168             debug = debug + 1
    169         if o == '-e':
    170             extensions.append(a)
    171         if o == '-m':
    172             modargs = 1
    173         if o == '-o':
    174             odir = a
    175         if o == '-p':
    176             prefix = a
    177         if o == '-P':
    178             exec_prefix = a
    179         if o == '-q':
    180             debug = 0
    181         if o == '-w':
    182             win = not win
    183         if o == '-s':
    184             if not win:
    185                 usage("-s subsystem option only on Windows")
    186             subsystem = a
    187         if o == '-x':
    188             exclude.append(a)
    189         if o == '-X':
    190             exclude.append(a)
    191             fail_import.append(a)
    192         if o == '-E':
    193             error_if_any_missing = 1
    194         if o == '-l':
    195             addn_link.append(a)
    196         if o == '-a':
    197             apply(modulefinder.AddPackagePath, tuple(a.split("=", 2)))
    198         if o == '-r':
    199             f,r = a.split("=", 2)
    200             replace_paths.append( (f,r) )
    201 
    202     # modules that are imported by the Python runtime
    203     implicits = []
    204     for module in ('site', 'warnings',):
    205         if module not in exclude:
    206             implicits.append(module)
    207 
    208     # default prefix and exec_prefix
    209     if not exec_prefix:
    210         if prefix:
    211             exec_prefix = prefix
    212         else:
    213             exec_prefix = sys.exec_prefix
    214     if not prefix:
    215         prefix = sys.prefix
    216 
    217     # determine whether -p points to the Python source tree
    218     ishome = os.path.exists(os.path.join(prefix, 'Python', 'ceval.c'))
    219 
    220     # locations derived from options
    221     version = sys.version[:3]
    222     if win:
    223         extensions_c = 'frozen_extensions.c'
    224     if ishome:
    225         print "(Using Python source directory)"
    226         binlib = exec_prefix
    227         incldir = os.path.join(prefix, 'Include')
    228         config_h_dir = exec_prefix
    229         config_c_in = os.path.join(prefix, 'Modules', 'config.c.in')
    230         frozenmain_c = os.path.join(prefix, 'Python', 'frozenmain.c')
    231         makefile_in = os.path.join(exec_prefix, 'Makefile')
    232         if win:
    233             frozendllmain_c = os.path.join(exec_prefix, 'Pc\\frozen_dllmain.c')
    234     else:
    235         binlib = os.path.join(exec_prefix,
    236                               'lib', 'python%s' % version, 'config')
    237         incldir = os.path.join(prefix, 'include', 'python%s' % version)
    238         config_h_dir = os.path.join(exec_prefix, 'include',
    239                                     'python%s' % version)
    240         config_c_in = os.path.join(binlib, 'config.c.in')
    241         frozenmain_c = os.path.join(binlib, 'frozenmain.c')
    242         makefile_in = os.path.join(binlib, 'Makefile')
    243         frozendllmain_c = os.path.join(binlib, 'frozen_dllmain.c')
    244     supp_sources = []
    245     defines = []
    246     includes = ['-I' + incldir, '-I' + config_h_dir]
    247 
    248     # sanity check of directories and files
    249     check_dirs = [prefix, exec_prefix, binlib, incldir]
    250     if not win:
    251         # These are not directories on Windows.
    252         check_dirs = check_dirs + extensions
    253     for dir in check_dirs:
    254         if not os.path.exists(dir):
    255             usage('needed directory %s not found' % dir)
    256         if not os.path.isdir(dir):
    257             usage('%s: not a directory' % dir)
    258     if win:
    259         files = supp_sources + extensions # extensions are files on Windows.
    260     else:
    261         files = [config_c_in, makefile_in] + supp_sources
    262     for file in supp_sources:
    263         if not os.path.exists(file):
    264             usage('needed file %s not found' % file)
    265         if not os.path.isfile(file):
    266             usage('%s: not a plain file' % file)
    267     if not win:
    268         for dir in extensions:
    269             setup = os.path.join(dir, 'Setup')
    270             if not os.path.exists(setup):
    271                 usage('needed file %s not found' % setup)
    272             if not os.path.isfile(setup):
    273                 usage('%s: not a plain file' % setup)
    274 
    275     # check that enough arguments are passed
    276     if not args:
    277         usage('at least one filename argument required')
    278 
    279     # check that file arguments exist
    280     for arg in args:
    281         if arg == '-m':
    282             break
    283         # if user specified -m on the command line before _any_
    284         # file names, then nothing should be checked (as the
    285         # very first file should be a module name)
    286         if modargs:
    287             break
    288         if not os.path.exists(arg):
    289             usage('argument %s not found' % arg)
    290         if not os.path.isfile(arg):
    291             usage('%s: not a plain file' % arg)
    292 
    293     # process non-option arguments
    294     scriptfile = args[0]
    295     modules = args[1:]
    296 
    297     # derive target name from script name
    298     base = os.path.basename(scriptfile)
    299     base, ext = os.path.splitext(base)
    300     if base:
    301         if base != scriptfile:
    302             target = base
    303         else:
    304             target = base + '.bin'
    305 
    306     # handle -o option
    307     base_frozen_c = frozen_c
    308     base_config_c = config_c
    309     base_target = target
    310     if odir and not os.path.isdir(odir):
    311         try:
    312             os.mkdir(odir)
    313             print "Created output directory", odir
    314         except os.error, msg:
    315             usage('%s: mkdir failed (%s)' % (odir, str(msg)))
    316     base = ''
    317     if odir:
    318         base = os.path.join(odir, '')
    319         frozen_c = os.path.join(odir, frozen_c)
    320         config_c = os.path.join(odir, config_c)
    321         target = os.path.join(odir, target)
    322         makefile = os.path.join(odir, makefile)
    323         if win: extensions_c = os.path.join(odir, extensions_c)
    324 
    325     # Handle special entry point requirements
    326     # (on Windows, some frozen programs do not use __main__, but
    327     # import the module directly.  Eg, DLLs, Services, etc
    328     custom_entry_point = None  # Currently only used on Windows
    329     python_entry_is_main = 1   # Is the entry point called __main__?
    330     # handle -s option on Windows
    331     if win:
    332         import winmakemakefile
    333         try:
    334             custom_entry_point, python_entry_is_main = \
    335                 winmakemakefile.get_custom_entry_point(subsystem)
    336         except ValueError, why:
    337             usage(why)
    338 
    339 
    340     # Actual work starts here...
    341 
    342     # collect all modules of the program
    343     dir = os.path.dirname(scriptfile)
    344     path[0] = dir
    345     mf = modulefinder.ModuleFinder(path, debug, exclude, replace_paths)
    346 
    347     if win and subsystem=='service':
    348         # If a Windows service, then add the "built-in" module.
    349         mod = mf.add_module("servicemanager")
    350         mod.__file__="dummy.pyd" # really built-in to the resulting EXE
    351 
    352     for mod in implicits:
    353         mf.import_hook(mod)
    354     for mod in modules:
    355         if mod == '-m':
    356             modargs = 1
    357             continue
    358         if modargs:
    359             if mod[-2:] == '.*':
    360                 mf.import_hook(mod[:-2], None, ["*"])
    361             else:
    362                 mf.import_hook(mod)
    363         else:
    364             mf.load_file(mod)
    365 
    366     # Add the main script as either __main__, or the actual module name.
    367     if python_entry_is_main:
    368         mf.run_script(scriptfile)
    369     else:
    370         mf.load_file(scriptfile)
    371 
    372     if debug > 0:
    373         mf.report()
    374         print
    375     dict = mf.modules
    376 
    377     if error_if_any_missing:
    378         missing = mf.any_missing()
    379         if missing:
    380             sys.exit("There are some missing modules: %r" % missing)
    381 
    382     # generate output for frozen modules
    383     files = makefreeze.makefreeze(base, dict, debug, custom_entry_point,
    384                                   fail_import)
    385 
    386     # look for unfrozen modules (builtin and of unknown origin)
    387     builtins = []
    388     unknown = []
    389     mods = dict.keys()
    390     mods.sort()
    391     for mod in mods:
    392         if dict[mod].__code__:
    393             continue
    394         if not dict[mod].__file__:
    395             builtins.append(mod)
    396         else:
    397             unknown.append(mod)
    398 
    399     # search for unknown modules in extensions directories (not on Windows)
    400     addfiles = []
    401     frozen_extensions = [] # Windows list of modules.
    402     if unknown or (not win and builtins):
    403         if not win:
    404             addfiles, addmods = \
    405                       checkextensions.checkextensions(unknown+builtins,
    406                                                       extensions)
    407             for mod in addmods:
    408                 if mod in unknown:
    409                     unknown.remove(mod)
    410                     builtins.append(mod)
    411         else:
    412             # Do the windows thang...
    413             import checkextensions_win32
    414             # Get a list of CExtension instances, each describing a module
    415             # (including its source files)
    416             frozen_extensions = checkextensions_win32.checkextensions(
    417                 unknown, extensions, prefix)
    418             for mod in frozen_extensions:
    419                 unknown.remove(mod.name)
    420 
    421     # report unknown modules
    422     if unknown:
    423         sys.stderr.write('Warning: unknown modules remain: %s\n' %
    424                          ' '.join(unknown))
    425 
    426     # windows gets different treatment
    427     if win:
    428         # Taking a shortcut here...
    429         import winmakemakefile, checkextensions_win32
    430         checkextensions_win32.write_extension_table(extensions_c,
    431                                                     frozen_extensions)
    432         # Create a module definition for the bootstrap C code.
    433         xtras = [frozenmain_c, os.path.basename(frozen_c),
    434                  frozendllmain_c, os.path.basename(extensions_c)] + files
    435         maindefn = checkextensions_win32.CExtension( '__main__', xtras )
    436         frozen_extensions.append( maindefn )
    437         outfp = open(makefile, 'w')
    438         try:
    439             winmakemakefile.makemakefile(outfp,
    440                                          locals(),
    441                                          frozen_extensions,
    442                                          os.path.basename(target))
    443         finally:
    444             outfp.close()
    445         return
    446 
    447     # generate config.c and Makefile
    448     builtins.sort()
    449     infp = open(config_c_in)
    450     outfp = bkfile.open(config_c, 'w')
    451     try:
    452         makeconfig.makeconfig(infp, outfp, builtins)
    453     finally:
    454         outfp.close()
    455     infp.close()
    456 
    457     cflags = ['$(OPT)']
    458     cppflags = defines + includes
    459     libs = [os.path.join(binlib, 'libpython$(VERSION).a')]
    460 
    461     somevars = {}
    462     if os.path.exists(makefile_in):
    463         makevars = parsesetup.getmakevars(makefile_in)
    464         for key in makevars.keys():
    465             somevars[key] = makevars[key]
    466 
    467     somevars['CFLAGS'] = ' '.join(cflags) # override
    468     somevars['CPPFLAGS'] = ' '.join(cppflags) # override
    469     files = [base_config_c, base_frozen_c] + \
    470             files + supp_sources +  addfiles + libs + \
    471             ['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)']
    472 
    473     outfp = bkfile.open(makefile, 'w')
    474     try:
    475         makemakefile.makemakefile(outfp, somevars, files, base_target)
    476     finally:
    477         outfp.close()
    478 
    479     # Done!
    480 
    481     if odir:
    482         print 'Now run "make" in', odir,
    483         print 'to build the target:', base_target
    484     else:
    485         print 'Now run "make" to build the target:', base_target
    486 
    487 
    488 # Print usage message and exit
    489 
    490 def usage(msg):
    491     sys.stdout = sys.stderr
    492     print "Error:", msg
    493     print "Use ``%s -h'' for help" % sys.argv[0]
    494     sys.exit(2)
    495 
    496 
    497 main()
    498