]> crepu.dev Git - config.git/blob - djavu-asus/elpy/rpc-venv/lib/python3.11/site-packages/setuptools/msvc.py
ActualizaciĆ³n de Readme
[config.git] / djavu-asus / elpy / rpc-venv / lib / python3.11 / site-packages / setuptools / msvc.py
1 """
2 Improved support for Microsoft Visual C++ compilers.
3
4 Known supported compilers:
5 --------------------------
6 Microsoft Visual C++ 14.X:
7 Microsoft Visual C++ Build Tools 2015 (x86, x64, arm)
8 Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64)
9 Microsoft Visual Studio Build Tools 2019 (x86, x64, arm, arm64)
10
11 This may also support compilers shipped with compatible Visual Studio versions.
12 """
13
14 import json
15 from io import open
16 from os import listdir, pathsep
17 from os.path import join, isfile, isdir, dirname
18 import sys
19 import contextlib
20 import platform
21 import itertools
22 import subprocess
23 import distutils.errors
24 from setuptools.extern.packaging.version import LegacyVersion
25 from setuptools.extern.more_itertools import unique_everseen
26
27 from .monkey import get_unpatched
28
29 if platform.system() == 'Windows':
30 import winreg
31 from os import environ
32 else:
33 # Mock winreg and environ so the module can be imported on this platform.
34
35 class winreg:
36 HKEY_USERS = None
37 HKEY_CURRENT_USER = None
38 HKEY_LOCAL_MACHINE = None
39 HKEY_CLASSES_ROOT = None
40
41 environ = dict()
42
43
44 def _msvc14_find_vc2015():
45 """Python 3.8 "distutils/_msvccompiler.py" backport"""
46 try:
47 key = winreg.OpenKey(
48 winreg.HKEY_LOCAL_MACHINE,
49 r"Software\Microsoft\VisualStudio\SxS\VC7",
50 0,
51 winreg.KEY_READ | winreg.KEY_WOW64_32KEY
52 )
53 except OSError:
54 return None, None
55
56 best_version = 0
57 best_dir = None
58 with key:
59 for i in itertools.count():
60 try:
61 v, vc_dir, vt = winreg.EnumValue(key, i)
62 except OSError:
63 break
64 if v and vt == winreg.REG_SZ and isdir(vc_dir):
65 try:
66 version = int(float(v))
67 except (ValueError, TypeError):
68 continue
69 if version >= 14 and version > best_version:
70 best_version, best_dir = version, vc_dir
71 return best_version, best_dir
72
73
74 def _msvc14_find_vc2017():
75 """Python 3.8 "distutils/_msvccompiler.py" backport
76
77 Returns "15, path" based on the result of invoking vswhere.exe
78 If no install is found, returns "None, None"
79
80 The version is returned to avoid unnecessarily changing the function
81 result. It may be ignored when the path is not None.
82
83 If vswhere.exe is not available, by definition, VS 2017 is not
84 installed.
85 """
86 root = environ.get("ProgramFiles(x86)") or environ.get("ProgramFiles")
87 if not root:
88 return None, None
89
90 try:
91 path = subprocess.check_output([
92 join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"),
93 "-latest",
94 "-prerelease",
95 "-requiresAny",
96 "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
97 "-requires", "Microsoft.VisualStudio.Workload.WDExpress",
98 "-property", "installationPath",
99 "-products", "*",
100 ]).decode(encoding="mbcs", errors="strict").strip()
101 except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
102 return None, None
103
104 path = join(path, "VC", "Auxiliary", "Build")
105 if isdir(path):
106 return 15, path
107
108 return None, None
109
110
111 PLAT_SPEC_TO_RUNTIME = {
112 'x86': 'x86',
113 'x86_amd64': 'x64',
114 'x86_arm': 'arm',
115 'x86_arm64': 'arm64'
116 }
117
118
119 def _msvc14_find_vcvarsall(plat_spec):
120 """Python 3.8 "distutils/_msvccompiler.py" backport"""
121 _, best_dir = _msvc14_find_vc2017()
122 vcruntime = None
123
124 if plat_spec in PLAT_SPEC_TO_RUNTIME:
125 vcruntime_plat = PLAT_SPEC_TO_RUNTIME[plat_spec]
126 else:
127 vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86'
128
129 if best_dir:
130 vcredist = join(best_dir, "..", "..", "redist", "MSVC", "**",
131 vcruntime_plat, "Microsoft.VC14*.CRT",
132 "vcruntime140.dll")
133 try:
134 import glob
135 vcruntime = glob.glob(vcredist, recursive=True)[-1]
136 except (ImportError, OSError, LookupError):
137 vcruntime = None
138
139 if not best_dir:
140 best_version, best_dir = _msvc14_find_vc2015()
141 if best_version:
142 vcruntime = join(best_dir, 'redist', vcruntime_plat,
143 "Microsoft.VC140.CRT", "vcruntime140.dll")
144
145 if not best_dir:
146 return None, None
147
148 vcvarsall = join(best_dir, "vcvarsall.bat")
149 if not isfile(vcvarsall):
150 return None, None
151
152 if not vcruntime or not isfile(vcruntime):
153 vcruntime = None
154
155 return vcvarsall, vcruntime
156
157
158 def _msvc14_get_vc_env(plat_spec):
159 """Python 3.8 "distutils/_msvccompiler.py" backport"""
160 if "DISTUTILS_USE_SDK" in environ:
161 return {
162 key.lower(): value
163 for key, value in environ.items()
164 }
165
166 vcvarsall, vcruntime = _msvc14_find_vcvarsall(plat_spec)
167 if not vcvarsall:
168 raise distutils.errors.DistutilsPlatformError(
169 "Unable to find vcvarsall.bat"
170 )
171
172 try:
173 out = subprocess.check_output(
174 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec),
175 stderr=subprocess.STDOUT,
176 ).decode('utf-16le', errors='replace')
177 except subprocess.CalledProcessError as exc:
178 raise distutils.errors.DistutilsPlatformError(
179 "Error executing {}".format(exc.cmd)
180 ) from exc
181
182 env = {
183 key.lower(): value
184 for key, _, value in
185 (line.partition('=') for line in out.splitlines())
186 if key and value
187 }
188
189 if vcruntime:
190 env['py_vcruntime_redist'] = vcruntime
191 return env
192
193
194 def msvc14_get_vc_env(plat_spec):
195 """
196 Patched "distutils._msvccompiler._get_vc_env" for support extra
197 Microsoft Visual C++ 14.X compilers.
198
199 Set environment without use of "vcvarsall.bat".
200
201 Parameters
202 ----------
203 plat_spec: str
204 Target architecture.
205
206 Return
207 ------
208 dict
209 environment
210 """
211
212 # Always use backport from CPython 3.8
213 try:
214 return _msvc14_get_vc_env(plat_spec)
215 except distutils.errors.DistutilsPlatformError as exc:
216 _augment_exception(exc, 14.0)
217 raise
218
219
220 def msvc14_gen_lib_options(*args, **kwargs):
221 """
222 Patched "distutils._msvccompiler.gen_lib_options" for fix
223 compatibility between "numpy.distutils" and "distutils._msvccompiler"
224 (for Numpy < 1.11.2)
225 """
226 if "numpy.distutils" in sys.modules:
227 import numpy as np
228 if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'):
229 return np.distutils.ccompiler.gen_lib_options(*args, **kwargs)
230 return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs)
231
232
233 def _augment_exception(exc, version, arch=''):
234 """
235 Add details to the exception message to help guide the user
236 as to what action will resolve it.
237 """
238 # Error if MSVC++ directory not found or environment not set
239 message = exc.args[0]
240
241 if "vcvarsall" in message.lower() or "visual c" in message.lower():
242 # Special error message if MSVC++ not installed
243 tmpl = 'Microsoft Visual C++ {version:0.1f} or greater is required.'
244 message = tmpl.format(**locals())
245 msdownload = 'www.microsoft.com/download/details.aspx?id=%d'
246 if version == 9.0:
247 if arch.lower().find('ia64') > -1:
248 # For VC++ 9.0, if IA64 support is needed, redirect user
249 # to Windows SDK 7.0.
250 # Note: No download link available from Microsoft.
251 message += ' Get it with "Microsoft Windows SDK 7.0"'
252 else:
253 # For VC++ 9.0 redirect user to Vc++ for Python 2.7 :
254 # This redirection link is maintained by Microsoft.
255 # Contact vspython@microsoft.com if it needs updating.
256 message += ' Get it from http://aka.ms/vcpython27'
257 elif version == 10.0:
258 # For VC++ 10.0 Redirect user to Windows SDK 7.1
259 message += ' Get it with "Microsoft Windows SDK 7.1": '
260 message += msdownload % 8279
261 elif version >= 14.0:
262 # For VC++ 14.X Redirect user to latest Visual C++ Build Tools
263 message += (' Get it with "Microsoft C++ Build Tools": '
264 r'https://visualstudio.microsoft.com'
265 r'/visual-cpp-build-tools/')
266
267 exc.args = (message, )
268
269
270 class PlatformInfo:
271 """
272 Current and Target Architectures information.
273
274 Parameters
275 ----------
276 arch: str
277 Target architecture.
278 """
279 current_cpu = environ.get('processor_architecture', '').lower()
280
281 def __init__(self, arch):
282 self.arch = arch.lower().replace('x64', 'amd64')
283
284 @property
285 def target_cpu(self):
286 """
287 Return Target CPU architecture.
288
289 Return
290 ------
291 str
292 Target CPU
293 """
294 return self.arch[self.arch.find('_') + 1:]
295
296 def target_is_x86(self):
297 """
298 Return True if target CPU is x86 32 bits..
299
300 Return
301 ------
302 bool
303 CPU is x86 32 bits
304 """
305 return self.target_cpu == 'x86'
306
307 def current_is_x86(self):
308 """
309 Return True if current CPU is x86 32 bits..
310
311 Return
312 ------
313 bool
314 CPU is x86 32 bits
315 """
316 return self.current_cpu == 'x86'
317
318 def current_dir(self, hidex86=False, x64=False):
319 """
320 Current platform specific subfolder.
321
322 Parameters
323 ----------
324 hidex86: bool
325 return '' and not '\x86' if architecture is x86.
326 x64: bool
327 return '\x64' and not '\amd64' if architecture is amd64.
328
329 Return
330 ------
331 str
332 subfolder: '\target', or '' (see hidex86 parameter)
333 """
334 return (
335 '' if (self.current_cpu == 'x86' and hidex86) else
336 r'\x64' if (self.current_cpu == 'amd64' and x64) else
337 r'\%s' % self.current_cpu
338 )
339
340 def target_dir(self, hidex86=False, x64=False):
341 r"""
342 Target platform specific subfolder.
343
344 Parameters
345 ----------
346 hidex86: bool
347 return '' and not '\x86' if architecture is x86.
348 x64: bool
349 return '\x64' and not '\amd64' if architecture is amd64.
350
351 Return
352 ------
353 str
354 subfolder: '\current', or '' (see hidex86 parameter)
355 """
356 return (
357 '' if (self.target_cpu == 'x86' and hidex86) else
358 r'\x64' if (self.target_cpu == 'amd64' and x64) else
359 r'\%s' % self.target_cpu
360 )
361
362 def cross_dir(self, forcex86=False):
363 r"""
364 Cross platform specific subfolder.
365
366 Parameters
367 ----------
368 forcex86: bool
369 Use 'x86' as current architecture even if current architecture is
370 not x86.
371
372 Return
373 ------
374 str
375 subfolder: '' if target architecture is current architecture,
376 '\current_target' if not.
377 """
378 current = 'x86' if forcex86 else self.current_cpu
379 return (
380 '' if self.target_cpu == current else
381 self.target_dir().replace('\\', '\\%s_' % current)
382 )
383
384
385 class RegistryInfo:
386 """
387 Microsoft Visual Studio related registry information.
388
389 Parameters
390 ----------
391 platform_info: PlatformInfo
392 "PlatformInfo" instance.
393 """
394 HKEYS = (winreg.HKEY_USERS,
395 winreg.HKEY_CURRENT_USER,
396 winreg.HKEY_LOCAL_MACHINE,
397 winreg.HKEY_CLASSES_ROOT)
398
399 def __init__(self, platform_info):
400 self.pi = platform_info
401
402 @property
403 def visualstudio(self):
404 """
405 Microsoft Visual Studio root registry key.
406
407 Return
408 ------
409 str
410 Registry key
411 """
412 return 'VisualStudio'
413
414 @property
415 def sxs(self):
416 """
417 Microsoft Visual Studio SxS registry key.
418
419 Return
420 ------
421 str
422 Registry key
423 """
424 return join(self.visualstudio, 'SxS')
425
426 @property
427 def vc(self):
428 """
429 Microsoft Visual C++ VC7 registry key.
430
431 Return
432 ------
433 str
434 Registry key
435 """
436 return join(self.sxs, 'VC7')
437
438 @property
439 def vs(self):
440 """
441 Microsoft Visual Studio VS7 registry key.
442
443 Return
444 ------
445 str
446 Registry key
447 """
448 return join(self.sxs, 'VS7')
449
450 @property
451 def vc_for_python(self):
452 """
453 Microsoft Visual C++ for Python registry key.
454
455 Return
456 ------
457 str
458 Registry key
459 """
460 return r'DevDiv\VCForPython'
461
462 @property
463 def microsoft_sdk(self):
464 """
465 Microsoft SDK registry key.
466
467 Return
468 ------
469 str
470 Registry key
471 """
472 return 'Microsoft SDKs'
473
474 @property
475 def windows_sdk(self):
476 """
477 Microsoft Windows/Platform SDK registry key.
478
479 Return
480 ------
481 str
482 Registry key
483 """
484 return join(self.microsoft_sdk, 'Windows')
485
486 @property
487 def netfx_sdk(self):
488 """
489 Microsoft .NET Framework SDK registry key.
490
491 Return
492 ------
493 str
494 Registry key
495 """
496 return join(self.microsoft_sdk, 'NETFXSDK')
497
498 @property
499 def windows_kits_roots(self):
500 """
501 Microsoft Windows Kits Roots registry key.
502
503 Return
504 ------
505 str
506 Registry key
507 """
508 return r'Windows Kits\Installed Roots'
509
510 def microsoft(self, key, x86=False):
511 """
512 Return key in Microsoft software registry.
513
514 Parameters
515 ----------
516 key: str
517 Registry key path where look.
518 x86: str
519 Force x86 software registry.
520
521 Return
522 ------
523 str
524 Registry key
525 """
526 node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node'
527 return join('Software', node64, 'Microsoft', key)
528
529 def lookup(self, key, name):
530 """
531 Look for values in registry in Microsoft software registry.
532
533 Parameters
534 ----------
535 key: str
536 Registry key path where look.
537 name: str
538 Value name to find.
539
540 Return
541 ------
542 str
543 value
544 """
545 key_read = winreg.KEY_READ
546 openkey = winreg.OpenKey
547 closekey = winreg.CloseKey
548 ms = self.microsoft
549 for hkey in self.HKEYS:
550 bkey = None
551 try:
552 bkey = openkey(hkey, ms(key), 0, key_read)
553 except (OSError, IOError):
554 if not self.pi.current_is_x86():
555 try:
556 bkey = openkey(hkey, ms(key, True), 0, key_read)
557 except (OSError, IOError):
558 continue
559 else:
560 continue
561 try:
562 return winreg.QueryValueEx(bkey, name)[0]
563 except (OSError, IOError):
564 pass
565 finally:
566 if bkey:
567 closekey(bkey)
568
569
570 class SystemInfo:
571 """
572 Microsoft Windows and Visual Studio related system information.
573
574 Parameters
575 ----------
576 registry_info: RegistryInfo
577 "RegistryInfo" instance.
578 vc_ver: float
579 Required Microsoft Visual C++ version.
580 """
581
582 # Variables and properties in this class use originals CamelCase variables
583 # names from Microsoft source files for more easy comparison.
584 WinDir = environ.get('WinDir', '')
585 ProgramFiles = environ.get('ProgramFiles', '')
586 ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles)
587
588 def __init__(self, registry_info, vc_ver=None):
589 self.ri = registry_info
590 self.pi = self.ri.pi
591
592 self.known_vs_paths = self.find_programdata_vs_vers()
593
594 # Except for VS15+, VC version is aligned with VS version
595 self.vs_ver = self.vc_ver = (
596 vc_ver or self._find_latest_available_vs_ver())
597
598 def _find_latest_available_vs_ver(self):
599 """
600 Find the latest VC version
601
602 Return
603 ------
604 float
605 version
606 """
607 reg_vc_vers = self.find_reg_vs_vers()
608
609 if not (reg_vc_vers or self.known_vs_paths):
610 raise distutils.errors.DistutilsPlatformError(
611 'No Microsoft Visual C++ version found')
612
613 vc_vers = set(reg_vc_vers)
614 vc_vers.update(self.known_vs_paths)
615 return sorted(vc_vers)[-1]
616
617 def find_reg_vs_vers(self):
618 """
619 Find Microsoft Visual Studio versions available in registry.
620
621 Return
622 ------
623 list of float
624 Versions
625 """
626 ms = self.ri.microsoft
627 vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs)
628 vs_vers = []
629 for hkey, key in itertools.product(self.ri.HKEYS, vckeys):
630 try:
631 bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ)
632 except (OSError, IOError):
633 continue
634 with bkey:
635 subkeys, values, _ = winreg.QueryInfoKey(bkey)
636 for i in range(values):
637 with contextlib.suppress(ValueError):
638 ver = float(winreg.EnumValue(bkey, i)[0])
639 if ver not in vs_vers:
640 vs_vers.append(ver)
641 for i in range(subkeys):
642 with contextlib.suppress(ValueError):
643 ver = float(winreg.EnumKey(bkey, i))
644 if ver not in vs_vers:
645 vs_vers.append(ver)
646 return sorted(vs_vers)
647
648 def find_programdata_vs_vers(self):
649 r"""
650 Find Visual studio 2017+ versions from information in
651 "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances".
652
653 Return
654 ------
655 dict
656 float version as key, path as value.
657 """
658 vs_versions = {}
659 instances_dir = \
660 r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances'
661
662 try:
663 hashed_names = listdir(instances_dir)
664
665 except (OSError, IOError):
666 # Directory not exists with all Visual Studio versions
667 return vs_versions
668
669 for name in hashed_names:
670 try:
671 # Get VS installation path from "state.json" file
672 state_path = join(instances_dir, name, 'state.json')
673 with open(state_path, 'rt', encoding='utf-8') as state_file:
674 state = json.load(state_file)
675 vs_path = state['installationPath']
676
677 # Raises OSError if this VS installation does not contain VC
678 listdir(join(vs_path, r'VC\Tools\MSVC'))
679
680 # Store version and path
681 vs_versions[self._as_float_version(
682 state['installationVersion'])] = vs_path
683
684 except (OSError, IOError, KeyError):
685 # Skip if "state.json" file is missing or bad format
686 continue
687
688 return vs_versions
689
690 @staticmethod
691 def _as_float_version(version):
692 """
693 Return a string version as a simplified float version (major.minor)
694
695 Parameters
696 ----------
697 version: str
698 Version.
699
700 Return
701 ------
702 float
703 version
704 """
705 return float('.'.join(version.split('.')[:2]))
706
707 @property
708 def VSInstallDir(self):
709 """
710 Microsoft Visual Studio directory.
711
712 Return
713 ------
714 str
715 path
716 """
717 # Default path
718 default = join(self.ProgramFilesx86,
719 'Microsoft Visual Studio %0.1f' % self.vs_ver)
720
721 # Try to get path from registry, if fail use default path
722 return self.ri.lookup(self.ri.vs, '%0.1f' % self.vs_ver) or default
723
724 @property
725 def VCInstallDir(self):
726 """
727 Microsoft Visual C++ directory.
728
729 Return
730 ------
731 str
732 path
733 """
734 path = self._guess_vc() or self._guess_vc_legacy()
735
736 if not isdir(path):
737 msg = 'Microsoft Visual C++ directory not found'
738 raise distutils.errors.DistutilsPlatformError(msg)
739
740 return path
741
742 def _guess_vc(self):
743 """
744 Locate Visual C++ for VS2017+.
745
746 Return
747 ------
748 str
749 path
750 """
751 if self.vs_ver <= 14.0:
752 return ''
753
754 try:
755 # First search in known VS paths
756 vs_dir = self.known_vs_paths[self.vs_ver]
757 except KeyError:
758 # Else, search with path from registry
759 vs_dir = self.VSInstallDir
760
761 guess_vc = join(vs_dir, r'VC\Tools\MSVC')
762
763 # Subdir with VC exact version as name
764 try:
765 # Update the VC version with real one instead of VS version
766 vc_ver = listdir(guess_vc)[-1]
767 self.vc_ver = self._as_float_version(vc_ver)
768 return join(guess_vc, vc_ver)
769 except (OSError, IOError, IndexError):
770 return ''
771
772 def _guess_vc_legacy(self):
773 """
774 Locate Visual C++ for versions prior to 2017.
775
776 Return
777 ------
778 str
779 path
780 """
781 default = join(self.ProgramFilesx86,
782 r'Microsoft Visual Studio %0.1f\VC' % self.vs_ver)
783
784 # Try to get "VC++ for Python" path from registry as default path
785 reg_path = join(self.ri.vc_for_python, '%0.1f' % self.vs_ver)
786 python_vc = self.ri.lookup(reg_path, 'installdir')
787 default_vc = join(python_vc, 'VC') if python_vc else default
788
789 # Try to get path from registry, if fail use default path
790 return self.ri.lookup(self.ri.vc, '%0.1f' % self.vs_ver) or default_vc
791
792 @property
793 def WindowsSdkVersion(self):
794 """
795 Microsoft Windows SDK versions for specified MSVC++ version.
796
797 Return
798 ------
799 tuple of str
800 versions
801 """
802 if self.vs_ver <= 9.0:
803 return '7.0', '6.1', '6.0a'
804 elif self.vs_ver == 10.0:
805 return '7.1', '7.0a'
806 elif self.vs_ver == 11.0:
807 return '8.0', '8.0a'
808 elif self.vs_ver == 12.0:
809 return '8.1', '8.1a'
810 elif self.vs_ver >= 14.0:
811 return '10.0', '8.1'
812
813 @property
814 def WindowsSdkLastVersion(self):
815 """
816 Microsoft Windows SDK last version.
817
818 Return
819 ------
820 str
821 version
822 """
823 return self._use_last_dir_name(join(self.WindowsSdkDir, 'lib'))
824
825 @property # noqa: C901
826 def WindowsSdkDir(self): # noqa: C901 # is too complex (12) # FIXME
827 """
828 Microsoft Windows SDK directory.
829
830 Return
831 ------
832 str
833 path
834 """
835 sdkdir = ''
836 for ver in self.WindowsSdkVersion:
837 # Try to get it from registry
838 loc = join(self.ri.windows_sdk, 'v%s' % ver)
839 sdkdir = self.ri.lookup(loc, 'installationfolder')
840 if sdkdir:
841 break
842 if not sdkdir or not isdir(sdkdir):
843 # Try to get "VC++ for Python" version from registry
844 path = join(self.ri.vc_for_python, '%0.1f' % self.vc_ver)
845 install_base = self.ri.lookup(path, 'installdir')
846 if install_base:
847 sdkdir = join(install_base, 'WinSDK')
848 if not sdkdir or not isdir(sdkdir):
849 # If fail, use default new path
850 for ver in self.WindowsSdkVersion:
851 intver = ver[:ver.rfind('.')]
852 path = r'Microsoft SDKs\Windows Kits\%s' % intver
853 d = join(self.ProgramFiles, path)
854 if isdir(d):
855 sdkdir = d
856 if not sdkdir or not isdir(sdkdir):
857 # If fail, use default old path
858 for ver in self.WindowsSdkVersion:
859 path = r'Microsoft SDKs\Windows\v%s' % ver
860 d = join(self.ProgramFiles, path)
861 if isdir(d):
862 sdkdir = d
863 if not sdkdir:
864 # If fail, use Platform SDK
865 sdkdir = join(self.VCInstallDir, 'PlatformSDK')
866 return sdkdir
867
868 @property
869 def WindowsSDKExecutablePath(self):
870 """
871 Microsoft Windows SDK executable directory.
872
873 Return
874 ------
875 str
876 path
877 """
878 # Find WinSDK NetFx Tools registry dir name
879 if self.vs_ver <= 11.0:
880 netfxver = 35
881 arch = ''
882 else:
883 netfxver = 40
884 hidex86 = True if self.vs_ver <= 12.0 else False
885 arch = self.pi.current_dir(x64=True, hidex86=hidex86)
886 fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-'))
887
888 # list all possibles registry paths
889 regpaths = []
890 if self.vs_ver >= 14.0:
891 for ver in self.NetFxSdkVersion:
892 regpaths += [join(self.ri.netfx_sdk, ver, fx)]
893
894 for ver in self.WindowsSdkVersion:
895 regpaths += [join(self.ri.windows_sdk, 'v%sA' % ver, fx)]
896
897 # Return installation folder from the more recent path
898 for path in regpaths:
899 execpath = self.ri.lookup(path, 'installationfolder')
900 if execpath:
901 return execpath
902
903 @property
904 def FSharpInstallDir(self):
905 """
906 Microsoft Visual F# directory.
907
908 Return
909 ------
910 str
911 path
912 """
913 path = join(self.ri.visualstudio, r'%0.1f\Setup\F#' % self.vs_ver)
914 return self.ri.lookup(path, 'productdir') or ''
915
916 @property
917 def UniversalCRTSdkDir(self):
918 """
919 Microsoft Universal CRT SDK directory.
920
921 Return
922 ------
923 str
924 path
925 """
926 # Set Kit Roots versions for specified MSVC++ version
927 vers = ('10', '81') if self.vs_ver >= 14.0 else ()
928
929 # Find path of the more recent Kit
930 for ver in vers:
931 sdkdir = self.ri.lookup(self.ri.windows_kits_roots,
932 'kitsroot%s' % ver)
933 if sdkdir:
934 return sdkdir or ''
935
936 @property
937 def UniversalCRTSdkLastVersion(self):
938 """
939 Microsoft Universal C Runtime SDK last version.
940
941 Return
942 ------
943 str
944 version
945 """
946 return self._use_last_dir_name(join(self.UniversalCRTSdkDir, 'lib'))
947
948 @property
949 def NetFxSdkVersion(self):
950 """
951 Microsoft .NET Framework SDK versions.
952
953 Return
954 ------
955 tuple of str
956 versions
957 """
958 # Set FxSdk versions for specified VS version
959 return (('4.7.2', '4.7.1', '4.7',
960 '4.6.2', '4.6.1', '4.6',
961 '4.5.2', '4.5.1', '4.5')
962 if self.vs_ver >= 14.0 else ())
963
964 @property
965 def NetFxSdkDir(self):
966 """
967 Microsoft .NET Framework SDK directory.
968
969 Return
970 ------
971 str
972 path
973 """
974 sdkdir = ''
975 for ver in self.NetFxSdkVersion:
976 loc = join(self.ri.netfx_sdk, ver)
977 sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder')
978 if sdkdir:
979 break
980 return sdkdir
981
982 @property
983 def FrameworkDir32(self):
984 """
985 Microsoft .NET Framework 32bit directory.
986
987 Return
988 ------
989 str
990 path
991 """
992 # Default path
993 guess_fw = join(self.WinDir, r'Microsoft.NET\Framework')
994
995 # Try to get path from registry, if fail use default path
996 return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw
997
998 @property
999 def FrameworkDir64(self):
1000 """
1001 Microsoft .NET Framework 64bit directory.
1002
1003 Return
1004 ------
1005 str
1006 path
1007 """
1008 # Default path
1009 guess_fw = join(self.WinDir, r'Microsoft.NET\Framework64')
1010
1011 # Try to get path from registry, if fail use default path
1012 return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw
1013
1014 @property
1015 def FrameworkVersion32(self):
1016 """
1017 Microsoft .NET Framework 32bit versions.
1018
1019 Return
1020 ------
1021 tuple of str
1022 versions
1023 """
1024 return self._find_dot_net_versions(32)
1025
1026 @property
1027 def FrameworkVersion64(self):
1028 """
1029 Microsoft .NET Framework 64bit versions.
1030
1031 Return
1032 ------
1033 tuple of str
1034 versions
1035 """
1036 return self._find_dot_net_versions(64)
1037
1038 def _find_dot_net_versions(self, bits):
1039 """
1040 Find Microsoft .NET Framework versions.
1041
1042 Parameters
1043 ----------
1044 bits: int
1045 Platform number of bits: 32 or 64.
1046
1047 Return
1048 ------
1049 tuple of str
1050 versions
1051 """
1052 # Find actual .NET version in registry
1053 reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits)
1054 dot_net_dir = getattr(self, 'FrameworkDir%d' % bits)
1055 ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or ''
1056
1057 # Set .NET versions for specified MSVC++ version
1058 if self.vs_ver >= 12.0:
1059 return ver, 'v4.0'
1060 elif self.vs_ver >= 10.0:
1061 return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5'
1062 elif self.vs_ver == 9.0:
1063 return 'v3.5', 'v2.0.50727'
1064 elif self.vs_ver == 8.0:
1065 return 'v3.0', 'v2.0.50727'
1066
1067 @staticmethod
1068 def _use_last_dir_name(path, prefix=''):
1069 """
1070 Return name of the last dir in path or '' if no dir found.
1071
1072 Parameters
1073 ----------
1074 path: str
1075 Use dirs in this path
1076 prefix: str
1077 Use only dirs starting by this prefix
1078
1079 Return
1080 ------
1081 str
1082 name
1083 """
1084 matching_dirs = (
1085 dir_name
1086 for dir_name in reversed(listdir(path))
1087 if isdir(join(path, dir_name)) and
1088 dir_name.startswith(prefix)
1089 )
1090 return next(matching_dirs, None) or ''
1091
1092
1093 class EnvironmentInfo:
1094 """
1095 Return environment variables for specified Microsoft Visual C++ version
1096 and platform : Lib, Include, Path and libpath.
1097
1098 This function is compatible with Microsoft Visual C++ 9.0 to 14.X.
1099
1100 Script created by analysing Microsoft environment configuration files like
1101 "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ...
1102
1103 Parameters
1104 ----------
1105 arch: str
1106 Target architecture.
1107 vc_ver: float
1108 Required Microsoft Visual C++ version. If not set, autodetect the last
1109 version.
1110 vc_min_ver: float
1111 Minimum Microsoft Visual C++ version.
1112 """
1113
1114 # Variables and properties in this class use originals CamelCase variables
1115 # names from Microsoft source files for more easy comparison.
1116
1117 def __init__(self, arch, vc_ver=None, vc_min_ver=0):
1118 self.pi = PlatformInfo(arch)
1119 self.ri = RegistryInfo(self.pi)
1120 self.si = SystemInfo(self.ri, vc_ver)
1121
1122 if self.vc_ver < vc_min_ver:
1123 err = 'No suitable Microsoft Visual C++ version found'
1124 raise distutils.errors.DistutilsPlatformError(err)
1125
1126 @property
1127 def vs_ver(self):
1128 """
1129 Microsoft Visual Studio.
1130
1131 Return
1132 ------
1133 float
1134 version
1135 """
1136 return self.si.vs_ver
1137
1138 @property
1139 def vc_ver(self):
1140 """
1141 Microsoft Visual C++ version.
1142
1143 Return
1144 ------
1145 float
1146 version
1147 """
1148 return self.si.vc_ver
1149
1150 @property
1151 def VSTools(self):
1152 """
1153 Microsoft Visual Studio Tools.
1154
1155 Return
1156 ------
1157 list of str
1158 paths
1159 """
1160 paths = [r'Common7\IDE', r'Common7\Tools']
1161
1162 if self.vs_ver >= 14.0:
1163 arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
1164 paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow']
1165 paths += [r'Team Tools\Performance Tools']
1166 paths += [r'Team Tools\Performance Tools%s' % arch_subdir]
1167
1168 return [join(self.si.VSInstallDir, path) for path in paths]
1169
1170 @property
1171 def VCIncludes(self):
1172 """
1173 Microsoft Visual C++ & Microsoft Foundation Class Includes.
1174
1175 Return
1176 ------
1177 list of str
1178 paths
1179 """
1180 return [join(self.si.VCInstallDir, 'Include'),
1181 join(self.si.VCInstallDir, r'ATLMFC\Include')]
1182
1183 @property
1184 def VCLibraries(self):
1185 """
1186 Microsoft Visual C++ & Microsoft Foundation Class Libraries.
1187
1188 Return
1189 ------
1190 list of str
1191 paths
1192 """
1193 if self.vs_ver >= 15.0:
1194 arch_subdir = self.pi.target_dir(x64=True)
1195 else:
1196 arch_subdir = self.pi.target_dir(hidex86=True)
1197 paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir]
1198
1199 if self.vs_ver >= 14.0:
1200 paths += [r'Lib\store%s' % arch_subdir]
1201
1202 return [join(self.si.VCInstallDir, path) for path in paths]
1203
1204 @property
1205 def VCStoreRefs(self):
1206 """
1207 Microsoft Visual C++ store references Libraries.
1208
1209 Return
1210 ------
1211 list of str
1212 paths
1213 """
1214 if self.vs_ver < 14.0:
1215 return []
1216 return [join(self.si.VCInstallDir, r'Lib\store\references')]
1217
1218 @property
1219 def VCTools(self):
1220 """
1221 Microsoft Visual C++ Tools.
1222
1223 Return
1224 ------
1225 list of str
1226 paths
1227 """
1228 si = self.si
1229 tools = [join(si.VCInstallDir, 'VCPackages')]
1230
1231 forcex86 = True if self.vs_ver <= 10.0 else False
1232 arch_subdir = self.pi.cross_dir(forcex86)
1233 if arch_subdir:
1234 tools += [join(si.VCInstallDir, 'Bin%s' % arch_subdir)]
1235
1236 if self.vs_ver == 14.0:
1237 path = 'Bin%s' % self.pi.current_dir(hidex86=True)
1238 tools += [join(si.VCInstallDir, path)]
1239
1240 elif self.vs_ver >= 15.0:
1241 host_dir = (r'bin\HostX86%s' if self.pi.current_is_x86() else
1242 r'bin\HostX64%s')
1243 tools += [join(
1244 si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))]
1245
1246 if self.pi.current_cpu != self.pi.target_cpu:
1247 tools += [join(
1248 si.VCInstallDir, host_dir % self.pi.current_dir(x64=True))]
1249
1250 else:
1251 tools += [join(si.VCInstallDir, 'Bin')]
1252
1253 return tools
1254
1255 @property
1256 def OSLibraries(self):
1257 """
1258 Microsoft Windows SDK Libraries.
1259
1260 Return
1261 ------
1262 list of str
1263 paths
1264 """
1265 if self.vs_ver <= 10.0:
1266 arch_subdir = self.pi.target_dir(hidex86=True, x64=True)
1267 return [join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)]
1268
1269 else:
1270 arch_subdir = self.pi.target_dir(x64=True)
1271 lib = join(self.si.WindowsSdkDir, 'lib')
1272 libver = self._sdk_subdir
1273 return [join(lib, '%sum%s' % (libver, arch_subdir))]
1274
1275 @property
1276 def OSIncludes(self):
1277 """
1278 Microsoft Windows SDK Include.
1279
1280 Return
1281 ------
1282 list of str
1283 paths
1284 """
1285 include = join(self.si.WindowsSdkDir, 'include')
1286
1287 if self.vs_ver <= 10.0:
1288 return [include, join(include, 'gl')]
1289
1290 else:
1291 if self.vs_ver >= 14.0:
1292 sdkver = self._sdk_subdir
1293 else:
1294 sdkver = ''
1295 return [join(include, '%sshared' % sdkver),
1296 join(include, '%sum' % sdkver),
1297 join(include, '%swinrt' % sdkver)]
1298
1299 @property
1300 def OSLibpath(self):
1301 """
1302 Microsoft Windows SDK Libraries Paths.
1303
1304 Return
1305 ------
1306 list of str
1307 paths
1308 """
1309 ref = join(self.si.WindowsSdkDir, 'References')
1310 libpath = []
1311
1312 if self.vs_ver <= 9.0:
1313 libpath += self.OSLibraries
1314
1315 if self.vs_ver >= 11.0:
1316 libpath += [join(ref, r'CommonConfiguration\Neutral')]
1317
1318 if self.vs_ver >= 14.0:
1319 libpath += [
1320 ref,
1321 join(self.si.WindowsSdkDir, 'UnionMetadata'),
1322 join(
1323 ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),
1324 join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'),
1325 join(
1326 ref, 'Windows.Networking.Connectivity.WwanContract',
1327 '1.0.0.0'),
1328 join(
1329 self.si.WindowsSdkDir, 'ExtensionSDKs', 'Microsoft.VCLibs',
1330 '%0.1f' % self.vs_ver, 'References', 'CommonConfiguration',
1331 'neutral'),
1332 ]
1333 return libpath
1334
1335 @property
1336 def SdkTools(self):
1337 """
1338 Microsoft Windows SDK Tools.
1339
1340 Return
1341 ------
1342 list of str
1343 paths
1344 """
1345 return list(self._sdk_tools())
1346
1347 def _sdk_tools(self):
1348 """
1349 Microsoft Windows SDK Tools paths generator.
1350
1351 Return
1352 ------
1353 generator of str
1354 paths
1355 """
1356 if self.vs_ver < 15.0:
1357 bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86'
1358 yield join(self.si.WindowsSdkDir, bin_dir)
1359
1360 if not self.pi.current_is_x86():
1361 arch_subdir = self.pi.current_dir(x64=True)
1362 path = 'Bin%s' % arch_subdir
1363 yield join(self.si.WindowsSdkDir, path)
1364
1365 if self.vs_ver in (10.0, 11.0):
1366 if self.pi.target_is_x86():
1367 arch_subdir = ''
1368 else:
1369 arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
1370 path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir
1371 yield join(self.si.WindowsSdkDir, path)
1372
1373 elif self.vs_ver >= 15.0:
1374 path = join(self.si.WindowsSdkDir, 'Bin')
1375 arch_subdir = self.pi.current_dir(x64=True)
1376 sdkver = self.si.WindowsSdkLastVersion
1377 yield join(path, '%s%s' % (sdkver, arch_subdir))
1378
1379 if self.si.WindowsSDKExecutablePath:
1380 yield self.si.WindowsSDKExecutablePath
1381
1382 @property
1383 def _sdk_subdir(self):
1384 """
1385 Microsoft Windows SDK version subdir.
1386
1387 Return
1388 ------
1389 str
1390 subdir
1391 """
1392 ucrtver = self.si.WindowsSdkLastVersion
1393 return ('%s\\' % ucrtver) if ucrtver else ''
1394
1395 @property
1396 def SdkSetup(self):
1397 """
1398 Microsoft Windows SDK Setup.
1399
1400 Return
1401 ------
1402 list of str
1403 paths
1404 """
1405 if self.vs_ver > 9.0:
1406 return []
1407
1408 return [join(self.si.WindowsSdkDir, 'Setup')]
1409
1410 @property
1411 def FxTools(self):
1412 """
1413 Microsoft .NET Framework Tools.
1414
1415 Return
1416 ------
1417 list of str
1418 paths
1419 """
1420 pi = self.pi
1421 si = self.si
1422
1423 if self.vs_ver <= 10.0:
1424 include32 = True
1425 include64 = not pi.target_is_x86() and not pi.current_is_x86()
1426 else:
1427 include32 = pi.target_is_x86() or pi.current_is_x86()
1428 include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64'
1429
1430 tools = []
1431 if include32:
1432 tools += [join(si.FrameworkDir32, ver)
1433 for ver in si.FrameworkVersion32]
1434 if include64:
1435 tools += [join(si.FrameworkDir64, ver)
1436 for ver in si.FrameworkVersion64]
1437 return tools
1438
1439 @property
1440 def NetFxSDKLibraries(self):
1441 """
1442 Microsoft .Net Framework SDK Libraries.
1443
1444 Return
1445 ------
1446 list of str
1447 paths
1448 """
1449 if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
1450 return []
1451
1452 arch_subdir = self.pi.target_dir(x64=True)
1453 return [join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)]
1454
1455 @property
1456 def NetFxSDKIncludes(self):
1457 """
1458 Microsoft .Net Framework SDK Includes.
1459
1460 Return
1461 ------
1462 list of str
1463 paths
1464 """
1465 if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
1466 return []
1467
1468 return [join(self.si.NetFxSdkDir, r'include\um')]
1469
1470 @property
1471 def VsTDb(self):
1472 """
1473 Microsoft Visual Studio Team System Database.
1474
1475 Return
1476 ------
1477 list of str
1478 paths
1479 """
1480 return [join(self.si.VSInstallDir, r'VSTSDB\Deploy')]
1481
1482 @property
1483 def MSBuild(self):
1484 """
1485 Microsoft Build Engine.
1486
1487 Return
1488 ------
1489 list of str
1490 paths
1491 """
1492 if self.vs_ver < 12.0:
1493 return []
1494 elif self.vs_ver < 15.0:
1495 base_path = self.si.ProgramFilesx86
1496 arch_subdir = self.pi.current_dir(hidex86=True)
1497 else:
1498 base_path = self.si.VSInstallDir
1499 arch_subdir = ''
1500
1501 path = r'MSBuild\%0.1f\bin%s' % (self.vs_ver, arch_subdir)
1502 build = [join(base_path, path)]
1503
1504 if self.vs_ver >= 15.0:
1505 # Add Roslyn C# & Visual Basic Compiler
1506 build += [join(base_path, path, 'Roslyn')]
1507
1508 return build
1509
1510 @property
1511 def HTMLHelpWorkshop(self):
1512 """
1513 Microsoft HTML Help Workshop.
1514
1515 Return
1516 ------
1517 list of str
1518 paths
1519 """
1520 if self.vs_ver < 11.0:
1521 return []
1522
1523 return [join(self.si.ProgramFilesx86, 'HTML Help Workshop')]
1524
1525 @property
1526 def UCRTLibraries(self):
1527 """
1528 Microsoft Universal C Runtime SDK Libraries.
1529
1530 Return
1531 ------
1532 list of str
1533 paths
1534 """
1535 if self.vs_ver < 14.0:
1536 return []
1537
1538 arch_subdir = self.pi.target_dir(x64=True)
1539 lib = join(self.si.UniversalCRTSdkDir, 'lib')
1540 ucrtver = self._ucrt_subdir
1541 return [join(lib, '%sucrt%s' % (ucrtver, arch_subdir))]
1542
1543 @property
1544 def UCRTIncludes(self):
1545 """
1546 Microsoft Universal C Runtime SDK Include.
1547
1548 Return
1549 ------
1550 list of str
1551 paths
1552 """
1553 if self.vs_ver < 14.0:
1554 return []
1555
1556 include = join(self.si.UniversalCRTSdkDir, 'include')
1557 return [join(include, '%sucrt' % self._ucrt_subdir)]
1558
1559 @property
1560 def _ucrt_subdir(self):
1561 """
1562 Microsoft Universal C Runtime SDK version subdir.
1563
1564 Return
1565 ------
1566 str
1567 subdir
1568 """
1569 ucrtver = self.si.UniversalCRTSdkLastVersion
1570 return ('%s\\' % ucrtver) if ucrtver else ''
1571
1572 @property
1573 def FSharp(self):
1574 """
1575 Microsoft Visual F#.
1576
1577 Return
1578 ------
1579 list of str
1580 paths
1581 """
1582 if 11.0 > self.vs_ver > 12.0:
1583 return []
1584
1585 return [self.si.FSharpInstallDir]
1586
1587 @property
1588 def VCRuntimeRedist(self):
1589 """
1590 Microsoft Visual C++ runtime redistributable dll.
1591
1592 Return
1593 ------
1594 str
1595 path
1596 """
1597 vcruntime = 'vcruntime%d0.dll' % self.vc_ver
1598 arch_subdir = self.pi.target_dir(x64=True).strip('\\')
1599
1600 # Installation prefixes candidates
1601 prefixes = []
1602 tools_path = self.si.VCInstallDir
1603 redist_path = dirname(tools_path.replace(r'\Tools', r'\Redist'))
1604 if isdir(redist_path):
1605 # Redist version may not be exactly the same as tools
1606 redist_path = join(redist_path, listdir(redist_path)[-1])
1607 prefixes += [redist_path, join(redist_path, 'onecore')]
1608
1609 prefixes += [join(tools_path, 'redist')] # VS14 legacy path
1610
1611 # CRT directory
1612 crt_dirs = ('Microsoft.VC%d.CRT' % (self.vc_ver * 10),
1613 # Sometime store in directory with VS version instead of VC
1614 'Microsoft.VC%d.CRT' % (int(self.vs_ver) * 10))
1615
1616 # vcruntime path
1617 for prefix, crt_dir in itertools.product(prefixes, crt_dirs):
1618 path = join(prefix, arch_subdir, crt_dir, vcruntime)
1619 if isfile(path):
1620 return path
1621
1622 def return_env(self, exists=True):
1623 """
1624 Return environment dict.
1625
1626 Parameters
1627 ----------
1628 exists: bool
1629 It True, only return existing paths.
1630
1631 Return
1632 ------
1633 dict
1634 environment
1635 """
1636 env = dict(
1637 include=self._build_paths('include',
1638 [self.VCIncludes,
1639 self.OSIncludes,
1640 self.UCRTIncludes,
1641 self.NetFxSDKIncludes],
1642 exists),
1643 lib=self._build_paths('lib',
1644 [self.VCLibraries,
1645 self.OSLibraries,
1646 self.FxTools,
1647 self.UCRTLibraries,
1648 self.NetFxSDKLibraries],
1649 exists),
1650 libpath=self._build_paths('libpath',
1651 [self.VCLibraries,
1652 self.FxTools,
1653 self.VCStoreRefs,
1654 self.OSLibpath],
1655 exists),
1656 path=self._build_paths('path',
1657 [self.VCTools,
1658 self.VSTools,
1659 self.VsTDb,
1660 self.SdkTools,
1661 self.SdkSetup,
1662 self.FxTools,
1663 self.MSBuild,
1664 self.HTMLHelpWorkshop,
1665 self.FSharp],
1666 exists),
1667 )
1668 if self.vs_ver >= 14 and isfile(self.VCRuntimeRedist):
1669 env['py_vcruntime_redist'] = self.VCRuntimeRedist
1670 return env
1671
1672 def _build_paths(self, name, spec_path_lists, exists):
1673 """
1674 Given an environment variable name and specified paths,
1675 return a pathsep-separated string of paths containing
1676 unique, extant, directories from those paths and from
1677 the environment variable. Raise an error if no paths
1678 are resolved.
1679
1680 Parameters
1681 ----------
1682 name: str
1683 Environment variable name
1684 spec_path_lists: list of str
1685 Paths
1686 exists: bool
1687 It True, only return existing paths.
1688
1689 Return
1690 ------
1691 str
1692 Pathsep-separated paths
1693 """
1694 # flatten spec_path_lists
1695 spec_paths = itertools.chain.from_iterable(spec_path_lists)
1696 env_paths = environ.get(name, '').split(pathsep)
1697 paths = itertools.chain(spec_paths, env_paths)
1698 extant_paths = list(filter(isdir, paths)) if exists else paths
1699 if not extant_paths:
1700 msg = "%s environment variable is empty" % name.upper()
1701 raise distutils.errors.DistutilsPlatformError(msg)
1702 unique_paths = unique_everseen(extant_paths)
1703 return pathsep.join(unique_paths)