Added a test for the ability to specify a class attribute in Formatter configuration...
[python.git] / Tools / freeze / freeze.py
blob836f53261f949543973f8c9b54e4a1237b446fda
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. It will still be imported
46 by the frozen binary if it exists on the host system.
48 -X module Like -x, except the module can never be imported by
49 the frozen binary.
51 -E: Freeze will fail if any modules can't be found (that
52 were not excluded using -x or -X).
54 -i filename: Include a file with additional command line options. Used
55 to prevent command lines growing beyond the capabilities of
56 the shell/OS. All arguments specified in filename
57 are read and the -i option replaced with the parsed
58 params (note - quoting args in this file is NOT supported)
60 -s subsystem: Specify the subsystem (For Windows only.);
61 'console' (default), 'windows', 'service' or 'com_dll'
63 -w: Toggle Windows (NT or 95) behavior.
64 (For debugging only -- on a win32 platform, win32 behavior
65 is automatic.)
67 -r prefix=f: Replace path prefix.
68 Replace prefix with f in the source path references
69 contained in the resulting binary.
71 Arguments:
73 script: The Python script to be executed by the resulting binary.
75 module ...: Additional Python modules (referenced by pathname)
76 that will be included in the resulting binary. These
77 may be .py or .pyc files. If -m is specified, these are
78 module names that are search in the path instead.
80 NOTES:
82 In order to use freeze successfully, you must have built Python and
83 installed it ("make install").
85 The script should not use modules provided only as shared libraries;
86 if it does, the resulting binary is not self-contained.
87 """
90 # Import standard modules
92 import modulefinder
93 import getopt
94 import os
95 import sys
98 # Import the freeze-private modules
100 import checkextensions
101 import makeconfig
102 import makefreeze
103 import makemakefile
104 import parsesetup
105 import bkfile
108 # Main program
110 def main():
111 # overridable context
112 prefix = None # settable with -p option
113 exec_prefix = None # settable with -P option
114 extensions = []
115 exclude = [] # settable with -x option
116 addn_link = [] # settable with -l, but only honored under Windows.
117 path = sys.path[:]
118 modargs = 0
119 debug = 1
120 odir = ''
121 win = sys.platform[:3] == 'win'
122 replace_paths = [] # settable with -r option
123 error_if_any_missing = 0
125 # default the exclude list for each platform
126 if win: exclude = exclude + [
127 'dos', 'dospath', 'mac', 'macpath', 'macfs', 'MACFS', 'posix',
128 'os2', 'ce', 'riscos', 'riscosenviron', 'riscospath',
131 fail_import = exclude[:]
133 # output files
134 frozen_c = 'frozen.c'
135 config_c = 'config.c'
136 target = 'a.out' # normally derived from script name
137 makefile = 'Makefile'
138 subsystem = 'console'
140 # parse command line by first replacing any "-i" options with the
141 # file contents.
142 pos = 1
143 while pos < len(sys.argv)-1:
144 # last option can not be "-i", so this ensures "pos+1" is in range!
145 if sys.argv[pos] == '-i':
146 try:
147 options = open(sys.argv[pos+1]).read().split()
148 except IOError, why:
149 usage("File name '%s' specified with the -i option "
150 "can not be read - %s" % (sys.argv[pos+1], why) )
151 # Replace the '-i' and the filename with the read params.
152 sys.argv[pos:pos+2] = options
153 pos = pos + len(options) - 1 # Skip the name and the included args.
154 pos = pos + 1
156 # Now parse the command line with the extras inserted.
157 try:
158 opts, args = getopt.getopt(sys.argv[1:], 'r:a:dEe:hmo:p:P:qs:wX:x:l:')
159 except getopt.error, msg:
160 usage('getopt error: ' + str(msg))
162 # proces option arguments
163 for o, a in opts:
164 if o == '-h':
165 print __doc__
166 return
167 if o == '-d':
168 debug = debug + 1
169 if o == '-e':
170 extensions.append(a)
171 if o == '-m':
172 modargs = 1
173 if o == '-o':
174 odir = a
175 if o == '-p':
176 prefix = a
177 if o == '-P':
178 exec_prefix = a
179 if o == '-q':
180 debug = 0
181 if o == '-w':
182 win = not win
183 if o == '-s':
184 if not win:
185 usage("-s subsystem option only on Windows")
186 subsystem = a
187 if o == '-x':
188 exclude.append(a)
189 if o == '-X':
190 exclude.append(a)
191 fail_import.append(a)
192 if o == '-E':
193 error_if_any_missing = 1
194 if o == '-l':
195 addn_link.append(a)
196 if o == '-a':
197 apply(modulefinder.AddPackagePath, tuple(a.split("=", 2)))
198 if o == '-r':
199 f,r = a.split("=", 2)
200 replace_paths.append( (f,r) )
202 # modules that are imported by the Python runtime
203 implicits = []
204 for module in ('site', 'warnings',):
205 if module not in exclude:
206 implicits.append(module)
208 # default prefix and exec_prefix
209 if not exec_prefix:
210 if prefix:
211 exec_prefix = prefix
212 else:
213 exec_prefix = sys.exec_prefix
214 if not prefix:
215 prefix = sys.prefix
217 # determine whether -p points to the Python source tree
218 ishome = os.path.exists(os.path.join(prefix, 'Python', 'ceval.c'))
220 # locations derived from options
221 version = sys.version[:3]
222 if win:
223 extensions_c = 'frozen_extensions.c'
224 if ishome:
225 print "(Using Python source directory)"
226 binlib = exec_prefix
227 incldir = os.path.join(prefix, 'Include')
228 config_h_dir = exec_prefix
229 config_c_in = os.path.join(prefix, 'Modules', 'config.c.in')
230 frozenmain_c = os.path.join(prefix, 'Python', 'frozenmain.c')
231 makefile_in = os.path.join(exec_prefix, 'Makefile')
232 if win:
233 frozendllmain_c = os.path.join(exec_prefix, 'Pc\\frozen_dllmain.c')
234 else:
235 binlib = os.path.join(exec_prefix,
236 'lib', 'python%s' % version, 'config')
237 incldir = os.path.join(prefix, 'include', 'python%s' % version)
238 config_h_dir = os.path.join(exec_prefix, 'include',
239 'python%s' % version)
240 config_c_in = os.path.join(binlib, 'config.c.in')
241 frozenmain_c = os.path.join(binlib, 'frozenmain.c')
242 makefile_in = os.path.join(binlib, 'Makefile')
243 frozendllmain_c = os.path.join(binlib, 'frozen_dllmain.c')
244 supp_sources = []
245 defines = []
246 includes = ['-I' + incldir, '-I' + config_h_dir]
248 # sanity check of directories and files
249 check_dirs = [prefix, exec_prefix, binlib, incldir]
250 if not win:
251 # These are not directories on Windows.
252 check_dirs = check_dirs + extensions
253 for dir in check_dirs:
254 if not os.path.exists(dir):
255 usage('needed directory %s not found' % dir)
256 if not os.path.isdir(dir):
257 usage('%s: not a directory' % dir)
258 if win:
259 files = supp_sources + extensions # extensions are files on Windows.
260 else:
261 files = [config_c_in, makefile_in] + supp_sources
262 for file in supp_sources:
263 if not os.path.exists(file):
264 usage('needed file %s not found' % file)
265 if not os.path.isfile(file):
266 usage('%s: not a plain file' % file)
267 if not win:
268 for dir in extensions:
269 setup = os.path.join(dir, 'Setup')
270 if not os.path.exists(setup):
271 usage('needed file %s not found' % setup)
272 if not os.path.isfile(setup):
273 usage('%s: not a plain file' % setup)
275 # check that enough arguments are passed
276 if not args:
277 usage('at least one filename argument required')
279 # check that file arguments exist
280 for arg in args:
281 if arg == '-m':
282 break
283 # if user specified -m on the command line before _any_
284 # file names, then nothing should be checked (as the
285 # very first file should be a module name)
286 if modargs:
287 break
288 if not os.path.exists(arg):
289 usage('argument %s not found' % arg)
290 if not os.path.isfile(arg):
291 usage('%s: not a plain file' % arg)
293 # process non-option arguments
294 scriptfile = args[0]
295 modules = args[1:]
297 # derive target name from script name
298 base = os.path.basename(scriptfile)
299 base, ext = os.path.splitext(base)
300 if base:
301 if base != scriptfile:
302 target = base
303 else:
304 target = base + '.bin'
306 # handle -o option
307 base_frozen_c = frozen_c
308 base_config_c = config_c
309 base_target = target
310 if odir and not os.path.isdir(odir):
311 try:
312 os.mkdir(odir)
313 print "Created output directory", odir
314 except os.error, msg:
315 usage('%s: mkdir failed (%s)' % (odir, str(msg)))
316 base = ''
317 if odir:
318 base = os.path.join(odir, '')
319 frozen_c = os.path.join(odir, frozen_c)
320 config_c = os.path.join(odir, config_c)
321 target = os.path.join(odir, target)
322 makefile = os.path.join(odir, makefile)
323 if win: extensions_c = os.path.join(odir, extensions_c)
325 # Handle special entry point requirements
326 # (on Windows, some frozen programs do not use __main__, but
327 # import the module directly. Eg, DLLs, Services, etc
328 custom_entry_point = None # Currently only used on Windows
329 python_entry_is_main = 1 # Is the entry point called __main__?
330 # handle -s option on Windows
331 if win:
332 import winmakemakefile
333 try:
334 custom_entry_point, python_entry_is_main = \
335 winmakemakefile.get_custom_entry_point(subsystem)
336 except ValueError, why:
337 usage(why)
340 # Actual work starts here...
342 # collect all modules of the program
343 dir = os.path.dirname(scriptfile)
344 path[0] = dir
345 mf = modulefinder.ModuleFinder(path, debug, exclude, replace_paths)
347 if win and subsystem=='service':
348 # If a Windows service, then add the "built-in" module.
349 mod = mf.add_module("servicemanager")
350 mod.__file__="dummy.pyd" # really built-in to the resulting EXE
352 for mod in implicits:
353 mf.import_hook(mod)
354 for mod in modules:
355 if mod == '-m':
356 modargs = 1
357 continue
358 if modargs:
359 if mod[-2:] == '.*':
360 mf.import_hook(mod[:-2], None, ["*"])
361 else:
362 mf.import_hook(mod)
363 else:
364 mf.load_file(mod)
366 # Add the main script as either __main__, or the actual module name.
367 if python_entry_is_main:
368 mf.run_script(scriptfile)
369 else:
370 mf.load_file(scriptfile)
372 if debug > 0:
373 mf.report()
374 print
375 dict = mf.modules
377 if error_if_any_missing:
378 missing = mf.any_missing()
379 if missing:
380 sys.exit("There are some missing modules: %r" % missing)
382 # generate output for frozen modules
383 files = makefreeze.makefreeze(base, dict, debug, custom_entry_point,
384 fail_import)
386 # look for unfrozen modules (builtin and of unknown origin)
387 builtins = []
388 unknown = []
389 mods = dict.keys()
390 mods.sort()
391 for mod in mods:
392 if dict[mod].__code__:
393 continue
394 if not dict[mod].__file__:
395 builtins.append(mod)
396 else:
397 unknown.append(mod)
399 # search for unknown modules in extensions directories (not on Windows)
400 addfiles = []
401 frozen_extensions = [] # Windows list of modules.
402 if unknown or (not win and builtins):
403 if not win:
404 addfiles, addmods = \
405 checkextensions.checkextensions(unknown+builtins,
406 extensions)
407 for mod in addmods:
408 if mod in unknown:
409 unknown.remove(mod)
410 builtins.append(mod)
411 else:
412 # Do the windows thang...
413 import checkextensions_win32
414 # Get a list of CExtension instances, each describing a module
415 # (including its source files)
416 frozen_extensions = checkextensions_win32.checkextensions(
417 unknown, extensions, prefix)
418 for mod in frozen_extensions:
419 unknown.remove(mod.name)
421 # report unknown modules
422 if unknown:
423 sys.stderr.write('Warning: unknown modules remain: %s\n' %
424 ' '.join(unknown))
426 # windows gets different treatment
427 if win:
428 # Taking a shortcut here...
429 import winmakemakefile, checkextensions_win32
430 checkextensions_win32.write_extension_table(extensions_c,
431 frozen_extensions)
432 # Create a module definition for the bootstrap C code.
433 xtras = [frozenmain_c, os.path.basename(frozen_c),
434 frozendllmain_c, os.path.basename(extensions_c)] + files
435 maindefn = checkextensions_win32.CExtension( '__main__', xtras )
436 frozen_extensions.append( maindefn )
437 outfp = open(makefile, 'w')
438 try:
439 winmakemakefile.makemakefile(outfp,
440 locals(),
441 frozen_extensions,
442 os.path.basename(target))
443 finally:
444 outfp.close()
445 return
447 # generate config.c and Makefile
448 builtins.sort()
449 infp = open(config_c_in)
450 outfp = bkfile.open(config_c, 'w')
451 try:
452 makeconfig.makeconfig(infp, outfp, builtins)
453 finally:
454 outfp.close()
455 infp.close()
457 cflags = ['$(OPT)']
458 cppflags = defines + includes
459 libs = [os.path.join(binlib, 'libpython$(VERSION).a')]
461 somevars = {}
462 if os.path.exists(makefile_in):
463 makevars = parsesetup.getmakevars(makefile_in)
464 for key in makevars.keys():
465 somevars[key] = makevars[key]
467 somevars['CFLAGS'] = ' '.join(cflags) # override
468 somevars['CPPFLAGS'] = ' '.join(cppflags) # override
469 files = [base_config_c, base_frozen_c] + \
470 files + supp_sources + addfiles + libs + \
471 ['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)']
473 outfp = bkfile.open(makefile, 'w')
474 try:
475 makemakefile.makemakefile(outfp, somevars, files, base_target)
476 finally:
477 outfp.close()
479 # Done!
481 if odir:
482 print 'Now run "make" in', odir,
483 print 'to build the target:', base_target
484 else:
485 print 'Now run "make" to build the target:', base_target
488 # Print usage message and exit
490 def usage(msg):
491 sys.stdout = sys.stderr
492 print "Error:", msg
493 print "Use ``%s -h'' for help" % sys.argv[0]
494 sys.exit(2)
497 main()