]> granicus.if.org Git - python/commitdiff
Lawrence Hudson, SF #401702: Modify co_filename in frozen programs
authorGuido van Rossum <guido@python.org>
Tue, 20 Mar 2001 20:43:34 +0000 (20:43 +0000)
committerGuido van Rossum <guido@python.org>
Tue, 20 Mar 2001 20:43:34 +0000 (20:43 +0000)
  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

Tools/freeze/freeze.py
Tools/freeze/modulefinder.py

index eea8134880979ef4672c4168d410f913071a9053..dfd0369627d8825520905e16c5a4a27db85aeb4b 100755 (executable)
@@ -57,6 +57,10 @@ Options:
               (For debugging only -- on a win32 platform, win32 behavior
               is automatic.)
 
+-r prefix=f:  Replace path prefix.
+              Replace prefix with f in the source path references 
+              contained in the resulting binary.
+
 Arguments:
 
 script:       The Python script to be executed by the resulting binary.
@@ -109,6 +113,7 @@ def main():
     debug = 1
     odir = ''
     win = sys.platform[:3] == 'win'
+    replace_paths = []                  # settable with -r option
 
     # default the exclude list for each platform
     if win: exclude = exclude + [
@@ -139,7 +144,7 @@ def main():
 
     # Now parse the command line with the extras inserted.
     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:
         usage('getopt error: ' + str(msg))
 
@@ -174,6 +179,9 @@ def main():
             addn_link.append(a)
         if o == '-a':
             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
     if not exec_prefix:
@@ -310,7 +318,7 @@ def main():
     # collect all modules of the program
     dir = os.path.dirname(scriptfile)
     path[0] = dir
-    mf = modulefinder.ModuleFinder(path, debug, exclude)
+    mf = modulefinder.ModuleFinder(path, debug, exclude, replace_paths)
     
     if win and subsystem=='service':
         # If a Windows service, then add the "built-in" module.
index 8c4b74d442f974cf7b0b69765cf7f98b618659b7..42cbbab038b257fd2a6e37eb48c81027e4cf3f46 100644 (file)
@@ -7,6 +7,7 @@ import os
 import re
 import string
 import sys
+import new
 
 IMPORT_NAME = dis.opname.index('IMPORT_NAME')
 IMPORT_FROM = dis.opname.index('IMPORT_FROM')
@@ -49,7 +50,7 @@ class Module:
 
 class ModuleFinder:
 
-    def __init__(self, path=None, debug=0, excludes = []):
+    def __init__(self, path=None, debug=0, excludes = [], replace_paths = []):
         if path is None:
             path = sys.path
         self.path = path
@@ -58,6 +59,8 @@ class ModuleFinder:
         self.debug = debug
         self.indent = 0
         self.excludes = excludes
+        self.replace_paths = replace_paths
+        self.processed_paths = []   # Used in debugging only
 
     def msg(self, level, str, *args):
         if level <= self.debug:
@@ -250,6 +253,8 @@ class ModuleFinder:
         m = self.add_module(fqname)
         m.__file__ = pathname
         if co:
+            if self.replace_paths:
+                co = self.replace_paths_in_code(co)
             m.__code__ = co
             self.scan_code(co, m)
         self.msgout(2, "load_module ->", m)
@@ -369,6 +374,32 @@ class ModuleFinder:
                 mods.sort()
                 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():
     # Parse command line