]> granicus.if.org Git - python/commitdiff
Big changes by Mark Hammond to support freezing with DLLs on Windows.
authorGuido van Rossum <guido@python.org>
Fri, 20 Mar 1998 17:37:24 +0000 (17:37 +0000)
committerGuido van Rossum <guido@python.org>
Fri, 20 Mar 1998 17:37:24 +0000 (17:37 +0000)
Tools/freeze/freeze.py
Tools/freeze/makefreeze.py
Tools/freeze/modulefinder.py
Tools/freeze/winmakemakefile.py

index 3de6bdbc346f8c42ed4bcba0b51bf4605284a27f..ce0a454f558c85e79b32db769311f53a1c92d23c 100755 (executable)
@@ -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()
index 88265b0b5d84fbf5c7eb87b91e3da8f83460b8b6..25c52bcf5fd1924cb02e949c6cd16dbe9e0b4918 100644 (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.
index 3aa69410a68c52bb368f4c2791fedc51b8a947d5..408c2bafe34ec04ce92f588168e8b803b46148cc 100644 (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)
 
index dde241bddcbaa1688b1c8ec7b619b8f9a9c30662..2c13ad331d68b4d7dcdb5a213dd036a19c72fde0 100644 (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
-
-    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