Additions for Mark Hammond's Win32 specific hacks.

This commit is contained in:
Guido van Rossum 1998-05-19 20:18:37 +00:00
parent e194beb829
commit 110f3652d6
3 changed files with 398 additions and 0 deletions

View File

@ -0,0 +1,161 @@
"""Extension management for Windows.
Under Windows it is unlikely the .obj files are of use, as special compiler options
are needed (primarily to toggle the behaviour of "public" symbols.
I dont consider it worth parsing the MSVC makefiles for compiler options. Even if
we get it just right, a specific freeze application may have specific compiler
options anyway (eg, to enable or disable specific functionality)
So my basic stragtegy is:
* Have a Windows INI file which "describes" an extension module.
* This description can include:
- The MSVC .dsp file for the extension. The .c source file names
are extraced from there.
- Specific compiler options
- Flag to indicate if Unicode compilation is expected.
At the moment the name and location of this INI file is hardcoded,
but an obvious enhancement would be to provide command line options.
"""
import os, win32api, string, sys
class CExtension:
"""An abstraction of an extension implemented in C/C++
"""
def __init__(self, name, sourceFiles):
self.name = name
# A list of strings defining additional compiler options.
self.sourceFiles = sourceFiles
# A list of special compiler options to be applied to
# all source modules in this extension.
self.compilerOptions = []
# A list of .lib files the final .EXE will need.
self.linkerLibs = []
def GetSourceFiles(self):
return self.sourceFiles
def AddCompilerOption(self, option):
self.compilerOptions.append(option)
def GetCompilerOptions(self):
return self.compilerOptions
def AddLinkerLib(self, lib):
self.linkerLibs.append(lib)
def GetLinkerLibs(self):
return self.linkerLibs
def checkextensions(unknown, ignored):
# Create a table of frozen extensions
mapFileName = os.path.join( os.path.split(sys.argv[0])[0], "extensions_win32.ini")
ret = []
for mod in unknown:
defn = get_extension_defn( mod, mapFileName )
if defn is not None:
ret.append( defn )
return ret
def get_extension_defn(moduleName, mapFileName):
dsp = win32api.GetProfileVal(moduleName, "dsp", "", mapFileName)
if dsp=="":
sys.stderr.write("No definition of module %s in map file '%s'\n" % (moduleName, mapFileName))
return None
# We allow environment variables in the file name
dsp = win32api.ExpandEnvironmentStrings(dsp)
sourceFiles = parse_dsp(dsp)
if sourceFiles is None:
return None
module = CExtension(moduleName, sourceFiles)
cl_options = win32api.GetProfileVal(moduleName, "cl", "", mapFileName)
if cl_options:
module.AddCompilerOption(win32api.ExpandEnvironmentStrings(cl_options))
exclude = win32api.GetProfileVal(moduleName, "exclude", "", mapFileName)
exclude = string.split(exclude)
if win32api.GetProfileVal(moduleName, "Unicode", 0, mapFileName):
module.AddCompilerOption('/D UNICODE /D _UNICODE')
libs = string.split(win32api.GetProfileVal(moduleName, "libs", "", mapFileName))
for lib in libs:
module.AddLinkerLib(lib)
for exc in exclude:
if exc in module.sourceFiles:
modules.sourceFiles.remove(exc)
return module
# Given an MSVC DSP file, locate C source files it uses
# returns a list of source files.
def parse_dsp(dsp):
# print "Processing", dsp
# For now, only support
ret = []
dsp_path, dsp_name = os.path.split(dsp)
try:
lines = open(dsp, "r").readlines()
except IOError, msg:
sys.stderr.write("%s: %s\n" % (dsp, msg))
return None
for line in lines:
fields = string.split(string.strip(line), "=", 2)
if fields[0]=="SOURCE":
if string.lower(os.path.splitext(fields[1])[1]) in ['.cpp', '.c']:
ret.append( win32api.GetFullPathName(os.path.join(dsp_path, fields[1] ) ) )
return ret
def write_extension_table(fname, modules):
fp = open(fname, "w")
try:
fp.write (ext_src_header)
# Write fn protos
for module in modules:
# bit of a hack for .pyd's as part of packages.
name = string.split(module.name,'.')[-1]
fp.write('extern void init%s(void);\n' % (name) )
# Write the table
fp.write (ext_tab_header)
for module in modules:
name = string.split(module.name,'.')[-1]
fp.write('\t{"%s", init%s},\n' % (name, name) )
fp.write (ext_tab_footer)
fp.write(ext_src_footer)
finally:
fp.close()
ext_src_header = """\
#include "Python.h"
"""
ext_tab_header = """\
static struct _inittab extensions[] = {
"""
ext_tab_footer = """\
/* Sentinel */
{0, 0}
};
"""
ext_src_footer = """\
extern int PyImport_ExtendInittab(struct _inittab *newtab);
int PyInitFrozenExtensions()
{
return PyImport_ExtendInittab(extensions);
}
"""

