]> granicus.if.org Git - python/commitdiff
Issue #25027: Reverts partial-static build options and adds vcruntime140.dll to Windo...
authorSteve Dower <steve.dower@microsoft.com>
Wed, 9 Sep 2015 04:39:01 +0000 (21:39 -0700)
committerSteve Dower <steve.dower@microsoft.com>
Wed, 9 Sep 2015 04:39:01 +0000 (21:39 -0700)
13 files changed:
Lib/distutils/_msvccompiler.py
Lib/distutils/tests/test_msvccompiler.py
Misc/NEWS
PCbuild/pyproject.props
PCbuild/tcl.vcxproj
PCbuild/tix.vcxproj
PCbuild/tk.vcxproj
Tools/msi/build.bat
Tools/msi/buildrelease.bat
Tools/msi/exe/exe_files.wxs
Tools/msi/make_zip.proj
Tools/msi/make_zip.py
Tools/msi/msi.props

index b344616e600f01abbb7e45a4ddbf5edf0ab5164a..82b78a0ffe43630a9af983bcf6d02335ac45ecec 100644 (file)
@@ -14,6 +14,8 @@ for older versions in distutils.msvc9compiler and distutils.msvccompiler.
 # ported to VS 2015 by Steve Dower
 
 import os
+import shutil
+import stat
 import subprocess
 
 from distutils.errors import DistutilsExecError, DistutilsPlatformError, \
@@ -25,7 +27,7 @@ from distutils.util import get_platform
 import winreg
 from itertools import count
 
-def _find_vcvarsall():
+def _find_vcvarsall(plat_spec):
     with winreg.OpenKeyEx(
         winreg.HKEY_LOCAL_MACHINE,
         r"Software\Microsoft\VisualStudio\SxS\VC7",
@@ -33,7 +35,7 @@ def _find_vcvarsall():
     ) as key:
         if not key:
             log.debug("Visual C++ is not registered")
-            return None
+            return None, None
 
         best_version = 0
         best_dir = None
@@ -51,14 +53,23 @@ def _find_vcvarsall():
                     best_version, best_dir = version, vc_dir
         if not best_version:
             log.debug("No suitable Visual C++ version found")
-            return None
+            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
+            return None, None
 
-        return vcvarsall
+        vcruntime = None
+        vcruntime_spec = _VCVARS_PLAT_TO_VCRUNTIME_REDIST.get(plat_spec)
+        if vcruntime_spec:
+            vcruntime = os.path.join(best_dir,
+                vcruntime_spec.format(best_version))
+            if not os.path.isfile(vcruntime):
+                log.debug("%s cannot be found", vcruntime)
+                vcruntime = None
+
+        return vcvarsall, vcruntime
 
 def _get_vc_env(plat_spec):
     if os.getenv("DISTUTILS_USE_SDK"):
@@ -67,7 +78,7 @@ def _get_vc_env(plat_spec):
             for key, value in os.environ.items()
         }
 
-    vcvarsall = _find_vcvarsall()
+    vcvarsall, vcruntime = _find_vcvarsall(plat_spec)
     if not vcvarsall:
         raise DistutilsPlatformError("Unable to find vcvarsall.bat")
 
@@ -83,12 +94,16 @@ def _get_vc_env(plat_spec):
         raise DistutilsPlatformError("Error executing {}"
                 .format(exc.cmd))
 
