Source code for guidata.disthelpers

# -*- coding: utf-8 -*-
#
# Copyright © 2009-2011 CEA
# Pierre Raybaut
# Licensed under the terms of the CECILL License
# (see guidata/__init__.py for details)

# pylint: disable=W0613

"""
disthelpers
-----------

The ``guidata.disthelpers`` module provides helper functions for Python
package distribution on Microsoft Windows platforms with ``py2exe`` or on
all platforms thanks to ``cx_Freeze``.
"""

import sys
import os
import os.path as osp
import shutil
import traceback
import atexit
import imp
from subprocess import Popen, PIPE
import warnings

# Local imports
from guidata.configtools import get_module_path


# ==============================================================================
# Dependency management
# ==============================================================================
[docs]def get_changeset(path, rev=None): """Return Mercurial repository *path* revision number""" args = ["hg", "parent"] if rev is not None: args += ["--rev", str(rev)] process = Popen(args, stdout=PIPE, stderr=PIPE, cwd=path, shell=True) try: return process.stdout.read().splitlines()[0].split()[1] except IndexError: raise RuntimeError(process.stderr.read())
[docs]def prepend_module_to_path(module_path): """ Prepend to sys.path module located in *module_path* Return string with module infos: name, revision, changeset Use this function: 1) In your application to import local frozen copies of internal libraries 2) In your py2exe distributed package to add a text file containing the returned string """ if not osp.isdir(module_path): # Assuming py2exe distribution return sys.path.insert(0, osp.abspath(module_path)) changeset = get_changeset(module_path) name = osp.basename(module_path) prefix = "Prepending module to sys.path" message = prefix + ("%s [revision %s]" % (name, changeset)).rjust( 80 - len(prefix), "." ) print(message, file=sys.stderr) if name in sys.modules: sys.modules.pop(name) nbsp = 0 for modname in sys.modules.keys(): if modname.startswith(name + "."): sys.modules.pop(modname) nbsp += 1 warning = "(removed %s from sys.modules" % name if nbsp: warning += " and %d subpackages" % nbsp warning += ")" print(warning.rjust(80), file=sys.stderr) return message
[docs]def prepend_modules_to_path(module_base_path): """Prepend to sys.path all modules located in *module_base_path*""" if not osp.isdir(module_base_path): # Assuming py2exe distribution return fnames = [osp.join(module_base_path, name) for name in os.listdir(module_base_path)] messages = [ prepend_module_to_path(dirname) for dirname in fnames if osp.isdir(dirname) ] return os.linesep.join(messages)
# ============================================================================== # Distribution helpers # ============================================================================== def _remove_later(fname): """Try to remove file later (at exit)""" def try_to_remove(fname): if osp.exists(fname): os.remove(fname) atexit.register(try_to_remove, osp.abspath(fname))
[docs]def get_msvc_version(python_version): """Return Microsoft Visual C++ version used to build this Python version""" if python_version is None: python_version = "%s.%s" % (sys.version_info.major, sys.version_info.minor) warnings.warn("Assuming Python %s target" % python_version) if python_version in ("2.6", "2.7", "3.0", "3.1", "3.2"): # Python 2.6-2.7, 3.0-3.2 were built with Visual Studio 9.0.21022.8 # (i.e. Visual C++ 2008, not Visual C++ 2008 SP1!) return "9.0.21022.8" elif python_version in ("3.3", "3.4"): # Python 3.3+ were built with Visual Studio 10.0.30319.1 # (i.e. Visual C++ 2010) return "10.0" else: raise RuntimeError("Unsupported Python version %s" % python_version)
[docs]def get_dll_architecture(path): """Return DLL architecture (32 or 64bit) using Microsoft dumpbin.exe""" os.environ[ "PATH" ] += r";C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\;C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\BIN;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\;C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\BIN" process = Popen( ["dumpbin", "/HEADERS", osp.basename(path)], stdout=PIPE, stderr=PIPE, cwd=osp.dirname(path), shell=True, ) output = process.stdout.read() error = process.stderr.read() if error: raise RuntimeError(error) elif "x86" in output: return 32 elif "x64" in output: return 64 else: raise ValueError("Unable to get DLL architecture")
[docs]def get_msvc_dlls(msvc_version, architecture=None, check_architecture=False): """Get the list of Microsoft Visual C++ DLLs associated to architecture and Python version, create the manifest file. architecture: integer (32 or 64) -- if None, take the Python build arch python_version: X.Y""" current_architecture = 64 if sys.maxsize > 2 ** 32 else 32 if architecture is None: architecture = current_architecture assert architecture in (32, 64) filelist = [] msvc_major = msvc_version.split(".")[0] msvc_minor = msvc_version.split(".")[1] if msvc_major == "9": key = "1fc8b3b9a1e18e3b" atype = "" if architecture == 64 else "win32" arch = "amd64" if architecture == 64 else "x86" groups = { "CRT": ("msvcr90.dll", "msvcp90.dll", "msvcm90.dll"), # 'OPENMP': ('vcomp90.dll',) } for group, dll_list in groups.items(): dlls = "" for dll in dll_list: dlls += ' <file name="%s" />%s' % (dll, os.linesep) manifest = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!-- Copyright (c) Microsoft Corporation. All rights reserved. --> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <noInheritable/> <assemblyIdentity type="%(atype)s" name="Microsoft.VC90.%(group)s" version="%(version)s" processorArchitecture="%(arch)s" publicKeyToken="%(key)s" /> %(dlls)s</assembly> """ % dict( version=msvc_version, key=key, atype=atype, arch=arch, group=group, dlls=dlls, ) vc90man = "Microsoft.VC90.%s.manifest" % group open(vc90man, "w").write(manifest) _remove_later(vc90man) filelist += [vc90man] winsxs = osp.join(os.environ["windir"], "WinSxS") vcstr = "%s_Microsoft.VC90.%s_%s_%s" % (arch, group, key, msvc_version) for fname in os.listdir(winsxs): path = osp.join(winsxs, fname) if osp.isdir(path) and fname.lower().startswith(vcstr.lower()): for dllname in os.listdir(path): filelist.append(osp.join(path, dllname)) break else: raise RuntimeError( "Microsoft Visual C++ %s DLLs version %s " "were not found" % (group, msvc_version) ) elif msvc_major == "10": namelist = [ name % (msvc_major + msvc_minor) for name in ( "msvcp%s.dll", "msvcr%s.dll", "vcomp%s.dll", ) ] windir = os.environ["windir"] is_64bit_windows = osp.isdir(osp.join(windir, "SysWOW64")) # Reminder: WoW64 (*W*indows 32-bit *o*n *W*indows *64*-bit) is a # subsystem of the Windows operating system capable of running 32-bit # applications and is included on all 64-bit versions of Windows # (source: http://en.wikipedia.org/wiki/WoW64) # # In other words, "SysWOW64" contains 32-bit DLL and applications, # whereas "System32" contains 64-bit DLL and applications on a 64-bit # system. if architecture == 64: # 64-bit DLLs are located in... if is_64bit_windows: sysdir = "System32" # on a 64-bit OS else: # ...no directory to be found! raise RuntimeError("Can't find 64-bit DLLs on a 32-bit OS") else: # 32-bit DLLs are located in... if is_64bit_windows: sysdir = "SysWOW64" # on a 64-bit OS else: sysdir = "System32" # on a 32-bit OS for dllname in namelist: fname = osp.join(windir, sysdir, dllname) if osp.exists(fname): filelist.append(fname) else: raise RuntimeError( "Microsoft Visual C++ DLLs version %s " "were not found" % msvc_version ) else: raise RuntimeError("Unsupported MSVC version %s" % msvc_version) if check_architecture: for path in filelist: if path.endswith(".dll"): try: arch = get_dll_architecture(path) except RuntimeError: return if arch != architecture: raise RuntimeError( "%s: expecting %dbit, found %dbit" % (path, architecture, arch) ) return filelist
[docs]def create_msvc_data_files(architecture=None, python_version=None, verbose=False): """Including Microsoft Visual C++ DLLs""" msvc_version = get_msvc_version(python_version) filelist = get_msvc_dlls(msvc_version, architecture=architecture) print(create_msvc_data_files.__doc__) if verbose: for name in filelist: print(" ", name) msvc_major = msvc_version.split(".")[0] if msvc_major == "9": return [ ("Microsoft.VC90.CRT", filelist), ] else: return [ ("", filelist), ]
[docs]def to_include_files(data_files): """Convert data_files list to include_files list data_files: * this is the ``py2exe`` data files format * list of tuples (dest_dirname, (src_fname1, src_fname2, ...)) include_files: * this is the ``cx_Freeze`` data files format * list of tuples ((src_fname1, dst_fname1), (src_fname2, dst_fname2), ...)) """ include_files = [] for dest_dir, fnames in data_files: for source_fname in fnames: dest_fname = osp.join(dest_dir, osp.basename(source_fname)) include_files.append((source_fname, dest_fname)) return include_files
[docs]def strip_version(version): """Return version number with digits only (Windows does not support strings in version numbers)""" return version.split("beta")[0].split("alpha")[0].split("rc")[0].split("dev")[0]
[docs]def remove_dir(dirname): """Remove directory *dirname* and all its contents Print details about the operation (progress, success/failure)""" print("Removing directory '%s'..." % dirname, end=" ") try: shutil.rmtree(dirname, ignore_errors=True) print("OK") except Exception: print("Failed!") traceback.print_exc()
[docs]class Distribution(object): """Distribution object Help creating an executable using ``py2exe`` or ``cx_Freeze`` """ DEFAULT_EXCLUDES = [ "Tkconstants", "Tkinter", "tcl", "tk", "wx", "_imagingtk", "curses", "PIL._imagingtk", "ImageTk", "PIL.ImageTk", "FixTk", "bsddb", "email", "pywin.debugger", "pywin.debugger.dbgcon", "matplotlib", ] if sys.version_info.major == 2: # Fixes compatibility issue with IPython (more specifically with one # of its dependencies: `jsonschema`) on Python 2.7 DEFAULT_EXCLUDES += ["collections.abc"] DEFAULT_INCLUDES = [] DEFAULT_BIN_EXCLUDES = [ "MSVCP100.dll", "MSVCP90.dll", "w9xpopen.exe", "MSVCP80.dll", "MSVCR80.dll", ] DEFAULT_BIN_INCLUDES = [] DEFAULT_BIN_PATH_INCLUDES = [] DEFAULT_BIN_PATH_EXCLUDES = [] def __init__(self): self.name = None self.version = None self.description = None self.script = None self.target_name = None self._target_dir = None self.icon = None self.data_files = [] self.includes = self.DEFAULT_INCLUDES self.excludes = self.DEFAULT_EXCLUDES self.bin_includes = self.DEFAULT_BIN_INCLUDES self.bin_excludes = self.DEFAULT_BIN_EXCLUDES self.bin_path_includes = self.DEFAULT_BIN_PATH_INCLUDES self.bin_path_excludes = self.DEFAULT_BIN_PATH_EXCLUDES self.msvc = os.name == "nt" self._py2exe_is_loaded = False self._pyqt_added = False self._pyside_added = False # Attributes relative to cx_Freeze: self.executables = [] @property def target_dir(self): """Return target directory (default: 'dist')""" dirname = self._target_dir if dirname is None: return "dist" else: return dirname @target_dir.setter # analysis:ignore def target_dir(self, value): self._target_dir = value
[docs] def setup( self, name, version, description, script, target_name=None, target_dir=None, icon=None, data_files=None, includes=None, excludes=None, bin_includes=None, bin_excludes=None, bin_path_includes=None, bin_path_excludes=None, msvc=None, ): """Setup distribution object Notes: * bin_path_excludes is specific to cx_Freeze (ignored if it's None) * if msvc is None, it's set to True by default on Windows platforms, False on non-Windows platforms """ self.name = name self.version = strip_version(version) if os.name == "nt" else version self.description = description assert osp.isfile(script) self.script = script self.target_name = target_name self.target_dir = target_dir self.icon = icon if data_files is not None: self.data_files += data_files if includes is not None: self.includes += includes if excludes is not None: self.excludes += excludes if bin_includes is not None: self.bin_includes += bin_includes if bin_excludes is not None: self.bin_excludes += bin_excludes if bin_path_includes is not None: self.bin_path_includes += bin_path_includes if bin_path_excludes is not None: self.bin_path_excludes += bin_path_excludes if msvc is not None: self.msvc = msvc if self.msvc: try: self.data_files += create_msvc_data_files() except IOError: print( "Setting the msvc option to False " "will avoid this error", file=sys.stderr, ) raise # cx_Freeze: self.add_executable(self.script, self.target_name, icon=self.icon)
[docs] def add_text_data_file(self, filename, contents): """Create temporary data file *filename* with *contents* and add it to *data_files*""" open(filename, "wb").write(bytes(contents, "utf-8")) self.data_files += [("", (filename,))] _remove_later(filename)
def add_data_file(self, filename, destdir=""): self.data_files += [(destdir, (filename,))] # ------ Adding packages
[docs] def add_pyqt(self): """Include module PyQt5 to the distribution""" # TODO: Add PyQt6 support if self._pyqt_added: return self._pyqt_added = True import PyQt5 as PyQt qtver = 5 self.includes += [ "sip", "PyQt%d.Qt" % qtver, "PyQt%d.QtSvg" % qtver, "PyQt%d.QtNetwork" % qtver, ] pyqt_path = osp.dirname(PyQt.__file__) # Configuring PyQt conf = os.linesep.join(["[Paths]", "Prefix = .", "Binaries = ."]) self.add_text_data_file("qt.conf", conf) # Including plugins (.svg icons support, QtDesigner support, ...) if self.msvc: vc90man = "Microsoft.VC90.CRT.manifest" pyqt_tmp = "pyqt_tmp" if osp.isdir(pyqt_tmp): shutil.rmtree(pyqt_tmp) os.mkdir(pyqt_tmp) vc90man_pyqt = osp.join(pyqt_tmp, vc90man) if osp.isfile(vc90man): man = ( open(vc90man, "r") .read() .replace('<file name="', '<file name="Microsoft.VC90.CRT\\') ) open(vc90man_pyqt, "w").write(man) else: vc90man_pyqt = None for dirpath, _, filenames in os.walk(osp.join(pyqt_path, "plugins")): filelist = [ osp.join(dirpath, f) for f in filenames if osp.splitext(f)[1] in (".dll", ".py") ] if ( self.msvc and vc90man_pyqt is not None and [f for f in filelist if osp.splitext(f)[1] == ".dll"] ): # Where there is a DLL build with Microsoft Visual C++ 2008, # there must be a manifest file as well... # ...congrats to Microsoft for this great simplification! filelist.append(vc90man_pyqt) self.data_files.append( (dirpath[len(pyqt_path) + len(os.pathsep) :], filelist) ) if self.msvc: atexit.register(remove_dir, pyqt_tmp) # Including french translation fr_trans = osp.join(pyqt_path, "translations", "qt_fr.qm") if osp.exists(fr_trans): self.data_files.append(("translations", (fr_trans,)))
[docs] def add_pyside(self): """Include module PySide to the distribution""" if self._pyside_added: return self._pyside_added = True self.includes += [ "PySide.QtDeclarative", "PySide.QtHelp", "PySide.QtMultimedia", "PySide.QtNetwork", "PySide.QtOpenGL", "PySide.QtScript", "PySide.QtScriptTools", "PySide.QtSql", "PySide.QtSvg", "PySide.QtTest", "PySide.QtUiTools", "PySide.QtWebKit", "PySide.QtXml", "PySide.QtXmlPatterns", ] import PySide pyside_path = osp.dirname(PySide.__file__) # Configuring PySide conf = os.linesep.join(["[Paths]", "Prefix = .", "Binaries = ."]) self.add_text_data_file("qt.conf", conf) # Including plugins (.svg icons support, QtDesigner support, ...) if self.msvc: vc90man = "Microsoft.VC90.CRT.manifest" os.mkdir("pyside_tmp") vc90man_pyside = osp.join("pyside_tmp", vc90man) man = ( open(vc90man, "r") .read() .replace('<file name="', '<file name="Microsoft.VC90.CRT\\') ) open(vc90man_pyside, "w").write(man) for dirpath, _, filenames in os.walk(osp.join(pyside_path, "plugins")): filelist = [ osp.join(dirpath, f) for f in filenames if osp.splitext(f)[1] in (".dll", ".py") ] if self.msvc and [f for f in filelist if osp.splitext(f)[1] == ".dll"]: # Where there is a DLL build with Microsoft Visual C++ 2008, # there must be a manifest file as well... # ...congrats to Microsoft for this great simplification! filelist.append(vc90man_pyside) self.data_files.append( (dirpath[len(pyside_path) + len(os.pathsep) :], filelist) ) # Replacing dlls found by cx_Freeze by the real PySide Qt dlls: # (http://qt-project.org/wiki/Packaging_PySide_applications_on_Windows) dlls = [ osp.join(pyside_path, fname) for fname in os.listdir(pyside_path) if osp.splitext(fname)[1] == ".dll" ] self.data_files.append(("", dlls)) if self.msvc: atexit.register(remove_dir, "pyside_tmp") # Including french translation fr_trans = osp.join(pyside_path, "translations", "qt_fr.qm") if osp.exists(fr_trans): self.data_files.append(("translations", (fr_trans,)))
[docs] def add_qt_bindings(self): """Include Qt bindings, i.e. PyQt or PySide""" try: imp.find_module("PyQt5") self.add_modules("PyQt5") except ImportError: self.add_modules("PySide")
[docs] def add_matplotlib(self): """Include module Matplotlib to the distribution""" if "matplotlib" in self.excludes: self.excludes.remove("matplotlib") try: import matplotlib.numerix # analysis:ignore self.includes += [ "matplotlib.numerix.ma", "matplotlib.numerix.fft", "matplotlib.numerix.linear_algebra", "matplotlib.numerix.mlab", "matplotlib.numerix.random_array", ] except ImportError: pass self.add_module_data_files( "matplotlib", ("mpl-data",), ( ".conf", ".glade", "", ".png", ".svg", ".xpm", ".ppm", ".npy", ".afm", ".ttf", ), )
[docs] def add_modules(self, *module_names): """Include module *module_name*""" for module_name in module_names: print("Configuring module '%s'" % module_name) # TODO: Add support for PyQt6 if module_name == "PyQt5": self.add_pyqt() elif module_name == "PySide": self.add_pyside() elif module_name == "scipy": self.add_module_dir("scipy") elif module_name == "matplotlib": self.add_matplotlib() elif module_name == "h5py": self.add_module_dir("h5py") if self.bin_path_excludes is not None and os.name == "nt": # Specific to cx_Freeze on Windows: avoid including a zlib dll # built with another version of Microsoft Visual Studio self.bin_path_excludes += [ r"C:\Program Files", r"C:\Program Files (x86)", ] self.data_files.append( # necessary for cx_Freeze only ("", (osp.join(get_module_path("h5py"), "zlib1.dll"),)) ) elif module_name in ("docutils", "rst2pdf", "sphinx"): self.includes += [ "docutils.writers.null", "docutils.languages.en", "docutils.languages.fr", ] if module_name == "rst2pdf": self.add_module_data_files( "rst2pdf", ("styles",), (".json", ".style"), copy_to_root=True ) if module_name == "sphinx": import sphinx.ext for fname in os.listdir(osp.dirname(sphinx.ext.__file__)): if osp.splitext(fname)[1] == ".py": modname = "sphinx.ext.%s" % osp.splitext(fname)[0] self.includes.append(modname) elif module_name == "pygments": self.includes += [ "pygments", "pygments.formatters", "pygments.lexers", "pygments.lexers.agile", ] elif module_name == "zmq": # FIXME: this is not working, yet... (missing DLL) self.includes += [ "zmq", "zmq.core._poll", "zmq.core._version", "zmq.core.constants", "zmq.core.context", "zmq.core.device", "zmq.core.error", "zmq.core.message", "zmq.core.socket", "zmq.core.stopwatch", ] if os.name == "nt": self.bin_includes += ["libzmq.dll"] elif module_name == "guidata": self.add_module_data_files( "guidata", ("images",), (".png", ".svg"), copy_to_root=False ) self.add_qt_bindings() elif module_name == "guiqwt": self.add_module_data_files( "guiqwt", ("images",), (".png", ".svg"), copy_to_root=False ) if os.name == "nt": # Specific to cx_Freeze: including manually MinGW DLLs self.bin_includes += ["libgcc_s_dw2-1.dll", "libstdc++-6.dll"] else: try: # Modules based on the same scheme as guidata and guiqwt self.add_module_data_files( module_name, ("images",), (".png", ".svg"), copy_to_root=False ) except IOError: raise RuntimeError("Module not supported: %s" % module_name)
[docs] def add_module_data_dir( self, module_name, data_dir_name, extensions, copy_to_root=True, verbose=False, exclude_dirs=[], ): """ Collect data files in *data_dir_name* for module *module_name* and add them to *data_files* *extensions*: list of file extensions, e.g. ('.png', '.svg') """ module_dir = get_module_path(module_name) nstrip = len(module_dir) + len(osp.sep) data_dir = osp.join(module_dir, data_dir_name) if not osp.isdir(data_dir): raise IOError("Directory not found: %s" % data_dir) for dirpath, _dirnames, filenames in os.walk(data_dir): dirname = dirpath[nstrip:] if osp.basename(dirpath) in exclude_dirs: continue if not copy_to_root: dirname = osp.join(module_name, dirname) pathlist = [ osp.join(dirpath, f) for f in filenames if osp.splitext(f)[1].lower() in extensions ] self.data_files.append((dirname, pathlist)) if verbose: for name in pathlist: print(" ", name)
[docs] def add_module_dir(self, module_name, verbose=False, exclude_dirs=[]): """ Collect all module files for module *module_name* and add them to *data_files* """ module_dir = get_module_path(module_name) nstrip = len(module_dir) + len(osp.sep) for dirpath, dirnames, filenames in os.walk(module_dir): if osp.basename(dirpath) in exclude_dirs: continue for dn in dirnames[:]: if not osp.isfile(osp.join(dirpath, dn, "__init__.py")): dirnames.remove(dn) dirname = osp.join(module_name, dirpath[nstrip:]) for filename in filenames: ext = osp.splitext(filename)[1].lower() if ext in (".py", ".pyd"): if filename == "__init__.py": fn = dirname else: fn = osp.splitext(osp.join(dirname, filename))[0] if fn.endswith(os.sep): fn = fn[:-1] modname = ".".join(fn.split(os.sep)) self.includes += [modname] if verbose: print(" + ", modname)
[docs] def add_module_data_files( self, module_name, data_dir_names, extensions, copy_to_root=True, verbose=False, exclude_dirs=[], ): """ Collect data files for module *module_name* and add them to *data_files* *data_dir_names*: list of dirnames, e.g. ('images', ) *extensions*: list of file extensions, e.g. ('.png', '.svg') """ print( "Adding module '%s' data files in %s (%s)" % (module_name, ", ".join(data_dir_names), ", ".join(extensions)) ) module_dir = get_module_path(module_name) for data_dir_name in data_dir_names: self.add_module_data_dir( module_name, data_dir_name, extensions, copy_to_root, verbose, exclude_dirs, ) translation_file = osp.join( module_dir, "locale", "fr", "LC_MESSAGES", "%s.mo" % module_name ) if osp.isfile(translation_file): self.data_files.append( ( osp.join(module_name, "locale", "fr", "LC_MESSAGES"), (translation_file,), ) ) print( "Adding module '%s' translation file: %s" % (module_name, osp.basename(translation_file)) )
[docs] def build(self, library, cleanup=True, create_archive=None): """Build executable with given library. library: * 'py2exe': deploy using the `py2exe` library * 'cx_Freeze': deploy using the `cx_Freeze` library cleanup: remove 'build/dist' directories before building distribution create_archive (requires the executable `zip`): * None or False: do nothing * 'add': add target directory to a ZIP archive * 'move': move target directory to a ZIP archive """ if library == "py2exe": self.build_py2exe(cleanup=cleanup, create_archive=create_archive) elif library == "cx_Freeze": self.build_cx_freeze(cleanup=cleanup, create_archive=create_archive) else: raise RuntimeError("Unsupported library %s" % library)
def __cleanup(self): """Remove old build and dist directories""" remove_dir("build") if osp.isdir("dist"): remove_dir("dist") remove_dir(self.target_dir) def __create_archive(self, option): """Create a ZIP archive option: * 'add': add target directory to a ZIP archive * 'move': move target directory to a ZIP archive """ name = self.target_dir os.system('zip "%s.zip" -r "%s"' % (name, name)) if option == "move": shutil.rmtree(name)
[docs] def build_py2exe( self, cleanup=True, compressed=2, optimize=2, company_name=None, copyright=None, create_archive=None, ): """Build executable with py2exe cleanup: remove 'build/dist' directories before building distribution create_archive (requires the executable `zip`): * None or False: do nothing * 'add': add target directory to a ZIP archive * 'move': move target directory to a ZIP archive """ from distutils.core import setup import py2exe # Patching distutils -- analysis:ignore self._py2exe_is_loaded = True if cleanup: self.__cleanup() sys.argv += ["py2exe"] options = dict( compressed=compressed, optimize=optimize, includes=self.includes, excludes=self.excludes, dll_excludes=self.bin_excludes, dist_dir=self.target_dir, ) windows = dict( name=self.name, description=self.description, script=self.script, icon_resources=[(0, self.icon)], bitmap_resources=[], other_resources=[], dest_base=osp.splitext(self.target_name)[0], version=self.version, company_name=company_name, copyright=copyright, ) setup( data_files=self.data_files, windows=[ windows, ], options=dict(py2exe=options), ) if create_archive: self.__create_archive(create_archive)
[docs] def add_executable(self, script, target_name, icon=None): """Add executable to the cx_Freeze distribution Not supported for py2exe""" from cx_Freeze import Executable base = None if script.endswith(".pyw") and os.name == "nt": base = "win32gui" self.executables += [ Executable(script, base=base, icon=icon, targetName=target_name) ]
[docs] def build_cx_freeze(self, cleanup=True, create_archive=None): """Build executable with cx_Freeze cleanup: remove 'build/dist' directories before building distribution create_archive (requires the executable `zip`): * None or False: do nothing * 'add': add target directory to a ZIP archive * 'move': move target directory to a ZIP archive """ assert not self._py2exe_is_loaded, "cx_Freeze can't be executed after py2exe" from cx_Freeze import setup # ===== Monkey-patching cx_Freeze (backported from v5.0 dev) =========== from cx_Freeze import hooks def load_h5py(finder, module): """h5py module has a number of implicit imports""" finder.IncludeModule("h5py.defs") finder.IncludeModule("h5py.utils") finder.IncludeModule("h5py._proxy") try: import h5py.api_gen finder.IncludeModule("h5py.api_gen") except ImportError: pass finder.IncludeModule("h5py._errors") finder.IncludeModule("h5py.h5ac") hooks.load_h5py = load_h5py # ===== Monkey-patching cx_Freeze (backported from v5.0 dev) =========== # ===== Monkey-patching cx_Freeze for Scipy ============================ def load_scipy(finder, module): pass hooks.load_scipy = load_scipy # ===== Monkey-patching cx_Freeze for Scipy ============================ if cleanup: self.__cleanup() sys.argv += ["build"] excv = "3" if sys.version[0] == "2" else "2" self.excludes += ["sympy.mpmath.libmp.exec_py%s" % excv] build_exe = dict( include_files=to_include_files(self.data_files), includes=self.includes, excludes=self.excludes, bin_excludes=self.bin_excludes, bin_includes=self.bin_includes, bin_path_includes=self.bin_path_includes, bin_path_excludes=self.bin_path_excludes, build_exe=self.target_dir, ) setup( name=self.name, version=self.version, description=self.description, executables=self.executables, options=dict(build_exe=build_exe), ) if create_archive: self.__create_archive(create_archive)
if __name__ == "__main__": for python_version in ("2.7", "3.3"): for arch in (32, 64): print("Python %s %dbit" % (python_version, arch)) msvc_version = get_msvc_version(python_version) filelist = get_msvc_dlls(msvc_version, architecture=arch) for fname in filelist: if ".dll" in fname: print(get_dll_architecture(fname)) print()