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