"""distutils.command.bdist_wininst Implements the Distutils 'bdist_wininst' command: create a windows installer exe-program.""" # created 2000/06/02, Thomas Heller __revision__ = "$Id$" import sys, os, string from distutils.core import Command from distutils.util import get_platform from distutils.dir_util import create_tree, remove_tree from distutils.errors import * class bdist_wininst (Command): description = "create an executable installer for MS Windows" user_options = [('bdist-dir=', 'd', "temporary directory for creating the distribution"), ('keep-temp', 'k', "keep the pseudo-installation tree around after " + "creating the distribution archive"), ('target-version=', 'v', "require a specific python version" + " on the target system"), ('no-target-compile', 'c', "do not compile .py to .pyc on the target system"), ('no-target-optimize', 'o', "do not compile .py to .pyo (optimized)" "on the target system"), ('dist-dir=', 'd', "directory to put final built distributions in"), ] boolean_options = ['keep-temp'] def initialize_options (self): self.bdist_dir = None self.keep_temp = 0 self.no_target_compile = 0 self.no_target_optimize = 0 self.target_version = None self.dist_dir = None # initialize_options() def finalize_options (self): if self.bdist_dir is None: bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'wininst') if not self.target_version: self.target_version = "" if self.distribution.has_ext_modules(): short_version = sys.version[:3] if self.target_version and self.target_version != short_version: raise DistutilsOptionError ("target version can only be" + short_version) self.target_version = short_version self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) # finalize_options() def run (self): if (sys.platform != "win32" and (self.distribution.has_ext_modules() or self.distribution.has_c_libraries())): raise DistutilsPlatformError \ ("distribution contains extensions and/or C libraries; " "must be compiled on a Windows 32 platform") self.run_command ('build') install = self.reinitialize_command('install') install.root = self.bdist_dir if os.name != 'nt': # Must force install to use the 'nt' scheme; we set the # prefix too just because it looks silly to put stuff # in (eg.) ".../usr/Scripts", "usr/Include", etc. install.prefix = "Python" install.select_scheme('nt') install_lib = self.reinitialize_command('install_lib') # we do not want to include pyc or pyo files install_lib.compile = 0 install_lib.optimize = 0 install_lib.ensure_finalized() self.announce ("installing to %s" % self.bdist_dir) install.ensure_finalized() install.run() # And make an archive relative to the root of the # pseudo-installation tree. fullname = self.distribution.get_fullname() archive_basename = os.path.join(self.bdist_dir, "%s.win32" % fullname) # Our archive MUST be relative to sys.prefix, which is the # same as install_purelib in the 'nt' scheme. root_dir = os.path.normpath (install.install_purelib) # Sanity check: Make sure everything is included for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): attrname = 'install_' + key install_x = getattr (install, attrname) # (Use normpath so that we can string.find to look for # subdirectories) install_x = os.path.normpath (install_x) if string.find (install_x, root_dir) != 0: raise DistutilsInternalError \ ("'%s' not included in install_lib" % key) arcname = self.make_archive (archive_basename, "zip", root_dir=root_dir) self.create_exe (arcname, fullname) if not self.keep_temp: remove_tree (self.bdist_dir, self.verbose, self.dry_run) # run() def get_inidata (self): # Return data describing the installation. lines = [] metadata = self.distribution.metadata # Write the [metadata] section. Values are written with # repr()[1:-1], so they do not contain unprintable characters, and # are not surrounded by quote chars. lines.append ("[metadata]") # 'info' will be displayed in the installer's dialog box, # describing the items to be installed. info = (metadata.long_description or '') + '\n' for name in dir (metadata): if (name != 'long_description'): data = getattr (metadata, name) if data: info = info + ("\n %s: %s" % \ (string.capitalize (name), data)) lines.append ("%s=%s" % (name, repr (data)[1:-1])) # The [setup] section contains entries controlling # the installer runtime. lines.append ("\n[Setup]") lines.append ("info=%s" % repr (info)[1:-1]) lines.append ("target_compile=%d" % (not self.no_target_compile)) lines.append ("target_optimize=%d" % (not self.no_target_optimize)) if self.target_version: lines.append ("target_version=%s" % self.target_version) title = self.distribution.get_fullname() lines.append ("title=%s" % repr (title)[1:-1]) import time import distutils build_info = "Build %s with distutils-%s" % \ (time.ctime (time.time()), distutils.__version__) lines.append ("build_info=%s" % build_info) return string.join (lines, "\n") # get_inidata() def create_exe (self, arcname, fullname): import struct self.mkpath(self.dist_dir) cfgdata = self.get_inidata() if self.target_version: # if we create an installer for a specific python version, # it's better to include this in the name installer_name = os.path.join(self.dist_dir, "%s.win32-py%s.exe" % (fullname, self.target_version)) else: installer_name = os.path.join(self.dist_dir, "%s.win32.exe" % fullname) self.announce ("creating %s" % installer_name) file = open (installer_name, "wb") file.write (self.get_exe_bytes ()) file.write (cfgdata) header = struct.pack ("