import os from distutils.core import Command from distutils.file_util import write_file import string class bdist_inno(Command): """Command to create a windows installer with Inno Setup""" description = "Create a windows installer with Inno Setup" user_options = [ ('skip-build', None, "skip the build steps"), ('bdist-dir=', None, "temporary directory for creating the distribution"), ('run-inno', None, "Run inno-setup to create the installer. On by default on nt"), ('iss-name', None, "The name of the iss file to generate. " "Shouldn't contain directories"), # Parameters for the Inno Setup script ('copyright', None, "Copyright notice for the Inno Setup file"), ('default-dir-name', None, "Default installation directory. Defaults to '{pf}\\'"), ('default-group-name', None, "Default program group name. Defaults to '"), ("license-file", None, "File containing the license."), ("output-basename", None, "Base filename for the Inno Setup output " "(defaults to --)."), ("iss-revision", None, "revision of the generated installer of the package version"), ("icons-entries", None, "List if InnoIconItems " "(this can only be set from inside the setup.py script)"), ] boolean_options = ["do-symlink"] def initialize_options(self): self.skip_build = 0 self.bdist_dir = None self.run_inno = None self.iss_name = None self.copyright = "" self.default_dir_name = None self.default_group_name = None self.license_file = None self.output_basename = None self.iss_revision = None self.icons_entries = [] def finalize_options(self): self.set_undefined_options("install", ('skip_build', 'skip_build')) if self.bdist_dir is None: bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'inno') if self.run_inno is None: self.run_inno = os.name == "nt" name = self.distribution.get_name() if self.iss_name is None: self.iss_name = name + '.iss' if self.default_dir_name is None: self.default_dir_name = "{pf}\\" + name if self.default_group_name is None: self.default_group_name = name if self.iss_revision is None: self.iss_revision = 0 if self.output_basename is None: self.output_basename = "%s-%s-%d" \ % (name, self.distribution.get_version(), self.iss_revision) def run(self, install_options = None): """Execute the command. install_options if given, should be a directory of additional options to set in the install step""" # Obviously have to build before we can install if not self.skip_build: self.run_command('build') # Install in a temporary directory install = self.reinitialize_command('install') install.root = self.bdist_dir if install_options is not None: for key, value in install_options.items(): setattr(install, key, value) if os.name != 'nt': # Must force install to use the 'nt' scheme; install.select_scheme('nt') self.announce("installing to %s" % self.bdist_dir) install.ensure_finalized() install.run() # Create the iss file iss_file = os.path.join(self.bdist_dir, self.iss_name) self.execute(write_file, (iss_file, self.generate_iss()), "Create Inno Setup script file %s" % iss_file) # and invoke if self.run_inno: self.spawn(["iscc", iss_file]) def generate_iss(self): """Return the contents of the iss file as list of strings, one string per line""" # first, turn the icons entries into a more usable form icons = {} for item in self.icons_entries: icons[item.filename] = item iss = [] name = self.distribution.get_name() iss.extend(["[Setup]", "AppName=" + name, "AppVerName=" + name + " "+self.distribution.get_version(), "DefaultDirName=" + self.default_dir_name, "DefaultGroupName=" + self.default_group_name, ]) if self.copyright: iss.append("AppCopyright=" + self.copyright) if self.license_file: iss.append("LicenseFile=" + self.license_file) iss.append("OutputBasefilename=" + self.output_basename) iss.append("") iss.append("[Files]") install = self.get_finalized_command("install") install_scripts = self.get_finalized_command("install_scripts") script_files = install_scripts.get_outputs() prefixlen = len(self.bdist_dir) + len(os.sep) for filename in install.get_outputs(): filename = filename[prefixlen:] icon = icons.get(filename) dirname = os.path.dirname(filename) if os.name != "nt": # change the separators to \ on non-windos systems filename = string.join(string.split(filename, os.sep), "\\") dirname = string.join(string.split(dirname, os.sep), "\\") line = 'Source: "%s"' % filename if icon is not None: # install it as defined in the icon object backslash = string.rfind(icon.install_name, "\\") if backslash >= 0: dirname = icon.install_name[:backslash] basename = icon.install_name[backslash + 1:] else: dirname = "" basename = icon.install_name line = '%s; DestDir: "%s"; DestName: "%s"' % (line, dirname, basename) else: line = line + '; DestDir: "{app}\\%s"' % (dirname) iss.append(line) iss.append("") iss.append("[Icons]") for icon in self.icons_entries: line = 'Name: "{group}\\%s"; Filename: "%s";' \ % (icon.title, icon.install_name) iss.append(line) return iss class InnoIconItem: """Describe one item for the start menu for the Inno Setup installer""" def __init__(self, filename, title, install_name = None): self.filename = filename self.title = title if install_name is not None: self.install_name = install_name else: self.install_name = filename