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