Big changes by Mark Hammond to support freezing with DLLs on Windows.
This commit is contained in:
parent
11af5a4469
commit
78fc3634cb
|
@ -28,6 +28,8 @@ Options:
|
|||
|
||||
-m: Additional arguments are module names instead of filenames.
|
||||
|
||||
-l file: Pass the file to the linker (windows only)
|
||||
|
||||
-d: Debugging mode for the module finder.
|
||||
|
||||
-q: Make the module finder totally quiet.
|
||||
|
@ -38,8 +40,11 @@ Options:
|
|||
(For debugging only -- on a win32 platform, win32 behaviour
|
||||
is automatic.)
|
||||
|
||||
-s subsystem: Specify the subsystem; 'windows' or 'console' (default).
|
||||
(For Windows only.)
|
||||
-x module Exclude the specified module.
|
||||
|
||||
-s subsystem: Specify the subsystem (For Windows only.);
|
||||
'console' (default), 'windows', 'service' or 'com_dll'
|
||||
|
||||
|
||||
Arguments:
|
||||
|
||||
|
@ -87,12 +92,17 @@ def main():
|
|||
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'
|
||||
|
||||
# default the exclude list for each platform
|
||||
# if win: exclude = exclude + ['dos', 'dospath', 'mac', 'macpath', 'MACFS', 'posix', 'os2']
|
||||
|
||||
# modules that are imported by the Python runtime
|
||||
implicits = ["site", "exceptions"]
|
||||
|
||||
|
@ -105,7 +115,7 @@ def main():
|
|||
|
||||
# parse command line
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'de:hmo:p:P:qs:w')
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'de:hmo:p:P:qs:wx:l:')
|
||||
except getopt.error, msg:
|
||||
usage('getopt error: ' + str(msg))
|
||||
|
||||
|
@ -134,6 +144,10 @@ def main():
|
|||
if not win:
|
||||
usage("-s subsystem option only on Windows")
|
||||
subsystem = a
|
||||
if o == '-x':
|
||||
exclude.append(a)
|
||||
if o == '-l':
|
||||
addn_link.append(a)
|
||||
|
||||
# default prefix and exec_prefix
|
||||
if not exec_prefix:
|
||||
|
@ -156,6 +170,7 @@ def main():
|
|||
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, 'Modules', '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')
|
||||
|
@ -198,12 +213,15 @@ def main():
|
|||
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)
|
||||
if modargs:
|
||||
break
|
||||
|
||||
# process non-option arguments
|
||||
scriptfile = args[0]
|
||||
|
@ -234,12 +252,32 @@ def main():
|
|||
target = os.path.join(odir, target)
|
||||
makefile = os.path.join(odir, makefile)
|
||||
|
||||
# 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)
|
||||
mf = modulefinder.ModuleFinder(path, debug, exclude)
|
||||
|
||||
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:
|
||||
|
@ -253,7 +291,16 @@ def main():
|
|||
mf.import_hook(mod)
|
||||
else:
|
||||
mf.load_file(mod)
|
||||
mf.run_script(scriptfile)
|
||||
|
||||
# Add the main script as either __main__, or the actual module name.
|
||||
if python_entry_is_main:
|
||||
mf.run_script(scriptfile)
|
||||
else:
|
||||
if modargs:
|
||||
mf.import_hook(scriptfile)
|
||||
else:
|
||||
mf.load_file(scriptfile)
|
||||
|
||||
if debug > 0:
|
||||
mf.report()
|
||||
print
|
||||
|
@ -267,10 +314,7 @@ def main():
|
|||
backup = None
|
||||
outfp = open(frozen_c, 'w')
|
||||
try:
|
||||
makefreeze.makefreeze(outfp, dict, debug)
|
||||
if win and subsystem == 'windows':
|
||||
import winmakemakefile
|
||||
outfp.write(winmakemakefile.WINMAINTEMPLATE)
|
||||
makefreeze.makefreeze(outfp, dict, debug, custom_entry_point)
|
||||
finally:
|
||||
outfp.close()
|
||||
if backup:
|
||||
|
@ -294,12 +338,29 @@ def main():
|
|||
|
||||
# search for unknown modules in extensions directories (not on Windows)
|
||||
addfiles = []
|
||||
if unknown and not win:
|
||||
addfiles, addmods = \
|
||||
checkextensions.checkextensions(unknown, extensions)
|
||||
for mod in addmods:
|
||||
unknown.remove(mod)
|
||||
builtins = builtins + addmods
|
||||
addmoddefns = [] # Windows list of modules.
|
||||
if unknown:
|
||||
if not win:
|
||||
addfiles, addmods = \
|
||||
checkextensions.checkextensions(unknown, extensions)
|
||||
for mod in addmods:
|
||||
unknown.remove(mod)
|
||||
builtins = builtins + addmods
|
||||
else:
|
||||
# Do the windows thang...
|
||||
import checkextensions_win32
|
||||
# Get a list of CExtension instances, each describing a module
|
||||
# (including its source files)
|
||||
addmoddefns = checkextensions_win32.checkextensions(unknown, extensions)
|
||||
maindefn = checkextensions_win32.CExtension( '__main__',
|
||||
[frozenmain_c, os.path.basename(frozen_c),frozendllmain_c])
|
||||
|
||||
for mod in addmoddefns:
|
||||
unknown.remove(mod.name)
|
||||
builtins.append(mod.name)
|
||||
|
||||
addmoddefns.append( maindefn )
|
||||
|
||||
|
||||
# report unknown modules
|
||||
if unknown:
|
||||
|
@ -314,8 +375,7 @@ def main():
|
|||
try:
|
||||
winmakemakefile.makemakefile(outfp,
|
||||
locals(),
|
||||
[frozenmain_c,
|
||||
os.path.basename(frozen_c)],
|
||||
addmoddefns,
|
||||
os.path.basename(target))
|
||||
finally:
|
||||
outfp.close()
|
||||
|
|
|
@ -12,7 +12,9 @@ static struct _frozen _PyImport_FrozenModules[] = {
|
|||
trailer = """\
|
||||
{0, 0, 0} /* sentinel */
|
||||
};
|
||||
"""
|
||||
|
||||
default_entry_point = """
|
||||
int
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
|
@ -24,7 +26,8 @@ main(argc, argv)
|
|||
|
||||
"""
|
||||
|
||||
def makefreeze(outfp, dict, debug=0):
|
||||
def makefreeze(outfp, dict, debug=0, entry_point = None):
|
||||
if entry_point is None: entry_point = default_entry_point
|
||||
done = []
|
||||
mods = dict.keys()
|
||||
mods.sort()
|
||||
|
@ -47,6 +50,8 @@ def makefreeze(outfp, dict, debug=0):
|
|||
for mod, mangled, size in done:
|
||||
outfp.write('\t{"%s", M_%s, %d},\n' % (mod, mangled, size))
|
||||
outfp.write(trailer)
|
||||
outfp.write(entry_point)
|
||||
|
||||
|
||||
|
||||
# Write a C initializer for a module containing the frozen python code.
|
||||
|
|
|
@ -8,6 +8,16 @@ import re
|
|||
import string
|
||||
import sys
|
||||
|
||||
if sys.platform=="win32":
|
||||
# On Windows, we can locate modules in the registry with
|
||||
# the help of the win32api package.
|
||||
try:
|
||||
import win32api
|
||||
except ImportError:
|
||||
print "The win32api module is not available - modules listed"
|
||||
print "in the registry will not be found."
|
||||
win32api = None
|
||||
|
||||
|
||||
IMPORT_NAME = dis.opname.index('IMPORT_NAME')
|
||||
IMPORT_FROM = dis.opname.index('IMPORT_FROM')
|
||||
|
@ -33,7 +43,7 @@ class Module:
|
|||
|
||||
class ModuleFinder:
|
||||
|
||||
def __init__(self, path=None, debug=0):
|
||||
def __init__(self, path=None, debug=0, excludes = []):
|
||||
if path is None:
|
||||
path = sys.path
|
||||
self.path = path
|
||||
|
@ -41,6 +51,7 @@ class ModuleFinder:
|
|||
self.badmodules = {}
|
||||
self.debug = debug
|
||||
self.indent = 0
|
||||
self.excludes = excludes
|
||||
|
||||
def msg(self, level, str, *args):
|
||||
if level <= self.debug:
|
||||
|
@ -219,7 +230,7 @@ class ModuleFinder:
|
|||
self.msgout(2, "load_module ->", m)
|
||||
return m
|
||||
if type == imp.PY_SOURCE:
|
||||
co = compile(fp.read(), pathname, 'exec')
|
||||
co = compile(fp.read()+'\n', pathname, 'exec')
|
||||
elif type == imp.PY_COMPILED:
|
||||
if fp.read(4) != imp.get_magic():
|
||||
self.msgout(2, "raise ImportError: Bad magic number", pathname)
|
||||
|
@ -289,9 +300,26 @@ class ModuleFinder:
|
|||
return m
|
||||
|
||||
def find_module(self, name, path):
|
||||
if name in self.excludes:
|
||||
self.msgout(3, "find_module -> Excluded")
|
||||
raise ImportError, name
|
||||
|
||||
if path is None:
|
||||
if name in sys.builtin_module_names:
|
||||
return (None, None, ("", "", imp.C_BUILTIN))
|
||||
|
||||
# Emulate the Registered Module support on Windows.
|
||||
if sys.platform=="win32" and win32api is not None:
|
||||
HKEY_LOCAL_MACHINE = 0x80000002
|
||||
try:
|
||||
pathname = win32api.RegQueryValue(HKEY_LOCAL_MACHINE, "Software\\Python\\PythonCore\\%s\\Modules\\%s" % (sys.winver, name))
|
||||
fp = open(pathname, "rb")
|
||||
# XXX - To do - remove the hard code of C_EXTENSION.
|
||||
stuff = "", "rb", imp.C_EXTENSION
|
||||
return fp, pathname, stuff
|
||||
except win32api.error:
|
||||
pass
|
||||
|
||||
path = self.path
|
||||
return imp.find_module(name, path)
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import sys, os, string
|
||||
|
||||
# Template used then the program is a GUI program
|
||||
WINMAINTEMPLATE = """
|
||||
#include <windows.h>
|
||||
|
||||
|
@ -10,10 +11,36 @@ int WINAPI WinMain(
|
|||
int nCmdShow // show state of window
|
||||
)
|
||||
{
|
||||
return main(__argc, __argv);
|
||||
PyImport_FrozenModules = _PyImport_FrozenModules;
|
||||
return Py_FrozenMain(__argc, __argv);
|
||||
}
|
||||
"""
|
||||
|
||||
SERVICETEMPLATE = """
|
||||
extern int PythonService_main(int, char **);
|
||||
|
||||
int main( int argc, char **argv)
|
||||
{
|
||||
PyImport_FrozenModules = _PyImport_FrozenModules;
|
||||
return PythonService_main(argc, argv);
|
||||
}
|
||||
"""
|
||||
|
||||
subsystem_details = {
|
||||
# -s flag : (C entry point template), (is it __main__?), (is it a DLL?)
|
||||
'console' : (None, 1, 0),
|
||||
'windows' : (WINMAINTEMPLATE, 1, 0),
|
||||
'service' : (SERVICETEMPLATE, 0, 0),
|
||||
'com_dll' : ("", 0, 1),
|
||||
}
|
||||
|
||||
def get_custom_entry_point(subsystem):
|
||||
try:
|
||||
return subsystem_details[subsystem][:2]
|
||||
except KeyError:
|
||||
raise ValueError, "The subsystem %s is not known" % subsystem
|
||||
|
||||
|
||||
def makemakefile(outfp, vars, files, target):
|
||||
save = sys.stdout
|
||||
try:
|
||||
|
@ -22,7 +49,7 @@ def makemakefile(outfp, vars, files, target):
|
|||
finally:
|
||||
sys.stdout = save
|
||||
|
||||
def realwork(vars, files, target):
|
||||
def realwork(vars, moddefns, target):
|
||||
print "# Makefile for Windows (NT or 95) generated by freeze.py script"
|
||||
print
|
||||
print 'target = %s' % target
|
||||
|
@ -30,35 +57,69 @@ def realwork(vars, files, target):
|
|||
# XXX The following line is fishy and may need manual fixing
|
||||
print 'pythonlib = "%s"' % (vars['exec_prefix'] +
|
||||
"/pcbuild/release/python15.lib")
|
||||
print "subsystem =", vars['subsystem']
|
||||
|
||||
# We only ever write one "entry point" symbol - either
|
||||
# "main" or "WinMain". Therefore, there is no need to
|
||||
# pass a subsystem switch to the linker as it works it
|
||||
# out all by itself. However, the subsystem _does_ determine
|
||||
# the file extension and additional linker flags.
|
||||
target_link_flags = ""
|
||||
target_ext = ".exe"
|
||||
if subsystem_details[vars['subsystem']][2]:
|
||||
target_link_flags = "-dll"
|
||||
target_ext = ".dll"
|
||||
|
||||
print "cdl = /MD" # XXX - Should this come from vars? User may have specific requirements...
|
||||
print
|
||||
print "all: $(target).exe"
|
||||
print "all: $(target)%s" % (target_ext)
|
||||
print
|
||||
|
||||
objects = []
|
||||
for file in files:
|
||||
base = os.path.basename(file)
|
||||
base, ext = os.path.splitext(base)
|
||||
objects.append(base + ".obj")
|
||||
print '%s.obj: "%s"' % (base, file)
|
||||
print "\t$(CC) -c $(cdl)",
|
||||
print "-I$(pythonhome)/Include -I$(pythonhome)/PC \\"
|
||||
print "\t\t$(cflags) $(cdebug) $(cinclude) \\"
|
||||
print '\t\t"%s"' % file
|
||||
print
|
||||
libs = ["shell32.lib", "comdlg32.lib", "wsock32.lib", "user32.lib"]
|
||||
for moddefn in moddefns:
|
||||
print "# Module", moddefn.name
|
||||
for file in moddefn.sourceFiles:
|
||||
base = os.path.basename(file)
|
||||
base, ext = os.path.splitext(base)
|
||||
objects.append(base + ".obj")
|
||||
print '%s.obj: "%s"' % (base, file)
|
||||
print "\t@$(CC) -c -nologo $(cdl) /D BUILD_FREEZE",
|
||||
print "-I$(pythonhome)/Include -I$(pythonhome)/PC \\"
|
||||
print "\t\t$(cflags) $(cdebug) $(cinclude) \\"
|
||||
extra = moddefn.GetCompilerOptions()
|
||||
if extra:
|
||||
print "\t\t%s \\" % (string.join(extra),)
|
||||
print '\t\t"%s"' % file
|
||||
print
|
||||
|
||||
print "$(target).exe:",
|
||||
for obj in objects: print obj,
|
||||
print
|
||||
print "\tlink -out:$(target).exe",
|
||||
for obj in objects: print obj,
|
||||
print "\\"
|
||||
print "\t\t$(pythonlib) $(lcustom) shell32.lib comdlg32.lib wsock32.lib \\"
|
||||
print "\t\t-subsystem:$(subsystem) $(resources)"
|
||||
# Add .lib files this module needs
|
||||
for modlib in moddefn.GetLinkerLibs():
|
||||
if modlib not in libs:
|
||||
libs.append(modlib)
|
||||
|
||||
print "ADDN_LINK_FILES=",
|
||||
for addn in vars['addn_link']: print '"%s"' % (addn),
|
||||
print ; print
|
||||
|
||||
print "OBJS=",
|
||||
for obj in objects: print '"%s"' % (obj),
|
||||
print ; print
|
||||
|
||||
print "LIBS=",
|
||||
for lib in libs: print '"%s"' % (lib),
|
||||
print ; print
|
||||
|
||||
print "$(target)%s: $(OBJS)" % (target_ext)
|
||||
print "\tlink -out:$(target)%s %s" % (target_ext, target_link_flags),
|
||||
print "\t$(OBJS) \\"
|
||||
print "\t$(LIBS) \\"
|
||||
print "\t$(ADDN_LINK_FILES) \\"
|
||||
print "\t\t$(pythonlib) $(lcustom)\\"
|
||||
print "\t\t$(resources)"
|
||||
print
|
||||
print "clean:"
|
||||
print "\t\t-rm *.obj"
|
||||
print "\t\t-rm $(target).exe"
|
||||
print "\t-rm -f *.obj"
|
||||
print "\t-rm -f $(target).exe"
|
||||
|
||||
# Local Variables:
|
||||
# indent-tabs-mode: nil
|
||||
|
|
Loading…
Reference in New Issue