Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/setuptools/msvc.py: 27%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

604 statements  

1""" 

2Improved support for Microsoft Visual C++ compilers. 

3 

4Known supported compilers: 

5-------------------------- 

6Microsoft Visual C++ 9.0: 

7 Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) 

8 Microsoft Windows SDK 6.1 (x86, x64, ia64) 

9 Microsoft Windows SDK 7.0 (x86, x64, ia64) 

10 

11Microsoft Visual C++ 10.0: 

12 Microsoft Windows SDK 7.1 (x86, x64, ia64) 

13 

14Microsoft Visual C++ 14.X: 

15 Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) 

16 Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64) 

17 Microsoft Visual Studio Build Tools 2019 (x86, x64, arm, arm64) 

18 

19This may also support compilers shipped with compatible Visual Studio versions. 

20""" 

21 

22import json 

23from io import open 

24from os import listdir, pathsep 

25from os.path import join, isfile, isdir, dirname 

26import sys 

27import platform 

28import itertools 

29import distutils.errors 

30from setuptools.extern.packaging.version import LegacyVersion 

31 

32from setuptools.extern.six.moves import filterfalse 

33 

34from .monkey import get_unpatched 

35 

36if platform.system() == 'Windows': 

37 from setuptools.extern.six.moves import winreg 

38 from os import environ 

39else: 

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 

59try: 

60 from distutils.msvc9compiler import Reg 

61except _msvc9_suppress_errors: 

62 pass 

63 

64 

65def 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 

107def 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 

145def msvc14_get_vc_env(plat_spec): 

146 """ 

147 Patched "distutils._msvccompiler._get_vc_env" for support extra 

148 Microsoft Visual C++ 14.X compilers. 

149 

150 Set environment without use of "vcvarsall.bat". 

151 

152 Parameters 

153 ---------- 

154 plat_spec: str 

155 Target architecture. 

156 

157 Return 

158 ------ 

159 dict 

160 environment 

161 """ 

162 # Try to get environment from vcvarsall.bat (Classical way) 

163 try: 

164 return get_unpatched(msvc14_get_vc_env)(plat_spec) 

165 except distutils.errors.DistutilsPlatformError: 

166 # Pass error Vcvarsall.bat is missing 

167 pass 

168 

169 # If error, try to set environment directly 

170 try: 

171 return EnvironmentInfo(plat_spec, vc_min_ver=14.0).return_env() 

172 except distutils.errors.DistutilsPlatformError as exc: 

173 _augment_exception(exc, 14.0) 

174 raise 

175 

176 

177def msvc14_gen_lib_options(*args, **kwargs): 

178 """ 

179 Patched "distutils._msvccompiler.gen_lib_options" for fix 

180 compatibility between "numpy.distutils" and "distutils._msvccompiler" 

181 (for Numpy < 1.11.2) 

182 """ 

183 if "numpy.distutils" in sys.modules: 

184 import numpy as np 

185 if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'): 

186 return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) 

187 return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs) 

188 

189 

190def _augment_exception(exc, version, arch=''): 

191 """ 

192 Add details to the exception message to help guide the user 

193 as to what action will resolve it. 

194 """ 

195 # Error if MSVC++ directory not found or environment not set 

196 message = exc.args[0] 

197 

198 if "vcvarsall" in message.lower() or "visual c" in message.lower(): 

199 # Special error message if MSVC++ not installed 

200 tmpl = 'Microsoft Visual C++ {version:0.1f} is required.' 

201 message = tmpl.format(**locals()) 

202 msdownload = 'www.microsoft.com/download/details.aspx?id=%d' 

203 if version == 9.0: 

204 if arch.lower().find('ia64') > -1: 

205 # For VC++ 9.0, if IA64 support is needed, redirect user 

206 # to Windows SDK 7.0. 

207 # Note: No download link available from Microsoft. 

208 message += ' Get it with "Microsoft Windows SDK 7.0"' 

209 else: 

210 # For VC++ 9.0 redirect user to Vc++ for Python 2.7 : 

211 # This redirection link is maintained by Microsoft. 

212 # Contact vspython@microsoft.com if it needs updating. 

213 message += ' Get it from http://aka.ms/vcpython27' 

214 elif version == 10.0: 

215 # For VC++ 10.0 Redirect user to Windows SDK 7.1 

216 message += ' Get it with "Microsoft Windows SDK 7.1": ' 

217 message += msdownload % 8279 

218 elif version >= 14.0: 

219 # For VC++ 14.X Redirect user to latest Visual C++ Build Tools 

220 message += (' Get it with "Build Tools for Visual Studio": ' 

221 r'https://visualstudio.microsoft.com/downloads/') 

222 

223 exc.args = (message, ) 

224 

225 

226class PlatformInfo: 

227 """ 

228 Current and Target Architectures information. 

229 

230 Parameters 

231 ---------- 

232 arch: str 

233 Target architecture. 

234 """ 

235 current_cpu = environ.get('processor_architecture', '').lower() 

236 

237 def __init__(self, arch): 

238 self.arch = arch.lower().replace('x64', 'amd64') 

239 

240 @property 

241 def target_cpu(self): 

242 """ 

243 Return Target CPU architecture. 

244 

245 Return 

246 ------ 

247 str 

248 Target CPU 

249 """ 

250 return self.arch[self.arch.find('_') + 1:] 

251 

252 def target_is_x86(self): 

253 """ 

254 Return True if target CPU is x86 32 bits.. 

255 

256 Return 

257 ------ 

258 bool 

259 CPU is x86 32 bits 

260 """ 

261 return self.target_cpu == 'x86' 

262 

263 def current_is_x86(self): 

264 """ 

265 Return True if current CPU is x86 32 bits.. 

266 

267 Return 

268 ------ 

269 bool 

270 CPU is x86 32 bits 

271 """ 

272 return self.current_cpu == 'x86' 

273 

274 def current_dir(self, hidex86=False, x64=False): 

275 """ 

276 Current 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 str 

288 subfolder: '\target', or '' (see hidex86 parameter) 

289 """ 

