Support for the distutils extention 'py2exe' added.
Description:
- If the py2exe distutils extention is installed this patch allows
building standalone exe for windows - example:
> python setup.py build --compiler=mingw32 py2exe
- The 'out of the box' py2exe is not able to resolve
the dependencies due to 'demandload'. A new helper module
of scanning the mercurial package has been added.
Changed:
- setup.py: importing py2exe and sub classing its command class
to fetch the build directory and insert the needed includes
- packagescan.py: new helper module added, that scans the distutil
build directory for modules to be included.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/packagescan.py Tue Sep 20 19:25:14 2005 -0700
@@ -0,0 +1,74 @@
+# packagescan.py - Helper module for identifing used modules.
+# Used for the py2exe distutil.
+#
+# Copyright 2005 Volker Kleinfeld <Volker.Kleinfeld@gmx.de>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+import glob
+import os
+import sys
+import demandload
+import ihooks
+
+requiredmodules = {} # Will contain the modules imported by demandload
+def demandload(scope, modules):
+ """ fake demandload function that collects the required modules """
+ for m in modules.split():
+ mod = None
+ mod = __import__(m,scope,scope)
+ scope[m] = mod
+ requiredmodules[mod.__name__] = 1
+
+def getmodules(libpath,packagename):
+ """ helper for finding all required modules of package <packagename> """
+ # Use the package in the build directory
+ libpath = os.path.abspath(libpath)
+ sys.path.insert(0,libpath)
+ packdir = os.path.join(libpath,packagename)
+ # A normal import would not find the package in
+ # the build directory. ihook is used to force the import.
+ # After the package is imported the import scope for
+ # the following imports is settled.
+ p = importfrom(packdir)
+ globals()[packagename] = p
+ sys.modules[packagename] = p
+ # Fetch the python modules in the package
+ cwd = os.getcwd()
+ os.chdir(packdir)
+ pymodulefiles = glob.glob('*.py')
+ extmodulefiles = glob.glob('*.pyd')
+ os.chdir(cwd)
+ # Install a fake demandload module
+ sys.modules['mercurial.demandload'] = sys.modules['mercurial.packagescan']
+ # Import all python modules and by that run the fake demandload
+ for m in pymodulefiles:
+ if m == '__init__.py': continue
+ tmp = {}
+ mname,ext = os.path.splitext(m)
+ fullname = packagename+'.'+mname
+ __import__(fullname,tmp,tmp)
+ requiredmodules[fullname] = 1
+ # Import all extension modules and by that run the fake demandload
+ for m in extmodulefiles:
+ tmp = {}
+ mname,ext = os.path.splitext(m)
+ fullname = packagename+'.'+mname
+ __import__(fullname,tmp,tmp)
+ requiredmodules[fullname] = 1
+ includes = requiredmodules.keys()
+ return includes
+
+def importfrom(filename):
+ """
+ import module/package from a named file and returns the module.
+ It does not check on sys.modules or includes the module in the scope.
+ """
+ loader = ihooks.BasicModuleLoader()
+ path, file = os.path.split(filename)
+ name, ext = os.path.splitext(file)
+ m = loader.find_module_in_dir(name, path)
+ if not m:
+ raise ImportError, name
+ m = loader.load_module(name, m)
+ return m
--- a/setup.py Tue Sep 20 11:38:01 2005 -0700
+++ b/setup.py Tue Sep 20 19:25:14 2005 -0700
@@ -11,6 +11,34 @@
import mercurial.version
+# py2exe needs to be installed to work
+try:
+ import py2exe
+
+ # Due to the use of demandload py2exe is not finding the modules.
+ # packagescan.getmodules creates a list of modules included in
+ # the mercurial package plus depdent modules.
+ import mercurial.packagescan
+ from py2exe.build_exe import py2exe as build_exe
+
+ class py2exe_for_demandload(build_exe):
+ """ overwrites the py2exe command class for getting the build
+ directory and for setting the 'includes' option."""
+ def initialize_options(self):
+ self.build_lib = None
+ build_exe.initialize_options(self)
+ def finalize_options(self):
+ # Get the build directory, ie. where to search for modules.
+ self.set_undefined_options('build',
+ ('build_lib', 'build_lib'))
+ # Sets the 'includes' option with the list of needed modules
+ if not self.includes:
+ self.includes = []
+ self.includes += mercurial.packagescan.getmodules(self.build_lib,'mercurial')
+ build_exe.finalize_options(self)
+except ImportError: pass
+
+
# specify version string, otherwise 'hg identify' will be used:
version = ''
@@ -36,7 +64,9 @@
['templates/map'] +
glob.glob('templates/map-*') +
glob.glob('templates/*.tmpl'))],
- cmdclass = { 'install_data' : install_package_data },
- scripts=['hg', 'hgmerge'])
+ cmdclass = { 'install_data' : install_package_data,
+ 'py2exe' : py2exe_for_demandload},
+ scripts=['hg', 'hgmerge'],
+ console = ['hg'])
finally:
mercurial.version.forget_version()