mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			497 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			497 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
#! /usr/bin/env python
 | 
						|
 | 
						|
"""Freeze a Python script into a binary.
 | 
						|
 | 
						|
usage: freeze [options...] script [module]...
 | 
						|
 | 
						|
Options:
 | 
						|
-p prefix:    This is the prefix used when you ran ``make install''
 | 
						|
              in the Python build directory.
 | 
						|
              (If you never ran this, freeze won't work.)
 | 
						|
              The default is whatever sys.prefix evaluates to.
 | 
						|
              It can also be the top directory of the Python source
 | 
						|
              tree; then -P must point to the build tree.
 | 
						|
 | 
						|
-P exec_prefix: Like -p but this is the 'exec_prefix', used to
 | 
						|
                install objects etc.  The default is whatever sys.exec_prefix
 | 
						|
                evaluates to, or the -p argument if given.
 | 
						|
                If -p points to the Python source tree, -P must point
 | 
						|
                to the build tree, if different.
 | 
						|
 | 
						|
-e extension: A directory containing additional .o files that
 | 
						|
              may be used to resolve modules.  This directory
 | 
						|
              should also have a Setup file describing the .o files.
 | 
						|
              On Windows, the name of a .INI file describing one
 | 
						|
              or more extensions is passed.
 | 
						|
              More than one -e option may be given.
 | 
						|
 | 
						|
-o dir:       Directory where the output files are created; default '.'.
 | 
						|
 | 
						|
-m:           Additional arguments are module names instead of filenames.
 | 
						|
 | 
						|
-a package=dir: Additional directories to be added to the package's
 | 
						|
                __path__.  Used to simulate directories added by the
 | 
						|
                package at runtime (eg, by OpenGL and win32com).
 | 
						|
                More than one -a option may be given for each package.
 | 
						|
 | 
						|
-l file:      Pass the file to the linker (windows only)
 | 
						|
 | 
						|
-d:           Debugging mode for the module finder.
 | 
						|
 | 
						|
-q:           Make the module finder totally quiet.
 | 
						|
 | 
						|
-h:           Print this help message.
 | 
						|
 | 
						|
-x module     Exclude the specified module. It will still be imported
 | 
						|
              by the frozen binary if it exists on the host system.
 | 
						|
 | 
						|
-X module     Like -x, except the module can never be imported by
 | 
						|
              the frozen binary.
 | 
						|
 | 
						|
-E:           Freeze will fail if any modules can't be found (that
 | 
						|
              were not excluded using -x or -X).
 | 
						|
 | 
						|
-i filename:  Include a file with additional command line options.  Used
 | 
						|
              to prevent command lines growing beyond the capabilities of
 | 
						|
              the shell/OS.  All arguments specified in filename
 | 
						|
              are read and the -i option replaced with the parsed
 | 
						|
              params (note - quoting args in this file is NOT supported)
 | 
						|
 | 
						|
-s subsystem: Specify the subsystem (For Windows only.);
 | 
						|
              'console' (default), 'windows', 'service' or 'com_dll'
 | 
						|
 | 
						|
-w:           Toggle Windows (NT or 95) behavior.
 | 
						|
              (For debugging only -- on a win32 platform, win32 behavior
 | 
						|
              is automatic.)
 | 
						|
 | 
						|
-r prefix=f:  Replace path prefix.
 | 
						|
              Replace prefix with f in the source path references
 | 
						|
              contained in the resulting binary.
 | 
						|
 | 
						|
Arguments:
 | 
						|
 | 
						|
script:       The Python script to be executed by the resulting binary.
 | 
						|
 | 
						|
module ...:   Additional Python modules (referenced by pathname)
 | 
						|
              that will be included in the resulting binary.  These
 | 
						|
              may be .py or .pyc files.  If -m is specified, these are
 | 
						|
              module names that are search in the path instead.
 | 
						|
 | 
						|
NOTES:
 | 
						|
 | 
						|
In order to use freeze successfully, you must have built Python and
 | 
						|
installed it ("make install").
 | 
						|
 | 
						|
The script should not use modules provided only as shared libraries;
 | 
						|
if it does, the resulting binary is not self-contained.
 | 
						|
"""
 | 
						|
 | 
						|
 | 
						|
