Path: blob/main/Tools/freeze/checkextensions_win32.py
12 views
"""Extension management for Windows.12Under Windows it is unlikely the .obj files are of use, as special compiler options3are needed (primarily to toggle the behavior of "public" symbols.45I don't consider it worth parsing the MSVC makefiles for compiler options. Even if6we get it just right, a specific freeze application may have specific compiler7options anyway (eg, to enable or disable specific functionality)89So my basic strategy is:1011* Have some Windows INI files which "describe" one or more extension modules.12(Freeze comes with a default one for all known modules - but you can specify13your own).14* This description can include:15- The MSVC .dsp file for the extension. The .c source file names16are extracted from there.17- Specific compiler/linker options18- Flag to indicate if Unicode compilation is expected.1920At the moment the name and location of this INI file is hardcoded,21but an obvious enhancement would be to provide command line options.22"""2324import os, sys25try:26import win32api27except ImportError:28win32api = None # User has already been warned2930class CExtension:31"""An abstraction of an extension implemented in C/C++32"""33def __init__(self, name, sourceFiles):34self.name = name35# A list of strings defining additional compiler options.36self.sourceFiles = sourceFiles37# A list of special compiler options to be applied to38# all source modules in this extension.39self.compilerOptions = []40# A list of .lib files the final .EXE will need.41self.linkerLibs = []4243def GetSourceFiles(self):44return self.sourceFiles4546def AddCompilerOption(self, option):47self.compilerOptions.append(option)48def GetCompilerOptions(self):49return self.compilerOptions5051def AddLinkerLib(self, lib):52self.linkerLibs.append(lib)53def GetLinkerLibs(self):54return self.linkerLibs5556def checkextensions(unknown, extra_inis, prefix):57# Create a table of frozen extensions5859defaultMapName = os.path.join( os.path.split(sys.argv[0])[0], "extensions_win32.ini")60if not os.path.isfile(defaultMapName):61sys.stderr.write("WARNING: %s can not be found - standard extensions may not be found\n" % defaultMapName)62else:63# must go on end, so other inis can override.64extra_inis.append(defaultMapName)6566ret = []67for mod in unknown:68for ini in extra_inis:69# print "Looking for", mod, "in", win32api.GetFullPathName(ini),"...",70defn = get_extension_defn( mod, ini, prefix )71if defn is not None:72# print "Yay - found it!"73ret.append( defn )74break75# print "Nope!"76else: # For not broken!77sys.stderr.write("No definition of module %s in any specified map file.\n" % (mod))7879return ret8081def get_extension_defn(moduleName, mapFileName, prefix):82if win32api is None: return None83os.environ['PYTHONPREFIX'] = prefix84dsp = win32api.GetProfileVal(moduleName, "dsp", "", mapFileName)85if dsp=="":86return None8788# We allow environment variables in the file name89dsp = win32api.ExpandEnvironmentStrings(dsp)90# If the path to the .DSP file is not absolute, assume it is relative91# to the description file.92if not os.path.isabs(dsp):93dsp = os.path.join( os.path.split(mapFileName)[0], dsp)94# Parse it to extract the source files.95sourceFiles = parse_dsp(dsp)96if sourceFiles is None:97return None9899module = CExtension(moduleName, sourceFiles)100# Put the path to the DSP into the environment so entries can reference it.101os.environ['dsp_path'] = os.path.split(dsp)[0]102os.environ['ini_path'] = os.path.split(mapFileName)[0]103104cl_options = win32api.GetProfileVal(moduleName, "cl", "", mapFileName)105if cl_options:106module.AddCompilerOption(win32api.ExpandEnvironmentStrings(cl_options))107108exclude = win32api.GetProfileVal(moduleName, "exclude", "", mapFileName)109exclude = exclude.split()110111if win32api.GetProfileVal(moduleName, "Unicode", 0, mapFileName):112module.AddCompilerOption('/D UNICODE /D _UNICODE')113114libs = win32api.GetProfileVal(moduleName, "libs", "", mapFileName).split()115for lib in libs:116module.AddLinkerLib(win32api.ExpandEnvironmentStrings(lib))117118for exc in exclude:119if exc in module.sourceFiles:120module.sourceFiles.remove(exc)121122return module123124# Given an MSVC DSP file, locate C source files it uses125# returns a list of source files.126def parse_dsp(dsp):127# print "Processing", dsp128# For now, only support129ret = []130dsp_path, dsp_name = os.path.split(dsp)131try:132with open(dsp, "r") as fp:133lines = fp.readlines()134except IOError as msg:135sys.stderr.write("%s: %s\n" % (dsp, msg))136return None137for line in lines:138fields = line.strip().split("=", 2)139if fields[0]=="SOURCE":140if os.path.splitext(fields[1])[1].lower() in ['.cpp', '.c']:141ret.append( win32api.GetFullPathName(os.path.join(dsp_path, fields[1] ) ) )142return ret143144def write_extension_table(fname, modules):145fp = open(fname, "w")146try:147fp.write (ext_src_header)148# Write fn protos149for module in modules:150# bit of a hack for .pyd's as part of packages.151name = module.name.split('.')[-1]152fp.write('extern void init%s(void);\n' % (name) )153# Write the table154fp.write (ext_tab_header)155for module in modules:156name = module.name.split('.')[-1]157fp.write('\t{"%s", init%s},\n' % (name, name) )158159fp.write (ext_tab_footer)160fp.write(ext_src_footer)161finally:162fp.close()163164165ext_src_header = """\166#include "Python.h"167"""168169ext_tab_header = """\170171static struct _inittab extensions[] = {172"""173174ext_tab_footer = """\175/* Sentinel */176{0, 0}177};178"""179180ext_src_footer = """\181extern DL_IMPORT(int) PyImport_ExtendInittab(struct _inittab *newtab);182183int PyInitFrozenExtensions()184{185return PyImport_ExtendInittab(extensions);186}187188"""189190191