Apparently the code to forestall Tk eating events was too aggressive (Tk user input...
[python/dscho.git] / Tools / freeze / freeze.py
blobdfd0369627d8825520905e16c5a4a27db85aeb4b
1 #! /usr/bin/env python
3 """Freeze a Python script into a binary.
5 usage: freeze [options...] script [module]...
7 Options:
8 -p prefix: This is the prefix used when you ran ``make install''
9 in the Python build directory.
10 (If you never ran this, freeze won't work.)
11 The default is whatever sys.prefix evaluates to.
12 It can also be the top directory of the Python source
13 tree; then -P must point to the build tree.
15 -P exec_prefix: Like -p but this is the 'exec_prefix', used to
16 install objects etc. The default is whatever sys.exec_prefix
17 evaluates to, or the -p argument if given.
18 If -p points to the Python source tree, -P must point
19 to the build tree, if different.
21 -e extension: A directory containing additional .o files that
22 may be used to resolve modules. This directory
23 should also have a Setup file describing the .o files.
24 On Windows, the name of a .INI file describing one
25 or more extensions is passed.
26 More than one -e option may be given.
28 -o dir: Directory where the output files are created; default '.'.
30 -m: Additional arguments are module names instead of filenames.
32 -a package=dir: Additional directories to be added to the package's
33 __path__. Used to simulate directories added by the
34 package at runtime (eg, by OpenGL and win32com).
35 More than one -a option may be given for each package.
37 -l file: Pass the file to the linker (windows only)
39 -d: Debugging mode for the module finder.
41 -q: Make the module finder totally quiet.
43 -h: Print this help message.
45 -x module Exclude the specified module.
47 -i filename: Include a file with additional command line options. Used
48 to prevent command lines growing beyond the capabilities of
49 the shell/OS. All arguments specified in filename
50 are read and the -i option replaced with the parsed
51 params (note - quoting args in this file is NOT supported)
53 -s subsystem: Specify the subsystem (For Windows only.);
54 'console' (default), 'windows', 'service' or 'com_dll'
56 -w: Toggle Windows (NT or 95) behavior.
57 (For debugging only -- on a win32 platform, win32 behavior
58 is automatic.)
60 -r prefix=f: Replace path prefix.
61 Replace prefix with f in the source path references
62 contained in the resulting binary.
64 Arguments:
66 script: The Python script to be executed by the resulting binary.
68 module ...: Additional Python modules (referenced by pathname)
69 that will be included in the resulting binary. These
70 may be .py or .pyc files. If -m is specified, these are
71 module names that are search in the path instead.
73 NOTES:
75 In order to use freeze successfully, you must have built Python and
76 installed it ("make install").
78 The script should not use modules provided only as shared libraries;
79 if it does, the resulting binary is not self-contained.
80 """
83 # Import standard modules
85 import getopt
86 import os
87 import string
88 import sys
91 # Import the freeze-private modules
93 import checkextensions
94 import modulefinder
95 import makeconfig
96 import makefreeze
97 import makemakefile
98 import parsesetup
99 import bkfile
102 # Main program
104 def main():
105 # overridable context
106 prefix = None # settable with -p option
107 exec_prefix = None # settable with -P option
108 extensions = []
109 exclude = [] # settable with -x option
110 addn_link = [] # settable with -l, but only honored under Windows.
111 path = sys.path[:]
112 modargs = 0
113 debug = 1
114 odir = ''
115 win = sys.platform[:3] == 'win'
116 replace_paths = [] # settable with -r option
118 # default the exclude list for each platform
119 if win: exclude = exclude + [
120 'dos', 'dospath', 'mac', 'macpath', 'macfs', 'MACFS', 'posix', 'os2', 'ce']
122 # modules that are imported by the Python runtime
123 implicits = ["site", "exceptions"]
125 # output files
126 frozen_c = 'frozen.c'
127 config_c = 'config.c'
128 target = 'a.out' # normally derived from script name
129 makefile = 'Makefile'
130 subsystem = 'console'
132 # parse command line by first replacing any "-i" options with the file contents.
133 pos = 1
134 while pos < len(sys.argv)-1: # last option can not be "-i", so this ensures "pos+1" is in range!
135 if sys.argv[pos] == '-i':
136 try:
137 options = string.split(open(sys.argv[pos+1]).read())
138 except IOError, why:
139 usage("File name '%s' specified with the -i option can not be read - %s" % (sys.argv[pos+1], why) )
140 # Replace the '-i' and the filename with the read params.
141 sys.argv[pos:pos+2] = options
142 pos = pos + len(options) - 1 # Skip the name and the included args.
143 pos = pos + 1
145 # Now parse the command line with the extras inserted.
146 try:
147 opts, args = getopt.getopt(sys.argv[1:], 'r:a:de:hmo:p:P:qs:wx:l:')
148 except getopt.error, msg:
149 usage('getopt error: ' + str(msg))
151 # proces option arguments
152 for o, a in opts:
153 if o == '-h':
154 print __doc__
155 return
156 if o == '-d':
157 debug = debug + 1
158 if o == '-e':
159 extensions.append(a)
160 if o == '-m':
161 modargs = 1
162 if o == '-o':
163 odir = a
164 if o == '-p':
165 prefix = a
166 if o == '-P':
167 exec_prefix = a
168 if o == '-q':
169 debug = 0
170 if o == '-w':
171 win = not win
172 if o == '-s':
173 if not win:
174 usage("-s subsystem option only on Windows")
175 subsystem = a
176 if o == '-x':
177 exclude.append(a)
178 if o == '-l':
179 addn_link.append(a)
180 if o == '-a':
181 apply(modulefinder.AddPackagePath, tuple(string.split(a,"=", 2)))
182 if o == '-r':
183 f,r = string.split(a,"=", 2)
184 replace_paths.append( (f,r) )
186 # default prefix and exec_prefix
187 if not exec_prefix:
188 if prefix:
189 exec_prefix = prefix
190 else:
191 exec_prefix = sys.exec_prefix
192 if not prefix:
193 prefix = sys.prefix
195 # determine whether -p points to the Python source tree
196 ishome = os.path.exists(os.path.join(prefix, 'Python', 'ceval.c'))
198 # locations derived from options
199 version = sys.version[:3]
200 if win:
201 extensions_c = 'frozen_extensions.c'
202 if ishome:
203 print "(Using Python source directory)"
204 binlib = exec_prefix
205 incldir = os.path.join(prefix, 'Include')
206 config_h_dir = exec_prefix
207 config_c_in = os.path.join(prefix, 'Modules', 'config.c.in')
208 frozenmain_c = os.path.join(prefix, 'Python', 'frozenmain.c')
209 makefile_in = os.path.join(exec_prefix, 'Modules', 'Makefile')
210 if win:
211 frozendllmain_c = os.path.join(exec_prefix, 'Pc\\frozen_dllmain.c')
212 else:
213 binlib = os.path.join(exec_prefix,
214 'lib', 'python%s' % version, 'config')
215 incldir = os.path.join(prefix, 'include', 'python%s' % version)
216 config_h_dir = os.path.join(exec_prefix, 'include',
217 'python%s' % version)
218 config_c_in = os.path.join(binlib, 'config.c.in')
219 frozenmain_c = os.path.join(binlib, 'frozenmain.c')
220 makefile_in = os.path.join(binlib, 'Makefile')
221 frozendllmain_c = os.path.join(binlib, 'frozen_dllmain.c')
222 supp_sources = []
223 defines = []
224 includes = ['-I' + incldir, '-I' + config_h_dir]
226 # sanity check of directories and files
227 check_dirs = [prefix, exec_prefix, binlib, incldir]
228 if not win: check_dirs = check_dirs + extensions # These are not directories on Windows.
229 for dir in check_dirs:
230 if not os.path.exists(dir):
231 usage('needed directory %s not found' % dir)
232 if not os.path.isdir(dir):
233 usage('%s: not a directory' % dir)
234 if win:
235 files = supp_sources + extensions # extensions are files on Windows.
236 else:
237 files = [config_c_in, makefile_in] + supp_sources
238 for file in supp_sources:
239 if not os.path.exists(file):
240 usage('needed file %s not found' % file)
241 if not os.path.isfile(file):
242 usage('%s: not a plain file' % file)
243 if not win:
244 for dir in extensions:
245 setup = os.path.join(dir, 'Setup')
246 if not os.path.exists(setup):
247 usage('needed file %s not found' % setup)
248 if not os.path.isfile(setup):
249 usage('%s: not a plain file' % setup)
251 # check that enough arguments are passed
252 if not args:
253 usage('at least one filename argument required')
255 # check that file arguments exist
256 for arg in args:
257 if arg == '-m':
258 break
259 # if user specified -m on the command line before _any_
260 # file names, then nothing should be checked (as the
261 # very first file should be a module name)
262 if modargs:
263 break
264 if not os.path.exists(arg):
265 usage('argument %s not found' % arg)
266 if not os.path.isfile(arg):
267 usage('%s: not a plain file' % arg)
269 # process non-option arguments
270 scriptfile = args[0]
271 modules = args[1:]
273 # derive target name from script name
274 base = os.path.basename(scriptfile)
275 base, ext = os.path.splitext(base)
276 if base:
277 if base != scriptfile:
278 target = base
279 else:
280 target = base + '.bin'
282 # handle -o option
283 base_frozen_c = frozen_c
284 base_config_c = config_c
285 base_target = target
286 if odir and not os.path.isdir(odir):
287 try:
288 os.mkdir(odir)
289 print "Created output directory", odir
290 except os.error, msg:
291 usage('%s: mkdir failed (%s)' % (odir, str(msg)))
292 base = ''
293 if odir:
294 base = os.path.join(odir, '')
295 frozen_c = os.path.join(odir, frozen_c)
296 config_c = os.path.join(odir, config_c)
297 target = os.path.join(odir, target)
298 makefile = os.path.join(odir, makefile)
299 if win: extensions_c = os.path.join(odir, extensions_c)
301 # Handle special entry point requirements
302 # (on Windows, some frozen programs do not use __main__, but
303 # import the module directly. Eg, DLLs, Services, etc
304 custom_entry_point = None # Currently only used on Windows
305 python_entry_is_main = 1 # Is the entry point called __main__?
306 # handle -s option on Windows
307 if win:
308 import winmakemakefile
309 try:
310 custom_entry_point, python_entry_is_main = \
311 winmakemakefile.get_custom_entry_point(subsystem)
312 except ValueError, why:
313 usage(why)
316 # Actual work starts here...
318 # collect all modules of the program
319 dir = os.path.dirname(scriptfile)
320 path[0] = dir
321 mf = modulefinder.ModuleFinder(path, debug, exclude, replace_paths)
323 if win and subsystem=='service':
324 # If a Windows service, then add the "built-in" module.
325 mod = mf.add_module("servicemanager")
326 mod.__file__="dummy.pyd" # really built-in to the resulting EXE
328 for mod in implicits:
329 mf.import_hook(mod)
330 for mod in modules:
331 if mod == '-m':
332 modargs = 1
333 continue
334 if modargs:
335 if mod[-2:] == '.*':
336 mf.import_hook(mod[:-2], None, ["*"])
337 else:
338 mf.import_hook(mod)
339 else:
340 mf.load_file(mod)
342 # Add the main script as either __main__, or the actual module name.
343 if python_entry_is_main:
344 mf.run_script(scriptfile)
345 else:
346 mf.load_file(scriptfile)
348 if debug > 0:
349 mf.report()
350 print
351 dict = mf.modules
353 # generate output for frozen modules
354 files = makefreeze.makefreeze(base, dict, debug, custom_entry_point)
356 # look for unfrozen modules (builtin and of unknown origin)
357 builtins = []
358 unknown = []
359 mods = dict.keys()
360 mods.sort()
361 for mod in mods:
362 if dict[mod].__code__:
363 continue
364 if not dict[mod].__file__:
365 builtins.append(mod)
366 else:
367 unknown.append(mod)
369 # search for unknown modules in extensions directories (not on Windows)
370 addfiles = []
371 frozen_extensions = [] # Windows list of modules.
372 if unknown or (not win and builtins):
373 if not win:
374 addfiles, addmods = \
375 checkextensions.checkextensions(unknown+builtins,
376 extensions)
377 for mod in addmods:
378 if mod in unknown:
379 unknown.remove(mod)
380 builtins.append(mod)
381 else:
382 # Do the windows thang...
383 import checkextensions_win32
384 # Get a list of CExtension instances, each describing a module
385 # (including its source files)
386 frozen_extensions = checkextensions_win32.checkextensions(
387 unknown, extensions, prefix)
388 for mod in frozen_extensions:
389 unknown.remove(mod.name)
391 # report unknown modules
392 if unknown:
393 sys.stderr.write('Warning: unknown modules remain: %s\n' %
394 string.join(unknown))
396 # windows gets different treatment
397 if win:
398 # Taking a shortcut here...
399 import winmakemakefile, checkextensions_win32
400 checkextensions_win32.write_extension_table(extensions_c,
401 frozen_extensions)
402 # Create a module definition for the bootstrap C code.
403 xtras = [frozenmain_c, os.path.basename(frozen_c),
404 frozendllmain_c, os.path.basename(extensions_c)] + files
405 maindefn = checkextensions_win32.CExtension( '__main__', xtras )
406 frozen_extensions.append( maindefn )
407 outfp = open(makefile, 'w')
408 try:
409 winmakemakefile.makemakefile(outfp,
410 locals(),
411 frozen_extensions,
412 os.path.basename(target))
413 finally:
414 outfp.close()
415 return
417 # generate config.c and Makefile
418 builtins.sort()
419 infp = open(config_c_in)
420 outfp = bkfile.open(config_c, 'w')
421 try:
422 makeconfig.makeconfig(infp, outfp, builtins)
423 finally:
424 outfp.close()
425 infp.close()
427 cflags = defines + includes + ['$(OPT)']
428 libs = [os.path.join(binlib, 'libpython$(VERSION).a')]
430 somevars = {}
431 if os.path.exists(makefile_in):
432 makevars = parsesetup.getmakevars(makefile_in)
433 for key in makevars.keys():
434 somevars[key] = makevars[key]
436 somevars['CFLAGS'] = string.join(cflags) # override
437 files = ['$(OPT)', '$(LDFLAGS)', base_config_c, base_frozen_c] + \
438 files + supp_sources + addfiles + libs + \
439 ['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)']
441 outfp = bkfile.open(makefile, 'w')
442 try:
443 makemakefile.makemakefile(outfp, somevars, files, base_target)
444 finally:
445 outfp.close()
447 # Done!
449 if odir:
450 print 'Now run "make" in', odir,
451 print 'to build the target:', base_target
452 else:
453 print 'Now run "make" to build the target:', base_target
456 # Print usage message and exit
458 def usage(msg):
459 sys.stdout = sys.stderr
460 print "Error:", msg
461 print "Use ``%s -h'' for help" % sys.argv[0]
462 sys.exit(2)
465 main()