~inkscape.dev/inkscape-devlibs/trunk

« back to all changes in this revision

Viewing changes to python/Lib/site-packages/setuptools/msvc.py

  • Committer: Eduard Braun
  • Date: 2016-12-22 23:29:22 UTC
  • Revision ID: eduard.braun2@gmx.de-20161222232922-45qxef2vw6v0ekr2
Sync python with devlibs64

r35:
Python: include pyserial (3.2.1) and pip (9.0.1) packages

r36:
Python: Fix scripts (executables in in Scripts directory)
They include an absolute path to the python executable (which is system dependent). Replace this with a simple call to "python.exe" (which means python has to be on the search path).

See also http://www.clemens-sielaff.com/create-a-portable-python-with-pip-on-windows/

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 pkg_resources.extern.packaging.version import LegacyVersion
 
24
 
 
25
from setuptools.extern.six.moves import filterfalse
 
26
 
 
27
from .monkey import get_unpatched
 
28
 
 
29
if platform.system() == 'Windows':
 
30
    from setuptools.extern.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 ''