View File

@ -0,0 +1,117 @@
; This is a list of modules generally build as .pyd files.
;
; Each section contains enough information about a module for
; freeze to include the module as a static, built-in module
; in a frozen .EXE/.DLL.
; This is all setup for all the win32 extension modules
; released by Mark Hammond.
; You must ensure that the environment variable PYTHONEX is set
; to point to the root win32 extensions directory
;--------------------------------------------------------------
;
; Win32 Projects.
;
[perfmon]
dsp=%PYTHONEX%\win32\perfmon.dsp
cl=/I %PYTHONEX%\win32\src
Unicode=1
[pywintypes]
dsp=%PYTHONEX%\win32\pywintypes.dsp
cl=/I %PYTHONEX%\win32\src
libs=ole32.lib oleaut32.lib
[win32api]
dsp=%PYTHONEX%\win32\win32api.dsp
cl=/I %PYTHONEX%\win32\src
libs=kernel32.lib user32.lib shell32.lib advapi32.lib
[win32service]
dsp=%PYTHONEX%\win32\win32service.dsp
cl=/I %PYTHONEX%\win32\src
Unicode=1
libs=advapi32.lib
[win32evtlog]
dsp=%PYTHONEX%\win32\win32evtlog.dsp
cl=/I %PYTHONEX%\win32\src
[win32event]
dsp=%PYTHONEX%\win32\win32event.dsp
cl=/I %PYTHONEX%\win32\src
[win32file]
dsp=%PYTHONEX%\win32\win32file.dsp
cl=/I %PYTHONEX%\win32\src
[win32net]
dsp=%PYTHONEX%\win32\win32net.dsp
cl=/I %PYTHONEX%\win32\src
libs=netapi32.lib
[win32pdh]
dsp=%PYTHONEX%\win32\win32pdh.dsp
cl=/I %PYTHONEX%\win32\src
[win32pipe]
dsp=%PYTHONEX%\win32\win32pipe.dsp
cl=/I %PYTHONEX%\win32\src
[win32security]
dsp=%PYTHONEX%\win32\win32security.dsp
cl=/I %PYTHONEX%\win32\src
[win32service]
dsp=%PYTHONEX%\win32\win32service.dsp
cl=/I %PYTHONEX%\win32\src
[win32trace]
dsp=%PYTHONEX%\win32\win32trace.dsp
cl=/I %PYTHONEX%\win32\src
;--------------------------------------------------------------
;
; COM Projects.
;
[pythoncom]
dsp=%PYTHONEX%\com\win32com.dsp
cl=/I %PYTHONEX%\com\win32com\src\include /I %PYTHONEX%\win32\src
libs=uuid.lib
[win32com.axscript.axscript]
dsp=%PYTHONEX%\com\Active Scripting.dsp
cl=/I %PYTHONEX%\win32\src /I %PYTHONEX%\com\win32com\src\include
[win32com.axdebug.axdebug]
dsp=%PYTHONEX%\com\Active Debugging.dsp
cl=/I %PYTHONEX%\win32\src /I %PYTHONEX%\com\win32com\src\include
[win32com.mapi.mapi]
dsp=%PYTHONEX%\com\mapi.dsp
cl=/I %PYTHONEX%\win32\src /I %PYTHONEX%\com\win32com\src\include
libs=MBLOGON.lib ADDRLKUP.LIB mapi32.lib version.lib
[win32com.mapi.exchange]
dsp=%PYTHONEX%\com\exchange.dsp
cl=/I %PYTHONEX%\win32\src /I %PYTHONEX%\com\win32com\src\include
libs=MBLOGON.lib ADDRLKUP.LIB exchinst.lib EDKCFG.LIB EDKUTILS.LIB EDKMAPI.LIB mapi32.lib version.lib
[win32com.mapi.exchdapi]
dsp=%PYTHONEX%\com\exchdapi.dsp
cl=/I %PYTHONEX%\win32\src /I %PYTHONEX%\com\win32com\src\include
libs=DAPI.LIB
[servicemanager]
dsp=%PYTHONEX%\win32\PythonService EXE.dsp
Unicode = 1
; Pythonwin
[win32ui]
dsp=%PYTHONEX%\Pythonwin\win32ui.dsp
cl=/I %PYTHONEX%\win32\src
libs=mfc42.lib

120
Tools/freeze/win32.html Normal file
View File

@ -0,0 +1,120 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<META NAME="Generator" CONTENT="Microsoft Word 97">
<TITLE>win32</TITLE>
<META NAME="Version" CONTENT="8.0.3410">
<META NAME="Date" CONTENT="10/11/96">
<META NAME="Template" CONTENT="D:\Program Files\Microsoft Office\Office\HTML.DOT">
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#ffffff">
<H1>Freeze for Win32</H1>
<P>This document describes how to use Freeze for the Win32 platform. </P>
<P>Freeze itself is a Python tool for creating stand-alone executables from Python source code. This document does not attempt to document freeze itself - only the win32 specific changes.</P>
<H2>Frozen programs under Win32</H2>
<P>Frozen programs under Win32 can (theoretically) freeze any type of program supported by Python on Win32 - At the moment, Console .EXE and NT Service .EXE programs are supported. GUI Python programs and COM .EXE programs are very nearly all ready to go.</P>
<H3>Program Dependencies</H3>
<P>The person freezing the program has control over what external DLLs are required by a frozen program. The following dependencies are supported:</P>
<H4>Minimal frozen programs</H4>
<P>These programs freeze only .py files in your program. All external DLLs are required at run-time. This includes all .pyd/.dll modules used by your program, Python15.dll, and msvcrt.dll. </P>
<P>A small Python program would typically create a .EXE around 300kb.</P>
<H4>Frozen Extension programs</H4>
<B><I><P>Note:</B></I> For Python1.5.1, you must get a patch from Guido to import.c for this to work.</P>
<P>These programs also freeze in the sources from all .pyd and .dll files used at runtime. This means the resulting .EXE is only dependent on Python15.dll and msvcrt.dll.</P>
<P>A small Python program using win32api, win32con and one or 2 other win32 extensions would typically create a .EXE around 400kb.</P>
<H4>Completely frozen programs</H4>
<P>Completely stand-alone programs, as is the default on Unix systems. These are currently not supported, mainly as the size of a decent Python program gets very large. However, by tweaking the existing Unix support, this would not be difficult to do.</P>
<H2>Freezing Extension Modules</H2>
<P>By default, a file in the main "freeze" directory called "extensions_win32.ini" is used to obtain information about frozen extensions. A typical entry is:</P>
<CODE><P>[win32api]</P>
<P>dsp=%PYTHONEX%\win32\win32api.dsp</P>
<P>cl=/I %PYTHONEX%\win32\src</P>
<P>libs=kernel32.lib user32.lib shell32.lib advapi32.lib</P>
</CODE><P>&nbsp;</P>
<P>This entry indicates that the win32api extension module is defined in the MSVC project file "<CODE>%PYTHONEX%\win32\win32api.dsp</CODE>". Note the use of<CODE> </CODE>"<CODE>%PYTHONEX%" </CODE>- most strings are substituted with environment variables. In this case, it is assumed variable PYTHONEX points to the main "Python Extensions" source directory (which is assumed to be in the same structure as the release of the extension sources).</P>
<P>An entry in a .INI file can also control specific compiler options, and also the .lib files necessary to be linked with the application.</P>
<H3>Freezing Extension Module Considerations</H3>
<P>To prevent freezing extension modules, simply exclude that module using the freeze "-x" switch.</P>
<P>Occasionally, it will be necessary to explicitly include dependent modules. For example, many win32 modules are dependent on the "pywintypes" module - for example, the win32api module. In this case, the module may be explicitly included using the freeze "-m" option.</P>
<H3>Freezing win32com and PythonCOM</H3>
<P>PythonCOM.dll can be frozen as long as you are not implementing COM Servers. Ie, you can freeze programs which control other applications, but can not implement programs that are themselves controlled by other applications.</P>
<P>If you use any of the win32com .pyd extensions (ex, axscript, mapi, internet, axcontrol), then you will need to specify an additional "-a" option to point to the win32comext directory. There is an example below.</P>
<P>The use of the "win32com.client.gencache" module is not supported (although could be quite easily??)</P>
<H2>Examples</H2>
<P>Before we start, we must:</P>
<CODE><P>D:\temp\delme&gt;set PYTHONEX=d:\src\pythonex</P>
</CODE><H3>Helloworld.py</H3>
<H4>Source Code</H4><DIR>
<DIR>
<CODE><P>import string</P>
<P>import sys</P>
<P>&nbsp;</P>
<P>print string.join( ["Hello", "world"] + sys.argv[1:] )</P></DIR>
</DIR>
</CODE><H4>Command Line used</H4><DIR>
<DIR>
<FONT FACE="Courier New" SIZE=2><P>\src\python-1.5.1\tools\freeze\freeze.py helloworld.py</P>
<P>nmake</P></DIR>
</DIR>
</FONT><P>Resulting helloworld.exe: 114,688 bytes.</P>
<H3>Helloworld2.py</H3>
<P>Uses win32api. Demonstrates requirement for pywintypes, and difference between freezing extensions and not.</P>
<H4>Source Code</H4><DIR>
<DIR>
<P>import win32api</P>
<P>print "Hello from", win32api.GetComputerName()</P></DIR>
</DIR>
<H4>Command Line used</H4>
<P>By default, win32api will be frozen in with the .EXE. If you do not provide the "pywintypes" inclusion, then the link step will fail looking for all the pywintypes modules.</P><DIR>
<DIR>
<FONT FACE="Courier New" SIZE=2><P>\src\python-1.5.1\tools\freeze\freeze.py helloworld2.py -m pywintypes</P>
<P>nmake</P></DIR>
</DIR>
</FONT><P>Resulting helloworld2.exe: 167,936 bytes</P>
<P>Simply adding win32con to the mix gives an EXE of size: 352,768 bytes.</P>
<H4>Command Line used</H4>
<P>Using this build, we are dependent at runtime on the win32api.pyd and pywintypes15.dll files.</P><DIR>
<DIR>
<P>\src\python-1.5.1\tools\freeze\freeze.py -x win32api helloworld.py</P></DIR>
</DIR>
<P>Resulting helloworld2.exe: 114,688</P>
<P>Adding win32con to this build results in a size of: 252,928</P>
<H3>Testmapi.py</H3>
<P>Uses MAPI, a PythonCOM extension, and win32api.</P>
<H4>Source Code</H4>
<P>from win32com.mapi import mapi</P>
<P>import win32api</P>
<P>mapi.MAPIInitialize( (mapi.MAPI_INIT_VERSION, 0) )</P>
<P>print "Hello from", win32api.GetComputerName()</P>
<P>mapi.MAPIUninitialize()</P>
<H4>Command Line used</H4>
<P>As it does not import pythoncom or pywintypes itself, they must be specified. As it uses the win32comext directory, -a must be used. If you have built the win32com extensions from sources, then the second -a is required.</P><DIR>
<DIR>
<CODE><P>\src\python-1.5.1\tools\freeze\freeze.py -a win32com=%PYTHONEX%\com\win32comext -a win32com.mapi=%PYTHONEX%\com\build\release testmapi.py -m pywintypes -m pythoncom</P></DIR>
</DIR>
</CODE><P>Resulting testmapi.exe: 352,768 bytes</P>
<H3>PipeTestService.py</H3>
<P>This is a standard Python demo in the Win32 extensions. It can be found in the "win32\demos\service" directory.</P>
<H4>Command Line used</H4>
<P>This will create a native NT Service EXE, dependent only on the main Python15.dll. All other modules are built-in to the final .EXE</P><DIR>
<DIR>
<CODE><P>\src\python-1.5.1\tools\freeze\freeze.py -s service %PYTHONEX%\win32\demos\service\pipeTestService.py</P></DIR>
</DIR>
<P>Resulting pipeTestService.exe: </CODE><FONT FACE="Courier New" SIZE=2>533,504 bytes.</P></FONT></BODY>
</HTML>