blob: 7018acac449fa08f1297e4c3bf8a0126f15f5e69 [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 Rossum150316e1995-08-08 14:21:07 +0000112 getpath_c = os.path.join(binlib, 'getpath.c')
113 supp_sources = [frozenmain_c, getpath_c]
Guido van Rossum00ff4331994-10-03 16:33:08 +0000114 makefile_in = os.path.join(binlib, 'Makefile')
Guido van Rossumaa233f11996-08-26 03:49:49 +0000115 defines = ['-DPYTHONPATH=\\"$(PYTHONPATH)\\"']
Guido van Rossum00ff4331994-10-03 16:33:08 +0000116 includes = ['-I' + incldir, '-I' + binlib]
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000117
Guido van Rossumd8336c21994-10-05 16:13:01 +0000118 # sanity check of directories and files
Guido van Rossum150316e1995-08-08 14:21:07 +0000119 for dir in [prefix, exec_prefix, binlib, incldir] + extensions:
Guido van Rossum00ff4331994-10-03 16:33:08 +0000120 if not os.path.exists(dir):
Guido van Rossumd8336c21994-10-05 16:13:01 +0000121 usage('needed directory %s not found' % dir)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000122 if not os.path.isdir(dir):
123 usage('%s: not a directory' % dir)
Guido van Rossum150316e1995-08-08 14:21:07 +0000124 for file in [config_c_in, makefile_in] + supp_sources:
Guido van Rossum00ff4331994-10-03 16:33:08 +0000125 if not os.path.exists(file):
126 usage('needed file %s not found' % file)
127 if not os.path.isfile(file):
128 usage('%s: not a plain file' % file)
Guido van Rossumd8336c21994-10-05 16:13:01 +0000129 for dir in extensions:
130 setup = os.path.join(dir, 'Setup')
131 if not os.path.exists(setup):
132 usage('needed file %s not found' % setup)
133 if not os.path.isfile(setup):
134 usage('%s: not a plain file' % setup)
135
136 # check that enough arguments are passed
137 if not args:
138 usage('at least one filename argument required')
Guido van Rossum00ff4331994-10-03 16:33:08 +0000139
Guido van Rossumd4cc04c1996-06-17 17:49:13 +0000140 # check that the script name ends in ".py"
141 if args[0][-3:] != ".py":
142 usage('the script name must have a .py suffix')
143
Guido van Rossum00ff4331994-10-03 16:33:08 +0000144 # check that file arguments exist
145 for arg in args:
146 if not os.path.exists(arg):
147 usage('argument %s not found' % arg)
148 if not os.path.isfile(arg):
149 usage('%s: not a plain file' % arg)
150
151 # process non-option arguments
152 scriptfile = args[0]
153 modules = args[1:]
154
155 # derive target name from script name
156 base = os.path.basename(scriptfile)
157 base, ext = os.path.splitext(base)
158 if base:
159 if base != scriptfile:
160 target = base
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000161 else:
Guido van Rossum00ff4331994-10-03 16:33:08 +0000162 target = base + '.bin'
Guido van Rossum96c4dd91996-08-26 05:14:20 +0000163
164 # handle -o option
165 base_frozen_c = frozen_c
166 base_config_c = config_c
167 base_target = target
168 if odir and not os.path.isdir(odir):
169 try:
170 os.mkdir(odir)
171 print "Created output directory", odir
172 except os.error, msg:
173 usage('%s: mkdir failed (%s)' % (odir, str(msg)))
174 if odir:
175 frozen_c = os.path.join(odir, frozen_c)
176 config_c = os.path.join(odir, config_c)
177 target = os.path.join(odir, target)
178 makefile = os.path.join(odir,makefile)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000179
180 # Actual work starts here...
181
182 dict = findmodules.findmodules(scriptfile, modules, path)
Guido van Rossum150316e1995-08-08 14:21:07 +0000183 names = dict.keys()
184 names.sort()
185 print "Modules being frozen:"
186 for name in names:
187 print '\t', name
Guido van Rossum00ff4331994-10-03 16:33:08 +0000188
Guido van Rossumd8336c21994-10-05 16:13:01 +0000189 backup = frozen_c + '~'
190 try:
191 os.rename(frozen_c, backup)
192 except os.error:
193 backup = None
194 outfp = open(frozen_c, 'w')
195 try:
196 makefreeze.makefreeze(outfp, dict)
197 finally:
198 outfp.close()
199 if backup:
200 if cmp.cmp(backup, frozen_c):
201 sys.stderr.write('%s not changed, not written\n' %
202 frozen_c)
203 os.rename(backup, frozen_c)
204
Guido van Rossum00ff4331994-10-03 16:33:08 +0000205 builtins = []
Guido van Rossumd8336c21994-10-05 16:13:01 +0000206 unknown = []
Guido van Rossum619e1be1994-10-03 16:37:36 +0000207 mods = dict.keys()
208 mods.sort()
209 for mod in mods:
Guido van Rossum00ff4331994-10-03 16:33:08 +0000210 if dict[mod] == '<builtin>':
211 builtins.append(mod)
Guido van Rossum619e1be1994-10-03 16:37:36 +0000212 elif dict[mod] == '<unknown>':
Guido van Rossumd8336c21994-10-05 16:13:01 +0000213 unknown.append(mod)
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000214
Guido van Rossumd8336c21994-10-05 16:13:01 +0000215 addfiles = []
216 if unknown:
217 addfiles, addmods = \
218 checkextensions.checkextensions(unknown, extensions)
219 for mod in addmods:
220 unknown.remove(mod)
221 builtins = builtins + addmods
222 if unknown:
223 sys.stderr.write('Warning: unknown modules remain: %s\n' %
224 string.join(unknown))
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000225
Guido van Rossumd8336c21994-10-05 16:13:01 +0000226 builtins.sort()
Guido van Rossum00ff4331994-10-03 16:33:08 +0000227 infp = open(config_c_in)
Guido van Rossumd8336c21994-10-05 16:13:01 +0000228 backup = config_c + '~'
229 try:
230 os.rename(config_c, backup)
231 except os.error:
232 backup = None
Guido van Rossum00ff4331994-10-03 16:33:08 +0000233 outfp = open(config_c, 'w')
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000234 try:
Guido van Rossum00ff4331994-10-03 16:33:08 +0000235 makeconfig.makeconfig(infp, outfp, builtins)
236 finally:
237 outfp.close()
238 infp.close()
Guido van Rossumd8336c21994-10-05 16:13:01 +0000239 if backup:
240 if cmp.cmp(backup, config_c):
241 sys.stderr.write('%s not changed, not written\n' %
242 config_c)
243 os.rename(backup, config_c)
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000244
Guido van Rossum00ff4331994-10-03 16:33:08 +0000245 cflags = defines + includes + ['$(OPT)']
246 libs = []
247 for n in 'Modules', 'Python', 'Objects', 'Parser':
248 n = 'lib%s.a' % n
249 n = os.path.join(binlib, n)
250 libs.append(n)
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000251
Guido van Rossum00ff4331994-10-03 16:33:08 +0000252 makevars = parsesetup.getmakevars(makefile_in)
253 somevars = {}
254 for key in makevars.keys():
255 somevars[key] = makevars[key]
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000256
Guido van Rossum00ff4331994-10-03 16:33:08 +0000257 somevars['CFLAGS'] = string.join(cflags) # override
Guido van Rossum96c4dd91996-08-26 05:14:20 +0000258 files = ['$(OPT)', '$(LDFLAGS)', base_config_c, base_frozen_c] + \
Guido van Rossum150316e1995-08-08 14:21:07 +0000259 supp_sources + addfiles + libs + \
Guido van Rossum00ff4331994-10-03 16:33:08 +0000260 ['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)']
261
Guido van Rossum6d67b601995-04-05 10:58:39 +0000262 backup = makefile + '~'
263 try:
264 os.rename(makefile, backup)
265 except os.error:
266 backup = None
Guido van Rossum00ff4331994-10-03 16:33:08 +0000267 outfp = open(makefile, 'w')
268 try:
Guido van Rossum96c4dd91996-08-26 05:14:20 +0000269 makemakefile.makemakefile(outfp, somevars, files, base_target)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000270 finally:
271 outfp.close()
Guido van Rossum6d67b601995-04-05 10:58:39 +0000272 if backup:
273 if not cmp.cmp(backup, makefile):
274 print 'previous Makefile saved as', backup
275 else:
276 sys.stderr.write('%s not changed, not written\n' %
277 makefile)
278 os.rename(backup, makefile)
Guido van Rossum00ff4331994-10-03 16:33:08 +0000279
280 # Done!
281
Guido van Rossum96c4dd91996-08-26 05:14:20 +0000282 if odir:
283 print 'Now run make in', odir,
284 print 'to build the target:', base_target
285 else:
286 print 'Now run make to build the target:', base_target
Guido van Rossum00ff4331994-10-03 16:33:08 +0000287
Guido van Rossumd8336c21994-10-05 16:13:01 +0000288
289# Print usage message and exit
290
291def usage(msg = None):
Guido van Rossum96c4dd91996-08-26 05:14:20 +0000292 sys.stderr.write(__doc__)
Guido van Rossumd4cc04c1996-06-17 17:49:13 +0000293 # Put the error last since the usage message scrolls off the screen
294 if msg:
295 sys.stderr.write('\nError: ' + str(msg) + '\n')
Guido van Rossumd8336c21994-10-05 16:13:01 +0000296 sys.exit(2)
297
298
Guido van Rossumdbaf3321994-10-03 10:25:54 +0000299main()