blob: 4a555dd42f556fb9bae322b779bb96c0428b5eb3 [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 Rossum96c4dd91996-08-26 05:14:20 +00009-p prefix: This is the prefix used when you ran ``name install''
10 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 Rossumd8336c21994-10-05 16:13:01 +000013
Guido van Rossum150316e1995-08-08 14:21:07 +000014-P exec_prefix: Like -p but this is the 'exec_prefix', used to
Guido van Rossum96c4dd91996-08-26 05:14:20 +000015 install objects etc. The default is whatever sys.exec_prefix
16 evaluates to, or the -p argument if given.
Guido van Rossum150316e1995-08-08 14:21:07 +000017
Guido van Rossumd8336c21994-10-05 16:13:01 +000018-e extension: A directory containing additional .o files that
19 may be used to resolve modules. This directory
20 should also have a Setup file describing the .o files.
21 More than one -e option may be given.
22
Guido van Rossum96c4dd91996-08-26 05:14:20 +000023-o dir: Directory where the output files are created; default '.'.
24
25Arguments:
26
Guido van Rossumd4cc04c1996-06-17 17:49:13 +000027script.py: The Python script to be executed by the resulting binary.
Guido van Rossum150316e1995-08-08 14:21:07 +000028 It *must* end with a .py suffix!
Guido van Rossumd8336c21994-10-05 16:13:01 +000029
30module ...: Additional Python modules (referenced by pathname)
31 that will be included in the resulting binary. These
32 may be .py or .pyc files.
Guido van Rossum150316e1995-08-08 14:21:07 +000033
34NOTES:
35
36In order to use freeze successfully, you must have built Python and
Guido van Rossumd4cc04c1996-06-17 17:49:13 +000037installed it ("make install").
Guido van Rossum150316e1995-08-08 14:21:07 +000038
Guido van Rossum96c4dd91996-08-26 05:14:20 +000039The script should not use modules provided only as shared libraries;
40if it does, the resulting binary is not self-contained.
Guido van Rossumd8336c21994-10-05 16:13:01 +000041"""
Guido van Rossum00ff4331994-10-03 16:33:08 +000042
43
Guido van Rossum00ff4331994-10-03 16:33:08 +000044# Import standard modules
45
Guido van Rossumd8336c21994-10-05 16:13:01 +000046import cmp
Guido van Rossumdbaf3321994-10-03 10:25:54 +000047import getopt
Guido van Rossum00ff4331994-10-03 16:33:08 +000048import os
Guido van Rossumdbaf3321994-10-03 10:25:54 +000049import string
Guido van Rossum00ff4331994-10-03 16:33:08 +000050import sys
51import addpack
Guido van Rossumdbaf3321994-10-03 10:25:54 +000052
Guido van Rossumdbaf3321994-10-03 10:25:54 +000053
Guido van Rossum00ff4331994-10-03 16:33:08 +000054# Import the freeze-private modules
55
Guido van Rossumd8336c21994-10-05 16:13:01 +000056import checkextensions
Guido van Rossum00ff4331994-10-03 16:33:08 +000057import findmodules
58import makeconfig
59import makefreeze
60import makemakefile
61import parsesetup
62
Guido van Rossum00ff4331994-10-03 16:33:08 +000063
64# Main program
65
Guido van Rossumdbaf3321994-10-03 10:25:54 +000066def main():
Guido van Rossum00ff4331994-10-03 16:33:08 +000067 # overridable context
Guido van Rossum96c4dd91996-08-26 05:14:20 +000068 prefix = None # settable with -p option
Guido van Rossum150316e1995-08-08 14:21:07 +000069 exec_prefix = None # settable with -P option
Guido van Rossumd8336c21994-10-05 16:13:01 +000070 extensions = []
Guido van Rossum00ff4331994-10-03 16:33:08 +000071 path = sys.path
Guido van Rossum96c4dd91996-08-26 05:14:20 +000072 odir = ''
Guido van Rossum00ff4331994-10-03 16:33:08 +000073
74 # output files
75 frozen_c = 'frozen.c'
76 config_c = 'config.c'
77 target = 'a.out' # normally derived from script name
78 makefile = 'Makefile'
79
80 # parse command line
Guido van Rossumdbaf3321994-10-03 10:25:54 +000081 try:
Guido van Rossum96c4dd91996-08-26 05:14:20 +000082 opts, args = getopt.getopt(sys.argv[1:], 'e:o:p:P:')
Guido van Rossumdbaf3321994-10-03 10:25:54 +000083 except getopt.error, msg:
Guido van Rossum00ff4331994-10-03 16:33:08 +000084 usage('getopt error: ' + str(msg))
85
86 # proces option arguments
Guido van Rossumdbaf3321994-10-03 10:25:54 +000087 for o, a in opts:
Guido van Rossumd8336c21994-10-05 16:13:01 +000088 if o == '-e':
89 extensions.append(a)
Guido van Rossum96c4dd91996-08-26 05:14:20 +000090 if o == '-o':
91 odir = a
Guido van Rossum00ff4331994-10-03 16:33:08 +000092 if o == '-p':
93 prefix = a
Guido van Rossum150316e1995-08-08 14:21:07 +000094 if o == '-P':
95 exec_prefix = a
96
Guido van Rossum96c4dd91996-08-26 05:14:20 +000097 # default prefix and exec_prefix
98 if not exec_prefix:
99 if prefix:
Guido van Rossum150316e1995-08-08 14:21:07 +0000100 exec_prefix = prefix
Guido van Rossum96c4dd91996-08-26 05:14:20 +0000101 else:
102 exec_prefix = sys.exec_prefix
103 if not prefix:
104 prefix = sys.prefix
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000105
Guido van Rossum00ff4331994-10-03 16:33:08 +0000106 # locations derived from options
Guido van Rossum96c4dd91996-08-26 05:14:20 +0000107 version = sys.version[:3]
108 binlib = os.path.join(exec_prefix, 'lib/python%s/config' % version)
109 incldir = os.path.join(prefix, 'include/python%s' % version)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000110 config_c_in = os.path.join(binlib, 'config.c.in')
111 frozenmain_c = os.path.join(binlib, 'frozenmain.c')
Guido van Rossumb3afce51997-07-19 21:58:30 +0000112 supp_sources = []
Guido van Rossum00ff4331994-10-03 16:33:08 +0000113 makefile_in = os.path.join(binlib, 'Makefile')
Guido van Rossumb3afce51997-07-19 21:58:30 +0000114 defines = []
Guido van Rossum00ff4331994-10-03 16:33:08 +0000115 includes = ['-I' + incldir, '-I' + binlib]
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000116
Guido van Rossumd8336c21994-10-05 16:13:01 +0000117 # sanity check of directories and files
Guido van Rossum150316e1995-08-08 14:21:07 +0000118 for dir in [prefix, exec_prefix, binlib, incldir] + extensions:
Guido van Rossum00ff4331994-10-03 16:33:08 +0000119 if not os.path.exists(dir):
Guido van Rossumd8336c21994-10-05 16:13:01 +0000120 usage('needed directory %s not found' % dir)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000121 if not os.path.isdir(dir):
122 usage('%s: not a directory' % dir)
Guido van Rossum150316e1995-08-08 14:21:07 +0000123 for file in [config_c_in, makefile_in] + supp_sources:
Guido van Rossum00ff4331994-10-03 16:33:08 +0000124 if not os.path.exists(file):
125 usage('needed file %s not found' % file)
126 if not os.path.isfile(file):
127 usage('%s: not a plain file' % file)
Guido van Rossumd8336c21994-10-05 16:13:01 +0000128 for dir in extensions:
129 setup = os.path.join(dir, 'Setup')
130 if not os.path.exists(setup):
131 usage('needed file %s not found' % setup)
132 if not os.path.isfile(setup):
133 usage('%s: not a plain file' % setup)
134
135 # check that enough arguments are passed
136 if not args:
137 usage('at least one filename argument required')
Guido van Rossum00ff4331994-10-03 16:33:08 +0000138
Guido van Rossumd4cc04c1996-06-17 17:49:13 +0000139 # check that the script name ends in ".py"
140 if args[0][-3:] != ".py":
141 usage('the script name must have a .py suffix')
142
Guido van Rossum00ff4331994-10-03 16:33:08 +0000143 # check that file arguments exist
144 for arg in args:
145 if not os.path.exists(arg):
146 usage('argument %s not found' % arg)
147 if not os.path.isfile(arg):
148 usage('%s: not a plain file' % arg)
149
150 # process non-option arguments
151 scriptfile = args[0]
152 modules = args[1:]
153
154 # derive target name from script name
155 base = os.path.basename(scriptfile)
156 base, ext = os.path.splitext(base)
157 if base:
158 if base != scriptfile:
159 target = base
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000160 else:
Guido van Rossum00ff4331994-10-03 16:33:08 +0000161 target = base + '.bin'
Guido van Rossum96c4dd91996-08-26 05:14:20 +0000162
163 # handle -o option
164 base_frozen_c = frozen_c
165 base_config_c = config_c
166 base_target = target
167 if odir and not os.path.isdir(odir):
168 try:
169 os.mkdir(odir)
170 print "Created output directory", odir
171 except os.error, msg:
172 usage('%s: mkdir failed (%s)' % (odir, str(msg)))
173 if odir:
174 frozen_c = os.path.join(odir, frozen_c)
175 config_c = os.path.join(odir, config_c)
176 target = os.path.join(odir, target)
177 makefile = os.path.join(odir,makefile)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000178
179 # Actual work starts here...
180
181 dict = findmodules.findmodules(scriptfile, modules, path)
Guido van Rossum150316e1995-08-08 14:21:07 +0000182 names = dict.keys()
183 names.sort()
184 print "Modules being frozen:"
185 for name in names:
186 print '\t', name
Guido van Rossum00ff4331994-10-03 16:33:08 +0000187
Guido van Rossumd8336c21994-10-05 16:13:01 +0000188 backup = frozen_c + '~'
189 try:
190 os.rename(frozen_c, backup)
191 except os.error:
192 backup = None
193 outfp = open(frozen_c, 'w')
194 try:
195 makefreeze.makefreeze(outfp, dict)
196 finally:
197 outfp.close()
198 if backup:
199 if cmp.cmp(backup, frozen_c):
200 sys.stderr.write('%s not changed, not written\n' %
201 frozen_c)
202 os.rename(backup, frozen_c)
203
Guido van Rossum00ff4331994-10-03 16:33:08 +0000204 builtins = []
Guido van Rossumd8336c21994-10-05 16:13:01 +0000205 unknown = []
Guido van Rossum619e1be1994-10-03 16:37:36 +0000206 mods = dict.keys()
207 mods.sort()
208 for mod in mods:
Guido van Rossum00ff4331994-10-03 16:33:08 +0000209 if dict[mod] == '<builtin>':
210 builtins.append(mod)
Guido van Rossum619e1be1994-10-03 16:37:36 +0000211 elif dict[mod] == '<unknown>':
Guido van Rossumd8336c21994-10-05 16:13:01 +0000212 unknown.append(mod)
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000213
Guido van Rossumd8336c21994-10-05 16:13:01 +0000214 addfiles = []
215 if unknown:
216 addfiles, addmods = \
217 checkextensions.checkextensions(unknown, extensions)
218 for mod in addmods:
219 unknown.remove(mod)
220 builtins = builtins + addmods
221 if unknown:
222 sys.stderr.write('Warning: unknown modules remain: %s\n' %
223 string.join(unknown))
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000224
Guido van Rossumd8336c21994-10-05 16:13:01 +0000225 builtins.sort()
Guido van Rossum00ff4331994-10-03 16:33:08 +0000226 infp = open(config_c_in)
Guido van Rossumd8336c21994-10-05 16:13:01 +0000227 backup = config_c + '~'
228 try:
229 os.rename(config_c, backup)
230 except os.error:
231 backup = None
Guido van Rossum00ff4331994-10-03 16:33:08 +0000232 outfp = open(config_c, 'w')
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000233 try:
Guido van Rossum00ff4331994-10-03 16:33:08 +0000234 makeconfig.makeconfig(infp, outfp, builtins)
235 finally:
236 outfp.close()
237 infp.close()
Guido van Rossumd8336c21994-10-05 16:13:01 +0000238 if backup:
239 if cmp.cmp(backup, config_c):
240 sys.stderr.write('%s not changed, not written\n' %
241 config_c)
242 os.rename(backup, config_c)
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000243
Guido van Rossum00ff4331994-10-03 16:33:08 +0000244 cflags = defines + includes + ['$(OPT)']
Guido van Rossumb3afce51997-07-19 21:58:30 +0000245 libs = [os.path.join(binlib, 'libpython$(VERSION).a')]
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000246
Guido van Rossum00ff4331994-10-03 16:33:08 +0000247 makevars = parsesetup.getmakevars(makefile_in)
248 somevars = {}
249 for key in makevars.keys():
250 somevars[key] = makevars[key]
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000251
Guido van Rossum00ff4331994-10-03 16:33:08 +0000252 somevars['CFLAGS'] = string.join(cflags) # override
Guido van Rossum96c4dd91996-08-26 05:14:20 +0000253 files = ['$(OPT)', '$(LDFLAGS)', base_config_c, base_frozen_c] + \
Guido van Rossum150316e1995-08-08 14:21:07 +0000254 supp_sources + addfiles + libs + \
Guido van Rossum00ff4331994-10-03 16:33:08 +0000255 ['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)']
256
Guido van Rossum6d67b601995-04-05 10:58:39 +0000257 backup = makefile + '~'
258 try:
259 os.rename(makefile, backup)
260 except os.error:
261 backup = None
Guido van Rossum00ff4331994-10-03 16:33:08 +0000262 outfp = open(makefile, 'w')
263 try:
Guido van Rossum96c4dd91996-08-26 05:14:20 +0000264 makemakefile.makemakefile(outfp, somevars, files, base_target)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000265 finally:
266 outfp.close()
Guido van Rossum6d67b601995-04-05 10:58:39 +0000267 if backup:
268 if not cmp.cmp(backup, makefile):
269 print 'previous Makefile saved as', backup
270 else:
271 sys.stderr.write('%s not changed, not written\n' %
272 makefile)
273 os.rename(backup, makefile)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000274
275 # Done!
276
Guido van Rossum96c4dd91996-08-26 05:14:20 +0000277 if odir:
Guido van Rossumb3afce51997-07-19 21:58:30 +0000278 print 'Now run "make" in', odir,
Guido van Rossum96c4dd91996-08-26 05:14:20 +0000279 print 'to build the target:', base_target
280 else:
Guido van Rossumb3afce51997-07-19 21:58:30 +0000281 print 'Now run "make" to build the target:', base_target
Guido van Rossum00ff4331994-10-03 16:33:08 +0000282
Guido van Rossumd8336c21994-10-05 16:13:01 +0000283
284# Print usage message and exit
285
286def usage(msg = None):
Guido van Rossum96c4dd91996-08-26 05:14:20 +0000287 sys.stderr.write(__doc__)
Guido van Rossumd4cc04c1996-06-17 17:49:13 +0000288 # Put the error last since the usage message scrolls off the screen
289 if msg:
290 sys.stderr.write('\nError: ' + str(msg) + '\n')
Guido van Rossumd8336c21994-10-05 16:13:01 +0000291 sys.exit(2)
292
293
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000294main()