mirror of
				https://github.com/python/cpython.git
				synced 2025-10-30 09:57:32 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			188 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """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 behavior 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 some Windows INI files which "describe" one or more extension modules.
 | |
|   (Freeze comes with a default one for all known modules - but you can specify
 | |
|   your own).
 | |
| * This description can include:
 | |
|   - The MSVC .dsp file for the extension.  The .c source file names
 | |
|     are extraced from there.
 | |
|   - Specific compiler/linker 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, sys
 | |
| try:
 | |
|     import win32api
 | |
| except ImportError:
 | |
|     win32api = None # User has already been warned
 | |
| 
 | |
| 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, extra_inis, prefix):
 | |
|     # Create a table of frozen extensions
 | |
| 
 | |
|     defaultMapName = os.path.join( os.path.split(sys.argv[0])[0], "extensions_win32.ini")
 | |
|     if not os.path.isfile(defaultMapName):
 | |
|         sys.stderr.write("WARNING: %s can not be found - standard extensions may not be found\n" % defaultMapName)
 | |
|     else:
 | |
|         # must go on end, so other inis can override.
 | |
|         extra_inis.append(defaultMapName)
 | |
| 
 | |
|     ret = []
 | |
|     for mod in unknown:
 | |
|         for ini in extra_inis:
 | |
| #                       print "Looking for", mod, "in", win32api.GetFullPathName(ini),"...",
 | |
|             defn = get_extension_defn( mod, ini, prefix )
 | |
|             if defn is not None:
 | |
| #                               print "Yay - found it!"
 | |
|                 ret.append( defn )
 | |
|                 break
 | |
| #                       print "Nope!"
 | |
|         else: # For not broken!
 | |
|             sys.stderr.write("No definition of module %s in any specified map file.\n" % (mod))
 | |
| 
 | |
|     return ret
 | |
| 
 | |
| def get_extension_defn(moduleName, mapFileName, prefix):
 | |
|     if win32api is None: return None
 | |
|     os.environ['PYTHONPREFIX'] = prefix
 | |
|     dsp = win32api.GetProfileVal(moduleName, "dsp", "", mapFileName)
 | |
|     if dsp=="":
 | |
|         return None
 | |
| 
 | |
|     # We allow environment variables in the file name
 | |
|     dsp = win32api.ExpandEnvironmentStrings(dsp)
 | |
|     # If the path to the .DSP file is not absolute, assume it is relative
 | |
|     # to the description file.
 | |
|     if not os.path.isabs(dsp):
 | |
|         dsp = os.path.join( os.path.split(mapFileName)[0], dsp)
 | |
|     # Parse it to extract the source files.
 | |
|     sourceFiles = parse_dsp(dsp)
 | |
|     if sourceFiles is None:
 | |
|         return None
 | |
| 
 | |
|     module = CExtension(moduleName, sourceFiles)
 | |
|     # Put the path to the DSP into the environment so entries can reference it.
 | |
|     os.environ['dsp_path'] = os.path.split(dsp)[0]
 | |
|     os.environ['ini_path'] = os.path.split(mapFileName)[0]
 | |
| 
 | |
|     cl_options = win32api.GetProfileVal(moduleName, "cl", "", mapFileName)
 | |
|     if cl_options:
 | |
|         module.AddCompilerOption(win32api.ExpandEnvironmentStrings(cl_options))
 | |
| 
 | |
|     exclude = win32api.GetProfileVal(moduleName, "exclude", "", mapFileName)
 | |
|     exclude = exclude.split()
 | |
| 
 | |
|     if win32api.GetProfileVal(moduleName, "Unicode", 0, mapFileName):
 | |
|         module.AddCompilerOption('/D UNICODE /D _UNICODE')
 | |
| 
 | |
|     libs = win32api.GetProfileVal(moduleName, "libs", "", mapFileName).split()
 | |
|     for lib in libs:
 | |
|         module.AddLinkerLib(win32api.ExpandEnvironmentStrings(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 = line.strip().split("=", 2)
 | |
|         if fields[0]=="SOURCE":
 | |
|             if os.path.splitext(fields[1])[1].lower() 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 = module.name.split('.')[-1]
 | |
|             fp.write('extern void init%s(void);\n' % (name) )
 | |
|         # Write the table
 | |
|         fp.write (ext_tab_header)
 | |
|         for module in modules:
 | |
|             name = module.name.split('.')[-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 DL_IMPORT(int) PyImport_ExtendInittab(struct _inittab *newtab);
 | |
| 
 | |
| int PyInitFrozenExtensions()
 | |
| {
 | |
|         return PyImport_ExtendInittab(extensions);
 | |
| }
 | |
| 
 | |
| """
 | 
