| 1 | """Extension management for Windows.
|
|---|
| 2 |
|
|---|
| 3 | Under Windows it is unlikely the .obj files are of use, as special compiler options
|
|---|
| 4 | are needed (primarily to toggle the behavior of "public" symbols.
|
|---|
| 5 |
|
|---|
| 6 | I dont consider it worth parsing the MSVC makefiles for compiler options. Even if
|
|---|
| 7 | we get it just right, a specific freeze application may have specific compiler
|
|---|
| 8 | options anyway (eg, to enable or disable specific functionality)
|
|---|
| 9 |
|
|---|
| 10 | So my basic stragtegy is:
|
|---|
| 11 |
|
|---|
| 12 | * Have some Windows INI files which "describe" one or more extension modules.
|
|---|
| 13 | (Freeze comes with a default one for all known modules - but you can specify
|
|---|
| 14 | your own).
|
|---|
| 15 | * This description can include:
|
|---|
| 16 | - The MSVC .dsp file for the extension. The .c source file names
|
|---|
| 17 | are extraced from there.
|
|---|
| 18 | - Specific compiler/linker options
|
|---|
| 19 | - Flag to indicate if Unicode compilation is expected.
|
|---|
| 20 |
|
|---|
| 21 | At the moment the name and location of this INI file is hardcoded,
|
|---|
| 22 | but an obvious enhancement would be to provide command line options.
|
|---|
| 23 | """
|
|---|
| 24 |
|
|---|
| 25 | import os, sys
|
|---|
| 26 | try:
|
|---|
| 27 | import win32api
|
|---|
| 28 | except ImportError:
|
|---|
| 29 | win32api = None # User has already been warned
|
|---|
| 30 |
|
|---|
| 31 | class CExtension:
|
|---|
| 32 | """An abstraction of an extension implemented in C/C++
|
|---|
| 33 | """
|
|---|
| 34 | def __init__(self, name, sourceFiles):
|
|---|
| 35 | self.name = name
|
|---|
| 36 | # A list of strings defining additional compiler options.
|
|---|
| 37 | self.sourceFiles = sourceFiles
|
|---|
| 38 | # A list of special compiler options to be applied to
|
|---|
| 39 | # all source modules in this extension.
|
|---|
| 40 | self.compilerOptions = []
|
|---|
| 41 | # A list of .lib files the final .EXE will need.
|
|---|
| 42 | self.linkerLibs = []
|
|---|
| 43 |
|
|---|
| 44 | def GetSourceFiles(self):
|
|---|
| 45 | return self.sourceFiles
|
|---|
| 46 |
|
|---|
| 47 | def AddCompilerOption(self, option):
|
|---|
| 48 | self.compilerOptions.append(option)
|
|---|
| 49 | def GetCompilerOptions(self):
|
|---|
| 50 | return self.compilerOptions
|
|---|
| 51 |
|
|---|
| 52 | def AddLinkerLib(self, lib):
|
|---|
| 53 | self.linkerLibs.append(lib)
|
|---|
| 54 | def GetLinkerLibs(self):
|
|---|
| 55 | return self.linkerLibs
|
|---|
| 56 |
|
|---|
| 57 | def checkextensions(unknown, extra_inis, prefix):
|
|---|
| 58 | # Create a table of frozen extensions
|
|---|
| 59 |
|
|---|
| 60 | defaultMapName = os.path.join( os.path.split(sys.argv[0])[0], "extensions_win32.ini")
|
|---|
| 61 | if not os.path.isfile(defaultMapName):
|
|---|
| 62 | sys.stderr.write("WARNING: %s can not be found - standard extensions may not be found\n" % defaultMapName)
|
|---|
| 63 | else:
|
|---|
| 64 | # must go on end, so other inis can override.
|
|---|
| 65 | extra_inis.append(defaultMapName)
|
|---|
| 66 |
|
|---|
| 67 | ret = []
|
|---|
| 68 | for mod in unknown:
|
|---|
| 69 | for ini in extra_inis:
|
|---|
| 70 | # print "Looking for", mod, "in", win32api.GetFullPathName(ini),"...",
|
|---|
| 71 | defn = get_extension_defn( mod, ini, prefix )
|
|---|
| 72 | if defn is not None:
|
|---|
| 73 | # print "Yay - found it!"
|
|---|
| 74 | ret.append( defn )
|
|---|
| 75 | break
|
|---|
| 76 | # print "Nope!"
|
|---|
| 77 | else: # For not broken!
|
|---|
| 78 | sys.stderr.write("No definition of module %s in any specified map file.\n" % (mod))
|
|---|
| 79 |
|
|---|
| 80 | return ret
|
|---|
| 81 |
|
|---|
| 82 | def get_extension_defn(moduleName, mapFileName, prefix):
|
|---|
| 83 | if win32api is None: return None
|
|---|
| 84 | os.environ['PYTHONPREFIX'] = prefix
|
|---|
| 85 | dsp = win32api.GetProfileVal(moduleName, "dsp", "", mapFileName)
|
|---|
| 86 | if dsp=="":
|
|---|
| 87 | return None
|
|---|
| 88 |
|
|---|
| 89 | # We allow environment variables in the file name
|
|---|
| 90 | dsp = win32api.ExpandEnvironmentStrings(dsp)
|
|---|
| 91 | # If the path to the .DSP file is not absolute, assume it is relative
|
|---|
| 92 | # to the description file.
|
|---|
| 93 | if not os.path.isabs(dsp):
|
|---|
| 94 | dsp = os.path.join( os.path.split(mapFileName)[0], dsp)
|
|---|
| 95 | # Parse it to extract the source files.
|
|---|
| 96 | sourceFiles = parse_dsp(dsp)
|
|---|
| 97 | if sourceFiles is None:
|
|---|
| 98 | return None
|
|---|
| 99 |
|
|---|
| 100 | module = CExtension(moduleName, sourceFiles)
|
|---|
| 101 | # Put the path to the DSP into the environment so entries can reference it.
|
|---|
| 102 | os.environ['dsp_path'] = os.path.split(dsp)[0]
|
|---|
| 103 | os.environ['ini_path'] = os.path.split(mapFileName)[0]
|
|---|
| 104 |
|
|---|
| 105 | cl_options = win32api.GetProfileVal(moduleName, "cl", "", mapFileName)
|
|---|
| 106 | if cl_options:
|
|---|
| 107 | module.AddCompilerOption(win32api.ExpandEnvironmentStrings(cl_options))
|
|---|
| 108 |
|
|---|
| 109 | exclude = win32api.GetProfileVal(moduleName, "exclude", "", mapFileName)
|
|---|
| 110 | exclude = exclude.split()
|
|---|
| 111 |
|
|---|
| 112 | if win32api.GetProfileVal(moduleName, "Unicode", 0, mapFileName):
|
|---|
| 113 | module.AddCompilerOption('/D UNICODE /D _UNICODE')
|
|---|
| 114 |
|
|---|
| 115 | libs = win32api.GetProfileVal(moduleName, "libs", "", mapFileName).split()
|
|---|
| 116 | for lib in libs:
|
|---|
| 117 | module.AddLinkerLib(win32api.ExpandEnvironmentStrings(lib))
|
|---|
| 118 |
|
|---|
| 119 | for exc in exclude:
|
|---|
| 120 | if exc in module.sourceFiles:
|
|---|
| 121 | modules.sourceFiles.remove(exc)
|
|---|
| 122 |
|
|---|
| 123 | return module
|
|---|
| 124 |
|
|---|
| 125 | # Given an MSVC DSP file, locate C source files it uses
|
|---|
| 126 | # returns a list of source files.
|
|---|
| 127 | def parse_dsp(dsp):
|
|---|
| 128 | # print "Processing", dsp
|
|---|
| 129 | # For now, only support
|
|---|
| 130 | ret = []
|
|---|
| 131 | dsp_path, dsp_name = os.path.split(dsp)
|
|---|
| 132 | try:
|
|---|
| 133 | lines = open(dsp, "r").readlines()
|
|---|
| 134 | except IOError, msg:
|
|---|
| 135 | sys.stderr.write("%s: %s\n" % (dsp, msg))
|
|---|
| 136 | return None
|
|---|
| 137 | for line in lines:
|
|---|
| 138 | fields = line.strip().split("=", 2)
|
|---|
| 139 | if fields[0]=="SOURCE":
|
|---|
| 140 | if os.path.splitext(fields[1])[1].lower() in ['.cpp', '.c']:
|
|---|
| 141 | ret.append( win32api.GetFullPathName(os.path.join(dsp_path, fields[1] ) ) )
|
|---|
| 142 | return ret
|
|---|
| 143 |
|
|---|
| 144 | def write_extension_table(fname, modules):
|
|---|
| 145 | fp = open(fname, "w")
|
|---|
| 146 | try:
|
|---|
| 147 | fp.write (ext_src_header)
|
|---|
| 148 | # Write fn protos
|
|---|
| 149 | for module in modules:
|
|---|
| 150 | # bit of a hack for .pyd's as part of packages.
|
|---|
| 151 | name = module.name.split('.')[-1]
|
|---|
| 152 | fp.write('extern void init%s(void);\n' % (name) )
|
|---|
| 153 | # Write the table
|
|---|
| 154 | fp.write (ext_tab_header)
|
|---|
| 155 | for module in modules:
|
|---|
| 156 | name = module.name.split('.')[-1]
|
|---|
| 157 | fp.write('\t{"%s", init%s},\n' % (name, name) )
|
|---|
| 158 |
|
|---|
| 159 | fp.write (ext_tab_footer)
|
|---|
| 160 | fp.write(ext_src_footer)
|
|---|
| 161 | finally:
|
|---|
| 162 | fp.close()
|
|---|
| 163 |
|
|---|
| 164 |
|
|---|
| 165 | ext_src_header = """\
|
|---|
| 166 | #include "Python.h"
|
|---|
| 167 | """
|
|---|
| 168 |
|
|---|
| 169 | ext_tab_header = """\
|
|---|
| 170 |
|
|---|
| 171 | static struct _inittab extensions[] = {
|
|---|
| 172 | """
|
|---|
| 173 |
|
|---|
| 174 | ext_tab_footer = """\
|
|---|
| 175 | /* Sentinel */
|
|---|
| 176 | {0, 0}
|
|---|
| 177 | };
|
|---|
| 178 | """
|
|---|
| 179 |
|
|---|
| 180 | ext_src_footer = """\
|
|---|
| 181 | extern DL_IMPORT(int) PyImport_ExtendInittab(struct _inittab *newtab);
|
|---|
| 182 |
|
|---|
| 183 | int PyInitFrozenExtensions()
|
|---|
| 184 | {
|
|---|
| 185 | return PyImport_ExtendInittab(extensions);
|
|---|
| 186 | }
|
|---|
| 187 |
|
|---|
| 188 | """
|
|---|