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