blob: b6f2d4ada2a92818d626e5cf288d424bd60edbf1 [file] [log] [blame]
Guido van Rossumf06ee5f1996-11-27 19:52:01 +00001#! /usr/bin/env python
Guido van Rossumdbaf3321994-10-03 10:25:54 +00002
Guido van Rossum96c4dd91996-08-26 05:14:20 +00003"""Freeze a Python script into a binary.
Guido van Rossumdbaf3321994-10-03 10:25:54 +00004
Guido van Rossum96c4dd91996-08-26 05:14:20 +00005usage: freeze [options...] script.py [module]...
Guido van Rossum00ff4331994-10-03 16:33:08 +00006
Guido van Rossum96c4dd91996-08-26 05:14:20 +00007Options:
Guido van Rossum00ff4331994-10-03 16:33:08 +00008
Guido van Rossum9a6e8551997-08-10 16:47:17 +00009-p prefix: This is the prefix used when you ran ``make install''
Guido van Rossum96c4dd91996-08-26 05:14:20 +000010 in the Python build directory.
Guido van Rossumd8336c21994-10-05 16:13:01 +000011 (If you never ran this, freeze won't work.)
Guido van Rossum96c4dd91996-08-26 05:14:20 +000012 The default is whatever sys.prefix evaluates to.
Guido van Rossum0b4b8a21997-08-10 16:56:48 +000013 It can also be the top directory of the Python source
14 tree; then -P must point to the build tree.
Guido van Rossumd8336c21994-10-05 16:13:01 +000015
Guido van Rossum150316e1995-08-08 14:21:07 +000016-P exec_prefix: Like -p but this is the 'exec_prefix', used to
Guido van Rossum0b4b8a21997-08-10 16:56:48 +000017 install objects etc. The default is whatever sys.exec_prefix
18 evaluates to, or the -p argument if given.
19 If -p points to the Python source tree, -P must point
20 to the build tree, if different.
Guido van Rossum150316e1995-08-08 14:21:07 +000021
Guido van Rossumd8336c21994-10-05 16:13:01 +000022-e extension: A directory containing additional .o files that
23 may be used to resolve modules. This directory
24 should also have a Setup file describing the .o files.
25 More than one -e option may be given.
26
Guido van Rossum96c4dd91996-08-26 05:14:20 +000027-o dir: Directory where the output files are created; default '.'.
28
Guido van Rossum75dc4961998-03-05 03:42:00 +000029-m: Additional arguments are module names instead of filenames.
30
31-d: Debugging mode for the module finder.
32
33-q: Make the module finder totally quiet.
34
Guido van Rossum9a6e8551997-08-10 16:47:17 +000035-h: Print this help message.
36
Guido van Rossum58a59481997-08-14 01:45:33 +000037-w: Toggle Windows (NT or 95) behavior.
38 (For debugging only -- on a win32 platform, win32 behaviour
39 is automatic.)
40
41-s subsystem: Specify the subsystem; 'windows' or 'console' (default).
42 (For Windows only.)
43
Guido van Rossum96c4dd91996-08-26 05:14:20 +000044Arguments:
45
Guido van Rossumd4cc04c1996-06-17 17:49:13 +000046script.py: The Python script to be executed by the resulting binary.
Guido van Rossum0b4b8a21997-08-10 16:56:48 +000047 It *must* end with a .py suffix!
Guido van Rossumd8336c21994-10-05 16:13:01 +000048
49module ...: Additional Python modules (referenced by pathname)
50 that will be included in the resulting binary. These
Guido van Rossum75dc4961998-03-05 03:42:00 +000051 may be .py or .pyc files. If -m is specified, these are
52 module names that are search in the path instead.
Guido van Rossum150316e1995-08-08 14:21:07 +000053
54NOTES:
55
56In order to use freeze successfully, you must have built Python and
Guido van Rossumd4cc04c1996-06-17 17:49:13 +000057installed it ("make install").
Guido van Rossum150316e1995-08-08 14:21:07 +000058
Guido van Rossum96c4dd91996-08-26 05:14:20 +000059The script should not use modules provided only as shared libraries;
60if it does, the resulting binary is not self-contained.
Guido van Rossumd8336c21994-10-05 16:13:01 +000061"""
Guido van Rossum00ff4331994-10-03 16:33:08 +000062
63
Guido van Rossum00ff4331994-10-03 16:33:08 +000064# Import standard modules
65
Guido van Rossumd8336c21994-10-05 16:13:01 +000066import cmp
Guido van Rossumdbaf3321994-10-03 10:25:54 +000067import getopt
Guido van Rossum00ff4331994-10-03 16:33:08 +000068import os
Guido van Rossumdbaf3321994-10-03 10:25:54 +000069import string
Guido van Rossum00ff4331994-10-03 16:33:08 +000070import sys
71import addpack
Guido van Rossumdbaf3321994-10-03 10:25:54 +000072
Guido van Rossumdbaf3321994-10-03 10:25:54 +000073
Guido van Rossum00ff4331994-10-03 16:33:08 +000074# Import the freeze-private modules
75
Guido van Rossumd8336c21994-10-05 16:13:01 +000076import checkextensions
Guido van Rossum75dc4961998-03-05 03:42:00 +000077import modulefinder
Guido van Rossum00ff4331994-10-03 16:33:08 +000078import makeconfig
79import makefreeze
80import makemakefile
81import parsesetup
82
Guido van Rossum00ff4331994-10-03 16:33:08 +000083
84# Main program
85
Guido van Rossumdbaf3321994-10-03 10:25:54 +000086def main():
Guido van Rossum0b4b8a21997-08-10 16:56:48 +000087 # overridable context
88 prefix = None # settable with -p option
89 exec_prefix = None # settable with -P option
90 extensions = []
91 path = sys.path
Guido van Rossum75dc4961998-03-05 03:42:00 +000092 modargs = 0
93 debug = 1
Guido van Rossum0b4b8a21997-08-10 16:56:48 +000094 odir = ''
Guido van Rossum58a59481997-08-14 01:45:33 +000095 win = sys.platform[:3] == 'win'
Guido van Rossum00ff4331994-10-03 16:33:08 +000096
Guido van Rossum94ce0d11997-12-08 05:01:06 +000097 # modules that are imported by the Python runtime
98 implicits = ["site", "exceptions"]
99
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000100 # output files
101 frozen_c = 'frozen.c'
102 config_c = 'config.c'
103 target = 'a.out' # normally derived from script name
104 makefile = 'Makefile'
Guido van Rossum58a59481997-08-14 01:45:33 +0000105 subsystem = 'console'
Guido van Rossum00ff4331994-10-03 16:33:08 +0000106
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000107 # parse command line
108 try:
Guido van Rossum75dc4961998-03-05 03:42:00 +0000109 opts, args = getopt.getopt(sys.argv[1:], 'deh:mo:p:P:qs:w')
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000110 except getopt.error, msg:
111 usage('getopt error: ' + str(msg))
Guido van Rossum00ff4331994-10-03 16:33:08 +0000112
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000113 # proces option arguments
114 for o, a in opts:
115 if o == '-h':
116 print __doc__
117 return
Guido van Rossum75dc4961998-03-05 03:42:00 +0000118 if o == '-d':
119 debug = debug + 1
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000120 if o == '-e':
121 extensions.append(a)
Guido van Rossum75dc4961998-03-05 03:42:00 +0000122 if o == '-m':
123 modargs = 1
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000124 if o == '-o':
125 odir = a
126 if o == '-p':
127 prefix = a
128 if o == '-P':
129 exec_prefix = a
Guido van Rossum75dc4961998-03-05 03:42:00 +0000130 if o == '-q':
131 debug = 0
Guido van Rossum58a59481997-08-14 01:45:33 +0000132 if o == '-w':
133 win = not win
134 if o == '-s':
135 if not win:
136 usage("-s subsystem option only on Windows")
137 subsystem = a
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000138
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000139 # default prefix and exec_prefix
140 if not exec_prefix:
141 if prefix:
142 exec_prefix = prefix
143 else:
144 exec_prefix = sys.exec_prefix
145 if not prefix:
146 prefix = sys.prefix
Guido van Rossum9a6e8551997-08-10 16:47:17 +0000147
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000148 # determine whether -p points to the Python source tree
149 ishome = os.path.exists(os.path.join(prefix, 'Include', 'pythonrun.h'))
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000150
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000151 # locations derived from options
152 version = sys.version[:3]
153 if ishome:
154 print "(Using Python source directory)"
155 binlib = exec_prefix
156 incldir = os.path.join(prefix, 'Include')
157 config_c_in = os.path.join(prefix, 'Modules', 'config.c.in')
Guido van Rossum58a59481997-08-14 01:45:33 +0000158 frozenmain_c = os.path.join(prefix, 'Python', 'frozenmain.c')
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000159 makefile_in = os.path.join(exec_prefix, 'Modules', 'Makefile')
160 else:
161 binlib = os.path.join(exec_prefix,
162 'lib', 'python%s' % version, 'config')
163 incldir = os.path.join(prefix, 'include', 'python%s' % version)
164 config_c_in = os.path.join(binlib, 'config.c.in')
165 frozenmain_c = os.path.join(binlib, 'frozenmain.c')
166 makefile_in = os.path.join(binlib, 'Makefile')
167 supp_sources = []
168 defines = []
169 includes = ['-I' + incldir, '-I' + binlib]
Guido van Rossumd8336c21994-10-05 16:13:01 +0000170
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000171 # sanity check of directories and files
172 for dir in [prefix, exec_prefix, binlib, incldir] + extensions:
173 if not os.path.exists(dir):
174 usage('needed directory %s not found' % dir)
175 if not os.path.isdir(dir):
176 usage('%s: not a directory' % dir)
Guido van Rossum58a59481997-08-14 01:45:33 +0000177 if win:
178 files = supp_sources
179 else:
180 files = [config_c_in, makefile_in] + supp_sources
181 for file in supp_sources:
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000182 if not os.path.exists(file):
183 usage('needed file %s not found' % file)
184 if not os.path.isfile(file):
185 usage('%s: not a plain file' % file)
Guido van Rossum58a59481997-08-14 01:45:33 +0000186 if not win:
187 for dir in extensions:
188 setup = os.path.join(dir, 'Setup')
189 if not os.path.exists(setup):
190 usage('needed file %s not found' % setup)
191 if not os.path.isfile(setup):
192 usage('%s: not a plain file' % setup)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000193
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000194 # check that enough arguments are passed
195 if not args:
196 usage('at least one filename argument required')
Guido van Rossumd4cc04c1996-06-17 17:49:13 +0000197
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000198 # check that the script name ends in ".py"
199 if args[0][-3:] != ".py":
200 usage('the script name must have a .py suffix')
Guido van Rossum00ff4331994-10-03 16:33:08 +0000201
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000202 # check that file arguments exist
203 for arg in args:
204 if not os.path.exists(arg):
205 usage('argument %s not found' % arg)
206 if not os.path.isfile(arg):
207 usage('%s: not a plain file' % arg)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000208
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000209 # process non-option arguments
210 scriptfile = args[0]
211 modules = args[1:]
Guido van Rossum00ff4331994-10-03 16:33:08 +0000212
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000213 # derive target name from script name
214 base = os.path.basename(scriptfile)
215 base, ext = os.path.splitext(base)
216 if base:
217 if base != scriptfile:
218 target = base
219 else:
220 target = base + '.bin'
Guido van Rossum00ff4331994-10-03 16:33:08 +0000221
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000222 # handle -o option
223 base_frozen_c = frozen_c
224 base_config_c = config_c
225 base_target = target
226 if odir and not os.path.isdir(odir):
227 try:
228 os.mkdir(odir)
229 print "Created output directory", odir
230 except os.error, msg:
231 usage('%s: mkdir failed (%s)' % (odir, str(msg)))
232 if odir:
233 frozen_c = os.path.join(odir, frozen_c)
234 config_c = os.path.join(odir, config_c)
235 target = os.path.join(odir, target)
236 makefile = os.path.join(odir, makefile)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000237
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000238 # Actual work starts here...
Guido van Rossumd8336c21994-10-05 16:13:01 +0000239
Guido van Rossum75dc4961998-03-05 03:42:00 +0000240 # collect all modules of the program
241 mf = modulefinder.ModuleFinder(path, debug)
242 for mod in implicits:
243 mf.import_hook(mod)
244 for mod in modules:
245 if mod == '-m':
246 modargs = 1
247 continue
248 if modargs:
249 if mod[-2:] == '.*':
250 mf.import_hook(mod[:-2], None, ["*"])
251 else:
252 mf.import_hook(mod)
253 else:
254 mf.load_file(mod)
255 mf.run_script(scriptfile)
256 if debug > 0:
257 mf.report()
258 print
259 dict = mf.modules
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000260
Guido van Rossum75dc4961998-03-05 03:42:00 +0000261 # generate output for frozen modules
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000262 backup = frozen_c + '~'
263 try:
264 os.rename(frozen_c, backup)
265 except os.error:
266 backup = None
267 outfp = open(frozen_c, 'w')
268 try:
Guido van Rossum75dc4961998-03-05 03:42:00 +0000269 makefreeze.makefreeze(outfp, dict, debug)
Guido van Rossumf8883501998-03-04 18:12:39 +0000270 if win and subsystem == 'windows':
271 import winmakemakefile
272 outfp.write(winmakemakefile.WINMAINTEMPLATE)
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000273 finally:
274 outfp.close()
275 if backup:
276 if cmp.cmp(backup, frozen_c):
277 sys.stderr.write('%s not changed, not written\n' %
278 frozen_c)
279 os.rename(backup, frozen_c)
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000280
Guido van Rossum75dc4961998-03-05 03:42:00 +0000281 # windows gets different treatment
Guido van Rossum58a59481997-08-14 01:45:33 +0000282 if win:
283 # Taking a shortcut here...
284 import winmakemakefile
285 outfp = open(makefile, 'w')
286 try:
287 winmakemakefile.makemakefile(outfp,
288 locals(),
289 [frozenmain_c, frozen_c],
290 target)
291 finally:
292 outfp.close()
293 return
294
Guido van Rossum75dc4961998-03-05 03:42:00 +0000295 # generate config.c and Makefile
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000296 builtins = []
297 unknown = []
298 mods = dict.keys()
299 mods.sort()
300 for mod in mods:
Guido van Rossum75dc4961998-03-05 03:42:00 +0000301 if dict[mod].__code__:
302 continue
303 if not dict[mod].__file__:
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000304 builtins.append(mod)
Guido van Rossum75dc4961998-03-05 03:42:00 +0000305 else:
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000306 unknown.append(mod)
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000307
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000308 addfiles = []
309 if unknown:
310 addfiles, addmods = \
311 checkextensions.checkextensions(unknown, extensions)
312 for mod in addmods:
313 unknown.remove(mod)
314 builtins = builtins + addmods
315 if unknown:
316 sys.stderr.write('Warning: unknown modules remain: %s\n' %
317 string.join(unknown))
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000318
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000319 builtins.sort()
320 infp = open(config_c_in)
321 backup = config_c + '~'
322 try:
323 os.rename(config_c, backup)
324 except os.error:
325 backup = None
326 outfp = open(config_c, 'w')
327 try:
328 makeconfig.makeconfig(infp, outfp, builtins)
329 finally:
330 outfp.close()
331 infp.close()
332 if backup:
333 if cmp.cmp(backup, config_c):
334 sys.stderr.write('%s not changed, not written\n' %
335 config_c)
336 os.rename(backup, config_c)
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000337
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000338 cflags = defines + includes + ['$(OPT)']
339 libs = [os.path.join(binlib, 'libpython$(VERSION).a')]
Guido van Rossum00ff4331994-10-03 16:33:08 +0000340
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000341 somevars = {}
Guido van Rossum345df171997-11-22 22:10:01 +0000342 if os.path.exists(makefile_in):
343 makevars = parsesetup.getmakevars(makefile_in)
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000344 for key in makevars.keys():
345 somevars[key] = makevars[key]
Guido van Rossum00ff4331994-10-03 16:33:08 +0000346
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000347 somevars['CFLAGS'] = string.join(cflags) # override
348 files = ['$(OPT)', '$(LDFLAGS)', base_config_c, base_frozen_c] + \
349 supp_sources + addfiles + libs + \
350 ['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)']
Guido van Rossum00ff4331994-10-03 16:33:08 +0000351
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000352 backup = makefile + '~'
353 try:
354 os.rename(makefile, backup)
355 except os.error:
356 backup = None
357 outfp = open(makefile, 'w')
358 try:
359 makemakefile.makemakefile(outfp, somevars, files, base_target)
360 finally:
361 outfp.close()
362 if backup:
363 if not cmp.cmp(backup, makefile):
364 print 'previous Makefile saved as', backup
365 else:
366 sys.stderr.write('%s not changed, not written\n' %
367 makefile)
368 os.rename(backup, makefile)
369
370 # Done!
371
372 if odir:
373 print 'Now run "make" in', odir,
374 print 'to build the target:', base_target
375 else:
376 print 'Now run "make" to build the target:', base_target
Guido van Rossum00ff4331994-10-03 16:33:08 +0000377
Guido van Rossumd8336c21994-10-05 16:13:01 +0000378
379# Print usage message and exit
380
Guido van Rossum9a6e8551997-08-10 16:47:17 +0000381def usage(msg):
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000382 sys.stdout = sys.stderr
383 print "Error:", msg
384 print "Use ``%s -h'' for help" % sys.argv[0]
385 sys.exit(2)
Guido van Rossumd8336c21994-10-05 16:13:01 +0000386
387
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000388main()
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000389
390# Local Variables:
391# indent-tabs-mode: nil
392# End: