]>
Commit | Line | Data |
---|---|---|
53e6db90 DC |
1 | """distutils.msvccompiler |
2 | ||
3 | Contains MSVCCompiler, an implementation of the abstract CCompiler class | |
4 | for the Microsoft Visual Studio. | |
5 | """ | |
6 | ||
7 | # Written by Perry Stoll | |
8 | # hacked by Robin Becker and Thomas Heller to do a better job of | |
9 | # finding DevStudio (through the registry) | |
10 | ||
11 | import sys | |
12 | import os | |
13 | import warnings | |
14 | from .errors import ( | |
15 | DistutilsExecError, | |
16 | DistutilsPlatformError, | |
17 | CompileError, | |
18 | LibError, | |
19 | LinkError, | |
20 | ) | |
21 | from .ccompiler import CCompiler, gen_lib_options | |
22 | from ._log import log | |
23 | ||
24 | _can_read_reg = False | |
25 | try: | |
26 | import winreg | |
27 | ||
28 | _can_read_reg = True | |
29 | hkey_mod = winreg | |
30 | ||
31 | RegOpenKeyEx = winreg.OpenKeyEx | |
32 | RegEnumKey = winreg.EnumKey | |
33 | RegEnumValue = winreg.EnumValue | |
34 | RegError = winreg.error | |
35 | ||
36 | except ImportError: | |
37 | try: | |
38 | import win32api | |
39 | import win32con | |
40 | ||
41 | _can_read_reg = True | |
42 | hkey_mod = win32con | |
43 | ||
44 | RegOpenKeyEx = win32api.RegOpenKeyEx | |
45 | RegEnumKey = win32api.RegEnumKey | |
46 | RegEnumValue = win32api.RegEnumValue | |
47 | RegError = win32api.error | |
48 | except ImportError: | |
49 | log.info( | |
50 | "Warning: Can't read registry to find the " | |
51 | "necessary compiler setting\n" | |
52 | "Make sure that Python modules winreg, " | |
53 | "win32api or win32con are installed." | |
54 | ) | |
55 | pass | |
56 | ||
57 | if _can_read_reg: | |
58 | HKEYS = ( | |
59 | hkey_mod.HKEY_USERS, | |
60 | hkey_mod.HKEY_CURRENT_USER, | |
61 | hkey_mod.HKEY_LOCAL_MACHINE, | |
62 | hkey_mod.HKEY_CLASSES_ROOT, | |
63 | ) | |
64 | ||
65 | ||
66 | warnings.warn( | |
67 | "msvccompiler is deprecated and slated to be removed " | |
68 | "in the future. Please discontinue use or file an issue " | |
69 | "with pypa/distutils describing your use case.", | |
70 | DeprecationWarning, | |
71 | ) | |
72 | ||
73 | ||
74 | def read_keys(base, key): | |
75 | """Return list of registry keys.""" | |
76 | try: | |
77 | handle = RegOpenKeyEx(base, key) | |
78 | except RegError: | |
79 | return None | |
80 | L = [] | |
81 | i = 0 | |
82 | while True: | |
83 | try: | |
84 | k = RegEnumKey(handle, i) | |
85 | except RegError: | |
86 | break | |
87 | L.append(k) | |
88 | i += 1 | |
89 | return L | |
90 | ||
91 | ||
92 | def read_values(base, key): | |
93 | """Return dict of registry keys and values. | |
94 | ||
95 | All names are converted to lowercase. | |
96 | """ | |
97 | try: | |
98 | handle = RegOpenKeyEx(base, key) | |
99 | except RegError: | |
100 | return None | |
101 | d = {} | |
102 | i = 0 | |
103 | while True: | |
104 | try: | |
105 | name, value, type = RegEnumValue(handle, i) | |
106 | except RegError: | |
107 | break | |
108 | name = name.lower() | |
109 | d[convert_mbcs(name)] = convert_mbcs(value) | |
110 | i += 1 | |
111 | return d | |
112 | ||
113 | ||
114 | def convert_mbcs(s): | |
115 | dec = getattr(s, "decode", None) | |
116 | if dec is not None: | |
117 | try: | |
118 | s = dec("mbcs") | |
119 | except UnicodeError: | |
120 | pass | |
121 | return s | |
122 | ||
123 | ||
124 | class MacroExpander: | |
125 | def __init__(self, version): | |
126 | self.macros = {} | |
127 | self.load_macros(version) | |
128 | ||
129 | def set_macro(self, macro, path, key): | |
130 | for base in HKEYS: | |
131 | d = read_values(base, path) | |
132 | if d: | |
133 | self.macros["$(%s)" % macro] = d[key] | |
134 | break | |
135 | ||
136 | def load_macros(self, version): | |
137 | vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version | |
138 | self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir") | |
139 | self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir") | |
140 | net = r"Software\Microsoft\.NETFramework" | |
141 | self.set_macro("FrameworkDir", net, "installroot") | |
142 | try: | |
143 | if version > 7.0: | |
144 | self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") | |
145 | else: | |
146 | self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") | |
147 | except KeyError: | |
148 | raise DistutilsPlatformError( | |
149 | """Python was built with Visual Studio 2003; | |
150 | extensions must be built with a compiler than can generate compatible binaries. | |
151 | Visual Studio 2003 was not found on this system. If you have Cygwin installed, | |
152 | you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""" | |
153 | ) | |
154 | ||
155 | p = r"Software\Microsoft\NET Framework Setup\Product" | |
156 | for base in HKEYS: | |
157 | try: | |
158 | h = RegOpenKeyEx(base, p) | |
159 | except RegError: | |
160 | continue | |
161 | key = RegEnumKey(h, 0) | |
162 | d = read_values(base, r"{}\{}".format(p, key)) | |
163 | self.macros["$(FrameworkVersion)"] = d["version"] | |
164 | ||
165 | def sub(self, s): | |
166 | for k, v in self.macros.items(): | |
167 | s = s.replace(k, v) | |
168 | return s | |
169 | ||
170 | ||
171 | def get_build_version(): | |
172 | """Return the version of MSVC that was used to build Python. | |
173 | ||
174 | For Python 2.3 and up, the version number is included in | |
175 | sys.version. For earlier versions, assume the compiler is MSVC 6. | |
176 | """ | |
177 | prefix = "MSC v." | |
178 | i = sys.version.find(prefix) | |
179 | if i == -1: | |
180 | return 6 | |
181 | i = i + len(prefix) | |
182 | s, rest = sys.version[i:].split(" ", 1) | |
183 | majorVersion = int(s[:-2]) - 6 | |
184 | if majorVersion >= 13: | |
185 | # v13 was skipped and should be v14 | |
186 | majorVersion += 1 | |
187 | minorVersion = int(s[2:3]) / 10.0 | |
188 | # I don't think paths are affected by minor version in version 6 | |
189 | if majorVersion == 6: | |
190 | minorVersion = 0 | |
191 | if majorVersion >= 6: | |
192 | return majorVersion + minorVersion | |
193 | # else we don't know what version of the compiler this is | |
194 | return None | |
195 | ||
196 | ||
197 | def get_build_architecture(): | |
198 | """Return the processor architecture. | |
199 | ||
200 | Possible results are "Intel" or "AMD64". | |
201 | """ | |
202 | ||
203 | prefix = " bit (" | |
204 | i = sys.version.find(prefix) | |
205 | if i == -1: | |
206 | return "Intel" | |
207 | j = sys.version.find(")", i) | |
208 | return sys.version[i + len(prefix) : j] | |
209 | ||
210 | ||
211 | def normalize_and_reduce_paths(paths): | |
212 | """Return a list of normalized paths with duplicates removed. | |
213 | ||
214 | The current order of paths is maintained. | |
215 | """ | |
216 | # Paths are normalized so things like: /a and /a/ aren't both preserved. | |
217 | reduced_paths = [] | |
218 | for p in paths: | |
219 | np = os.path.normpath(p) | |
220 | # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. | |
221 | if np not in reduced_paths: | |
222 | reduced_paths.append(np) | |
223 | return reduced_paths | |
224 | ||
225 | ||
226 | class MSVCCompiler(CCompiler): | |
227 | """Concrete class that implements an interface to Microsoft Visual C++, | |
228 | as defined by the CCompiler abstract class.""" | |
229 | ||
230 | compiler_type = 'msvc' | |
231 | ||
232 | # Just set this so CCompiler's constructor doesn't barf. We currently | |
233 | # don't use the 'set_executables()' bureaucracy provided by CCompiler, | |
234 | # as it really isn't necessary for this sort of single-compiler class. | |
235 | # Would be nice to have a consistent interface with UnixCCompiler, | |
236 | # though, so it's worth thinking about. | |
237 | executables = {} | |
238 | ||
239 | # Private class data (need to distinguish C from C++ source for compiler) | |
240 | _c_extensions = ['.c'] | |
241 | _cpp_extensions = ['.cc', '.cpp', '.cxx'] | |
242 | _rc_extensions = ['.rc'] | |
243 | _mc_extensions = ['.mc'] | |
244 | ||
245 | # Needed for the filename generation methods provided by the | |
246 | # base class, CCompiler. | |
247 | src_extensions = _c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions | |
248 | res_extension = '.res' | |
249 | obj_extension = '.obj' | |
250 | static_lib_extension = '.lib' | |
251 | shared_lib_extension = '.dll' | |
252 | static_lib_format = shared_lib_format = '%s%s' | |
253 | exe_extension = '.exe' | |
254 | ||
255 | def __init__(self, verbose=0, dry_run=0, force=0): | |
256 | super().__init__(verbose, dry_run, force) | |
257 | self.__version = get_build_version() | |
258 | self.__arch = get_build_architecture() | |
259 | if self.__arch == "Intel": | |
260 | # x86 | |
261 | if self.__version >= 7: | |
262 | self.__root = r"Software\Microsoft\VisualStudio" | |
263 | self.__macros = MacroExpander(self.__version) | |
264 | else: | |
265 | self.__root = r"Software\Microsoft\Devstudio" | |
266 | self.__product = "Visual Studio version %s" % self.__version | |
267 | else: | |
268 | # Win64. Assume this was built with the platform SDK | |
269 | self.__product = "Microsoft SDK compiler %s" % (self.__version + 6) | |
270 | ||
271 | self.initialized = False | |
272 | ||
273 | def initialize(self): | |
274 | self.__paths = [] | |
275 | if ( | |
276 | "DISTUTILS_USE_SDK" in os.environ | |
277 | and "MSSdk" in os.environ | |
278 | and self.find_exe("cl.exe") | |
279 | ): | |
280 | # Assume that the SDK set up everything alright; don't try to be | |
281 | # smarter | |
282 | self.cc = "cl.exe" | |
283 | self.linker = "link.exe" | |
284 | self.lib = "lib.exe" | |
285 | self.rc = "rc.exe" | |
286 | self.mc = "mc.exe" | |
287 | else: | |
288 | self.__paths = self.get_msvc_paths("path") | |
289 | ||
290 | if len(self.__paths) == 0: | |
291 | raise DistutilsPlatformError( | |
292 | "Python was built with %s, " | |
293 | "and extensions need to be built with the same " | |
294 | "version of the compiler, but it isn't installed." % self.__product | |
295 | ) | |
296 | ||
297 | self.cc = self.find_exe("cl.exe") | |
298 | self.linker = self.find_exe("link.exe") | |
299 | self.lib = self.find_exe("lib.exe") | |
300 | self.rc = self.find_exe("rc.exe") # resource compiler | |
301 | self.mc = self.find_exe("mc.exe") # message compiler | |
302 | self.set_path_env_var('lib') | |
303 | self.set_path_env_var('include') | |
304 | ||
305 | # extend the MSVC path with the current path | |
306 | try: | |
307 | for p in os.environ['path'].split(';'): | |
308 | self.__paths.append(p) | |
309 | except KeyError: | |
310 | pass | |
311 | self.__paths = normalize_and_reduce_paths(self.__paths) | |
312 | os.environ['path'] = ";".join(self.__paths) | |
313 | ||
314 | self.preprocess_options = None | |
315 | if self.__arch == "Intel": | |
316 | self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GX', '/DNDEBUG'] | |
317 | self.compile_options_debug = [ | |
318 | '/nologo', | |
319 | '/Od', | |
320 | '/MDd', | |
321 | '/W3', | |
322 | '/GX', | |
323 | '/Z7', | |
324 | '/D_DEBUG', | |
325 | ] | |
326 | else: | |
327 | # Win64 | |
328 | self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GS-', '/DNDEBUG'] | |
329 | self.compile_options_debug = [ | |
330 | '/nologo', | |
331 | '/Od', | |
332 | '/MDd', | |
333 | '/W3', | |
334 | '/GS-', | |
335 | '/Z7', | |
336 | '/D_DEBUG', | |
337 | ] | |
338 | ||
339 | self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] | |
340 | if self.__version >= 7: | |
341 | self.ldflags_shared_debug = ['/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'] | |
342 | else: | |
343 | self.ldflags_shared_debug = [ | |
344 | '/DLL', | |
345 | '/nologo', | |
346 | '/INCREMENTAL:no', | |
347 | '/pdb:None', | |
348 | '/DEBUG', | |
349 | ] | |
350 | self.ldflags_static = ['/nologo'] | |
351 | ||
352 | self.initialized = True | |
353 | ||
354 | # -- Worker methods ------------------------------------------------ | |
355 | ||
356 | def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): | |
357 | # Copied from ccompiler.py, extended to return .res as 'object'-file | |
358 | # for .rc input file | |
359 | if output_dir is None: | |
360 | output_dir = '' | |
361 | obj_names = [] | |
362 | for src_name in source_filenames: | |
363 | (base, ext) = os.path.splitext(src_name) | |
364 | base = os.path.splitdrive(base)[1] # Chop off the drive | |
365 | base = base[os.path.isabs(base) :] # If abs, chop off leading / | |
366 | if ext not in self.src_extensions: | |
367 | # Better to raise an exception instead of silently continuing | |
368 | # and later complain about sources and targets having | |
369 | # different lengths | |
370 | raise CompileError("Don't know how to compile %s" % src_name) | |
371 | if strip_dir: | |
372 | base = os.path.basename(base) | |
373 | if ext in self._rc_extensions: | |
374 | obj_names.append(os.path.join(output_dir, base + self.res_extension)) | |
375 | elif ext in self._mc_extensions: | |
376 | obj_names.append(os.path.join(output_dir, base + self.res_extension)) | |
377 | else: | |
378 | obj_names.append(os.path.join(output_dir, base + self.obj_extension)) | |
379 | return obj_names | |
380 | ||
381 | def compile( # noqa: C901 | |
382 | self, | |
383 | sources, | |
384 | output_dir=None, | |
385 | macros=None, | |
386 | include_dirs=None, | |
387 | debug=0, | |
388 | extra_preargs=None, | |
389 | extra_postargs=None, | |
390 | depends=None, | |
391 | ): | |
392 | ||
393 | if not self.initialized: | |
394 | self.initialize() | |
395 | compile_info = self._setup_compile( | |
396 | output_dir, macros, include_dirs, sources, depends, extra_postargs | |
397 | ) | |
398 | macros, objects, extra_postargs, pp_opts, build = compile_info | |
399 | ||
400 | compile_opts = extra_preargs or [] | |
401 | compile_opts.append('/c') | |
402 | if debug: | |
403 | compile_opts.extend(self.compile_options_debug) | |
404 | else: | |
405 | compile_opts.extend(self.compile_options) | |
406 | ||
407 | for obj in objects: | |
408 | try: | |
409 | src, ext = build[obj] | |
410 | except KeyError: | |
411 | continue | |
412 | if debug: | |
413 | # pass the full pathname to MSVC in debug mode, | |
414 | # this allows the debugger to find the source file | |
415 | # without asking the user to browse for it | |
416 | src = os.path.abspath(src) | |
417 | ||
418 | if ext in self._c_extensions: | |
419 | input_opt = "/Tc" + src | |
420 | elif ext in self._cpp_extensions: | |
421 | input_opt = "/Tp" + src | |
422 | elif ext in self._rc_extensions: | |
423 | # compile .RC to .RES file | |
424 | input_opt = src | |
425 | output_opt = "/fo" + obj | |
426 | try: | |
427 | self.spawn([self.rc] + pp_opts + [output_opt] + [input_opt]) | |
428 | except DistutilsExecError as msg: | |
429 | raise CompileError(msg) | |
430 | continue | |
431 | elif ext in self._mc_extensions: | |
432 | # Compile .MC to .RC file to .RES file. | |
433 | # * '-h dir' specifies the directory for the | |
434 | # generated include file | |
435 | # * '-r dir' specifies the target directory of the | |
436 | # generated RC file and the binary message resource | |
437 | # it includes | |
438 | # | |
439 | # For now (since there are no options to change this), | |
440 | # we use the source-directory for the include file and | |
441 | # the build directory for the RC file and message | |
442 | # resources. This works at least for win32all. | |
443 | h_dir = os.path.dirname(src) | |
444 | rc_dir = os.path.dirname(obj) | |
445 | try: | |
446 | # first compile .MC to .RC and .H file | |
447 | self.spawn([self.mc] + ['-h', h_dir, '-r', rc_dir] + [src]) | |
448 | base, _ = os.path.splitext(os.path.basename(src)) | |
449 | rc_file = os.path.join(rc_dir, base + '.rc') | |
450 | # then compile .RC to .RES file | |
451 | self.spawn([self.rc] + ["/fo" + obj] + [rc_file]) | |
452 | ||
453 | except DistutilsExecError as msg: | |
454 | raise CompileError(msg) | |
455 | continue | |
456 | else: | |
457 | # how to handle this file? | |
458 | raise CompileError( | |
459 | "Don't know how to compile {} to {}".format(src, obj) | |
460 | ) | |
461 | ||
462 | output_opt = "/Fo" + obj | |
463 | try: | |
464 | self.spawn( | |
465 | [self.cc] | |
466 | + compile_opts | |
467 | + pp_opts | |
468 | + [input_opt, output_opt] | |
469 | + extra_postargs | |
470 | ) | |
471 | except DistutilsExecError as msg: | |
472 | raise CompileError(msg) | |
473 | ||
474 | return objects | |
475 | ||
476 | def create_static_lib( | |
477 | self, objects, output_libname, output_dir=None, debug=0, target_lang=None | |
478 | ): | |
479 | ||
480 | if not self.initialized: | |
481 | self.initialize() | |
482 | (objects, output_dir) = self._fix_object_args(objects, output_dir) | |
483 | output_filename = self.library_filename(output_libname, output_dir=output_dir) | |
484 | ||
485 | if self._need_link(objects, output_filename): | |
486 | lib_args = objects + ['/OUT:' + output_filename] | |
487 | if debug: | |
488 | pass # XXX what goes here? | |
489 | try: | |
490 | self.spawn([self.lib] + lib_args) | |
491 | except DistutilsExecError as msg: | |
492 | raise LibError(msg) | |
493 | else: | |
494 | log.debug("skipping %s (up-to-date)", output_filename) | |
495 | ||
496 | def link( # noqa: C901 | |
497 | self, | |
498 | target_desc, | |
499 | objects, | |
500 | output_filename, | |
501 | output_dir=None, | |
502 | libraries=None, | |
503 | library_dirs=None, | |
504 | runtime_library_dirs=None, | |
505 | export_symbols=None, | |
506 | debug=0, | |
507 | extra_preargs=None, | |
508 | extra_postargs=None, | |
509 | build_temp=None, | |
510 | target_lang=None, | |
511 | ): | |
512 | ||
513 | if not self.initialized: | |
514 | self.initialize() | |
515 | (objects, output_dir) = self._fix_object_args(objects, output_dir) | |
516 | fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) | |
517 | (libraries, library_dirs, runtime_library_dirs) = fixed_args | |
518 | ||
519 | if runtime_library_dirs: | |
520 | self.warn( | |
521 | "I don't know what to do with 'runtime_library_dirs': " | |
522 | + str(runtime_library_dirs) | |
523 | ) | |
524 | ||
525 | lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries) | |
526 | if output_dir is not None: | |
527 | output_filename = os.path.join(output_dir, output_filename) | |
528 | ||
529 | if self._need_link(objects, output_filename): | |
530 | if target_desc == CCompiler.EXECUTABLE: | |
531 | if debug: | |
532 | ldflags = self.ldflags_shared_debug[1:] | |
533 | else: | |
534 | ldflags = self.ldflags_shared[1:] | |
535 | else: | |
536 | if debug: | |
537 | ldflags = self.ldflags_shared_debug | |
538 | else: | |
539 | ldflags = self.ldflags_shared | |
540 | ||
541 | export_opts = [] | |
542 | for sym in export_symbols or []: | |
543 | export_opts.append("/EXPORT:" + sym) | |
544 | ||
545 | ld_args = ( | |
546 | ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename] | |
547 | ) | |
548 | ||
549 | # The MSVC linker generates .lib and .exp files, which cannot be | |
550 | # suppressed by any linker switches. The .lib files may even be | |
551 | # needed! Make sure they are generated in the temporary build | |
552 | # directory. Since they have different names for debug and release | |
553 | # builds, they can go into the same directory. | |
554 | if export_symbols is not None: | |
555 | (dll_name, dll_ext) = os.path.splitext( | |
556 | os.path.basename(output_filename) | |
557 | ) | |
558 | implib_file = os.path.join( | |
559 | os.path.dirname(objects[0]), self.library_filename(dll_name) | |
560 | ) | |
561 | ld_args.append('/IMPLIB:' + implib_file) | |
562 | ||
563 | if extra_preargs: | |
564 | ld_args[:0] = extra_preargs | |
565 | if extra_postargs: | |
566 | ld_args.extend(extra_postargs) | |
567 | ||
568 | self.mkpath(os.path.dirname(output_filename)) | |
569 | try: | |
570 | self.spawn([self.linker] + ld_args) | |
571 | except DistutilsExecError as msg: | |
572 | raise LinkError(msg) | |
573 | ||
574 | else: | |
575 | log.debug("skipping %s (up-to-date)", output_filename) | |
576 | ||
577 | # -- Miscellaneous methods ----------------------------------------- | |
578 | # These are all used by the 'gen_lib_options() function, in | |
579 | # ccompiler.py. | |
580 | ||
581 | def library_dir_option(self, dir): | |
582 | return "/LIBPATH:" + dir | |
583 | ||
584 | def runtime_library_dir_option(self, dir): | |
585 | raise DistutilsPlatformError( | |
586 | "don't know how to set runtime library search path for MSVC++" | |
587 | ) | |
588 | ||
589 | def library_option(self, lib): | |
590 | return self.library_filename(lib) | |
591 | ||
592 | def find_library_file(self, dirs, lib, debug=0): | |
593 | # Prefer a debugging library if found (and requested), but deal | |
594 | # with it if we don't have one. | |
595 | if debug: | |
596 | try_names = [lib + "_d", lib] | |
597 | else: | |
598 | try_names = [lib] | |
599 | for dir in dirs: | |
600 | for name in try_names: | |
601 | libfile = os.path.join(dir, self.library_filename(name)) | |
602 | if os.path.exists(libfile): | |
603 | return libfile | |
604 | else: | |
605 | # Oops, didn't find it in *any* of 'dirs' | |
606 | return None | |
607 | ||
608 | # Helper methods for using the MSVC registry settings | |
609 | ||
610 | def find_exe(self, exe): | |
611 | """Return path to an MSVC executable program. | |
612 | ||
613 | Tries to find the program in several places: first, one of the | |
614 | MSVC program search paths from the registry; next, the directories | |
615 | in the PATH environment variable. If any of those work, return an | |
616 | absolute path that is known to exist. If none of them work, just | |
617 | return the original program name, 'exe'. | |
618 | """ | |
619 | for p in self.__paths: | |
620 | fn = os.path.join(os.path.abspath(p), exe) | |
621 | if os.path.isfile(fn): | |
622 | return fn | |
623 | ||
624 | # didn't find it; try existing path | |
625 | for p in os.environ['Path'].split(';'): | |
626 | fn = os.path.join(os.path.abspath(p), exe) | |
627 | if os.path.isfile(fn): | |
628 | return fn | |
629 | ||
630 | return exe | |
631 | ||
632 | def get_msvc_paths(self, path, platform='x86'): | |
633 | """Get a list of devstudio directories (include, lib or path). | |
634 | ||
635 | Return a list of strings. The list will be empty if unable to | |
636 | access the registry or appropriate registry keys not found. | |
637 | """ | |
638 | if not _can_read_reg: | |
639 | return [] | |
640 | ||
641 | path = path + " dirs" | |
642 | if self.__version >= 7: | |
643 | key = r"{}\{:0.1f}\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories".format( | |
644 | self.__root, | |
645 | self.__version, | |
646 | ) | |
647 | else: | |
648 | key = ( | |
649 | r"%s\6.0\Build System\Components\Platforms" | |
650 | r"\Win32 (%s)\Directories" % (self.__root, platform) | |
651 | ) | |
652 | ||
653 | for base in HKEYS: | |
654 | d = read_values(base, key) | |
655 | if d: | |
656 | if self.__version >= 7: | |
657 | return self.__macros.sub(d[path]).split(";") | |
658 | else: | |
659 | return d[path].split(";") | |
660 | # MSVC 6 seems to create the registry entries we need only when | |
661 | # the GUI is run. | |
662 | if self.__version == 6: | |
663 | for base in HKEYS: | |
664 | if read_values(base, r"%s\6.0" % self.__root) is not None: | |
665 | self.warn( | |
666 | "It seems you have Visual Studio 6 installed, " | |
667 | "but the expected registry settings are not present.\n" | |
668 | "You must at least run the Visual Studio GUI once " | |
669 | "so that these entries are created." | |
670 | ) | |
671 | break | |
672 | return [] | |
673 | ||
674 | def set_path_env_var(self, name): | |
675 | """Set environment variable 'name' to an MSVC path type value. | |
676 | ||
677 | This is equivalent to a SET command prior to execution of spawned | |
678 | commands. | |
679 | """ | |
680 | ||
681 | if name == "lib": | |
682 | p = self.get_msvc_paths("library") | |
683 | else: | |
684 | p = self.get_msvc_paths(name) | |
685 | if p: | |
686 | os.environ[name] = ';'.join(p) | |
687 | ||
688 | ||
689 | if get_build_version() >= 8.0: | |
690 | log.debug("Importing new compiler from distutils.msvc9compiler") | |
691 | OldMSVCCompiler = MSVCCompiler | |
692 | from distutils.msvc9compiler import MSVCCompiler | |
693 | ||
694 | # get_build_architecture not really relevant now we support cross-compile | |
695 | from distutils.msvc9compiler import MacroExpander # noqa: F811 |