1#! /usr/bin/env python 2 3"""Freeze a Python script into a binary. 4 5usage: freeze [options...] script [module]... 6 7Options: 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 71Arguments: 72 73script: The Python script to be executed by the resulting binary. 74 75module ...: 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 80NOTES: 81 82In order to use freeze successfully, you must have built Python and 83installed it ("make install"). 84 85The script should not use modules provided only as shared libraries; 86if it does, the resulting binary is not self-contained. 87""" 88 89 90# Import standard modules 91 92import modulefinder 93import getopt 94import os 95import sys 96 97 98# Import the freeze-private modules 99 100import checkextensions 101import makeconfig 102import makefreeze 103import makemakefile 104import parsesetup 105import bkfile 106 107 108# Main program 109 110def 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 # process 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 490def 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 497main() 498