# Import standard modules
 | 
						|
 | 
						|
import modulefinder
 | 
						|
import getopt
 | 
						|
import os
 | 
						|
import sys
 | 
						|
 | 
						|
 | 
						|
# Import the freeze-private modules
 | 
						|
 | 
						|
import checkextensions
 | 
						|
import makeconfig
 | 
						|
import makefreeze
 | 
						|
import makemakefile
 | 
						|
import parsesetup
 | 
						|
import bkfile
 | 
						|
 | 
						|
 | 
						|
# Main program
 | 
						|
 | 
						|
def main():
 | 
						|
    # overridable context
 | 
						|
    prefix = None                       # settable with -p option
 | 
						|
    exec_prefix = None                  # settable with -P option
 | 
						|
    extensions = []
 | 
						|
    exclude = []                        # settable with -x option
 | 
						|
    addn_link = []      # settable with -l, but only honored under Windows.
 | 
						|
    path = sys.path[:]
 | 
						|
    modargs = 0
 | 
						|
    debug = 1
 | 
						|
    odir = ''
 | 
						|
    win = sys.platform[:3] == 'win'
 | 
						|
    replace_paths = []                  # settable with -r option
 | 
						|
    error_if_any_missing = 0
 | 
						|
 | 
						|
    # default the exclude list for each platform
 | 
						|
    if win: exclude = exclude + [
 | 
						|
        'dos', 'dospath', 'mac', 'macpath', 'macfs', 'MACFS', 'posix',
 | 
						|
        'os2', 'ce', 'riscos', 'riscosenviron', 'riscospath',
 | 
						|
        ]
 | 
						|
 | 
						|
    fail_import = exclude[:]
 | 
						|
 | 
						|
    # output files
 | 
						|
    frozen_c = 'frozen.c'
 | 
						|
    config_c = 'config.c'
 | 
						|
    target = 'a.out'                    # normally derived from script name
 | 
						|
    makefile = 'Makefile'
 | 
						|
    subsystem = 'console'
 | 
						|
 | 
						|
    # parse command line by first replacing any "-i" options with the
 | 
						|
    # file contents.
 | 
						|
    pos = 1
 | 
						|
    while pos < len(sys.argv)-1:
 | 
						|
        # last option can not be "-i", so this ensures "pos+1" is in range!
 | 
						|
        if sys.argv[pos] == '-i':
 | 
						|
            try:
 | 
						|
                options = open(sys.argv[pos+1]).read().split()
 | 
						|
            except IOError, why:
 | 
						|
                usage("File name '%s' specified with the -i option "
 | 
						|
                      "can not be read - %s" % (sys.argv[pos+1], why) )
 | 
						|
            # Replace the '-i' and the filename with the read params.
 | 
						|
            sys.argv[pos:pos+2] = options
 | 
						|
            pos = pos + len(options) - 1 # Skip the name and the included args.
 | 
						|
        pos = pos + 1
 | 
						|
 | 
						|
    # Now parse the command line with the extras inserted.
 | 
						|
    try:
 | 
						|
        opts, args = getopt.getopt(sys.argv[1:], 'r:a:dEe:hmo:p:P:qs:wX:x:l:')
 | 
						|
    except getopt.error, msg:
 | 
						|
        usage('getopt error: ' + str(msg))
 | 
						|
 | 
						|
    # proces option arguments
 | 
						|
    for o, a in opts:
 | 
						|
        if o == '-h':
 | 
						|
            print __doc__
 | 
						|
            return
 | 
						|
        if o == '-d':
 | 
						|
            debug = debug + 1
 | 
						|
        if o == '-e':
 | 
						|
            extensions.append(a)
 | 
						|
        if o == '-m':
 | 
						|
            modargs = 1
 | 
						|
        if o == '-o':
 | 
						|
            odir = a
 | 
						|
        if o == '-p':
 | 
						|
            prefix = a
 | 
						|
        if o == '-P':
 | 
						|
            exec_prefix = a
 | 
						|
        if o == '-q':
 | 
						|
            debug = 0
 | 
						|
        if o == '-w':
 | 
						|
            win = not win
 | 
						|
        if o == '-s':
 | 
						|
            if not win:
 | 
						|
                usage("-s subsystem option only on Windows")
 | 
						|
            subsystem = a
 | 
						|
        if o == '-x':
 | 
						|
            exclude.append(a)
 | 
						|
        if o == '-X':
 | 
						|
            exclude.append(a)
 | 
						|
            fail_import.append(a)
 | 
						|
        if o == '-E':
 | 
						|
            error_if_any_missing = 1
 | 
						|
        if o == '-l':
 | 
						|
            addn_link.append(a)
 | 
						|
        if o == '-a':
 | 
						|
            apply(modulefinder.AddPackagePath, tuple(a.split("=", 2)))
 | 
						|
        if o == '-r':
 | 
						|
            f,r = a.split("=", 2)
 | 
						|
            replace_paths.append( (f,r) )
 | 
						|
 | 
						|
    # modules that are imported by the Python runtime
 | 
						|
    implicits = []
 | 
						|
    for module in ('site', 'warnings',):
 | 
						|
        if module not in exclude:
 | 
						|
            implicits.append(module)
 | 
						|
 | 
						|
    # default prefix and exec_prefix
 | 
						|
    if not exec_prefix:
 | 
						|
        if prefix:
 | 
						|
            exec_prefix = prefix
 | 
						|
        else:
 | 
						|
            exec_prefix = sys.exec_prefix
 | 
						|
    if not prefix:
 | 
						|
        prefix = sys.prefix
 | 
						|
 | 
						|
    # determine whether -p points to the Python source tree
 | 
						|
    ishome = os.path.exists(os.path.join(prefix, 'Python', 'ceval.c'))
 | 
						|
 | 
						|
    # locations derived from options
 | 
						|
    version = sys.version[:3]
 | 
						|
    if win:
 | 
						|
        extensions_c = 'frozen_extensions.c'
 | 
						|
    if ishome:
 | 
						|
        print "(Using Python source directory)"
 | 
						|
        binlib = exec_prefix
 | 
						|
        incldir = os.path.join(prefix, 'Include')
 | 
						|
        config_h_dir = exec_prefix
 | 
						|
        config_c_in = os.path.join(prefix, 'Modules', 'config.c.in')
 | 
						|
        frozenmain_c = os.path.join(prefix, 'Python', 'frozenmain.c')
 | 
						|
        makefile_in = os.path.join(exec_prefix, 'Makefile')
 | 
						|
        if win:
 | 
						|
            frozendllmain_c = os.path.join(exec_prefix, 'Pc\\frozen_dllmain.c')
 | 
						|
    else:
 | 
						|
        binlib = os.path.join(exec_prefix,
 | 
						|
                              'lib', 'python%s' % version, 'config')
 | 
						|
        incldir = os.path.join(prefix, 'include', 'python%s' % version)
 | 
						|
        config_h_dir = os.path.join(exec_prefix, 'include',
 | 
						|
                                    'python%s' % version)
 | 
						|
        config_c_in = os.path.join(binlib, 'config.c.in')
 | 
						|
        frozenmain_c = os.path.join(binlib, 'frozenmain.c')
 | 
						|
        makefile_in = os.path.join(binlib, 'Makefile')
 | 
						|
        frozendllmain_c = os.path.join(binlib, 'frozen_dllmain.c')
 | 
						|
    supp_sources = []
 | 
						|
    defines = []
 | 
						|
    includes = ['-I' + incldir, '-I' + config_h_dir]
 | 
						|
 | 
						|
    # sanity check of directories and files
 | 
						|
    check_dirs = [prefix, exec_prefix, binlib, incldir]
 | 
						|
    if not win:
 | 
						|
        # These are not directories on Windows.
 | 
						|
        check_dirs = check_dirs + extensions
 | 
						|
    for dir in check_dirs:
 | 
						|
        if not os.path.exists(dir):
 | 
						|
            usage('needed directory %s not found' % dir)
 | 
						|
        if not os.path.isdir(dir):
 | 
						|
            usage('%s: not a directory' % dir)
 | 
						|
    if win:
 | 
						|
        files = supp_sources + extensions # extensions are files on Windows.
 | 
						|
    else:
 | 
						|
        files = [config_c_in, makefile_in] + supp_sources
 | 
						|
    for file in supp_sources:
 | 
						|
        if not os.path.exists(file):
 | 
						|
            usage('needed file %s not found' % file)
 | 
						|
        if not os.path.isfile(file):
 | 
						|
            usage('%s: not a plain file' % file)
 | 
						|
    if not win:
 | 
						|
        for dir in extensions:
 | 
						|
            setup = os.path.join(dir, 'Setup')
 | 
						|
            if not os.path.exists(setup):
 | 
						|
                usage('needed file %s not found' % setup)
 | 
						|
            if not os.path.isfile(setup):
 | 
						|
                usage('%s: not a plain file' % setup)
 | 
						|
 | 
						|
    # check that enough arguments are passed
 | 
						|
    if not args:
 | 
						|
        usage('at least one filename argument required')
 | 
						|
 | 
						|
    # check that file arguments exist
 | 
						|
    for arg in args:
 | 
						|
        if arg == '-m':
 | 
						|
            break
 | 
						|
        # if user specified -m on the command line before _any_
 | 
						|
        # file names, then nothing should be checked (as the
 | 
						|
        # very first file should be a module name)
 | 
						|
        if modargs:
 | 
						|
            break
 | 
						|
        if not os.path.exists(arg):
 | 
						|
            usage('argument %s not found' % arg)
 | 
						|
        if not os.path.isfile(arg):
 | 
						|
            usage('%s: not a plain file' % arg)
 | 
						|
 | 
						|
    # process non-option arguments
 | 
						|
    scriptfile = args[0]
 | 
						|
    modules = args[1:]
 | 
						|
 | 
						|
    # derive target name from script name
 | 
						|
    base = os.path.basename(scriptfile)
 | 
						|
    base, ext = os.path.splitext(base)
 | 
						|
    if base:
 | 
						|
        if base != scriptfile:
 | 
						|
            target = base
 | 
						|
        else:
 | 
						|
            target = base + '.bin'
 | 
						|
 | 
						|
    # handle -o option
 | 
						|
    base_frozen_c = frozen_c
 | 
						|
    base_config_c = config_c
 | 
						|
    base_target = target
 | 
						|
    if odir and not os.path.isdir(odir):
 | 
						|
        try:
 | 
						|
            os.mkdir(odir)
 | 
						|
            print "Created output directory", odir
 | 
						|
        except os.error, msg:
 | 
						|
            usage('%s: mkdir failed (%s)' % (odir, str(msg)))
 | 
						|
    base = ''
 | 
						|
    if odir:
 | 
						|
        base = os.path.join(odir, '')
 | 
						|
        frozen_c = os.path.join(odir, frozen_c)
 | 
						|
        config_c = os.path.join(odir, config_c)
 | 
						|
        target = os.path.join(odir, target)
 | 
						|
        makefile = os.path.join(odir, makefile)
 | 
						|
        if win: extensions_c = os.path.join(odir, extensions_c)
 | 
						|
 | 
						|
    # Handle special entry point requirements
 | 
						|
    # (on Windows, some frozen programs do not use __main__, but
 | 
						|
    # import the module directly.  Eg, DLLs, Services, etc
 | 
						|
    custom_entry_point = None  # Currently only used on Windows
 | 
						|
    python_entry_is_main = 1   # Is the entry point called __main__?
 | 
						|
    # handle -s option on Windows
 | 
						|
    if win:
 | 
						|
        import winmakemakefile
 | 
						|
        try:
 | 
						|
            custom_entry_point, python_entry_is_main = \
 | 
						|
                winmakemakefile.get_custom_entry_point(subsystem)
 | 
						|
        except ValueError, why:
 | 
						|
            usage(why)
 | 
						|
 | 
						|
 | 
						|
    # Actual work starts here...
 | 
						|
 | 
						|
    # collect all modules of the program
 | 
						|
    dir = os.path.dirname(scriptfile)
 | 
						|
    path[0] = dir
 | 
						|
    mf = modulefinder.ModuleFinder(path, debug, exclude, replace_paths)
 | 
						|
 | 
						|
    if win and subsystem=='service':
 | 
						|
        # If a Windows service, then add the "built-in" module.
 | 
						|
        mod = mf.add_module("servicemanager")
 | 
						|
        mod.__file__="dummy.pyd" # really built-in to the resulting EXE
 | 
						|
 | 
						|
    for mod in implicits:
 | 
						|
        mf.import_hook(mod)
 | 
						|
    for mod in modules:
 | 
						|
        if mod == '-m':
 | 
						|
            modargs = 1
 | 
						|
            continue
 | 
						|
        if modargs:
 | 
						|
            if mod[-2:] == '.*':
 | 
						|
                mf.import_hook(mod[:-2], None, ["*"])
 | 
						|
            else:
 | 
						|
                mf.import_hook(mod)
 | 
						|
        else:
 | 
						|
            mf.load_file(mod)
 | 
						|
 | 
						|
    # Add the main script as either __main__, or the actual module name.
 | 
						|
    if python_entry_is_main:
 | 
						|
        mf.run_script(scriptfile)
 | 
						|
    else:
 | 
						|
        mf.load_file(scriptfile)
 | 
						|
 | 
						|
    if debug > 0:
 | 
						|
        mf.report()
 | 
						|
        print
 | 
						|
    dict = mf.modules
 | 
						|
 | 
						|
    if error_if_any_missing:
 | 
						|
        missing = mf.any_missing()
 | 
						|
        if missing:
 | 
						|
            sys.exit("There are some missing modules: %r" % missing)
 | 
						|
 | 
						|
    # generate output for frozen modules
 | 
						|
    files = makefreeze.makefreeze(base, dict, debug, custom_entry_point,
 | 
						|
                                  fail_import)
 | 
						|
 | 
						|
    # look for unfrozen modules (builtin and of unknown origin)
 | 
						|
    builtins = []
 | 
						|
    unknown = []
 | 
						|
    mods = dict.keys()
 | 
						|
    mods.sort()
 | 
						|
    for mod in mods:
 | 
						|
        if dict[mod].__code__:
 | 
						|
            continue
 | 
						|
        if not dict[mod].__file__:
 | 
						|
            builtins.append(mod)
 | 
						|
        else:
 | 
						|
            unknown.append(mod)
 | 
						|
 | 
						|
    # search for unknown modules in extensions directories (not on Windows)
 | 
						|
    addfiles = []
 | 
						|
    frozen_extensions = [] # Windows list of modules.
 | 
						|
    if unknown or (not win and builtins):
 | 
						|
        if not win:
 | 
						|
            addfiles, addmods = \
 | 
						|
                      checkextensions.checkextensions(unknown+builtins,
 | 
						|
                                                      extensions)
 | 
						|
            for mod in addmods:
 | 
						|
                if mod in unknown:
 | 
						|
                    unknown.remove(mod)
 | 
						|
                    builtins.append(mod)
 | 
						|
        else:
 | 
						|
            # Do the windows thang...
 | 
						|
            import checkextensions_win32
 | 
						|
            # Get a list of CExtension instances, each describing a module
 | 
						|
            # (including its source files)
 | 
						|
            frozen_extensions = checkextensions_win32.checkextensions(
 | 
						|
                unknown, extensions, prefix)
 | 
						|
            for mod in frozen_extensions:
 | 
						|
                unknown.remove(mod.name)
 | 
						|
 | 
						|
    # report unknown modules
 | 
						|
    if unknown:
 | 
						|
        sys.stderr.write('Warning: unknown modules remain: %s\n' %
 | 
						|
                         ' '.join(unknown))
 | 
						|
 | 
						|
    # windows gets different treatment
 | 
						|
    if win:
 | 
						|
        # Taking a shortcut here...
 | 
						|
        import winmakemakefile, checkextensions_win32
 | 
						|
        checkextensions_win32.write_extension_table(extensions_c,
 | 
						|
                                                    frozen_extensions)
 | 
						|
        # Create a module definition for the bootstrap C code.
 | 
						|
        xtras = [frozenmain_c, os.path.basename(frozen_c),
 | 
						|
                 frozendllmain_c, os.path.basename(extensions_c)] + files
 | 
						|
        maindefn = checkextensions_win32.CExtension( '__main__', xtras )
 | 
						|
        frozen_extensions.append( maindefn )
 | 
						|
        outfp = open(makefile, 'w')
 | 
						|
        try:
 | 
						|
            winmakemakefile.makemakefile(outfp,
 | 
						|
                                         locals(),
 | 
						|
                                         frozen_extensions,
 | 
						|
                                         os.path.basename(target))
 | 
						|
        finally:
 | 
						|
            outfp.close()
 | 
						|
        return
 | 
						|
 | 
						|
    # generate config.c and Makefile
 | 
						|
    builtins.sort()
 | 
						|
    infp = open(config_c_in)
 | 
						|
    outfp = bkfile.open(config_c, 'w')
 | 
						|
    try:
 | 
						|
        makeconfig.makeconfig(infp, outfp, builtins)
 | 
						|
    finally:
 | 
						|
        outfp.close()
 | 
						|
    infp.close()
 | 
						|
 | 
						|
    cflags = ['$(OPT)']
 | 
						|
    cppflags = defines + includes
 | 
						|
    libs = [os.path.join(binlib, 'libpython$(VERSION).a')]
 | 
						|
 | 
						|
    somevars = {}
 | 
						|
    if os.path.exists(makefile_in):
 | 
						|
        makevars = parsesetup.getmakevars(makefile_in)
 | 
						|
        for key in makevars.keys():
 | 
						|
            somevars[key] = makevars[key]
 | 
						|
 | 
						|
    somevars['CFLAGS'] = ' '.join(cflags) # override
 | 
						|
    somevars['CPPFLAGS'] = ' '.join(cppflags) # override
 | 
						|
    files = [base_config_c, base_frozen_c] + \
 | 
						|
            files + supp_sources +  addfiles + libs + \
 | 
						|
            ['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)']
 | 
						|
 | 
						|
    outfp = bkfile.open(makefile, 'w')
 | 
						|
    try:
 | 
						|
        makemakefile.makemakefile(outfp, somevars, files, base_target)
 | 
						|
    finally:
 | 
						|
        outfp.close()
 | 
						|
 | 
						|
    # Done!
 | 
						|
 | 
						|
    if odir:
 | 
						|
        print 'Now run "make" in', odir,
 | 
						|
        print 'to build the target:', base_target
 | 
						|
    else:
 | 
						|
        print 'Now run "make" to build the target:', base_target
 | 
						|
 | 
						|
 | 
						|
# Print usage message and exit
 | 
						|
 | 
						|
def usage(msg):
 | 
						|
    sys.stdout = sys.stderr
 | 
						|
    print "Error:", msg
 | 
						|
    print "Use ``%s -h'' for help" % sys.argv[0]
 | 
						|
    sys.exit(2)
 | 
						|
 | 
						|
 | 
						|
main()
 |