Added a -D option to freeze.py to cause it to not check to see if certain
[blender.git] / intern / python / freeze / freeze.py
1 #! /usr/bin/env python
2
3 # changes made by strubi@blender.nl
4
5 """Freeze a Python script into a binary.
6
7 usage: freeze [options...] script [module]...
8
9 Options:
10 -p prefix:    This is the prefix used when you ran ``make install''
11               in the Python build directory.
12               (If you never ran this, freeze won't work.)
13               The default is whatever sys.prefix evaluates to.
14               It can also be the top directory of the Python source
15               tree; then -P must point to the build tree.
16
17 -P exec_prefix: Like -p but this is the 'exec_prefix', used to
18                 install objects etc.  The default is whatever sys.exec_prefix
19                 evaluates to, or the -p argument if given.
20                 If -p points to the Python source tree, -P must point
21                 to the build tree, if different.
22
23 -e extension: A directory containing additional .o files that
24               may be used to resolve modules.  This directory
25               should also have a Setup file describing the .o files.
26               On Windows, the name of a .INI file describing one
27               or more extensions is passed.
28               More than one -e option may be given.
29
30 -o dir:       Directory where the output files are created; default '.'.
31
32 -m:           Additional arguments are module names instead of filenames.
33
34 -a package=dir: Additional directories to be added to the package's
35                 __path__.  Used to simulate directories added by the
36                 package at runtime (eg, by OpenGL and win32com).
37                 More than one -a option may be given for each package.
38
39 -l file:      Pass the file to the linker (windows only)
40
41 -d:           Debugging mode for the module finder.
42
43 -D:                       Do not check for existence of Python directories (HACK).
44
45 -q:           Make the module finder totally quiet.
46
47 -h:           Print this help message.
48
49 -x module     Exclude the specified module.
50
51 -i filename:  Include a file with additional command line options.  Used
52               to prevent command lines growing beyond the capabilities of
53               the shell/OS.  All arguments specified in filename
54               are read and the -i option replaced with the parsed
55               params (note - quoting args in this file is NOT supported)
56
57 -s subsystem: Specify the subsystem (For Windows only.); 
58               'console' (default), 'windows', 'service' or 'com_dll'
59               
60 -w:           Toggle Windows (NT or 95) behavior.
61               (For debugging only -- on a win32 platform, win32 behavior
62               is automatic.)
63
64 Arguments:
65
66 script:       The Python script to be executed by the resulting binary.
67
68 module ...:   Additional Python modules (referenced by pathname)
69               that will be included in the resulting binary.  These
70               may be .py or .pyc files.  If -m is specified, these are
71               module names that are search in the path instead.
72
73 NOTES:
74
75 In order to use freeze successfully, you must have built Python and
76 installed it ("make install").
77
78 The script should not use modules provided only as shared libraries;
79 if it does, the resulting binary is not self-contained.
80 """
81
82
83 # Import standard modules
84
85 import getopt
86 import os
87 import string
88 import sys
89
90
91 # Import the freeze-private modules
92
93 import checkextensions
94 import modulefinder
95 import makeconfig
96 import makefreeze
97 import makemakefile
98 import parsesetup
99 import bkfile
100
101
102 # Main program
103
104 def main():
105     # overridable context
106     prefix = None                       # settable with -p option
107     exec_prefix = None                  # settable with -P option
108     extensions = []
109     exclude = []                        # settable with -x option
110     addn_link = []      # settable with -l, but only honored under Windows.
111     path = sys.path[:]
112     modargs = 0
113     debug = 1
114     do_not_check_dirs = 0
115     odir = ''
116     win = sys.platform[:3] == 'win'
117
118     # default the exclude list for each platform
119     if win: exclude = exclude + [
120         'dos', 'dospath', 'mac', 'macpath', 'macfs', 'MACFS', 'posix', 'os2', 'ce']
121
122     # modules that are imported by the Python runtime
123     #implicits = ["site", "exceptions"]
124     implicits = ["exceptions"]
125
126     # output files
127     frozen_c = 'frozen.c'
128     config_c = 'config.c'
129     target = 'a.out'                    # normally derived from script name
130     makefile = 'Makefile'
131     subsystem = 'console'
132
133     # parse command line by first replacing any "-i" options with the file contents.
134     pos = 1
135     while pos < len(sys.argv)-1: # last option can not be "-i", so this ensures "pos+1" is in range!
136         if sys.argv[pos] == '-i':
137             try:
138                 options = string.split(open(sys.argv[pos+1]).read())
139             except IOError, why:
140                 usage("File name '%s' specified with the -i option can not be read - %s" % (sys.argv[pos+1], why) )
141             # Replace the '-i' and the filename with the read params.
142             sys.argv[pos:pos+2] = options
143             pos = pos + len(options) - 1 # Skip the name and the included args.
144         pos = pos + 1
145
146     # Now parse the command line with the extras inserted.
147     try:
148         opts, args = getopt.getopt(sys.argv[1:], 'a:dDe:hmo:p:P:I:qs:wx:l:')
149     except getopt.error, msg:
150         usage('getopt error: ' + str(msg))
151
152     # proces option arguments
153     for o, a in opts:
154         if o == '-h':
155             print __doc__
156             return
157         if o == '-d':
158             debug = debug + 1
159         if o == '-e':
160             extensions.append(a)
161         if o == '-I': # include path
162                         path.append(a)
163         if o == '-m':
164             modargs = 1
165         if o == '-o':
166             odir = a
167         if o == '-p':
168             prefix = a
169         if o == '-P':
170             exec_prefix = a
171         if o == '-q':
172             debug = 0
173         if o == '-w':
174             win = not win
175         if o == '-s':
176             if not win:
177                 usage("-s subsystem option only on Windows")
178             subsystem = a
179         if o == '-x':
180             exclude.append(a)
181         if o == '-l':
182             addn_link.append(a)
183         if o == '-a':
184             apply(modulefinder.AddPackagePath, tuple(string.split(a,"=", 2)))
185         if o == '-D':
186                         do_not_check_dirs = 1
187
188     # default prefix and exec_prefix
189     if not exec_prefix:
190         if prefix:
191             exec_prefix = prefix
192         else:
193             exec_prefix = sys.exec_prefix
194     if not prefix:
195         prefix = sys.prefix
196
197     # determine whether -p points to the Python source tree
198     ishome = os.path.exists(os.path.join(prefix, 'Python', 'ceval.c'))
199
200     # locations derived from options
201     version = sys.version[:3]
202     if win:
203         extensions_c = 'frozen_extensions.c'
204     if ishome:
205         print "(Using Python source directory)"
206         binlib = exec_prefix
207         incldir = os.path.join(prefix, 'Include')
208         config_h_dir = exec_prefix
209         config_c_in = os.path.join(prefix, 'Modules', 'config.c.in')
210         frozenmain_c = os.path.join(prefix, 'Python', 'frozenmain.c')
211         makefile_in = os.path.join(exec_prefix, 'Modules', 'Makefile')
212         if win:
213             frozendllmain_c = os.path.join(exec_prefix, 'Pc\\frozen_dllmain.c')
214     else:
215         binlib = os.path.join(exec_prefix,
216                               'lib', 'python%s' % version, 'config')
217         incldir = os.path.join(prefix, 'include', 'python%s' % version)
218         config_h_dir = os.path.join(exec_prefix, 'include',
219                                     'python%s' % version)
220         config_c_in = os.path.join(binlib, 'config.c.in')
221         frozenmain_c = os.path.join(binlib, 'frozenmain.c')
222         makefile_in = os.path.join(binlib, 'Makefile')
223         frozendllmain_c = os.path.join(binlib, 'frozen_dllmain.c')
224     supp_sources = []
225     defines = []
226     includes = ['-I' + incldir, '-I' + config_h_dir]
227
228     # sanity check of directories and files
229     check_dirs = [prefix, exec_prefix, binlib, incldir]
230     if not win: check_dirs = check_dirs + extensions # These are not directories on Windows.
231     if not do_not_check_dirs:
232             for dir in check_dirs:
233                 if not os.path.exists(dir):
234                         usage('needed directory %s not found' % dir)
235                         if not os.path.isdir(dir):
236                                 usage('%s: not a directory' % dir)
237     if win:
238         files = supp_sources + extensions # extensions are files on Windows.
239     else:
240         files = [config_c_in, makefile_in] + supp_sources
241     for file in supp_sources:
242         if not os.path.exists(file):
243             usage('needed file %s not found' % file)
244         if not os.path.isfile(file):
245             usage('%s: not a plain file' % file)
246     if not win:
247         for dir in extensions:
248             setup = os.path.join(dir, 'Setup')
249             if not os.path.exists(setup):
250                 usage('needed file %s not found' % setup)
251             if not os.path.isfile(setup):
252                 usage('%s: not a plain file' % setup)
253
254     # check that enough arguments are passed
255     if not args:
256         usage('at least one filename argument required')
257
258     # check that file arguments exist
259     for arg in args:
260         if arg == '-m':
261             break
262         # if user specified -m on the command line before _any_
263         # file names, then nothing should be checked (as the
264         # very first file should be a module name)
265         if modargs:
266             break
267         if not os.path.exists(arg):
268             usage('argument %s not found' % arg)
269         if not os.path.isfile(arg):
270             usage('%s: not a plain file' % arg)
271
272     # process non-option arguments
273     scriptfile = args[0]
274     modules = args[1:]
275
276     # derive target name from script name
277     base = os.path.basename(scriptfile)
278     base, ext = os.path.splitext(base)
279     if base:
280         if base != scriptfile:
281             target = base
282         else:
283             target = base + '.bin'
284
285     # handle -o option
286     base_frozen_c = frozen_c
287     base_config_c = config_c
288     base_target = target
289     if odir and not os.path.isdir(odir):
290         try:
291             os.mkdir(odir)
292             print "Created output directory", odir
293         except os.error, msg:
294             usage('%s: mkdir failed (%s)' % (odir, str(msg)))
295     base = ''
296     if odir:
297         base = os.path.join(odir, '')
298         frozen_c = os.path.join(odir, frozen_c)
299         config_c = os.path.join(odir, config_c)
300         target = os.path.join(odir, target)
301         makefile = os.path.join(odir, makefile)
302         if win: extensions_c = os.path.join(odir, extensions_c)
303
304     # Handle special entry point requirements
305     # (on Windows, some frozen programs do not use __main__, but
306     # import the module directly.  Eg, DLLs, Services, etc
307     custom_entry_point = None  # Currently only used on Windows
308     python_entry_is_main = 1   # Is the entry point called __main__?
309     # handle -s option on Windows
310     if win:
311         import winmakemakefile
312         try:
313             custom_entry_point, python_entry_is_main = \
314                 winmakemakefile.get_custom_entry_point(subsystem)
315         except ValueError, why:
316             usage(why)
317             
318
319     # Actual work starts here...
320
321     # collect all modules of the program
322     dir = os.path.dirname(scriptfile)
323     path[0] = dir
324     mf = modulefinder.ModuleFinder(path, debug, exclude)
325     
326     if win and subsystem=='service':
327         # If a Windows service, then add the "built-in" module.
328         mod = mf.add_module("servicemanager")
329         mod.__file__="dummy.pyd" # really built-in to the resulting EXE
330
331     for mod in implicits:
332         mf.import_hook(mod)
333     for mod in modules:
334         if mod == '-m':
335             modargs = 1
336             continue
337         if modargs:
338             if mod[-2:] == '.*':
339                 mf.import_hook(mod[:-2], None, ["*"])
340             else:
341                 mf.import_hook(mod)
342         else:
343             mf.load_file(mod)
344
345     # Add the main script as either __main__, or the actual module name.
346     if python_entry_is_main:
347         mf.run_script(scriptfile)
348     else:
349         mf.load_file(scriptfile)
350
351     if debug > 0:
352         mf.report()
353         print
354     dict = mf.modules
355
356     # generate output for frozen modules
357     files = makefreeze.makefreeze(base, dict, debug, custom_entry_point, 1)
358     # look for unfrozen modules (builtin and of unknown origin)
359     builtins = []
360     unknown = []
361     mods = dict.keys()
362     mods.sort()
363     for mod in mods:
364         if dict[mod].__code__:
365             continue
366         if not dict[mod].__file__:
367             builtins.append(mod)
368         else:
369             unknown.append(mod)
370
371     # search for unknown modules in extensions directories (not on Windows)
372     addfiles = []
373     frozen_extensions = [] # Windows list of modules.
374     if unknown or (not win and builtins):
375         if not win:
376             addfiles, addmods = \
377                       checkextensions.checkextensions(unknown+builtins,
378                                                       extensions)
379             for mod in addmods:
380                 if mod in unknown:
381                     unknown.remove(mod)
382                     builtins.append(mod)
383         else:
384             # Do the windows thang...
385             import checkextensions_win32
386             # Get a list of CExtension instances, each describing a module 
387             # (including its source files)
388             frozen_extensions = checkextensions_win32.checkextensions(
389                 unknown, extensions, prefix)
390             for mod in frozen_extensions:
391                 unknown.remove(mod.name)
392
393     # report unknown modules
394     if unknown:
395         sys.stderr.write('Warning: unknown modules remain: %s\n' %
396                          string.join(unknown))
397
398     # windows gets different treatment
399     if win:
400         # Taking a shortcut here...
401         import winmakemakefile, checkextensions_win32
402         checkextensions_win32.write_extension_table(extensions_c,
403                                                     frozen_extensions)
404         # Create a module definition for the bootstrap C code.
405         xtras = [frozenmain_c, os.path.basename(frozen_c),
406                  frozendllmain_c, os.path.basename(extensions_c)] + files
407         maindefn = checkextensions_win32.CExtension( '__main__', xtras )
408         frozen_extensions.append( maindefn )
409         outfp = open(makefile, 'w')
410         try:
411             winmakemakefile.makemakefile(outfp,
412                                          locals(),
413                                          frozen_extensions,
414                                          os.path.basename(target))
415         finally:
416             outfp.close()
417         return
418
419     # generate config.c and Makefile
420     builtins.sort()
421     infp = open(config_c_in)
422     outfp = bkfile.open(config_c, 'w')
423     try:
424         makeconfig.makeconfig(infp, outfp, builtins)
425     finally:
426         outfp.close()
427     infp.close()
428
429     cflags = defines + includes + ['$(OPT)']
430     libs = [os.path.join(binlib, 'libpython$(VERSION).a')]
431
432     somevars = {}
433     if os.path.exists(makefile_in):
434         makevars = parsesetup.getmakevars(makefile_in)
435     for key in makevars.keys():
436         somevars[key] = makevars[key]
437
438     somevars['CFLAGS'] = string.join(cflags) # override
439     files = ['$(OPT)', '$(LDFLAGS)', base_config_c, base_frozen_c] + \
440             files + supp_sources +  addfiles + libs + \
441             ['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)']
442
443     outfp = bkfile.open(makefile, 'w')
444     try:
445         makemakefile.makemakefile(outfp, somevars, files, base_target)
446     finally:
447         outfp.close()
448
449     # Done!
450
451     if odir:
452         print 'Now run "make" in', odir,
453         print 'to build the target:', base_target
454     else:
455         print 'Now run "make" to build the target:', base_target
456
457
458 # Print usage message and exit
459
460 def usage(msg):
461     sys.stdout = sys.stderr
462     print "Error:", msg
463     print "Use ``%s -h'' for help" % sys.argv[0]
464     sys.exit(2)
465
466
467 main()