distutils msvccompiler contains msvccompiler an implementation of the abstract ccompiler class for microsoft visual studio 2015 the module is compatible with vs 2015 and later you can find legacy support for older versions in distutils msvc9compiler and distutils msvccompiler written by perry stoll hacked by robin becker and thomas heller to do a better job of finding devstudio through the registry ported to vs 2005 and vs 2008 by christian heimes ported to vs 2015 by steve dower returns 15 path based on the result of invoking vswhere exe if no install is found returns none none the version is returned to avoid unnecessarily changing the function result it may be ignored when the path is not none if vswhere exe is not available by definition vs 2017 is not installed bpo38597 removed vcruntime return value return path to an msvc executable program tries to find the program in several places first one of the msvc program search paths from the registry next the directories in the path environment variable if any of those work return an absolute path that is known to exist if none of them work just return the original program name exe a map keyed by getplatform return values to values accepted by vcvarsall bat always crosscompile from x86 to work with the lighterweight msvc installs that do not include native 64bit tools concrete class that implements an interface to microsoft visual c as defined by the ccompiler abstract class compilertype msvc just set this so ccompiler s constructor doesn t barf we currently don t use the setexecutables bureaucracy provided by ccompiler as it really isn t necessary for this sort of singlecompiler class would be nice to have a consistent interface with unixccompiler though so it s worth thinking about executables private class data need to distinguish c from c source for compiler cextensions c cppextensions cc cpp cxx rcextensions rc mcextensions mc needed for the filename generation methods provided by the base class ccompiler srcextensions cextensions cppextensions rcextensions mcextensions resextension res objextension obj staticlibextension lib sharedlibextension dll staticlibformat sharedlibformat ss exeextension exe def initself verbose0 dryrun0 force0 ccompiler init self verbose dryrun force target platform platname is consistent with bdist self platname none self initialized false written by perry stoll hacked by robin becker and thomas heller to do a better job of finding devstudio through the registry ported to vs 2005 and vs 2008 by christian heimes ported to vs 2015 by steve dower returns 15 path based on the result of invoking vswhere exe if no install is found returns none none the version is returned to avoid unnecessarily changing the function result it may be ignored when the path is not none if vswhere exe is not available by definition vs 2017 is not installed bpo 38597 removed vcruntime return value return path to an msvc executable program tries to find the program in several places first one of the msvc program search paths from the registry next the directories in the path environment variable if any of those work return an absolute path that is known to exist if none of them work just return the original program name exe a map keyed by get_platform return values to values accepted by vcvarsall bat always cross compile from x86 to work with the lighter weight msvc installs that do not include native 64 bit tools concrete class that implements an interface to microsoft visual c as defined by the ccompiler abstract class just set this so ccompiler s constructor doesn t barf we currently don t use the set_executables bureaucracy provided by ccompiler as it really isn t necessary for this sort of single compiler class would be nice to have a consistent interface with unixccompiler though so it s worth thinking about private class data need to distinguish c from c source for compiler needed for the filename generation methods provided by the base class ccompiler target platform plat_name is consistent with bdist
import os
import subprocess
import winreg

from distutils.errors import DistutilsPlatformError
from distutils.ccompiler import CCompiler
from distutils import log

from itertools import count

def _find_vc2015():
    try:
        key = winreg.OpenKeyEx(
            winreg.HKEY_LOCAL_MACHINE,
            r"Software\Microsoft\VisualStudio\SxS\VC7",
            access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY
        )
    except OSError:
        log.debug("Visual C++ is not registered")
        return None, None

    best_version = 0
    best_dir = None
    with key:
        for i in count():
            try:
                v, vc_dir, vt = winreg.EnumValue(key, i)
            except OSError:
                break
            if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):
                try:
                    version = int(float(v))
                except (ValueError, TypeError):
                    continue
                if version >= 14 and version > best_version:
                    best_version, best_dir = version, vc_dir
    return best_version, best_dir

def _find_vc2017():
    
    root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
    if not root:
        return None, None

    try:
        path = subprocess.check_output([
            os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"),
            "-latest",
            "-prerelease",
            "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
            "-property", "installationPath",
            "-products", "*",
        ], encoding="mbcs", errors="strict").strip()
    except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
        return None, None

    path = os.path.join(path, "VC", "Auxiliary", "Build")
    if os.path.isdir(path):
        return 15, path

    return None, None

PLAT_SPEC_TO_RUNTIME = {
    'x86' : 'x86',
    'x86_amd64' : 'x64',
    'x86_arm' : 'arm',
    'x86_arm64' : 'arm64'
}

def _find_vcvarsall(plat_spec):
    
    _, best_dir = _find_vc2017()

    if not best_dir:
        best_version, best_dir = _find_vc2015()

    if not best_dir:
        log.debug("No suitable Visual C++ version found")
        return None, None

    vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
    if not os.path.isfile(vcvarsall):
        log.debug("%s cannot be found", vcvarsall)
        return None, None

    return vcvarsall, None

def _get_vc_env(plat_spec):
    if os.getenv("DISTUTILS_USE_SDK"):
        return {
            key.lower(): value
            for key, value in os.environ.items()
        }

    vcvarsall, _ = _find_vcvarsall(plat_spec)
    if not vcvarsall:
        raise DistutilsPlatformError("Unable to find vcvarsall.bat")

    try:
        out = subprocess.check_output(
            'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec),
            stderr=subprocess.STDOUT,
        ).decode('utf-16le', errors='replace')
    except subprocess.CalledProcessError as exc:
        log.error(exc.output)
        raise DistutilsPlatformError("Error executing {}"
                .format(exc.cmd))

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

    return env

def _find_exe(exe, paths=None):
    
    if not paths:
        paths = os.getenv('path').split(os.pathsep)
    for p in paths:
        fn = os.path.join(os.path.abspath(p), exe)
        if os.path.isfile(fn):
            return fn
    return exe




PLAT_TO_VCVARS = {
    'win32' : 'x86',
    'win-amd64' : 'x86_amd64',
    'win-arm32' : 'x86_arm',
    'win-arm64' : 'x86_arm64'
}

class MSVCCompiler(CCompiler) :
    

    compiler_type = 'msvc'

    
    
    
    
    
    executables = {}

    
    _c_extensions = ['.c']
    _cpp_extensions = ['.cc', '.cpp', '.cxx']
    _rc_extensions = ['.rc']
    _mc_extensions = ['.mc']

    
    
    src_extensions = (_c_extensions + _cpp_extensions +
                      _rc_extensions + _mc_extensions)
    res_extension = '.res'
    obj_extension = '.obj'
    static_lib_extension = '.lib'
    shared_lib_extension = '.dll'
    static_lib_format = shared_lib_format = '%s%s'
    exe_extension = '.exe'


    def __init__(self, verbose=0, dry_run=0, force=0):
        CCompiler.__init__ (self, verbose, dry_run, force)
        
        self.plat_name = None
        self.initialized = False