290 return ( 

291 '' if (self.current_cpu == 'x86' and hidex86) else 

292 r'\x64' if (self.current_cpu == 'amd64' and x64) else 

293 r'\%s' % self.current_cpu 

294 ) 

295 

296 def target_dir(self, hidex86=False, x64=False): 

297 r""" 

298 Target platform specific subfolder. 

299 

300 Parameters 

301 ---------- 

302 hidex86: bool 

303 return '' and not '\x86' if architecture is x86. 

304 x64: bool 

305 return '\x64' and not '\amd64' if architecture is amd64. 

306 

307 Return 

308 ------ 

309 str 

310 subfolder: '\current', or '' (see hidex86 parameter) 

311 """ 

312 return ( 

313 '' if (self.target_cpu == 'x86' and hidex86) else 

314 r'\x64' if (self.target_cpu == 'amd64' and x64) else 

315 r'\%s' % self.target_cpu 

316 ) 

317 

318 def cross_dir(self, forcex86=False): 

319 r""" 

320 Cross platform specific subfolder. 

321 

322 Parameters 

323 ---------- 

324 forcex86: bool 

325 Use 'x86' as current architecture even if current architecture is 

326 not x86. 

327 

328 Return 

329 ------ 

330 str 

331 subfolder: '' if target architecture is current architecture, 

332 '\current_target' if not. 

333 """ 

334 current = 'x86' if forcex86 else self.current_cpu 

335 return ( 

336 '' if self.target_cpu == current else 

337 self.target_dir().replace('\\', '\\%s_' % current) 

338 ) 

339 

340 

341class RegistryInfo: 

342 """ 

343 Microsoft Visual Studio related registry information. 

344 

345 Parameters 

346 ---------- 

347 platform_info: PlatformInfo 

348 "PlatformInfo" instance. 

349 """ 

350 HKEYS = (winreg.HKEY_USERS, 

351 winreg.HKEY_CURRENT_USER, 

352 winreg.HKEY_LOCAL_MACHINE, 

353 winreg.HKEY_CLASSES_ROOT) 

354 

355 def __init__(self, platform_info): 

356 self.pi = platform_info 

357 

358 @property 

359 def visualstudio(self): 

360 """ 

361 Microsoft Visual Studio root registry key. 

362 

363 Return 

364 ------ 

365 str 

366 Registry key 

367 """ 

368 return 'VisualStudio' 

369 

370 @property 

371 def sxs(self): 

372 """ 

373 Microsoft Visual Studio SxS registry key. 

374 

375 Return 

376 ------ 

377 str 

378 Registry key 

379 """ 

380 return join(self.visualstudio, 'SxS') 

381 

382 @property 

383 def vc(self): 

384 """ 

385 Microsoft Visual C++ VC7 registry key. 

386 

387 Return 

388 ------ 

389 str 

390 Registry key 

391 """ 

392 return join(self.sxs, 'VC7') 

393 

394 @property 

395 def vs(self): 

396 """ 

397 Microsoft Visual Studio VS7 registry key. 

398 

399 Return 

400 ------ 

401 str 

402 Registry key 

403 """ 

404 return join(self.sxs, 'VS7') 

405 

406 @property 

407 def vc_for_python(self): 

408 """ 

409 Microsoft Visual C++ for Python registry key. 

410 

411 Return 

412 ------ 

413 str 

414 Registry key 

415 """ 

416 return r'DevDiv\VCForPython' 

417 

418 @property 

419 def microsoft_sdk(self): 

420 """ 

421 Microsoft SDK registry key. 

422 

423 Return 

424 ------ 

425 str 

426 Registry key 

427 """ 

428 return 'Microsoft SDKs' 

429 

430 @property 

431 def windows_sdk(self): 

432 """ 

433 Microsoft Windows/Platform SDK registry key. 

434 

435 Return 

436 ------ 

437 str 

438 Registry key 

439 """ 

440 return join(self.microsoft_sdk, 'Windows') 

441 

442 @property 

443 def netfx_sdk(self): 

444 """ 

445 Microsoft .NET Framework SDK registry key. 

446 

447 Return 

448 ------ 

449 str 

450 Registry key 

451 """ 

452 return join(self.microsoft_sdk, 'NETFXSDK') 

453 

454 @property 

455 def windows_kits_roots(self): 

456 """ 

457 Microsoft Windows Kits Roots registry key. 

458 

459 Return 

460 ------ 

461 str 

462 Registry key 

463 """ 

464 return r'Windows Kits\Installed Roots' 

465 

466 def microsoft(self, key, x86=False): 

467 """ 

468 Return key in Microsoft software registry. 

469 

470 Parameters 

471 ---------- 

472 key: str 

473 Registry key path where look. 

474 x86: str 

475 Force x86 software registry. 

476 

477 Return 

478 ------ 

479 str 

480 Registry key 

481 """ 

482 node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node' 

483 return join('Software', node64, 'Microsoft', key) 

484 

485 def lookup(self, key, name): 

486 """ 

487 Look for values in registry in Microsoft software registry. 

488 

489 Parameters 

490 ---------- 

491 key: str 

492 Registry key path where look. 

493 name: str 

494 Value name to find. 

495 

496 Return 

497 ------ 

498 str 

499 value 

500 """ 

501 key_read = winreg.KEY_READ 

502 openkey = winreg.OpenKey 

503 ms = self.microsoft 

504 for hkey in self.HKEYS: 

505 try: 

506 bkey = openkey(hkey, ms(key), 0, key_read) 

507 except (OSError, IOError): 

508 if not self.pi.current_is_x86(): 

509 try: 

510 bkey = openkey(hkey, ms(key, True), 0, key_read) 

511 except (OSError, IOError): 

512 continue 

513 else: 

514 continue 

515 try: 

516 return winreg.QueryValueEx(bkey, name)[0] 

517 except (OSError, IOError): 

518 pass 

519 

520 

521class SystemInfo: 

522 """ 

523 Microsoft Windows and Visual Studio related system information. 

524 

525 Parameters 

526 ---------- 

527 registry_info: RegistryInfo 

528 "RegistryInfo" instance. 

529 vc_ver: float 

530 Required Microsoft Visual C++ version. 

531 """ 

532 

533 # Variables and properties in this class use originals CamelCase variables 

