# HG changeset patch # User Volker.Kleinfeld@gmx.de # Date 1127269514 25200 # Node ID f5faab34f32ed24cf60422e35623f91bdc900c6f # Parent c1a507ba398ba5cce35c9997ac5d180b63b10dc2 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. diff -r c1a507ba398b -r f5faab34f32e mercurial/packagescan.py --- /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 +# +# 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 """ + # 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 diff -r c1a507ba398b -r f5faab34f32e setup.py --- 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()