diff --git a/setup/installer/cx_Freeze/HISTORY.txt b/setup/installer/cx_Freeze/HISTORY.txt new file mode 100644 index 0000000000..acf9ad0dfe --- /dev/null +++ b/setup/installer/cx_Freeze/HISTORY.txt @@ -0,0 +1,244 @@ +Changes from 4.0 to 4.0.1 + 1) Added support for Python 2.6. On Windows a manifest file is now required + because of the switch to using the new Microsoft C runtime. + 2) Ensure that hooks are run for builtin modules. + +Changes from 4.0b1 to 4.0 + 1) Added support for copying files to the target directory. + 2) Added support for a hook that runs when a module is missing. + 3) Added support for binary path includes as well as excludes; use sequences + rather than dictionaries as a more convenient API; exclude the standard + locations for 32-bit and 64-bit libaries in multi-architecture systems. + 4) Added support for searching zip files (egg files) for modules. + 5) Added support for handling system exit exceptions similarly to what Python + does itself as requested by Sylvain. + 6) Added code to wait for threads to shut down like the normal Python + interpreter does. Thanks to Mariano Disanzo for discovering this + discrepancy. + 7) Hooks added or modified based on feedback from many people. + 8) Don't include the version name in the display name of the MSI. + 9) Use the OS dependent path normalization routines rather than simply use the + lowercase value as on Unix case is important; thanks to Artie Eoff for + pointing this out. +10) Include a version attribute in the cx_Freeze package and display it in the + output for the --version option to the script. +11) Include build instructions as requested by Norbert Sebok. +12) Add support for copying files when modules are included which require data + files to operate properly; add support for copying the necessary files for + the Tkinter and matplotlib modules. +13) Handle deferred imports recursively as needed; ensure that from lists do + not automatically indicate that they are part of the module or the deferred + import processing doesn't actually work! +14) Handle the situation where a module imports everything from a package and + the __all__ variable has been defined but the package has not actually + imported everything in the __all__ variable during initialization. +15) Modified license text to more closely match the Python Software Foundation + license as was intended. +16) Added sample script for freezing an application using matplotlib. +17) Renamed freeze to cxfreeze to avoid conflict with another package that uses + that executable as requested by Siegfried Gevatter. + +Changes from 3.0.3 to 4.0b1 + 1) Added support for placing modules in library.zip or in a separate zip file + for each executable that is produced. + 2) Added support for copying binary dependent files (DLLs and shared + libraries) + 3) Added support for including all submodules in a package + 4) Added support for including icons in Windows executables + 5) Added support for constants module which can be used for determining + certain build constants at runtime + 6) Added support for relative imports available in Python 2.5 and up + 7) Added support for building Windows installers (Python 2.5 and up) and + RPM packages + 8) Added support for distutils configuration scripts + 9) Added support for hooks which can force inclusion or exclusion of modules + when certain modules are included +10) Added documentation and samples +11) Added setup.py for building the cx_Freeze package instead of a script + used to build only the frozen bases +12) FreezePython renamed to a script called freeze in the Python distribution +13) On Linux and other platforms that support it set LD_RUN_PATH to include + the directory in which the executable is located + +Changes from 3.0.2 to 3.0.3 + 1) In Common.c, used MAXPATHLEN defined in the Python OS independent include + file rather than the PATH_MAX define which is OS dependent and is not + available on IRIX as noted by Andrew Jones. + 2) In the initscript ConsoleSetLibPath.py, added lines from initscript + Console.py that should have been there since the only difference between + that script and this one is the automatic re-execution of the executable. + 3) Added an explicit "import encodings" to the initscripts in order to handle + Unicode encodings a little better. Thanks to Ralf Schmitt for pointing out + the problem and its solution. + 4) Generated a meaningful name for the extension loader script so that it is + clear which particular extension module is being loaded when an exception + is being raised. + 5) In MakeFrozenBases.py, use distutils to figure out a few more + platform-dependent linker flags as suggested by Ralf Schmitt. + +Changes from 3.0.1 to 3.0.2 + 1) Add support for compressing the byte code in the zip files that are + produced. + 2) Add better support for the win32com package as requested by Barry Scott. + 3) Prevent deletion of target file if it happens to be identical to the + source file. + 4) Include additional flags for local modifications to a Python build as + suggested by Benjamin Rutt. + 5) Expanded instructions for building cx_Freeze from source based on a + suggestion from Gregg Lind. + 6) Fix typo in help string. + +Changes from 3.0 to 3.0.1 + 1) Added option --default-path which is used to specify the path used when + finding modules. This is particularly useful when performing cross + compilations (such as for building a frozen executable for Windows CE). + 2) Added option --shared-lib-name which can be used to specify the name of + the shared library (DLL) implementing the Python runtime that is required + for the frozen executable to work. This option is also particularly useful + when cross compiling since the normal method for determining this + information cannot be used. + 3) Added option --zip-include which allows for additional files to be added + to the zip file that contains the modules that implement the Python + script. Thanks to Barray Warsaw for providing the initial patch. + 4) Added support for handling read-only files properly. Thanks to Peter + Grayson for pointing out the problem and providing a solution. + 5) Added support for a frozen executable to be a symbolic link. Thanks to + Robert Kiendl for providing the initial patch. + 6) Enhanced the support for running a frozen executable that uses an existing + Python installation to locate modules it requires. This is primarily of + use for embedding Python where the interface is C but the ability to run + from source is still desired. + 7) Modified the documentation to indicate that building from source on + Windows currently requires the mingw compiler (http://www.mingw.org). + 8) Workaround the problem in Python 2.3 (fixed in Python 2.4) which causes a + broken module to be left in sys.modules if an ImportError takes place + during the execution of the code in that module. Thanks to Roger Binns + for pointing this out. + +Changes from 3.0 beta3 to 3.0 + 1) Ensure that ldd is only run on extension modules. + 2) Allow for using a compiler other than gcc for building the frozen base + executables by setting the environment variable CC. + 3) Ensure that the import lock is not held while executing the main script; + otherwise, attempts to import a module within a thread will hang that + thread as noted by Roger Binns. + 4) Added support for replacing the paths in all frozen modules with something + else (so that for example the path of the machine on which the freezing + was done is not displayed in tracebacks) + +Changes from 3.0 beta2 to 3.0 beta3 + 1) Explicitly include the warnings module so that at runtime warnings are + suppressed as when running Python normally. + 2) Improve the extension loader so that an ImportError is raised when the + dynamic module is not located; otherwise an error about missing attributes + is raised instead. + 3) Extension loaders are only created when copying dependencies since the + normal module should be loadable in the situation where a Python + installation is available. + 4) Added support for Python 2.4. + 5) Fixed the dependency checking for wxPython to be a little more + intelligent. + +Changes from 3.0 beta1 to 3.0 beta2 + 1) Fix issues with locating the initscripts and bases relative to the + directory in which the executable was started. + 2) Added new base executable ConsoleKeepPath which is used when an existing + Python installation is required (such as for FreezePython itself). + 3) Forced the existence of a Python installation to be ignored when using the + standard Console base executable. + 4) Remove the existing file when copying dependent files; otherwise, an error + is raised when attempting to overwrite read-only files. + 5) Added option -O (or -OO) to FreezePython to set the optimization used when + generating bytecode. + +Changes from 2.2 to 3.0 beta1 + 1) cx_Freeze now requires Python 2.3 or higher since it takes advantage of + the ability of Python 2.3 and higher to import modules from zip files. + This makes the freezing process considerably simpler and also allows for + the execution of multiple frozen packages (such as found in COM servers or + shared libraries) without requiring modification to the Python modules. + 2) All external dependencies have been removed. cx_Freeze now only requires + a standard Python distribution to do its work. + 3) Added the ability to define the initialization scripts that cx_Freeze uses + on startup of the frozen program. Previously, these scripts were written + in C and could not easily be changed; now they are written in Python and + can be found in the initscripts directory (and chosen with the + new --init-script option to FreezePython). + 4) The base executable ConsoleSetLibPath has been removed and replaced with + the initscript ConsoleSetLibPath. + 5) Removed base executables for Win32 services and Win32 COM servers. This + functionality will be restored in the future but it is not currently in a + state that is ready for release. If this functionality is required, please + use py2exe or contact me for my work in progress. + 6) The attribute sys.frozen is now set so that more recent pywin32 modules + work as expected when frozen. + 7) Added option --include-path to FreezePython to allow overriding of + sys.path without modifying the environment variable PYTHONPATH. + 8) Added option --target-dir/--install-dir to specify the directory in which + the frozen executable and its dependencies will be placed. + 9) Removed the option --shared-lib since it was used for building shared + libraries and can be managed with the initscript SharedLib.py. +10) MakeFrozenBases.py now checks the platform specific include directory as + requested by Michael Partridge. + + +Changes from 2.1 to 2.2 + 1) Add option (--ext-list-file) to FreezePython to write the list of + extensions copied to the installation directory to a file. This option is + useful in cases where multiple builds are performed into the same + installation directory. + 2) Pass the arguments on the command line through to Win32 GUI applications. + Thanks to Michael Porter for pointing this out. + 3) Link directly against the python DLL when building the frozen bases on + Windows, thus eliminating the need for building an import library. + 4) Force sys.path to include the directory in which the script to be frozen + is found. + 5) Make sure that the installation directory exists before attempting to + copy the target binary into it. + 6) The Win32GUI base has been modified to display fatal errors in message + boxes, rather than printing errors to stderr, since on Windows the + standard file IO handles are all closed. + +Changes from 2.0 to 2.1 + 1) Remove dependency on Python 2.2. Thanks to Paul Moore for not only + pointing it out but providing patches. + 2) Set up the list of frozen modules in advance, rather than doing it after + Python is initialized so that implicit imports done by Python can be + satisfied. The bug in Python 2.3 that demonstrated this issue has been + fixed in the first release candidate. Thanks to Thomas Heller for pointing + out the obvious in this instance! + 3) Added additional base executable (ConsoleSetLibPath) to support setting + the LD_LIBRARY_PATH variable on Unix platforms and restarting the + executable to put the new setting into effect. This is primarily of use + in distributing wxPython applications on Unix where the shared library + has an embedded RPATH value which can cause problems. + 4) Small improvements of documentation based on feedback from several people. + 5) Print information about the files written or copied during the freezing + process. + 6) Do not copy extensions when freezing if the path is being overridden since + it is expected that a full Python installation is available to the target + users of the frozen binary. + 7) Provide meaningful error message when the wxPython library cannot be + found during the freezing process. + +Changes from 1.1 to 2.0 + 1) Added support for in process (DLL) COM servers using PythonCOM. + 2) Ensured that the frozen flag is set prior to determining the full path for + the program in order to avoid warnings about Python not being found on + some platforms. + 3) Added include file and resource file to the source tree to avoid the + dependency on the Wine message compiler for Win32 builds. + 4) Dropped the option --copy-extensions; this now happens automatically since + the resulting binary is useless without them. + 5) Added a sample for building a Win32 service. + 6) Make use of improved modules from Python 2.3 (which function under 2.2) + +Changes from 1.0 to 1.1 + 1) Fixed import error with C extensions in packages; thanks to Thomas Heller + for pointing out the solution to this problem. + 2) Added options to FreezePython to allow for the inclusion of modules which + will not be found by the module finder (--include-modules) and the + exclusion of modules which will be found by the module finder but should + not be included (--exclude-modules). + 3) Fixed typo in README.txt. + diff --git a/setup/installer/cx_Freeze/LICENSE.txt b/setup/installer/cx_Freeze/LICENSE.txt new file mode 100644 index 0000000000..cb9ee05a8a --- /dev/null +++ b/setup/installer/cx_Freeze/LICENSE.txt @@ -0,0 +1,53 @@ +Copyright © 2007-2008, Colt Engineering, Edmonton, Alberta, Canada. +Copyright © 2001-2006, Computronix (Canada) Ltd., Edmonton, Alberta, Canada. +All rights reserved. + +NOTE: this license is derived from the Python Software Foundation License +which can be found at http://www.python.org/psf/license + +License for cx_Freeze 4.0.1 +--------------------------- + +1. This LICENSE AGREEMENT is between the copyright holders and the Individual + or Organization ("Licensee") accessing and otherwise using cx_Freeze + software in source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, the + copyright holders hereby grant Licensee a nonexclusive, royalty-free, + world-wide license to reproduce, analyze, test, perform and/or display + publicly, prepare derivative works, distribute, and otherwise use cx_Freeze + alone or in any derivative version, provided, however, that this License + Agreement and this notice of copyright are retained in cx_Freeze alone or in + any derivative version prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on or + incorporates cx_Freeze or any part thereof, and wants to make the derivative + work available to others as provided herein, then Licensee hereby agrees to + include in any such work a brief summary of the changes made to cx_Freeze. + +4. The copyright holders are making cx_Freeze available to Licensee on an + "AS IS" basis. THE COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, + EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, THE COPYRIGHT + HOLDERS MAKE NO AND DISCLAIM ANY REPRESENTATION OR WARRANTY OF + MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF + CX_FREEZE WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. THE COPYRIGHT HOLDERS SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF + CX_FREEZE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS + A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING CX_FREEZE, OR ANY + DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach + of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any relationship + of agency, partnership, or joint venture between the copyright holders and + Licensee. This License Agreement does not grant permission to use + copyright holder's trademarks or trade name in a trademark sense to endorse + or promote products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using cx_Freeze, Licensee agrees to be + bound by the terms and conditions of this License Agreement. + +Computronix® is a registered trademark of Computronix (Canada) Ltd. + diff --git a/setup/installer/cx_Freeze/MANIFEST.in b/setup/installer/cx_Freeze/MANIFEST.in new file mode 100644 index 0000000000..2348a66973 --- /dev/null +++ b/setup/installer/cx_Freeze/MANIFEST.in @@ -0,0 +1,6 @@ +include MANIFEST.in +include *.txt +recursive-include doc *.html +recursive-include initscripts *.py +recursive-include samples *.py +recursive-include source *.c *.rc diff --git a/setup/installer/cx_Freeze/PKG-INFO b/setup/installer/cx_Freeze/PKG-INFO new file mode 100644 index 0000000000..aa53b57914 --- /dev/null +++ b/setup/installer/cx_Freeze/PKG-INFO @@ -0,0 +1,22 @@ +Metadata-Version: 1.0 +Name: cx_Freeze +Version: 4.0.1 +Summary: create standalone executables from Python scripts +Home-page: http://cx-freeze.sourceforge.net +Author: Anthony Tuininga +Author-email: anthony.tuininga@gmail.com +License: Python Software Foundation License +Description: create standalone executables from Python scripts +Keywords: freeze +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Python Software Foundation License +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: C +Classifier: Programming Language :: Python +Classifier: Topic :: Software Development :: Build Tools +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: System :: Software Distribution +Classifier: Topic :: Utilities diff --git a/setup/installer/cx_Freeze/README.txt b/setup/installer/cx_Freeze/README.txt new file mode 100644 index 0000000000..1ac67dc749 --- /dev/null +++ b/setup/installer/cx_Freeze/README.txt @@ -0,0 +1,12 @@ +Please see cx_Freeze.html for documentation on how to use cx_Freeze. + +To build: + +python setup.py build +python setup.py install + +On Windows I have used the MinGW compiler (http://www.mingw.org) + +python setup.py build --compiler=mingw32 +python setup.py build --compiler=mingw32 install + diff --git a/setup/installer/cx_Freeze/cx_Freeze/__init__.py b/setup/installer/cx_Freeze/cx_Freeze/__init__.py new file mode 100644 index 0000000000..545883eb3e --- /dev/null +++ b/setup/installer/cx_Freeze/cx_Freeze/__init__.py @@ -0,0 +1,14 @@ +version = "4.0.1" + +import sys +from dist import * +if sys.platform == "win32" and sys.version_info[:2] >= (2, 5): + from windist import * +from finder import * +from freezer import * +from main import * + +del dist +del finder +del freezer + diff --git a/setup/installer/cx_Freeze/cx_Freeze/dist.py b/setup/installer/cx_Freeze/cx_Freeze/dist.py new file mode 100644 index 0000000000..c2af2ac623 --- /dev/null +++ b/setup/installer/cx_Freeze/cx_Freeze/dist.py @@ -0,0 +1,279 @@ +import distutils.command.bdist_rpm +import distutils.command.build +import distutils.command.install +import distutils.core +import distutils.dir_util +import distutils.dist +import distutils.util +import distutils.version +import os +import sys + +import cx_Freeze + +__all__ = [ "bdist_rpm", "build", "build_exe", "install", "install_exe", + "setup" ] + +class Distribution(distutils.dist.Distribution): + + def __init__(self, attrs): + self.executables = [] + distutils.dist.Distribution.__init__(self, attrs) + + +class bdist_rpm(distutils.command.bdist_rpm.bdist_rpm): + + def finalize_options(self): + distutils.command.bdist_rpm.bdist_rpm.finalize_options(self) + self.use_rpm_opt_flags = 1 + + def _make_spec_file(self): + contents = distutils.command.bdist_rpm.bdist_rpm._make_spec_file(self) + return [c for c in contents if c != 'BuildArch: noarch'] + + +class build(distutils.command.build.build): + user_options = distutils.command.build.build.user_options + [ + ('build-exe=', None, 'build directory for executables') + ] + + def get_sub_commands(self): + subCommands = distutils.command.build.build.get_sub_commands(self) + if self.distribution.executables: + subCommands.append("build_exe") + return subCommands + + def initialize_options(self): + distutils.command.build.build.initialize_options(self) + self.build_exe = None + + def finalize_options(self): + distutils.command.build.build.finalize_options(self) + if self.build_exe is None: + dirName = "exe.%s-%s" % \ + (distutils.util.get_platform(), sys.version[0:3]) + self.build_exe = os.path.join(self.build_base, dirName) + + +class build_exe(distutils.core.Command): + description = "build executables from Python scripts" + user_options = [ + ('build-exe=', 'b', + 'directory for built executables'), + ('optimize=', 'O', + 'optimization level: -O1 for "python -O", ' + '-O2 for "python -OO" and -O0 to disable [default: -O0]'), + ('excludes=', 'e', + 'comma-separated list of modules to exclude'), + ('includes=', 'i', + 'comma-separated list of modules to include'), + ('packages=', 'p', + 'comma-separated list of packages to include'), + ('replace-paths=', None, + 'comma-separated list of paths to replace in included modules'), + ('path=', None, + 'comma-separated list of paths to search'), + ('init-script=', 'i', + 'name of script to use during initialization'), + ('base=', None, + 'name of base executable to use'), + ('compressed', 'c', + 'create a compressed zipfile'), + ('copy-dependent-files', None, + 'copy all dependent files'), + ('create-shared-zip', None, + 'create a shared zip file containing shared modules'), + ('append-script-to-exe', None, + 'append the script module to the exe'), + ('include-in-shared-zip', None, + 'include the script module in the shared zip file'), + ('icon', None, + 'include the icon along with the frozen executable(s)'), + ('constants=', None, + 'comma-separated list of constants to include'), + ('include-files=', 'f', + 'list of tuples of additional files to include in distribution'), + ('bin-includes', None, + 'list of names of files to include when determining dependencies'), + ('bin-excludes', None, + 'list of names of files to exclude when determining dependencies') + ] + boolean_options = ["compressed", "copy_dependent_files", + "create_shared_zip", "append_script_to_exe", + "include_in_shared_zip"] + + def _normalize(self, attrName): + value = getattr(self, attrName) + if value is None: + normalizedValue = [] + elif isinstance(value, basestring): + normalizedValue = value.split() + else: + normalizedValue = list(value) + setattr(self, attrName, normalizedValue) + + def initialize_options(self): + self.optimize = 0 + self.build_exe = None + self.excludes = [] + self.includes = [] + self.packages = [] + self.replace_paths = [] + self.compressed = None + self.copy_dependent_files = None + self.init_script = None + self.base = None + self.path = None + self.create_shared_zip = None + self.append_script_to_exe = None + self.include_in_shared_zip = None + self.icon = None + self.constants = [] + self.include_files = [] + self.bin_excludes = [] + self.bin_includes = [] + + def finalize_options(self): + self.set_undefined_options('build', ('build_exe', 'build_exe')) + self.optimize = int(self.optimize) + self._normalize("excludes") + self._normalize("includes") + self._normalize("packages") + self._normalize("constants") + + def run(self): + metadata = self.distribution.metadata + constantsModule = cx_Freeze.ConstantsModule(metadata.version) + for constant in self.constants: + parts = constant.split("=") + if len(parts) == 1: + name = constant + value = None + else: + name, stringValue = parts + value = eval(stringValue) + constantsModule.values[name] = value + freezer = cx_Freeze.Freezer(self.distribution.executables, + [constantsModule], self.includes, self.excludes, self.packages, + self.replace_paths, self.compressed, self.optimize, + 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, + includeFiles = self.include_files, + binIncludes = self.bin_includes, + binExcludes = self.bin_excludes) + freezer.Freeze() + + +class install(distutils.command.install.install): + user_options = distutils.command.install.install.user_options + [ + ('install-exe=', None, + 'installation directory for executables') + ] + + def expand_dirs(self): + distutils.command.install.install.expand_dirs(self) + self._expand_attrs(['install_exe']) + + def get_sub_commands(self): + subCommands = distutils.command.install.install.get_sub_commands(self) + if self.distribution.executables: + subCommands.append("install_exe") + return [s for s in subCommands if s != "install_egg_info"] + + def initialize_options(self): + distutils.command.install.install.initialize_options(self) + self.install_exe = None + + def finalize_options(self): + if self.prefix is None and sys.platform == "win32": + import _winreg + key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, + r"Software\Microsoft\Windows\CurrentVersion") + prefix = str(_winreg.QueryValueEx(key, "ProgramFilesDir")[0]) + metadata = self.distribution.metadata + dirName = "%s-%s" % (metadata.name, metadata.version) + self.prefix = "%s/%s" % (prefix, dirName) + distutils.command.install.install.finalize_options(self) + self.convert_paths('exe') + if self.root is not None: + self.change_roots('exe') + + def select_scheme(self, name): + distutils.command.install.install.select_scheme(self, name) + if self.install_exe is None: + if sys.platform == "win32": + self.install_exe = '$base' + else: + metadata = self.distribution.metadata + dirName = "%s-%s" % (metadata.name, metadata.version) + self.install_exe = '$base/lib/%s' % dirName + + +class install_exe(distutils.core.Command): + description = "install executables built from Python scripts" + user_options = [ + ('install-dir=', 'd', 'directory to install executables to'), + ('build-dir=', 'b', 'build directory (where to install from)'), + ('force', 'f', 'force installation (overwrite existing files)'), + ('skip-build', None, 'skip the build steps') + ] + + def initialize_options(self): + self.install_dir = None + self.force = 0 + self.build_dir = None + self.skip_build = None + + def finalize_options(self): + self.set_undefined_options('build', ('build_exe', 'build_dir')) + self.set_undefined_options('install', + ('install_exe', 'install_dir'), + ('force', 'force'), + ('skip_build', 'skip_build')) + + def run(self): + if not self.skip_build: + self.run_command('build_exe') + self.outfiles = self.copy_tree(self.build_dir, self.install_dir) + if sys.platform != "win32": + baseDir = os.path.dirname(os.path.dirname(self.install_dir)) + binDir = os.path.join(baseDir, "bin") + if not os.path.exists(binDir): + os.makedirs(binDir) + sourceDir = os.path.join("..", self.install_dir[len(baseDir) + 1:]) + for executable in self.distribution.executables: + name = os.path.basename(executable.targetName) + source = os.path.join(sourceDir, name) + target = os.path.join(binDir, name) + if os.path.exists(target): + os.unlink(target) + os.symlink(source, target) + self.outfiles.append(target) + + def get_inputs(self): + return self.distribution.executables or [] + + def get_outputs(self): + return self.outfiles or [] + + +def _AddCommandClass(commandClasses, name, cls): + if name not in commandClasses: + commandClasses[name] = cls + + +def setup(**attrs): + attrs["distclass"] = Distribution + commandClasses = attrs.setdefault("cmdclass", {}) + if sys.platform == "win32": + if sys.version_info[:2] >= (2, 5): + _AddCommandClass(commandClasses, "bdist_msi", cx_Freeze.bdist_msi) + else: + _AddCommandClass(commandClasses, "bdist_rpm", cx_Freeze.bdist_rpm) + _AddCommandClass(commandClasses, "build", build) + _AddCommandClass(commandClasses, "build_exe", build_exe) + _AddCommandClass(commandClasses, "install", install) + _AddCommandClass(commandClasses, "install_exe", install_exe) + distutils.core.setup(**attrs) + diff --git a/setup/installer/cx_Freeze/cx_Freeze/finder.py b/setup/installer/cx_Freeze/cx_Freeze/finder.py new file mode 100644 index 0000000000..f815db97be --- /dev/null +++ b/setup/installer/cx_Freeze/cx_Freeze/finder.py @@ -0,0 +1,455 @@ +""" +Base class for finding modules. +""" + +import dis +import imp +import marshal +import new +import opcode +import os +import sys +import zipfile + +import cx_Freeze.hooks + +BUILD_LIST = opcode.opmap["BUILD_LIST"] +INPLACE_ADD = opcode.opmap["INPLACE_ADD"] +LOAD_CONST = opcode.opmap["LOAD_CONST"] +IMPORT_NAME = opcode.opmap["IMPORT_NAME"] +IMPORT_FROM = opcode.opmap["IMPORT_FROM"] +STORE_NAME = opcode.opmap["STORE_NAME"] +STORE_GLOBAL = opcode.opmap["STORE_GLOBAL"] +STORE_OPS = (STORE_NAME, STORE_GLOBAL) + +__all__ = [ "Module", "ModuleFinder" ] + +class ModuleFinder(object): + + def __init__(self, includeFiles, excludes, path, replacePaths): + self.includeFiles = includeFiles + self.excludes = dict.fromkeys(excludes) + self.replacePaths = replacePaths + self.path = path or sys.path + self.modules = [] + self.aliases = {} + self._modules = dict.fromkeys(excludes) + self._builtinModules = dict.fromkeys(sys.builtin_module_names) + self._badModules = {} + self._zipFileEntries = {} + self._zipFiles = {} + cx_Freeze.hooks.initialize(self) + + def _AddModule(self, name): + """Add a module to the list of modules but if one is already found, + then return it instead; this is done so that packages can be + handled properly.""" + module = self._modules.get(name) + if module is None: + module = self._modules[name] = Module(name) + self.modules.append(module) + if name in self._badModules: + del self._badModules[name] + return module + + def _DetermineParent(self, caller): + """Determine the parent to use when searching packages.""" + if caller is not None: + if caller.path is not None: + return caller + return self._GetParentByName(caller.name) + + def _EnsureFromList(self, caller, packageModule, fromList, + deferredImports): + """Ensure that the from list is satisfied. This is only necessary for + package modules. If the caller is the package itself, actually + attempt to import right then since it must be a submodule; otherwise + defer until after all global names are defined in order to avoid + spurious complaints about missing modules.""" + if caller is not packageModule: + deferredImports.append((packageModule, fromList)) + else: + if fromList == ("*",): + fromList = packageModule.allNames + for name in fromList: + if name in packageModule.globalNames: + continue + subModuleName = "%s.%s" % (packageModule.name, name) + self._ImportModule(subModuleName, deferredImports, caller) + + def _FindModule(self, name, path): + try: + return imp.find_module(name, path) + except ImportError: + if not path: + path = [] + for location in path: + if name in self._zipFileEntries: + break + if location in self._zipFiles: + continue + if os.path.isdir(location) or not zipfile.is_zipfile(location): + self._zipFiles[location] = None + continue + zip = zipfile.ZipFile(location) + for archiveName in zip.namelist(): + baseName, ext = os.path.splitext(archiveName) + if ext not in ('.pyc', '.pyo'): + continue + moduleName = ".".join(baseName.split("/")) + if moduleName in self._zipFileEntries: + continue + self._zipFileEntries[moduleName] = (zip, archiveName) + self._zipFiles[location] = None + info = self._zipFileEntries.get(name) + if info is not None: + zip, archiveName = info + fp = zip.read(archiveName) + info = (".pyc", "rb", imp.PY_COMPILED) + return fp, os.path.join(zip.filename, archiveName), info + raise + + def _GetParentByName(self, name): + """Return the parent module given the name of a module.""" + pos = name.rfind(".") + if pos > 0: + parentName = name[:pos] + return self._modules[parentName] + + def _ImportAllSubModules(self, module, deferredImports, recursive = True): + """Import all sub modules to the given package.""" + suffixes = dict.fromkeys([s[0] for s in imp.get_suffixes()]) + for dir in module.path: + try: + fileNames = os.listdir(dir) + except os.error: + continue + for fileName in fileNames: + name, ext = os.path.splitext(fileName) + if ext not in suffixes: + continue + if name == "__init__": + continue + subModuleName = "%s.%s" % (module.name, name) + subModule, returnError = \ + self._InternalImportModule(subModuleName, + deferredImports) + if returnError and subModule is None: + raise ImportError, "No module named %s" % subModuleName + module.globalNames[name] = None + if subModule.path and recursive: + self._ImportAllSubModules(subModule, deferredImports, + recursive) + + def _ImportDeferredImports(self, deferredImports): + """Import any sub modules that were deferred, if applicable.""" + while deferredImports: + newDeferredImports = [] + for packageModule, subModuleNames in deferredImports: + self._EnsureFromList(packageModule, packageModule, + subModuleNames, newDeferredImports) + deferredImports = newDeferredImports + + def _ImportModule(self, name, deferredImports, caller = None, + relativeImportIndex = 0): + """Attempt to find the named module and return it or None if no module + by that name could be found.""" + + # absolute import (available in Python 2.5 and up) + # the name given is the only name that will be searched + if relativeImportIndex == 0: + module, returnError = self._InternalImportModule(name, + deferredImports) + + # old style relative import (only possibility in Python 2.4 and prior) + # the name given is tried in all parents until a match is found and if + # no match is found, the global namespace is searched + elif relativeImportIndex < 0: + parent = self._DetermineParent(caller) + while parent is not None: + fullName = "%s.%s" % (parent.name, name) + module, returnError = self._InternalImportModule(fullName, + deferredImports) + if module is not None: + parent.globalNames[name] = None + return module + parent = self._GetParentByName(parent.name) + module, returnError = self._InternalImportModule(name, + deferredImports) + + # new style relative import (available in Python 2.5 and up) + # the index indicates how many levels to traverse and only that level + # is searched for the named module + elif relativeImportIndex > 0: + parent = caller + if parent.path is not None: + relativeImportIndex -= 1 + while parent is not None and relativeImportIndex > 0: + parent = self._GetParentByName(parent.name) + relativeImportIndex -= 1 + if parent is None: + module = None + returnError = True + elif not name: + module = parent + else: + name = "%s.%s" % (parent.name, name) + module, returnError = self._InternalImportModule(name, + deferredImports) + + # if module not found, track that fact + if module is None: + if caller is None: + raise ImportError, "No module named %s" % name + self._RunHook("missing", name, caller) + if returnError and name not in caller.ignoreNames: + callers = self._badModules.setdefault(name, {}) + callers[caller.name] = None + + return module + + def _InternalImportModule(self, name, deferredImports): + """Internal method used for importing a module which assumes that the + name given is an absolute name. None is returned if the module + cannot be found.""" + try: + return self._modules[name], False + except KeyError: + pass + if name in self._builtinModules: + module = self._AddModule(name) + self._RunHook("load", module.name, module) + return module, False + pos = name.rfind(".") + if pos < 0: + path = self.path + searchName = name + parentModule = None + else: + parentName = name[:pos] + parentModule, returnError = \ + self._InternalImportModule(parentName, deferredImports) + if parentModule is None: + return None, returnError + path = parentModule.path + searchName = name[pos + 1:] + if name in self.aliases: + actualName = self.aliases[name] + module, returnError = \ + self._InternalImportModule(actualName, deferredImports) + self._modules[name] = module + return module, returnError + try: + fp, path, info = self._FindModule(searchName, path) + except ImportError: + self._modules[name] = None + return None, True + module = self._LoadModule(name, fp, path, info, deferredImports, + parentModule) + return module, False + + def _LoadModule(self, name, fp, path, info, deferredImports, + parent = None): + """Load the module, given the information acquired by the finder.""" + suffix, mode, type = info + if type == imp.PKG_DIRECTORY: + return self._LoadPackage(name, path, parent, deferredImports) + module = self._AddModule(name) + module.file = path + module.parent = parent + if type == imp.PY_SOURCE: + module.code = compile(fp.read() + "\n", path, "exec") + elif type == imp.PY_COMPILED: + if isinstance(fp, str): + magic = fp[:4] + else: + magic = fp.read(4) + if magic != imp.get_magic(): + raise ImportError, "Bad magic number in %s" % path + if isinstance(fp, str): + module.code = marshal.loads(fp[8:]) + module.inZipFile = True + else: + fp.read(4) + module.code = marshal.load(fp) + self._RunHook("load", module.name, module) + if module.code is not None: + if self.replacePaths: + topLevelModule = module + while topLevelModule.parent is not None: + topLevelModule = topLevelModule.parent + module.code = self._ReplacePathsInCode(topLevelModule, + module.code) + self._ScanCode(module.code, module, deferredImports) + return module + + def _LoadPackage(self, name, path, parent, deferredImports): + """Load the package, given its name and path.""" + module = self._AddModule(name) + module.path = [path] + fp, path, info = imp.find_module("__init__", module.path) + self._LoadModule(name, fp, path, info, deferredImports, parent) + return module + + def _ReplacePathsInCode(self, topLevelModule, co): + """Replace paths in the code as directed, returning a new code object + with the modified paths in place.""" + origFileName = newFileName = os.path.normpath(co.co_filename) + for searchValue, replaceValue in self.replacePaths: + if searchValue == "*": + searchValue = os.path.dirname(topLevelModule.file) + if topLevelModule.path: + searchValue = os.path.dirname(searchValue) + if searchValue: + searchValue = searchValue + os.pathsep + elif not origFileName.startswith(searchValue): + continue + newFileName = replaceValue + origFileName[len(searchValue):] + break + constants = list(co.co_consts) + for i, value in enumerate(constants): + if isinstance(value, type(co)): + constants[i] = self._ReplacePathsInCode(topLevelModule, value) + return new.code(co.co_argcount, co.co_nlocals, co.co_stacksize, + co.co_flags, co.co_code, tuple(constants), co.co_names, + co.co_varnames, newFileName, co.co_name, co.co_firstlineno, + co.co_lnotab, co.co_freevars, co.co_cellvars) + + def _RunHook(self, hookName, moduleName, *args): + """Run hook for the given module if one is present.""" + name = "%s_%s" % (hookName, moduleName.replace(".", "_")) + method = getattr(cx_Freeze.hooks, name, None) + if method is not None: + method(self, *args) + + def _ScanCode(self, co, module, deferredImports): + """Scan code, looking for imported modules and keeping track of the + constants that have been created in order to better tell which + modules are truly missing.""" + opIndex = 0 + arguments = [] + code = co.co_code + numOps = len(code) + while opIndex < numOps: + op = ord(code[opIndex]) + opIndex += 1 + if op >= dis.HAVE_ARGUMENT: + opArg = ord(code[opIndex]) + ord(code[opIndex + 1]) * 256 + opIndex += 2 + if op == LOAD_CONST: + arguments.append(co.co_consts[opArg]) + elif op == IMPORT_NAME: + name = co.co_names[opArg] + if len(arguments) == 2: + relativeImportIndex, fromList = arguments + else: + relativeImportIndex = -1 + fromList, = arguments + if name not in module.excludeNames: + subModule = self._ImportModule(name, deferredImports, + module, relativeImportIndex) + if subModule is not None: + module.globalNames.update(subModule.globalNames) + if fromList and subModule.path is not None: + self._EnsureFromList(module, subModule, fromList, + deferredImports) + elif op == IMPORT_FROM: + opIndex += 3 + elif op not in (BUILD_LIST, INPLACE_ADD): + if op in STORE_OPS: + name = co.co_names[opArg] + if name == "__all__": + module.allNames.extend(arguments) + module.globalNames[name] = None + arguments = [] + for constant in co.co_consts: + if isinstance(constant, type(co)): + self._ScanCode(constant, module, deferredImports) + + def AddAlias(self, name, aliasFor): + """Add an alias for a particular module; when an attempt is made to + import a module using the alias name, import the actual name + instead.""" + self.aliases[name] = aliasFor + + def ExcludeModule(self, name): + """Exclude the named module from the resulting frozen executable.""" + self.excludes[name] = None + self._modules[name] = None + + def IncludeFile(self, path, moduleName = None): + """Include the named file as a module in the frozen executable.""" + name, ext = os.path.splitext(os.path.basename(path)) + if moduleName is None: + moduleName = name + info = (ext, "r", imp.PY_SOURCE) + deferredImports = [] + module = self._LoadModule(moduleName, file(path, "U"), path, info, + deferredImports) + self._ImportDeferredImports(deferredImports) + return module + + def IncludeFiles(self, sourcePath, targetPath): + """Include the files in the given directory in the target build.""" + self.includeFiles.append((sourcePath, targetPath)) + + def IncludeModule(self, name): + """Include the named module in the frozen executable.""" + deferredImports = [] + module = self._ImportModule(name, deferredImports) + self._ImportDeferredImports(deferredImports) + return module + + def IncludePackage(self, name): + """Include the named package and any submodules in the frozen + executable.""" + deferredImports = [] + module = self._ImportModule(name, deferredImports) + if module.path: + self._ImportAllSubModules(module, deferredImports) + self._ImportDeferredImports(deferredImports) + return module + + def ReportMissingModules(self): + if self._badModules: + print "Missing modules:" + names = self._badModules.keys() + names.sort() + for name in names: + callers = self._badModules[name].keys() + callers.sort() + print "?", name, "imported from", ", ".join(callers) + print + + +class Module(object): + + def __init__(self, name): + self.name = name + self.file = None + self.path = None + self.code = None + self.parent = None + self.globalNames = {} + self.excludeNames = {} + self.ignoreNames = {} + self.allNames = [] + self.inZipFile = False + + def __repr__(self): + parts = ["name=%s" % repr(self.name)] + if self.file is not None: + parts.append("file=%s" % repr(self.file)) + if self.path is not None: + parts.append("path=%s" % repr(self.path)) + return "" % ", ".join(parts) + + def AddGlobalName(self, name): + self.globalNames[name] = None + + def ExcludeName(self, name): + self.excludeNames[name] = None + + def IgnoreName(self, name): + self.ignoreNames[name] = None + diff --git a/setup/installer/cx_Freeze/cx_Freeze/freezer.py b/setup/installer/cx_Freeze/cx_Freeze/freezer.py new file mode 100644 index 0000000000..e0739e8a4e --- /dev/null +++ b/setup/installer/cx_Freeze/cx_Freeze/freezer.py @@ -0,0 +1,550 @@ +""" +Base class for freezing scripts into executables. +""" + +import datetime +import distutils.sysconfig +import imp +import marshal +import os +import shutil +import socket +import stat +import struct +import sys +import time +import zipfile + +import cx_Freeze +import cx_Freeze.util + +__all__ = [ "ConfigError", "ConstantsModule", "Executable", "Freezer" ] + +if sys.platform == "win32": + pythonDll = "python%s%s.dll" % sys.version_info[:2] + GLOBAL_BIN_PATH_EXCLUDES = [cx_Freeze.util.GetSystemDir()] + GLOBAL_BIN_INCLUDES = [ + pythonDll, + "gdiplus.dll", + "mfc71.dll", + "msvcp71.dll", + "msvcr71.dll" + ] + GLOBAL_BIN_EXCLUDES = [ + "comctl32.dll", + "oci.dll", + "cx_Logging.pyd" + ] +else: + extension = distutils.sysconfig.get_config_var("SO") + pythonSharedLib = "libpython%s.%s%s" % \ + (sys.version_info[:2] + (extension,)) + GLOBAL_BIN_INCLUDES = [pythonSharedLib] + GLOBAL_BIN_EXCLUDES = [ + "libclntsh.so", + "libwtc9.so" + ] + GLOBAL_BIN_PATH_EXCLUDES = ["/lib", "/lib32", "/lib64", "/usr/lib", + "/usr/lib32", "/usr/lib64"] + + +# NOTE: the try: except: block in this code is not necessary under Python 2.4 +# and higher and can be removed once support for Python 2.3 is no longer needed +EXTENSION_LOADER_SOURCE = \ +""" +import imp, os, sys + +found = False +for p in sys.path: + if not os.path.isdir(p): + continue + f = os.path.join(p, "%s") + if not os.path.exists(f): + continue + try: + m = imp.load_dynamic(__name__, f) + except ImportError: + del sys.modules[__name__] + raise + sys.modules[__name__] = m + found = True + break +if not found: + del sys.modules[__name__] + raise ImportError, "No module named %%s" %% __name__ +""" + + +class Freezer(object): + + def __init__(self, executables, constantsModules = [], includes = [], + excludes = [], packages = [], replacePaths = [], compress = None, + optimizeFlag = 0, copyDependentFiles = None, initScript = None, + base = None, path = None, createLibraryZip = None, + appendScriptToExe = None, appendScriptToLibrary = None, + targetDir = None, binIncludes = [], binExcludes = [], + binPathIncludes = [], binPathExcludes = [], icon = None, + includeFiles = []): + self.executables = executables + self.constantsModules = constantsModules + self.includes = includes + self.excludes = excludes + self.packages = packages + self.replacePaths = replacePaths + self.compress = compress + self.optimizeFlag = optimizeFlag + self.copyDependentFiles = copyDependentFiles + self.initScript = initScript + self.base = base + self.path = path + self.createLibraryZip = createLibraryZip + self.appendScriptToExe = appendScriptToExe + self.appendScriptToLibrary = appendScriptToLibrary + self.targetDir = targetDir + self.binIncludes = [os.path.normcase(n) \ + for n in GLOBAL_BIN_INCLUDES + binIncludes] + self.binExcludes = [os.path.normcase(n) \ + for n in GLOBAL_BIN_EXCLUDES + binExcludes] + self.binPathIncludes = [os.path.normcase(n) for n in binPathIncludes] + self.binPathExcludes = [os.path.normcase(n) \ + for n in GLOBAL_BIN_PATH_EXCLUDES + binPathExcludes] + self.icon = icon + self.includeFiles = includeFiles + self._VerifyConfiguration() + + def _CopyFile(self, source, target, copyDependentFiles, + includeMode = False): + normalizedSource = os.path.normcase(os.path.normpath(source)) + normalizedTarget = os.path.normcase(os.path.normpath(target)) + if normalizedTarget in self.filesCopied: + return + if normalizedSource == normalizedTarget: + return + self._RemoveFile(target) + targetDir = os.path.dirname(target) + self._CreateDirectory(targetDir) + print "copying", source, "->", target + shutil.copyfile(source, target) + if includeMode: + shutil.copymode(source, target) + self.filesCopied[normalizedTarget] = None + if copyDependentFiles: + for source in self._GetDependentFiles(source): + target = os.path.join(targetDir, os.path.basename(source)) + self._CopyFile(source, target, copyDependentFiles) + + def _CreateDirectory(self, path): + if not os.path.isdir(path): + print "creating directory", path + os.makedirs(path) + + def _FreezeExecutable(self, exe): + if self.createLibraryZip: + finder = self.finder + else: + finder = self._GetModuleFinder(exe) + if exe.script is None: + scriptModule = None + else: + scriptModule = finder.IncludeFile(exe.script, exe.moduleName) + self._CopyFile(exe.base, exe.targetName, exe.copyDependentFiles, + includeMode = True) + if exe.icon is not None: + if sys.platform == "win32": + cx_Freeze.util.AddIcon(exe.targetName, exe.icon) + else: + targetName = os.path.join(os.path.dirname(exe.targetName), + os.path.basename(exe.icon)) + self._CopyFile(exe.icon, targetName, + copyDependentFiles = False) + if not os.access(exe.targetName, os.W_OK): + mode = os.stat(exe.targetName).st_mode + os.chmod(exe.targetName, mode | stat.S_IWUSR) + if not exe.appendScriptToLibrary: + if exe.appendScriptToExe: + fileName = exe.targetName + else: + baseFileName, ext = os.path.splitext(exe.targetName) + fileName = baseFileName + ".zip" + self._RemoveFile(fileName) + if not self.createLibraryZip and exe.copyDependentFiles: + scriptModule = None + self._WriteModules(fileName, exe.initScript, finder, exe.compress, + exe.copyDependentFiles, scriptModule) + + def _GetBaseFileName(self, argsSource = None): + if argsSource is None: + argsSource = self + name = argsSource.base + if name is None: + if argsSource.copyDependentFiles: + name = "Console" + else: + name = "ConsoleKeepPath" + argsSource.base = self._GetFileName("bases", name) + if argsSource.base is None: + raise ConfigError("no base named %s", name) + + def _GetDependentFiles(self, path): + dependentFiles = self.dependentFiles.get(path) + if dependentFiles is None: + if sys.platform == "win32": + origPath = os.environ["PATH"] + os.environ["PATH"] = origPath + os.pathsep + \ + os.pathsep.join(sys.path) + dependentFiles = cx_Freeze.util.GetDependentFiles(path) + os.environ["PATH"] = origPath + else: + dependentFiles = [] + for line in os.popen('ldd "%s"' % path): + parts = line.strip().split(" => ") + if len(parts) != 2: + continue + dependentFile = parts[1] + if dependentFile == "not found": + print "WARNING: cannot find", parts[0] + continue + pos = dependentFile.find(" (") + if pos >= 0: + dependentFile = dependentFile[:pos].strip() + if dependentFile: + dependentFiles.append(dependentFile) + dependentFiles = self.dependentFiles[path] = \ + [f for f in dependentFiles if self._ShouldCopyFile(f)] + return dependentFiles + + def _GetFileName(self, dir, name): + if os.path.isabs(name): + return name + name = os.path.normcase(name) + fullDir = os.path.join(os.path.dirname(cx_Freeze.__file__), dir) + if os.path.isdir(fullDir): + for fileName in os.listdir(fullDir): + if name == os.path.splitext(os.path.normcase(fileName))[0]: + return os.path.join(fullDir, fileName) + + def _GetInitScriptFileName(self, argsSource = None): + if argsSource is None: + argsSource = self + name = argsSource.initScript + if name is None: + if argsSource.copyDependentFiles: + name = "Console" + else: + name = "ConsoleKeepPath" + argsSource.initScript = self._GetFileName("initscripts", name) + if argsSource.initScript is None: + raise ConfigError("no initscript named %s", name) + + def _GetModuleFinder(self, argsSource = None): + if argsSource is None: + argsSource = self + finder = cx_Freeze.ModuleFinder(self.includeFiles, argsSource.excludes, + argsSource.path, argsSource.replacePaths) + if argsSource.copyDependentFiles: + finder.IncludeModule("imp") + finder.IncludeModule("os") + finder.IncludeModule("sys") + if argsSource.compress: + finder.IncludeModule("zlib") + for name in argsSource.includes: + finder.IncludeModule(name) + for name in argsSource.packages: + finder.IncludePackage(name) + return finder + + def _PrintReport(self, fileName, modules): + print "writing zip file", fileName + print + print " %-25s %s" % ("Name", "File") + print " %-25s %s" % ("----", "----") + for module in modules: + if module.path: + print "P", + else: + print "m", + print "%-25s" % module.name, module.file or "" + print + + def _RemoveFile(self, path): + if os.path.exists(path): + os.chmod(path, 0777) + os.remove(path) + + def _ShouldCopyFile(self, path): + dir, name = os.path.split(os.path.normcase(path)) + parts = name.split(".") + tweaked = False + while True: + if not parts[-1].isdigit(): + break + parts.pop(-1) + tweaked = True + if tweaked: + name = ".".join(parts) + if name in self.binIncludes: + return True + if name in self.binExcludes: + return False + for path in self.binPathIncludes: + if dir.startswith(path): + return True + for path in self.binPathExcludes: + if dir.startswith(path): + return False + return True + + def _VerifyCanAppendToLibrary(self): + if not self.createLibraryZip: + raise ConfigError("script cannot be appended to library zip if " + "one is not being created") + + def _VerifyConfiguration(self): + if self.compress is None: + self.compress = True + if self.copyDependentFiles is None: + self.copyDependentFiles = True + if self.createLibraryZip is None: + self.createLibraryZip = True + if self.appendScriptToExe is None: + self.appendScriptToExe = False + if self.appendScriptToLibrary is None: + self.appendScriptToLibrary = \ + self.createLibraryZip and not self.appendScriptToExe + if self.targetDir is None: + self.targetDir = os.path.abspath("dist") + self._GetInitScriptFileName() + self._GetBaseFileName() + if self.path is None: + self.path = sys.path + if self.appendScriptToLibrary: + self._VerifyCanAppendToLibrary() + for sourceFileName, targetFileName in self.includeFiles: + if not os.path.exists(sourceFileName): + raise ConfigError("cannot find file/directory named %s", + sourceFileName) + if os.path.isabs(targetFileName): + raise ConfigError("target file/directory cannot be absolute") + for executable in self.executables: + executable._VerifyConfiguration(self) + + def _WriteModules(self, fileName, initScript, finder, compress, + copyDependentFiles, scriptModule = None): + initModule = finder.IncludeFile(initScript, "cx_Freeze__init__") + if scriptModule is None: + for module in self.constantsModules: + module.Create(finder) + modules = [m for m in finder.modules \ + if m.name not in self.excludeModules] + else: + modules = [initModule, scriptModule] + self.excludeModules[initModule.name] = None + self.excludeModules[scriptModule.name] = None + itemsToSort = [(m.name, m) for m in modules] + itemsToSort.sort() + modules = [m for n, m in itemsToSort] + self._PrintReport(fileName, modules) + if scriptModule is None: + finder.ReportMissingModules() + targetDir = os.path.dirname(fileName) + self._CreateDirectory(targetDir) + filesToCopy = [] + if os.path.exists(fileName): + mode = "a" + else: + mode = "w" + outFile = zipfile.PyZipFile(fileName, mode, zipfile.ZIP_DEFLATED) + for module in modules: + if module.code is None and module.file is not None: + fileName = os.path.basename(module.file) + baseFileName, ext = os.path.splitext(fileName) + if baseFileName != module.name and module.name != "zlib": + if "." in module.name: + fileName = module.name + ext + generatedFileName = "ExtensionLoader_%s.py" % \ + module.name.replace(".", "_") + module.code = compile(EXTENSION_LOADER_SOURCE % fileName, + generatedFileName, "exec") + target = os.path.join(targetDir, fileName) + filesToCopy.append((module, target)) + if module.code is None: + continue + fileName = "/".join(module.name.split(".")) + if module.path: + fileName += "/__init__" + if module.file is not None and os.path.exists(module.file): + mtime = os.stat(module.file).st_mtime + else: + mtime = time.time() + zipTime = time.localtime(mtime)[:6] + data = imp.get_magic() + struct.pack("" % self.script + + def _VerifyConfiguration(self, freezer): + if self.path is None: + self.path = freezer.path + if self.targetDir is None: + self.targetDir = freezer.targetDir + if self.includes is None: + self.includes = freezer.includes + if self.excludes is None: + self.excludes = freezer.excludes + if self.packages is None: + self.packages = freezer.packages + if self.replacePaths is None: + self.replacePaths = freezer.replacePaths + if self.compress is None: + self.compress = freezer.compress + if self.copyDependentFiles is None: + self.copyDependentFiles = freezer.copyDependentFiles + if self.appendScriptToExe is None: + self.appendScriptToExe = freezer.appendScriptToExe + if self.appendScriptToLibrary is None: + self.appendScriptToLibrary = freezer.appendScriptToLibrary + if self.initScript is None: + self.initScript = freezer.initScript + else: + freezer._GetInitScriptFileName(self) + if self.base is None: + self.base = freezer.base + else: + freezer._GetBaseFileName(self) + if self.appendScriptToLibrary: + freezer._VerifyCanAppendToLibrary() + if self.icon is None: + self.icon = freezer.icon + if self.script is not None: + name, ext = os.path.splitext(os.path.basename(self.script)) + if self.appendScriptToLibrary: + self.moduleName = "%s__main__" % os.path.normcase(name) + else: + self.moduleName = "__main__" + if self.targetName is None: + baseName, ext = os.path.splitext(self.base) + self.targetName = name + ext + self.targetName = os.path.join(self.targetDir, self.targetName) + + +class ConstantsModule(object): + + def __init__(self, releaseString = None, copyright = None, + moduleName = "BUILD_CONSTANTS", timeFormat = "%B %d, %Y %H:%M:%S"): + self.moduleName = moduleName + self.timeFormat = timeFormat + self.values = {} + self.values["BUILD_RELEASE_STRING"] = releaseString + self.values["BUILD_COPYRIGHT"] = copyright + + def Create(self, finder): + """Create the module which consists of declaration statements for each + of the values.""" + today = datetime.datetime.today() + sourceTimestamp = 0 + for module in finder.modules: + if module.file is None: + continue + if module.inZipFile: + continue + if not os.path.exists(module.file): + raise ConfigError("no file named %s", module.file) + timestamp = os.stat(module.file).st_mtime + sourceTimestamp = max(sourceTimestamp, timestamp) + sourceTimestamp = datetime.datetime.fromtimestamp(sourceTimestamp) + self.values["BUILD_TIMESTAMP"] = today.strftime(self.timeFormat) + self.values["BUILD_HOST"] = socket.gethostname().split(".")[0] + self.values["SOURCE_TIMESTAMP"] = \ + sourceTimestamp.strftime(self.timeFormat) + module = finder._AddModule(self.moduleName) + sourceParts = [] + names = self.values.keys() + names.sort() + for name in names: + value = self.values[name] + sourceParts.append("%s = %r" % (name, value)) + source = "\n".join(sourceParts) + module.code = compile(source, "%s.py" % self.moduleName, "exec") + diff --git a/setup/installer/cx_Freeze/cx_Freeze/hooks.py b/setup/installer/cx_Freeze/cx_Freeze/hooks.py new file mode 100644 index 0000000000..edc2f78788 --- /dev/null +++ b/setup/installer/cx_Freeze/cx_Freeze/hooks.py @@ -0,0 +1,281 @@ +import os +import sys + +def initialize(finder): + """upon initialization of the finder, this routine is called to set up some + automatic exclusions for various platforms.""" + finder.ExcludeModule("FCNTL") + finder.ExcludeModule("os.path") + if os.name == "nt": + finder.ExcludeModule("fcntl") + finder.ExcludeModule("grp") + finder.ExcludeModule("pwd") + finder.ExcludeModule("termios") + else: + finder.ExcludeModule("_winreg") + finder.ExcludeModule("msilib") + finder.ExcludeModule("msvcrt") + finder.ExcludeModule("nt") + if os.name not in ("os2", "ce"): + finder.ExcludeModule("ntpath") + finder.ExcludeModule("nturl2path") + finder.ExcludeModule("pythoncom") + finder.ExcludeModule("pywintypes") + finder.ExcludeModule("winerror") + finder.ExcludeModule("winsound") + finder.ExcludeModule("win32api") + finder.ExcludeModule("win32con") + finder.ExcludeModule("win32event") + finder.ExcludeModule("win32file") + finder.ExcludeModule("win32pdh") + finder.ExcludeModule("win32pipe") + finder.ExcludeModule("win32process") + finder.ExcludeModule("win32security") + finder.ExcludeModule("win32service") + finder.ExcludeModule("wx.activex") + if os.name != "posix": + finder.ExcludeModule("posix") + if os.name != "mac": + finder.ExcludeModule("Carbon") + finder.ExcludeModule("gestalt") + finder.ExcludeModule("ic") + finder.ExcludeModule("mac") + finder.ExcludeModule("MacOS") + finder.ExcludeModule("macpath") + finder.ExcludeModule("macurl2path") + if os.name != "nt": + finder.ExcludeModule("EasyDialogs") + if os.name != "os2": + finder.ExcludeModule("os2") + finder.ExcludeModule("os2emxpath") + finder.ExcludeModule("_emx_link") + if os.name != "ce": + finder.ExcludeModule("ce") + if os.name != "riscos": + finder.ExcludeModule("riscos") + finder.ExcludeModule("riscosenviron") + finder.ExcludeModule("riscospath") + finder.ExcludeModule("rourl2path") + if sys.platform[:4] != "java": + finder.ExcludeModule("java.lang") + finder.ExcludeModule("org.python.core") + + +def load_cElementTree(finder, module): + """the cElementTree module implicitly loads the elementtree.ElementTree + module; make sure this happens.""" + finder.IncludeModule("elementtree.ElementTree") + + +def load_ceODBC(finder, module): + """the ceODBC module implicitly imports both datetime and decimal; make + sure this happens.""" + finder.IncludeModule("datetime") + finder.IncludeModule("decimal") + + +def load_cx_Oracle(finder, module): + """the cx_Oracle module implicitly imports datetime; make sure this + happens.""" + finder.IncludeModule("datetime") + + +def load_docutils_frontend(finder, module): + """The optik module is the old name for the optparse module; ignore the + module if it cannot be found.""" + module.IgnoreName("optik") + + +def load_dummy_threading(finder, module): + """the dummy_threading module plays games with the name of the threading + module for its own purposes; ignore that here""" + finder.ExcludeModule("_dummy_threading") + + +def load_email(finder, module): + """the email package has a bunch of aliases as the submodule names were + all changed to lowercase in Python 2.5; mimic that here.""" + if sys.version_info[:2] >= (2, 5): + for name in ("Charset", "Encoders", "Errors", "FeedParser", + "Generator", "Header", "Iterators", "Message", "Parser", + "Utils", "base64MIME", "quopriMIME"): + finder.AddAlias("email.%s" % name, "email.%s" % name.lower()) + + +def load_ftplib(finder, module): + """the ftplib module attempts to import the SOCKS module; ignore this + module if it cannot be found""" + module.IgnoreName("SOCKS") + + +def load_matplotlib(finder, module): + """the matplotlib module requires data to be found in mpl-data in the + same directory as the frozen executable so oblige it""" + dir = os.path.join(module.path[0], "mpl-data") + finder.IncludeFiles(dir, "mpl-data") + + +def load_matplotlib_numerix(finder, module): + """the numpy.numerix module loads a number of modules dynamically""" + for name in ("ma", "fft", "linear_algebra", "random_array", "mlab"): + finder.IncludeModule("%s.%s" % (module.name, name)) + + +def load_numpy_linalg(finder, module): + """the numpy.linalg module implicitly loads the lapack_lite module; make + sure this happens""" + finder.IncludeModule("numpy.linalg.lapack_lite") + + +def load_pty(finder, module): + """The sgi module is not needed for this module to function.""" + module.IgnoreName("sgi") + + +def load_pythoncom(finder, module): + """the pythoncom module is actually contained in a DLL but since those + cannot be loaded directly in Python 2.5 and higher a special module is + used to perform that task; simply use that technique directly to + determine the name of the DLL and ensure it is included as a normal + extension; also load the pywintypes module which is implicitly + loaded.""" + import pythoncom + module.file = pythoncom.__file__ + module.code = None + finder.IncludeModule("pywintypes") + + +def load_pywintypes(finder, module): + """the pywintypes module is actually contained in a DLL but since those + cannot be loaded directly in Python 2.5 and higher a special module is + used to perform that task; simply use that technique directly to + determine the name of the DLL and ensure it is included as a normal + extension.""" + import pywintypes + module.file = pywintypes.__file__ + module.code = None + + +def load_PyQt4_Qt(finder, module): + """the PyQt4.Qt module is an extension module which imports a number of + other modules and injects their namespace into its own. It seems a + foolish way of doing things but perhaps there is some hidden advantage + to this technique over pure Python; ignore the absence of some of + the modules since not every installation includes all of them.""" + finder.IncludeModule("PyQt4.QtCore") + finder.IncludeModule("PyQt4.QtGui") + finder.IncludeModule("sip") + for name in ("PyQt4.QtSvg", "PyQt4.Qsci", "PyQt4.QtAssistant", + "PyQt4.QtNetwork", "PyQt4.QtOpenGL", "PyQt4.QtScript", "PyQt4._qt", + "PyQt4.QtSql", "PyQt4.QtSvg", "PyQt4.QtTest", "PyQt4.QtXml"): + try: + finder.IncludeModule(name) + except ImportError: + pass + + +def load_Tkinter(finder, module): + """the Tkinter module has data files that are required to be loaded so + ensure that they are copied into the directory that is expected at + runtime.""" + import Tkinter + import _tkinter + tk = _tkinter.create() + tclDir = os.path.dirname(tk.call("info", "library")) + tclSourceDir = os.path.join(tclDir, "tcl%s" % _tkinter.TCL_VERSION) + tkSourceDir = os.path.join(tclDir, "tk%s" % _tkinter.TK_VERSION) + finder.IncludeFiles(tclSourceDir, "tcl") + finder.IncludeFiles(tkSourceDir, "tk") + + +def load_tempfile(finder, module): + """the tempfile module attempts to load the fcntl and thread modules but + continues if these modules cannot be found; ignore these modules if they + cannot be found.""" + module.IgnoreName("fcntl") + module.IgnoreName("thread") + + +def load_time(finder, module): + """the time module implicitly loads _strptime; make sure this happens.""" + finder.IncludeModule("_strptime") + + +def load_win32api(finder, module): + """the win32api module implicitly loads the pywintypes module; make sure + this happens.""" + finder.IncludeModule("pywintypes") + + +def load_win32com(finder, module): + """the win32com package manipulates its search path at runtime to include + the sibling directory called win32comext; simulate that by changing the + search path in a similar fashion here.""" + baseDir = os.path.dirname(os.path.dirname(module.file)) + module.path.append(os.path.join(baseDir, "win32comext")) + + +def load_win32file(finder, module): + """the win32api module implicitly loads the pywintypes module; make sure + this happens.""" + finder.IncludeModule("pywintypes") + + +def load_xml(finder, module): + """the builtin xml package attempts to load the _xmlplus module to see if + that module should take its role instead; ignore the failure to find + this module, though.""" + module.IgnoreName("_xmlplus") + + +def load_xml_etree_cElementTree(finder, module): + """the xml.etree.cElementTree module implicitly loads the + xml.etree.ElementTree module; make sure this happens.""" + finder.IncludeModule("xml.etree.ElementTree") + +def load_IPython(finder, module): + ipy = os.path.join(os.path.dirname(module.file), 'Extensions') + extensions = set([]) + for m in os.listdir(ipy): + extensions.add(os.path.splitext(m)[0]) + extensions.remove('__init__') + for m in extensions: + finder.IncludeModule('IPython.Extensions.'+m) + +def load_lxml(finder, module): + finder.IncludeModule('lxml._elementpath') + +def load_cherrypy(finder, module): + finder.IncludeModule('cherrypy.lib.encoding') + +def missing_cElementTree(finder, caller): + """the cElementTree has been incorporated into the standard library in + Python 2.5 so ignore its absence if it cannot found.""" + if sys.version_info[:2] >= (2, 5): + caller.IgnoreName("cElementTree") + + +def missing_EasyDialogs(finder, caller): + """the EasyDialogs module is not normally present on Windows but it also + may be so instead of excluding it completely, ignore it if it can't be + found""" + if sys.platform == "win32": + caller.IgnoreName("EasyDialogs") + + +def missing_readline(finder, caller): + """the readline module is not normally present on Windows but it also may + be so instead of excluding it completely, ignore it if it can't be + found""" + if sys.platform == "win32": + caller.IgnoreName("readline") + + +def missing_xml_etree(finder, caller): + """the xml.etree package is new for Python 2.5 but it is common practice + to use a try..except.. block in order to support versions earlier than + Python 2.5 transparently; ignore the absence of the package in this + situation.""" + if sys.version_info[:2] < (2, 5): + caller.IgnoreName("xml.etree") + diff --git a/setup/installer/cx_Freeze/cx_Freeze/main.py b/setup/installer/cx_Freeze/cx_Freeze/main.py new file mode 100644 index 0000000000..1704c598d2 --- /dev/null +++ b/setup/installer/cx_Freeze/cx_Freeze/main.py @@ -0,0 +1,171 @@ +import optparse +import os +import shutil +import stat +import sys + +import cx_Freeze + +__all__ = ["main"] + +USAGE = \ +""" +%prog [options] [SCRIPT] + +Freeze a Python script and all of its referenced modules to a base +executable which can then be distributed without requiring a Python +installation.""" + +VERSION = \ +""" +%%prog %s +Copyright (c) 2007-2008 Colt Engineering. All rights reserved. +Copyright (c) 2001-2006 Computronix Corporation. All rights reserved.""" % \ + cx_Freeze.version + + +def ParseCommandLine(): + parser = optparse.OptionParser(version = VERSION.strip(), + usage = USAGE.strip()) + parser.add_option("-O", + action = "count", + default = 0, + dest = "optimized", + help = "optimize generated bytecode as per PYTHONOPTIMIZE; " + "use -OO in order to remove doc strings") + parser.add_option("-c", "--compress", + action = "store_true", + dest = "compress", + help = "compress byte code in zip files") + parser.add_option("--base-name", + dest = "baseName", + metavar = "NAME", + help = "file on which to base the target file; if the name of the " + "file is not an absolute file name, the subdirectory bases " + "(rooted in the directory in which the freezer is found) " + "will be searched for a file matching the name") + parser.add_option("--init-script", + dest = "initScript", + metavar = "NAME", + help = "script which will be executed upon startup; if the name " + "of the file is not an absolute file name, the " + "subdirectory initscripts (rooted in the directory in " + "which the cx_Freeze package is found) will be searched " + "for a file matching the name") + parser.add_option("--target-dir", "--install-dir", + dest = "targetDir", + metavar = "DIR", + help = "the directory in which to place the target file and " + "any dependent files") + parser.add_option("--target-name", + dest = "targetName", + metavar = "NAME", + help = "the name of the file to create instead of the base name " + "of the script and the extension of the base binary") + parser.add_option("--no-copy-deps", + dest = "copyDeps", + default = True, + action = "store_false", + help = "do not copy the dependent files (extensions, shared " + "libraries, etc.) to the target directory; this also " + "modifies the default init script to ConsoleKeepPath.py " + "and means that the target executable requires a Python " + "installation to execute properly") + parser.add_option("--default-path", + action = "append", + dest = "defaultPath", + metavar = "DIRS", + help = "list of paths separated by the standard path separator " + "for the platform which will be used to initialize " + "sys.path prior to running the module finder") + parser.add_option("--include-path", + action = "append", + dest = "includePath", + metavar = "DIRS", + help = "list of paths separated by the standard path separator " + "for the platform which will be used to modify sys.path " + "prior to running the module finder") + parser.add_option("--replace-paths", + dest = "replacePaths", + metavar = "DIRECTIVES", + help = "replace all the paths in modules found in the given paths " + "with the given replacement string; multiple values are " + "separated by the standard path separator and each value " + "is of the form path=replacement_string; path can be * " + "which means all paths not already specified") + parser.add_option("--include-modules", + dest = "includeModules", + metavar = "NAMES", + help = "comma separated list of modules to include") + parser.add_option("--exclude-modules", + dest = "excludeModules", + metavar = "NAMES", + help = "comma separated list of modules to exclude") + parser.add_option("--ext-list-file", + dest = "extListFile", + metavar = "NAME", + help = "name of file in which to place the list of dependent files " + "which were copied into the target directory") + parser.add_option("-z", "--zip-include", + dest = "zipIncludes", + action = "append", + default = [], + metavar = "SPEC", + help = "name of file to add to the zip file or a specification of " + "the form name=arcname which will specify the archive name " + "to use; multiple --zip-include arguments can be used") + options, args = parser.parse_args() + if len(args) == 0: + options.script = None + elif len(args) == 1: + options.script, = args + else: + parser.error("only one script can be specified") + if not args and options.includeModules is None and options.copyDeps: + parser.error("script or a list of modules must be specified") + if not args and options.targetName is None: + parser.error("script or a target name must be specified") + if options.excludeModules: + options.excludeModules = options.excludeModules.split(",") + else: + options.excludeModules = [] + if options.includeModules: + options.includeModules = options.includeModules.split(",") + else: + options.includeModules = [] + replacePaths = [] + if options.replacePaths: + for directive in options.replacePaths.split(os.pathsep): + fromPath, replacement = directive.split("=") + replacePaths.append((fromPath, replacement)) + options.replacePaths = replacePaths + if options.defaultPath is not None: + sys.path = [p for mp in options.defaultPath \ + for p in mp.split(os.pathsep)] + if options.includePath is not None: + paths = [p for mp in options.includePath for p in mp.split(os.pathsep)] + sys.path = paths + sys.path + if options.script is not None: + sys.path.insert(0, os.path.dirname(options.script)) + return options + + +def main(): + options = ParseCommandLine() + executables = [cx_Freeze.Executable(options.script, + targetName = options.targetName)] + freezer = cx_Freeze.Freezer(executables, + includes = options.includeModules, + excludes = options.excludeModules, + replacePaths = options.replacePaths, + compress = options.compress, + optimizeFlag = options.optimized, + copyDependentFiles = options.copyDeps, + initScript = options.initScript, + base = options.baseName, + path = None, + createLibraryZip = False, + appendScriptToExe = True, + targetDir = options.targetDir) + freezer.Freeze() + diff --git a/setup/installer/cx_Freeze/cx_Freeze/windist.py b/setup/installer/cx_Freeze/cx_Freeze/windist.py new file mode 100644 index 0000000000..51af544771 --- /dev/null +++ b/setup/installer/cx_Freeze/cx_Freeze/windist.py @@ -0,0 +1,337 @@ +import distutils.command.bdist_msi +import msilib +import os + +__all__ = [ "bdist_msi" ] + +# force the remove existing products action to happen first since Windows +# installer appears to be braindead and doesn't handle files shared between +# different "products" very well +sequence = msilib.sequence.InstallExecuteSequence +for index, info in enumerate(sequence): + if info[0] == u'RemoveExistingProducts': + sequence[index] = (info[0], info[1], 1450) + + +class bdist_msi(distutils.command.bdist_msi.bdist_msi): + user_options = distutils.command.bdist_msi.bdist_msi.user_options + [ + ('add-to-path=', None, 'add target dir to PATH environment variable'), + ('upgrade-code=', None, 'upgrade code to use') + ] + x = y = 50 + width = 370 + height = 300 + title = "[ProductName] Setup" + modeless = 1 + modal = 3 + + def add_config(self, fullname): + initialTargetDir = self.get_initial_target_dir(fullname) + if self.add_to_path is None: + self.add_to_path = False + for executable in self.distribution.executables: + if os.path.basename(executable.base).startswith("Console"): + self.add_to_path = True + break + if self.add_to_path: + msilib.add_data(self.db, 'Environment', + [("E_PATH", "Path", r"[~];[TARGETDIR]", "TARGETDIR")]) + msilib.add_data(self.db, 'CustomAction', + [("InitialTargetDir", 256 + 51, "TARGETDIR", initialTargetDir) + ]) + msilib.add_data(self.db, 'InstallExecuteSequence', + [("InitialTargetDir", 'TARGETDIR=""', 401)]) + msilib.add_data(self.db, 'InstallUISequence', + [("PrepareDlg", None, 140), + ("InitialTargetDir", 'TARGETDIR=""', 401), + ("SelectDirectoryDlg", "not Installed", 1230), + ("MaintenanceTypeDlg", + "Installed and not Resume and not Preselected", 1250), + ("ProgressDlg", None, 1280) + ]) + + def add_cancel_dialog(self): + dialog = msilib.Dialog(self.db, "CancelDlg", 50, 10, 260, 85, 3, + self.title, "No", "No", "No") + dialog.text("Text", 48, 15, 194, 30, 3, + "Are you sure you want to cancel [ProductName] installation?") + button = dialog.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No") + button.event("EndDialog", "Exit") + button = dialog.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes") + button.event("EndDialog", "Return") + + def add_error_dialog(self): + dialog = msilib.Dialog(self.db, "ErrorDlg", 50, 10, 330, 101, 65543, + self.title, "ErrorText", None, None) + dialog.text("ErrorText", 50, 9, 280, 48, 3, "") + for text, x in [("No", 120), ("Yes", 240), ("Abort", 0), + ("Cancel", 42), ("Ignore", 81), ("Ok", 159), ("Retry", 198)]: + button = dialog.pushbutton(text[0], x, 72, 81, 21, 3, text, None) + button.event("EndDialog", "Error%s" % text) + + def add_exit_dialog(self): + dialog = distutils.command.bdist_msi.PyDialog(self.db, "ExitDialog", + self.x, self.y, self.width, self.height, self.modal, + self.title, "Finish", "Finish", "Finish") + dialog.title("Completing the [ProductName] installer") + dialog.back("< Back", "Finish", active = False) + dialog.cancel("Cancel", "Back", active = False) + dialog.text("Description", 15, 235, 320, 20, 0x30003, + "Click the Finish button to exit the installer.") + button = dialog.next("Finish", "Cancel", name = "Finish") + button.event("EndDialog", "Return") + + def add_fatal_error_dialog(self): + dialog = distutils.command.bdist_msi.PyDialog(self.db, "FatalError", + self.x, self.y, self.width, self.height, self.modal, + self.title, "Finish", "Finish", "Finish") + dialog.title("[ProductName] installer ended prematurely") + dialog.back("< Back", "Finish", active = False) + dialog.cancel("Cancel", "Back", active = False) + dialog.text("Description1", 15, 70, 320, 80, 0x30003, + "[ProductName] setup ended prematurely because of an error. " + "Your system has not been modified. To install this program " + "at a later time, please run the installation again.") + dialog.text("Description2", 15, 155, 320, 20, 0x30003, + "Click the Finish button to exit the installer.") + button = dialog.next("Finish", "Cancel", name = "Finish") + button.event("EndDialog", "Exit") + + def add_files_in_use_dialog(self): + dialog = distutils.command.bdist_msi.PyDialog(self.db, "FilesInUse", + self.x, self.y, self.width, self.height, 19, self.title, + "Retry", "Retry", "Retry", bitmap = False) + dialog.text("Title", 15, 6, 200, 15, 0x30003, + r"{\DlgFontBold8}Files in Use") + dialog.text("Description", 20, 23, 280, 20, 0x30003, + "Some files that need to be updated are currently in use.") + dialog.text("Text", 20, 55, 330, 50, 3, + "The following applications are using files that need to be " + "updated by this setup. Close these applications and then " + "click Retry to continue the installation or Cancel to exit " + "it.") + dialog.control("List", "ListBox", 20, 107, 330, 130, 7, + "FileInUseProcess", None, None, None) + button = dialog.back("Exit", "Ignore", name = "Exit") + button.event("EndDialog", "Exit") + button = dialog.next("Ignore", "Retry", name = "Ignore") + button.event("EndDialog", "Ignore") + button = dialog.cancel("Retry", "Exit", name = "Retry") + button.event("EndDialog", "Retry") + + def add_maintenance_type_dialog(self): + dialog = distutils.command.bdist_msi.PyDialog(self.db, + "MaintenanceTypeDlg", self.x, self.y, self.width, self.height, + self.modal, self.title, "Next", "Next", "Cancel") + dialog.title("Welcome to the [ProductName] Setup Wizard") + dialog.text("BodyText", 15, 63, 330, 42, 3, + "Select whether you want to repair or remove [ProductName].") + group = dialog.radiogroup("RepairRadioGroup", 15, 108, 330, 60, 3, + "MaintenanceForm_Action", "", "Next") + group.add("Repair", 0, 18, 300, 17, "&Repair [ProductName]") + group.add("Remove", 0, 36, 300, 17, "Re&move [ProductName]") + dialog.back("< Back", None, active = False) + button = dialog.next("Finish", "Cancel") + button.event("[REINSTALL]", "ALL", + 'MaintenanceForm_Action="Repair"', 5) + button.event("[Progress1]", "Repairing", + 'MaintenanceForm_Action="Repair"', 6) + button.event("[Progress2]", "repairs", + 'MaintenanceForm_Action="Repair"', 7) + button.event("Reinstall", "ALL", + 'MaintenanceForm_Action="Repair"', 8) + button.event("[REMOVE]", "ALL", + 'MaintenanceForm_Action="Remove"', 11) + button.event("[Progress1]", "Removing", + 'MaintenanceForm_Action="Remove"', 12) + button.event("[Progress2]", "removes", + 'MaintenanceForm_Action="Remove"', 13) + button.event("Remove", "ALL", + 'MaintenanceForm_Action="Remove"', 14) + button.event("EndDialog", "Return", + 'MaintenanceForm_Action<>"Change"', 20) + button = dialog.cancel("Cancel", "RepairRadioGroup") + button.event("SpawnDialog", "CancelDlg") + + def add_prepare_dialog(self): + dialog = distutils.command.bdist_msi.PyDialog(self.db, "PrepareDlg", + self.x, self.y, self.width, self.height, self.modeless, + self.title, "Cancel", "Cancel", "Cancel") + dialog.text("Description", 15, 70, 320, 40, 0x30003, + "Please wait while the installer prepares to guide you through" + "the installation.") + dialog.title("Welcome to the [ProductName] installer") + text = dialog.text("ActionText", 15, 110, 320, 20, 0x30003, + "Pondering...") + text.mapping("ActionText", "Text") + text = dialog.text("ActionData", 15, 135, 320, 30, 0x30003, None) + text.mapping("ActionData", "Text") + dialog.back("Back", None, active = False) + dialog.next("Next", None, active = False) + button = dialog.cancel("Cancel", None) + button.event("SpawnDialog", "CancelDlg") + + def add_progress_dialog(self): + dialog = distutils.command.bdist_msi.PyDialog(self.db, "ProgressDlg", + self.x, self.y, self.width, self.height, self.modeless, + self.title, "Cancel", "Cancel", "Cancel", bitmap = False) + dialog.text("Title", 20, 15, 200, 15, 0x30003, + r"{\DlgFontBold8}[Progress1] [ProductName]") + dialog.text("Text", 35, 65, 300, 30, 3, + "Please wait while the installer [Progress2] [ProductName].") + dialog.text("StatusLabel", 35, 100 ,35, 20, 3, "Status:") + text = dialog.text("ActionText", 70, 100, self.width - 70, 20, 3, + "Pondering...") + text.mapping("ActionText", "Text") + control = dialog.control("ProgressBar", "ProgressBar", 35, 120, 300, + 10, 65537, None, "Progress done", None, None) + control.mapping("SetProgress", "Progress") + dialog.back("< Back", "Next", active = False) + dialog.next("Next >", "Cancel", active = False) + button = dialog.cancel("Cancel", "Back") + button.event("SpawnDialog", "CancelDlg") + + def add_properties(self): + metadata = self.distribution.metadata + props = [ + ('DistVersion', metadata.get_version()), + ('DefaultUIFont', 'DlgFont8'), + ('ErrorDialog', 'ErrorDlg'), + ('Progress1', 'Install'), + ('Progress2', 'installs'), + ('MaintenanceForm_Action', 'Repair') + ] + email = metadata.author_email or metadata.maintainer_email + if email: + props.append(("ARPCONTACT", email)) + if metadata.url: + props.append(("ARPURLINFOABOUT", metadata.url)) + if self.upgrade_code is not None: + props.append(("UpgradeCode", self.upgrade_code)) + msilib.add_data(self.db, 'Property', props) + + def add_select_directory_dialog(self): + dialog = distutils.command.bdist_msi.PyDialog(self.db, + "SelectDirectoryDlg", self.x, self.y, self.width, self.height, + self.modal, self.title, "Next", "Next", "Cancel") + dialog.title("Select destination directory") + dialog.back("< Back", None, active = False) + button = dialog.next("Next >", "Cancel") + button.event("SetTargetPath", "TARGETDIR", ordering = 1) + button.event("SpawnWaitDialog", "WaitForCostingDlg", ordering = 2) + button.event("EndDialog", "Return", ordering = 3) + button = dialog.cancel("Cancel", "DirectoryCombo") + button.event("SpawnDialog", "CancelDlg") + dialog.control("DirectoryCombo", "DirectoryCombo", 15, 70, 272, 80, + 393219, "TARGETDIR", None, "DirectoryList", None) + dialog.control("DirectoryList", "DirectoryList", 15, 90, 308, 136, 3, + "TARGETDIR", None, "PathEdit", None) + dialog.control("PathEdit", "PathEdit", 15, 230, 306, 16, 3, + "TARGETDIR", None, "Next", None) + button = dialog.pushbutton("Up", 306, 70, 18, 18, 3, "Up", None) + button.event("DirectoryListUp", "0") + button = dialog.pushbutton("NewDir", 324, 70, 30, 18, 3, "New", None) + button.event("DirectoryListNew", "0") + + def add_text_styles(self): + msilib.add_data(self.db, 'TextStyle', + [("DlgFont8", "Tahoma", 9, None, 0), + ("DlgFontBold8", "Tahoma", 8, None, 1), + ("VerdanaBold10", "Verdana", 10, None, 1), + ("VerdanaRed9", "Verdana", 9, 255, 0) + ]) + + def add_ui(self): + self.add_text_styles() + self.add_error_dialog() + self.add_fatal_error_dialog() + self.add_cancel_dialog() + self.add_exit_dialog() + self.add_user_exit_dialog() + self.add_files_in_use_dialog() + self.add_wait_for_costing_dialog() + self.add_prepare_dialog() + self.add_select_directory_dialog() + self.add_progress_dialog() + self.add_maintenance_type_dialog() + + def add_upgrade_config(self, sversion): + if self.upgrade_code is not None: + msilib.add_data(self.db, 'Upgrade', + [(self.upgrade_code, None, sversion, None, 513, None, + "REMOVEOLDVERSION"), + (self.upgrade_code, sversion, None, None, 257, None, + "REMOVENEWVERSION") + ]) + + def add_user_exit_dialog(self): + dialog = distutils.command.bdist_msi.PyDialog(self.db, "UserExit", + self.x, self.y, self.width, self.height, self.modal, + self.title, "Finish", "Finish", "Finish") + dialog.title("[ProductName] installer was interrupted") + dialog.back("< Back", "Finish", active = False) + dialog.cancel("Cancel", "Back", active = False) + dialog.text("Description1", 15, 70, 320, 80, 0x30003, + "[ProductName] setup was interrupted. Your system has not " + "been modified. To install this program at a later time, " + "please run the installation again.") + dialog.text("Description2", 15, 155, 320, 20, 0x30003, + "Click the Finish button to exit the installer.") + button = dialog.next("Finish", "Cancel", name = "Finish") + button.event("EndDialog", "Exit") + + def add_wait_for_costing_dialog(self): + dialog = msilib.Dialog(self.db, "WaitForCostingDlg", 50, 10, 260, 85, + self.modal, self.title, "Return", "Return", "Return") + dialog.text("Text", 48, 15, 194, 30, 3, + "Please wait while the installer finishes determining your " + "disk space requirements.") + button = dialog.pushbutton("Return", 102, 57, 56, 17, 3, "Return", + None) + button.event("EndDialog", "Exit") + + def get_initial_target_dir(self, fullname): + return r"[ProgramFilesFolder]\%s" % fullname + + def get_installer_filename(self, fullname): + return os.path.join(self.dist_dir, "%s.msi" % fullname) + + def initialize_options(self): + distutils.command.bdist_msi.bdist_msi.initialize_options(self) + self.upgrade_code = None + self.add_to_path = None + + def run(self): + if not self.skip_build: + self.run_command('build') + install = self.reinitialize_command('install', reinit_subcommands = 1) + install.prefix = self.bdist_dir + install.skip_build = self.skip_build + install.warn_dir = 0 + distutils.log.info("installing to %s", self.bdist_dir) + install.ensure_finalized() + install.run() + self.mkpath(self.dist_dir) + fullname = self.distribution.get_fullname() + filename = os.path.abspath(self.get_installer_filename(fullname)) + if os.path.exists(filename): + os.unlink(filename) + metadata = self.distribution.metadata + author = metadata.author or metadata.maintainer or "UNKNOWN" + version = metadata.get_version() + sversion = "%d.%d.%d" % \ + distutils.version.StrictVersion(version).version + self.db = msilib.init_database(filename, msilib.schema, + self.distribution.metadata.name, msilib.gen_uuid(), sversion, + author) + msilib.add_tables(self.db, msilib.sequence) + self.add_properties() + self.add_config(fullname) + self.add_upgrade_config(sversion) + self.add_ui() + self.add_files() + self.db.Commit() + if not self.keep_temp: + distutils.dir_util.remove_tree(self.bdist_dir, + dry_run = self.dry_run) + diff --git a/setup/installer/cx_Freeze/cxfreeze b/setup/installer/cx_Freeze/cxfreeze new file mode 100755 index 0000000000..acd6789833 --- /dev/null +++ b/setup/installer/cx_Freeze/cxfreeze @@ -0,0 +1,6 @@ +#!/usr/bin/python + +from cx_Freeze import main + +main() + diff --git a/setup/installer/cx_Freeze/initscripts/Console.py b/setup/installer/cx_Freeze/initscripts/Console.py new file mode 100755 index 0000000000..e10649d722 --- /dev/null +++ b/setup/installer/cx_Freeze/initscripts/Console.py @@ -0,0 +1,35 @@ +#------------------------------------------------------------------------------ +# Console.py +# Initialization script for cx_Freeze which manipulates the path so that the +# directory in which the executable is found is searched for extensions but +# no other directory is searched. It also sets the attribute sys.frozen so that +# the Win32 extensions behave as expected. +#------------------------------------------------------------------------------ + +import encodings +import os +import sys +import warnings +import zipimport + +sys.frozen = True +sys.path = sys.path[:4] + +os.environ["TCL_LIBRARY"] = os.path.join(DIR_NAME, "tcl") +os.environ["TK_LIBRARY"] = os.path.join(DIR_NAME, "tk") + +m = __import__("__main__") +importer = zipimport.zipimporter(INITSCRIPT_ZIP_FILE_NAME) +if INITSCRIPT_ZIP_FILE_NAME != SHARED_ZIP_FILE_NAME: + moduleName = m.__name__ +else: + name, ext = os.path.splitext(os.path.basename(os.path.normcase(FILE_NAME))) + moduleName = "%s__main__" % name +code = importer.get_code(moduleName) +exec code in m.__dict__ + +if sys.version_info[:2] >= (2, 5): + module = sys.modules.get("threading") + if module is not None: + module._shutdown() + diff --git a/setup/installer/cx_Freeze/initscripts/ConsoleKeepPath.py b/setup/installer/cx_Freeze/initscripts/ConsoleKeepPath.py new file mode 100755 index 0000000000..60151a1ff6 --- /dev/null +++ b/setup/installer/cx_Freeze/initscripts/ConsoleKeepPath.py @@ -0,0 +1,19 @@ +#------------------------------------------------------------------------------ +# ConsoleKeepPath.py +# Initialization script for cx_Freeze which leaves the path alone and does +# not set the sys.frozen attribute. +#------------------------------------------------------------------------------ + +import sys +import zipimport + +m = __import__("__main__") +importer = zipimport.zipimporter(INITSCRIPT_ZIP_FILE_NAME) +code = importer.get_code(m.__name__) +exec code in m.__dict__ + +if sys.version_info[:2] >= (2, 5): + module = sys.modules.get("threading") + if module is not None: + module._shutdown() + diff --git a/setup/installer/cx_Freeze/initscripts/ConsoleSetLibPath.py b/setup/installer/cx_Freeze/initscripts/ConsoleSetLibPath.py new file mode 100755 index 0000000000..b558652c0a --- /dev/null +++ b/setup/installer/cx_Freeze/initscripts/ConsoleSetLibPath.py @@ -0,0 +1,38 @@ +#------------------------------------------------------------------------------ +# ConsoleSetLibPath.py +# Initialization script for cx_Freeze which manipulates the path so that the +# directory in which the executable is found is searched for extensions but +# no other directory is searched. The environment variable LD_LIBRARY_PATH is +# manipulated first, however, to ensure that shared libraries found in the +# target directory are found. This requires a restart of the executable because +# the environment variable LD_LIBRARY_PATH is only checked at startup. +#------------------------------------------------------------------------------ + +import encodings +import os +import sys +import warnings +import zipimport + +paths = os.environ.get("LD_LIBRARY_PATH", "").split(os.pathsep) +if DIR_NAME not in paths: + paths.insert(0, DIR_NAME) + os.environ["LD_LIBRARY_PATH"] = os.pathsep.join(paths) + os.execv(sys.executable, sys.argv) + +sys.frozen = True +sys.path = sys.path[:4] + +os.environ["TCL_LIBRARY"] = os.path.join(DIR_NAME, "tcl") +os.environ["TK_LIBRARY"] = os.path.join(DIR_NAME, "tk") + +m = __import__("__main__") +importer = zipimport.zipimporter(INITSCRIPT_ZIP_FILE_NAME) +code = importer.get_code(m.__name__) +exec code in m.__dict__ + +if sys.version_info[:2] >= (2, 5): + module = sys.modules.get("threading") + if module is not None: + module._shutdown() + diff --git a/setup/installer/cx_Freeze/initscripts/SharedLib.py b/setup/installer/cx_Freeze/initscripts/SharedLib.py new file mode 100755 index 0000000000..0445367010 --- /dev/null +++ b/setup/installer/cx_Freeze/initscripts/SharedLib.py @@ -0,0 +1,20 @@ +#------------------------------------------------------------------------------ +# SharedLib.py +# Initialization script for cx_Freeze which behaves similarly to the one for +# console based applications but must handle the case where Python has already +# been initialized and another DLL of this kind has been loaded. As such it +# does not block the path unless sys.frozen is not already set. +#------------------------------------------------------------------------------ + +import encodings +import os +import sys +import warnings + +if not hasattr(sys, "frozen"): + sys.frozen = True + sys.path = sys.path[:4] + +os.environ["TCL_LIBRARY"] = os.path.join(DIR_NAME, "tcl") +os.environ["TK_LIBRARY"] = os.path.join(DIR_NAME, "tk") + diff --git a/setup/installer/cx_Freeze/initscripts/SharedLibSource.py b/setup/installer/cx_Freeze/initscripts/SharedLibSource.py new file mode 100755 index 0000000000..3edae93694 --- /dev/null +++ b/setup/installer/cx_Freeze/initscripts/SharedLibSource.py @@ -0,0 +1,23 @@ +#------------------------------------------------------------------------------ +# SharedLibSource.py +# Initialization script for cx_Freeze which imports the site module (as per +# normal processing of a Python script) and then searches for a file with the +# same name as the shared library but with the extension .pth. The entries in +# this file are used to modify the path to use for subsequent imports. +#------------------------------------------------------------------------------ + +import os +import sys +import warnings + +# the site module must be imported for normal behavior to take place; it is +# done dynamically so that cx_Freeze will not add all modules referenced by +# the site module to the frozen executable +__import__("site") + +# now locate the pth file to modify the path appropriately +baseName, ext = os.path.splitext(FILE_NAME) +pathFileName = baseName + ".pth" +sys.path = [s.strip() for s in file(pathFileName).read().splitlines()] + \ + sys.path + diff --git a/setup/installer/cx_Freeze/samples/advanced/advanced_1.py b/setup/installer/cx_Freeze/samples/advanced/advanced_1.py new file mode 100644 index 0000000000..2f1b68bceb --- /dev/null +++ b/setup/installer/cx_Freeze/samples/advanced/advanced_1.py @@ -0,0 +1,7 @@ +import sys + +print "Hello from cx_Freeze Advanced #1" +print + +module = __import__("testfreeze_1") + diff --git a/setup/installer/cx_Freeze/samples/advanced/advanced_2.py b/setup/installer/cx_Freeze/samples/advanced/advanced_2.py new file mode 100644 index 0000000000..1a6fe37e62 --- /dev/null +++ b/setup/installer/cx_Freeze/samples/advanced/advanced_2.py @@ -0,0 +1,7 @@ +import sys + +print "Hello from cx_Freeze Advanced #2" +print + +module = __import__("testfreeze_2") + diff --git a/setup/installer/cx_Freeze/samples/advanced/modules/testfreeze_1.py b/setup/installer/cx_Freeze/samples/advanced/modules/testfreeze_1.py new file mode 100644 index 0000000000..6157b72a69 --- /dev/null +++ b/setup/installer/cx_Freeze/samples/advanced/modules/testfreeze_1.py @@ -0,0 +1 @@ +print "Test freeze module #1" diff --git a/setup/installer/cx_Freeze/samples/advanced/modules/testfreeze_2.py b/setup/installer/cx_Freeze/samples/advanced/modules/testfreeze_2.py new file mode 100644 index 0000000000..ca133a7d58 --- /dev/null +++ b/setup/installer/cx_Freeze/samples/advanced/modules/testfreeze_2.py @@ -0,0 +1 @@ +print "Test freeze module #2" diff --git a/setup/installer/cx_Freeze/samples/advanced/setup.py b/setup/installer/cx_Freeze/samples/advanced/setup.py new file mode 100644 index 0000000000..3a79cf23af --- /dev/null +++ b/setup/installer/cx_Freeze/samples/advanced/setup.py @@ -0,0 +1,31 @@ +# An advanced setup script to create multiple executables and demonstrate a few +# of the features available to setup scripts +# +# hello.py is a very simple "Hello, world" type script which also displays the +# environment in which the script runs +# +# Run the build process by running the command 'python setup.py build' +# +# If everything works well you should find a subdirectory in the build +# subdirectory that contains the files needed to run the script without Python + +import sys +from cx_Freeze import setup, Executable + +executables = [ + Executable("advanced_1.py"), + Executable("advanced_2.py") +] + +buildOptions = dict( + compressed = True, + includes = ["testfreeze_1", "testfreeze_2"], + path = sys.path + ["modules"]) + +setup( + name = "advanced_cx_Freeze_sample", + version = "0.1", + description = "Advanced sample cx_Freeze script", + options = dict(build_exe = buildOptions), + executables = executables) + diff --git a/setup/installer/cx_Freeze/samples/matplotlib/setup.py b/setup/installer/cx_Freeze/samples/matplotlib/setup.py new file mode 100644 index 0000000000..54bd97fc3f --- /dev/null +++ b/setup/installer/cx_Freeze/samples/matplotlib/setup.py @@ -0,0 +1,27 @@ +# A simple setup script to create an executable using matplotlib. +# +# test_matplotlib.py is a very simple matplotlib application that demonstrates +# its use. +# +# Run the build process by running the command 'python setup.py build' +# +# If everything works well you should find a subdirectory in the build +# subdirectory that contains the files needed to run the application + +import cx_Freeze +import sys + +base = None +if sys.platform == "win32": + base = "Win32GUI" + +executables = [ + cx_Freeze.Executable("test_matplotlib.py", base = base) +] + +cx_Freeze.setup( + name = "test_matplotlib", + version = "0.1", + description = "Sample matplotlib script", + executables = executables) + diff --git a/setup/installer/cx_Freeze/samples/matplotlib/test_matplotlib.py b/setup/installer/cx_Freeze/samples/matplotlib/test_matplotlib.py new file mode 100644 index 0000000000..2029845ca5 --- /dev/null +++ b/setup/installer/cx_Freeze/samples/matplotlib/test_matplotlib.py @@ -0,0 +1,48 @@ +from numpy import arange, sin, pi +import matplotlib +matplotlib.use('WXAgg') +from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas +from matplotlib.backends.backend_wx import NavigationToolbar2Wx +from matplotlib.figure import Figure +from wx import * + +class CanvasFrame(Frame): + def __init__(self): + Frame.__init__(self,None,-1, 'CanvasFrame',size=(550,350)) + self.SetBackgroundColour(NamedColor("WHITE")) + self.figure = Figure() + self.axes = self.figure.add_subplot(111) + t = arange(0.0,3.0,0.01) + s = sin(2*pi*t) + self.axes.plot(t,s) + self.canvas = FigureCanvas(self, -1, self.figure) + self.sizer = BoxSizer(VERTICAL) + self.sizer.Add(self.canvas, 1, LEFT | TOP | GROW) + self.SetSizerAndFit(self.sizer) + self.add_toolbar() + + def add_toolbar(self): + self.toolbar = NavigationToolbar2Wx(self.canvas) + self.toolbar.Realize() + if Platform == '__WXMAC__': + self.SetToolBar(self.toolbar) + else: + tw, th = self.toolbar.GetSizeTuple() + fw, fh = self.canvas.GetSizeTuple() + self.toolbar.SetSize(Size(fw, th)) + self.sizer.Add(self.toolbar, 0, LEFT | EXPAND) + self.toolbar.update() + + def OnPaint(self, event): + self.canvas.draw() + +class App(App): + def OnInit(self): + 'Create the main window and insert the custom frame' + frame = CanvasFrame() + frame.Show(True) + return True + +app = App(0) +app.MainLoop() + diff --git a/setup/installer/cx_Freeze/samples/relimport/pkg1/__init__.py b/setup/installer/cx_Freeze/samples/relimport/pkg1/__init__.py new file mode 100644 index 0000000000..5a170fd2dd --- /dev/null +++ b/setup/installer/cx_Freeze/samples/relimport/pkg1/__init__.py @@ -0,0 +1,3 @@ +print "importing pkg1" +from . import sub1 +from . import pkg2 diff --git a/setup/installer/cx_Freeze/samples/relimport/pkg1/pkg2/__init__.py b/setup/installer/cx_Freeze/samples/relimport/pkg1/pkg2/__init__.py new file mode 100644 index 0000000000..71e0b1fbe6 --- /dev/null +++ b/setup/installer/cx_Freeze/samples/relimport/pkg1/pkg2/__init__.py @@ -0,0 +1,3 @@ +print "importing pkg1.pkg2" +from . import sub3 +from .. import sub4 diff --git a/setup/installer/cx_Freeze/samples/relimport/pkg1/pkg2/sub3.py b/setup/installer/cx_Freeze/samples/relimport/pkg1/pkg2/sub3.py new file mode 100644 index 0000000000..1719aadb41 --- /dev/null +++ b/setup/installer/cx_Freeze/samples/relimport/pkg1/pkg2/sub3.py @@ -0,0 +1,3 @@ +print "importing pkg1.pkg2.sub3" +from . import sub5 +from .. import sub6 diff --git a/setup/installer/cx_Freeze/samples/relimport/pkg1/pkg2/sub5.py b/setup/installer/cx_Freeze/samples/relimport/pkg1/pkg2/sub5.py new file mode 100644 index 0000000000..1c91b8fa23 --- /dev/null +++ b/setup/installer/cx_Freeze/samples/relimport/pkg1/pkg2/sub5.py @@ -0,0 +1 @@ +print "importing pkg1.pkg2.sub5" diff --git a/setup/installer/cx_Freeze/samples/relimport/pkg1/sub1.py b/setup/installer/cx_Freeze/samples/relimport/pkg1/sub1.py new file mode 100644 index 0000000000..514bd88a87 --- /dev/null +++ b/setup/installer/cx_Freeze/samples/relimport/pkg1/sub1.py @@ -0,0 +1,2 @@ +print "importing pkg1.sub1" +from . import sub2 diff --git a/setup/installer/cx_Freeze/samples/relimport/pkg1/sub2.py b/setup/installer/cx_Freeze/samples/relimport/pkg1/sub2.py new file mode 100644 index 0000000000..63a0838b25 --- /dev/null +++ b/setup/installer/cx_Freeze/samples/relimport/pkg1/sub2.py @@ -0,0 +1 @@ +print "importing pkg1.sub2" diff --git a/setup/installer/cx_Freeze/samples/relimport/pkg1/sub4.py b/setup/installer/cx_Freeze/samples/relimport/pkg1/sub4.py new file mode 100644 index 0000000000..3a8e760e43 --- /dev/null +++ b/setup/installer/cx_Freeze/samples/relimport/pkg1/sub4.py @@ -0,0 +1 @@ +print 'importing pkg1.sub4' diff --git a/setup/installer/cx_Freeze/samples/relimport/pkg1/sub6.py b/setup/installer/cx_Freeze/samples/relimport/pkg1/sub6.py new file mode 100644 index 0000000000..1e7d7955d6 --- /dev/null +++ b/setup/installer/cx_Freeze/samples/relimport/pkg1/sub6.py @@ -0,0 +1 @@ +print "importing pkg1.sub6" diff --git a/setup/installer/cx_Freeze/samples/relimport/relimport.py b/setup/installer/cx_Freeze/samples/relimport/relimport.py new file mode 100644 index 0000000000..39cb50c91d --- /dev/null +++ b/setup/installer/cx_Freeze/samples/relimport/relimport.py @@ -0,0 +1 @@ +import pkg1 diff --git a/setup/installer/cx_Freeze/samples/relimport/setup.py b/setup/installer/cx_Freeze/samples/relimport/setup.py new file mode 100644 index 0000000000..b8b3f3853b --- /dev/null +++ b/setup/installer/cx_Freeze/samples/relimport/setup.py @@ -0,0 +1,16 @@ +# relimport.py is a very simple script that tests importing using relative +# imports (available in Python 2.5 and up) +# +# Run the build process by running the command 'python setup.py build' +# +# If everything works well you should find a subdirectory in the build +# subdirectory that contains the files needed to run the script without Python + +from cx_Freeze import setup, Executable + +setup( + name = "relimport", + version = "0.1", + description = "Sample cx_Freeze script for relative imports", + executables = [Executable("relimport.py")]) + diff --git a/setup/installer/cx_Freeze/samples/simple/hello.py b/setup/installer/cx_Freeze/samples/simple/hello.py new file mode 100644 index 0000000000..0fb32405bc --- /dev/null +++ b/setup/installer/cx_Freeze/samples/simple/hello.py @@ -0,0 +1,19 @@ +import sys + +print "Hello from cx_Freeze" +print + +print "sys.executable", sys.executable +print "sys.prefix", sys.prefix +print + +print "ARGUMENTS:" +for a in sys.argv: + print a +print + +print "PATH:" +for p in sys.path: + print p +print + diff --git a/setup/installer/cx_Freeze/samples/simple/setup.py b/setup/installer/cx_Freeze/samples/simple/setup.py new file mode 100644 index 0000000000..25de838b10 --- /dev/null +++ b/setup/installer/cx_Freeze/samples/simple/setup.py @@ -0,0 +1,18 @@ +# A very simple setup script to create a single executable +# +# hello.py is a very simple "Hello, world" type script which also displays the +# environment in which the script runs +# +# Run the build process by running the command 'python setup.py build' +# +# If everything works well you should find a subdirectory in the build +# subdirectory that contains the files needed to run the script without Python + +from cx_Freeze import setup, Executable + +setup( + name = "hello", + version = "0.1", + description = "Sample cx_Freeze script", + executables = [Executable("hello.py")]) + diff --git a/setup/installer/cx_Freeze/samples/wx/setup.py b/setup/installer/cx_Freeze/samples/wx/setup.py new file mode 100644 index 0000000000..9412996859 --- /dev/null +++ b/setup/installer/cx_Freeze/samples/wx/setup.py @@ -0,0 +1,25 @@ +# A simple setup script to create an executable running wxPython. This also +# demonstrates the method for creating a Windows executable that does not have +# an associated console. +# +# wxapp.py is a very simple "Hello, world" type wxPython application +# +# Run the build process by running the command 'python setup.py build' +# +# If everything works well you should find a subdirectory in the build +# subdirectory that contains the files needed to run the application + +import sys + +from cx_Freeze import setup, Executable + +base = None +if sys.platform == "win32": + base = "Win32GUI" + +setup( + name = "hello", + version = "0.1", + description = "Sample cx_Freeze wxPython script", + executables = [Executable("wxapp.py", base = base)]) + diff --git a/setup/installer/cx_Freeze/samples/wx/wxapp.py b/setup/installer/cx_Freeze/samples/wx/wxapp.py new file mode 100644 index 0000000000..7baa90b8d1 --- /dev/null +++ b/setup/installer/cx_Freeze/samples/wx/wxapp.py @@ -0,0 +1,42 @@ +import wx + +class Frame(wx.Frame): + + def __init__(self): + wx.Frame.__init__(self, parent = None, title = "Hello from cx_Freeze") + panel = wx.Panel(self) + closeMeButton = wx.Button(panel, -1, "Close Me") + wx.EVT_BUTTON(self, closeMeButton.GetId(), self.OnCloseMe) + wx.EVT_CLOSE(self, self.OnCloseWindow) + pushMeButton = wx.Button(panel, -1, "Push Me") + wx.EVT_BUTTON(self, pushMeButton.GetId(), self.OnPushMe) + sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer.Add(closeMeButton, flag = wx.ALL, border = 20) + sizer.Add(pushMeButton, flag = wx.ALL, border = 20) + panel.SetSizer(sizer) + topSizer = wx.BoxSizer(wx.VERTICAL) + topSizer.Add(panel, flag = wx.ALL | wx.EXPAND) + topSizer.Fit(self) + + def OnCloseMe(self, event): + self.Close(True) + + def OnPushMe(self, event): + 1 / 0 + + def OnCloseWindow(self, event): + self.Destroy() + + +class App(wx.App): + + def OnInit(self): + frame = Frame() + frame.Show(True) + self.SetTopWindow(frame) + return True + + +app = App(1) +app.MainLoop() + diff --git a/setup/installer/cx_Freeze/setup.py b/setup/installer/cx_Freeze/setup.py new file mode 100755 index 0000000000..b51453aa7a --- /dev/null +++ b/setup/installer/cx_Freeze/setup.py @@ -0,0 +1,197 @@ +""" +Distutils script for cx_Freeze. +""" + +import distutils.command.bdist_rpm +import distutils.command.build_ext +import distutils.command.build_scripts +import distutils.command.install +import distutils.command.install_data +import distutils.sysconfig +import os +import sys + +from distutils.core import setup +from distutils.extension import Extension + +class bdist_rpm(distutils.command.bdist_rpm.bdist_rpm): + + # rpm automatically byte compiles all Python files in a package but we + # don't want that to happen for initscripts and samples so we tell it to + # ignore those files + def _make_spec_file(self): + specFile = distutils.command.bdist_rpm.bdist_rpm._make_spec_file(self) + specFile.insert(0, "%define _unpackaged_files_terminate_build 0%{nil}") + return specFile + + def run(self): + distutils.command.bdist_rpm.bdist_rpm.run(self) + specFile = os.path.join(self.rpm_base, "SPECS", + "%s.spec" % self.distribution.get_name()) + queryFormat = "%{name}-%{version}-%{release}.%{arch}.rpm" + command = "rpm -q --qf '%s' --specfile %s" % (queryFormat, specFile) + origFileName = os.popen(command).read() + parts = origFileName.split("-") + parts.insert(2, "py%s%s" % sys.version_info[:2]) + newFileName = "-".join(parts) + self.move_file(os.path.join("dist", origFileName), + os.path.join("dist", newFileName)) + + +class build_ext(distutils.command.build_ext.build_ext): + + def build_extension(self, ext): + if ext.name.find("bases") < 0: + distutils.command.build_ext.build_ext.build_extension(self, ext) + return + os.environ["LD_RUN_PATH"] = "${ORIGIN}:${ORIGIN}/../lib" + objects = self.compiler.compile(ext.sources, + output_dir = self.build_temp, + include_dirs = ext.include_dirs, + debug = self.debug, + depends = ext.depends) + fileName = os.path.splitext(self.get_ext_filename(ext.name))[0] + fullName = os.path.join(self.build_lib, fileName) + libraryDirs = ext.library_dirs or [] + libraries = self.get_libraries(ext) + extraArgs = ext.extra_link_args or [] + if sys.platform != "win32": + vars = distutils.sysconfig.get_config_vars() + libraryDirs.append(vars["LIBPL"]) + libraries.append("python%s.%s" % sys.version_info[:2]) + if vars["LINKFORSHARED"]: + extraArgs.extend(vars["LINKFORSHARED"].split()) + if vars["LIBS"]: + extraArgs.extend(vars["LIBS"].split()) + if vars["LIBM"]: + extraArgs.append(vars["LIBM"]) + if vars["BASEMODLIBS"]: + extraArgs.extend(vars["BASEMODLIBS"].split()) + if vars["LOCALMODLIBS"]: + extraArgs.extend(vars["LOCALMODLIBS"].split()) + extraArgs.append("-s") + self.compiler.link_executable(objects, fullName, + libraries = libraries, + library_dirs = libraryDirs, + runtime_library_dirs = ext.runtime_library_dirs, + extra_postargs = extraArgs, + debug = self.debug) + + def get_ext_filename(self, name): + fileName = distutils.command.build_ext.build_ext.get_ext_filename(self, + name) + if name.find("bases") < 0: + return fileName + ext = self.compiler.exe_extension or "" + return os.path.splitext(fileName)[0] + ext + + +class build_scripts(distutils.command.build_scripts.build_scripts): + + def copy_scripts(self): + distutils.command.build_scripts.build_scripts.copy_scripts(self) + if sys.platform == "win32": + for script in self.scripts: + batFileName = os.path.join(self.build_dir, script + ".bat") + fullScriptName = r"%s\Scripts\%s" % \ + (os.path.dirname(sys.executable), script) + command = "%s %s %%1 %%2 %%3 %%4 %%5 %%6 %%7 %%8 %%9" % \ + (sys.executable, fullScriptName) + file(batFileName, "w").write("@echo off\n\n%s" % command) + + +class install(distutils.command.install.install): + + def get_sub_commands(self): + subCommands = distutils.command.install.install.get_sub_commands(self) + subCommands.append("install_packagedata") + return subCommands + + +class install_packagedata(distutils.command.install_data.install_data): + + def run(self): + installCommand = self.get_finalized_command("install") + installDir = getattr(installCommand, "install_lib") + sourceDirs = ["samples", "initscripts"] + while sourceDirs: + sourceDir = sourceDirs.pop(0) + targetDir = os.path.join(installDir, "cx_Freeze", sourceDir) + self.mkpath(targetDir) + for name in os.listdir(sourceDir): + if name == "build" or name.startswith("."): + continue + fullSourceName = os.path.join(sourceDir, name) + if os.path.isdir(fullSourceName): + sourceDirs.append(fullSourceName) + else: + fullTargetName = os.path.join(targetDir, name) + self.copy_file(fullSourceName, fullTargetName) + self.outfiles.append(fullTargetName) + + +commandClasses = dict( + build_ext = build_ext, + build_scripts = build_scripts, + bdist_rpm = bdist_rpm, + install = install, + install_packagedata = install_packagedata) + +if sys.platform == "win32": + libraries = ["imagehlp"] +else: + libraries = [] +utilModule = Extension("cx_Freeze.util", ["source/util.c"], + libraries = libraries) +depends = ["source/bases/Common.c"] +if sys.platform == "win32": + if sys.version_info[:2] >= (2, 6): + extraSources = ["source/bases/manifest.rc"] + else: + extraSources = ["source/bases/dummy.rc"] +else: + extraSources = [] +console = Extension("cx_Freeze.bases.Console", + ["source/bases/Console.c"] + extraSources, depends = depends) +consoleKeepPath = Extension("cx_Freeze.bases.ConsoleKeepPath", + ["source/bases/ConsoleKeepPath.c"] + extraSources, depends = depends) +extensions = [utilModule, console, consoleKeepPath] +if sys.platform == "win32": + gui = Extension("cx_Freeze.bases.Win32GUI", + ["source/bases/Win32GUI.c"] + extraSources, + depends = depends, extra_link_args = ["-mwindows"]) + extensions.append(gui) + +docFiles = "LICENSE.txt README.txt HISTORY.txt doc/cx_Freeze.html" + +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: Python Software Foundation License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: C", + "Programming Language :: Python", + "Topic :: Software Development :: Build Tools", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: System :: Software Distribution", + "Topic :: Utilities" +] + +setup(name = "cx_Freeze", + description = "create standalone executables from Python scripts", + long_description = "create standalone executables from Python scripts", + version = "4.0.1", + cmdclass = commandClasses, + options = dict(bdist_rpm = dict(doc_files = docFiles), + install = dict(optimize = 1)), + ext_modules = extensions, + packages = ['cx_Freeze'], + maintainer="Anthony Tuininga", + maintainer_email="anthony.tuininga@gmail.com", + url = "http://cx-freeze.sourceforge.net", + scripts = ["cxfreeze"], + classifiers = classifiers, + keywords = "freeze", + license = "Python Software Foundation License") + diff --git a/setup/installer/cx_Freeze/source/bases/Common.c b/setup/installer/cx_Freeze/source/bases/Common.c new file mode 100644 index 0000000000..ce1e137ebb --- /dev/null +++ b/setup/installer/cx_Freeze/source/bases/Common.c @@ -0,0 +1,262 @@ +//----------------------------------------------------------------------------- +// Common.c +// Routines which are common to running frozen executables. +//----------------------------------------------------------------------------- + +#include +#include +#include + +// global variables (used for simplicity) +static PyObject *g_FileName = NULL; +static PyObject *g_DirName = NULL; +static PyObject *g_ExclusiveZipFileName = NULL; +static PyObject *g_SharedZipFileName = NULL; +static PyObject *g_InitScriptZipFileName = NULL; + +//----------------------------------------------------------------------------- +// GetDirName() +// Return the directory name of the given path. +//----------------------------------------------------------------------------- +static int GetDirName( + const char *path, // path to calculate dir name for + PyObject **dirName) // directory name (OUT) +{ + int i; + + for (i = strlen(path); i > 0 && path[i] != SEP; --i); + *dirName = PyString_FromStringAndSize(path, i); + if (!*dirName) + return FatalError("cannot create string for directory name"); + return 0; +} + + +//----------------------------------------------------------------------------- +// SetExecutableName() +// Set the script to execute and calculate the directory in which the +// executable is found as well as the exclusive (only for this executable) and +// shared zip file names. +//----------------------------------------------------------------------------- +static int SetExecutableName( + const char *fileName) // script to execute +{ + char temp[MAXPATHLEN + 12], *ptr; +#ifndef WIN32 + char linkData[MAXPATHLEN + 1]; + struct stat statData; + size_t linkSize, i; + PyObject *dirName; +#endif + + // store file name + g_FileName = PyString_FromString(fileName); + if (!g_FileName) + return FatalError("cannot create string for file name"); + +#ifndef WIN32 + for (i = 0; i < 25; i++) { + if (lstat(fileName, &statData) < 0) { + PyErr_SetFromErrnoWithFilename(PyExc_OSError, (char*) fileName); + return FatalError("unable to stat file"); + } + if (!S_ISLNK(statData.st_mode)) + break; + linkSize = readlink(fileName, linkData, sizeof(linkData)); + if (linkSize < 0) { + PyErr_SetFromErrnoWithFilename(PyExc_OSError, (char*) fileName); + return FatalError("unable to stat file"); + } + if (linkData[0] == '/') { + Py_DECREF(g_FileName); + g_FileName = PyString_FromStringAndSize(linkData, linkSize); + } else { + if (GetDirName(PyString_AS_STRING(g_FileName), &dirName) < 0) + return -1; + if (PyString_GET_SIZE(dirName) + linkSize + 1 > MAXPATHLEN) { + Py_DECREF(dirName); + return FatalError("cannot dereference link, path too large"); + } + strcpy(temp, PyString_AS_STRING(dirName)); + strcat(temp, "/"); + strcat(temp, linkData); + Py_DECREF(g_FileName); + g_FileName = PyString_FromString(temp); + } + if (!g_FileName) + return FatalError("cannot create string for linked file name"); + fileName = PyString_AS_STRING(g_FileName); + } +#endif + + // calculate and store directory name + if (GetDirName(fileName, &g_DirName) < 0) + return -1; + + // calculate and store exclusive zip file name + strcpy(temp, fileName); + ptr = temp + strlen(temp) - 1; + while (ptr > temp && *ptr != SEP && *ptr != '.') + ptr--; + if (*ptr == '.') + *ptr = '\0'; + strcat(temp, ".zip"); + g_ExclusiveZipFileName = PyString_FromString(temp); + if (!g_ExclusiveZipFileName) + return FatalError("cannot create string for exclusive zip file name"); + + // calculate and store shared zip file name + strcpy(temp, PyString_AS_STRING(g_DirName)); + ptr = temp + strlen(temp); + *ptr++ = SEP; + strcpy(ptr, "library.zip"); + g_SharedZipFileName = PyString_FromString(temp); + if (!g_SharedZipFileName) + return FatalError("cannot create string for shared zip file name"); + + return 0; +} + + +//----------------------------------------------------------------------------- +// SetPathToSearch() +// Set the path to search. This includes the file (for those situations where +// a zip file is attached to the executable itself), the directory where the +// executable is found (to search for extensions), the exclusive zip file +// name and the shared zip file name. +//----------------------------------------------------------------------------- +static int SetPathToSearch(void) +{ + PyObject *pathList; + + pathList = PySys_GetObject("path"); + if (!pathList) + return FatalError("cannot acquire sys.path"); + if (PyList_Insert(pathList, 0, g_FileName) < 0) + return FatalError("cannot insert file name into sys.path"); + if (PyList_Insert(pathList, 1, g_DirName) < 0) + return FatalError("cannot insert directory name into sys.path"); + if (PyList_Insert(pathList, 2, g_ExclusiveZipFileName) < 0) + return FatalError("cannot insert exclusive zip name into sys.path"); + if (PyList_Insert(pathList, 3, g_SharedZipFileName) < 0) + return FatalError("cannot insert shared zip name into sys.path"); + return 0; +} + + +//----------------------------------------------------------------------------- +// GetImporterHelper() +// Helper which is used to locate the importer for the initscript. +//----------------------------------------------------------------------------- +static PyObject *GetImporterHelper( + PyObject *module, // zipimport module + PyObject *fileName) // name of file to search +{ + PyObject *importer; + + importer = PyObject_CallMethod(module, "zipimporter", "O", fileName); + if (importer) + g_InitScriptZipFileName = fileName; + else + PyErr_Clear(); + return importer; +} + + +//----------------------------------------------------------------------------- +// GetImporter() +// Return the importer which will be used for importing the initialization +// script. The executable itself is searched first, followed by the exclusive +// zip file and finally by the shared zip file. +//----------------------------------------------------------------------------- +static int GetImporter( + PyObject **importer) // importer (OUT) +{ + PyObject *module; + + module = PyImport_ImportModule("zipimport"); + if (!module) + return FatalError("cannot import zipimport module"); + *importer = GetImporterHelper(module, g_FileName); + if (!*importer) { + *importer = GetImporterHelper(module, g_ExclusiveZipFileName); + if (!*importer) + *importer = GetImporterHelper(module, g_SharedZipFileName); + } + Py_DECREF(module); + if (!*importer) + return FatalError("cannot get zipimporter instance"); + return 0; +} + + +//----------------------------------------------------------------------------- +// PopulateInitScriptDict() +// Return the dictionary used by the initialization script. +//----------------------------------------------------------------------------- +static int PopulateInitScriptDict( + PyObject *dict) // dictionary to populate +{ + if (!dict) + return FatalError("unable to create temporary dictionary"); + if (PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins()) < 0) + return FatalError("unable to set __builtins__"); + if (PyDict_SetItemString(dict, "FILE_NAME", g_FileName) < 0) + return FatalError("unable to set FILE_NAME"); + if (PyDict_SetItemString(dict, "DIR_NAME", g_DirName) < 0) + return FatalError("unable to set DIR_NAME"); + if (PyDict_SetItemString(dict, "EXCLUSIVE_ZIP_FILE_NAME", + g_ExclusiveZipFileName) < 0) + return FatalError("unable to set EXCLUSIVE_ZIP_FILE_NAME"); + if (PyDict_SetItemString(dict, "SHARED_ZIP_FILE_NAME", + g_SharedZipFileName) < 0) + return FatalError("unable to set SHARED_ZIP_FILE_NAME"); + if (PyDict_SetItemString(dict, "INITSCRIPT_ZIP_FILE_NAME", + g_InitScriptZipFileName) < 0) + return FatalError("unable to set INITSCRIPT_ZIP_FILE_NAME"); + return 0; +} + + + + +//----------------------------------------------------------------------------- +// ExecuteScript() +// Execute the script found within the file. +//----------------------------------------------------------------------------- +static int ExecuteScript( + const char *fileName) // name of file containing Python code +{ + PyObject *importer, *dict, *code, *temp; + + if (SetExecutableName(fileName) < 0) + return -1; + if (SetPathToSearch() < 0) + return -1; + importer = NULL; + if (GetImporter(&importer) < 0) + return -1; + + // create and populate dictionary for initscript module + dict = PyDict_New(); + if (PopulateInitScriptDict(dict) < 0) { + Py_XDECREF(dict); + Py_DECREF(importer); + return -1; + } + + // locate and execute script + code = PyObject_CallMethod(importer, "get_code", "s", "cx_Freeze__init__"); + Py_DECREF(importer); + if (!code) + return FatalError("unable to locate initialization module"); + temp = PyEval_EvalCode( (PyCodeObject*) code, dict, dict); + Py_DECREF(code); + Py_DECREF(dict); + if (!temp) + return FatalScriptError(); + Py_DECREF(temp); + + return 0; +} + diff --git a/setup/installer/cx_Freeze/source/bases/Console.c b/setup/installer/cx_Freeze/source/bases/Console.c new file mode 100644 index 0000000000..d6a8a515a0 --- /dev/null +++ b/setup/installer/cx_Freeze/source/bases/Console.c @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Console.c +// Main routine for frozen programs which run in a console. +//----------------------------------------------------------------------------- + +#include +#ifdef __WIN32__ +#include +#endif + +//----------------------------------------------------------------------------- +// FatalError() +// Prints a fatal error. +//----------------------------------------------------------------------------- +static int FatalError( + const char *message) // message to print +{ + PyErr_Print(); + Py_FatalError(message); + return -1; +} + + +//----------------------------------------------------------------------------- +// FatalScriptError() +// Prints a fatal error in the initialization script. +//----------------------------------------------------------------------------- +static int FatalScriptError(void) +{ + PyErr_Print(); + return -1; +} + + +#include "Common.c" + + +//----------------------------------------------------------------------------- +// main() +// Main routine for frozen programs. +//----------------------------------------------------------------------------- +int main(int argc, char **argv) +{ + const char *fileName; + char *encoding; + + // initialize Python + Py_NoSiteFlag = 1; + Py_FrozenFlag = 1; + Py_IgnoreEnvironmentFlag = 1; + + encoding = getenv("PYTHONIOENCODING"); + if (encoding != NULL) { + Py_FileSystemDefaultEncoding = strndup(encoding, 100); + } + + Py_SetPythonHome(""); + Py_SetProgramName(argv[0]); + fileName = Py_GetProgramFullPath(); + + Py_Initialize(); + PySys_SetArgv(argc, argv); + + + // do the work + if (ExecuteScript(fileName) < 0) + return 1; + + Py_Finalize(); + return 0; +} + diff --git a/setup/installer/cx_Freeze/source/bases/ConsoleKeepPath.c b/setup/installer/cx_Freeze/source/bases/ConsoleKeepPath.c new file mode 100644 index 0000000000..3ad00f8488 --- /dev/null +++ b/setup/installer/cx_Freeze/source/bases/ConsoleKeepPath.c @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// ConsoleKeepPath.c +// Main routine for frozen programs which need a Python installation to do +// their work. +//----------------------------------------------------------------------------- + +#include +#ifdef __WIN32__ +#include +#endif + +//----------------------------------------------------------------------------- +// FatalError() +// Prints a fatal error. +//----------------------------------------------------------------------------- +static int FatalError( + const char *message) // message to print +{ + PyErr_Print(); + Py_FatalError(message); + return -1; +} + + +//----------------------------------------------------------------------------- +// FatalScriptError() +// Prints a fatal error in the initialization script. +//----------------------------------------------------------------------------- +static int FatalScriptError(void) +{ + PyErr_Print(); + return -1; +} + + +#include "Common.c" + + +//----------------------------------------------------------------------------- +// main() +// Main routine for frozen programs. +//----------------------------------------------------------------------------- +int main(int argc, char **argv) +{ + const char *fileName; + + // initialize Python + Py_SetProgramName(argv[0]); + fileName = Py_GetProgramFullPath(); + Py_Initialize(); + PySys_SetArgv(argc, argv); + + // do the work + if (ExecuteScript(fileName) < 0) + return 1; + + Py_Finalize(); + return 0; +} + diff --git a/setup/installer/cx_Freeze/source/bases/Win32GUI.c b/setup/installer/cx_Freeze/source/bases/Win32GUI.c new file mode 100644 index 0000000000..f5bbe74dba --- /dev/null +++ b/setup/installer/cx_Freeze/source/bases/Win32GUI.c @@ -0,0 +1,242 @@ +//----------------------------------------------------------------------------- +// Win32GUI.c +// Main routine for frozen programs written for the Win32 GUI subsystem. +//----------------------------------------------------------------------------- + +#include +#include + +//----------------------------------------------------------------------------- +// FatalError() +// Handle a fatal error. +//----------------------------------------------------------------------------- +static int FatalError( + char *a_Message) // message to display +{ + MessageBox(NULL, a_Message, "cx_Freeze Fatal Error", MB_ICONERROR); + Py_Finalize(); + return -1; +} + + +//----------------------------------------------------------------------------- +// StringifyObject() +// Stringify a Python object. +//----------------------------------------------------------------------------- +static char *StringifyObject( + PyObject *object, // object to stringify + PyObject **stringRep) // string representation +{ + if (object) { + *stringRep = PyObject_Str(object); + if (*stringRep) + return PyString_AS_STRING(*stringRep); + return "Unable to stringify"; + } + + // object is NULL + *stringRep = NULL; + return "None"; +} + + +//----------------------------------------------------------------------------- +// FatalPythonErrorNoTraceback() +// Handle a fatal Python error without traceback. +//----------------------------------------------------------------------------- +static int FatalPythonErrorNoTraceback( + PyObject *origType, // exception type + PyObject *origValue, // exception value + char *message) // message to display +{ + PyObject *typeStrRep, *valueStrRep, *origTypeStrRep, *origValueStrRep; + char *totalMessage, *typeStr, *valueStr, *origTypeStr, *origValueStr; + PyObject *type, *value, *traceback; + int totalMessageLength; + char *messageFormat; + + // fetch error and string representations of the error + PyErr_Fetch(&type, &value, &traceback); + origTypeStr = StringifyObject(origType, &origTypeStrRep); + origValueStr = StringifyObject(origValue, &origValueStrRep); + typeStr = StringifyObject(type, &typeStrRep); + valueStr = StringifyObject(value, &valueStrRep); + + // fill out the message to be displayed + messageFormat = "Type: %s\nValue: %s\nOther Type: %s\nOtherValue: %s\n%s"; + totalMessageLength = strlen(origTypeStr) + strlen(origValueStr) + + strlen(typeStr) + strlen(valueStr) + strlen(message) + + strlen(messageFormat) + 1; + totalMessage = malloc(totalMessageLength); + if (!totalMessage) + return FatalError("Out of memory!"); + sprintf(totalMessage, messageFormat, typeStr, valueStr, origTypeStr, + origValueStr, message); + + // display the message + MessageBox(NULL, totalMessage, + "cx_Freeze: Python error in main script (traceback unavailable)", + MB_ICONERROR); + free(totalMessage); + return -1; +} + + +//----------------------------------------------------------------------------- +// ArgumentValue() +// Return a suitable argument value by replacing NULL with Py_None. +//----------------------------------------------------------------------------- +static PyObject *ArgumentValue( + PyObject *object) // argument to massage +{ + if (object) { + Py_INCREF(object); + return object; + } + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// HandleSystemExitException() +// Handles a system exit exception differently. If an integer value is passed +// through then that becomes the exit value; otherwise the string value of the +// value passed through is displayed in a message box. +//----------------------------------------------------------------------------- +static void HandleSystemExitException() +{ + PyObject *type, *value, *traceback, *valueStr; + int exitCode = 0; + char *message; + + PyErr_Fetch(&type, &value, &traceback); + if (PyInstance_Check(value)) { + PyObject *code = PyObject_GetAttrString(value, "code"); + if (code) { + Py_DECREF(value); + value = code; + if (value == Py_None) + Py_Exit(0); + } + } + if (PyInt_Check(value)) + exitCode = PyInt_AsLong(value); + else { + message = StringifyObject(value, &valueStr); + MessageBox(NULL, message, "cx_Freeze: Application Terminated", + MB_ICONERROR); + Py_XDECREF(valueStr); + exitCode = 1; + } + Py_Exit(exitCode); +} + + +//----------------------------------------------------------------------------- +// FatalScriptError() +// Handle a fatal Python error with traceback. +//----------------------------------------------------------------------------- +static int FatalScriptError() +{ + PyObject *type, *value, *traceback, *argsTuple, *module, *method, *result; + int tracebackLength, i; + char *tracebackStr; + + // if a system exception, handle it specially + if (PyErr_ExceptionMatches(PyExc_SystemExit)) + HandleSystemExitException(); + + // get the exception details + PyErr_Fetch(&type, &value, &traceback); + + // import the traceback module + module = PyImport_ImportModule("traceback"); + if (!module) + return FatalPythonErrorNoTraceback(type, value, + "Cannot import traceback module."); + + // get the format_exception method + method = PyObject_GetAttrString(module, "format_exception"); + Py_DECREF(module); + if (!method) + return FatalPythonErrorNoTraceback(type, value, + "Cannot get format_exception method."); + + // create a tuple for the arguments + argsTuple = PyTuple_New(3); + if (!argsTuple) { + Py_DECREF(method); + return FatalPythonErrorNoTraceback(type, value, + "Cannot create arguments tuple for traceback."); + } + PyTuple_SET_ITEM(argsTuple, 0, ArgumentValue(type)); + PyTuple_SET_ITEM(argsTuple, 1, ArgumentValue(value)); + PyTuple_SET_ITEM(argsTuple, 2, ArgumentValue(traceback)); + + // call the format_exception method + result = PyObject_CallObject(method, argsTuple); + Py_DECREF(method); + Py_DECREF(argsTuple); + if (!result) + return FatalPythonErrorNoTraceback(type, value, + "Failed calling format_exception method."); + + // determine length of string representation of formatted traceback + tracebackLength = 1; + for (i = 0; i < PyList_GET_SIZE(result); i++) + tracebackLength += PyString_GET_SIZE(PyList_GET_ITEM(result, i)); + + // create a string representation of the formatted traceback + tracebackStr = malloc(tracebackLength); + if (!tracebackStr) { + Py_DECREF(result); + return FatalError("Out of memory!"); + } + tracebackStr[0] = '\0'; + for (i = 0; i < PyList_GET_SIZE(result); i++) + strcat(tracebackStr, PyString_AS_STRING(PyList_GET_ITEM(result, i))); + Py_DECREF(result); + + // bring up the error + MessageBox(NULL, tracebackStr, "cx_Freeze: Python error in main script", + MB_ICONERROR); + Py_Finalize(); + return 1; +} + + +#include "Common.c" + + +//----------------------------------------------------------------------------- +// WinMain() +// Main routine for the executable in Windows. +//----------------------------------------------------------------------------- +int WINAPI WinMain( + HINSTANCE instance, // handle to application + HINSTANCE prevInstance, // previous handle to application + LPSTR commandLine, // command line + int showFlag) // show flag +{ + const char *fileName; + + // initialize Python + Py_NoSiteFlag = 1; + Py_FrozenFlag = 1; + Py_IgnoreEnvironmentFlag = 1; + Py_SetPythonHome(""); + Py_SetProgramName(__argv[0]); + fileName = Py_GetProgramFullPath(); + Py_Initialize(); + PySys_SetArgv(__argc, __argv); + + // do the work + if (ExecuteScript(fileName) < 0) + return 1; + + // terminate Python + Py_Finalize(); + return 0; +} + diff --git a/setup/installer/cx_Freeze/source/bases/dummy.rc b/setup/installer/cx_Freeze/source/bases/dummy.rc new file mode 100644 index 0000000000..5c1fa1a194 --- /dev/null +++ b/setup/installer/cx_Freeze/source/bases/dummy.rc @@ -0,0 +1,5 @@ +STRINGTABLE +{ + 1, "Just to ensure that buggy EndUpdateResource doesn't fall over." +} + diff --git a/setup/installer/cx_Freeze/source/bases/manifest.rc b/setup/installer/cx_Freeze/source/bases/manifest.rc new file mode 100644 index 0000000000..2b7ee27ab5 --- /dev/null +++ b/setup/installer/cx_Freeze/source/bases/manifest.rc @@ -0,0 +1,3 @@ +#include "dummy.rc" + +1 24 source/bases/manifest.txt diff --git a/setup/installer/cx_Freeze/source/util.c b/setup/installer/cx_Freeze/source/util.c new file mode 100644 index 0000000000..1c8eb0c0ca --- /dev/null +++ b/setup/installer/cx_Freeze/source/util.c @@ -0,0 +1,418 @@ +//----------------------------------------------------------------------------- +// util.c +// Shared library for use by cx_Freeze. +//----------------------------------------------------------------------------- + +#include + +#ifdef WIN32 +#include +#include + +#pragma pack(2) + +typedef struct { + BYTE bWidth; // Width, in pixels, of the image + BYTE bHeight; // Height, in pixels, of the image + BYTE bColorCount; // Number of colors in image + BYTE bReserved; // Reserved ( must be 0) + WORD wPlanes; // Color Planes + WORD wBitCount; // Bits per pixel + DWORD dwBytesInRes; // How many bytes in this resource? + DWORD dwImageOffset; // Where in the file is this image? +} ICONDIRENTRY; + +typedef struct { + WORD idReserved; // Reserved (must be 0) + WORD idType; // Resource Type (1 for icons) + WORD idCount; // How many images? + ICONDIRENTRY idEntries[0]; // An entry for each image +} ICONDIR; + +typedef struct { + BYTE bWidth; // Width, in pixels, of the image + BYTE bHeight; // Height, in pixels, of the image + BYTE bColorCount; // Number of colors in image + BYTE bReserved; // Reserved ( must be 0) + WORD wPlanes; // Color Planes + WORD wBitCount; // Bits per pixel + DWORD dwBytesInRes; // How many bytes in this resource? + WORD nID; // resource ID +} GRPICONDIRENTRY; + +typedef struct { + WORD idReserved; // Reserved (must be 0) + WORD idType; // Resource Type (1 for icons) + WORD idCount; // How many images? + GRPICONDIRENTRY idEntries[0]; // An entry for each image +} GRPICONDIR; +#endif + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- +#ifdef WIN32 +static PyObject *g_BindErrorException = NULL; +static PyObject *g_ImageNames = NULL; +#endif + + +#ifdef WIN32 +//----------------------------------------------------------------------------- +// BindStatusRoutine() +// Called by BindImageEx() at various points. This is used to determine the +// dependency tree which is later examined by cx_Freeze. +//----------------------------------------------------------------------------- +static BOOL __stdcall BindStatusRoutine( + IMAGEHLP_STATUS_REASON reason, // reason called + PSTR imageName, // name of image being examined + PSTR dllName, // name of DLL + ULONG virtualAddress, // computed virtual address + ULONG parameter) // parameter (value depends on reason) +{ + char fileName[MAX_PATH + 1]; + + switch (reason) { + case BindImportModule: + if (!SearchPath(NULL, dllName, NULL, sizeof(fileName), fileName, + NULL)) + return FALSE; + Py_INCREF(Py_None); + if (PyDict_SetItemString(g_ImageNames, fileName, Py_None) < 0) + return FALSE; + break; + default: + break; + } + return TRUE; +} + + +//----------------------------------------------------------------------------- +// GetFileData() +// Return the data for the given file. +//----------------------------------------------------------------------------- +static int GetFileData( + const char *fileName, // name of file to read + char **data) // pointer to data (OUT) +{ + DWORD numberOfBytesRead, dataSize; + HANDLE file; + + file = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (file == INVALID_HANDLE_VALUE) + return -1; + dataSize = GetFileSize(file, NULL); + if (dataSize == INVALID_FILE_SIZE) { + CloseHandle(file); + return -1; + } + *data = PyMem_Malloc(dataSize); + if (!*data) { + CloseHandle(file); + return -1; + } + if (!ReadFile(file, *data, dataSize, &numberOfBytesRead, NULL)) { + CloseHandle(file); + return -1; + } + CloseHandle(file); + return 0; +} + + +//----------------------------------------------------------------------------- +// CreateGroupIconResource() +// Return the group icon resource given the icon file data. +//----------------------------------------------------------------------------- +static GRPICONDIR *CreateGroupIconResource( + ICONDIR *iconDir, // icon information + DWORD *resourceSize) // size of resource (OUT) +{ + GRPICONDIR *groupIconDir; + int i; + + *resourceSize = sizeof(GRPICONDIR) + + sizeof(GRPICONDIRENTRY) * iconDir->idCount; + groupIconDir = PyMem_Malloc(*resourceSize); + if (!groupIconDir) + return NULL; + groupIconDir->idReserved = iconDir->idReserved; + groupIconDir->idType = iconDir->idType; + groupIconDir->idCount = iconDir->idCount; + for (i = 0; i < iconDir->idCount; i++) { + groupIconDir->idEntries[i].bWidth = iconDir->idEntries[i].bWidth; + groupIconDir->idEntries[i].bHeight = iconDir->idEntries[i].bHeight; + groupIconDir->idEntries[i].bColorCount = + iconDir->idEntries[i].bColorCount; + groupIconDir->idEntries[i].bReserved = iconDir->idEntries[i].bReserved; + groupIconDir->idEntries[i].wPlanes = iconDir->idEntries[i].wPlanes; + groupIconDir->idEntries[i].wBitCount = iconDir->idEntries[i].wBitCount; + groupIconDir->idEntries[i].dwBytesInRes = + iconDir->idEntries[i].dwBytesInRes; + groupIconDir->idEntries[i].nID = i + 1; + } + + return groupIconDir; +} + + +//----------------------------------------------------------------------------- +// ExtAddIcon() +// Add the icon as a resource to the specified file. +//----------------------------------------------------------------------------- +static PyObject *ExtAddIcon( + PyObject *self, // passthrough argument + PyObject *args) // arguments +{ + char *executableName, *iconName, *data, *iconData; + GRPICONDIR *groupIconDir; + DWORD resourceSize; + ICONDIR *iconDir; + BOOL succeeded; + HANDLE handle; + int i; + + if (!PyArg_ParseTuple(args, "ss", &executableName, &iconName)) + return NULL; + + // begin updating the executable + handle = BeginUpdateResource(executableName, FALSE); + if (!handle) { + PyErr_SetExcFromWindowsErrWithFilename(PyExc_WindowsError, + GetLastError(), executableName); + return NULL; + } + + // first attempt to get the data from the icon file + data = NULL; + succeeded = TRUE; + groupIconDir = NULL; + if (GetFileData(iconName, &data) < 0) + succeeded = FALSE; + iconDir = (ICONDIR*) data; + + // next, attempt to add a group icon resource + if (succeeded) { + groupIconDir = CreateGroupIconResource(iconDir, &resourceSize); + if (groupIconDir) + succeeded = UpdateResource(handle, RT_GROUP_ICON, + MAKEINTRESOURCE(1), + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), + groupIconDir, resourceSize); + else succeeded = FALSE; + } + + // next, add each icon as a resource + if (succeeded) { + for (i = 0; i < iconDir->idCount; i++) { + iconData = &data[iconDir->idEntries[i].dwImageOffset]; + resourceSize = iconDir->idEntries[i].dwBytesInRes; + succeeded = UpdateResource(handle, RT_ICON, MAKEINTRESOURCE(i + 1), + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), iconData, + resourceSize); + if (!succeeded) + break; + } + } + + // finish writing the resource (or discarding the changes upon an error) + if (!EndUpdateResource(handle, !succeeded)) { + if (succeeded) { + succeeded = FALSE; + PyErr_SetExcFromWindowsErrWithFilename(PyExc_WindowsError, + GetLastError(), executableName); + } + } + + // clean up + if (groupIconDir) + PyMem_Free(groupIconDir); + if (data) + PyMem_Free(data); + if (!succeeded) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// ExtBeginUpdateResource() +// Wrapper for BeginUpdateResource(). +//----------------------------------------------------------------------------- +static PyObject *ExtBeginUpdateResource( + PyObject *self, // passthrough argument + PyObject *args) // arguments +{ + BOOL deleteExistingResources; + char *fileName; + HANDLE handle; + + deleteExistingResources = TRUE; + if (!PyArg_ParseTuple(args, "s|i", &fileName, &deleteExistingResources)) + return NULL; + handle = BeginUpdateResource(fileName, deleteExistingResources); + if (!handle) { + PyErr_SetExcFromWindowsErrWithFilename(PyExc_WindowsError, + GetLastError(), fileName); + return NULL; + } + return PyInt_FromLong((long) handle); +} + + +//----------------------------------------------------------------------------- +// ExtUpdateResource() +// Wrapper for UpdateResource(). +//----------------------------------------------------------------------------- +static PyObject *ExtUpdateResource( + PyObject *self, // passthrough argument + PyObject *args) // arguments +{ + int resourceType, resourceId, resourceDataSize; + char *resourceData; + HANDLE handle; + + if (!PyArg_ParseTuple(args, "iiis#", &handle, &resourceType, &resourceId, + &resourceData, &resourceDataSize)) + return NULL; + if (!UpdateResource(handle, MAKEINTRESOURCE(resourceType), + MAKEINTRESOURCE(resourceId), + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), resourceData, + resourceDataSize)) { + PyErr_SetExcFromWindowsErr(PyExc_WindowsError, GetLastError()); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// ExtEndUpdateResource() +// Wrapper for EndUpdateResource(). +//----------------------------------------------------------------------------- +static PyObject *ExtEndUpdateResource( + PyObject *self, // passthrough argument + PyObject *args) // arguments +{ + BOOL discardChanges; + HANDLE handle; + + discardChanges = FALSE; + if (!PyArg_ParseTuple(args, "i|i", &handle, &discardChanges)) + return NULL; + if (!EndUpdateResource(handle, discardChanges)) { + PyErr_SetExcFromWindowsErr(PyExc_WindowsError, GetLastError()); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// ExtGetDependentFiles() +// Return a list of files that this file depends on. +//----------------------------------------------------------------------------- +static PyObject *ExtGetDependentFiles( + PyObject *self, // passthrough argument + PyObject *args) // arguments +{ + PyObject *results; + char *imageName; + + if (!PyArg_ParseTuple(args, "s", &imageName)) + return NULL; + g_ImageNames = PyDict_New(); + if (!g_ImageNames) + return NULL; + if (!BindImageEx(BIND_NO_BOUND_IMPORTS | BIND_NO_UPDATE | BIND_ALL_IMAGES, + imageName, NULL, NULL, BindStatusRoutine)) { + Py_DECREF(g_ImageNames); + PyErr_SetExcFromWindowsErrWithFilename(g_BindErrorException, + GetLastError(), imageName); + return NULL; + } + results = PyDict_Keys(g_ImageNames); + Py_DECREF(g_ImageNames); + return results; +} + + +//----------------------------------------------------------------------------- +// ExtGetSystemDir() +// Return the Windows directory (C:\Windows for example). +//----------------------------------------------------------------------------- +static PyObject *ExtGetSystemDir( + PyObject *self, // passthrough argument + PyObject *args) // arguments (ignored) +{ + char dir[MAX_PATH + 1]; + + if (GetSystemDirectory(dir, sizeof(dir))) + return PyString_FromString(dir); + PyErr_SetExcFromWindowsErr(PyExc_RuntimeError, GetLastError()); + return NULL; +} +#endif + + +//----------------------------------------------------------------------------- +// ExtSetOptimizeFlag() +// Set the optimize flag as needed. +//----------------------------------------------------------------------------- +static PyObject *ExtSetOptimizeFlag( + PyObject *self, // passthrough argument + PyObject *args) // arguments +{ + if (!PyArg_ParseTuple(args, "i", &Py_OptimizeFlag)) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// Methods +//----------------------------------------------------------------------------- +static PyMethodDef g_ModuleMethods[] = { + { "SetOptimizeFlag", ExtSetOptimizeFlag, METH_VARARGS }, +#ifdef WIN32 + { "BeginUpdateResource", ExtBeginUpdateResource, METH_VARARGS }, + { "UpdateResource", ExtUpdateResource, METH_VARARGS }, + { "EndUpdateResource", ExtEndUpdateResource, METH_VARARGS }, + { "AddIcon", ExtAddIcon, METH_VARARGS }, + { "GetDependentFiles", ExtGetDependentFiles, METH_VARARGS }, + { "GetSystemDir", ExtGetSystemDir, METH_NOARGS }, +#endif + { NULL } +}; + + +//----------------------------------------------------------------------------- +// initutil() +// Initialization routine for the shared libary. +//----------------------------------------------------------------------------- +void initutil(void) +{ + PyObject *module; + + module = Py_InitModule("cx_Freeze.util", g_ModuleMethods); + if (!module) + return; +#ifdef WIN32 + g_BindErrorException = PyErr_NewException("cx_Freeze.util.BindError", + NULL, NULL); + if (!g_BindErrorException) + return; + if (PyModule_AddObject(module, "BindError", g_BindErrorException) < 0) + return; +#endif +} + diff --git a/setup/installer/linux/freeze.py b/setup/installer/linux/freeze.py index 382c7ffeee..4fc2bd76fe 100644 --- a/setup/installer/linux/freeze.py +++ b/setup/installer/linux/freeze.py @@ -116,6 +116,8 @@ class LinuxFreeze(Command): glob.glob('src/calibre/gui2/convert/*.py')] includes += ['calibre.gui2.catalog.'+x.split('/')[-1].rpartition('.')[0] for x in \ glob.glob('src/calibre/gui2/catalog/*.py')] + includes += ['calibre.gui2.actions.'+x.split('/')[-1].rpartition('.')[0] for x in \ + glob.glob('src/calibre/gui2/actions/*.py')] LOADER = '/tmp/loader.py'