534 # names from Microsoft source files for more easy comparison. 

535 WinDir = environ.get('WinDir', '') 

536 ProgramFiles = environ.get('ProgramFiles', '') 

537 ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles) 

538 

539 def __init__(self, registry_info, vc_ver=None): 

540 self.ri = registry_info 

541 self.pi = self.ri.pi 

542 

543 self.known_vs_paths = self.find_programdata_vs_vers() 

544 

545 # Except for VS15+, VC version is aligned with VS version 

546 self.vs_ver = self.vc_ver = ( 

547 vc_ver or self._find_latest_available_vs_ver()) 

548 

549 def _find_latest_available_vs_ver(self): 

550 """ 

551 Find the latest VC version 

552 

553 Return 

554 ------ 

555 float 

556 version 

557 """ 

558 reg_vc_vers = self.find_reg_vs_vers() 

559 

560 if not (reg_vc_vers or self.known_vs_paths): 

561 raise distutils.errors.DistutilsPlatformError( 

562 'No Microsoft Visual C++ version found') 

563 

564 vc_vers = set(reg_vc_vers) 

565 vc_vers.update(self.known_vs_paths) 

566 return sorted(vc_vers)[-1] 

567 

568 def find_reg_vs_vers(self): 

569 """ 

570 Find Microsoft Visual Studio versions available in registry. 

571 

572 Return 

573 ------ 

574 list of float 

575 Versions 

576 """ 

577 ms = self.ri.microsoft 

578 vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs) 

579 vs_vers = [] 

580 for hkey in self.ri.HKEYS: 

581 for key in vckeys: 

582 try: 

583 bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ) 

584 except (OSError, IOError): 

585 continue 

586 subkeys, values, _ = winreg.QueryInfoKey(bkey) 

587 for i in range(values): 

588 try: 

589 ver = float(winreg.EnumValue(bkey, i)[0]) 

590 if ver not in vs_vers: 

591 vs_vers.append(ver) 

592 except ValueError: 

593 pass 

594 for i in range(subkeys): 

595 try: 

596 ver = float(winreg.EnumKey(bkey, i)) 

597 if ver not in vs_vers: 

598 vs_vers.append(ver) 

599 except ValueError: 

600 pass 

601 return sorted(vs_vers) 

602 

603 def find_programdata_vs_vers(self): 

604 r""" 

605 Find Visual studio 2017+ versions from information in 

606 "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances". 

607 

608 Return 

609 ------ 

610 dict 

611 float version as key, path as value. 

612 """ 

613 vs_versions = {} 

614 instances_dir = \ 

615 r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances' 

616 

617 try: 

618 hashed_names = listdir(instances_dir) 

619 

620 except (OSError, IOError): 

621 # Directory not exists with all Visual Studio versions 

622 return vs_versions 

623 

624 for name in hashed_names: 

625 try: 

626 # Get VS installation path from "state.json" file 

627 state_path = join(instances_dir, name, 'state.json') 

628 with open(state_path, 'rt', encoding='utf-8') as state_file: 

629 state = json.load(state_file) 

630 vs_path = state['installationPath'] 

631 

632 # Raises OSError if this VS installation does not contain VC 

633 listdir(join(vs_path, r'VC\Tools\MSVC')) 

634 

635 # Store version and path 

636 vs_versions[self._as_float_version( 

637 state['installationVersion'])] = vs_path 

638 

639 except (OSError, IOError, KeyError): 

640 # Skip if "state.json" file is missing or bad format 

641 continue 

642 

643 return vs_versions 

644 

645 @staticmethod 

646 def _as_float_version(version): 

647 """ 

648 Return a string version as a simplified float version (major.minor) 

649 

650 Parameters 

651 ---------- 

652 version: str 

653 Version. 

654 

655 Return 

656 ------ 

657 float 

658 version 

659 """ 

660 return float('.'.join(version.split('.')[:2])) 

661 

662 @property 

663 def VSInstallDir(self): 

664 """ 

665 Microsoft Visual Studio directory. 

666 

667 Return 

668 ------ 

669 str 

670 path 

671 """ 

672 # Default path 

673 default = join(self.ProgramFilesx86, 

674 'Microsoft Visual Studio %0.1f' % self.vs_ver) 

675 

676 # Try to get path from registry, if fail use default path 

677 return self.ri.lookup(self.ri.vs, '%0.1f' % self.vs_ver) or default 

678 

679 @property 

680 def VCInstallDir(self): 

681 """ 

682 Microsoft Visual C++ directory. 

683 

684 Return 

685 ------ 

686 str 

687 path 

688 """ 

689 path = self._guess_vc() or self._guess_vc_legacy() 

690 

691 if not isdir(path): 

692 msg = 'Microsoft Visual C++ directory not found' 

693 raise distutils.errors.DistutilsPlatformError(msg) 

694 

695 return path 

696 

697 def _guess_vc(self): 

698 """ 

699 Locate Visual C++ for VS2017+. 

700 

701 Return 

702 ------ 

703 str 

704 path 

705 """ 

706 if self.vs_ver <= 14.0: 

707 return '' 

708 

709 try: 

710 # First search in known VS paths 

711 vs_dir = self.known_vs_paths[self.vs_ver] 

712 except KeyError: 

713 # Else, search with path from registry 

714 vs_dir = self.VSInstallDir 

715 

716 guess_vc = join(vs_dir, r'VC\Tools\MSVC') 

717 

718 # Subdir with VC exact version as name 

719 try: 

720 # Update the VC version with real one instead of VS version 

721 vc_ver = listdir(guess_vc)[-1] 

722 self.vc_ver = self._as_float_version(vc_ver) 

723 return join(guess_vc, vc_ver) 

724 except (OSError, IOError, IndexError): 

725 return '' 

726 

727 def _guess_vc_legacy(self): 

728 """ 

729 Locate Visual C++ for versions prior to 2017. 

730 

731 Return 

732 ------ 

733 str 

734 path 

735 """ 

736 default = join(self.ProgramFilesx86, 

737 r'Microsoft Visual Studio %0.1f\VC' % self.vs_ver) 

738 

739 # Try to get "VC++ for Python" path from registry as default path 

740 reg_path = join(self.ri.vc_for_python, '%0.1f' % self.vs_ver) 

741 python_vc = self.ri.lookup(reg_path, 'installdir') 

742 default_vc = join(python_vc, 'VC') if python_vc else default 

743 

744 # Try to get path from registry, if fail use default path 

745 return self.ri.lookup(self.ri.vc, '%0.1f' % self.vs_ver) or default_vc 

746 

747 @property 

748 def WindowsSdkVersion(self): 

749 """ 

750 Microsoft Windows SDK versions for specified MSVC++ version. 

751 

752 Return 

753 ------ 

754 tuple of str 

755 versions 

756 """ 

757 if self.vs_ver <= 9.0: 

758 return '7.0', '6.1', '6.0a' 

759 elif self.vs_ver == 10.0: 

760 return '7.1', '7.0a' 

761 elif self.vs_ver == 11.0: 

762 return '8.0', '8.0a' 

763 elif self.vs_ver == 12.0: 

764 return '8.1', '8.1a' 

765 elif self.vs_ver >= 14.0: 

766 return '10.0', '8.1' 

767 

768 @property 

769 def WindowsSdkLastVersion(self): 

770 """ 

771 Microsoft Windows SDK last version. 

772 

773 Return 

774 ------ 

775 str 

776 version 

777 """ 

778 return self._use_last_dir_name(join(self.WindowsSdkDir, 'lib')) 

779 

780 @property 

781 def WindowsSdkDir(self): 

782 """ 

783 Microsoft Windows SDK directory. 

784 

785 Return 

786 ------ 

787 str 

788 path 

789 """ 

790 sdkdir = '' 

791 for ver in self.WindowsSdkVersion: 

792 # Try to get it from registry 

793 loc = join(self.ri.windows_sdk, 'v%s' % ver) 

794 sdkdir = self.ri.lookup(loc, 'installationfolder') 

795 if sdkdir: 

796 break 

797 if not sdkdir or not isdir(sdkdir): 

798 # Try to get "VC++ for Python" version from registry 

799 path = join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) 

800 install_base = self.ri.lookup(path, 'installdir') 

801 if install_base: 

802 sdkdir = join(install_base, 'WinSDK') 

803 if not sdkdir or not isdir(sdkdir): 

804 # If fail, use default new path 

805 for ver in self.WindowsSdkVersion: 

806 intver = ver[:ver.rfind('.')] 

807 path = r'Microsoft SDKs\Windows Kits\%s' % intver 

808 d = join(self.ProgramFiles, path) 

809 if isdir(d): 

810 sdkdir = d 

811 if not sdkdir or not isdir(sdkdir): 

812 # If fail, use default old path 

813 for ver in self.WindowsSdkVersion: 

814 path = r'Microsoft SDKs\Windows\v%s' % ver 

815 d = join(self.ProgramFiles, path) 

816 if isdir(d): 

817 sdkdir = d 

818 if not sdkdir: 

819 # If fail, use Platform SDK 

820 sdkdir = join(self.VCInstallDir, 'PlatformSDK') 

821 return sdkdir 

822 

823 @property 

824 def WindowsSDKExecutablePath(self): 

825 """ 

826 Microsoft Windows SDK executable directory. 

827 

828 Return 

829 ------ 

830 str 

831 path 

832 """ 

833 # Find WinSDK NetFx Tools registry dir name 

834 if self.vs_ver <= 11.0: 

835 netfxver = 35 

836 arch = '' 

837 else: 

838 netfxver = 40 

839 hidex86 = True if self.vs_ver <= 12.0 else False 

840 arch = self.pi.current_dir(x64=True, hidex86=hidex86) 

841 fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-')) 

842 

843 # list all possibles registry paths 

844 regpaths = [] 

845 if self.vs_ver >= 14.0: 

846 for ver in self.NetFxSdkVersion: 

847 regpaths += [join(self.ri.netfx_sdk, ver, fx)] 

848 

849 for ver in self.WindowsSdkVersion: 

850 regpaths += [join(self.ri.windows_sdk, 'v%sA' % ver, fx)] 

851 

852 # Return installation folder from the more recent path 

853 for path in regpaths: 

854 execpath = self.ri.lookup(path, 'installationfolder') 

855 if execpath: 

856 return execpath 

857 

858 @property 

859 def FSharpInstallDir(self): 

860 """ 

861 Microsoft Visual F# directory. 

862 

863 Return 

864 ------ 

865 str 

866 path 

867 """ 

868 path = join(self.ri.visualstudio, r'%0.1f\Setup\F#' % self.vs_ver) 

869 return self.ri.lookup(path, 'productdir') or '' 

870 

871 @property 

872 def UniversalCRTSdkDir(self): 

873 """ 

874 Microsoft Universal CRT SDK directory. 

875 

876 Return 

877 ------ 

878 str 

879 path 

880 """ 

881 # Set Kit Roots versions for specified MSVC++ version 

882 vers = ('10', '81') if self.vs_ver >= 14.0 else () 

883 

884 # Find path of the more recent Kit 

885 for ver in vers: 

886 sdkdir = self.ri.lookup(self.ri.windows_kits_roots, 

887 'kitsroot%s' % ver) 

888 if sdkdir: 

889 return sdkdir or '' 

890 

891 @property 

892 def UniversalCRTSdkLastVersion(self): 

893 """ 

894 Microsoft Universal C Runtime SDK last version. 

895 

896 Return 

897 ------ 

898 str 

899 version 

900 """ 

901 return self._use_last_dir_name(join(self.UniversalCRTSdkDir, 'lib')) 

902 

903 @property 

904 def NetFxSdkVersion(self): 

905 """ 

906 Microsoft .NET Framework SDK versions. 

907 

908 Return 

909 ------ 

910 tuple of str 

911 versions 

912 """ 

913 # Set FxSdk versions for specified VS version 

914 return (('4.7.2', '4.7.1', '4.7', 

915 '4.6.2', '4.6.1', '4.6', 

916 '4.5.2', '4.5.1', '4.5') 

917 if self.vs_ver >= 14.0 else ()) 

918 

