bpo-38597: Never statically link extension initialization code on Windows (GH-18724)
(cherry picked from commit ce3a498408
)
Co-authored-by: Steve Dower <steve.dower@python.org>
This commit is contained in:
parent
9ddcb914f9
commit
0d20364b13
|
@ -97,28 +97,11 @@ PLAT_SPEC_TO_RUNTIME = {
|
|||
}
|
||||
|
||||
def _find_vcvarsall(plat_spec):
|
||||
# bpo-38597: Removed vcruntime return value
|
||||
_, best_dir = _find_vc2017()
|
||||
vcruntime = None
|
||||
|
||||
if plat_spec in PLAT_SPEC_TO_RUNTIME:
|
||||
vcruntime_plat = PLAT_SPEC_TO_RUNTIME[plat_spec]
|
||||
else:
|
||||
vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86'
|
||||
|
||||
if best_dir:
|
||||
vcredist = os.path.join(best_dir, "..", "..", "redist", "MSVC", "**",
|
||||
vcruntime_plat, "Microsoft.VC14*.CRT", "vcruntime140.dll")
|
||||
try:
|
||||
import glob
|
||||
vcruntime = glob.glob(vcredist, recursive=True)[-1]
|
||||
except (ImportError, OSError, LookupError):
|
||||
vcruntime = None
|
||||
|
||||
if not best_dir:
|
||||
best_version, best_dir = _find_vc2015()
|
||||
if best_version:
|
||||
vcruntime = os.path.join(best_dir, 'redist', vcruntime_plat,
|
||||
"Microsoft.VC140.CRT", "vcruntime140.dll")
|
||||
|
||||
if not best_dir:
|
||||
log.debug("No suitable Visual C++ version found")
|
||||
|
@ -129,11 +112,7 @@ def _find_vcvarsall(plat_spec):
|
|||
log.debug("%s cannot be found", vcvarsall)
|
||||
return None, None
|
||||
|
||||
if not vcruntime or not os.path.isfile(vcruntime):
|
||||
log.debug("%s cannot be found", vcruntime)
|
||||
vcruntime = None
|
||||
|
||||
return vcvarsall, vcruntime
|
||||
return vcvarsall, None
|
||||
|
||||
def _get_vc_env(plat_spec):
|
||||
if os.getenv("DISTUTILS_USE_SDK"):
|
||||
|
@ -142,7 +121,7 @@ def _get_vc_env(plat_spec):
|
|||
for key, value in os.environ.items()
|
||||
}
|
||||
|
||||
vcvarsall, vcruntime = _find_vcvarsall(plat_spec)
|
||||
vcvarsall, _ = _find_vcvarsall(plat_spec)
|
||||
if not vcvarsall:
|
||||
raise DistutilsPlatformError("Unable to find vcvarsall.bat")
|
||||
|
||||
|
@ -163,8 +142,6 @@ def _get_vc_env(plat_spec):
|
|||
if key and value
|
||||
}
|
||||
|
||||
if vcruntime:
|
||||
env['py_vcruntime_redist'] = vcruntime
|
||||
return env
|
||||
|
||||
def _find_exe(exe, paths=None):
|
||||
|
@ -194,12 +171,6 @@ PLAT_TO_VCVARS = {
|
|||
'win-arm64' : 'x86_arm64'
|
||||
}
|
||||
|
||||
# A set containing the DLLs that are guaranteed to be available for
|
||||
# all micro versions of this Python version. Known extension
|
||||
# dependencies that are not in this set will be copied to the output
|
||||
# path.
|
||||
_BUNDLED_DLLS = frozenset(['vcruntime140.dll'])
|
||||
|
||||
class MSVCCompiler(CCompiler) :
|
||||
"""Concrete class that implements an interface to Microsoft Visual C++,
|
||||
as defined by the CCompiler abstract class."""
|
||||
|
@ -263,7 +234,6 @@ class MSVCCompiler(CCompiler) :
|
|||
self.rc = _find_exe("rc.exe", paths) # resource compiler
|
||||
self.mc = _find_exe("mc.exe", paths) # message compiler
|
||||
self.mt = _find_exe("mt.exe", paths) # message compiler
|
||||
self._vcruntime_redist = vc_env.get('py_vcruntime_redist', '')
|
||||
|
||||
for dir in vc_env.get('include', '').split(os.pathsep):
|
||||
if dir:
|
||||
|
@ -274,13 +244,12 @@ class MSVCCompiler(CCompiler) :
|
|||
self.add_library_dir(dir.rstrip(os.sep))
|
||||
|
||||
self.preprocess_options = None
|
||||
# If vcruntime_redist is available, link against it dynamically. Otherwise,
|
||||
# use /MT[d] to build statically, then switch from libucrt[d].lib to ucrt[d].lib
|
||||
# later to dynamically link to ucrtbase but not vcruntime.
|
||||
# bpo-38597: Always compile with dynamic linking
|
||||
# Future releases of Python 3.x will include all past
|
||||
# versions of vcruntime*.dll for compatibility.
|
||||
self.compile_options = [
|
||||
'/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG'
|
||||
'/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG', '/MD'
|
||||
]
|
||||
self.compile_options.append('/MD' if self._vcruntime_redist else '/MT')
|
||||
|
||||
self.compile_options_debug = [
|
||||
'/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG'
|
||||
|
@ -289,8 +258,6 @@ class MSVCCompiler(CCompiler) :
|
|||
ldflags = [
|
||||
'/nologo', '/INCREMENTAL:NO', '/LTCG'
|
||||
]
|
||||
if not self._vcruntime_redist:
|
||||
ldflags.extend(('/nodefaultlib:libucrt.lib', 'ucrt.lib'))
|
||||
|
||||
ldflags_debug = [
|
||||
'/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL'
|
||||
|
@ -532,24 +499,11 @@ class MSVCCompiler(CCompiler) :
|
|||
try:
|
||||
log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args))
|
||||
self.spawn([self.linker] + ld_args)
|
||||
self._copy_vcruntime(output_dir)
|
||||
except DistutilsExecError as msg:
|
||||
raise LinkError(msg)
|
||||
else:
|
||||
log.debug("skipping %s (up-to-date)", output_filename)
|
||||
|
||||
def _copy_vcruntime(self, output_dir):
|
||||
vcruntime = self._vcruntime_redist
|
||||
if not vcruntime or not os.path.isfile(vcruntime):
|
||||
return
|
||||
|
||||
if os.path.basename(vcruntime).lower() in _BUNDLED_DLLS:
|
||||
return
|
||||
|
||||
log.debug('Copying "%s"', vcruntime)
|
||||
vcruntime = shutil.copy(vcruntime, output_dir)
|
||||
os.chmod(vcruntime, stat.S_IWRITE)
|
||||
|
||||
def spawn(self, cmd):
|
||||
old_path = os.getenv('path')
|
||||
try:
|
||||
|
|
|
@ -32,57 +32,6 @@ class msvccompilerTestCase(support.TempdirManager,
|
|||
finally:
|
||||
_msvccompiler._find_vcvarsall = old_find_vcvarsall
|
||||
|
||||
def test_compiler_options(self):
|
||||
import distutils._msvccompiler as _msvccompiler
|
||||
# suppress path to vcruntime from _find_vcvarsall to
|
||||
# check that /MT is added to compile options
|
||||
old_find_vcvarsall = _msvccompiler._find_vcvarsall
|
||||
def _find_vcvarsall(plat_spec):
|
||||
return old_find_vcvarsall(plat_spec)[0], None
|
||||
_msvccompiler._find_vcvarsall = _find_vcvarsall
|
||||
try:
|
||||
compiler = _msvccompiler.MSVCCompiler()
|
||||
compiler.initialize()
|
||||
|
||||
self.assertIn('/MT', compiler.compile_options)
|
||||
self.assertNotIn('/MD', compiler.compile_options)
|
||||
finally:
|
||||
_msvccompiler._find_vcvarsall = old_find_vcvarsall
|
||||
|
||||
def test_vcruntime_copy(self):
|
||||
import distutils._msvccompiler as _msvccompiler
|
||||
# force path to a known file - it doesn't matter
|
||||
# what we copy as long as its name is not in
|
||||
# _msvccompiler._BUNDLED_DLLS
|
||||
old_find_vcvarsall = _msvccompiler._find_vcvarsall
|
||||
def _find_vcvarsall(plat_spec):
|
||||
return old_find_vcvarsall(plat_spec)[0], __file__
|
||||
_msvccompiler._find_vcvarsall = _find_vcvarsall
|
||||
try:
|
||||
tempdir = self.mkdtemp()
|
||||
compiler = _msvccompiler.MSVCCompiler()
|
||||
compiler.initialize()
|
||||
compiler._copy_vcruntime(tempdir)
|
||||
|
||||
self.assertTrue(os.path.isfile(os.path.join(
|
||||
tempdir, os.path.basename(__file__))))
|
||||
finally:
|
||||
_msvccompiler._find_vcvarsall = old_find_vcvarsall
|
||||
|
||||
def test_vcruntime_skip_copy(self):
|
||||
import distutils._msvccompiler as _msvccompiler
|
||||
|
||||
tempdir = self.mkdtemp()
|
||||
compiler = _msvccompiler.MSVCCompiler()
|
||||
compiler.initialize()
|
||||
dll = compiler._vcruntime_redist
|
||||
self.assertTrue(os.path.isfile(dll), dll or "<None>")
|
||||
|
||||
compiler._copy_vcruntime(tempdir)
|
||||
|
||||
self.assertFalse(os.path.isfile(os.path.join(
|
||||
tempdir, os.path.basename(dll))), dll or "<None>")
|
||||
|
||||
def test_get_vc_env_unicode(self):
|
||||
import distutils._msvccompiler as _msvccompiler
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
:mod:`distutils` will no longer statically link :file:`vcruntime140.dll`
|
||||
when a redistributable version is unavailable. All future releases of
|
||||
CPython will include a copy of this DLL to ensure distributed extensions can
|
||||
continue to load.
|
|
@ -528,6 +528,8 @@
|
|||
<VCRuntimeDLL Include="$(VCRedistDir)\**\vcruntime*.dll" />
|
||||
</ItemGroup>
|
||||
<Target Name="_CopyVCRuntime" AfterTargets="Build" Inputs="@(VCRuntimeDLL)" Outputs="$(OutDir)%(Filename)%(Extension)">
|
||||
<!-- bpo-38597: When we switch to another VCRuntime DLL, include vcruntime140.dll as well -->
|
||||
<Warning Text="A copy of vcruntime140.dll is also required" Condition="!$(VCToolsRedistVersion.StartsWith(`14.`))" />
|
||||
<Copy SourceFiles="%(VCRuntimeDLL.FullPath)" DestinationFolder="$(OutDir)" />
|
||||
</Target>
|
||||
<Target Name="_CleanVCRuntime" AfterTargets="Clean">
|
||||
|
|
Loading…
Reference in New Issue