1 | #! /usr/bin/env python
|
---|
2 |
|
---|
3 | """Freeze a Python script into a binary.
|
---|
4 |
|
---|
5 | usage: freeze [options...] script [module]...
|
---|
6 |
|
---|
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.
|
---|
14 |
|
---|
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.
|
---|
20 |
|
---|
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.
|
---|
27 |
|
---|
28 | -o dir: Directory where the output files are created; default '.'.
|
---|
29 |
|
---|
30 | -m: Additional arguments are module names instead of filenames.
|
---|
31 |
|
---|
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.
|
---|
36 |
|
---|
37 | -l file: Pass the file to the linker (windows only)
|
---|
38 |
|
---|
39 | -d: Debugging mode for the module finder.
|
---|
40 |
|
---|
41 | -q: Make the module finder totally quiet.
|
---|
42 |
|
---|
43 | -h: Print this help message.
|
---|
44 |
|
---|
45 | -x module Exclude the specified module. It will still be imported
|
---|
46 | by the frozen binary if it exists on the host system.
|
---|
47 |
|
---|
48 | -X module Like -x, except the module can never be imported by
|
---|
49 | the frozen binary.
|
---|
50 |
|
---|
51 | -E: Freeze will fail if any modules can't be found (that
|
---|
52 | were not excluded using -x or -X).
|
---|
53 |
|
---|
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)
|
---|
59 |
|
---|
60 | -s subsystem: Specify the subsystem (For Windows only.);
|
---|
61 | 'console' (default), 'windows', 'service' or 'com_dll'
|
---|
62 |
|
---|
63 | -w: Toggle Windows (NT or 95) behavior.
|
---|
64 | (For debugging only -- on a win32 platform, win32 behavior
|
---|
65 | is automatic.)
|
---|
66 |
|
---|
67 | -r prefix=f: Replace path prefix.
|
---|
68 | Replace prefix with f in the source path references
|
---|
69 | contained in the resulting binary.
|
---|
70 |
|
---|
71 | Arguments:
|
---|
72 |
|
---|
73 | script: The Python script to be executed by the resulting binary.
|
---|
74 |
|
---|
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.
|
---|
79 |
|
---|
80 | NOTES:
|
---|
81 |
|
---|
82 | In order to use freeze successfully, you must have built Python and
|
---|
83 | installed it ("make install").
|
---|
84 |
|
---|
85 | The script should not use modules provided only as shared libraries;
|
---|
86 | if it does, the resulting binary is not self-contained.
|
---|
87 | """
|
---|
88 |
|
---|
89 |
|
---|
90 | # Import standard modules
|
---|
91 |
|
---|
92 | import modulefinder
|
---|
93 | import getopt
|
---|
94 | import os
|
---|
95 | import sys
|
---|
96 |
|
---|
97 |
|
---|
98 | # Import the freeze-private modules
|
---|
99 |
|
---|
100 | import checkextensions
|
---|
101 | import makeconfig
|
---|
102 | import makefreeze
|
---|
103 | import makemakefile
|
---|
104 | import parsesetup
|
---|
105 | import bkfile
|
---|
106 |
|
---|
107 |
|
---|
108 | # Main program
|
---|
109 |
|
---|
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
|
---|
124 |
|
---|
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',
|
---|
129 | ]
|
---|
130 |
|
---|
131 | fail_import = exclude[:]
|
---|
132 |
|
---|
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'
|
---|
139 |
|
---|
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
|
---|
155 |
|
---|
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))
|
---|
161 |
|
---|
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) )
|
---|
201 |
|
---|
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)
|
---|
207 |
|
---|
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
|
---|
216 |
|
---|
217 | # determine whether -p points to the Python source tree
|
---|
218 | ishome = os.path.exists(os.path.join(prefix, 'Python', 'ceval.c'))
|
---|
219 |
|
---|
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]
|
---|
247 |
|
---|
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)
|
---|
274 |
|
---|
275 | # check that enough arguments are passed
|
---|
276 | if not args:
|
---|
277 | usage('at least one filename argument required')
|
---|
278 |
|
---|
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)
|
---|
292 |
|
---|
293 | # process non-option arguments
|
---|
294 | scriptfile = args[0]
|
---|
295 | modules = args[1:]
|
---|
296 |
|
---|
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'
|
---|
305 |
|
---|
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)
|
---|
324 |
|
---|
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)
|
---|
338 |
|
---|
339 |
|
---|
340 | # Actual work starts here...
|
---|
341 |
|
---|
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)
|
---|
346 |
|
---|
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
|
---|
351 |
|
---|
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)
|
---|
365 |
|
---|
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)
|
---|
371 |
|
---|
372 | if debug > 0:
|
---|
373 | mf.report()
|
---|
374 | print
|
---|
375 | dict = mf.modules
|
---|
376 |
|
---|
377 | if error_if_any_missing:
|
---|
378 | missing = mf.any_missing()
|
---|
379 | if missing:
|
---|
380 | sys.exit("There are some missing modules: %r" % missing)
|
---|
381 |
|
---|
382 | # generate output for frozen modules
|
---|
383 | files = makefreeze.makefreeze(base, dict, debug, custom_entry_point,
|
---|
384 | fail_import)
|
---|
385 |
|
---|
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)
|
---|
398 |
|
---|
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)
|
---|
420 |
|
---|
421 | # report unknown modules
|
---|
422 | if unknown:
|
---|
423 | sys.stderr.write('Warning: unknown modules remain: %s\n' %
|
---|
424 | ' '.join(unknown))
|
---|
425 |
|
---|
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
|
---|
446 |
|
---|
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()
|
---|
456 |
|
---|
457 | cflags = ['$(OPT)']
|
---|
458 | cppflags = defines + includes
|
---|
459 | libs = [os.path.join(binlib, 'libpython$(VERSION).a')]
|
---|
460 |
|
---|
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]
|
---|
466 |
|
---|
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)']
|
---|
472 |
|
---|
473 | outfp = bkfile.open(makefile, 'w')
|
---|
474 | try:
|
---|
475 | makemakefile.makemakefile(outfp, somevars, files, base_target)
|
---|
476 | finally:
|
---|
477 | outfp.close()
|
---|
478 |
|
---|
479 | # Done!
|
---|
480 |
|
---|
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
|
---|
486 |
|
---|
487 |
|
---|
488 | # Print usage message and exit
|
---|
489 |
|
---|
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)
|
---|
495 |
|
---|
496 |
|
---|
497 | main()
|
---|