This is a minimal example of how BrianDorsey is building windows installers using Py2exe and InnoSetup.
The code borrows heavily from Thomas Heller's sample code for extending Py2exe.
Start with a Python program:
hello.py:
1 print "Hello."
2 print "This is my data:"
3 print open('data.txt').read()
and a datafile:
data.txt:
DATA, DATA, DATA, DATA, DATA, DATA, DATA, DATA, DATA, DATA, DATA.
Now, create a setup.py which will used py2exe to build executables, and extend the py2exe build step so that we can create an installer which will automatically include all of the files which py2exe outputs.
setup.py
1 from distutils.core import setup
2 import py2exe
3 import os
4
5 __version__ = "0.0.1.0"
6
7
8 # You'll have to set this to match your environment.
9 INNOSETUP_COMPILER = r'C:\InnoSetup\ISCC.exe'
10
11 ############################################################
12 class InnoScript:
13 def __init__(self,
14 name,
15 lib_dir,
16 dist_dir,
17 console_exe_files = [],
18 lib_files = [],
19 version = "1.0"):
20 self.lib_dir = lib_dir
21 self.dist_dir = dist_dir
22 if not self.dist_dir[-1] in "\\/":
23 self.dist_dir += "\\"
24 self.name = name
25 self.version = version
26 self.console_exe_files = [self.chop(p) for p in console_exe_files]
27 self.lib_files = [self.chop(p) for p in lib_files]
28
29 def chop(self, pathname):
30 assert pathname.startswith(self.dist_dir)
31 return pathname[len(self.dist_dir):]
32
33 def create(self, pathname=None):
34 """ Create the Inno Setup configuration script. """
35 if not pathname:
36 pathname = os.path.join(self.dist_dir, self.name) + '.iss'
37 print "InnoSetup script: %s" % pathname
38 self.pathname = pathname
39 ofi = self.file = open(pathname, "w")
40 print >> ofi, "; WARNING: This script has been created by py2exe. Changes to this script"
41 print >> ofi, "; will be overwritten the next time py2exe is run!\n"
42 print >> ofi, r"[Setup]"
43 print >> ofi, r"AppName=%s" % self.name
44 print >> ofi, r"AppVersion=%s" % (self.version)
45 print >> ofi, r"AppVerName=%s %s" % (self.name, self.version)
46 print >> ofi, r"AppPublisher=Your Name Here."
47 print >> ofi, r"DefaultDirName={pf}\%s" % self.name
48 print >> ofi, r"DefaultGroupName=%s" % self.name
49 print >> ofi, r"Compression=lzma"
50 print >> ofi, r"OutputDir=%s" % self.dist_dir
51 print >> ofi, r"OutputBaseFilename=%s_%s_Setup" % (self.name,
52 self.version)
53 print >> ofi
54
55 print >> ofi, r"[Files]"
56 for path in self.console_exe_files + self.lib_files:
57 flags = 'ignoreversion'
58 if path.lower().endswith('.cfg'):
59 flags += ' onlyifdoesntexist '
60 print >> ofi, r'Source: "%s"; DestDir: "{app}\%s"; Flags: %s' % (path, os.path.dirname(path), flags)
61 print >> ofi
62
63 ofi.close()
64
65 def compile(self):
66 cmd = "%s %s" % (INNOSETUP_COMPILER, self.pathname)
67 print "Running InnoSetup: %s" % cmd
68 errorlevel = os.system(cmd)
69 print "InnoSetup returned errorlevel: %s" % errorlevel
70
71
72 ################################################################
73
74 from py2exe.build_exe import py2exe
75
76 class debug_installer(py2exe):
77 """A replacement for the default py2exe installer which prints out
78 all of the instance variables after py2exe completes."""
79 def run(self):
80 # First, let py2exe do it's work.
81 py2exe.run(self)
82 from pprint import pprint
83 print "#" * 79
84 print "#" * 79
85 pprint(vars(self))
86
87 class build_installer(py2exe):
88 """This class first builds the exe file(s), then creates a Windows installer.
89
90 You need InnoSetup for it."""
91 def run(self):
92 # First, let py2exe do it's work.
93 py2exe.run(self)
94
95 lib_dir = self.lib_dir
96 dist_dir = self.dist_dir
97
98 # create the Installer, using the files py2exe has created.
99 script = InnoScript("Hello", #The Setup product name
100 lib_dir,
101 dist_dir,
102 self.console_exe_files,
103 self.lib_files,
104 __version__)
105 print "*** creating the inno setup script***"
106 script.create()
107 print "*** compiling the inno setup script***"
108 script.compile()
109
110
111 ################################################################
112 ## Main setup
113
114 excludes = ["pywin", "pywin.debugger", "pywin.debugger.dbgcon",
115 "pywin.dialogs", "pywin.dialogs.list", "os.path", 'mx', 'mx.DateTime']
116 includes = []
117
118 class Target:
119 def __init__(self, **kw):
120 # for the versioninfo resources
121 self.name = "Hello"
122 self.description = "A simple app to demo py2exe and Inno Setup."
123 self.company_name = ""
124 self.copyright = ""
125 self.version = __version__
126 self.__dict__.update(kw)
127
128
129 setup(
130 options = {"py2exe": {
131 "excludes": excludes,
132 "includes": includes}},
133 zipfile = "lib/shared.lib",
134 console = [ Target(script = "hello.py",
135 name = "Hello",
136 description = "Monitors directories and updates MLC."),
137 ],
138 data_files = [('',['data.txt'])
139 ],
140 cmdclass = {"py2exe": build_installer},
141 )
Running python setup.py py2exe should create a dist directory containing the usual output from py2exe, an InnoSetup config file and the actual installer executable. {{{C:\Data\BrianDocuments\home\python\Py2exeInnoDemo>dir dist
- Volume in drive C is Local Disk Volume Serial Number is 1493-98B5
Directory of C:\Data\BrianDocuments\home\python\Py2exeInnoDemo\dist
12/09/2004 01:42p <DIR> . 12/09/2004 01:42p <DIR> .. 12/09/2004 01:25p 70 data.txt 12/09/2004 01:42p 20,480 hello.exe 12/09/2004 01:42p 816 Hello.iss 12/09/2004 01:42p 699,942 Hello_0.0.1.0_Setup.exe 12/09/2004 12:52p <DIR> lib 05/25/2004 08:17p 974,909 python23.dll
- 5 File(s) 1,696,217 bytes 3 Dir(s) 1,027,088,384 bytes free
}}}
Yea! You can now distribute Hello_0.0.1.0_Setup.exe and your users will be amazed! Have fun!
For reference, the generated Hello.iss file should look something like this: {{{; WARNING: This script has been created by py2exe. Changes to this script ; will be overwritten the next time py2exe is run!
[Setup] AppName=Hello AppVersion=0.0.1.0 AppVerName=Hello 0.0.1.0 AppPublisher=Your Name Here. DefaultDirName={pf}\Hello DefaultGroupName=Hello Compression=lzma OutputDir=C:\Data\BrianDocuments\home\python\Py2exeInnoDemo\dist\ OutputBaseFilename=Hello_0.0.1.0_Setup
[Files] Source: "hello.exe"; DestDir: "{app}\"; Flags: ignoreversion Source: "lib\_sre.pyd"; DestDir: "{app}\lib"; Flags: ignoreversion Source: "lib\shared.lib"; DestDir: "{app}\lib"; Flags: ignoreversion Source: "lib\w9xpopen.exe"; DestDir: "{app}\lib"; Flags: ignoreversion Source: "python23.dll"; DestDir: "{app}\"; Flags: ignoreversion Source: "data.txt"; DestDir: "{app}\"; Flags: ignoreversion }}}
This configuration barely scratches the surface of what InnoSetup can do. See the Inno Setup website for more info.