Commits

Anthony Tuininga committed 496f225

Added support for copying the MSVC runtime (and manifest, if necessary) to the
target directory to avoid needing the redistributable package installed. Thanks
to Almar Klein for the initial patch.

  • Participants
  • Parent commits 3b6824d

Comments (0)

Files changed (5)

cx_Freeze/dist.py

          'comma-separated list of constants to include'),
         ('include-files=', 'f',
          'list of tuples of additional files to include in distribution'),
+        ('include-msvcr=', None,
+         'include the Microsoft Visual C runtime files'),
         ('zip-includes=', None,
          'list of tuples of additional files to include in zip file'),
         ('bin-includes', None,
     ]
     boolean_options = ["compressed", "copy_dependent_files",
             "create_shared_zip", "append_script_to_exe",
-            "include_in_shared_zip", "silent"]
+            "include_in_shared_zip", "include_msvcr", "silent"]
 
     def _normalize(self, attrName):
         value = getattr(self, attrName)
         self.create_shared_zip = None
         self.append_script_to_exe = None
         self.include_in_shared_zip = None
+        self.include_msvcr = None
         self.icon = None
         self.constants = []
         self.include_files = []
                 self.copy_dependent_files, self.init_script, self.base,
                 self.path, self.create_shared_zip, self.append_script_to_exe,
                 self.include_in_shared_zip, self.build_exe, icon = self.icon,
+                includeMSVCR = self.include_msvcr,
                 includeFiles = self.include_files,
                 binIncludes = self.bin_includes,
                 binExcludes = self.bin_excludes,

cx_Freeze/freezer.py

 """
 
 
+MSVCR_MANIFEST_TEMPLATE = """
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<noInheritable/>
+<assemblyIdentity
+    type="win32"
+    name="Microsoft.VC90.CRT"
+    version="9.0.21022.8"
+    processorArchitecture="{PROC_ARCH}"
+    publicKeyToken="1fc8b3b9a1e18e3b"/>
+</assembly>
+"""
+
+
 class Freezer(object):
 
     def __init__(self, executables, constantsModules = [], includes = [],
             targetDir = None, binIncludes = [], binExcludes = [],
             binPathIncludes = [], binPathExcludes = [], icon = None,
             includeFiles = [], zipIncludes = [], silent = False,
-            namespacePackages = [], metadata = None):
+            namespacePackages = [], metadata = None,
+            includeMSVCR = False):
         self.executables = list(executables)
         self.constantsModules = list(constantsModules)
         self.includes = list(includes)
         self.base = base
         self.path = path
         self.createLibraryZip = createLibraryZip
+        self.includeMSVCR = includeMSVCR
         self.appendScriptToExe = appendScriptToExe
         self.appendScriptToLibrary = appendScriptToLibrary
         self.targetDir = targetDir
                 product = self.metadata.name)
         stamp(fileName, versionInfo)
 
-    def _CopyFile(self, source, target, copyDependentFiles,
+    def _CopyFile(self, source, target, copyDependentFiles = False,
             includeMode = False):
         normalizedSource = os.path.normcase(os.path.normpath(source))
         normalizedTarget = os.path.normcase(os.path.normpath(target))
             scriptModule = finder.IncludeFile(exe.script, exe.moduleName)
         self._CopyFile(exe.base, exe.targetName, exe.copyDependentFiles,
                 includeMode = True)
+        if self.includeMSVCR:
+            self._IncludeMSVCR(exe)
         if exe.icon is not None:
             if sys.platform == "win32":
                 import cx_Freeze.util
             finder.IncludePackage(name)
         return finder
 
+    def _IncludeMSVCR(self, exe):
+        msvcRuntimeDll = None
+        targetDir = os.path.dirname(exe.targetName)
+        for fullName in self.filesCopied:
+            path, name = os.path.split(os.path.normcase(fullName))
+            if name.startswith("msvcr") and name.endswith(".dll"):
+                msvcRuntimeDll = name
+                for otherName in [name.replace("r", c) for c in "mp"]:
+                    sourceName = os.path.join(self.msvcRuntimeDir, otherName)
+                    if not os.path.exists(sourceName):
+                        continue
+                    targetName = os.path.join(targetDir, otherName)
+                    self._CopyFile(sourceName, targetName)
+                break
+        if msvcRuntimeDll is not None and msvcRuntimeDll == "msvcr90.dll":
+            arch = "x86" if struct.calcsize("P") == 4 else "amd64"
+            manifest = MSVCR_MANIFEST_TEMPLATE.strip().replace("{PROC_ARCH}",
+                    arch)
+            fileName = os.path.join(targetDir, "Microsoft.VC90.CRT.manifest")
+            sys.stdout.write("creating %s\n" % fileName)
+            open(fileName, "w").write(manifest)
+
     def _PrintReport(self, fileName, modules):
         sys.stdout.write("writing zip file %s\n\n" % fileName)
         sys.stdout.write("  %-25s %s\n" % ("Name", "File"))
            Files are included unless specifically excluded but inclusions take
            precedence over exclusions."""
 
+        # check for C runtime, if desired
+        path = os.path.normcase(path)
+        dirName, fileName = os.path.split(path)
+        if fileName.startswith("msvcr") and fileName.endswith(".dll"):
+            self.msvcRuntimeDir = dirName
+            return self.includeMSVCR
+
         # check the full path
-        path = os.path.normcase(path)
         if path in self.binIncludes:
             return True
         if path in self.binExcludes:
             return False
 
         # check the file name by itself (with any included version numbers)
-        dirName, fileName = os.path.split(path)
         if fileName in self.binIncludes:
             return True
         if fileName in self.binExcludes:
         self.dependentFiles = {}
         self.filesCopied = {}
         self.linkerWarnings = {}
+        self.msvcRuntimeDir = None
         import cx_Freeze.util
         cx_Freeze.util.SetOptimizeFlag(self.optimizeFlag)
         if self.createLibraryZip:

doc/distutils.rst

 |                       | and CVS directories); the target must not be an     |
 |                       | absolute path                                       |
 +-----------------------+-----------------------------------------------------+
+| include-msvcr         | include the Microsoft Visual C runtime DLLs and (if |
+|                       | necessary) the manifest file required to run the    |
+|                       | executable without needing the redistributable      |
+|                       | package installed                                   |
++-----------------------+-----------------------------------------------------+
 | zip-includes          | list containing files to be included in the zip file|
 |                       | directory; it is expected that this list will       |
 |                       | contain strings or 2-tuples for the source and      |

doc/releasenotes.rst

    support, please use cx_Freeze 4.2.3.
 
 1) Added support for the final release of Python 3.3.
-2) Clarified the documentation on the --replace-paths option. Thanks to Thomas
+2) Added support for copying the MSVC runtime DLLs and manifest if desired by
+   using the --include-msvcr switch. Thanks to Almar Klein for the initial
+   patch.
+3) Clarified the documentation on the --replace-paths option. Thanks to Thomas
    Kluyver for the patch.
 
 Bugs fixed:

samples/simple/setup.py

         name = "hello",
         version = "0.1",
         description = "Sample cx_Freeze script",
-        executables = [Executable("hello.py")])
+        executables = [Executable("hello.py")],
+        options = dict(build_exe = dict(include_msvcr = True)))