919 @property 

920 def NetFxSdkDir(self): 

921 """ 

922 Microsoft .NET Framework SDK directory. 

923 

924 Return 

925 ------ 

926 str 

927 path 

928 """ 

929 sdkdir = '' 

930 for ver in self.NetFxSdkVersion: 

931 loc = join(self.ri.netfx_sdk, ver) 

932 sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder') 

933 if sdkdir: 

934 break 

935 return sdkdir 

936 

937 @property 

938 def FrameworkDir32(self): 

939 """ 

940 Microsoft .NET Framework 32bit directory. 

941 

942 Return 

943 ------ 

944 str 

945 path 

946 """ 

947 # Default path 

948 guess_fw = join(self.WinDir, r'Microsoft.NET\Framework') 

949 

950 # Try to get path from registry, if fail use default path 

951 return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw 

952 

953 @property 

954 def FrameworkDir64(self): 

955 """ 

956 Microsoft .NET Framework 64bit directory. 

957 

958 Return 

959 ------ 

960 str 

961 path 

962 """ 

963 # Default path 

964 guess_fw = join(self.WinDir, r'Microsoft.NET\Framework64') 

965 

966 # Try to get path from registry, if fail use default path 

967 return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw 

968 

969 @property 

970 def FrameworkVersion32(self): 

971 """ 

972 Microsoft .NET Framework 32bit versions. 

973 

974 Return 

975 ------ 

976 tuple of str 

977 versions 

978 """ 

979 return self._find_dot_net_versions(32) 

980 

981 @property 

982 def FrameworkVersion64(self): 

983 """ 

984 Microsoft .NET Framework 64bit versions. 

985 

986 Return 

987 ------ 

988 tuple of str 

989 versions 

990 """ 

991 return self._find_dot_net_versions(64) 

992 

993 def _find_dot_net_versions(self, bits): 

994 """ 

995 Find Microsoft .NET Framework versions. 

996 

997 Parameters 

998 ---------- 

999 bits: int 

1000 Platform number of bits: 32 or 64. 

1001 

1002 Return 

1003 ------ 

1004 tuple of str 

1005 versions 

1006 """ 

1007 # Find actual .NET version in registry 

1008 reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) 

1009 dot_net_dir = getattr(self, 'FrameworkDir%d' % bits) 

1010 ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or '' 

1011 

1012 # Set .NET versions for specified MSVC++ version 

1013 if self.vs_ver >= 12.0: 

1014 return ver, 'v4.0' 

1015 elif self.vs_ver >= 10.0: 

1016 return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5' 

1017 elif self.vs_ver == 9.0: 

1018 return 'v3.5', 'v2.0.50727' 

1019 elif self.vs_ver == 8.0: 

1020 return 'v3.0', 'v2.0.50727' 

1021 

1022 @staticmethod 

1023 def _use_last_dir_name(path, prefix=''): 

1024 """ 

1025 Return name of the last dir in path or '' if no dir found. 

1026 

1027 Parameters 

1028 ---------- 

1029 path: str 

1030 Use dirs in this path 

1031 prefix: str 

1032 Use only dirs starting by this prefix 

1033 

1034 Return 

1035 ------ 

1036 str 

1037 name 

1038 """ 

1039 matching_dirs = ( 

1040 dir_name 

1041 for dir_name in reversed(listdir(path)) 

1042 if isdir(join(path, dir_name)) and 

1043 dir_name.startswith(prefix) 

1044 ) 

1045 return next(matching_dirs, None) or '' 

1046 

1047 

1048class EnvironmentInfo: 

1049 """ 

1050 Return environment variables for specified Microsoft Visual C++ version 

1051 and platform : Lib, Include, Path and libpath. 

1052 

1053 This function is compatible with Microsoft Visual C++ 9.0 to 14.X. 

1054 

1055 Script created by analysing Microsoft environment configuration files like 

1056 "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ... 

1057 

1058 Parameters 

1059 ---------- 

1060 arch: str 

1061 Target architecture. 

1062 vc_ver: float 

1063 Required Microsoft Visual C++ version. If not set, autodetect the last 

1064 version. 

1065 vc_min_ver: float 

1066 Minimum Microsoft Visual C++ version. 

1067 """ 

1068 

1069 # Variables and properties in this class use originals CamelCase variables 

1070 # names from Microsoft source files for more easy comparison. 

1071 

1072 def __init__(self, arch, vc_ver=None, vc_min_ver=0): 

1073 self.pi = PlatformInfo(arch) 

1074 self.ri = RegistryInfo(self.pi) 

1075 self.si = SystemInfo(self.ri, vc_ver) 

1076 

1077 if self.vc_ver < vc_min_ver: 

1078 err = 'No suitable Microsoft Visual C++ version found' 

1079 raise distutils.errors.DistutilsPlatformError(err) 

1080 

1081 @property 

1082 def vs_ver(self): 

1083 """ 

1084 Microsoft Visual Studio. 

1085 

1086 Return 

1087 ------ 

1088 float 

1089 version 

1090 """ 

1091 return self.si.vs_ver 

1092 

1093 @property 

1094 def vc_ver(self): 

1095 """ 

1096 Microsoft Visual C++ version. 

1097 

1098 Return 

1099 ------ 

1100 float 

1101 version 

1102 """ 

1103 return self.si.vc_ver 

1104 

1105 @property 

1106 def VSTools(self): 

1107 """ 

1108 Microsoft Visual Studio Tools. 

1109 

1110 Return 

1111 ------ 

1112 list of str 

1113 paths 

1114 """ 

1115 paths = [r'Common7\IDE', r'Common7\Tools'] 

1116 

1117 if self.vs_ver >= 14.0: 

1118 arch_subdir = self.pi.current_dir(hidex86=True, x64=True) 

1119 paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow'] 

1120 paths += [r'Team Tools\Performance Tools'] 

1121 paths += [r'Team Tools\Performance Tools%s' % arch_subdir] 

1122 

1123 return [join(self.si.VSInstallDir, path) for path in paths] 

1124 

1125 @property 

1126 def VCIncludes(self): 

