|
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 """ |