summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>1998-03-20 17:37:24 +0000
committerGuido van Rossum <guido@python.org>1998-03-20 17:37:24 +0000
commit78fc3634cbfd65a6be8abfd1b7fc7cbd0ccbfb39 (patch)
tree900727a59c47577d9704abb126496e351be08a8a
parent11af5a446961aa4f7640196b9f6d6e9b03933d91 (diff)
downloadcpython-git-78fc3634cbfd65a6be8abfd1b7fc7cbd0ccbfb39.tar.gz
Big changes by Mark Hammond to support freezing with DLLs on Windows.
-rwxr-xr-xTools/freeze/freeze.py98
-rw-r--r--Tools/freeze/makefreeze.py7
-rw-r--r--Tools/freeze/modulefinder.py32
-rw-r--r--Tools/freeze/winmakemakefile.py111
4 files changed, 201 insertions, 47 deletions
diff --git a/Tools/freeze/freeze.py b/Tools/freeze/freeze.py
index 3de6bdbc34..ce0a454f55 100755
--- a/Tools/freeze/freeze.py
+++ b/Tools/freeze/freeze.py
@@ -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()
diff --git a/Tools/freeze/makefreeze.py b/Tools/freeze/makefreeze.py
index 88265b0b5d..25c52bcf5f 100644
--- a/Tools/freeze/makefreeze.py
+++ b/Tools/freeze/makefreeze.py
@@ -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.
diff --git a/Tools/freeze/modulefinder.py b/Tools/freeze/modulefinder.py
index 3aa69410a6..408c2bafe3 100644
--- a/Tools/freeze/modulefinder.py
+++ b/Tools/freeze/modulefinder.py
@@ -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)
diff --git a/Tools/freeze/winmakemakefile.py b/Tools/freeze/winmakemakefile.py
index dde241bddc..2c13ad331d 100644
--- a/Tools/freeze/winmakemakefile.py
+++ b/Tools/freeze/winmakemakefile.py
@@ -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
-
- 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)"
+ 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
+
+ # 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