1127 """ 

1128 Microsoft Visual C++ & Microsoft Foundation Class Includes. 

1129 

1130 Return 

1131 ------ 

1132 list of str 

1133 paths 

1134 """ 

1135 return [join(self.si.VCInstallDir, 'Include'), 

1136 join(self.si.VCInstallDir, r'ATLMFC\Include')] 

1137 

1138 @property 

1139 def VCLibraries(self): 

1140 """ 

1141 Microsoft Visual C++ & Microsoft Foundation Class Libraries. 

1142 

1143 Return 

1144 ------ 

1145 list of str 

1146 paths 

1147 """ 

1148 if self.vs_ver >= 15.0: 

1149 arch_subdir = self.pi.target_dir(x64=True) 

1150 else: 

1151 arch_subdir = self.pi.target_dir(hidex86=True) 

1152 paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir] 

1153 

1154 if self.vs_ver >= 14.0: 

1155 paths += [r'Lib\store%s' % arch_subdir] 

1156 

1157 return [join(self.si.VCInstallDir, path) for path in paths] 

1158 

1159 @property 

1160 def VCStoreRefs(self): 

1161 """ 

1162 Microsoft Visual C++ store references Libraries. 

1163 

1164 Return 

1165 ------ 

1166 list of str 

1167 paths 

1168 """ 

1169 if self.vs_ver < 14.0: 

1170 return [] 

1171 return [join(self.si.VCInstallDir, r'Lib\store\references')] 

1172 

1173 @property 

1174 def VCTools(self): 

1175 """ 

1176 Microsoft Visual C++ Tools. 

1177 

1178 Return 

1179 ------ 

1180 list of str 

1181 paths 

1182 """ 

1183 si = self.si 

1184 tools = [join(si.VCInstallDir, 'VCPackages')] 

1185 

1186 forcex86 = True if self.vs_ver <= 10.0 else False 

1187 arch_subdir = self.pi.cross_dir(forcex86) 

1188 if arch_subdir: 

1189 tools += [join(si.VCInstallDir, 'Bin%s' % arch_subdir)] 

1190 

1191 if self.vs_ver == 14.0: 

1192 path = 'Bin%s' % self.pi.current_dir(hidex86=True) 

1193 tools += [join(si.VCInstallDir, path)] 

1194 

1195 elif self.vs_ver >= 15.0: 

1196 host_dir = (r'bin\HostX86%s' if self.pi.current_is_x86() else 

1197 r'bin\HostX64%s') 

1198 tools += [join( 

1199 si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))] 

1200 

1201 if self.pi.current_cpu != self.pi.target_cpu: 

1202 tools += [join( 

1203 si.VCInstallDir, host_dir % self.pi.current_dir(x64=True))] 

1204 

1205 else: 

1206 tools += [join(si.VCInstallDir, 'Bin')] 

1207 

1208 return tools 

1209 

1210 @property 

1211 def OSLibraries(self): 

1212 """ 

1213 Microsoft Windows SDK Libraries. 

1214 

1215 Return 

1216 ------ 

1217 list of str 

1218 paths 

1219 """ 

1220 if self.vs_ver <= 10.0: 

1221 arch_subdir = self.pi.target_dir(hidex86=True, x64=True) 

1222 return [join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)] 

1223 

1224 else: 

1225 arch_subdir = self.pi.target_dir(x64=True) 

1226 lib = join(self.si.WindowsSdkDir, 'lib') 

1227 libver = self._sdk_subdir 

1228 return [join(lib, '%sum%s' % (libver , arch_subdir))] 

1229 

1230 @property 

1231 def OSIncludes(self): 

1232 """ 

1233 Microsoft Windows SDK Include. 

1234 

1235 Return 

1236 ------ 

1237 list of str 

1238 paths 

1239 """ 

1240 include = join(self.si.WindowsSdkDir, 'include') 

1241 

1242 if self.vs_ver <= 10.0: 

1243 return [include, join(include, 'gl')] 

1244 

1245 else: 

1246 if self.vs_ver >= 14.0: 

1247 sdkver = self._sdk_subdir 

1248 else: 

1249 sdkver = '' 

1250 return [join(include, '%sshared' % sdkver), 

1251 join(include, '%sum' % sdkver), 

1252 join(include, '%swinrt' % sdkver)] 

1253 

1254 @property 

1255 def OSLibpath(self): 

1256 """ 

1257 Microsoft Windows SDK Libraries Paths. 

1258 

1259 Return 

1260 ------ 

1261 list of str 

1262 paths 

1263 """ 

1264 ref = join(self.si.WindowsSdkDir, 'References') 

1265 libpath = [] 

1266 

1267 if self.vs_ver <= 9.0: 

1268 libpath += self.OSLibraries 

1269 

1270 if self.vs_ver >= 11.0: 

1271 libpath += [join(ref, r'CommonConfiguration\Neutral')] 

1272 

1273 if self.vs_ver >= 14.0: 

1274 libpath += [ 

1275 ref, 

1276 join(self.si.WindowsSdkDir, 'UnionMetadata'), 

1277 join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'), 

1278 join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'), 

1279 join(ref,'Windows.Networking.Connectivity.WwanContract', 

1280 '1.0.0.0'), 

1281 join(self.si.WindowsSdkDir, 'ExtensionSDKs', 'Microsoft.VCLibs', 

1282 '%0.1f' % self.vs_ver, 'References', 'CommonConfiguration', 

1283 'neutral'), 

1284 ] 

1285 return libpath 

1286 

1287 @property 

1288 def SdkTools(self): 

1289 """ 

1290 Microsoft Windows SDK Tools. 

1291 

1292 Return 

1293 ------ 

1294 list of str 

1295 paths 

1296 """ 

1297 return list(self._sdk_tools()) 

1298 

1299 def _sdk_tools(self): 

1300 """ 

1301 Microsoft Windows SDK Tools paths generator. 

1302 

1303 Return 

1304 ------ 

1305 generator of str 

1306 paths 

1307 """ 

1308 if self.vs_ver < 15.0: 

1309 bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86' 

1310 yield join(self.si.WindowsSdkDir, bin_dir) 

1311 

1312 if not self.pi.current_is_x86(): 

