"""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-compile', 'c', "compile to .pyc on the target system"), ('target-optimize', 'o', "compile to .pyo on the target system"), ('target-version=', 'v', "require a specific python version" + " on the target system (1.5 or 1.6/2.0)"), ('dist-dir=', 'd', "directory to put final built distributions in"), ] def initialize_options (self): self.bdist_dir = None self.keep_tree = 0 self.target_compile = 0 self.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 = "" else: if not self.target_version in ("1.5", "1.6", "2.0"): raise DistutilsOptionError ( "target version must be 1.5, 1.6, or 2.0") 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): self.run_command ('build') install = self.reinitialize_command('install') install.root = self.bdist_dir install_lib = self.reinitialize_command('install_lib') install_lib.compile = 0 install_lib.optimize = 0 # The packager can choose if .pyc and .pyo files should be created # on the TARGET system instead at the SOURCE system. ## # The compilation can only be done on the SOURCE system for one ## # python version (assuming 1.6/2.0 and 1.5 have incompatible ## # byte-codes). ## short_version = sys.version[:3] ## if self.target_version == short_version: ## if not self.target_compile: ## install_lib.compile = 1 ## if not self.target_optimize: ## install_lib.optimize = 1 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 create_inifile (self): # Create an inifile containing data describing the installation. # This could be done without creating a real file, but # a file is (at least) useful for debugging bdist_wininst. metadata = self.distribution.metadata ini_name = "%s.ini" % metadata.get_fullname() self.announce ("creating %s" % ini_name) inifile = open (ini_name, "w") # 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. inifile.write ("[metadata]\n") # 'info' will be displayed in the installer's dialog box, # describing the items to be installed. info = metadata.long_description + '\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)) inifile.write ("%s=%s\n" % (name, repr (data)[1:-1])) # The [setup] section contains entries controlling # the installer runtime. inifile.write ("\n[Setup]\n") inifile.write ("info=%s\n" % repr (info)[1:-1]) inifile.write ("pthname=%s.%s\n" % (metadata.name, metadata.version)) inifile.write ("pyc_compile=%d\n" % self.target_compile) inifile.write ("pyo_compile=%d\n" % self.target_optimize) if self.target_version: vers_minor = string.split (self.target_version, '.')[1] inifile.write ("vers_minor=%s\n" % vers_minor) title = self.distribution.get_fullname() inifile.write ("title=%s\n" % repr (title)[1:-1]) inifile.close() return ini_name # create_inifile() def create_exe (self, arcname, fullname): import struct, zlib cfgdata = open (self.create_inifile()).read() comp_method = zlib.DEFLATED co = zlib.compressobj (zlib.Z_DEFAULT_COMPRESSION, comp_method, -15) zcfgdata = co.compress (cfgdata) + co.flush() 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 (zcfgdata) crc32 = zlib.crc32 (cfgdata) header = struct.pack ("