Chris@87
|
1 """
|
Chris@87
|
2 Support code for building Python extensions on Windows.
|
Chris@87
|
3
|
Chris@87
|
4 # NT stuff
|
Chris@87
|
5 # 1. Make sure libpython<version>.a exists for gcc. If not, build it.
|
Chris@87
|
6 # 2. Force windows to use gcc (we're struggling with MSVC and g77 support)
|
Chris@87
|
7 # 3. Force windows to use g77
|
Chris@87
|
8
|
Chris@87
|
9 """
|
Chris@87
|
10 from __future__ import division, absolute_import, print_function
|
Chris@87
|
11
|
Chris@87
|
12 import os
|
Chris@87
|
13 import sys
|
Chris@87
|
14 import subprocess
|
Chris@87
|
15 import re
|
Chris@87
|
16
|
Chris@87
|
17 # Overwrite certain distutils.ccompiler functions:
|
Chris@87
|
18 import numpy.distutils.ccompiler
|
Chris@87
|
19
|
Chris@87
|
20 if sys.version_info[0] < 3:
|
Chris@87
|
21 from . import log
|
Chris@87
|
22 else:
|
Chris@87
|
23 from numpy.distutils import log
|
Chris@87
|
24 # NT stuff
|
Chris@87
|
25 # 1. Make sure libpython<version>.a exists for gcc. If not, build it.
|
Chris@87
|
26 # 2. Force windows to use gcc (we're struggling with MSVC and g77 support)
|
Chris@87
|
27 # --> this is done in numpy/distutils/ccompiler.py
|
Chris@87
|
28 # 3. Force windows to use g77
|
Chris@87
|
29
|
Chris@87
|
30 import distutils.cygwinccompiler
|
Chris@87
|
31 from distutils.version import StrictVersion
|
Chris@87
|
32 from numpy.distutils.ccompiler import gen_preprocess_options, gen_lib_options
|
Chris@87
|
33 from distutils.errors import DistutilsExecError, CompileError, UnknownFileError
|
Chris@87
|
34
|
Chris@87
|
35 from distutils.unixccompiler import UnixCCompiler
|
Chris@87
|
36 from distutils.msvccompiler import get_build_version as get_build_msvc_version
|
Chris@87
|
37 from numpy.distutils.misc_util import msvc_runtime_library, get_build_architecture
|
Chris@87
|
38
|
Chris@87
|
39 # Useful to generate table of symbols from a dll
|
Chris@87
|
40 _START = re.compile(r'\[Ordinal/Name Pointer\] Table')
|
Chris@87
|
41 _TABLE = re.compile(r'^\s+\[([\s*[0-9]*)\] ([a-zA-Z0-9_]*)')
|
Chris@87
|
42
|
Chris@87
|
43 # the same as cygwin plus some additional parameters
|
Chris@87
|
44 class Mingw32CCompiler(distutils.cygwinccompiler.CygwinCCompiler):
|
Chris@87
|
45 """ A modified MingW32 compiler compatible with an MSVC built Python.
|
Chris@87
|
46
|
Chris@87
|
47 """
|
Chris@87
|
48
|
Chris@87
|
49 compiler_type = 'mingw32'
|
Chris@87
|
50
|
Chris@87
|
51 def __init__ (self,
|
Chris@87
|
52 verbose=0,
|
Chris@87
|
53 dry_run=0,
|
Chris@87
|
54 force=0):
|
Chris@87
|
55
|
Chris@87
|
56 distutils.cygwinccompiler.CygwinCCompiler.__init__ (self,
|
Chris@87
|
57 verbose, dry_run, force)
|
Chris@87
|
58
|
Chris@87
|
59 # we need to support 3.2 which doesn't match the standard
|
Chris@87
|
60 # get_versions methods regex
|
Chris@87
|
61 if self.gcc_version is None:
|
Chris@87
|
62 import re
|
Chris@87
|
63 p = subprocess.Popen(['gcc', '-dumpversion'], shell=True,
|
Chris@87
|
64 stdout=subprocess.PIPE)
|
Chris@87
|
65 out_string = p.stdout.read()
|
Chris@87
|
66 p.stdout.close()
|
Chris@87
|
67 result = re.search('(\d+\.\d+)', out_string)
|
Chris@87
|
68 if result:
|
Chris@87
|
69 self.gcc_version = StrictVersion(result.group(1))
|
Chris@87
|
70
|
Chris@87
|
71 # A real mingw32 doesn't need to specify a different entry point,
|
Chris@87
|
72 # but cygwin 2.91.57 in no-cygwin-mode needs it.
|
Chris@87
|
73 if self.gcc_version <= "2.91.57":
|
Chris@87
|
74 entry_point = '--entry _DllMain@12'
|
Chris@87
|
75 else:
|
Chris@87
|
76 entry_point = ''
|
Chris@87
|
77
|
Chris@87
|
78 if self.linker_dll == 'dllwrap':
|
Chris@87
|
79 # Commented out '--driver-name g++' part that fixes weird
|
Chris@87
|
80 # g++.exe: g++: No such file or directory
|
Chris@87
|
81 # error (mingw 1.0 in Enthon24 tree, gcc-3.4.5).
|
Chris@87
|
82 # If the --driver-name part is required for some environment
|
Chris@87
|
83 # then make the inclusion of this part specific to that environment.
|
Chris@87
|
84 self.linker = 'dllwrap' # --driver-name g++'
|
Chris@87
|
85 elif self.linker_dll == 'gcc':
|
Chris@87
|
86 self.linker = 'g++'
|
Chris@87
|
87
|
Chris@87
|
88 # **changes: eric jones 4/11/01
|
Chris@87
|
89 # 1. Check for import library on Windows. Build if it doesn't exist.
|
Chris@87
|
90
|
Chris@87
|
91 build_import_library()
|
Chris@87
|
92
|
Chris@87
|
93 # Check for custom msvc runtime library on Windows. Build if it doesn't exist.
|
Chris@87
|
94 msvcr_success = build_msvcr_library()
|
Chris@87
|
95 msvcr_dbg_success = build_msvcr_library(debug=True)
|
Chris@87
|
96 if msvcr_success or msvcr_dbg_success:
|
Chris@87
|
97 # add preprocessor statement for using customized msvcr lib
|
Chris@87
|
98 self.define_macro('NPY_MINGW_USE_CUSTOM_MSVCR')
|
Chris@87
|
99
|
Chris@87
|
100 # Define the MSVC version as hint for MinGW
|
Chris@87
|
101 msvcr_version = '0x%03i0' % int(msvc_runtime_library().lstrip('msvcr'))
|
Chris@87
|
102 self.define_macro('__MSVCRT_VERSION__', msvcr_version)
|
Chris@87
|
103
|
Chris@87
|
104 # **changes: eric jones 4/11/01
|
Chris@87
|
105 # 2. increased optimization and turned off all warnings
|
Chris@87
|
106 # 3. also added --driver-name g++
|
Chris@87
|
107 #self.set_executables(compiler='gcc -mno-cygwin -O2 -w',
|
Chris@87
|
108 # compiler_so='gcc -mno-cygwin -mdll -O2 -w',
|
Chris@87
|
109 # linker_exe='gcc -mno-cygwin',
|
Chris@87
|
110 # linker_so='%s --driver-name g++ -mno-cygwin -mdll -static %s'
|
Chris@87
|
111 # % (self.linker, entry_point))
|
Chris@87
|
112
|
Chris@87
|
113 # MS_WIN64 should be defined when building for amd64 on windows, but
|
Chris@87
|
114 # python headers define it only for MS compilers, which has all kind of
|
Chris@87
|
115 # bad consequences, like using Py_ModuleInit4 instead of
|
Chris@87
|
116 # Py_ModuleInit4_64, etc... So we add it here
|
Chris@87
|
117 if get_build_architecture() == 'AMD64':
|
Chris@87
|
118 if self.gcc_version < "4.0":
|
Chris@87
|
119 self.set_executables(
|
Chris@87
|
120 compiler='gcc -g -DDEBUG -DMS_WIN64 -mno-cygwin -O0 -Wall',
|
Chris@87
|
121 compiler_so='gcc -g -DDEBUG -DMS_WIN64 -mno-cygwin -O0 -Wall -Wstrict-prototypes',
|
Chris@87
|
122 linker_exe='gcc -g -mno-cygwin',
|
Chris@87
|
123 linker_so='gcc -g -mno-cygwin -shared')
|
Chris@87
|
124 else:
|
Chris@87
|
125 # gcc-4 series releases do not support -mno-cygwin option
|
Chris@87
|
126 self.set_executables(
|
Chris@87
|
127 compiler='gcc -g -DDEBUG -DMS_WIN64 -O0 -Wall',
|
Chris@87
|
128 compiler_so='gcc -g -DDEBUG -DMS_WIN64 -O0 -Wall -Wstrict-prototypes',
|
Chris@87
|
129 linker_exe='gcc -g',
|
Chris@87
|
130 linker_so='gcc -g -shared')
|
Chris@87
|
131 else:
|
Chris@87
|
132 if self.gcc_version <= "3.0.0":
|
Chris@87
|
133 self.set_executables(compiler='gcc -mno-cygwin -O2 -w',
|
Chris@87
|
134 compiler_so='gcc -mno-cygwin -mdll -O2 -w -Wstrict-prototypes',
|
Chris@87
|
135 linker_exe='g++ -mno-cygwin',
|
Chris@87
|
136 linker_so='%s -mno-cygwin -mdll -static %s'
|
Chris@87
|
137 % (self.linker, entry_point))
|
Chris@87
|
138 elif self.gcc_version < "4.0":
|
Chris@87
|
139 self.set_executables(compiler='gcc -mno-cygwin -O2 -Wall',
|
Chris@87
|
140 compiler_so='gcc -mno-cygwin -O2 -Wall -Wstrict-prototypes',
|
Chris@87
|
141 linker_exe='g++ -mno-cygwin',
|
Chris@87
|
142 linker_so='g++ -mno-cygwin -shared')
|
Chris@87
|
143 else:
|
Chris@87
|
144 # gcc-4 series releases do not support -mno-cygwin option
|
Chris@87
|
145 self.set_executables(compiler='gcc -O2 -Wall',
|
Chris@87
|
146 compiler_so='gcc -O2 -Wall -Wstrict-prototypes',
|
Chris@87
|
147 linker_exe='g++ ',
|
Chris@87
|
148 linker_so='g++ -shared')
|
Chris@87
|
149 # added for python2.3 support
|
Chris@87
|
150 # we can't pass it through set_executables because pre 2.2 would fail
|
Chris@87
|
151 self.compiler_cxx = ['g++']
|
Chris@87
|
152
|
Chris@87
|
153 # Maybe we should also append -mthreads, but then the finished
|
Chris@87
|
154 # dlls need another dll (mingwm10.dll see Mingw32 docs)
|
Chris@87
|
155 # (-mthreads: Support thread-safe exception handling on `Mingw32')
|
Chris@87
|
156
|
Chris@87
|
157 # no additional libraries needed
|
Chris@87
|
158 #self.dll_libraries=[]
|
Chris@87
|
159 return
|
Chris@87
|
160
|
Chris@87
|
161 # __init__ ()
|
Chris@87
|
162
|
Chris@87
|
163 def link(self,
|
Chris@87
|
164 target_desc,
|
Chris@87
|
165 objects,
|
Chris@87
|
166 output_filename,
|
Chris@87
|
167 output_dir,
|
Chris@87
|
168 libraries,
|
Chris@87
|
169 library_dirs,
|
Chris@87
|
170 runtime_library_dirs,
|
Chris@87
|
171 export_symbols = None,
|
Chris@87
|
172 debug=0,
|
Chris@87
|
173 extra_preargs=None,
|
Chris@87
|
174 extra_postargs=None,
|
Chris@87
|
175 build_temp=None,
|
Chris@87
|
176 target_lang=None):
|
Chris@87
|
177 # Include the appropiate MSVC runtime library if Python was built
|
Chris@87
|
178 # with MSVC >= 7.0 (MinGW standard is msvcrt)
|
Chris@87
|
179 runtime_library = msvc_runtime_library()
|
Chris@87
|
180 if runtime_library:
|
Chris@87
|
181 if not libraries:
|
Chris@87
|
182 libraries = []
|
Chris@87
|
183 libraries.append(runtime_library)
|
Chris@87
|
184 args = (self,
|
Chris@87
|
185 target_desc,
|
Chris@87
|
186 objects,
|
Chris@87
|
187 output_filename,
|
Chris@87
|
188 output_dir,
|
Chris@87
|
189 libraries,
|
Chris@87
|
190 library_dirs,
|
Chris@87
|
191 runtime_library_dirs,
|
Chris@87
|
192 None, #export_symbols, we do this in our def-file
|
Chris@87
|
193 debug,
|
Chris@87
|
194 extra_preargs,
|
Chris@87
|
195 extra_postargs,
|
Chris@87
|
196 build_temp,
|
Chris@87
|
197 target_lang)
|
Chris@87
|
198 if self.gcc_version < "3.0.0":
|
Chris@87
|
199 func = distutils.cygwinccompiler.CygwinCCompiler.link
|
Chris@87
|
200 else:
|
Chris@87
|
201 func = UnixCCompiler.link
|
Chris@87
|
202 func(*args[:func.__code__.co_argcount])
|
Chris@87
|
203 return
|
Chris@87
|
204
|
Chris@87
|
205 def object_filenames (self,
|
Chris@87
|
206 source_filenames,
|
Chris@87
|
207 strip_dir=0,
|
Chris@87
|
208 output_dir=''):
|
Chris@87
|
209 if output_dir is None: output_dir = ''
|
Chris@87
|
210 obj_names = []
|
Chris@87
|
211 for src_name in source_filenames:
|
Chris@87
|
212 # use normcase to make sure '.rc' is really '.rc' and not '.RC'
|
Chris@87
|
213 (base, ext) = os.path.splitext (os.path.normcase(src_name))
|
Chris@87
|
214
|
Chris@87
|
215 # added these lines to strip off windows drive letters
|
Chris@87
|
216 # without it, .o files are placed next to .c files
|
Chris@87
|
217 # instead of the build directory
|
Chris@87
|
218 drv, base = os.path.splitdrive(base)
|
Chris@87
|
219 if drv:
|
Chris@87
|
220 base = base[1:]
|
Chris@87
|
221
|
Chris@87
|
222 if ext not in (self.src_extensions + ['.rc', '.res']):
|
Chris@87
|
223 raise UnknownFileError(
|
Chris@87
|
224 "unknown file type '%s' (from '%s')" % \
|
Chris@87
|
225 (ext, src_name))
|
Chris@87
|
226 if strip_dir:
|
Chris@87
|
227 base = os.path.basename (base)
|
Chris@87
|
228 if ext == '.res' or ext == '.rc':
|
Chris@87
|
229 # these need to be compiled to object files
|
Chris@87
|
230 obj_names.append (os.path.join (output_dir,
|
Chris@87
|
231 base + ext + self.obj_extension))
|
Chris@87
|
232 else:
|
Chris@87
|
233 obj_names.append (os.path.join (output_dir,
|
Chris@87
|
234 base + self.obj_extension))
|
Chris@87
|
235 return obj_names
|
Chris@87
|
236
|
Chris@87
|
237 # object_filenames ()
|
Chris@87
|
238
|
Chris@87
|
239
|
Chris@87
|
240 def find_python_dll():
|
Chris@87
|
241 maj, min, micro = [int(i) for i in sys.version_info[:3]]
|
Chris@87
|
242 dllname = 'python%d%d.dll' % (maj, min)
|
Chris@87
|
243 print("Looking for %s" % dllname)
|
Chris@87
|
244
|
Chris@87
|
245 # We can't do much here:
|
Chris@87
|
246 # - find it in python main dir
|
Chris@87
|
247 # - in system32,
|
Chris@87
|
248 # - ortherwise (Sxs), I don't know how to get it.
|
Chris@87
|
249 lib_dirs = []
|
Chris@87
|
250 lib_dirs.append(sys.prefix)
|
Chris@87
|
251 lib_dirs.append(os.path.join(sys.prefix, 'lib'))
|
Chris@87
|
252 try:
|
Chris@87
|
253 lib_dirs.append(os.path.join(os.environ['SYSTEMROOT'], 'system32'))
|
Chris@87
|
254 except KeyError:
|
Chris@87
|
255 pass
|
Chris@87
|
256
|
Chris@87
|
257 for d in lib_dirs:
|
Chris@87
|
258 dll = os.path.join(d, dllname)
|
Chris@87
|
259 if os.path.exists(dll):
|
Chris@87
|
260 return dll
|
Chris@87
|
261
|
Chris@87
|
262 raise ValueError("%s not found in %s" % (dllname, lib_dirs))
|
Chris@87
|
263
|
Chris@87
|
264 def dump_table(dll):
|
Chris@87
|
265 st = subprocess.Popen(["objdump.exe", "-p", dll], stdout=subprocess.PIPE)
|
Chris@87
|
266 return st.stdout.readlines()
|
Chris@87
|
267
|
Chris@87
|
268 def generate_def(dll, dfile):
|
Chris@87
|
269 """Given a dll file location, get all its exported symbols and dump them
|
Chris@87
|
270 into the given def file.
|
Chris@87
|
271
|
Chris@87
|
272 The .def file will be overwritten"""
|
Chris@87
|
273 dump = dump_table(dll)
|
Chris@87
|
274 for i in range(len(dump)):
|
Chris@87
|
275 if _START.match(dump[i].decode()):
|
Chris@87
|
276 break
|
Chris@87
|
277 else:
|
Chris@87
|
278 raise ValueError("Symbol table not found")
|
Chris@87
|
279
|
Chris@87
|
280 syms = []
|
Chris@87
|
281 for j in range(i+1, len(dump)):
|
Chris@87
|
282 m = _TABLE.match(dump[j].decode())
|
Chris@87
|
283 if m:
|
Chris@87
|
284 syms.append((int(m.group(1).strip()), m.group(2)))
|
Chris@87
|
285 else:
|
Chris@87
|
286 break
|
Chris@87
|
287
|
Chris@87
|
288 if len(syms) == 0:
|
Chris@87
|
289 log.warn('No symbols found in %s' % dll)
|
Chris@87
|
290
|
Chris@87
|
291 d = open(dfile, 'w')
|
Chris@87
|
292 d.write('LIBRARY %s\n' % os.path.basename(dll))
|
Chris@87
|
293 d.write(';CODE PRELOAD MOVEABLE DISCARDABLE\n')
|
Chris@87
|
294 d.write(';DATA PRELOAD SINGLE\n')
|
Chris@87
|
295 d.write('\nEXPORTS\n')
|
Chris@87
|
296 for s in syms:
|
Chris@87
|
297 #d.write('@%d %s\n' % (s[0], s[1]))
|
Chris@87
|
298 d.write('%s\n' % s[1])
|
Chris@87
|
299 d.close()
|
Chris@87
|
300
|
Chris@87
|
301 def find_dll(dll_name):
|
Chris@87
|
302
|
Chris@87
|
303 arch = {'AMD64' : 'amd64',
|
Chris@87
|
304 'Intel' : 'x86'}[get_build_architecture()]
|
Chris@87
|
305
|
Chris@87
|
306 def _find_dll_in_winsxs(dll_name):
|
Chris@87
|
307 # Walk through the WinSxS directory to find the dll.
|
Chris@87
|
308 winsxs_path = os.path.join(os.environ['WINDIR'], 'winsxs')
|
Chris@87
|
309 if not os.path.exists(winsxs_path):
|
Chris@87
|
310 return None
|
Chris@87
|
311 for root, dirs, files in os.walk(winsxs_path):
|
Chris@87
|
312 if dll_name in files and arch in root:
|
Chris@87
|
313 return os.path.join(root, dll_name)
|
Chris@87
|
314 return None
|
Chris@87
|
315
|
Chris@87
|
316 def _find_dll_in_path(dll_name):
|
Chris@87
|
317 # First, look in the Python directory, then scan PATH for
|
Chris@87
|
318 # the given dll name.
|
Chris@87
|
319 for path in [sys.prefix] + os.environ['PATH'].split(';'):
|
Chris@87
|
320 filepath = os.path.join(path, dll_name)
|
Chris@87
|
321 if os.path.exists(filepath):
|
Chris@87
|
322 return os.path.abspath(filepath)
|
Chris@87
|
323
|
Chris@87
|
324 return _find_dll_in_winsxs(dll_name) or _find_dll_in_path(dll_name)
|
Chris@87
|
325
|
Chris@87
|
326 def build_msvcr_library(debug=False):
|
Chris@87
|
327 if os.name != 'nt':
|
Chris@87
|
328 return False
|
Chris@87
|
329
|
Chris@87
|
330 msvcr_name = msvc_runtime_library()
|
Chris@87
|
331
|
Chris@87
|
332 # Skip using a custom library for versions < MSVC 8.0
|
Chris@87
|
333 if int(msvcr_name.lstrip('msvcr')) < 80:
|
Chris@87
|
334 log.debug('Skip building msvcr library: custom functionality not present')
|
Chris@87
|
335 return False
|
Chris@87
|
336
|
Chris@87
|
337 if debug:
|
Chris@87
|
338 msvcr_name += 'd'
|
Chris@87
|
339
|
Chris@87
|
340 # Skip if custom library already exists
|
Chris@87
|
341 out_name = "lib%s.a" % msvcr_name
|
Chris@87
|
342 out_file = os.path.join(sys.prefix, 'libs', out_name)
|
Chris@87
|
343 if os.path.isfile(out_file):
|
Chris@87
|
344 log.debug('Skip building msvcr library: "%s" exists' % (out_file))
|
Chris@87
|
345 return True
|
Chris@87
|
346
|
Chris@87
|
347 # Find the msvcr dll
|
Chris@87
|
348 msvcr_dll_name = msvcr_name + '.dll'
|
Chris@87
|
349 dll_file = find_dll(msvcr_dll_name)
|
Chris@87
|
350 if not dll_file:
|
Chris@87
|
351 log.warn('Cannot build msvcr library: "%s" not found' % msvcr_dll_name)
|
Chris@87
|
352 return False
|
Chris@87
|
353
|
Chris@87
|
354 def_name = "lib%s.def" % msvcr_name
|
Chris@87
|
355 def_file = os.path.join(sys.prefix, 'libs', def_name)
|
Chris@87
|
356
|
Chris@87
|
357 log.info('Building msvcr library: "%s" (from %s)' \
|
Chris@87
|
358 % (out_file, dll_file))
|
Chris@87
|
359
|
Chris@87
|
360 # Generate a symbol definition file from the msvcr dll
|
Chris@87
|
361 generate_def(dll_file, def_file)
|
Chris@87
|
362
|
Chris@87
|
363 # Create a custom mingw library for the given symbol definitions
|
Chris@87
|
364 cmd = ['dlltool', '-d', def_file, '-l', out_file]
|
Chris@87
|
365 retcode = subprocess.call(cmd)
|
Chris@87
|
366
|
Chris@87
|
367 # Clean up symbol definitions
|
Chris@87
|
368 os.remove(def_file)
|
Chris@87
|
369
|
Chris@87
|
370 return (not retcode)
|
Chris@87
|
371
|
Chris@87
|
372 def build_import_library():
|
Chris@87
|
373 if os.name != 'nt':
|
Chris@87
|
374 return
|
Chris@87
|
375
|
Chris@87
|
376 arch = get_build_architecture()
|
Chris@87
|
377 if arch == 'AMD64':
|
Chris@87
|
378 return _build_import_library_amd64()
|
Chris@87
|
379 elif arch == 'Intel':
|
Chris@87
|
380 return _build_import_library_x86()
|
Chris@87
|
381 else:
|
Chris@87
|
382 raise ValueError("Unhandled arch %s" % arch)
|
Chris@87
|
383
|
Chris@87
|
384 def _build_import_library_amd64():
|
Chris@87
|
385 dll_file = find_python_dll()
|
Chris@87
|
386
|
Chris@87
|
387 out_name = "libpython%d%d.a" % tuple(sys.version_info[:2])
|
Chris@87
|
388 out_file = os.path.join(sys.prefix, 'libs', out_name)
|
Chris@87
|
389 if os.path.isfile(out_file):
|
Chris@87
|
390 log.debug('Skip building import library: "%s" exists' % (out_file))
|
Chris@87
|
391 return
|
Chris@87
|
392
|
Chris@87
|
393 def_name = "python%d%d.def" % tuple(sys.version_info[:2])
|
Chris@87
|
394 def_file = os.path.join(sys.prefix, 'libs', def_name)
|
Chris@87
|
395
|
Chris@87
|
396 log.info('Building import library (arch=AMD64): "%s" (from %s)' \
|
Chris@87
|
397 % (out_file, dll_file))
|
Chris@87
|
398
|
Chris@87
|
399 generate_def(dll_file, def_file)
|
Chris@87
|
400
|
Chris@87
|
401 cmd = ['dlltool', '-d', def_file, '-l', out_file]
|
Chris@87
|
402 subprocess.Popen(cmd)
|
Chris@87
|
403
|
Chris@87
|
404 def _build_import_library_x86():
|
Chris@87
|
405 """ Build the import libraries for Mingw32-gcc on Windows
|
Chris@87
|
406 """
|
Chris@87
|
407 lib_name = "python%d%d.lib" % tuple(sys.version_info[:2])
|
Chris@87
|
408 lib_file = os.path.join(sys.prefix, 'libs', lib_name)
|
Chris@87
|
409 out_name = "libpython%d%d.a" % tuple(sys.version_info[:2])
|
Chris@87
|
410 out_file = os.path.join(sys.prefix, 'libs', out_name)
|
Chris@87
|
411 if not os.path.isfile(lib_file):
|
Chris@87
|
412 log.warn('Cannot build import library: "%s" not found' % (lib_file))
|
Chris@87
|
413 return
|
Chris@87
|
414 if os.path.isfile(out_file):
|
Chris@87
|
415 log.debug('Skip building import library: "%s" exists' % (out_file))
|
Chris@87
|
416 return
|
Chris@87
|
417 log.info('Building import library (ARCH=x86): "%s"' % (out_file))
|
Chris@87
|
418
|
Chris@87
|
419 from numpy.distutils import lib2def
|
Chris@87
|
420
|
Chris@87
|
421 def_name = "python%d%d.def" % tuple(sys.version_info[:2])
|
Chris@87
|
422 def_file = os.path.join(sys.prefix, 'libs', def_name)
|
Chris@87
|
423 nm_cmd = '%s %s' % (lib2def.DEFAULT_NM, lib_file)
|
Chris@87
|
424 nm_output = lib2def.getnm(nm_cmd)
|
Chris@87
|
425 dlist, flist = lib2def.parse_nm(nm_output)
|
Chris@87
|
426 lib2def.output_def(dlist, flist, lib2def.DEF_HEADER, open(def_file, 'w'))
|
Chris@87
|
427
|
Chris@87
|
428 dll_name = "python%d%d.dll" % tuple(sys.version_info[:2])
|
Chris@87
|
429 args = (dll_name, def_file, out_file)
|
Chris@87
|
430 cmd = 'dlltool --dllname %s --def %s --output-lib %s' % args
|
Chris@87
|
431 status = os.system(cmd)
|
Chris@87
|
432 # for now, fail silently
|
Chris@87
|
433 if status:
|
Chris@87
|
434 log.warn('Failed to build import library for gcc. Linking will fail.')
|
Chris@87
|
435 #if not success:
|
Chris@87
|
436 # msg = "Couldn't find import library, and failed to build it."
|
Chris@87
|
437 # raise DistutilsPlatformError(msg)
|
Chris@87
|
438 return
|
Chris@87
|
439
|
Chris@87
|
440 #=====================================
|
Chris@87
|
441 # Dealing with Visual Studio MANIFESTS
|
Chris@87
|
442 #=====================================
|
Chris@87
|
443
|
Chris@87
|
444 # Functions to deal with visual studio manifests. Manifest are a mechanism to
|
Chris@87
|
445 # enforce strong DLL versioning on windows, and has nothing to do with
|
Chris@87
|
446 # distutils MANIFEST. manifests are XML files with version info, and used by
|
Chris@87
|
447 # the OS loader; they are necessary when linking against a DLL not in the
|
Chris@87
|
448 # system path; in particular, official python 2.6 binary is built against the
|
Chris@87
|
449 # MS runtime 9 (the one from VS 2008), which is not available on most windows
|
Chris@87
|
450 # systems; python 2.6 installer does install it in the Win SxS (Side by side)
|
Chris@87
|
451 # directory, but this requires the manifest for this to work. This is a big
|
Chris@87
|
452 # mess, thanks MS for a wonderful system.
|
Chris@87
|
453
|
Chris@87
|
454 # XXX: ideally, we should use exactly the same version as used by python. I
|
Chris@87
|
455 # submitted a patch to get this version, but it was only included for python
|
Chris@87
|
456 # 2.6.1 and above. So for versions below, we use a "best guess".
|
Chris@87
|
457 _MSVCRVER_TO_FULLVER = {}
|
Chris@87
|
458 if sys.platform == 'win32':
|
Chris@87
|
459 try:
|
Chris@87
|
460 import msvcrt
|
Chris@87
|
461 # I took one version in my SxS directory: no idea if it is the good
|
Chris@87
|
462 # one, and we can't retrieve it from python
|
Chris@87
|
463 _MSVCRVER_TO_FULLVER['80'] = "8.0.50727.42"
|
Chris@87
|
464 _MSVCRVER_TO_FULLVER['90'] = "9.0.21022.8"
|
Chris@87
|
465 # Value from msvcrt.CRT_ASSEMBLY_VERSION under Python 3.3.0 on Windows XP:
|
Chris@87
|
466 _MSVCRVER_TO_FULLVER['100'] = "10.0.30319.460"
|
Chris@87
|
467 if hasattr(msvcrt, "CRT_ASSEMBLY_VERSION"):
|
Chris@87
|
468 major, minor, rest = msvcrt.CRT_ASSEMBLY_VERSION.split(".", 2)
|
Chris@87
|
469 _MSVCRVER_TO_FULLVER[major + minor] = msvcrt.CRT_ASSEMBLY_VERSION
|
Chris@87
|
470 del major, minor, rest
|
Chris@87
|
471 except ImportError:
|
Chris@87
|
472 # If we are here, means python was not built with MSVC. Not sure what to do
|
Chris@87
|
473 # in that case: manifest building will fail, but it should not be used in
|
Chris@87
|
474 # that case anyway
|
Chris@87
|
475 log.warn('Cannot import msvcrt: using manifest will not be possible')
|
Chris@87
|
476
|
Chris@87
|
477 def msvc_manifest_xml(maj, min):
|
Chris@87
|
478 """Given a major and minor version of the MSVCR, returns the
|
Chris@87
|
479 corresponding XML file."""
|
Chris@87
|
480 try:
|
Chris@87
|
481 fullver = _MSVCRVER_TO_FULLVER[str(maj * 10 + min)]
|
Chris@87
|
482 except KeyError:
|
Chris@87
|
483 raise ValueError("Version %d,%d of MSVCRT not supported yet" \
|
Chris@87
|
484 % (maj, min))
|
Chris@87
|
485 # Don't be fooled, it looks like an XML, but it is not. In particular, it
|
Chris@87
|
486 # should not have any space before starting, and its size should be
|
Chris@87
|
487 # divisible by 4, most likely for alignement constraints when the xml is
|
Chris@87
|
488 # embedded in the binary...
|
Chris@87
|
489 # This template was copied directly from the python 2.6 binary (using
|
Chris@87
|
490 # strings.exe from mingw on python.exe).
|
Chris@87
|
491 template = """\
|
Chris@87
|
492 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
Chris@87
|
493 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
Chris@87
|
494 <security>
|
Chris@87
|
495 <requestedPrivileges>
|
Chris@87
|
496 <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
|
Chris@87
|
497 </requestedPrivileges>
|
Chris@87
|
498 </security>
|
Chris@87
|
499 </trustInfo>
|
Chris@87
|
500 <dependency>
|
Chris@87
|
501 <dependentAssembly>
|
Chris@87
|
502 <assemblyIdentity type="win32" name="Microsoft.VC%(maj)d%(min)d.CRT" version="%(fullver)s" processorArchitecture="*" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
|
Chris@87
|
503 </dependentAssembly>
|
Chris@87
|
504 </dependency>
|
Chris@87
|
505 </assembly>"""
|
Chris@87
|
506
|
Chris@87
|
507 return template % {'fullver': fullver, 'maj': maj, 'min': min}
|
Chris@87
|
508
|
Chris@87
|
509 def manifest_rc(name, type='dll'):
|
Chris@87
|
510 """Return the rc file used to generate the res file which will be embedded
|
Chris@87
|
511 as manifest for given manifest file name, of given type ('dll' or
|
Chris@87
|
512 'exe').
|
Chris@87
|
513
|
Chris@87
|
514 Parameters
|
Chris@87
|
515 ----------
|
Chris@87
|
516 name : str
|
Chris@87
|
517 name of the manifest file to embed
|
Chris@87
|
518 type : str {'dll', 'exe'}
|
Chris@87
|
519 type of the binary which will embed the manifest
|
Chris@87
|
520
|
Chris@87
|
521 """
|
Chris@87
|
522 if type == 'dll':
|
Chris@87
|
523 rctype = 2
|
Chris@87
|
524 elif type == 'exe':
|
Chris@87
|
525 rctype = 1
|
Chris@87
|
526 else:
|
Chris@87
|
527 raise ValueError("Type %s not supported" % type)
|
Chris@87
|
528
|
Chris@87
|
529 return """\
|
Chris@87
|
530 #include "winuser.h"
|
Chris@87
|
531 %d RT_MANIFEST %s""" % (rctype, name)
|
Chris@87
|
532
|
Chris@87
|
533 def check_embedded_msvcr_match_linked(msver):
|
Chris@87
|
534 """msver is the ms runtime version used for the MANIFEST."""
|
Chris@87
|
535 # check msvcr major version are the same for linking and
|
Chris@87
|
536 # embedding
|
Chris@87
|
537 msvcv = msvc_runtime_library()
|
Chris@87
|
538 if msvcv:
|
Chris@87
|
539 assert msvcv.startswith("msvcr"), msvcv
|
Chris@87
|
540 # Dealing with something like "mscvr90" or "mscvr100", the last
|
Chris@87
|
541 # last digit is the minor release, want int("9") or int("10"):
|
Chris@87
|
542 maj = int(msvcv[5:-1])
|
Chris@87
|
543 if not maj == int(msver):
|
Chris@87
|
544 raise ValueError(
|
Chris@87
|
545 "Discrepancy between linked msvcr " \
|
Chris@87
|
546 "(%d) and the one about to be embedded " \
|
Chris@87
|
547 "(%d)" % (int(msver), maj))
|
Chris@87
|
548
|
Chris@87
|
549 def configtest_name(config):
|
Chris@87
|
550 base = os.path.basename(config._gen_temp_sourcefile("yo", [], "c"))
|
Chris@87
|
551 return os.path.splitext(base)[0]
|
Chris@87
|
552
|
Chris@87
|
553 def manifest_name(config):
|
Chris@87
|
554 # Get configest name (including suffix)
|
Chris@87
|
555 root = configtest_name(config)
|
Chris@87
|
556 exext = config.compiler.exe_extension
|
Chris@87
|
557 return root + exext + ".manifest"
|
Chris@87
|
558
|
Chris@87
|
559 def rc_name(config):
|
Chris@87
|
560 # Get configest name (including suffix)
|
Chris@87
|
561 root = configtest_name(config)
|
Chris@87
|
562 return root + ".rc"
|
Chris@87
|
563
|
Chris@87
|
564 def generate_manifest(config):
|
Chris@87
|
565 msver = get_build_msvc_version()
|
Chris@87
|
566 if msver is not None:
|
Chris@87
|
567 if msver >= 8:
|
Chris@87
|
568 check_embedded_msvcr_match_linked(msver)
|
Chris@87
|
569 ma = int(msver)
|
Chris@87
|
570 mi = int((msver - ma) * 10)
|
Chris@87
|
571 # Write the manifest file
|
Chris@87
|
572 manxml = msvc_manifest_xml(ma, mi)
|
Chris@87
|
573 man = open(manifest_name(config), "w")
|
Chris@87
|
574 config.temp_files.append(manifest_name(config))
|
Chris@87
|
575 man.write(manxml)
|
Chris@87
|
576 man.close()
|
Chris@87
|
577 # # Write the rc file
|
Chris@87
|
578 # manrc = manifest_rc(manifest_name(self), "exe")
|
Chris@87
|
579 # rc = open(rc_name(self), "w")
|
Chris@87
|
580 # self.temp_files.append(manrc)
|
Chris@87
|
581 # rc.write(manrc)
|
Chris@87
|
582 # rc.close()
|