Added 'description' class attribute to every command class (to help the
[python/dscho.git] / Tools / freeze / freeze.py
blobd6ef87e4c297e7bf0edae630da17c7247e3ad380
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 behaviour
58 is automatic.)
60 Arguments:
62 script: The Python script to be executed by the resulting binary.
64 module ...: Additional Python modules (referenced by pathname)
65 that will be included in the resulting binary. These
66 may be .py or .pyc files. If -m is specified, these are
67 module names that are search in the path instead.
69 NOTES:
71 In order to use freeze successfully, you must have built Python and
72 installed it ("make install").
74 The script should not use modules provided only as shared libraries;
75 if it does, the resulting binary is not self-contained.
76 """
79 # Import standard modules
81 import cmp
82 import getopt
83 import os
84 import string
85 import sys
88 # Import the freeze-private modules
90 import checkextensions
91 import modulefinder
92 import makeconfig
93 import makefreeze
94 import makemakefile
95 import parsesetup
96 import bkfile
99 # Main program
101 def main():
102 # overridable context
103 prefix = None # settable with -p option
104 exec_prefix = None # settable with -P option
105 extensions = []
106 exclude = [] # settable with -x option
107 addn_link = [] # settable with -l, but only honored under Windows.
108 path = sys.path[:]
109 modargs = 0
110 debug = 1
111 odir = ''
112 win = sys.platform[:3] == 'win'
114 # default the exclude list for each platform
115 if win: exclude = exclude + [
116 'dos', 'dospath', 'mac', 'macpath', 'macfs', 'MACFS', 'posix', 'os2']
118 # modules that are imported by the Python runtime
119 implicits = ["site", "exceptions"]
121 # output files
122 frozen_c = 'frozen.c'
123 config_c = 'config.c'
124 target = 'a.out' # normally derived from script name
125 makefile = 'Makefile'
126 subsystem = 'console'
128 # parse command line by first replacing any "-i" options with the file contents.
129 pos = 1
130 while pos < len(sys.argv)-1: # last option can not be "-i", so this ensures "pos+1" is in range!
131 if sys.argv[pos] == '-i':
132 try:
133 options = string.split(open(sys.argv[pos+1]).read())
134 except IOError, why:
135 usage("File name '%s' specified with the -i option can not be read - %s" % (sys.argv[pos+1], why) )
136 # Replace the '-i' and the filename with the read params.
137 sys.argv[pos:pos+2] = options
138 pos = pos + len(options) - 1 # Skip the name and the included args.
139 pos = pos + 1
141 # Now parse the command line with the extras inserted.
142 try:
143 opts, args = getopt.getopt(sys.argv[1:], 'a:de:hmo:p:P:qs:wx:l:')
144 except getopt.error, msg:
145 usage('getopt error: ' + str(msg))
147 # proces option arguments
148 for o, a in opts:
149 if o == '-h':
150 print __doc__
151 return
152 if o == '-d':
153 debug = debug + 1
154 if o == '-e':
155 extensions.append(a)
156 if o == '-m':
157 modargs = 1
158 if o == '-o':
159 odir = a
160 if o == '-p':
161 prefix = a
162 if o == '-P':
163 exec_prefix = a
164 if o == '-q':
165 debug = 0
166 if o == '-w':
167 win = not win
168 if o == '-s':
169 if not win:
170 usage("-s subsystem option only on Windows")
171 subsystem = a
172 if o == '-x':
173 exclude.append(a)
174 if o == '-l':
175 addn_link.append(a)
176 if o == '-a':
177 apply(modulefinder.AddPackagePath, tuple(string.split(a,"=", 2)))
179 # default prefix and exec_prefix
180 if not exec_prefix:
181 if prefix:
182 exec_prefix = prefix
183 else:
184 exec_prefix = sys.exec_prefix
185 if not prefix:
186 prefix = sys.prefix
188 # determine whether -p points to the Python source tree
189 ishome = os.path.exists(os.path.join(prefix, 'Python', 'ceval.c'))
191 # locations derived from options
192 version = sys.version[:3]
193 if win:
194 extensions_c = 'frozen_extensions.c'
195 if ishome:
196 print "(Using Python source directory)"
197 binlib = exec_prefix
198 incldir = os.path.join(prefix, 'Include')
199 config_h_dir = exec_prefix
200 config_c_in = os.path.join(prefix, 'Modules', 'config.c.in')
201 frozenmain_c = os.path.join(prefix, 'Python', 'frozenmain.c')
202 makefile_in = os.path.join(exec_prefix, 'Modules', 'Makefile')
203 if win:
204 frozendllmain_c = os.path.join(exec_prefix, 'Pc\\frozen_dllmain.c')
205 else:
206 binlib = os.path.join(exec_prefix,
207 'lib', 'python%s' % version, 'config')
208 incldir = os.path.join(prefix, 'include', 'python%s' % version)
209 config_h_dir = os.path.join(exec_prefix, 'include',
210 'python%s' % version)
211 config_c_in = os.path.join(binlib, 'config.c.in')
212 frozenmain_c = os.path.join(binlib, 'frozenmain.c')
213 makefile_in = os.path.join(binlib, 'Makefile')
214 frozendllmain_c = os.path.join(binlib, 'frozen_dllmain.c')
215 supp_sources = []
216 defines = []
217 includes = ['-I' + incldir, '-I' + config_h_dir]
219 # sanity check of directories and files
220 check_dirs = [prefix, exec_prefix, binlib, incldir]
221 if not win: check_dirs = check_dirs + extensions # These are not directories on Windows.
222 for dir in check_dirs:
223 if not os.path.exists(dir):
224 usage('needed directory %s not found' % dir)
225 if not os.path.isdir(dir):
226 usage('%s: not a directory' % dir)
227 if win:
228 files = supp_sources + extensions # extensions are files on Windows.
229 else:
230 files = [config_c_in, makefile_in] + supp_sources
231 for file in supp_sources:
232 if not os.path.exists(file):
233 usage('needed file %s not found' % file)
234 if not os.path.isfile(file):
235 usage('%s: not a plain file' % file)
236 if not win:
237 for dir in extensions:
238 setup = os.path.join(dir, 'Setup')
239 if not os.path.exists(setup):
240 usage('needed file %s not found' % setup)
241 if not os.path.isfile(setup):
242 usage('%s: not a plain file' % setup)
244 # check that enough arguments are passed
245 if not args:
246 usage('at least one filename argument required')
248 # check that file arguments exist
249 for arg in args:
250 if arg == '-m':
251 break
252 # if user specified -m on the command line before _any_
253 # file names, then nothing should be checked (as the
254 # very first file should be a module name)
255 if modargs:
256 break
257 if not os.path.exists(arg):
258 usage('argument %s not found' % arg)
259 if not os.path.isfile(arg):
260 usage('%s: not a plain file' % arg)
262 # process non-option arguments
263 scriptfile = args[0]
264 modules = args[1:]
266 # derive target name from script name
267 base = os.path.basename(scriptfile)
268 base, ext = os.path.splitext(base)
269 if base:
270 if base != scriptfile:
271 target = base
272 else:
273 target = base + '.bin'
275 # handle -o option
276 base_frozen_c = frozen_c
277 base_config_c = config_c
278 base_target = target
279 if odir and not os.path.isdir(odir):
280 try:
281 os.mkdir(odir)
282 print "Created output directory", odir
283 except os.error, msg:
284 usage('%s: mkdir failed (%s)' % (odir, str(msg)))
285 base = ''
286 if odir:
287 base = os.path.join(odir, '')
288 frozen_c = os.path.join(odir, frozen_c)
289 config_c = os.path.join(odir, config_c)
290 target = os.path.join(odir, target)
291 makefile = os.path.join(odir, makefile)
292 if win: extensions_c = os.path.join(odir, extensions_c)
294 # Handle special entry point requirements
295 # (on Windows, some frozen programs do not use __main__, but
296 # import the module directly. Eg, DLLs, Services, etc
297 custom_entry_point = None # Currently only used on Windows
298 python_entry_is_main = 1 # Is the entry point called __main__?
299 # handle -s option on Windows
300 if win:
301 import winmakemakefile
302 try:
303 custom_entry_point, python_entry_is_main = \
304 winmakemakefile.get_custom_entry_point(subsystem)
305 except ValueError, why:
306 usage(why)
309 # Actual work starts here...
311 # collect all modules of the program
312 dir = os.path.dirname(scriptfile)
313 path[0] = dir
314 mf = modulefinder.ModuleFinder(path, debug, exclude)
316 if win and subsystem=='service':
317 # If a Windows service, then add the "built-in" module.
318 mod = mf.add_module("servicemanager")
319 mod.__file__="dummy.pyd" # really built-in to the resulting EXE
321 for mod in implicits:
322 mf.import_hook(mod)
323 for mod in modules:
324 if mod == '-m':
325 modargs = 1
326 continue
327 if modargs:
328 if mod[-2:] == '.*':
329 mf.import_hook(mod[:-2], None, ["*"])
330 else:
331 mf.import_hook(mod)
332 else:
333 mf.load_file(mod)
335 # Add the main script as either __main__, or the actual module name.
336 if python_entry_is_main:
337 mf.run_script(scriptfile)
338 else:
339 if modargs:
340 mf.import_hook(scriptfile)
341 else:
342 mf.load_file(scriptfile)
344 if debug > 0:
345 mf.report()
346 print
347 dict = mf.modules
349 # generate output for frozen modules
350 files = makefreeze.makefreeze(base, dict, debug, custom_entry_point)
352 # look for unfrozen modules (builtin and of unknown origin)
353 builtins = []
354 unknown = []
355 mods = dict.keys()
356 mods.sort()
357 for mod in mods:
358 if dict[mod].__code__:
359 continue
360 if not dict[mod].__file__:
361 builtins.append(mod)
362 else:
363 unknown.append(mod)
365 # search for unknown modules in extensions directories (not on Windows)
366 addfiles = []
367 frozen_extensions = [] # Windows list of modules.
368 if unknown or (not win and builtins):
369 if not win:
370 addfiles, addmods = \
371 checkextensions.checkextensions(unknown+builtins,
372 extensions)
373 for mod in addmods:
374 if mod in unknown:
375 unknown.remove(mod)
376 builtins.append(mod)
377 else:
378 # Do the windows thang...
379 import checkextensions_win32
380 # Get a list of CExtension instances, each describing a module
381 # (including its source files)
382 frozen_extensions = checkextensions_win32.checkextensions(
383 unknown, extensions)
384 for mod in frozen_extensions:
385 unknown.remove(mod.name)
387 # report unknown modules
388 if unknown:
389 sys.stderr.write('Warning: unknown modules remain: %s\n' %
390 string.join(unknown))
392 # windows gets different treatment
393 if win:
394 # Taking a shortcut here...
395 import winmakemakefile, checkextensions_win32
396 checkextensions_win32.write_extension_table(extensions_c,
397 frozen_extensions)
398 # Create a module definition for the bootstrap C code.
399 xtras = [frozenmain_c, os.path.basename(frozen_c),
400 frozendllmain_c, os.path.basename(extensions_c)] + files
401 maindefn = checkextensions_win32.CExtension( '__main__', xtras )
402 frozen_extensions.append( maindefn )
403 outfp = open(makefile, 'w')
404 try:
405 winmakemakefile.makemakefile(outfp,
406 locals(),
407 frozen_extensions,
408 os.path.basename(target))
409 finally:
410 outfp.close()
411 return
413 # generate config.c and Makefile
414 builtins.sort()
415 infp = open(config_c_in)
416 outfp = bkfile.open(config_c, 'w')
417 try:
418 makeconfig.makeconfig(infp, outfp, builtins)
419 finally:
420 outfp.close()
421 infp.close()
423 cflags = defines + includes + ['$(OPT)']
424 libs = [os.path.join(binlib, 'libpython$(VERSION).a')]
426 somevars = {}
427 if os.path.exists(makefile_in):
428 makevars = parsesetup.getmakevars(makefile_in)
429 for key in makevars.keys():
430 somevars[key] = makevars[key]
432 somevars['CFLAGS'] = string.join(cflags) # override
433 files = ['$(OPT)', '$(LDFLAGS)', base_config_c, base_frozen_c] + \
434 files + supp_sources + addfiles + libs + \
435 ['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)']
437 outfp = bkfile.open(makefile, 'w')
438 try:
439 makemakefile.makemakefile(outfp, somevars, files, base_target)
440 finally:
441 outfp.close()
443 # Done!
445 if odir:
446 print 'Now run "make" in', odir,
447 print 'to build the target:', base_target
448 else:
449 print 'Now run "make" to build the target:', base_target
452 # Print usage message and exit
454 def usage(msg):
455 sys.stdout = sys.stderr
456 print "Error:", msg
457 print "Use ``%s -h'' for help" % sys.argv[0]
458 sys.exit(2)
461 main()