blob: 3de6bdbc346f8c42ed4bcba0b51bf4605284a27f [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 Rossum1e074031998-03-05 04:05:38 +00005usage: freeze [options...] script [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 Rossum1e074031998-03-05 04:05:38 +000046script: The Python script to be executed by the resulting binary.
Guido van Rossumd8336c21994-10-05 16:13:01 +000047
48module ...: Additional Python modules (referenced by pathname)
49 that will be included in the resulting binary. These
Guido van Rossum75dc4961998-03-05 03:42:00 +000050 may be .py or .pyc files. If -m is specified, these are
51 module names that are search in the path instead.
Guido van Rossum150316e1995-08-08 14:21:07 +000052
53NOTES:
54
55In order to use freeze successfully, you must have built Python and
Guido van Rossumd4cc04c1996-06-17 17:49:13 +000056installed it ("make install").
Guido van Rossum150316e1995-08-08 14:21:07 +000057
Guido van Rossum96c4dd91996-08-26 05:14:20 +000058The script should not use modules provided only as shared libraries;
59if it does, the resulting binary is not self-contained.
Guido van Rossumd8336c21994-10-05 16:13:01 +000060"""
Guido van Rossum00ff4331994-10-03 16:33:08 +000061
62
Guido van Rossum00ff4331994-10-03 16:33:08 +000063# Import standard modules
64
Guido van Rossumd8336c21994-10-05 16:13:01 +000065import cmp
Guido van Rossumdbaf3321994-10-03 10:25:54 +000066import getopt
Guido van Rossum00ff4331994-10-03 16:33:08 +000067import os
Guido van Rossumdbaf3321994-10-03 10:25:54 +000068import string
Guido van Rossum00ff4331994-10-03 16:33:08 +000069import sys
70import addpack
Guido van Rossumdbaf3321994-10-03 10:25:54 +000071
Guido van Rossumdbaf3321994-10-03 10:25:54 +000072
Guido van Rossum00ff4331994-10-03 16:33:08 +000073# Import the freeze-private modules
74
Guido van Rossumd8336c21994-10-05 16:13:01 +000075import checkextensions
Guido van Rossum75dc4961998-03-05 03:42:00 +000076import modulefinder
Guido van Rossum00ff4331994-10-03 16:33:08 +000077import makeconfig
78import makefreeze
79import makemakefile
80import parsesetup
81
Guido van Rossum00ff4331994-10-03 16:33:08 +000082
83# Main program
84
Guido van Rossumdbaf3321994-10-03 10:25:54 +000085def main():
Guido van Rossum0b4b8a21997-08-10 16:56:48 +000086 # overridable context
87 prefix = None # settable with -p option
88 exec_prefix = None # settable with -P option
89 extensions = []
Guido van Rossum1e074031998-03-05 04:05:38 +000090 path = sys.path[:]
Guido van Rossum75dc4961998-03-05 03:42:00 +000091 modargs = 0
92 debug = 1
Guido van Rossum0b4b8a21997-08-10 16:56:48 +000093 odir = ''
Guido van Rossum58a59481997-08-14 01:45:33 +000094 win = sys.platform[:3] == 'win'
Guido van Rossum00ff4331994-10-03 16:33:08 +000095
Guido van Rossum94ce0d11997-12-08 05:01:06 +000096 # modules that are imported by the Python runtime
97 implicits = ["site", "exceptions"]
98
Guido van Rossum0b4b8a21997-08-10 16:56:48 +000099 # output files
100 frozen_c = 'frozen.c'
101 config_c = 'config.c'
102 target = 'a.out' # normally derived from script name
103 makefile = 'Makefile'
Guido van Rossum58a59481997-08-14 01:45:33 +0000104 subsystem = 'console'
Guido van Rossum00ff4331994-10-03 16:33:08 +0000105
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000106 # parse command line
107 try:
Guido van Rossumb7da6421998-03-06 17:44:57 +0000108 opts, args = getopt.getopt(sys.argv[1:], 'de:hmo:p:P:qs:w')
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000109 except getopt.error, msg:
110 usage('getopt error: ' + str(msg))
Guido van Rossum00ff4331994-10-03 16:33:08 +0000111
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000112 # proces option arguments
113 for o, a in opts:
114 if o == '-h':
115 print __doc__
116 return
Guido van Rossum75dc4961998-03-05 03:42:00 +0000117 if o == '-d':
118 debug = debug + 1
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000119 if o == '-e':
120 extensions.append(a)
Guido van Rossum75dc4961998-03-05 03:42:00 +0000121 if o == '-m':
122 modargs = 1
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000123 if o == '-o':
124 odir = a
125 if o == '-p':
126 prefix = a
127 if o == '-P':
128 exec_prefix = a
Guido van Rossum75dc4961998-03-05 03:42:00 +0000129 if o == '-q':
130 debug = 0
Guido van Rossum58a59481997-08-14 01:45:33 +0000131 if o == '-w':
132 win = not win
133 if o == '-s':
134 if not win:
135 usage("-s subsystem option only on Windows")
136 subsystem = a
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000137
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000138 # default prefix and exec_prefix
139 if not exec_prefix:
140 if prefix:
141 exec_prefix = prefix
142 else:
143 exec_prefix = sys.exec_prefix
144 if not prefix:
145 prefix = sys.prefix
Guido van Rossum9a6e8551997-08-10 16:47:17 +0000146
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000147 # determine whether -p points to the Python source tree
Guido van Rossume0394251998-03-05 05:39:50 +0000148 ishome = os.path.exists(os.path.join(prefix, 'Python', 'ceval.c'))
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000149
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000150 # locations derived from options
151 version = sys.version[:3]
152 if ishome:
153 print "(Using Python source directory)"
154 binlib = exec_prefix
155 incldir = os.path.join(prefix, 'Include')
156 config_c_in = os.path.join(prefix, 'Modules', 'config.c.in')
Guido van Rossum58a59481997-08-14 01:45:33 +0000157 frozenmain_c = os.path.join(prefix, 'Python', 'frozenmain.c')
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000158 makefile_in = os.path.join(exec_prefix, 'Modules', 'Makefile')
159 else:
160 binlib = os.path.join(exec_prefix,
161 'lib', 'python%s' % version, 'config')
162 incldir = os.path.join(prefix, 'include', 'python%s' % version)
163 config_c_in = os.path.join(binlib, 'config.c.in')
164 frozenmain_c = os.path.join(binlib, 'frozenmain.c')
165 makefile_in = os.path.join(binlib, 'Makefile')
166 supp_sources = []
167 defines = []
168 includes = ['-I' + incldir, '-I' + binlib]
Guido van Rossumd8336c21994-10-05 16:13:01 +0000169
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000170 # sanity check of directories and files
171 for dir in [prefix, exec_prefix, binlib, incldir] + extensions:
172 if not os.path.exists(dir):
173 usage('needed directory %s not found' % dir)
174 if not os.path.isdir(dir):
175 usage('%s: not a directory' % dir)
Guido van Rossum58a59481997-08-14 01:45:33 +0000176 if win:
177 files = supp_sources
178 else:
179 files = [config_c_in, makefile_in] + supp_sources
180 for file in supp_sources:
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000181 if not os.path.exists(file):
182 usage('needed file %s not found' % file)
183 if not os.path.isfile(file):
184 usage('%s: not a plain file' % file)
Guido van Rossum58a59481997-08-14 01:45:33 +0000185 if not win:
186 for dir in extensions:
187 setup = os.path.join(dir, 'Setup')
188 if not os.path.exists(setup):
189 usage('needed file %s not found' % setup)
190 if not os.path.isfile(setup):
191 usage('%s: not a plain file' % setup)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000192
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000193 # check that enough arguments are passed
194 if not args:
195 usage('at least one filename argument required')
Guido van Rossumd4cc04c1996-06-17 17:49:13 +0000196
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000197 # check that file arguments exist
198 for arg in args:
Guido van Rossum1e074031998-03-05 04:05:38 +0000199 if arg == '-m':
200 break
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000201 if not os.path.exists(arg):
202 usage('argument %s not found' % arg)
203 if not os.path.isfile(arg):
204 usage('%s: not a plain file' % arg)
Guido van Rossum1e074031998-03-05 04:05:38 +0000205 if modargs:
206 break
Guido van Rossum00ff4331994-10-03 16:33:08 +0000207
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000208 # process non-option arguments
209 scriptfile = args[0]
210 modules = args[1:]
Guido van Rossum00ff4331994-10-03 16:33:08 +0000211
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000212 # derive target name from script name
213 base = os.path.basename(scriptfile)
214 base, ext = os.path.splitext(base)
215 if base:
216 if base != scriptfile:
217 target = base
218 else:
219 target = base + '.bin'
Guido van Rossum00ff4331994-10-03 16:33:08 +0000220
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000221 # handle -o option
222 base_frozen_c = frozen_c
223 base_config_c = config_c
224 base_target = target
225 if odir and not os.path.isdir(odir):
226 try:
227 os.mkdir(odir)
228 print "Created output directory", odir
229 except os.error, msg:
230 usage('%s: mkdir failed (%s)' % (odir, str(msg)))
231 if odir:
232 frozen_c = os.path.join(odir, frozen_c)
233 config_c = os.path.join(odir, config_c)
234 target = os.path.join(odir, target)
235 makefile = os.path.join(odir, makefile)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000236
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000237 # Actual work starts here...
Guido van Rossumd8336c21994-10-05 16:13:01 +0000238
Guido van Rossum75dc4961998-03-05 03:42:00 +0000239 # collect all modules of the program
Guido van Rossum1e074031998-03-05 04:05:38 +0000240 dir = os.path.dirname(scriptfile)
241 path[0] = dir
Guido van Rossum75dc4961998-03-05 03:42:00 +0000242 mf = modulefinder.ModuleFinder(path, debug)
243 for mod in implicits:
244 mf.import_hook(mod)
245 for mod in modules:
246 if mod == '-m':
247 modargs = 1
248 continue
249 if modargs:
250 if mod[-2:] == '.*':
251 mf.import_hook(mod[:-2], None, ["*"])
252 else:
253 mf.import_hook(mod)
254 else:
255 mf.load_file(mod)
256 mf.run_script(scriptfile)
257 if debug > 0:
258 mf.report()
259 print
260 dict = mf.modules
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000261
Guido van Rossum75dc4961998-03-05 03:42:00 +0000262 # generate output for frozen modules
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000263 backup = frozen_c + '~'
264 try:
265 os.rename(frozen_c, backup)
266 except os.error:
267 backup = None
268 outfp = open(frozen_c, 'w')
269 try:
Guido van Rossum75dc4961998-03-05 03:42:00 +0000270 makefreeze.makefreeze(outfp, dict, debug)
Guido van Rossumf8883501998-03-04 18:12:39 +0000271 if win and subsystem == 'windows':
272 import winmakemakefile
273 outfp.write(winmakemakefile.WINMAINTEMPLATE)
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000274 finally:
275 outfp.close()
276 if backup:
277 if cmp.cmp(backup, frozen_c):
Guido van Rossume0394251998-03-05 05:39:50 +0000278 sys.stderr.write('%s not changed, not written\n' % frozen_c)
279 os.unlink(frozen_c)
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000280 os.rename(backup, frozen_c)
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000281
Guido van Rossuma0e18351998-03-07 04:51:03 +0000282 # look for unfrozen modules (builtin and of unknown origin)
283 builtins = []
284 unknown = []
285 mods = dict.keys()
286 mods.sort()
287 for mod in mods:
288 if dict[mod].__code__:
289 continue
290 if not dict[mod].__file__:
291 builtins.append(mod)
292 else:
293 unknown.append(mod)
294
295 # search for unknown modules in extensions directories (not on Windows)
296 addfiles = []
297 if unknown and not win:
298 addfiles, addmods = \
299 checkextensions.checkextensions(unknown, extensions)
300 for mod in addmods:
301 unknown.remove(mod)
302 builtins = builtins + addmods
303
304 # report unknown modules
305 if unknown:
306 sys.stderr.write('Warning: unknown modules remain: %s\n' %
307 string.join(unknown))
308
Guido van Rossum75dc4961998-03-05 03:42:00 +0000309 # windows gets different treatment
Guido van Rossum58a59481997-08-14 01:45:33 +0000310 if win:
311 # Taking a shortcut here...
312 import winmakemakefile
313 outfp = open(makefile, 'w')
314 try:
315 winmakemakefile.makemakefile(outfp,
316 locals(),
Guido van Rossum31d53ed1998-03-07 04:08:04 +0000317 [frozenmain_c,
318 os.path.basename(frozen_c)],
319 os.path.basename(target))
Guido van Rossum58a59481997-08-14 01:45:33 +0000320 finally:
321 outfp.close()
322 return
323
Guido van Rossum75dc4961998-03-05 03:42:00 +0000324 # generate config.c and Makefile
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000325 builtins.sort()
326 infp = open(config_c_in)
327 backup = config_c + '~'
328 try:
329 os.rename(config_c, backup)
330 except os.error:
331 backup = None
332 outfp = open(config_c, 'w')
333 try:
334 makeconfig.makeconfig(infp, outfp, builtins)
335 finally:
336 outfp.close()
337 infp.close()
338 if backup:
339 if cmp.cmp(backup, config_c):
Guido van Rossume0394251998-03-05 05:39:50 +0000340 sys.stderr.write('%s not changed, not written\n' % config_c)
341 os.unlink(config_c)
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000342 os.rename(backup, config_c)
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000343
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000344 cflags = defines + includes + ['$(OPT)']
345 libs = [os.path.join(binlib, 'libpython$(VERSION).a')]
Guido van Rossum00ff4331994-10-03 16:33:08 +0000346
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000347 somevars = {}
Guido van Rossum345df171997-11-22 22:10:01 +0000348 if os.path.exists(makefile_in):
349 makevars = parsesetup.getmakevars(makefile_in)
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000350 for key in makevars.keys():
351 somevars[key] = makevars[key]
Guido van Rossum00ff4331994-10-03 16:33:08 +0000352
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000353 somevars['CFLAGS'] = string.join(cflags) # override
354 files = ['$(OPT)', '$(LDFLAGS)', base_config_c, base_frozen_c] + \
355 supp_sources + addfiles + libs + \
356 ['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)']
Guido van Rossum00ff4331994-10-03 16:33:08 +0000357
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000358 backup = makefile + '~'
Guido van Rossume0394251998-03-05 05:39:50 +0000359 if os.path.exists(makefile):
360 try:
361 os.unlink(backup)
362 except os.error:
363 pass
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000364 try:
365 os.rename(makefile, backup)
366 except os.error:
367 backup = None
368 outfp = open(makefile, 'w')
369 try:
370 makemakefile.makemakefile(outfp, somevars, files, base_target)
371 finally:
372 outfp.close()
373 if backup:
374 if not cmp.cmp(backup, makefile):
375 print 'previous Makefile saved as', backup
376 else:
Guido van Rossume0394251998-03-05 05:39:50 +0000377 sys.stderr.write('%s not changed, not written\n' % makefile)
378 os.unlink(makefile)
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000379 os.rename(backup, makefile)
380
381 # Done!
382
383 if odir:
384 print 'Now run "make" in', odir,
385 print 'to build the target:', base_target
386 else:
387 print 'Now run "make" to build the target:', base_target
Guido van Rossum00ff4331994-10-03 16:33:08 +0000388
Guido van Rossumd8336c21994-10-05 16:13:01 +0000389
390# Print usage message and exit
391
Guido van Rossum9a6e8551997-08-10 16:47:17 +0000392def usage(msg):
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000393 sys.stdout = sys.stderr
394 print "Error:", msg
395 print "Use ``%s -h'' for help" % sys.argv[0]
396 sys.exit(2)
Guido van Rossumd8336c21994-10-05 16:13:01 +0000397
398
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000399main()
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000400
401# Local Variables:
402# indent-tabs-mode: nil
403# End: