Path: blob/master/venv/Lib/site-packages/setuptools/_distutils/_msvccompiler.py
811 views
"""distutils._msvccompiler12Contains MSVCCompiler, an implementation of the abstract CCompiler class3for Microsoft Visual Studio 2015.45The module is compatible with VS 2015 and later. You can find legacy support6for older versions in distutils.msvc9compiler and distutils.msvccompiler.7"""89# Written by Perry Stoll10# hacked by Robin Becker and Thomas Heller to do a better job of11# finding DevStudio (through the registry)12# ported to VS 2005 and VS 2008 by Christian Heimes13# ported to VS 2015 by Steve Dower1415import os16import subprocess17import winreg1819from distutils.errors import DistutilsExecError, DistutilsPlatformError, \20CompileError, LibError, LinkError21from distutils.ccompiler import CCompiler, gen_lib_options22from distutils import log23from distutils.util import get_platform2425from itertools import count2627def _find_vc2015():28try:29key = winreg.OpenKeyEx(30winreg.HKEY_LOCAL_MACHINE,31r"Software\Microsoft\VisualStudio\SxS\VC7",32access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY33)34except OSError:35log.debug("Visual C++ is not registered")36return None, None3738best_version = 039best_dir = None40with key:41for i in count():42try:43v, vc_dir, vt = winreg.EnumValue(key, i)44except OSError:45break46if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):47try:48version = int(float(v))49except (ValueError, TypeError):50continue51if version >= 14 and version > best_version:52best_version, best_dir = version, vc_dir53return best_version, best_dir5455def _find_vc2017():56"""Returns "15, path" based on the result of invoking vswhere.exe57If no install is found, returns "None, None"5859The version is returned to avoid unnecessarily changing the function60result. It may be ignored when the path is not None.6162If vswhere.exe is not available, by definition, VS 2017 is not63installed.64"""65root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")66if not root:67return None, None6869try:70path = subprocess.check_output([71os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"),72"-latest",73"-prerelease",74"-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",75"-property", "installationPath",76"-products", "*",77], encoding="mbcs", errors="strict").strip()78except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):79return None, None8081path = os.path.join(path, "VC", "Auxiliary", "Build")82if os.path.isdir(path):83return 15, path8485return None, None8687PLAT_SPEC_TO_RUNTIME = {88'x86' : 'x86',89'x86_amd64' : 'x64',90'x86_arm' : 'arm',91'x86_arm64' : 'arm64'92}9394def _find_vcvarsall(plat_spec):95# bpo-38597: Removed vcruntime return value96_, best_dir = _find_vc2017()9798if not best_dir:99best_version, best_dir = _find_vc2015()100101if not best_dir:102log.debug("No suitable Visual C++ version found")103return None, None104105vcvarsall = os.path.join(best_dir, "vcvarsall.bat")106if not os.path.isfile(vcvarsall):107log.debug("%s cannot be found", vcvarsall)108return None, None109110return vcvarsall, None111112def _get_vc_env(plat_spec):113if os.getenv("DISTUTILS_USE_SDK"):114return {115key.lower(): value116for key, value in os.environ.items()117}118119vcvarsall, _ = _find_vcvarsall(plat_spec)120if not vcvarsall:121raise DistutilsPlatformError("Unable to find vcvarsall.bat")122123try:124out = subprocess.check_output(125'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec),126stderr=subprocess.STDOUT,127).decode('utf-16le', errors='replace')128except subprocess.CalledProcessError as exc:129log.error(exc.output)130raise DistutilsPlatformError("Error executing {}"131.format(exc.cmd))132133env = {134key.lower(): value135for key, _, value in136(line.partition('=') for line in out.splitlines())137if key and value138}139140return env141142def _find_exe(exe, paths=None):143"""Return path to an MSVC executable program.144145Tries to find the program in several places: first, one of the146MSVC program search paths from the registry; next, the directories147in the PATH environment variable. If any of those work, return an148absolute path that is known to exist. If none of them work, just149return the original program name, 'exe'.150"""151if not paths:152paths = os.getenv('path').split(os.pathsep)153for p in paths:154fn = os.path.join(os.path.abspath(p), exe)155if os.path.isfile(fn):156return fn157return exe158159# A map keyed by get_platform() return values to values accepted by160# 'vcvarsall.bat'. Always cross-compile from x86 to work with the161# lighter-weight MSVC installs that do not include native 64-bit tools.162PLAT_TO_VCVARS = {163'win32' : 'x86',164'win-amd64' : 'x86_amd64',165'win-arm32' : 'x86_arm',166'win-arm64' : 'x86_arm64'167}168169class MSVCCompiler(CCompiler) :170"""Concrete class that implements an interface to Microsoft Visual C++,171as defined by the CCompiler abstract class."""172173compiler_type = 'msvc'174175# Just set this so CCompiler's constructor doesn't barf. We currently176# don't use the 'set_executables()' bureaucracy provided by CCompiler,177# as it really isn't necessary for this sort of single-compiler class.178# Would be nice to have a consistent interface with UnixCCompiler,179# though, so it's worth thinking about.180executables = {}181182# Private class data (need to distinguish C from C++ source for compiler)183_c_extensions = ['.c']184_cpp_extensions = ['.cc', '.cpp', '.cxx']185_rc_extensions = ['.rc']186_mc_extensions = ['.mc']187188# Needed for the filename generation methods provided by the189# base class, CCompiler.190src_extensions = (_c_extensions + _cpp_extensions +191_rc_extensions + _mc_extensions)192res_extension = '.res'193obj_extension = '.obj'194static_lib_extension = '.lib'195shared_lib_extension = '.dll'196static_lib_format = shared_lib_format = '%s%s'197exe_extension = '.exe'198199200def __init__(self, verbose=0, dry_run=0, force=0):201CCompiler.__init__ (self, verbose, dry_run, force)202# target platform (.plat_name is consistent with 'bdist')203self.plat_name = None204self.initialized = False205206def initialize(self, plat_name=None):207# multi-init means we would need to check platform same each time...208assert not self.initialized, "don't init multiple times"209if plat_name is None:210plat_name = get_platform()211# sanity check for platforms to prevent obscure errors later.212if plat_name not in PLAT_TO_VCVARS:213raise DistutilsPlatformError("--plat-name must be one of {}"214.format(tuple(PLAT_TO_VCVARS)))215216# Get the vcvarsall.bat spec for the requested platform.217plat_spec = PLAT_TO_VCVARS[plat_name]218219vc_env = _get_vc_env(plat_spec)220if not vc_env:221raise DistutilsPlatformError("Unable to find a compatible "222"Visual Studio installation.")223224self._paths = vc_env.get('path', '')225paths = self._paths.split(os.pathsep)226self.cc = _find_exe("cl.exe", paths)227self.linker = _find_exe("link.exe", paths)228self.lib = _find_exe("lib.exe", paths)229self.rc = _find_exe("rc.exe", paths) # resource compiler230self.mc = _find_exe("mc.exe", paths) # message compiler231self.mt = _find_exe("mt.exe", paths) # message compiler232233for dir in vc_env.get('include', '').split(os.pathsep):234if dir:235self.add_include_dir(dir.rstrip(os.sep))236237for dir in vc_env.get('lib', '').split(os.pathsep):238if dir:239self.add_library_dir(dir.rstrip(os.sep))240241self.preprocess_options = None242# bpo-38597: Always compile with dynamic linking243# Future releases of Python 3.x will include all past244# versions of vcruntime*.dll for compatibility.245self.compile_options = [246'/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG', '/MD'247]248249self.compile_options_debug = [250'/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG'251]252253ldflags = [254'/nologo', '/INCREMENTAL:NO', '/LTCG'255]256257ldflags_debug = [258'/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL'259]260261self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1']262self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1']263self.ldflags_shared = [*ldflags, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO']264self.ldflags_shared_debug = [*ldflags_debug, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO']265self.ldflags_static = [*ldflags]266self.ldflags_static_debug = [*ldflags_debug]267268self._ldflags = {269(CCompiler.EXECUTABLE, None): self.ldflags_exe,270(CCompiler.EXECUTABLE, False): self.ldflags_exe,271(CCompiler.EXECUTABLE, True): self.ldflags_exe_debug,272(CCompiler.SHARED_OBJECT, None): self.ldflags_shared,273(CCompiler.SHARED_OBJECT, False): self.ldflags_shared,274(CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug,275(CCompiler.SHARED_LIBRARY, None): self.ldflags_static,276(CCompiler.SHARED_LIBRARY, False): self.ldflags_static,277(CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug,278}279280self.initialized = True281282# -- Worker methods ------------------------------------------------283284def object_filenames(self,285source_filenames,286strip_dir=0,287output_dir=''):288ext_map = {289**{ext: self.obj_extension for ext in self.src_extensions},290**{ext: self.res_extension for ext in self._rc_extensions + self._mc_extensions},291}292293output_dir = output_dir or ''294295def make_out_path(p):296base, ext = os.path.splitext(p)297if strip_dir:298base = os.path.basename(base)299else:300_, base = os.path.splitdrive(base)301if base.startswith((os.path.sep, os.path.altsep)):302base = base[1:]303try:304# XXX: This may produce absurdly long paths. We should check305# the length of the result and trim base until we fit within306# 260 characters.307return os.path.join(output_dir, base + ext_map[ext])308except LookupError:309# Better to raise an exception instead of silently continuing310# and later complain about sources and targets having311# different lengths312raise CompileError("Don't know how to compile {}".format(p))313314return list(map(make_out_path, source_filenames))315316317def compile(self, sources,318output_dir=None, macros=None, include_dirs=None, debug=0,319extra_preargs=None, extra_postargs=None, depends=None):320321if not self.initialized:322self.initialize()323compile_info = self._setup_compile(output_dir, macros, include_dirs,324sources, depends, extra_postargs)325macros, objects, extra_postargs, pp_opts, build = compile_info326327compile_opts = extra_preargs or []328compile_opts.append('/c')329if debug:330compile_opts.extend(self.compile_options_debug)331else:332compile_opts.extend(self.compile_options)333334335add_cpp_opts = False336337for obj in objects:338try:339src, ext = build[obj]340except KeyError:341continue342if debug:343# pass the full pathname to MSVC in debug mode,344# this allows the debugger to find the source file345# without asking the user to browse for it346src = os.path.abspath(src)347348if ext in self._c_extensions:349input_opt = "/Tc" + src350elif ext in self._cpp_extensions:351input_opt = "/Tp" + src352add_cpp_opts = True353elif ext in self._rc_extensions:354# compile .RC to .RES file355input_opt = src356output_opt = "/fo" + obj357try:358self.spawn([self.rc] + pp_opts + [output_opt, input_opt])359except DistutilsExecError as msg:360raise CompileError(msg)361continue362elif ext in self._mc_extensions:363# Compile .MC to .RC file to .RES file.364# * '-h dir' specifies the directory for the365# generated include file366# * '-r dir' specifies the target directory of the367# generated RC file and the binary message resource368# it includes369#370# For now (since there are no options to change this),371# we use the source-directory for the include file and372# the build directory for the RC file and message373# resources. This works at least for win32all.374h_dir = os.path.dirname(src)375rc_dir = os.path.dirname(obj)376try:377# first compile .MC to .RC and .H file378self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src])379base, _ = os.path.splitext(os.path.basename (src))380rc_file = os.path.join(rc_dir, base + '.rc')381# then compile .RC to .RES file382self.spawn([self.rc, "/fo" + obj, rc_file])383384except DistutilsExecError as msg:385raise CompileError(msg)386continue387else:388# how to handle this file?389raise CompileError("Don't know how to compile {} to {}"390.format(src, obj))391392args = [self.cc] + compile_opts + pp_opts393if add_cpp_opts:394args.append('/EHsc')395args.append(input_opt)396args.append("/Fo" + obj)397args.extend(extra_postargs)398399try:400self.spawn(args)401except DistutilsExecError as msg:402raise CompileError(msg)403404return objects405406407def create_static_lib(self,408objects,409output_libname,410output_dir=None,411debug=0,412target_lang=None):413414if not self.initialized:415self.initialize()416objects, output_dir = self._fix_object_args(objects, output_dir)417output_filename = self.library_filename(output_libname,418output_dir=output_dir)419420if self._need_link(objects, output_filename):421lib_args = objects + ['/OUT:' + output_filename]422if debug:423pass # XXX what goes here?424try:425log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args))426self.spawn([self.lib] + lib_args)427except DistutilsExecError as msg:428raise LibError(msg)429else:430log.debug("skipping %s (up-to-date)", output_filename)431432433def link(self,434target_desc,435objects,436output_filename,437output_dir=None,438libraries=None,439library_dirs=None,440runtime_library_dirs=None,441export_symbols=None,442debug=0,443extra_preargs=None,444extra_postargs=None,445build_temp=None,446target_lang=None):447448if not self.initialized:449self.initialize()450objects, output_dir = self._fix_object_args(objects, output_dir)451fixed_args = self._fix_lib_args(libraries, library_dirs,452runtime_library_dirs)453libraries, library_dirs, runtime_library_dirs = fixed_args454455if runtime_library_dirs:456self.warn("I don't know what to do with 'runtime_library_dirs': "457+ str(runtime_library_dirs))458459lib_opts = gen_lib_options(self,460library_dirs, runtime_library_dirs,461libraries)462if output_dir is not None:463output_filename = os.path.join(output_dir, output_filename)464465if self._need_link(objects, output_filename):466ldflags = self._ldflags[target_desc, debug]467468export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])]469470ld_args = (ldflags + lib_opts + export_opts +471objects + ['/OUT:' + output_filename])472473# The MSVC linker generates .lib and .exp files, which cannot be474# suppressed by any linker switches. The .lib files may even be475# needed! Make sure they are generated in the temporary build476# directory. Since they have different names for debug and release477# builds, they can go into the same directory.478build_temp = os.path.dirname(objects[0])479if export_symbols is not None:480(dll_name, dll_ext) = os.path.splitext(481os.path.basename(output_filename))482implib_file = os.path.join(483build_temp,484self.library_filename(dll_name))485ld_args.append ('/IMPLIB:' + implib_file)486487if extra_preargs:488ld_args[:0] = extra_preargs489if extra_postargs:490ld_args.extend(extra_postargs)491492output_dir = os.path.dirname(os.path.abspath(output_filename))493self.mkpath(output_dir)494try:495log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args))496self.spawn([self.linker] + ld_args)497except DistutilsExecError as msg:498raise LinkError(msg)499else:500log.debug("skipping %s (up-to-date)", output_filename)501502def spawn(self, cmd):503old_path = os.getenv('path')504try:505os.environ['path'] = self._paths506return super().spawn(cmd)507finally:508os.environ['path'] = old_path509510# -- Miscellaneous methods -----------------------------------------511# These are all used by the 'gen_lib_options() function, in512# ccompiler.py.513514def library_dir_option(self, dir):515return "/LIBPATH:" + dir516517def runtime_library_dir_option(self, dir):518raise DistutilsPlatformError(519"don't know how to set runtime library search path for MSVC")520521def library_option(self, lib):522return self.library_filename(lib)523524def find_library_file(self, dirs, lib, debug=0):525# Prefer a debugging library if found (and requested), but deal526# with it if we don't have one.527if debug:528try_names = [lib + "_d", lib]529else:530try_names = [lib]531for dir in dirs:532for name in try_names:533libfile = os.path.join(dir, self.library_filename(name))534if os.path.isfile(libfile):535return libfile536else:537# Oops, didn't find it in *any* of 'dirs'538return None539540541