| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145 |
- # -*- 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``.
- """
- from __future__ import print_function
- import sys
- import os
- import os.path as osp
- import shutil
- import traceback
- import atexit
- import imp
- from subprocess import Popen, PIPE
- import warnings
- from distutils.version import LooseVersion, StrictVersion
- # ==============================================================================
- # Module, scripts, programs
- # ==============================================================================
- def get_module_path(modname):
- """Return module *modname* base path"""
- module = sys.modules.get(modname, __import__(modname))
- return osp.abspath(osp.dirname(module.__file__))
- # ==============================================================================
- # Dependency management
- # ==============================================================================
- 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())
- 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
- 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))
- def get_msvc_version(python_version):
- """Return Microsoft Visual C++ version used to build this Python version"""
- if python_version is None:
- python_version = '2.7'
- warnings.warn("assuming Python 2.7 target")
- 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'
- elif python_version in ('3.5', '3.6'):
- return '15.0'
- elif python_version in ('3.7', '3.8'):
- return '15.0'
- elif StrictVersion(python_version) >= StrictVersion('3.9'):
- return '15.0'
- else:
- raise RuntimeError(
- "Unsupported Python version %s" % python_version
- )
- def get_msvc_dlls(msvc_version, architecture=None):
- """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
- filelist = []
- # simple vs2015 situation: nothing (system dll)
- if msvc_version == '14.0':
- return 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' or msvc_major == '15'
- ): # 15 for vs 2015
- namelist = [
- name % (msvc_major + msvc_minor)
- for name in (
- 'msvcp%s.dll',
- 'msvcr%s.dll',
- 'vcomp%s.dll',
- )
- ]
- if msvc_major == '15' and architecture == 64:
- namelist = [
- name % ('14' + msvc_minor)
- for name in (
- 'vcruntime%s.dll',
- 'vcruntime%s_1.dll',
- 'msvcp%s.dll',
- 'vccorlib%s.dll',
- 'concrt%s.dll',
- 'vcomp%s.dll',
- )
- ]
- if msvc_major == '15' and architecture != 64:
- namelist = [
- name % ('14' + msvc_minor)
- for name in (
- 'vcruntime%s.dll',
- 'msvcp%s.dll',
- 'vccorlib%s.dll',
- 'concrt%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 64-bit DLL and applications,
- # whereas "System32" contains 64-bit DLL and applications on a 64-bit
- # system.
- sysdir = "System32"
- if not is_64bit_windows and architecture == 64:
- raise RuntimeError(
- "Can't find 64-bit MSVC DLLs on a 32-bit OS"
- )
- if is_64bit_windows and architecture == 32:
- sysdir = "SysWOW64"
- for dllname in namelist:
- fname = osp.join(windir, sysdir, dllname)
- print('searching', fname)
- 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
- )
- return filelist
- 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)]
- 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
- 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]
- )
- 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()
- 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',
- ]
- 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.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._pyqt4_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
- 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
- )
- 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(contents)
- self.data_files += [("", (filename,))]
- _remove_later(filename)
- def add_data_file(self, filename, destdir=''):
- self.data_files += [(destdir, (filename,))]
- # ------ Adding packages
- def add_pyqt4(self):
- """Include module PyQt4 to the distribution"""
- if self._pyqt4_added:
- return
- self._pyqt4_added = True
- self.includes += [
- 'sip',
- 'PyQt4.Qt',
- 'PyQt4.QtSvg',
- 'PyQt4.QtNetwork',
- ]
- import PyQt4
- pyqt_path = osp.dirname(PyQt4.__file__)
- # Configuring PyQt4
- 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)
- man = (
- open(vc90man, "r")
- .read()
- .replace(
- '<file name="',
- '<file name="Microsoft.VC90.CRT\\',
- )
- )
- open(vc90man_pyqt, 'w').write(man)
- 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 [
- 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,))
- )
- 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,))
- )
- def add_qt_bindings(self):
- """Include Qt bindings, i.e. PyQt4 or PySide"""
- try:
- imp.find_module('PyQt4')
- self.add_modules('PyQt4')
- except ImportError:
- self.add_modules('PySide')
- def add_matplotlib(self):
- """Include module Matplotlib to the distribution"""
- if 'matplotlib' in self.excludes:
- self.excludes.pop(
- self.excludes.index('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',
- ),
- )
- def add_modules(self, *module_names):
- """Include module *module_name*"""
- for module_name in module_names:
- print("Configuring module '%s'" % module_name)
- if module_name == 'PyQt4':
- self.add_pyqt4()
- elif module_name == 'PySide':
- self.add_pyside()
- elif module_name == 'scipy.io':
- self.includes += ['scipy.io.matlab.streams']
- elif module_name == 'matplotlib':
- self.add_matplotlib()
- elif module_name == 'h5py':
- import h5py
- for attr in [
- '_stub',
- '_sync',
- 'utils',
- '_conv',
- '_proxy',
- 'defs',
- ]:
- if hasattr(h5py, attr):
- self.includes.append(
- 'h5py.%s' % attr
- )
- 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,
- )
- try:
- imp.find_module('PyQt4')
- self.add_pyqt4()
- except ImportError:
- self.add_pyside()
- 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
- )
- 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)
- 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),
- )
- )
- 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)
- 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)
- 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(
- self.script,
- base=base,
- icon=self.icon,
- targetName=self.target_name,
- )
- ]
- 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
- if cleanup:
- self.__cleanup()
- sys.argv += ["build"]
- 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)
|