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 don't 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 strategy 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 extracted 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 | """
|
---|