home *** CD-ROM | disk | FTP | other *** search
/ Freelog Special Freeware 31 / FreelogHS31.iso / Texte / scribus / scribus-1.3.3.9-win32-install.exe / lib / distutils / command / bdist_wininst.py < prev    next >
Text File  |  2004-11-18  |  13KB  |  323 lines

  1. """distutils.command.bdist_wininst
  2.  
  3. Implements the Distutils 'bdist_wininst' command: create a windows installer
  4. exe-program."""
  5.  
  6. # This module should be kept compatible with Python 2.1.
  7.  
  8. __revision__ = "$Id: bdist_wininst.py,v 1.56 2004/11/10 22:23:14 loewis Exp $"
  9.  
  10. import sys, os, string
  11. from distutils.core import Command
  12. from distutils.util import get_platform
  13. from distutils.dir_util import create_tree, remove_tree
  14. from distutils.errors import *
  15. from distutils.sysconfig import get_python_version
  16. from distutils import log
  17.  
  18. class bdist_wininst (Command):
  19.  
  20.     description = "create an executable installer for MS Windows"
  21.  
  22.     user_options = [('bdist-dir=', None,
  23.                      "temporary directory for creating the distribution"),
  24.                     ('keep-temp', 'k',
  25.                      "keep the pseudo-installation tree around after " +
  26.                      "creating the distribution archive"),
  27.                     ('target-version=', None,
  28.                      "require a specific python version" +
  29.                      " on the target system"),
  30.                     ('no-target-compile', 'c',
  31.                      "do not compile .py to .pyc on the target system"),
  32.                     ('no-target-optimize', 'o',
  33.                      "do not compile .py to .pyo (optimized)"
  34.                      "on the target system"),
  35.                     ('dist-dir=', 'd',
  36.                      "directory to put final built distributions in"),
  37.                     ('bitmap=', 'b',
  38.                      "bitmap to use for the installer instead of python-powered logo"),
  39.                     ('title=', 't',
  40.                      "title to display on the installer background instead of default"),
  41.                     ('skip-build', None,
  42.                      "skip rebuilding everything (for testing/debugging)"),
  43.                     ('install-script=', None,
  44.                      "basename of installation script to be run after"
  45.                      "installation or before deinstallation"),
  46.                     ('pre-install-script=', None,
  47.                      "Fully qualified filename of a script to be run before "
  48.                      "any files are installed.  This script need not be in the "
  49.                      "distribution"),
  50.                    ]
  51.  
  52.     boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize',
  53.                        'skip-build']
  54.  
  55.     def initialize_options (self):
  56.         self.bdist_dir = None
  57.         self.keep_temp = 0
  58.         self.no_target_compile = 0
  59.         self.no_target_optimize = 0
  60.         self.target_version = None
  61.         self.dist_dir = None
  62.         self.bitmap = None
  63.         self.title = None
  64.         self.skip_build = 0
  65.         self.install_script = None
  66.         self.pre_install_script = None
  67.  
  68.     # initialize_options()
  69.  
  70.  
  71.     def finalize_options (self):
  72.         if self.bdist_dir is None:
  73.             bdist_base = self.get_finalized_command('bdist').bdist_base
  74.             self.bdist_dir = os.path.join(bdist_base, 'wininst')
  75.         if not self.target_version:
  76.             self.target_version = ""
  77.         if not self.skip_build and self.distribution.has_ext_modules():
  78.             short_version = get_python_version()
  79.             if self.target_version and self.target_version != short_version:
  80.                 raise DistutilsOptionError, \
  81.                       "target version can only be %s, or the '--skip_build'" \
  82.                       " option must be specified" % (short_version,)
  83.             self.target_version = short_version
  84.  
  85.         self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
  86.  
  87.         if self.install_script:
  88.             for script in self.distribution.scripts:
  89.                 if self.install_script == os.path.basename(script):
  90.                     break
  91.             else:
  92.                 raise DistutilsOptionError, \
  93.                       "install_script '%s' not found in scripts" % \
  94.                       self.install_script
  95.     # finalize_options()
  96.  
  97.  
  98.     def run (self):
  99.         if (sys.platform != "win32" and
  100.             (self.distribution.has_ext_modules() or
  101.              self.distribution.has_c_libraries())):
  102.             raise DistutilsPlatformError \
  103.                   ("distribution contains extensions and/or C libraries; "
  104.                    "must be compiled on a Windows 32 platform")
  105.  
  106.         if not self.skip_build:
  107.             self.run_command('build')
  108.  
  109.         install = self.reinitialize_command('install', reinit_subcommands=1)
  110.         install.root = self.bdist_dir
  111.         install.skip_build = self.skip_build
  112.         install.warn_dir = 0
  113.  
  114.         install_lib = self.reinitialize_command('install_lib')
  115.         # we do not want to include pyc or pyo files
  116.         install_lib.compile = 0
  117.         install_lib.optimize = 0
  118.  
  119.         if self.distribution.has_ext_modules():
  120.             # If we are building an installer for a Python version other
  121.             # than the one we are currently running, then we need to ensure
  122.             # our build_lib reflects the other Python version rather than ours.
  123.             # Note that for target_version!=sys.version, we must have skipped the
  124.             # build step, so there is no issue with enforcing the build of this
  125.             # version.
  126.             target_version = self.target_version
  127.             if not target_version:
  128.                 assert self.skip_build, "Should have already checked this"
  129.                 target_version = sys.version[0:3]
  130.             plat_specifier = ".%s-%s" % (get_platform(), target_version)
  131.             build = self.get_finalized_command('build')
  132.             build.build_lib = os.path.join(build.build_base,
  133.                                            'lib' + plat_specifier)
  134.  
  135.         # Use a custom scheme for the zip-file, because we have to decide
  136.         # at installation time which scheme to use.
  137.         for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'):
  138.             value = string.upper(key)
  139.             if key == 'headers':
  140.                 value = value + '/Include/$dist_name'
  141.             setattr(install,
  142.                     'install_' + key,
  143.                     value)
  144.  
  145.         log.info("installing to %s", self.bdist_dir)
  146.         install.ensure_finalized()
  147.  
  148.         # avoid warning of 'install_lib' about installing
  149.         # into a directory not in sys.path
  150.         sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB'))
  151.  
  152.         install.run()
  153.  
  154.         del sys.path[0]
  155.  
  156.         # And make an archive relative to the root of the
  157.         # pseudo-installation tree.
  158.         from tempfile import mktemp
  159.         archive_basename = mktemp()
  160.         fullname = self.distribution.get_fullname()
  161.         arcname = self.make_archive(archive_basename, "zip",
  162.                                     root_dir=self.bdist_dir)
  163.         # create an exe containing the zip-file
  164.         self.create_exe(arcname, fullname, self.bitmap)
  165.         # remove the zip-file again
  166.         log.debug("removing temporary file '%s'", arcname)
  167.         os.remove(arcname)
  168.  
  169.         if not self.keep_temp:
  170.             remove_tree(self.bdist_dir, dry_run=self.dry_run)
  171.  
  172.     # run()
  173.  
  174.     def get_inidata (self):
  175.         # Return data describing the installation.
  176.  
  177.         lines = []
  178.         metadata = self.distribution.metadata
  179.  
  180.         # Write the [metadata] section.
  181.         lines.append("[metadata]")
  182.  
  183.         # 'info' will be displayed in the installer's dialog box,
  184.         # describing the items to be installed.
  185.         info = (metadata.long_description or '') + '\n'
  186.  
  187.         # Escape newline characters
  188.         def escape(s):
  189.             return string.replace(s, "\n", "\\n")
  190.  
  191.         for name in ["author", "author_email", "description", "maintainer",
  192.                      "maintainer_email", "name", "url", "version"]:
  193.             data = getattr(metadata, name, "")
  194.             if data:
  195.                 info = info + ("\n    %s: %s" % \
  196.                                (string.capitalize(name), escape(data)))
  197.                 lines.append("%s=%s" % (name, escape(data)))
  198.  
  199.         # The [setup] section contains entries controlling
  200.         # the installer runtime.
  201.         lines.append("\n[Setup]")
  202.         if self.install_script:
  203.             lines.append("install_script=%s" % self.install_script)
  204.         lines.append("info=%s" % escape(info))
  205.         lines.append("target_compile=%d" % (not self.no_target_compile))
  206.         lines.append("target_optimize=%d" % (not self.no_target_optimize))
  207.         if self.target_version:
  208.             lines.append("target_version=%s" % self.target_version)
  209.  
  210.         title = self.title or self.distribution.get_fullname()
  211.         lines.append("title=%s" % escape(title))
  212.         import time
  213.         import distutils
  214.         build_info = "Built %s with distutils-%s" % \
  215.                      (time.ctime(time.time()), distutils.__version__)
  216.         lines.append("build_info=%s" % build_info)
  217.         return string.join(lines, "\n")
  218.  
  219.     # get_inidata()
  220.  
  221.     def create_exe (self, arcname, fullname, bitmap=None):
  222.         import struct
  223.  
  224.         self.mkpath(self.dist_dir)
  225.  
  226.         cfgdata = self.get_inidata()
  227.  
  228.         installer_name = self.get_installer_filename(fullname)
  229.         self.announce("creating %s" % installer_name)
  230.  
  231.         if bitmap:
  232.             bitmapdata = open(bitmap, "rb").read()
  233.             bitmaplen = len(bitmapdata)
  234.         else:
  235.             bitmaplen = 0
  236.  
  237.         file = open(installer_name, "wb")
  238.         file.write(self.get_exe_bytes())
  239.         if bitmap:
  240.             file.write(bitmapdata)
  241.  
  242.         # Convert cfgdata from unicode to ascii, mbcs encoded
  243.         try:
  244.             unicode
  245.         except NameError:
  246.             pass
  247.         else:
  248.             if isinstance(cfgdata, unicode):
  249.                 cfgdata = cfgdata.encode("mbcs")
  250.  
  251.         # Append the pre-install script
  252.         cfgdata = cfgdata + "\0"
  253.         if self.pre_install_script:
  254.             script_data = open(self.pre_install_script, "r").read()
  255.             cfgdata = cfgdata + script_data + "\n\0"
  256.         else:
  257.             # empty pre-install script
  258.             cfgdata = cfgdata + "\0"
  259.         file.write(cfgdata)
  260.  
  261.         # The 'magic number' 0x1234567B is used to make sure that the
  262.         # binary layout of 'cfgdata' is what the wininst.exe binary
  263.         # expects.  If the layout changes, increment that number, make
  264.         # the corresponding changes to the wininst.exe sources, and
  265.         # recompile them.
  266.         header = struct.pack("<iii",
  267.                              0x1234567B,       # tag
  268.                              len(cfgdata),     # length
  269.                              bitmaplen,        # number of bytes in bitmap
  270.                              )
  271.         file.write(header)
  272.         file.write(open(arcname, "rb").read())
  273.  
  274.     # create_exe()
  275.  
  276.     def get_installer_filename(self, fullname):
  277.         # Factored out to allow overriding in subclasses
  278.         if self.target_version:
  279.             # if we create an installer for a specific python version,
  280.             # it's better to include this in the name
  281.             installer_name = os.path.join(self.dist_dir,
  282.                                           "%s.win32-py%s.exe" %
  283.                                            (fullname, self.target_version))
  284.         else:
  285.             installer_name = os.path.join(self.dist_dir,
  286.                                           "%s.win32.exe" % fullname)
  287.         return installer_name
  288.     # get_installer_filename()
  289.  
  290.     def get_exe_bytes (self):
  291.         from distutils.msvccompiler import get_build_version
  292.         # If a target-version other than the current version has been
  293.         # specified, then using the MSVC version from *this* build is no good.
  294.         # Without actually finding and executing the target version and parsing
  295.         # its sys.version, we just hard-code our knowledge of old versions.
  296.         # NOTE: Possible alternative is to allow "--target-version" to
  297.         # specify a Python executable rather than a simple version string.
  298.         # We can then execute this program to obtain any info we need, such
  299.         # as the real sys.version string for the build.
  300.         cur_version = get_python_version()
  301.         if self.target_version and self.target_version != cur_version:
  302.             # If the target version is *later* than us, then we assume they
  303.             # use what we use
  304.             # string compares seem wrong, but are what sysconfig.py itself uses
  305.             if self.target_version > cur_version:
  306.                 bv = get_build_version()
  307.             else:
  308.                 if self.target_version < "2.4":
  309.                     bv = "6"
  310.                 else:
  311.                     bv = "7.1"
  312.         else:
  313.             # for current version - use authoritative check.
  314.             bv = get_build_version()
  315.  
  316.         # wininst-x.y.exe is in the same directory as this file
  317.         directory = os.path.dirname(__file__)
  318.         # we must use a wininst-x.y.exe built with the same C compiler
  319.         # used for python.  XXX What about mingw, borland, and so on?
  320.         filename = os.path.join(directory, "wininst-%s.exe" % bv)
  321.         return open(filename, "rb").read()
  322. # class bdist_wininst
  323.