| """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 strategy 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); | |
| } | |
| """ |