-    return {
+    env = {
         key.lower(): value
         for key, _, value in
         (line.partition('=') for line in out.splitlines())
         if key and value
     }
+    
+    if vcruntime:
+        env['py_vcruntime_redist'] = vcruntime
+    return env
 
 def _find_exe(exe, paths=None):
     """Return path to an MSVC executable program.
@@ -115,6 +130,20 @@ PLAT_TO_VCVARS = {
     'win-amd64' : 'amd64',
 }
 
+# A map keyed by get_platform() return values to the file under
+# the VC install directory containing the vcruntime redistributable.
+_VCVARS_PLAT_TO_VCRUNTIME_REDIST = {
+    'x86' : 'redist\\x86\\Microsoft.VC{0}0.CRT\\vcruntime{0}0.dll',
+    'amd64' : 'redist\\x64\\Microsoft.VC{0}0.CRT\\vcruntime{0}0.dll',
+    'x86_amd64' : 'redist\\x64\\Microsoft.VC{0}0.CRT\\vcruntime{0}0.dll',
+}
+
+# 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."""
@@ -189,6 +218,7 @@ 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:
@@ -199,20 +229,26 @@ class MSVCCompiler(CCompiler) :
                 self.add_library_dir(dir)
 
         self.preprocess_options = None
-        # Use /MT[d] to build statically, then switch from libucrt[d].lib to ucrt[d].lib
+        # 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.
         self.compile_options = [
-            '/nologo', '/Ox', '/MT', '/W3', '/GL', '/DNDEBUG'
+            '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG'
         ]
+        self.compile_options.append('/MD' if self._vcruntime_redist else '/MT')
+        
         self.compile_options_debug = [
-            '/nologo', '/Od', '/MTd', '/Zi', '/W3', '/D_DEBUG'
+            '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG'
         ]
 
         ldflags = [
-            '/nologo', '/INCREMENTAL:NO', '/LTCG', '/nodefaultlib:libucrt.lib', 'ucrt.lib',
+            '/nologo', '/INCREMENTAL:NO', '/LTCG'
         ]
+        if not self._vcruntime_redist:
+            ldflags.extend(('/nodefaultlib:libucrt.lib', 'ucrt.lib'))
+
         ldflags_debug = [
-            '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL', '/nodefaultlib:libucrtd.lib', 'ucrtd.lib',
+            '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL'
         ]
 
         self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1']
@@ -446,15 +482,29 @@ class MSVCCompiler(CCompiler) :
             if extra_postargs:
                 ld_args.extend(extra_postargs)
 
-            self.mkpath(os.path.dirname(output_filename))
+            output_dir = os.path.dirname(os.path.abspath(output_filename))
+            self.mkpath(output_dir)
             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:
index 1f8890792e2a2e5fefe34008b6adeefffa0bc173..0b8a69fa5a404a4b8239ef78313d301b0d35a39a 100644 (file)
@@ -3,6 +3,8 @@ import sys
 import unittest
 import os
 
+import distutils._msvccompiler as _msvccompiler
+
 from distutils.errors import DistutilsPlatformError
 from distutils.tests import support
 from test.support import run_unittest
@@ -19,19 +21,65 @@ class msvccompilerTestCase(support.TempdirManager,
         # makes sure query_vcvarsall raises
         # a DistutilsPlatformError if the compiler
         # is not found
-        from distutils._msvccompiler import _get_vc_env
-        def _find_vcvarsall():
-            return None
+        def _find_vcvarsall(plat_spec):
+            return None, None
 
-        import distutils._msvccompiler as _msvccompiler
         old_find_vcvarsall = _msvccompiler._find_vcvarsall
         _msvccompiler._find_vcvarsall = _find_vcvarsall
         try:
-            self.assertRaises(DistutilsPlatformError, _get_vc_env,
+            self.assertRaises(DistutilsPlatformError,
+                              _msvccompiler._get_vc_env,
                              'wont find this version')
         finally:
             _msvccompiler._find_vcvarsall = old_find_vcvarsall
 
+    def test_compiler_options(self):
+        # 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):
+        # 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):
+        tempdir = self.mkdtemp()
+        compiler = _msvccompiler.MSVCCompiler()
+        compiler.initialize()
+        dll = compiler._vcruntime_redist
+        self.assertTrue(os.path.isfile(dll))
+        
+        compiler._copy_vcruntime(tempdir)
+
+        self.assertFalse(os.path.isfile(os.path.join(
+            tempdir, os.path.basename(dll))))
+
 def test_suite():
     return unittest.makeSuite(msvccompilerTestCase)
 
index d5d658647fbe6bf682517508ab0444950642e9d9..5cfa2d60cac0805ca4a52fdef8b2a156469cd511 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,13 @@ Library
 
 - Issue #25029: Fixes MemoryError in test_strptime
 
+Build
+-----
+
+- Issue #25027: Reverts partial-static build options and adds
+  vcruntime140.dll to Windows installation.
+
+
 What's New in Python 3.5.0 release candidate 3?
 ===============================================
 
index f63c30dd13787779edcb99023eaa667505bbf8e4..c1303e1aacbde24ef351b8f2c8959be460780aa7 100644 (file)
@@ -36,7 +36,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <StringPooling>true</StringPooling>
       <ExceptionHandling></ExceptionHandling>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <WarningLevel>Level3</WarningLevel>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
@@ -47,7 +47,7 @@
     <ClCompile Condition="$(Configuration) == 'Debug'">
       <Optimization>Disabled</Optimization>
       <WholeProgramOptimization>false</WholeProgramOptimization>
-      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
     </ClCompile>
     <Link>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
@@ -57,9 +57,7 @@
       <RandomizedBaseAddress>true</RandomizedBaseAddress>
       <DataExecutionPrevention>true</DataExecutionPrevention>
       <SuppressStartupBanner>true</SuppressStartupBanner>
-      <AdditionalDependencies Condition="$(Configuration) == 'Debug'">ucrtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalDependencies Condition="$(Configuration) != 'Debug'">ucrt.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <IgnoreSpecificDefaultLibraries>LIBC;libucrt.lib;libucrtd.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
+      <IgnoreSpecificDefaultLibraries>LIBC;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <TargetMachine>MachineX86</TargetMachine>
       <TargetMachine Condition="'$(Platform)' == 'x64'">MachineX64</TargetMachine>
       <ProfileGuidedDatabase Condition="$(SupportPGO)">$(OutDir)$(TargetName).pgd</ProfileGuidedDatabase>
index e9287c7b37d0a8f3c1b7571a496ad6da054380ea..464c83ce80cf9dd1e3a444fdaa345585d0c8bff9 100644 (file)
@@ -61,8 +61,8 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   
   <PropertyGroup>
-    <TclOpts>ucrt</TclOpts>
-    <TclOpts Condition="$(Configuration) == 'Debug'">symbols,ucrt</TclOpts>
+    <TclOpts>msvcrt</TclOpts>
+    <TclOpts Condition="$(Configuration) == 'Debug'">symbols,msvcrt</TclOpts>
     <TclDirs>INSTALLDIR="$(OutDir.TrimEnd(`\`))" INSTALL_DIR="$(OutDir.TrimEnd(`\`))"</TclDirs>
     <DebugFlags Condition="'$(Configuration)' == 'Debug'">DEBUGFLAGS="-wd4456 -wd4457 -wd4458 -wd4459 -wd4996"</DebugFlags>
     <NMakeBuildCommandLine>setlocal
index a1dad1e301e988111951c5e3e02c7a232771494d..f857f9e67f1735602f49dbe2e54b2b1a3edae256 100644 (file)
@@ -57,8 +57,8 @@
   
   <PropertyGroup>
     <TixDirs>BUILDDIRTOP="$(BuildDirTop)" TCL_DIR="$(tclDir.TrimEnd(`\`))" TK_DIR="$(tkDir.TrimEnd(`\`))" INSTALL_DIR="$(OutDir.TrimEnd(`\`))"</TixDirs>
-    <DebugFlags Condition="'$(Configuration)' == 'Debug'">DEBUG=1 NODEBUG=0 UCRT=1 TCL_DBGX=g TK_DBGX=g</DebugFlags>
-    <DebugFlags Condition="'$(Configuration)' != 'Debug'">DEBUG=0 NODEBUG=1 UCRT=1</DebugFlags>
+    <DebugFlags Condition="'$(Configuration)' == 'Debug'">DEBUG=1 NODEBUG=0 TCL_DBGX=g TK_DBGX=g</DebugFlags>
+    <DebugFlags Condition="'$(Configuration)' != 'Debug'">DEBUG=0 NODEBUG=1</DebugFlags>
     <NMakeBuildCommandLine>setlocal
 @(ExpectedOutputs->'if not exist "%(FullPath)" goto build','
 ')
index 589338cf5cf30fdb75efc2918fafa3b033909ca7..20749f719f59450087da517b4a9c075f215e92fd 100644 (file)
@@ -60,8 +60,8 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   
   <PropertyGroup>
-    <TkOpts>ucrt</TkOpts>
-    <TkOpts Condition="$(Configuration) == 'Debug'">symbols,ucrt</TkOpts>
+    <TkOpts>msvcrt</TkOpts>
+    <TkOpts Condition="$(Configuration) == 'Debug'">symbols,msvcrt</TkOpts>
     <TkDirs>TCLDIR="$(tclDir.TrimEnd(`\`))" INSTALLDIR="$(OutDir.TrimEnd(`\`))"</TkDirs>
     <DebugFlags Condition="'$(Configuration)' == 'Debug'">DEBUGFLAGS="-wd4456 -wd4457 -wd4458 -wd4459 -wd4996"</DebugFlags>
     <NMakeBuildCommandLine>setlocal
index 5ae512fbbe4b2f5b11209f7b17d4b06c82955ee3..008c93762c60094bff211dd1c45747da6055ef1b 100644 (file)
@@ -20,15 +20,15 @@ if not defined BUILDX86 if not defined BUILDX64 (set BUILDX86=1) && (set BUILDX6
 call "%PCBUILD%env.bat" x86\r
 \r
 if defined BUILDX86 (\r
-    call "%PCBUILD%build.bat" -d\r
+    call "%PCBUILD%build.bat" -d -e\r
     if errorlevel 1 goto :eof\r
-    call "%PCBUILD%build.bat"\r
+    call "%PCBUILD%build.bat" -e\r
     if errorlevel 1 goto :eof\r
 )\r
 if defined BUILDX64 (\r
-    call "%PCBUILD%build.bat" -p x64 -d\r
+    call "%PCBUILD%build.bat" -p x64 -d -e\r
     if errorlevel 1 goto :eof\r
-    call "%PCBUILD%build.bat" -p x64\r
+    call "%PCBUILD%build.bat" -p x64 -e\r
     if errorlevel 1 goto :eof\r
 )\r
 \r
index 2e73f8fe7cf41f1a0280f22fa58e0261d00d3313..d22ba1013fbef552a1440039b90fa9eaaeb68dcd 100644 (file)
@@ -121,7 +121,7 @@ if not "%CERTNAME%" EQU "" (
 if not "%SKIPBUILD%" EQU "1" (\r
     call "%PCBUILD%build.bat" -e -p %BUILD_PLAT% -d -t %TARGET% %CERTOPTS%\r
     if errorlevel 1 exit /B\r
-    call "%PCBUILD%build.bat" -p %BUILD_PLAT% -t %TARGET% %CERTOPTS%\r
+    call "%PCBUILD%build.bat" -e -p %BUILD_PLAT% -t %TARGET% %CERTOPTS%\r
     if errorlevel 1 exit /B\r
     @rem build.bat turns echo back on, so we disable it again\r
     @echo off\r
index 3b5fce446c6b55691f2e5efda728addb1fcbde0f..9e47b5d9809d0ad052f134af74e44ea26a869870 100644 (file)
@@ -29,6 +29,9 @@
             <Component Id="pythonw.exe" Directory="InstallDirectory" Guid="$(var.PythonwExeComponentGuid)">
                 <File Name="pythonw.exe" KeyPath="yes" />
             </Component>
+            <Component Id="vcruntime140.dll" Directory="InstallDirectory" Guid="*">
+                <File Name="vcruntime140.dll" Source="!(bindpath.redist)vcruntime140.dll" KeyPath="yes" />
+            </Component>
         </ComponentGroup>
     </Fragment>
 
index 2f16032086c413547c7d7079acf0d1b4359e7e98..766fe8597c0ba84d96fbd1f143cec93ffc3d2b72 100644 (file)
@@ -16,7 +16,8 @@
         <TargetPath>$(OutputPath)\en-us\$(TargetName)$(TargetExt)</TargetPath>
         <Arguments>"$(PythonExe)" "$(MSBuildThisFileDirectory)\make_zip.py"</Arguments>
         <Arguments>$(Arguments) -e -o "$(TargetPath)" -t "$(IntermediateOutputPath)\zip_$(ArchName)" -a $(ArchName)</Arguments>
-        <Environment>set DOC_FILENAME=python$(PythonVersion).chm</Environment>
+        <Environment>set DOC_FILENAME=python$(PythonVersion).chm
+set VCREDIST_PATH=$(VS140COMNTOOLS)\..\..\VC\redist\$(Platform)\Microsoft.VC140.CRT</Environment>
     </PropertyGroup>
 
     <Target Name="_Build">
index ba711c484a9b58e3c847f05d4dd9a9566e1cb4eb..09ee49166d98926a65547b487bc875c351c4c370 100644 (file)
@@ -64,9 +64,6 @@ FULL_LAYOUT = [
     ('Tools/', 'Tools', '**/*', include_in_tools),
 ]
 
-if os.getenv('DOC_FILENAME'):
-    FULL_LAYOUT.append(('Doc/', 'Doc/build/htmlhelp', os.getenv('DOC_FILENAME'), None))
-
 EMBED_LAYOUT = [
     ('/', 'PCBuild/$arch', 'python*.exe', is_not_debug),
     ('/', 'PCBuild/$arch', '*.pyd', is_not_debug),
@@ -74,6 +71,12 @@ EMBED_LAYOUT = [
     ('python35.zip', 'Lib', '**/*', include_in_lib),
 ]
 
+if os.getenv('DOC_FILENAME'):
+    FULL_LAYOUT.append(('Doc/', 'Doc/build/htmlhelp', os.getenv('DOC_FILENAME'), None))
+if os.getenv('VCREDIST_PATH'):
+    FULL_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None))
+    EMBED_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None))
+
 def copy_to_layout(target, rel_sources):
     count = 0
 
index 29be12932801fc432bab38c82e89c96713fb2bde..cc52c7ae5cade05fc7a9fabcc18309194c7c8b18 100644 (file)
         <LinkerBindInputPaths Include="$(CRTRedist)" Condition="'$(CRTRedist)' != ''">
             <BindName>redist</BindName>
         </LinkerBindInputPaths>
+        <LinkerBindInputPaths Include="$(VS140COMNTOOLS)\..\..\VC\redist\$(Platform)\Microsoft.VC140.CRT">
+            <BindName>redist</BindName>
+        </LinkerBindInputPaths>
     </ItemGroup>
 
     <Target Name="_ValidateMsiProps" BeforeTargets="PrepareForBuild">