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

This commit is contained in:
Guido van Rossum 1998-03-20 17:37:24 +00:00
parent 11af5a4469
commit 78fc3634cb
4 changed files with 200 additions and 46 deletions

View File

@ -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()

View File

@ -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.

View File

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

View File

@ -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