blob: 5e74c6753ce8dbb386f03aa957fa32eb03a18126 [file] [log] [blame]
Guido van Rossumf06ee5f1996-11-27 19:52:01 +00001#! /usr/bin/env python
Guido van Rossumdbaf3321994-10-03 10:25:54 +00002
Guido van Rossum96c4dd91996-08-26 05:14:20 +00003"""Freeze a Python script into a binary.
Guido van Rossumdbaf3321994-10-03 10:25:54 +00004
Guido van Rossum96c4dd91996-08-26 05:14:20 +00005usage: freeze [options...] script.py [module]...
Guido van Rossum00ff4331994-10-03 16:33:08 +00006
Guido van Rossum96c4dd91996-08-26 05:14:20 +00007Options:
Guido van Rossum00ff4331994-10-03 16:33:08 +00008
Guido van Rossum9a6e8551997-08-10 16:47:17 +00009-p prefix: This is the prefix used when you ran ``make install''
Guido van Rossum96c4dd91996-08-26 05:14:20 +000010 in the Python build directory.
Guido van Rossumd8336c21994-10-05 16:13:01 +000011 (If you never ran this, freeze won't work.)
Guido van Rossum96c4dd91996-08-26 05:14:20 +000012 The default is whatever sys.prefix evaluates to.
Guido van Rossum0b4b8a21997-08-10 16:56:48 +000013 It can also be the top directory of the Python source
14 tree; then -P must point to the build tree.
Guido van Rossumd8336c21994-10-05 16:13:01 +000015
Guido van Rossum150316e1995-08-08 14:21:07 +000016-P exec_prefix: Like -p but this is the 'exec_prefix', used to
Guido van Rossum0b4b8a21997-08-10 16:56:48 +000017 install objects etc. The default is whatever sys.exec_prefix
18 evaluates to, or the -p argument if given.
19 If -p points to the Python source tree, -P must point
20 to the build tree, if different.
Guido van Rossum150316e1995-08-08 14:21:07 +000021
Guido van Rossumd8336c21994-10-05 16:13:01 +000022-e extension: A directory containing additional .o files that
23 may be used to resolve modules. This directory
24 should also have a Setup file describing the .o files.
25 More than one -e option may be given.
26
Guido van Rossum96c4dd91996-08-26 05:14:20 +000027-o dir: Directory where the output files are created; default '.'.
28
Guido van Rossum9a6e8551997-08-10 16:47:17 +000029-h: Print this help message.
30
Guido van Rossum96c4dd91996-08-26 05:14:20 +000031Arguments:
32
Guido van Rossumd4cc04c1996-06-17 17:49:13 +000033script.py: The Python script to be executed by the resulting binary.
Guido van Rossum0b4b8a21997-08-10 16:56:48 +000034 It *must* end with a .py suffix!
Guido van Rossumd8336c21994-10-05 16:13:01 +000035
36module ...: Additional Python modules (referenced by pathname)
37 that will be included in the resulting binary. These
38 may be .py or .pyc files.
Guido van Rossum150316e1995-08-08 14:21:07 +000039
40NOTES:
41
42In order to use freeze successfully, you must have built Python and
Guido van Rossumd4cc04c1996-06-17 17:49:13 +000043installed it ("make install").
Guido van Rossum150316e1995-08-08 14:21:07 +000044
Guido van Rossum96c4dd91996-08-26 05:14:20 +000045The script should not use modules provided only as shared libraries;
46if it does, the resulting binary is not self-contained.
Guido van Rossumd8336c21994-10-05 16:13:01 +000047"""
Guido van Rossum00ff4331994-10-03 16:33:08 +000048
49
Guido van Rossum00ff4331994-10-03 16:33:08 +000050# Import standard modules
51
Guido van Rossumd8336c21994-10-05 16:13:01 +000052import cmp
Guido van Rossumdbaf3321994-10-03 10:25:54 +000053import getopt
Guido van Rossum00ff4331994-10-03 16:33:08 +000054import os
Guido van Rossumdbaf3321994-10-03 10:25:54 +000055import string
Guido van Rossum00ff4331994-10-03 16:33:08 +000056import sys
57import addpack
Guido van Rossumdbaf3321994-10-03 10:25:54 +000058
Guido van Rossumdbaf3321994-10-03 10:25:54 +000059
Guido van Rossum00ff4331994-10-03 16:33:08 +000060# Import the freeze-private modules
61
Guido van Rossumd8336c21994-10-05 16:13:01 +000062import checkextensions
Guido van Rossum00ff4331994-10-03 16:33:08 +000063import findmodules
64import makeconfig
65import makefreeze
66import makemakefile
67import parsesetup
68
Guido van Rossum00ff4331994-10-03 16:33:08 +000069
70# Main program
71
Guido van Rossumdbaf3321994-10-03 10:25:54 +000072def main():
Guido van Rossum0b4b8a21997-08-10 16:56:48 +000073 # overridable context
74 prefix = None # settable with -p option
75 exec_prefix = None # settable with -P option
76 extensions = []
77 path = sys.path
78 odir = ''
Guido van Rossum00ff4331994-10-03 16:33:08 +000079
Guido van Rossum0b4b8a21997-08-10 16:56:48 +000080 # output files
81 frozen_c = 'frozen.c'
82 config_c = 'config.c'
83 target = 'a.out' # normally derived from script name
84 makefile = 'Makefile'
Guido van Rossum00ff4331994-10-03 16:33:08 +000085
Guido van Rossum0b4b8a21997-08-10 16:56:48 +000086 # parse command line
87 try:
88 opts, args = getopt.getopt(sys.argv[1:], 'he:o:p:P:')
89 except getopt.error, msg:
90 usage('getopt error: ' + str(msg))
Guido van Rossum00ff4331994-10-03 16:33:08 +000091
Guido van Rossum0b4b8a21997-08-10 16:56:48 +000092 # proces option arguments
93 for o, a in opts:
94 if o == '-h':
95 print __doc__
96 return
97 if o == '-e':
98 extensions.append(a)
99 if o == '-o':
100 odir = a
101 if o == '-p':
102 prefix = a
103 if o == '-P':
104 exec_prefix = a
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000105
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000106 # default prefix and exec_prefix
107 if not exec_prefix:
108 if prefix:
109 exec_prefix = prefix
110 else:
111 exec_prefix = sys.exec_prefix
112 if not prefix:
113 prefix = sys.prefix
Guido van Rossum9a6e8551997-08-10 16:47:17 +0000114
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000115 # determine whether -p points to the Python source tree
116 ishome = os.path.exists(os.path.join(prefix, 'Include', 'pythonrun.h'))
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000117
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000118 # locations derived from options
119 version = sys.version[:3]
120 if ishome:
121 print "(Using Python source directory)"
122 binlib = exec_prefix
123 incldir = os.path.join(prefix, 'Include')
124 config_c_in = os.path.join(prefix, 'Modules', 'config.c.in')
125 frozenmain_c = os.path.join(prefix, 'Modules', 'frozenmain.c')
126 makefile_in = os.path.join(exec_prefix, 'Modules', 'Makefile')
127 else:
128 binlib = os.path.join(exec_prefix,
129 'lib', 'python%s' % version, 'config')
130 incldir = os.path.join(prefix, 'include', 'python%s' % version)
131 config_c_in = os.path.join(binlib, 'config.c.in')
132 frozenmain_c = os.path.join(binlib, 'frozenmain.c')
133 makefile_in = os.path.join(binlib, 'Makefile')
134 supp_sources = []
135 defines = []
136 includes = ['-I' + incldir, '-I' + binlib]
Guido van Rossumd8336c21994-10-05 16:13:01 +0000137
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000138 # sanity check of directories and files
139 for dir in [prefix, exec_prefix, binlib, incldir] + extensions:
140 if not os.path.exists(dir):
141 usage('needed directory %s not found' % dir)
142 if not os.path.isdir(dir):
143 usage('%s: not a directory' % dir)
144 for file in [config_c_in, makefile_in] + supp_sources:
145 if not os.path.exists(file):
146 usage('needed file %s not found' % file)
147 if not os.path.isfile(file):
148 usage('%s: not a plain file' % file)
149 for dir in extensions:
150 setup = os.path.join(dir, 'Setup')
151 if not os.path.exists(setup):
152 usage('needed file %s not found' % setup)
153 if not os.path.isfile(setup):
154 usage('%s: not a plain file' % setup)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000155
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000156 # check that enough arguments are passed
157 if not args:
158 usage('at least one filename argument required')
Guido van Rossumd4cc04c1996-06-17 17:49:13 +0000159
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000160 # check that the script name ends in ".py"
161 if args[0][-3:] != ".py":
162 usage('the script name must have a .py suffix')
Guido van Rossum00ff4331994-10-03 16:33:08 +0000163
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000164 # check that file arguments exist
165 for arg in args:
166 if not os.path.exists(arg):
167 usage('argument %s not found' % arg)
168 if not os.path.isfile(arg):
169 usage('%s: not a plain file' % arg)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000170
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000171 # process non-option arguments
172 scriptfile = args[0]
173 modules = args[1:]
Guido van Rossum00ff4331994-10-03 16:33:08 +0000174
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000175 # derive target name from script name
176 base = os.path.basename(scriptfile)
177 base, ext = os.path.splitext(base)
178 if base:
179 if base != scriptfile:
180 target = base
181 else:
182 target = base + '.bin'
Guido van Rossum00ff4331994-10-03 16:33:08 +0000183
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000184 # handle -o option
185 base_frozen_c = frozen_c
186 base_config_c = config_c
187 base_target = target
188 if odir and not os.path.isdir(odir):
189 try:
190 os.mkdir(odir)
191 print "Created output directory", odir
192 except os.error, msg:
193 usage('%s: mkdir failed (%s)' % (odir, str(msg)))
194 if odir:
195 frozen_c = os.path.join(odir, frozen_c)
196 config_c = os.path.join(odir, config_c)
197 target = os.path.join(odir, target)
198 makefile = os.path.join(odir, makefile)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000199
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000200 # Actual work starts here...
Guido van Rossumd8336c21994-10-05 16:13:01 +0000201
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000202 dict = findmodules.findmodules(scriptfile, modules, path)
203 names = dict.keys()
204 names.sort()
205 print "Modules being frozen:"
206 for name in names:
207 print '\t', name
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000208
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000209 backup = frozen_c + '~'
210 try:
211 os.rename(frozen_c, backup)
212 except os.error:
213 backup = None
214 outfp = open(frozen_c, 'w')
215 try:
216 makefreeze.makefreeze(outfp, dict)
217 finally:
218 outfp.close()
219 if backup:
220 if cmp.cmp(backup, frozen_c):
221 sys.stderr.write('%s not changed, not written\n' %
222 frozen_c)
223 os.rename(backup, frozen_c)
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000224
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000225 builtins = []
226 unknown = []
227 mods = dict.keys()
228 mods.sort()
229 for mod in mods:
230 if dict[mod] == '<builtin>':
231 builtins.append(mod)
232 elif dict[mod] == '<unknown>':
233 unknown.append(mod)
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000234
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000235 addfiles = []
236 if unknown:
237 addfiles, addmods = \
238 checkextensions.checkextensions(unknown, extensions)
239 for mod in addmods:
240 unknown.remove(mod)
241 builtins = builtins + addmods
242 if unknown:
243 sys.stderr.write('Warning: unknown modules remain: %s\n' %
244 string.join(unknown))
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000245
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000246 builtins.sort()
247 infp = open(config_c_in)
248 backup = config_c + '~'
249 try:
250 os.rename(config_c, backup)
251 except os.error:
252 backup = None
253 outfp = open(config_c, 'w')
254 try:
255 makeconfig.makeconfig(infp, outfp, builtins)
256 finally:
257 outfp.close()
258 infp.close()
259 if backup:
260 if cmp.cmp(backup, config_c):
261 sys.stderr.write('%s not changed, not written\n' %
262 config_c)
263 os.rename(backup, config_c)
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000264
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000265 cflags = defines + includes + ['$(OPT)']
266 libs = [os.path.join(binlib, 'libpython$(VERSION).a')]
Guido van Rossum00ff4331994-10-03 16:33:08 +0000267
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000268 makevars = parsesetup.getmakevars(makefile_in)
269 somevars = {}
270 for key in makevars.keys():
271 somevars[key] = makevars[key]
Guido van Rossum00ff4331994-10-03 16:33:08 +0000272
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000273 somevars['CFLAGS'] = string.join(cflags) # override
274 files = ['$(OPT)', '$(LDFLAGS)', base_config_c, base_frozen_c] + \
275 supp_sources + addfiles + libs + \
276 ['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)']
Guido van Rossum00ff4331994-10-03 16:33:08 +0000277
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000278 backup = makefile + '~'
279 try:
280 os.rename(makefile, backup)
281 except os.error:
282 backup = None
283 outfp = open(makefile, 'w')
284 try:
285 makemakefile.makemakefile(outfp, somevars, files, base_target)
286 finally:
287 outfp.close()
288 if backup:
289 if not cmp.cmp(backup, makefile):
290 print 'previous Makefile saved as', backup
291 else:
292 sys.stderr.write('%s not changed, not written\n' %
293 makefile)
294 os.rename(backup, makefile)
295
296 # Done!
297
298 if odir:
299 print 'Now run "make" in', odir,
300 print 'to build the target:', base_target
301 else:
302 print 'Now run "make" to build the target:', base_target
Guido van Rossum00ff4331994-10-03 16:33:08 +0000303
Guido van Rossumd8336c21994-10-05 16:13:01 +0000304
305# Print usage message and exit
306
Guido van Rossum9a6e8551997-08-10 16:47:17 +0000307def usage(msg):
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000308 sys.stdout = sys.stderr
309 print "Error:", msg
310 print "Use ``%s -h'' for help" % sys.argv[0]
311 sys.exit(2)
Guido van Rossumd8336c21994-10-05 16:13:01 +0000312
313
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000314main()
Guido van Rossum0b4b8a21997-08-10 16:56:48 +0000315
316# Local Variables:
317# indent-tabs-mode: nil
318# End: