Kaydet (Commit) 78fc3634 authored tarafından Guido van Rossum's avatar Guido van Rossum

Big changes by Mark Hammond to support freezing with DLLs on Windows.

üst 11af5a44
...@@ -28,6 +28,8 @@ Options: ...@@ -28,6 +28,8 @@ Options:
-m: Additional arguments are module names instead of filenames. -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. -d: Debugging mode for the module finder.
-q: Make the module finder totally quiet. -q: Make the module finder totally quiet.
...@@ -38,8 +40,11 @@ Options: ...@@ -38,8 +40,11 @@ Options:
(For debugging only -- on a win32 platform, win32 behaviour (For debugging only -- on a win32 platform, win32 behaviour
is automatic.) is automatic.)
-s subsystem: Specify the subsystem; 'windows' or 'console' (default). -x module Exclude the specified module.
(For Windows only.)
-s subsystem: Specify the subsystem (For Windows only.);
'console' (default), 'windows', 'service' or 'com_dll'
Arguments: Arguments:
...@@ -87,12 +92,17 @@ def main(): ...@@ -87,12 +92,17 @@ def main():
prefix = None # settable with -p option prefix = None # settable with -p option
exec_prefix = None # settable with -P option exec_prefix = None # settable with -P option
extensions = [] extensions = []
exclude = [] # settable with -x option
addn_link = [] # settable with -l, but only honored under Windows.
path = sys.path[:] path = sys.path[:]
modargs = 0 modargs = 0
debug = 1 debug = 1
odir = '' odir = ''
win = sys.platform[:3] == 'win' 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 # modules that are imported by the Python runtime
implicits = ["site", "exceptions"] implicits = ["site", "exceptions"]
...@@ -105,7 +115,7 @@ def main(): ...@@ -105,7 +115,7 @@ def main():
# parse command line # parse command line
try: 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: except getopt.error, msg:
usage('getopt error: ' + str(msg)) usage('getopt error: ' + str(msg))
...@@ -134,6 +144,10 @@ def main(): ...@@ -134,6 +144,10 @@ def main():
if not win: if not win:
usage("-s subsystem option only on Windows") usage("-s subsystem option only on Windows")
subsystem = a subsystem = a
if o == '-x':
exclude.append(a)
if o == '-l':
addn_link.append(a)
# default prefix and exec_prefix # default prefix and exec_prefix
if not exec_prefix: if not exec_prefix:
...@@ -156,6 +170,7 @@ def main(): ...@@ -156,6 +170,7 @@ def main():
config_c_in = os.path.join(prefix, 'Modules', 'config.c.in') config_c_in = os.path.join(prefix, 'Modules', 'config.c.in')
frozenmain_c = os.path.join(prefix, 'Python', 'frozenmain.c') frozenmain_c = os.path.join(prefix, 'Python', 'frozenmain.c')
makefile_in = os.path.join(exec_prefix, 'Modules', 'Makefile') makefile_in = os.path.join(exec_prefix, 'Modules', 'Makefile')
if win: frozendllmain_c = os.path.join(exec_prefix, 'Pc\\frozen_dllmain.c')
else: else:
binlib = os.path.join(exec_prefix, binlib = os.path.join(exec_prefix,
'lib', 'python%s' % version, 'config') 'lib', 'python%s' % version, 'config')
...@@ -198,12 +213,15 @@ def main(): ...@@ -198,12 +213,15 @@ def main():
for arg in args: for arg in args:
if arg == '-m': if arg == '-m':
break 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): if not os.path.exists(arg):
usage('argument %s not found' % arg) usage('argument %s not found' % arg)
if not os.path.isfile(arg): if not os.path.isfile(arg):
usage('%s: not a plain file' % arg) usage('%s: not a plain file' % arg)
if modargs:
break
# process non-option arguments # process non-option arguments
scriptfile = args[0] scriptfile = args[0]
...@@ -234,12 +252,32 @@ def main(): ...@@ -234,12 +252,32 @@ def main():
target = os.path.join(odir, target) target = os.path.join(odir, target)
makefile = os.path.join(odir, makefile) 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... # Actual work starts here...
# collect all modules of the program # collect all modules of the program
dir = os.path.dirname(scriptfile) dir = os.path.dirname(scriptfile)
path[0] = dir 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: for mod in implicits:
mf.import_hook(mod) mf.import_hook(mod)
for mod in modules: for mod in modules:
...@@ -253,7 +291,16 @@ def main(): ...@@ -253,7 +291,16 @@ def main():
mf.import_hook(mod) mf.import_hook(mod)
else: else:
mf.load_file(mod) 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) mf.run_script(scriptfile)
else:
if modargs:
mf.import_hook(scriptfile)
else:
mf.load_file(scriptfile)
if debug > 0: if debug > 0:
mf.report() mf.report()
print print
...@@ -267,10 +314,7 @@ def main(): ...@@ -267,10 +314,7 @@ def main():
backup = None backup = None
outfp = open(frozen_c, 'w') outfp = open(frozen_c, 'w')
try: try:
makefreeze.makefreeze(outfp, dict, debug) makefreeze.makefreeze(outfp, dict, debug, custom_entry_point)
if win and subsystem == 'windows':
import winmakemakefile
outfp.write(winmakemakefile.WINMAINTEMPLATE)
finally: finally:
outfp.close() outfp.close()
if backup: if backup:
...@@ -294,12 +338,29 @@ def main(): ...@@ -294,12 +338,29 @@ def main():
# search for unknown modules in extensions directories (not on Windows) # search for unknown modules in extensions directories (not on Windows)
addfiles = [] addfiles = []
if unknown and not win: addmoddefns = [] # Windows list of modules.
if unknown:
if not win:
addfiles, addmods = \ addfiles, addmods = \
checkextensions.checkextensions(unknown, extensions) checkextensions.checkextensions(unknown, extensions)
for mod in addmods: for mod in addmods:
unknown.remove(mod) unknown.remove(mod)
builtins = builtins + addmods 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 # report unknown modules
if unknown: if unknown:
...@@ -314,8 +375,7 @@ def main(): ...@@ -314,8 +375,7 @@ def main():
try: try:
winmakemakefile.makemakefile(outfp, winmakemakefile.makemakefile(outfp,
locals(), locals(),
[frozenmain_c, addmoddefns,
os.path.basename(frozen_c)],
os.path.basename(target)) os.path.basename(target))
finally: finally:
outfp.close() outfp.close()
......
...@@ -12,7 +12,9 @@ static struct _frozen _PyImport_FrozenModules[] = { ...@@ -12,7 +12,9 @@ static struct _frozen _PyImport_FrozenModules[] = {
trailer = """\ trailer = """\
{0, 0, 0} /* sentinel */ {0, 0, 0} /* sentinel */
}; };
"""
default_entry_point = """
int int
main(argc, argv) main(argc, argv)
int argc; int argc;
...@@ -24,7 +26,8 @@ main(argc, argv) ...@@ -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 = [] done = []
mods = dict.keys() mods = dict.keys()
mods.sort() mods.sort()
...@@ -47,6 +50,8 @@ def makefreeze(outfp, dict, debug=0): ...@@ -47,6 +50,8 @@ def makefreeze(outfp, dict, debug=0):
for mod, mangled, size in done: for mod, mangled, size in done:
outfp.write('\t{"%s", M_%s, %d},\n' % (mod, mangled, size)) outfp.write('\t{"%s", M_%s, %d},\n' % (mod, mangled, size))
outfp.write(trailer) outfp.write(trailer)
outfp.write(entry_point)
# Write a C initializer for a module containing the frozen python code. # Write a C initializer for a module containing the frozen python code.
......
...@@ -8,6 +8,16 @@ import re ...@@ -8,6 +8,16 @@ import re
import string import string
import sys 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_NAME = dis.opname.index('IMPORT_NAME')
IMPORT_FROM = dis.opname.index('IMPORT_FROM') IMPORT_FROM = dis.opname.index('IMPORT_FROM')
...@@ -33,7 +43,7 @@ class Module: ...@@ -33,7 +43,7 @@ class Module:
class ModuleFinder: class ModuleFinder:
def __init__(self, path=None, debug=0): def __init__(self, path=None, debug=0, excludes = []):
if path is None: if path is None:
path = sys.path path = sys.path
self.path = path self.path = path
...@@ -41,6 +51,7 @@ class ModuleFinder: ...@@ -41,6 +51,7 @@ class ModuleFinder:
self.badmodules = {} self.badmodules = {}
self.debug = debug self.debug = debug
self.indent = 0 self.indent = 0
self.excludes = excludes
def msg(self, level, str, *args): def msg(self, level, str, *args):
if level <= self.debug: if level <= self.debug:
...@@ -219,7 +230,7 @@ class ModuleFinder: ...@@ -219,7 +230,7 @@ class ModuleFinder:
self.msgout(2, "load_module ->", m) self.msgout(2, "load_module ->", m)
return m return m
if type == imp.PY_SOURCE: if type == imp.PY_SOURCE:
co = compile(fp.read(), pathname, 'exec') co = compile(fp.read()+'\n', pathname, 'exec')
elif type == imp.PY_COMPILED: elif type == imp.PY_COMPILED:
if fp.read(4) != imp.get_magic(): if fp.read(4) != imp.get_magic():
self.msgout(2, "raise ImportError: Bad magic number", pathname) self.msgout(2, "raise ImportError: Bad magic number", pathname)
...@@ -289,9 +300,26 @@ class ModuleFinder: ...@@ -289,9 +300,26 @@ class ModuleFinder:
return m return m
def find_module(self, name, path): 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 path is None:
if name in sys.builtin_module_names: if name in sys.builtin_module_names:
return (None, None, ("", "", imp.C_BUILTIN)) 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 path = self.path
return imp.find_module(name, path) return imp.find_module(name, path)
......
import sys, os, string import sys, os, string
# Template used then the program is a GUI program
WINMAINTEMPLATE = """ WINMAINTEMPLATE = """
#include <windows.h> #include <windows.h>
...@@ -10,10 +11,36 @@ int WINAPI WinMain( ...@@ -10,10 +11,36 @@ int WINAPI WinMain(
int nCmdShow // show state of window 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): def makemakefile(outfp, vars, files, target):
save = sys.stdout save = sys.stdout
try: try:
...@@ -22,7 +49,7 @@ def makemakefile(outfp, vars, files, target): ...@@ -22,7 +49,7 @@ def makemakefile(outfp, vars, files, target):
finally: finally:
sys.stdout = save 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 "# Makefile for Windows (NT or 95) generated by freeze.py script"
print print
print 'target = %s' % target print 'target = %s' % target
...@@ -30,35 +57,69 @@ def realwork(vars, files, target): ...@@ -30,35 +57,69 @@ def realwork(vars, files, target):
# XXX The following line is fishy and may need manual fixing # XXX The following line is fishy and may need manual fixing
print 'pythonlib = "%s"' % (vars['exec_prefix'] + print 'pythonlib = "%s"' % (vars['exec_prefix'] +
"/pcbuild/release/python15.lib") "/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
print "all: $(target).exe" print "all: $(target)%s" % (target_ext)
print print
objects = [] objects = []
for file in files: 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 = os.path.basename(file)
base, ext = os.path.splitext(base) base, ext = os.path.splitext(base)
objects.append(base + ".obj") objects.append(base + ".obj")
print '%s.obj: "%s"' % (base, file) print '%s.obj: "%s"' % (base, file)
print "\t$(CC) -c $(cdl)", print "\t@$(CC) -c -nologo $(cdl) /D BUILD_FREEZE",
print "-I$(pythonhome)/Include -I$(pythonhome)/PC \\" print "-I$(pythonhome)/Include -I$(pythonhome)/PC \\"
print "\t\t$(cflags) $(cdebug) $(cinclude) \\" print "\t\t$(cflags) $(cdebug) $(cinclude) \\"
extra = moddefn.GetCompilerOptions()
if extra:
print "\t\t%s \\" % (string.join(extra),)
print '\t\t"%s"' % file print '\t\t"%s"' % file
print print
print "$(target).exe:", # Add .lib files this module needs
for obj in objects: print obj, for modlib in moddefn.GetLinkerLibs():
print if modlib not in libs:
print "\tlink -out:$(target).exe", libs.append(modlib)
for obj in objects: print obj,
print "\\" print "ADDN_LINK_FILES=",
print "\t\t$(pythonlib) $(lcustom) shell32.lib comdlg32.lib wsock32.lib \\" for addn in vars['addn_link']: print '"%s"' % (addn),
print "\t\t-subsystem:$(subsystem) $(resources)" 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
print "clean:" print "clean:"
print "\t\t-rm *.obj" print "\t-rm -f *.obj"
print "\t\t-rm $(target).exe" print "\t-rm -f $(target).exe"
# Local Variables: # Local Variables:
# indent-tabs-mode: nil # indent-tabs-mode: nil
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment