"""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-tree', '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"), ] def initialize_options (self): self.bdist_dir = None self.keep_tree = 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 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) # XXX hack! Our archive MUST be relative to sys.prefix # XXX What about .install_data, .install_scripts, ...? # [Perhaps require that all installation dirs be under sys.prefix # on Windows? this will be acceptable until we start dealing # with Python applications, at which point we should zip up # the application directory -- and again everything can be # under one dir --GPW] root_dir = install.install_lib arcname = self.make_archive (archive_basename, "zip", root_dir=root_dir) self.create_exe (arcname, fullname) if not self.keep_tree: 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 ("pthname=%s.%s" % (metadata.name, metadata.version)) 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]) 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 ("