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