~ubuntu-branches/ubuntu/maverick/python3.1/maverick

« back to all changes in this revision

Viewing changes to Tools/freeze/freeze.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2009-03-23 00:01:27 UTC
  • Revision ID: james.westby@ubuntu.com-20090323000127-5fstfxju4ufrhthq
Tags: upstream-3.1~a1+20090322
ImportĀ upstreamĀ versionĀ 3.1~a1+20090322

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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',
 
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 as 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 as 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
            modulefinder.AddPackagePath(*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 as 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 as 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 = sorted(dict.keys())
 
390
    for mod in mods:
 
391
        if dict[mod].__code__:
 
392
            continue
 
393
        if not dict[mod].__file__:
 
394
            builtins.append(mod)
 
395
        else:
 
396
            unknown.append(mod)
 
397
 
 
398
    # search for unknown modules in extensions directories (not on Windows)
 
399
    addfiles = []
 
400
    frozen_extensions = [] # Windows list of modules.
 
401
    if unknown or (not win and builtins):
 
402
        if not win:
 
403
            addfiles, addmods = \
 
404
                      checkextensions.checkextensions(unknown+builtins,
 
405
                                                      extensions)
 
406
            for mod in addmods:
 
407
                if mod in unknown:
 
408
                    unknown.remove(mod)
 
409
                    builtins.append(mod)
 
410
        else:
 
411
            # Do the windows thang...
 
412
            import checkextensions_win32
 
413
            # Get a list of CExtension instances, each describing a module
 
414
            # (including its source files)
 
415
            frozen_extensions = checkextensions_win32.checkextensions(
 
416
                unknown, extensions, prefix)
 
417
            for mod in frozen_extensions:
 
418
                unknown.remove(mod.name)
 
419
 
 
420
    # report unknown modules
 
421
    if unknown:
 
422
        sys.stderr.write('Warning: unknown modules remain: %s\n' %
 
423
                         ' '.join(unknown))
 
424
 
 
425
    # windows gets different treatment
 
426
    if win:
 
427
        # Taking a shortcut here...
 
428
        import winmakemakefile, checkextensions_win32
 
429
        checkextensions_win32.write_extension_table(extensions_c,
 
430
                                                    frozen_extensions)
 
431
        # Create a module definition for the bootstrap C code.
 
432
        xtras = [frozenmain_c, os.path.basename(frozen_c),
 
433
                 frozendllmain_c, os.path.basename(extensions_c)] + files
 
434
        maindefn = checkextensions_win32.CExtension( '__main__', xtras )
 
435
        frozen_extensions.append( maindefn )
 
436
        outfp = open(makefile, 'w')
 
437
        try:
 
438
            winmakemakefile.makemakefile(outfp,
 
439
                                         locals(),
 
440
                                         frozen_extensions,
 
441
                                         os.path.basename(target))
 
442
        finally:
 
443
            outfp.close()
 
444
        return
 
445
 
 
446
    # generate config.c and Makefile
 
447
    builtins.sort()
 
448
    infp = open(config_c_in)
 
449
    outfp = bkfile.open(config_c, 'w')
 
450
    try:
 
451
        makeconfig.makeconfig(infp, outfp, builtins)
 
452
    finally:
 
453
        outfp.close()
 
454
    infp.close()
 
455
 
 
456
    cflags = ['$(OPT)']
 
457
    cppflags = defines + includes
 
458
    libs = [os.path.join(binlib, 'libpython$(VERSION).a')]
 
459
 
 
460
    somevars = {}
 
461
    if os.path.exists(makefile_in):
 
462
        makevars = parsesetup.getmakevars(makefile_in)
 
463
        for key in makevars:
 
464
            somevars[key] = makevars[key]
 
465
 
 
466
    somevars['CFLAGS'] = ' '.join(cflags) # override
 
467
    somevars['CPPFLAGS'] = ' '.join(cppflags) # override
 
468
    files = [base_config_c, base_frozen_c] + \
 
469
            files + supp_sources +  addfiles + libs + \
 
470
            ['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)']
 
471
 
 
472
    outfp = bkfile.open(makefile, 'w')
 
473
    try:
 
474
        makemakefile.makemakefile(outfp, somevars, files, base_target)
 
475
    finally:
 
476
        outfp.close()
 
477
 
 
478
    # Done!
 
479
 
 
480
    if odir:
 
481
        print('Now run "make" in', odir, end=' ')
 
482
        print('to build the target:', base_target)
 
483
    else:
 
484
        print('Now run "make" to build the target:', base_target)
 
485
 
 
486
 
 
487
# Print usage message and exit
 
488
 
 
489
def usage(msg):
 
490
    sys.stdout = sys.stderr
 
491
    print("Error:", msg)
 
492
    print("Use ``%s -h'' for help" % sys.argv[0])
 
493
    sys.exit(2)
 
494
 
 
495
 
 
496
main()