Kaydet (Commit) 6b767ac8 authored tarafından Guido van Rossum's avatar Guido van Rossum

Lawrence Hudson, SF #401702: Modify co_filename in frozen programs

  This patch was developed primarily to reduce the size of the
  frozen binary.  It is particularly useful when freezing for 'small'
  platforms, such as Palm OS, where you really want to save that
  last miserable byte.

  A limitation of this patch is that it does not provide any feedback
  about the replacements being made.  As the path matching
  is case-sensitive this may lead to unexpected behaviour for DOS
  and Windows people, eg
      > freeze.py -r C:\Python\Lib\=py\ goats.py
  should probably be:
      > freeze.py -r c:\python\lib\=py\ goats.py
üst b845cb09
...@@ -57,6 +57,10 @@ Options: ...@@ -57,6 +57,10 @@ Options:
(For debugging only -- on a win32 platform, win32 behavior (For debugging only -- on a win32 platform, win32 behavior
is automatic.) is automatic.)
-r prefix=f: Replace path prefix.
Replace prefix with f in the source path references
contained in the resulting binary.
Arguments: Arguments:
script: The Python script to be executed by the resulting binary. script: The Python script to be executed by the resulting binary.
...@@ -109,6 +113,7 @@ def main(): ...@@ -109,6 +113,7 @@ def main():
debug = 1 debug = 1
odir = '' odir = ''
win = sys.platform[:3] == 'win' win = sys.platform[:3] == 'win'
replace_paths = [] # settable with -r option
# default the exclude list for each platform # default the exclude list for each platform
if win: exclude = exclude + [ if win: exclude = exclude + [
...@@ -139,7 +144,7 @@ def main(): ...@@ -139,7 +144,7 @@ def main():
# Now parse the command line with the extras inserted. # Now parse the command line with the extras inserted.
try: try:
opts, args = getopt.getopt(sys.argv[1:], 'a:de:hmo:p:P:qs:wx:l:') opts, args = getopt.getopt(sys.argv[1:], 'r:a:de:hmo:p:P:qs:wx:l:')
except getopt.error, msg: except getopt.error, msg:
usage('getopt error: ' + str(msg)) usage('getopt error: ' + str(msg))
...@@ -174,6 +179,9 @@ def main(): ...@@ -174,6 +179,9 @@ def main():
addn_link.append(a) addn_link.append(a)
if o == '-a': if o == '-a':
apply(modulefinder.AddPackagePath, tuple(string.split(a,"=", 2))) apply(modulefinder.AddPackagePath, tuple(string.split(a,"=", 2)))
if o == '-r':
f,r = string.split(a,"=", 2)
replace_paths.append( (f,r) )
# default prefix and exec_prefix # default prefix and exec_prefix
if not exec_prefix: if not exec_prefix:
...@@ -310,7 +318,7 @@ def main(): ...@@ -310,7 +318,7 @@ def main():
# collect all modules of the program # collect all modules of the program
dir = os.path.dirname(scriptfile) dir = os.path.dirname(scriptfile)
path[0] = dir path[0] = dir
mf = modulefinder.ModuleFinder(path, debug, exclude) mf = modulefinder.ModuleFinder(path, debug, exclude, replace_paths)
if win and subsystem=='service': if win and subsystem=='service':
# If a Windows service, then add the "built-in" module. # If a Windows service, then add the "built-in" module.
......
...@@ -7,6 +7,7 @@ import os ...@@ -7,6 +7,7 @@ import os
import re import re
import string import string
import sys import sys
import new
IMPORT_NAME = dis.opname.index('IMPORT_NAME') IMPORT_NAME = dis.opname.index('IMPORT_NAME')
IMPORT_FROM = dis.opname.index('IMPORT_FROM') IMPORT_FROM = dis.opname.index('IMPORT_FROM')
...@@ -49,7 +50,7 @@ class Module: ...@@ -49,7 +50,7 @@ class Module:
class ModuleFinder: class ModuleFinder:
def __init__(self, path=None, debug=0, excludes = []): def __init__(self, path=None, debug=0, excludes = [], replace_paths = []):
if path is None: if path is None:
path = sys.path path = sys.path
self.path = path self.path = path
...@@ -58,6 +59,8 @@ class ModuleFinder: ...@@ -58,6 +59,8 @@ class ModuleFinder:
self.debug = debug self.debug = debug
self.indent = 0 self.indent = 0
self.excludes = excludes self.excludes = excludes
self.replace_paths = replace_paths
self.processed_paths = [] # Used in debugging only
def msg(self, level, str, *args): def msg(self, level, str, *args):
if level <= self.debug: if level <= self.debug:
...@@ -250,6 +253,8 @@ class ModuleFinder: ...@@ -250,6 +253,8 @@ class ModuleFinder:
m = self.add_module(fqname) m = self.add_module(fqname)
m.__file__ = pathname m.__file__ = pathname
if co: if co:
if self.replace_paths:
co = self.replace_paths_in_code(co)
m.__code__ = co m.__code__ = co
self.scan_code(co, m) self.scan_code(co, m)
self.msgout(2, "load_module ->", m) self.msgout(2, "load_module ->", m)
...@@ -369,6 +374,32 @@ class ModuleFinder: ...@@ -369,6 +374,32 @@ class ModuleFinder:
mods.sort() mods.sort()
print "?", key, "from", string.join(mods, ', ') print "?", key, "from", string.join(mods, ', ')
def replace_paths_in_code(self, co):
new_filename = original_filename = os.path.normpath(co.co_filename)
for f,r in self.replace_paths:
if original_filename.startswith(f):
new_filename = r+original_filename[len(f):]
break
if self.debug and original_filename not in self.processed_paths:
if new_filename!=original_filename:
self.msgout(2, "co_filename %r changed to %r" \
% (original_filename,new_filename,))
else:
self.msgout(2, "co_filename %r remains unchanged" \
% (original_filename,))
self.processed_paths.append(original_filename)
consts = list(co.co_consts)
for i in range(len(consts)):
if isinstance(consts[i], type(co)):
consts[i] = self.replace_paths_in_code(consts[i])
return new.code(co.co_argcount, co.co_nlocals, co.co_stacksize,
co.co_flags, co.co_code, tuple(consts), co.co_names,
co.co_varnames, new_filename, co.co_name,
co.co_firstlineno, co.co_lnotab)
def test(): def test():
# Parse command line # Parse command line
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment