Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/setuptools/msvc.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:32:28 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:d30785e31577 | 1:56ad4e20f292 |
---|---|
1 """ | |
2 Improved support for Microsoft Visual C++ compilers. | |
3 | |
4 Known supported compilers: | |
5 -------------------------- | |
6 Microsoft Visual C++ 9.0: | |
7 Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) | |
8 Microsoft Windows SDK 6.1 (x86, x64, ia64) | |
9 Microsoft Windows SDK 7.0 (x86, x64, ia64) | |
10 | |
11 Microsoft Visual C++ 10.0: | |
12 Microsoft Windows SDK 7.1 (x86, x64, ia64) | |
13 | |
14 Microsoft Visual C++ 14.0: | |
15 Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) | |
16 Microsoft Visual Studio 2017 (x86, x64, arm, arm64) | |
17 Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64) | |
18 """ | |
19 | |
20 import os | |
21 import sys | |
22 import platform | |
23 import itertools | |
24 import distutils.errors | |
25 from setuptools.extern.packaging.version import LegacyVersion | |
26 | |
27 from setuptools.extern.six.moves import filterfalse | |
28 | |
29 from .monkey import get_unpatched | |
30 | |
31 if platform.system() == 'Windows': | |
32 from setuptools.extern.six.moves import winreg | |
33 safe_env = os.environ | |
34 else: | |
35 """ | |
36 Mock winreg and environ so the module can be imported | |
37 on this platform. | |
38 """ | |
39 | |
40 class winreg: | |
41 HKEY_USERS = None | |
42 HKEY_CURRENT_USER = None | |
43 HKEY_LOCAL_MACHINE = None | |
44 HKEY_CLASSES_ROOT = None | |
45 | |
46 safe_env = dict() | |
47 | |
48 _msvc9_suppress_errors = ( | |
49 # msvc9compiler isn't available on some platforms | |
50 ImportError, | |
51 | |
52 # msvc9compiler raises DistutilsPlatformError in some | |
53 # environments. See #1118. | |
54 distutils.errors.DistutilsPlatformError, | |
55 ) | |
56 | |
57 try: | |
58 from distutils.msvc9compiler import Reg | |
59 except _msvc9_suppress_errors: | |
60 pass | |
61 | |
62 | |
63 def msvc9_find_vcvarsall(version): | |
64 """ | |
65 Patched "distutils.msvc9compiler.find_vcvarsall" to use the standalone | |
66 compiler build for Python (VCForPython). Fall back to original behavior | |
67 when the standalone compiler is not available. | |
68 | |
69 Redirect the path of "vcvarsall.bat". | |
70 | |
71 Known supported compilers | |
72 ------------------------- | |
73 Microsoft Visual C++ 9.0: | |
74 Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) | |
75 | |
76 Parameters | |
77 ---------- | |
78 version: float | |
79 Required Microsoft Visual C++ version. | |
80 | |
81 Return | |
82 ------ | |
83 vcvarsall.bat path: str | |
84 """ | |
85 VC_BASE = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f' | |
86 key = VC_BASE % ('', version) | |
87 try: | |
88 # Per-user installs register the compiler path here | |
89 productdir = Reg.get_value(key, "installdir") | |
90 except KeyError: | |
91 try: | |
92 # All-user installs on a 64-bit system register here | |
93 key = VC_BASE % ('Wow6432Node\\', version) | |
94 productdir = Reg.get_value(key, "installdir") | |
95 except KeyError: | |
96 productdir = None | |
97 | |
98 if productdir: | |
99 vcvarsall = os.path.os.path.join(productdir, "vcvarsall.bat") | |
100 if os.path.isfile(vcvarsall): | |
101 return vcvarsall | |
102 | |
103 return get_unpatched(msvc9_find_vcvarsall)(version) | |
104 | |
105 | |
106 def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): | |
107 """ | |
108 Patched "distutils.msvc9compiler.query_vcvarsall" for support extra | |
109 compilers. | |
110 | |
111 Set environment without use of "vcvarsall.bat". | |
112 | |
113 Known supported compilers | |
114 ------------------------- | |
115 Microsoft Visual C++ 9.0: | |
116 Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) | |
117 Microsoft Windows SDK 6.1 (x86, x64, ia64) | |
118 Microsoft Windows SDK 7.0 (x86, x64, ia64) | |
119 | |
120 Microsoft Visual C++ 10.0: | |
121 Microsoft Windows SDK 7.1 (x86, x64, ia64) | |
122 | |
123 Parameters | |
124 ---------- | |
125 ver: float | |
126 Required Microsoft Visual C++ version. | |
127 arch: str | |
128 Target architecture. | |
129 | |
130 Return | |
131 ------ | |
132 environment: dict | |
133 """ | |
134 # Try to get environement from vcvarsall.bat (Classical way) | |
135 try: | |
136 orig = get_unpatched(msvc9_query_vcvarsall) | |
137 return orig(ver, arch, *args, **kwargs) | |
138 except distutils.errors.DistutilsPlatformError: | |
139 # Pass error if Vcvarsall.bat is missing | |
140 pass | |
141 except ValueError: | |
142 # Pass error if environment not set after executing vcvarsall.bat | |
143 pass | |
144 | |
145 # If error, try to set environment directly | |
146 try: | |
147 return EnvironmentInfo(arch, ver).return_env() | |
148 except distutils.errors.DistutilsPlatformError as exc: | |
149 _augment_exception(exc, ver, arch) | |
150 raise | |
151 | |
152 | |
153 def msvc14_get_vc_env(plat_spec): | |
154 """ | |
155 Patched "distutils._msvccompiler._get_vc_env" for support extra | |
156 compilers. | |
157 | |
158 Set environment without use of "vcvarsall.bat". | |
159 | |
160 Known supported compilers | |
161 ------------------------- | |
162 Microsoft Visual C++ 14.0: | |
163 Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) | |
164 Microsoft Visual Studio 2017 (x86, x64, arm, arm64) | |
165 Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64) | |
166 | |
167 Parameters | |
168 ---------- | |
169 plat_spec: str | |
170 Target architecture. | |
171 | |
172 Return | |
173 ------ | |
174 environment: dict | |
175 """ | |
176 # Try to get environment from vcvarsall.bat (Classical way) | |
177 try: | |
178 return get_unpatched(msvc14_get_vc_env)(plat_spec) | |
179 except distutils.errors.DistutilsPlatformError: | |
180 # Pass error Vcvarsall.bat is missing | |
181 pass | |
182 | |
183 # If error, try to set environment directly | |
184 try: | |
185 return EnvironmentInfo(plat_spec, vc_min_ver=14.0).return_env() | |
186 except distutils.errors.DistutilsPlatformError as exc: | |
187 _augment_exception(exc, 14.0) | |
188 raise | |
189 | |
190 | |
191 def msvc14_gen_lib_options(*args, **kwargs): | |
192 """ | |
193 Patched "distutils._msvccompiler.gen_lib_options" for fix | |
194 compatibility between "numpy.distutils" and "distutils._msvccompiler" | |
195 (for Numpy < 1.11.2) | |
196 """ | |
197 if "numpy.distutils" in sys.modules: | |
198 import numpy as np | |
199 if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'): | |
200 return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) | |
201 return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs) | |
202 | |
203 | |
204 def _augment_exception(exc, version, arch=''): | |
205 """ | |
206 Add details to the exception message to help guide the user | |
207 as to what action will resolve it. | |
208 """ | |
209 # Error if MSVC++ directory not found or environment not set | |
210 message = exc.args[0] | |
211 | |
212 if "vcvarsall" in message.lower() or "visual c" in message.lower(): | |
213 # Special error message if MSVC++ not installed | |
214 tmpl = 'Microsoft Visual C++ {version:0.1f} is required.' | |
215 message = tmpl.format(**locals()) | |
216 msdownload = 'www.microsoft.com/download/details.aspx?id=%d' | |
217 if version == 9.0: | |
218 if arch.lower().find('ia64') > -1: | |
219 # For VC++ 9.0, if IA64 support is needed, redirect user | |
220 # to Windows SDK 7.0 | |
221 message += ' Get it with "Microsoft Windows SDK 7.0": ' | |
222 message += msdownload % 3138 | |
223 else: | |
224 # For VC++ 9.0 redirect user to Vc++ for Python 2.7 : | |
225 # This redirection link is maintained by Microsoft. | |
226 # Contact vspython@microsoft.com if it needs updating. | |
227 message += ' Get it from http://aka.ms/vcpython27' | |
228 elif version == 10.0: | |
229 # For VC++ 10.0 Redirect user to Windows SDK 7.1 | |
230 message += ' Get it with "Microsoft Windows SDK 7.1": ' | |
231 message += msdownload % 8279 | |
232 elif version >= 14.0: | |
233 # For VC++ 14.0 Redirect user to Visual C++ Build Tools | |
234 message += (' Get it with "Microsoft Visual C++ Build Tools": ' | |
235 r'https://visualstudio.microsoft.com/downloads/') | |
236 | |
237 exc.args = (message, ) | |
238 | |
239 | |
240 class PlatformInfo: | |
241 """ | |
242 Current and Target Architectures informations. | |
243 | |
244 Parameters | |
245 ---------- | |
246 arch: str | |
247 Target architecture. | |
248 """ | |
249 current_cpu = safe_env.get('processor_architecture', '').lower() | |
250 | |
251 def __init__(self, arch): | |
252 self.arch = arch.lower().replace('x64', 'amd64') | |
253 | |
254 @property | |
255 def target_cpu(self): | |
256 return self.arch[self.arch.find('_') + 1:] | |
257 | |
258 def target_is_x86(self): | |
259 return self.target_cpu == 'x86' | |
260 | |
261 def current_is_x86(self): | |
262 return self.current_cpu == 'x86' | |
263 | |
264 def current_dir(self, hidex86=False, x64=False): | |
265 """ | |
266 Current platform specific subfolder. | |
267 | |
268 Parameters | |
269 ---------- | |
270 hidex86: bool | |
271 return '' and not '\x86' if architecture is x86. | |
272 x64: bool | |
273 return '\x64' and not '\amd64' if architecture is amd64. | |
274 | |
275 Return | |
276 ------ | |
277 subfolder: str | |
278 '\target', or '' (see hidex86 parameter) | |
279 """ | |
280 return ( | |
281 '' if (self.current_cpu == 'x86' and hidex86) else | |
282 r'\x64' if (self.current_cpu == 'amd64' and x64) else | |
283 r'\%s' % self.current_cpu | |
284 ) | |
285 | |
286 def target_dir(self, hidex86=False, x64=False): | |
287 r""" | |
288 Target platform specific subfolder. | |
289 | |
290 Parameters | |
291 ---------- | |
292 hidex86: bool | |
293 return '' and not '\x86' if architecture is x86. | |
294 x64: bool | |
295 return '\x64' and not '\amd64' if architecture is amd64. | |
296 | |
297 Return | |
298 ------ | |
299 subfolder: str | |
300 '\current', or '' (see hidex86 parameter) | |
301 """ | |
302 return ( | |
303 '' if (self.target_cpu == 'x86' and hidex86) else | |
304 r'\x64' if (self.target_cpu == 'amd64' and x64) else | |
305 r'\%s' % self.target_cpu | |
306 ) | |
307 | |
308 def cross_dir(self, forcex86=False): | |
309 r""" | |
310 Cross platform specific subfolder. | |
311 | |
312 Parameters | |
313 ---------- | |
314 forcex86: bool | |
315 Use 'x86' as current architecture even if current acritecture is | |
316 not x86. | |
317 | |
318 Return | |
319 ------ | |
320 subfolder: str | |
321 '' if target architecture is current architecture, | |
322 '\current_target' if not. | |
323 """ | |
324 current = 'x86' if forcex86 else self.current_cpu | |
325 return ( | |
326 '' if self.target_cpu == current else | |
327 self.target_dir().replace('\\', '\\%s_' % current) | |
328 ) | |
329 | |
330 | |
331 class RegistryInfo: | |
332 """ | |
333 Microsoft Visual Studio related registry informations. | |
334 | |
335 Parameters | |
336 ---------- | |
337 platform_info: PlatformInfo | |
338 "PlatformInfo" instance. | |
339 """ | |
340 HKEYS = (winreg.HKEY_USERS, | |
341 winreg.HKEY_CURRENT_USER, | |
342 winreg.HKEY_LOCAL_MACHINE, | |
343 winreg.HKEY_CLASSES_ROOT) | |
344 | |
345 def __init__(self, platform_info): | |
346 self.pi = platform_info | |
347 | |
348 @property | |
349 def visualstudio(self): | |
350 """ | |
351 Microsoft Visual Studio root registry key. | |
352 """ | |
353 return 'VisualStudio' | |
354 | |
355 @property | |
356 def sxs(self): | |
357 """ | |
358 Microsoft Visual Studio SxS registry key. | |
359 """ | |
360 return os.path.join(self.visualstudio, 'SxS') | |
361 | |
362 @property | |
363 def vc(self): | |
364 """ | |
365 Microsoft Visual C++ VC7 registry key. | |
366 """ | |
367 return os.path.join(self.sxs, 'VC7') | |
368 | |
369 @property | |
370 def vs(self): | |
371 """ | |
372 Microsoft Visual Studio VS7 registry key. | |
373 """ | |
374 return os.path.join(self.sxs, 'VS7') | |
375 | |
376 @property | |
377 def vc_for_python(self): | |
378 """ | |
379 Microsoft Visual C++ for Python registry key. | |
380 """ | |
381 return r'DevDiv\VCForPython' | |
382 | |
383 @property | |
384 def microsoft_sdk(self): | |
385 """ | |
386 Microsoft SDK registry key. | |
387 """ | |
388 return 'Microsoft SDKs' | |
389 | |
390 @property | |
391 def windows_sdk(self): | |
392 """ | |
393 Microsoft Windows/Platform SDK registry key. | |
394 """ | |
395 return os.path.join(self.microsoft_sdk, 'Windows') | |
396 | |
397 @property | |
398 def netfx_sdk(self): | |
399 """ | |
400 Microsoft .NET Framework SDK registry key. | |
401 """ | |
402 return os.path.join(self.microsoft_sdk, 'NETFXSDK') | |
403 | |
404 @property | |
405 def windows_kits_roots(self): | |
406 """ | |
407 Microsoft Windows Kits Roots registry key. | |
408 """ | |
409 return r'Windows Kits\Installed Roots' | |
410 | |
411 def microsoft(self, key, x86=False): | |
412 """ | |
413 Return key in Microsoft software registry. | |
414 | |
415 Parameters | |
416 ---------- | |
417 key: str | |
418 Registry key path where look. | |
419 x86: str | |
420 Force x86 software registry. | |
421 | |
422 Return | |
423 ------ | |
424 str: value | |
425 """ | |
426 node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node' | |
427 return os.path.join('Software', node64, 'Microsoft', key) | |
428 | |
429 def lookup(self, key, name): | |
430 """ | |
431 Look for values in registry in Microsoft software registry. | |
432 | |
433 Parameters | |
434 ---------- | |
435 key: str | |
436 Registry key path where look. | |
437 name: str | |
438 Value name to find. | |
439 | |
440 Return | |
441 ------ | |
442 str: value | |
443 """ | |
444 KEY_READ = winreg.KEY_READ | |
445 openkey = winreg.OpenKey | |
446 ms = self.microsoft | |
447 for hkey in self.HKEYS: | |
448 try: | |
449 bkey = openkey(hkey, ms(key), 0, KEY_READ) | |
450 except (OSError, IOError): | |
451 if not self.pi.current_is_x86(): | |
452 try: | |
453 bkey = openkey(hkey, ms(key, True), 0, KEY_READ) | |
454 except (OSError, IOError): | |
455 continue | |
456 else: | |
457 continue | |
458 try: | |
459 return winreg.QueryValueEx(bkey, name)[0] | |
460 except (OSError, IOError): | |
461 pass | |
462 | |
463 | |
464 class SystemInfo: | |
465 """ | |
466 Microsoft Windows and Visual Studio related system inormations. | |
467 | |
468 Parameters | |
469 ---------- | |
470 registry_info: RegistryInfo | |
471 "RegistryInfo" instance. | |
472 vc_ver: float | |
473 Required Microsoft Visual C++ version. | |
474 """ | |
475 | |
476 # Variables and properties in this class use originals CamelCase variables | |
477 # names from Microsoft source files for more easy comparaison. | |
478 WinDir = safe_env.get('WinDir', '') | |
479 ProgramFiles = safe_env.get('ProgramFiles', '') | |
480 ProgramFilesx86 = safe_env.get('ProgramFiles(x86)', ProgramFiles) | |
481 | |
482 def __init__(self, registry_info, vc_ver=None): | |
483 self.ri = registry_info | |
484 self.pi = self.ri.pi | |
485 self.vc_ver = vc_ver or self._find_latest_available_vc_ver() | |
486 | |
487 def _find_latest_available_vc_ver(self): | |
488 try: | |
489 return self.find_available_vc_vers()[-1] | |
490 except IndexError: | |
491 err = 'No Microsoft Visual C++ version found' | |
492 raise distutils.errors.DistutilsPlatformError(err) | |
493 | |
494 def find_available_vc_vers(self): | |
495 """ | |
496 Find all available Microsoft Visual C++ versions. | |
497 """ | |
498 ms = self.ri.microsoft | |
499 vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs) | |
500 vc_vers = [] | |
501 for hkey in self.ri.HKEYS: | |
502 for key in vckeys: | |
503 try: | |
504 bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ) | |
505 except (OSError, IOError): | |
506 continue | |
507 subkeys, values, _ = winreg.QueryInfoKey(bkey) | |
508 for i in range(values): | |
509 try: | |
510 ver = float(winreg.EnumValue(bkey, i)[0]) | |
511 if ver not in vc_vers: | |
512 vc_vers.append(ver) | |
513 except ValueError: | |
514 pass | |
515 for i in range(subkeys): | |
516 try: | |
517 ver = float(winreg.EnumKey(bkey, i)) | |
518 if ver not in vc_vers: | |
519 vc_vers.append(ver) | |
520 except ValueError: | |
521 pass | |
522 return sorted(vc_vers) | |
523 | |
524 @property | |
525 def VSInstallDir(self): | |
526 """ | |
527 Microsoft Visual Studio directory. | |
528 """ | |
529 # Default path | |
530 name = 'Microsoft Visual Studio %0.1f' % self.vc_ver | |
531 default = os.path.join(self.ProgramFilesx86, name) | |
532 | |
533 # Try to get path from registry, if fail use default path | |
534 return self.ri.lookup(self.ri.vs, '%0.1f' % self.vc_ver) or default | |
535 | |
536 @property | |
537 def VCInstallDir(self): | |
538 """ | |
539 Microsoft Visual C++ directory. | |
540 """ | |
541 self.VSInstallDir | |
542 | |
543 guess_vc = self._guess_vc() or self._guess_vc_legacy() | |
544 | |
545 # Try to get "VC++ for Python" path from registry as default path | |
546 reg_path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) | |
547 python_vc = self.ri.lookup(reg_path, 'installdir') | |
548 default_vc = os.path.join(python_vc, 'VC') if python_vc else guess_vc | |
549 | |
550 # Try to get path from registry, if fail use default path | |
551 path = self.ri.lookup(self.ri.vc, '%0.1f' % self.vc_ver) or default_vc | |
552 | |
553 if not os.path.isdir(path): | |
554 msg = 'Microsoft Visual C++ directory not found' | |
555 raise distutils.errors.DistutilsPlatformError(msg) | |
556 | |
557 return path | |
558 | |
559 def _guess_vc(self): | |
560 """ | |
561 Locate Visual C for 2017 | |
562 """ | |
563 if self.vc_ver <= 14.0: | |
564 return | |
565 | |
566 default = r'VC\Tools\MSVC' | |
567 guess_vc = os.path.join(self.VSInstallDir, default) | |
568 # Subdir with VC exact version as name | |
569 try: | |
570 vc_exact_ver = os.listdir(guess_vc)[-1] | |
571 return os.path.join(guess_vc, vc_exact_ver) | |
572 except (OSError, IOError, IndexError): | |
573 pass | |
574 | |
575 def _guess_vc_legacy(self): | |
576 """ | |
577 Locate Visual C for versions prior to 2017 | |
578 """ | |
579 default = r'Microsoft Visual Studio %0.1f\VC' % self.vc_ver | |
580 return os.path.join(self.ProgramFilesx86, default) | |
581 | |
582 @property | |
583 def WindowsSdkVersion(self): | |
584 """ | |
585 Microsoft Windows SDK versions for specified MSVC++ version. | |
586 """ | |
587 if self.vc_ver <= 9.0: | |
588 return ('7.0', '6.1', '6.0a') | |
589 elif self.vc_ver == 10.0: | |
590 return ('7.1', '7.0a') | |
591 elif self.vc_ver == 11.0: | |
592 return ('8.0', '8.0a') | |
593 elif self.vc_ver == 12.0: | |
594 return ('8.1', '8.1a') | |
595 elif self.vc_ver >= 14.0: | |
596 return ('10.0', '8.1') | |
597 | |
598 @property | |
599 def WindowsSdkLastVersion(self): | |
600 """ | |
601 Microsoft Windows SDK last version | |
602 """ | |
603 return self._use_last_dir_name(os.path.join( | |
604 self.WindowsSdkDir, 'lib')) | |
605 | |
606 @property | |
607 def WindowsSdkDir(self): | |
608 """ | |
609 Microsoft Windows SDK directory. | |
610 """ | |
611 sdkdir = '' | |
612 for ver in self.WindowsSdkVersion: | |
613 # Try to get it from registry | |
614 loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver) | |
615 sdkdir = self.ri.lookup(loc, 'installationfolder') | |
616 if sdkdir: | |
617 break | |
618 if not sdkdir or not os.path.isdir(sdkdir): | |
619 # Try to get "VC++ for Python" version from registry | |
620 path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) | |
621 install_base = self.ri.lookup(path, 'installdir') | |
622 if install_base: | |
623 sdkdir = os.path.join(install_base, 'WinSDK') | |
624 if not sdkdir or not os.path.isdir(sdkdir): | |
625 # If fail, use default new path | |
626 for ver in self.WindowsSdkVersion: | |
627 intver = ver[:ver.rfind('.')] | |
628 path = r'Microsoft SDKs\Windows Kits\%s' % (intver) | |
629 d = os.path.join(self.ProgramFiles, path) | |
630 if os.path.isdir(d): | |
631 sdkdir = d | |
632 if not sdkdir or not os.path.isdir(sdkdir): | |
633 # If fail, use default old path | |
634 for ver in self.WindowsSdkVersion: | |
635 path = r'Microsoft SDKs\Windows\v%s' % ver | |
636 d = os.path.join(self.ProgramFiles, path) | |
637 if os.path.isdir(d): | |
638 sdkdir = d | |
639 if not sdkdir: | |
640 # If fail, use Platform SDK | |
641 sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK') | |
642 return sdkdir | |
643 | |
644 @property | |
645 def WindowsSDKExecutablePath(self): | |
646 """ | |
647 Microsoft Windows SDK executable directory. | |
648 """ | |
649 # Find WinSDK NetFx Tools registry dir name | |
650 if self.vc_ver <= 11.0: | |
651 netfxver = 35 | |
652 arch = '' | |
653 else: | |
654 netfxver = 40 | |
655 hidex86 = True if self.vc_ver <= 12.0 else False | |
656 arch = self.pi.current_dir(x64=True, hidex86=hidex86) | |
657 fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-')) | |
658 | |
659 # liste all possibles registry paths | |
660 regpaths = [] | |
661 if self.vc_ver >= 14.0: | |
662 for ver in self.NetFxSdkVersion: | |
663 regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)] | |
664 | |
665 for ver in self.WindowsSdkVersion: | |
666 regpaths += [os.path.join(self.ri.windows_sdk, 'v%sA' % ver, fx)] | |
667 | |
668 # Return installation folder from the more recent path | |
669 for path in regpaths: | |
670 execpath = self.ri.lookup(path, 'installationfolder') | |
671 if execpath: | |
672 break | |
673 return execpath | |
674 | |
675 @property | |
676 def FSharpInstallDir(self): | |
677 """ | |
678 Microsoft Visual F# directory. | |
679 """ | |
680 path = r'%0.1f\Setup\F#' % self.vc_ver | |
681 path = os.path.join(self.ri.visualstudio, path) | |
682 return self.ri.lookup(path, 'productdir') or '' | |
683 | |
684 @property | |
685 def UniversalCRTSdkDir(self): | |
686 """ | |
687 Microsoft Universal CRT SDK directory. | |
688 """ | |
689 # Set Kit Roots versions for specified MSVC++ version | |
690 if self.vc_ver >= 14.0: | |
691 vers = ('10', '81') | |
692 else: | |
693 vers = () | |
694 | |
695 # Find path of the more recent Kit | |
696 for ver in vers: | |
697 sdkdir = self.ri.lookup(self.ri.windows_kits_roots, | |
698 'kitsroot%s' % ver) | |
699 if sdkdir: | |
700 break | |
701 return sdkdir or '' | |
702 | |
703 @property | |
704 def UniversalCRTSdkLastVersion(self): | |
705 """ | |
706 Microsoft Universal C Runtime SDK last version | |
707 """ | |
708 return self._use_last_dir_name(os.path.join( | |
709 self.UniversalCRTSdkDir, 'lib')) | |
710 | |
711 @property | |
712 def NetFxSdkVersion(self): | |
713 """ | |
714 Microsoft .NET Framework SDK versions. | |
715 """ | |
716 # Set FxSdk versions for specified MSVC++ version | |
717 if self.vc_ver >= 14.0: | |
718 return ('4.6.1', '4.6') | |
719 else: | |
720 return () | |
721 | |
722 @property | |
723 def NetFxSdkDir(self): | |
724 """ | |
725 Microsoft .NET Framework SDK directory. | |
726 """ | |
727 for ver in self.NetFxSdkVersion: | |
728 loc = os.path.join(self.ri.netfx_sdk, ver) | |
729 sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder') | |
730 if sdkdir: | |
731 break | |
732 return sdkdir or '' | |
733 | |
734 @property | |
735 def FrameworkDir32(self): | |
736 """ | |
737 Microsoft .NET Framework 32bit directory. | |
738 """ | |
739 # Default path | |
740 guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework') | |
741 | |
742 # Try to get path from registry, if fail use default path | |
743 return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw | |
744 | |
745 @property | |
746 def FrameworkDir64(self): | |
747 """ | |
748 Microsoft .NET Framework 64bit directory. | |
749 """ | |
750 # Default path | |
751 guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64') | |
752 | |
753 # Try to get path from registry, if fail use default path | |
754 return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw | |
755 | |
756 @property | |
757 def FrameworkVersion32(self): | |
758 """ | |
759 Microsoft .NET Framework 32bit versions. | |
760 """ | |
761 return self._find_dot_net_versions(32) | |
762 | |
763 @property | |
764 def FrameworkVersion64(self): | |
765 """ | |
766 Microsoft .NET Framework 64bit versions. | |
767 """ | |
768 return self._find_dot_net_versions(64) | |
769 | |
770 def _find_dot_net_versions(self, bits): | |
771 """ | |
772 Find Microsoft .NET Framework versions. | |
773 | |
774 Parameters | |
775 ---------- | |
776 bits: int | |
777 Platform number of bits: 32 or 64. | |
778 """ | |
779 # Find actual .NET version in registry | |
780 reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) | |
781 dot_net_dir = getattr(self, 'FrameworkDir%d' % bits) | |
782 ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or '' | |
783 | |
784 # Set .NET versions for specified MSVC++ version | |
785 if self.vc_ver >= 12.0: | |
786 frameworkver = (ver, 'v4.0') | |
787 elif self.vc_ver >= 10.0: | |
788 frameworkver = ('v4.0.30319' if ver.lower()[:2] != 'v4' else ver, | |
789 'v3.5') | |
790 elif self.vc_ver == 9.0: | |
791 frameworkver = ('v3.5', 'v2.0.50727') | |
792 if self.vc_ver == 8.0: | |
793 frameworkver = ('v3.0', 'v2.0.50727') | |
794 return frameworkver | |
795 | |
796 def _use_last_dir_name(self, path, prefix=''): | |
797 """ | |
798 Return name of the last dir in path or '' if no dir found. | |
799 | |
800 Parameters | |
801 ---------- | |
802 path: str | |
803 Use dirs in this path | |
804 prefix: str | |
805 Use only dirs startings by this prefix | |
806 """ | |
807 matching_dirs = ( | |
808 dir_name | |
809 for dir_name in reversed(os.listdir(path)) | |
810 if os.path.isdir(os.path.join(path, dir_name)) and | |
811 dir_name.startswith(prefix) | |
812 ) | |
813 return next(matching_dirs, None) or '' | |
814 | |
815 | |
816 class EnvironmentInfo: | |
817 """ | |
818 Return environment variables for specified Microsoft Visual C++ version | |
819 and platform : Lib, Include, Path and libpath. | |
820 | |
821 This function is compatible with Microsoft Visual C++ 9.0 to 14.0. | |
822 | |
823 Script created by analysing Microsoft environment configuration files like | |
824 "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ... | |
825 | |
826 Parameters | |
827 ---------- | |
828 arch: str | |
829 Target architecture. | |
830 vc_ver: float | |
831 Required Microsoft Visual C++ version. If not set, autodetect the last | |
832 version. | |
833 vc_min_ver: float | |
834 Minimum Microsoft Visual C++ version. | |
835 """ | |
836 | |
837 # Variables and properties in this class use originals CamelCase variables | |
838 # names from Microsoft source files for more easy comparaison. | |
839 | |
840 def __init__(self, arch, vc_ver=None, vc_min_ver=0): | |
841 self.pi = PlatformInfo(arch) | |
842 self.ri = RegistryInfo(self.pi) | |
843 self.si = SystemInfo(self.ri, vc_ver) | |
844 | |
845 if self.vc_ver < vc_min_ver: | |
846 err = 'No suitable Microsoft Visual C++ version found' | |
847 raise distutils.errors.DistutilsPlatformError(err) | |
848 | |
849 @property | |
850 def vc_ver(self): | |
851 """ | |
852 Microsoft Visual C++ version. | |
853 """ | |
854 return self.si.vc_ver | |
855 | |
856 @property | |
857 def VSTools(self): | |
858 """ | |
859 Microsoft Visual Studio Tools | |
860 """ | |
861 paths = [r'Common7\IDE', r'Common7\Tools'] | |
862 | |
863 if self.vc_ver >= 14.0: | |
864 arch_subdir = self.pi.current_dir(hidex86=True, x64=True) | |
865 paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow'] | |
866 paths += [r'Team Tools\Performance Tools'] | |
867 paths += [r'Team Tools\Performance Tools%s' % arch_subdir] | |
868 | |
869 return [os.path.join(self.si.VSInstallDir, path) for path in paths] | |
870 | |
871 @property | |
872 def VCIncludes(self): | |
873 """ | |
874 Microsoft Visual C++ & Microsoft Foundation Class Includes | |
875 """ | |
876 return [os.path.join(self.si.VCInstallDir, 'Include'), | |
877 os.path.join(self.si.VCInstallDir, r'ATLMFC\Include')] | |
878 | |
879 @property | |
880 def VCLibraries(self): | |
881 """ | |
882 Microsoft Visual C++ & Microsoft Foundation Class Libraries | |
883 """ | |
884 if self.vc_ver >= 15.0: | |
885 arch_subdir = self.pi.target_dir(x64=True) | |
886 else: | |
887 arch_subdir = self.pi.target_dir(hidex86=True) | |
888 paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir] | |
889 | |
890 if self.vc_ver >= 14.0: | |
891 paths += [r'Lib\store%s' % arch_subdir] | |
892 | |
893 return [os.path.join(self.si.VCInstallDir, path) for path in paths] | |
894 | |
895 @property | |
896 def VCStoreRefs(self): | |
897 """ | |
898 Microsoft Visual C++ store references Libraries | |
899 """ | |
900 if self.vc_ver < 14.0: | |
901 return [] | |
902 return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')] | |
903 | |
904 @property | |
905 def VCTools(self): | |
906 """ | |
907 Microsoft Visual C++ Tools | |
908 """ | |
909 si = self.si | |
910 tools = [os.path.join(si.VCInstallDir, 'VCPackages')] | |
911 | |
912 forcex86 = True if self.vc_ver <= 10.0 else False | |
913 arch_subdir = self.pi.cross_dir(forcex86) | |
914 if arch_subdir: | |
915 tools += [os.path.join(si.VCInstallDir, 'Bin%s' % arch_subdir)] | |
916 | |
917 if self.vc_ver == 14.0: | |
918 path = 'Bin%s' % self.pi.current_dir(hidex86=True) | |
919 tools += [os.path.join(si.VCInstallDir, path)] | |
920 | |
921 elif self.vc_ver >= 15.0: | |
922 host_dir = (r'bin\HostX86%s' if self.pi.current_is_x86() else | |
923 r'bin\HostX64%s') | |
924 tools += [os.path.join( | |
925 si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))] | |
926 | |
927 if self.pi.current_cpu != self.pi.target_cpu: | |
928 tools += [os.path.join( | |
929 si.VCInstallDir, host_dir % self.pi.current_dir(x64=True))] | |
930 | |
931 else: | |
932 tools += [os.path.join(si.VCInstallDir, 'Bin')] | |
933 | |
934 return tools | |
935 | |
936 @property | |
937 def OSLibraries(self): | |
938 """ | |
939 Microsoft Windows SDK Libraries | |
940 """ | |
941 if self.vc_ver <= 10.0: | |
942 arch_subdir = self.pi.target_dir(hidex86=True, x64=True) | |
943 return [os.path.join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)] | |
944 | |
945 else: | |
946 arch_subdir = self.pi.target_dir(x64=True) | |
947 lib = os.path.join(self.si.WindowsSdkDir, 'lib') | |
948 libver = self._sdk_subdir | |
949 return [os.path.join(lib, '%sum%s' % (libver , arch_subdir))] | |
950 | |
951 @property | |
952 def OSIncludes(self): | |
953 """ | |
954 Microsoft Windows SDK Include | |
955 """ | |
956 include = os.path.join(self.si.WindowsSdkDir, 'include') | |
957 | |
958 if self.vc_ver <= 10.0: | |
959 return [include, os.path.join(include, 'gl')] | |
960 | |
961 else: | |
962 if self.vc_ver >= 14.0: | |
963 sdkver = self._sdk_subdir | |
964 else: | |
965 sdkver = '' | |
966 return [os.path.join(include, '%sshared' % sdkver), | |
967 os.path.join(include, '%sum' % sdkver), | |
968 os.path.join(include, '%swinrt' % sdkver)] | |
969 | |
970 @property | |
971 def OSLibpath(self): | |
972 """ | |
973 Microsoft Windows SDK Libraries Paths | |
974 """ | |
975 ref = os.path.join(self.si.WindowsSdkDir, 'References') | |
976 libpath = [] | |
977 | |
978 if self.vc_ver <= 9.0: | |
979 libpath += self.OSLibraries | |
980 | |
981 if self.vc_ver >= 11.0: | |
982 libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')] | |
983 | |
984 if self.vc_ver >= 14.0: | |
985 libpath += [ | |
986 ref, | |
987 os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'), | |
988 os.path.join( | |
989 ref, | |
990 'Windows.Foundation.UniversalApiContract', | |
991 '1.0.0.0', | |
992 ), | |
993 os.path.join( | |
994 ref, | |
995 'Windows.Foundation.FoundationContract', | |
996 '1.0.0.0', | |
997 ), | |
998 os.path.join( | |
999 ref, | |
1000 'Windows.Networking.Connectivity.WwanContract', | |
1001 '1.0.0.0', | |
1002 ), | |
1003 os.path.join( | |
1004 self.si.WindowsSdkDir, | |
1005 'ExtensionSDKs', | |
1006 'Microsoft.VCLibs', | |
1007 '%0.1f' % self.vc_ver, | |
1008 'References', | |
1009 'CommonConfiguration', | |
1010 'neutral', | |
1011 ), | |
1012 ] | |
1013 return libpath | |
1014 | |
1015 @property | |
1016 def SdkTools(self): | |
1017 """ | |
1018 Microsoft Windows SDK Tools | |
1019 """ | |
1020 return list(self._sdk_tools()) | |
1021 | |
1022 def _sdk_tools(self): | |
1023 """ | |
1024 Microsoft Windows SDK Tools paths generator | |
1025 """ | |
1026 if self.vc_ver < 15.0: | |
1027 bin_dir = 'Bin' if self.vc_ver <= 11.0 else r'Bin\x86' | |
1028 yield os.path.join(self.si.WindowsSdkDir, bin_dir) | |
1029 | |
1030 if not self.pi.current_is_x86(): | |
1031 arch_subdir = self.pi.current_dir(x64=True) | |
1032 path = 'Bin%s' % arch_subdir | |
1033 yield os.path.join(self.si.WindowsSdkDir, path) | |
1034 | |
1035 if self.vc_ver == 10.0 or self.vc_ver == 11.0: | |
1036 if self.pi.target_is_x86(): | |
1037 arch_subdir = '' | |
1038 else: | |
1039 arch_subdir = self.pi.current_dir(hidex86=True, x64=True) | |
1040 path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir | |
1041 yield os.path.join(self.si.WindowsSdkDir, path) | |
1042 | |
1043 elif self.vc_ver >= 15.0: | |
1044 path = os.path.join(self.si.WindowsSdkDir, 'Bin') | |
1045 arch_subdir = self.pi.current_dir(x64=True) | |
1046 sdkver = self.si.WindowsSdkLastVersion | |
1047 yield os.path.join(path, '%s%s' % (sdkver, arch_subdir)) | |
1048 | |
1049 if self.si.WindowsSDKExecutablePath: | |
1050 yield self.si.WindowsSDKExecutablePath | |
1051 | |
1052 @property | |
1053 def _sdk_subdir(self): | |
1054 """ | |
1055 Microsoft Windows SDK version subdir | |
1056 """ | |
1057 ucrtver = self.si.WindowsSdkLastVersion | |
1058 return ('%s\\' % ucrtver) if ucrtver else '' | |
1059 | |
1060 @property | |
1061 def SdkSetup(self): | |
1062 """ | |
1063 Microsoft Windows SDK Setup | |
1064 """ | |
1065 if self.vc_ver > 9.0: | |
1066 return [] | |
1067 | |
1068 return [os.path.join(self.si.WindowsSdkDir, 'Setup')] | |
1069 | |
1070 @property | |
1071 def FxTools(self): | |
1072 """ | |
1073 Microsoft .NET Framework Tools | |
1074 """ | |
1075 pi = self.pi | |
1076 si = self.si | |
1077 | |
1078 if self.vc_ver <= 10.0: | |
1079 include32 = True | |
1080 include64 = not pi.target_is_x86() and not pi.current_is_x86() | |
1081 else: | |
1082 include32 = pi.target_is_x86() or pi.current_is_x86() | |
1083 include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64' | |
1084 | |
1085 tools = [] | |
1086 if include32: | |
1087 tools += [os.path.join(si.FrameworkDir32, ver) | |
1088 for ver in si.FrameworkVersion32] | |
1089 if include64: | |
1090 tools += [os.path.join(si.FrameworkDir64, ver) | |
1091 for ver in si.FrameworkVersion64] | |
1092 return tools | |
1093 | |
1094 @property | |
1095 def NetFxSDKLibraries(self): | |
1096 """ | |
1097 Microsoft .Net Framework SDK Libraries | |
1098 """ | |
1099 if self.vc_ver < 14.0 or not self.si.NetFxSdkDir: | |
1100 return [] | |
1101 | |
1102 arch_subdir = self.pi.target_dir(x64=True) | |
1103 return [os.path.join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)] | |
1104 | |
1105 @property | |
1106 def NetFxSDKIncludes(self): | |
1107 """ | |
1108 Microsoft .Net Framework SDK Includes | |
1109 """ | |
1110 if self.vc_ver < 14.0 or not self.si.NetFxSdkDir: | |
1111 return [] | |
1112 | |
1113 return [os.path.join(self.si.NetFxSdkDir, r'include\um')] | |
1114 | |
1115 @property | |
1116 def VsTDb(self): | |
1117 """ | |
1118 Microsoft Visual Studio Team System Database | |
1119 """ | |
1120 return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')] | |
1121 | |
1122 @property | |
1123 def MSBuild(self): | |
1124 """ | |
1125 Microsoft Build Engine | |
1126 """ | |
1127 if self.vc_ver < 12.0: | |
1128 return [] | |
1129 elif self.vc_ver < 15.0: | |
1130 base_path = self.si.ProgramFilesx86 | |
1131 arch_subdir = self.pi.current_dir(hidex86=True) | |
1132 else: | |
1133 base_path = self.si.VSInstallDir | |
1134 arch_subdir = '' | |
1135 | |
1136 path = r'MSBuild\%0.1f\bin%s' % (self.vc_ver, arch_subdir) | |
1137 build = [os.path.join(base_path, path)] | |
1138 | |
1139 if self.vc_ver >= 15.0: | |
1140 # Add Roslyn C# & Visual Basic Compiler | |
1141 build += [os.path.join(base_path, path, 'Roslyn')] | |
1142 | |
1143 return build | |
1144 | |
1145 @property | |
1146 def HTMLHelpWorkshop(self): | |
1147 """ | |
1148 Microsoft HTML Help Workshop | |
1149 """ | |
1150 if self.vc_ver < 11.0: | |
1151 return [] | |
1152 | |
1153 return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')] | |
1154 | |
1155 @property | |
1156 def UCRTLibraries(self): | |
1157 """ | |
1158 Microsoft Universal C Runtime SDK Libraries | |
1159 """ | |
1160 if self.vc_ver < 14.0: | |
1161 return [] | |
1162 | |
1163 arch_subdir = self.pi.target_dir(x64=True) | |
1164 lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib') | |
1165 ucrtver = self._ucrt_subdir | |
1166 return [os.path.join(lib, '%sucrt%s' % (ucrtver, arch_subdir))] | |
1167 | |
1168 @property | |
1169 def UCRTIncludes(self): | |
1170 """ | |
1171 Microsoft Universal C Runtime SDK Include | |
1172 """ | |
1173 if self.vc_ver < 14.0: | |
1174 return [] | |
1175 | |
1176 include = os.path.join(self.si.UniversalCRTSdkDir, 'include') | |
1177 return [os.path.join(include, '%sucrt' % self._ucrt_subdir)] | |
1178 | |
1179 @property | |
1180 def _ucrt_subdir(self): | |
1181 """ | |
1182 Microsoft Universal C Runtime SDK version subdir | |
1183 """ | |
1184 ucrtver = self.si.UniversalCRTSdkLastVersion | |
1185 return ('%s\\' % ucrtver) if ucrtver else '' | |
1186 | |
1187 @property | |
1188 def FSharp(self): | |
1189 """ | |
1190 Microsoft Visual F# | |
1191 """ | |
1192 if self.vc_ver < 11.0 and self.vc_ver > 12.0: | |
1193 return [] | |
1194 | |
1195 return self.si.FSharpInstallDir | |
1196 | |
1197 @property | |
1198 def VCRuntimeRedist(self): | |
1199 """ | |
1200 Microsoft Visual C++ runtime redistribuable dll | |
1201 """ | |
1202 arch_subdir = self.pi.target_dir(x64=True) | |
1203 if self.vc_ver < 15: | |
1204 redist_path = self.si.VCInstallDir | |
1205 vcruntime = 'redist%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll' | |
1206 else: | |
1207 redist_path = self.si.VCInstallDir.replace('\\Tools', '\\Redist') | |
1208 vcruntime = 'onecore%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll' | |
1209 | |
1210 # Visual Studio 2017 is still Visual C++ 14.0 | |
1211 dll_ver = 14.0 if self.vc_ver == 15 else self.vc_ver | |
1212 | |
1213 vcruntime = vcruntime % (arch_subdir, self.vc_ver, dll_ver) | |
1214 return os.path.join(redist_path, vcruntime) | |
1215 | |
1216 def return_env(self, exists=True): | |
1217 """ | |
1218 Return environment dict. | |
1219 | |
1220 Parameters | |
1221 ---------- | |
1222 exists: bool | |
1223 It True, only return existing paths. | |
1224 """ | |
1225 env = dict( | |
1226 include=self._build_paths('include', | |
1227 [self.VCIncludes, | |
1228 self.OSIncludes, | |
1229 self.UCRTIncludes, | |
1230 self.NetFxSDKIncludes], | |
1231 exists), | |
1232 lib=self._build_paths('lib', | |
1233 [self.VCLibraries, | |
1234 self.OSLibraries, | |
1235 self.FxTools, | |
1236 self.UCRTLibraries, | |
1237 self.NetFxSDKLibraries], | |
1238 exists), | |
1239 libpath=self._build_paths('libpath', | |
1240 [self.VCLibraries, | |
1241 self.FxTools, | |
1242 self.VCStoreRefs, | |
1243 self.OSLibpath], | |
1244 exists), | |
1245 path=self._build_paths('path', | |
1246 [self.VCTools, | |
1247 self.VSTools, | |
1248 self.VsTDb, | |
1249 self.SdkTools, | |
1250 self.SdkSetup, | |
1251 self.FxTools, | |
1252 self.MSBuild, | |
1253 self.HTMLHelpWorkshop, | |
1254 self.FSharp], | |
1255 exists), | |
1256 ) | |
1257 if self.vc_ver >= 14 and os.path.isfile(self.VCRuntimeRedist): | |
1258 env['py_vcruntime_redist'] = self.VCRuntimeRedist | |
1259 return env | |
1260 | |
1261 def _build_paths(self, name, spec_path_lists, exists): | |
1262 """ | |
1263 Given an environment variable name and specified paths, | |
1264 return a pathsep-separated string of paths containing | |
1265 unique, extant, directories from those paths and from | |
1266 the environment variable. Raise an error if no paths | |
1267 are resolved. | |
1268 """ | |
1269 # flatten spec_path_lists | |
1270 spec_paths = itertools.chain.from_iterable(spec_path_lists) | |
1271 env_paths = safe_env.get(name, '').split(os.pathsep) | |
1272 paths = itertools.chain(spec_paths, env_paths) | |
1273 extant_paths = list(filter(os.path.isdir, paths)) if exists else paths | |
1274 if not extant_paths: | |
1275 msg = "%s environment variable is empty" % name.upper() | |
1276 raise distutils.errors.DistutilsPlatformError(msg) | |
1277 unique_paths = self._unique_everseen(extant_paths) | |
1278 return os.pathsep.join(unique_paths) | |
1279 | |
1280 # from Python docs | |
1281 def _unique_everseen(self, iterable, key=None): | |
1282 """ | |
1283 List unique elements, preserving order. | |
1284 Remember all elements ever seen. | |
1285 | |
1286 _unique_everseen('AAAABBBCCDAABBB') --> A B C D | |
1287 | |
1288 _unique_everseen('ABBCcAD', str.lower) --> A B C D | |
1289 """ | |
1290 seen = set() | |
1291 seen_add = seen.add | |
1292 if key is None: | |
1293 for element in filterfalse(seen.__contains__, iterable): | |
1294 seen_add(element) | |
1295 yield element | |
1296 else: | |
1297 for element in iterable: | |
1298 k = key(element) | |
1299 if k not in seen: | |
1300 seen_add(k) | |
1301 yield element |