1313 arch_subdir = self.pi.current_dir(x64=True) 

1314 path = 'Bin%s' % arch_subdir 

1315 yield join(self.si.WindowsSdkDir, path) 

1316 

1317 if self.vs_ver in (10.0, 11.0): 

1318 if self.pi.target_is_x86(): 

1319 arch_subdir = '' 

1320 else: 

1321 arch_subdir = self.pi.current_dir(hidex86=True, x64=True) 

1322 path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir 

1323 yield join(self.si.WindowsSdkDir, path) 

1324 

1325 elif self.vs_ver >= 15.0: 

1326 path = join(self.si.WindowsSdkDir, 'Bin') 

1327 arch_subdir = self.pi.current_dir(x64=True) 

1328 sdkver = self.si.WindowsSdkLastVersion 

1329 yield join(path, '%s%s' % (sdkver, arch_subdir)) 

1330 

1331 if self.si.WindowsSDKExecutablePath: 

1332 yield self.si.WindowsSDKExecutablePath 

1333 

1334 @property 

1335 def _sdk_subdir(self): 

1336 """ 

1337 Microsoft Windows SDK version subdir. 

1338 

1339 Return 

1340 ------ 

1341 str 

1342 subdir 

1343 """ 

1344 ucrtver = self.si.WindowsSdkLastVersion 

1345 return ('%s\\' % ucrtver) if ucrtver else '' 

1346 

1347 @property 

1348 def SdkSetup(self): 

1349 """ 

1350 Microsoft Windows SDK Setup. 

1351 

1352 Return 

1353 ------ 

1354 list of str 

1355 paths 

1356 """ 

1357 if self.vs_ver > 9.0: 

1358 return [] 

1359 

1360 return [join(self.si.WindowsSdkDir, 'Setup')] 

1361 

1362 @property 

1363 def FxTools(self): 

1364 """ 

1365 Microsoft .NET Framework Tools. 

1366 

1367 Return 

1368 ------ 

1369 list of str 

1370 paths 

1371 """ 

1372 pi = self.pi 

1373 si = self.si 

1374 

1375 if self.vs_ver <= 10.0: 

1376 include32 = True 

1377 include64 = not pi.target_is_x86() and not pi.current_is_x86() 

1378 else: 

1379 include32 = pi.target_is_x86() or pi.current_is_x86() 

1380 include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64' 

1381 

1382 tools = [] 

1383 if include32: 

1384 tools += [join(si.FrameworkDir32, ver) 

1385 for ver in si.FrameworkVersion32] 

1386 if include64: 

1387 tools += [join(si.FrameworkDir64, ver) 

1388 for ver in si.FrameworkVersion64] 

1389 return tools 

1390 

1391 @property 

1392 def NetFxSDKLibraries(self): 

1393 """ 

1394 Microsoft .Net Framework SDK Libraries. 

1395 

1396 Return 

1397 ------ 

1398 list of str 

1399 paths 

1400 """ 

1401 if self.vs_ver < 14.0 or not self.si.NetFxSdkDir: 

1402 return [] 

1403 

1404 arch_subdir = self.pi.target_dir(x64=True) 

1405 return [join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)] 

1406 

1407 @property 

1408 def NetFxSDKIncludes(self): 

1409 """ 

1410 Microsoft .Net Framework SDK Includes. 

1411 

1412 Return 

1413 ------ 

1414 list of str 

1415 paths 

1416 """ 

1417 if self.vs_ver < 14.0 or not self.si.NetFxSdkDir: 

1418 return [] 

1419 

1420 return [join(self.si.NetFxSdkDir, r'include\um')] 

1421 

1422 @property 

1423 def VsTDb(self): 

1424 """ 

1425 Microsoft Visual Studio Team System Database. 

1426 

1427 Return 

1428 ------ 

1429 list of str 

1430 paths 

1431 """ 

1432 return [join(self.si.VSInstallDir, r'VSTSDB\Deploy')] 

1433 

1434 @property 

1435 def MSBuild(self): 

1436 """ 

1437 Microsoft Build Engine. 

1438 

1439 Return 

1440 ------ 

1441 list of str 

1442 paths 

1443 """ 

1444 if self.vs_ver < 12.0: 

1445 return [] 

1446 elif self.vs_ver < 15.0: 

1447 base_path = self.si.ProgramFilesx86 

1448 arch_subdir = self.pi.current_dir(hidex86=True) 

1449 else: 

1450 base_path = self.si.VSInstallDir 

1451 arch_subdir = '' 

1452 

1453 path = r'MSBuild\%0.1f\bin%s' % (self.vs_ver, arch_subdir) 

1454 build = [join(base_path, path)] 

1455 

1456 if self.vs_ver >= 15.0: 

1457 # Add Roslyn C# & Visual Basic Compiler 

1458 build += [join(base_path, path, 'Roslyn')] 

1459 

1460 return build 

1461 

1462 @property 

1463 def HTMLHelpWorkshop(self): 

1464 """ 

1465 Microsoft HTML Help Workshop. 

1466 

1467 Return 

1468 ------ 

1469 list of str 

1470 paths 

1471 """ 

1472 if self.vs_ver < 11.0: 

1473 return [] 

1474 

1475 return [join(self.si.ProgramFilesx86, 'HTML Help Workshop')] 

1476 

1477 @property 

1478 def UCRTLibraries(self): 

1479 """ 

1480 Microsoft Universal C Runtime SDK Libraries. 

1481 

1482 Return 

1483 ------ 

1484 list of str 

1485 paths 

1486 """ 

1487 if self.vs_ver < 14.0: 

1488 return [] 

1489 

1490 arch_subdir = self.pi.target_dir(x64=True) 

1491 lib = join(self.si.UniversalCRTSdkDir, 'lib') 

1492 ucrtver = self._ucrt_subdir 

1493 return [join(lib, '%sucrt%s' % (ucrtver, arch_subdir))] 

1494 

1495 @property 

1496 def UCRTIncludes(self): 

1497 """ 

1498 Microsoft Universal C Runtime SDK Include. 

1499 

1500 Return 

1501 ------ 

1502 list of str 

1503 paths 

1504 """ 

