|
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() |