|
1 #! /usr/bin/env/python |
|
2 # Automatically build spec files containing a description of the project |
|
3 # Copyright (C) 2005, Giovanni Bajo |
|
4 # Based on previous work under copyright (c) 2002 McMillan Enterprises, Inc. |
|
5 # |
|
6 # This program is free software; you can redistribute it and/or |
|
7 # modify it under the terms of the GNU General Public License |
|
8 # as published by the Free Software Foundation; either version 2 |
|
9 # of the License, or (at your option) any later version. |
|
10 # |
|
11 # This program is distributed in the hope that it will be useful, |
|
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 # GNU General Public License for more details. |
|
15 # |
|
16 # You should have received a copy of the GNU General Public License |
|
17 # along with this program; if not, write to the Free Software |
|
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|
19 |
|
20 import sys, os, string |
|
21 |
|
22 # For Python 1.5 compatibility |
|
23 try: |
|
24 True |
|
25 except: |
|
26 True,False = 1,0 |
|
27 |
|
28 freezetmplt = """\ |
|
29 a = Analysis(%(scripts)s, |
|
30 pathex=%(pathex)s) |
|
31 pyz = PYZ(a.pure) |
|
32 exe = EXE(%(tkpkg)s pyz, |
|
33 a.scripts, |
|
34 a.binaries, |
|
35 name='%(exename)s', |
|
36 debug=%(debug)s, |
|
37 strip=%(strip)s, |
|
38 upx=%(upx)s, |
|
39 console=%(console)s %(exe_options)s) |
|
40 """ # pathex scripts exename tkpkg debug console |
|
41 |
|
42 collecttmplt = """\ |
|
43 a = Analysis(%(scripts)s, |
|
44 pathex=%(pathex)s) |
|
45 pyz = PYZ(a.pure) |
|
46 exe = EXE(pyz, |
|
47 a.scripts, |
|
48 exclude_binaries=1, |
|
49 name='%(builddir)s/%(exename)s', |
|
50 debug=%(debug)s, |
|
51 strip=%(strip)s, |
|
52 upx=%(upx)s, |
|
53 console=%(console)s %(exe_options)s) |
|
54 coll = COLLECT(%(tktree)s exe, |
|
55 a.binaries, |
|
56 strip=%(strip)s, |
|
57 upx=%(upx)s, |
|
58 name='%(distdir)s') |
|
59 """ # scripts pathex, exename, debug, console tktree distdir |
|
60 |
|
61 comsrvrtmplt = """\ |
|
62 a = Analysis(%(scripts)s, |
|
63 pathex=%(pathex)s) |
|
64 pyz = PYZ(a.pure) |
|
65 exe = EXE(pyz, |
|
66 a.scripts, |
|
67 exclude_binaries=1, |
|
68 name='%(builddir)s/%(exename)s', |
|
69 debug=%(debug)s, |
|
70 strip=%(strip)s, |
|
71 upx=%(upx)s, |
|
72 console=%(console)s %(exe_options)s) |
|
73 dll = DLL(pyz, |
|
74 a.scripts, |
|
75 exclude_binaries=1, |
|
76 name='%(builddir)s/%(dllname)s', |
|
77 debug=%(debug)s) |
|
78 coll = COLLECT(exe, dll, |
|
79 a.binaries, |
|
80 strip=%(strip)s, |
|
81 upx=%(upx)s, |
|
82 name='%(distdir)s') |
|
83 """ # scripts pathex, exename, debug, console tktree distdir |
|
84 HOME = os.path.dirname(sys.argv[0]) |
|
85 if HOME == '': |
|
86 HOME = os.getcwd() |
|
87 if not os.path.isabs(HOME): |
|
88 HOME = os.path.abspath(HOME) |
|
89 iswin = sys.platform[:3] == "win" |
|
90 cygwin = sys.platform == "cygwin" |
|
91 try: |
|
92 config = eval(open(os.path.join(HOME, 'config.dat'), 'r').read()) |
|
93 except IOError: |
|
94 print "You must run Configure.py before building!" |
|
95 sys.exit(1) |
|
96 |
|
97 if config['pythonVersion'] != sys.version: |
|
98 print "The current version of Python is not the same with which PyInstaller was configured." |
|
99 print "Please re-run Configure.py with this version." |
|
100 sys.exit(1) |
|
101 |
|
102 def quote_win_filepath( path ): |
|
103 # quote all \ with another \ after using normpath to clean up the path |
|
104 return string.join( string.split( os.path.normpath( path ), '\\' ), '\\\\' ) |
|
105 |
|
106 # Support for trying to avoid hard-coded paths in the .spec files. |
|
107 # Eg, all files rooted in the Installer directory tree will be |
|
108 # written using "HOMEPATH", thus allowing this spec file to |
|
109 # be used with any Installer installation. |
|
110 # Same thing could be done for other paths too. |
|
111 path_conversions = ( |
|
112 (HOME, "HOMEPATH"), |
|
113 # Add Tk etc? |
|
114 ) |
|
115 |
|
116 def make_variable_path(filename, conversions = path_conversions): |
|
117 for (from_path, to_name) in conversions: |
|
118 assert os.path.abspath(from_path)==from_path, \ |
|
119 "path '%s' should already be absolute" % (from_path,) |
|
120 if filename[:len(from_path)] == from_path: |
|
121 rest = filename[len(from_path):] |
|
122 if rest[0] in "\\/": |
|
123 rest = rest[1:] |
|
124 return to_name, rest |
|
125 return None, filename |
|
126 |
|
127 # An object used in place of a "path string" which knows how to repr() |
|
128 # itself using variable names instead of hard-coded paths. |
|
129 class Path: |
|
130 def __init__(self, *parts): |
|
131 self.path = apply(os.path.join, parts) |
|
132 self.variable_prefix = self.filename_suffix = None |
|
133 def __repr__(self): |
|
134 if self.filename_suffix is None: |
|
135 self.variable_prefix, self.filename_suffix = make_variable_path(self.path) |
|
136 if self.variable_prefix is None: |
|
137 return repr(self.path) |
|
138 return "os.path.join(" + self.variable_prefix + "," + repr(self.filename_suffix) + ")" |
|
139 |
|
140 def main(scripts, name=None, tk=0, freeze=0, console=1, debug=0, strip=0, upx=0, |
|
141 comserver=0, ascii=0, workdir=None, pathex=[], version_file=None, icon_file=None): |
|
142 if name is None: |
|
143 name = os.path.splitext(os.path.basename(scripts[0]))[0] |
|
144 distdir = "dist%s" % name |
|
145 builddir = "build%s" % name |
|
146 pathex = pathex[:] |
|
147 if workdir is None: |
|
148 workdir = os.getcwd() |
|
149 pathex.append(workdir) |
|
150 else: |
|
151 pathex.append(os.getcwd()) |
|
152 if workdir == HOME: |
|
153 workdir = os.path.join(HOME, name) |
|
154 if not os.path.exists(workdir): |
|
155 os.makedirs(workdir) |
|
156 exe_options = '' |
|
157 if version_file: |
|
158 exe_options = "%s, version='%s'" % (exe_options, quote_win_filepath(version_file)) |
|
159 if icon_file: |
|
160 exe_options = "%s, icon='%s'" % (exe_options, quote_win_filepath(icon_file)) |
|
161 if not ascii and config['hasUnicode']: |
|
162 scripts.insert(0, os.path.join(HOME, 'support', 'useUnicode.py')) |
|
163 for i in range(len(scripts)): |
|
164 scripts[i] = Path(scripts[i]) # Use relative path in specfiles |
|
165 |
|
166 d = {'tktree':'', |
|
167 'tkpkg' :'', |
|
168 'scripts':scripts, |
|
169 'pathex' :pathex, |
|
170 'exename': '', |
|
171 'distdir': distdir, |
|
172 'builddir': builddir, |
|
173 'debug': debug, |
|
174 'strip': strip, |
|
175 'upx' : upx, |
|
176 'console': console or debug, |
|
177 'exe_options': exe_options} |
|
178 if tk: |
|
179 d['tktree'] = "TkTree()," |
|
180 if freeze: |
|
181 scripts.insert(0, Path(HOME, 'support', 'useTK.py')) |
|
182 scripts.insert(0, Path(HOME, 'support', 'unpackTK.py')) |
|
183 scripts.append(Path(HOME, 'support', 'removeTK.py')) |
|
184 d['tkpkg'] = "TkPKG()," |
|
185 else: |
|
186 scripts.insert(0, Path(HOME, 'support', 'useTK.py')) |
|
187 scripts.insert(0, Path(HOME, 'support', '_mountzlib.py')) |
|
188 if iswin or cygwin: |
|
189 d['exename'] = name+'.exe' |
|
190 d['dllname'] = name+'.dll' |
|
191 else: |
|
192 d['exename'] = name |
|
193 d['console'] = 1 |
|
194 specfnm = os.path.join(workdir, name+'.spec') |
|
195 specfile = open(specfnm, 'w') |
|
196 if freeze: |
|
197 specfile.write(freezetmplt % d) |
|
198 elif comserver: |
|
199 specfile.write(comsrvrtmplt % d) |
|
200 else: |
|
201 specfile.write(collecttmplt % d) |
|
202 specfile.close() |
|
203 return specfnm |
|
204 |
|
205 if __name__ == '__main__': |
|
206 import optparse |
|
207 p = optparse.OptionParser( |
|
208 usage="python %prog [opts] <scriptname> [<scriptname> ...]" |
|
209 ) |
|
210 p.add_option("-F", "--onefile", dest="freeze", |
|
211 action="store_true", default=False, |
|
212 help="create a single file deployment") |
|
213 p.add_option("-D", "--onedir", dest="freeze", action="store_false", |
|
214 help="create a single directory deployment (default)") |
|
215 p.add_option("-w", "--windowed", "--noconsole", dest="console", |
|
216 action="store_false", default=True, |
|
217 help="use a Windows subsystem executable (Windows only)") |
|
218 p.add_option("-c", "--nowindowed", "--console", dest="console", |
|
219 action="store_true", |
|
220 help="use a console subsystem executable (Windows only) " |
|
221 "(default)") |
|
222 p.add_option("-a", "--ascii", action="store_true", default=False, |
|
223 help="do NOT include unicode encodings " |
|
224 "(default: included if available)") |
|
225 p.add_option("-d", "--debug", action="store_true", default=False, |
|
226 help="use the debug (verbose) build of the executable") |
|
227 p.add_option("-s", "--strip", action="store_true", default=False, |
|
228 help="strip the exe and shared libs " |
|
229 "(don't try this on Windows)") |
|
230 p.add_option("-X", "--upx", action="store_true", default=False, |
|
231 help="use UPX if available (works differently between " |
|
232 "Windows and *nix)") |
|
233 p.add_option("-K", "--tk", default=False, action="store_true", |
|
234 help="include TCL/TK in the deployment") |
|
235 p.add_option("-o", "--out", type="string", default=None, |
|
236 dest="workdir", metavar="DIR", |
|
237 help="generate the spec file in the specified directory") |
|
238 p.add_option("-n", "--name", type="string", default=None, |
|
239 help="name to assign to the project, from which the spec file " |
|
240 "name is generated. (default: use the basename of the " |
|
241 "(first) script)") |
|
242 p.add_option("-p", "--paths", type="string", default=[], dest="pathex", |
|
243 metavar="DIR", action="append", |
|
244 help="set base path for import (like using PYTHONPATH). " |
|
245 "Multiple directories are allowed, separating them " |
|
246 "with %s, or using this option multiple times" |
|
247 % repr(os.pathsep)) |
|
248 p.add_option("-v", "--version", type="string", |
|
249 dest="version_file", metavar="FILE", |
|
250 help="add a version resource from FILE to the exe " |
|
251 "(Windows only)") |
|
252 p.add_option("--icon", type="string", dest="icon_file", |
|
253 metavar="FILE.ICO or FILE.EXE,ID", |
|
254 help="If FILE is an .ico file, add the icon to the final " |
|
255 "executable. Otherwise, the syntax 'file.exe,id' to " |
|
256 "extract the icon with the specified id " |
|
257 "from file.exe and add it to the final executable") |
|
258 |
|
259 opts,args = p.parse_args() |
|
260 |
|
261 # Split pathex by using the path separator |
|
262 temppaths = opts.pathex[:] |
|
263 opts.pathex = [] |
|
264 for p in temppaths: |
|
265 opts.pathex.extend(string.split(p, os.pathsep)) |
|
266 |
|
267 if not args: |
|
268 p.print_help() |
|
269 sys.exit(1) |
|
270 |
|
271 nm = apply(main, (args,), opts.__dict__) |
|
272 print "wrote %s" % nm |
|
273 print "now run Build.py to build the executable" |