1505 if self.vs_ver < 14.0: 

1506 return [] 

1507 

1508 include = join(self.si.UniversalCRTSdkDir, 'include') 

1509 return [join(include, '%sucrt' % self._ucrt_subdir)] 

1510 

1511 @property 

1512 def _ucrt_subdir(self): 

1513 """ 

1514 Microsoft Universal C Runtime SDK version subdir. 

1515 

1516 Return 

1517 ------ 

1518 str 

1519 subdir 

1520 """ 

1521 ucrtver = self.si.UniversalCRTSdkLastVersion 

1522 return ('%s\\' % ucrtver) if ucrtver else '' 

1523 

1524 @property 

1525 def FSharp(self): 

1526 """ 

1527 Microsoft Visual F#. 

1528 

1529 Return 

1530 ------ 

1531 list of str 

1532 paths 

1533 """ 

1534 if 11.0 > self.vs_ver > 12.0: 

1535 return [] 

1536 

1537 return [self.si.FSharpInstallDir] 

1538 

1539 @property 

1540 def VCRuntimeRedist(self): 

1541 """ 

1542 Microsoft Visual C++ runtime redistributable dll. 

1543 

1544 Return 

1545 ------ 

1546 str 

1547 path 

1548 """ 

1549 vcruntime = 'vcruntime%d0.dll' % self.vc_ver 

1550 arch_subdir = self.pi.target_dir(x64=True).strip('\\') 

1551 

1552 # Installation prefixes candidates 

1553 prefixes = [] 

1554 tools_path = self.si.VCInstallDir 

1555 redist_path = dirname(tools_path.replace(r'\Tools', r'\Redist')) 

1556 if isdir(redist_path): 

1557 # Redist version may not be exactly the same as tools 

1558 redist_path = join(redist_path, listdir(redist_path)[-1]) 

1559 prefixes += [redist_path, join(redist_path, 'onecore')] 

1560 

1561 prefixes += [join(tools_path, 'redist')] # VS14 legacy path 

1562 

1563 # CRT directory 

1564 crt_dirs = ('Microsoft.VC%d.CRT' % (self.vc_ver * 10), 

1565 # Sometime store in directory with VS version instead of VC 

1566 'Microsoft.VC%d.CRT' % (int(self.vs_ver) * 10)) 

1567 

1568 # vcruntime path 

1569 for prefix, crt_dir in itertools.product(prefixes, crt_dirs): 

1570 path = join(prefix, arch_subdir, crt_dir, vcruntime) 

1571 if isfile(path): 

1572 return path 

1573 

1574 def return_env(self, exists=True): 

1575 """ 

1576 Return environment dict. 

1577 

1578 Parameters 

1579 ---------- 

1580 exists: bool 

1581 It True, only return existing paths. 

1582 

1583 Return 

1584 ------ 

1585 dict 

1586 environment 

1587 """ 

1588 env = dict( 

1589 include=self._build_paths('include', 

1590 [self.VCIncludes, 

1591 self.OSIncludes, 

1592 self.UCRTIncludes, 

1593 self.NetFxSDKIncludes], 

1594 exists), 

1595 lib=self._build_paths('lib', 

1596 [self.VCLibraries, 

1597 self.OSLibraries, 

1598 self.FxTools, 

1599 self.UCRTLibraries, 

1600 self.NetFxSDKLibraries], 

1601 exists), 

1602 libpath=self._build_paths('libpath', 

1603 [self.VCLibraries, 

1604 self.FxTools, 

1605 self.VCStoreRefs, 

1606 self.OSLibpath], 

1607 exists), 

1608 path=self._build_paths('path', 

1609 [self.VCTools, 

1610 self.VSTools, 

1611 self.VsTDb, 

1612 self.SdkTools, 

1613 self.SdkSetup, 

1614 self.FxTools, 

1615 self.MSBuild, 

1616 self.HTMLHelpWorkshop, 

1617 self.FSharp], 

1618 exists), 

1619 ) 

1620 if self.vs_ver >= 14 and isfile(self.VCRuntimeRedist): 

1621 env['py_vcruntime_redist'] = self.VCRuntimeRedist 

1622 return env 

1623 

1624 def _build_paths(self, name, spec_path_lists, exists): 

1625 """ 

1626 Given an environment variable name and specified paths, 

1627 return a pathsep-separated string of paths containing 

1628 unique, extant, directories from those paths and from 

1629 the environment variable. Raise an error if no paths 

1630 are resolved. 

1631 

1632 Parameters 

1633 ---------- 

1634 name: str 

1635 Environment variable name 

1636 spec_path_lists: list of str 

1637 Paths 

1638 exists: bool 

1639 It True, only return existing paths. 

1640 

1641 Return 

1642 ------ 

1643 str 

1644 Pathsep-separated paths 

1645 """ 

1646 # flatten spec_path_lists 

1647 spec_paths = itertools.chain.from_iterable(spec_path_lists) 

1648 env_paths = environ.get(name, '').split(pathsep) 

1649 paths = itertools.chain(spec_paths, env_paths) 

1650 extant_paths = list(filter(isdir, paths)) if exists else paths 

1651 if not extant_paths: 

1652 msg = "%s environment variable is empty" % name.upper() 

1653 raise distutils.errors.DistutilsPlatformError(msg) 

1654 unique_paths = self._unique_everseen(extant_paths) 

1655 return pathsep.join(unique_paths) 

1656 

1657 # from Python docs 

1658 @staticmethod 

1659 def _unique_everseen(iterable, key=None): 

1660 """ 

1661 List unique elements, preserving order. 

1662 Remember all elements ever seen. 

1663 

1664 _unique_everseen('AAAABBBCCDAABBB') --> A B C D 

1665 

1666 _unique_everseen('ABBCcAD', str.lower) --> A B C D 

1667 """ 

1668 seen = set() 

1669 seen_add = seen.add 

1670 if key is None: 

1671 for element in filterfalse(seen.__contains__, iterable): 

1672 seen_add(element) 

1673 yield element 

1674 else: 

1675 for element in iterable: 

1676 k = key(element) 

1677 if k not in seen: 

1678 seen_add(k) 

1679 yield element