msvc_detection.py 3.67 KB
Newer Older
1
import os
Martin Bauer's avatar
Martin Bauer committed
2
import subprocess
3
4


Martin Bauer's avatar
Martin Bauer committed
5
6
7
8
9
10
11
def get_environment(version_specifier, arch='x64'):
    """Returns an environment dictionary, for activating the Visual Studio compiler.

    Args:
        version_specifier: either a version number, year number, 'auto' or 'latest' for automatic detection of latest
                          installed version or 'setuptools' for setuptools-based detection
        arch: x86 or x64
12
    """
Martin Bauer's avatar
Martin Bauer committed
13
14
15
16
17
    if version_specifier == 'setuptools':
        return get_environment_from_setup_tools(arch)
    elif '\\' in version_specifier:
        vc_vars_path = find_vc_vars_all_via_filesystem_search(version_specifier)
        return get_environment_from_vc_vars_file(vc_vars_path, arch)
18
    else:
19
        try:
Martin Bauer's avatar
Martin Bauer committed
20
21
            if version_specifier in ('auto', 'latest'):
                version_nr = find_latest_msvc_version_using_environment_variables()
22
            else:
Martin Bauer's avatar
Martin Bauer committed
23
24
                version_nr = normalize_msvc_version(version_specifier)
            vc_vars_path = get_vc_vars_path_via_environment_variable(version_nr)
25
        except ValueError:
Martin Bauer's avatar
Martin Bauer committed
26
27
28
29
            vc_vars_path = find_vc_vars_all_via_filesystem_search("C:\\Program Files (x86)\\Microsoft Visual Studio")
            if vc_vars_path is None:
                vc_vars_path = find_vc_vars_all_via_filesystem_search("C:\\Program Files\\Microsoft Visual Studio")
            if vc_vars_path is None:
30
31
                raise ValueError("Visual Studio not found. Write path to VS folder in pystencils config")

Martin Bauer's avatar
Martin Bauer committed
32
        return get_environment_from_vc_vars_file(vc_vars_path, arch)
33
34


Martin Bauer's avatar
Martin Bauer committed
35
def find_latest_msvc_version_using_environment_variables():
36
    import re
Martin Bauer's avatar
Martin Bauer committed
37
    # noinspection SpellCheckingInspection
38
    regex = re.compile(r'VS(\d\d)\dCOMNTOOLS')
39
40
41
42
43
44
45
46
47
48
49
    versions = []
    for key, value in os.environ.items():
        match = regex.match(key)
        if match:
            versions.append(int(match.group(1)))
    if len(versions) == 0:
        raise ValueError("Visual Studio not found.")
    versions.sort()
    return versions[-1]


Martin Bauer's avatar
Martin Bauer committed
50
def normalize_msvc_version(version):
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
    """
    Takes version specifiers in the following form:
        - year: 2012, 2013, 2015, either as int or string
        - version numbers with or without dot i.e. 11.0 or 11
    :return: integer version number
    """
    if isinstance(version, str) and '.' in version:
        version = version.split('.')[0]

    version = int(version)
    mapping = {
        2015: 14,
        2013: 12,
        2012: 11
    }
    if version in mapping:
        return mapping[version]
    else:
        return version


Martin Bauer's avatar
Martin Bauer committed
72
def get_environment_from_vc_vars_file(vc_vars_file, arch):
73
    out = subprocess.check_output(
74
        f'cmd /u /c "{vc_vars_file}" {arch} && set',
75
76
77
78
79
80
81
        stderr=subprocess.STDOUT,
    ).decode('utf-16le', errors='replace')

    env = {key.upper(): value for key, _, value in (line.partition('=') for line in out.splitlines()) if key and value}
    return env


Martin Bauer's avatar
Martin Bauer committed
82
83
84
85
86
def get_vc_vars_path_via_environment_variable(version_nr):
    # noinspection SpellCheckingInspection
    environment_var_name = 'VS%d0COMNTOOLS' % (version_nr,)
    vc_path = os.environ[environment_var_name]
    path = os.path.join(vc_path, '..', '..', 'VC', 'vcvarsall.bat')
87
88
89
    return os.path.abspath(path)


Martin Bauer's avatar
Martin Bauer committed
90
def get_environment_from_setup_tools(arch):
91
    from setuptools.msvc import msvc14_get_vc_env
Martin Bauer's avatar
Martin Bauer committed
92
93
    msvc_env = msvc14_get_vc_env(arch)
    return {k.upper(): v for k, v in msvc_env.items()}
94
95


Martin Bauer's avatar
Martin Bauer committed
96
def find_vc_vars_all_via_filesystem_search(base_path):
97
    matches = []
Martin Bauer's avatar
Martin Bauer committed
98
99
    for root, dir_names, file_names in os.walk(base_path):
        for filename in file_names:
100
101
102
103
104
105
            if filename == 'vcvarsall.bat':
                matches.append(os.path.join(root, filename))

    matches.sort(reverse=True)
    if matches:
        return matches[0]