3 # copyright (c) 2006 Josselin Mouette <joss@debian.org>
4 # Licensed under the GNU Lesser General Public License, version 2.1
5 # See COPYING for details
7 from optparse import OptionParser
8 import os,os.path,re,sys
9 from hashlib import md5
11 sourcepath='usr/share/python-support'
12 modulepath='usr/share/pyshared'
13 extensionpath='usr/lib/pyshared'
14 installedpath='usr/lib/pymodules'
16 parser = OptionParser(usage="usage: %prog [options] [directory [...]]")
18 parser.add_option("-v", "--verbose", action="store_true", dest="verbose",
19 help="verbose output", default=False)
20 parser.add_option("-p", "--package", dest="package")
21 parser.add_option("-V", "--version-info", dest="version")
23 (options, args) = parser.parse_args()
25 sys.path.append("/usr/share/python-support/private/")
26 from pysupport import py_supported,py_supported_short
28 # Set the umask so that directories are created with correct permissions
32 parser.error("No directory to process.")
35 if not os.path.isdir(basedir):
36 parser.error("%s is not a directory."%basedir)
42 def addsum(self,file,pyver,sum):
43 if file not in self.d:
45 elif pyver in self.d[file] and self.d[file][pyver] != sum:
46 sys.stderr.write("WARNING: %s exists in multiple versions\n"%file)
47 self.d[file][pyver]=sum
48 def addpyver(self,pyver):
49 self.pylist.add(pyver)
50 def isallthesame(self,file):
51 if file.endswith(".so"):
52 # If there is a .so, no need to even check, it must be moved
54 elif re.search('\.so(?:\.\d+){0,3}$', file):
55 sys.stderr.write("WARNING: %s: versioned shared object\n"%file)
58 s=[ self.d[file][pyver] for pyver in self.pylist ]
61 return (s.count(s[0]) == len(self.pylist))
63 return self.d[file].keys()
67 # Rename by preserving relative links
68 def rename_subtle (source, destination, sourcedir):
69 if os.path.islink (source):
70 linkdest = os.readlink (source)
71 if not os.path.isabs (linkdest):
72 linkdest = os.path.normpath(os.path.join(os.path.dirname(source),linkdest))
73 if not linkdest.startswith(sourcedir+'/'):
74 prefix = os.path.dirname(os.path.commonprefix((linkdest, destination)))
75 linkdest = os.path.normpath(destination)[len(prefix)+1:].count('/') * '../' + \
76 linkdest[len(prefix)+1:]
77 destdir = os.path.dirname(destination)
78 if not os.path.isdir (destdir):
80 if os.path.lexists (destination):
81 os.remove (destination)
82 os.symlink (linkdest, destination)
85 os.removedirs(os.path.dirname(source))
89 os.renames (source, destination)
91 def do_simple_move (basedir, sourcedir):
93 absdir=os.path.join(basedir, sourcedir)
94 for dir, dirs, files in os.walk (absdir):
95 reldir = dir[len(absdir):].lstrip("/")
97 rename_subtle (os.path.join(absdir, reldir, curfile), os.path.join(basedir, modulepath, reldir, curfile), absdir)
98 fileset.add(os.path.join("/", modulepath, reldir, curfile))
101 def do_move (basedir, tuples):
103 for (pybasedir, suffixdir) in tuples:
104 for pyvers in py_supported:
105 pydir=os.path.join(basedir,pybasedir,pyvers,suffixdir)
106 if not os.path.isdir(pydir):
108 file_dict.addpyver(pyvers)
109 for dir, dirs, files in os.walk(pydir):
110 reldir = dir[len(pydir):].lstrip('/')
111 for curfile in files:
112 relfile = os.path.join(reldir,curfile)
113 absfile = os.path.join(pydir,relfile)
114 # remove bytecompiled files and libtool cruft
115 if relfile.split('.')[-1] in ["pyc", "pyo", "a", "la"]:
117 elif os.path.islink(absfile):
118 file_dict.addsum(relfile,pyvers,os.readlink(absfile))
119 elif absfile.endswith('/PKG-INFO') or absfile.endswith('.egg-info'):
120 # work-around for bug #575377
121 file_dict.addsum(relfile,pyvers,md5(''.join(line.strip()+'\n' for line in file(absfile))).digest())
123 file_dict.addsum(relfile,pyvers,md5(file(absfile).read()).digest())
127 for relfile in file_dict:
128 splitfile=not file_dict.isallthesame(relfile)
130 for pyver in file_dict.list(relfile):
131 for (pybasedir, suffixdir) in tuples:
132 sourcedir=os.path.join(basedir,pybasedir,pyver,suffixdir)
133 sourcefile=os.path.join(sourcedir,relfile)
134 if not os.path.lexists(sourcefile):
137 destdir=os.path.join(extensionpath,pyver)
138 pyversions.add(pyver)
139 if sourcefile.endswith("/__init__.py") and not os.path.getsize(sourcefile):
140 # Remove namespace packages, they will be added automatically
141 # by update-python-modules.
142 # This will avoid file conflicts at the dpkg level.
143 os.remove(sourcefile)
145 os.removedirs(os.path.dirname(sourcefile))
149 rename_subtle(sourcefile,os.path.join(basedir,destdir,relfile),sourcedir)
150 files.add(os.path.join("/",destdir,relfile))
152 # If we have some versions that appear in the extension path
153 # BUT that more versions were originally supported
154 # We must add these versions to the list of supported versions
155 pyversions.update(file_dict.pylist)
156 return files, pyversions
158 def do_listonly (basedir):
160 absdir=os.path.join(basedir, modulepath)
161 for dir, dirs, files in os.walk (absdir):
162 reldir = dir[len(basedir):].lstrip("/")
163 for curfile in files:
164 fileset.add(os.path.join("/",reldir,curfile))
167 # Remove the -py$(VERSION) part of the egg directories
168 def do_eggrename (basedir, pybasedir, suffixdir=""):
169 for vers in py_supported_short:
170 pydir=os.path.join(basedir,pybasedir,"python"+vers,suffixdir)
171 suffix="-py"+vers+".egg-info"
172 if not os.path.isdir(pydir):
174 for item in os.listdir(pydir):
175 item=os.path.join(pydir,item)
176 if item.endswith(suffix):
177 new_item = item[:-len(suffix)]+".egg-info"
178 if not os.path.exists(new_item): # You never know
179 os.rename(item, new_item)
182 basedir=basedir.rstrip('/')
183 package=options.package
185 package=os.path.split(basedir)[1]
187 raise Exception("Unable to extract the package name.")
189 public_file = os.path.join(basedir,sourcepath,package+".public")
190 if os.path.isfile (public_file):
191 # movemodules was already run, do nothing
194 # /usr/share/pyshared (files installed by hand)
195 files = do_listonly (basedir)
197 # /usr/lib/pythonX.Y/site-packages (python <= 2.5)
198 # /usr/lib/pythonX.Y/dist-packages (python >= 2.6 with deb layout)
199 # /usr/local/lib/pythonX.Y/dist-packages (python >= 2.6 without deb layout)
200 do_eggrename (basedir, "usr/lib", "site-packages")
201 do_eggrename (basedir, "usr/lib", "dist-packages")
202 do_eggrename (basedir, "usr/local/lib", "dist-packages")
203 files2, pyversions = do_move (basedir, [("usr/lib", "site-packages"),
204 ("usr/lib", "dist-packages"),
205 ("usr/local/lib", "dist-packages")])
208 # /var/lib/python-support/pythonX.Y
209 do_eggrename (basedir, "var/lib/python-support")
210 files2, pyversions2 = do_move (basedir, [("var/lib/python-support", "")])
212 pyversions.update(pyversions2)
214 # /usr/lib/pymodules/pythonX.Y
215 do_eggrename (basedir, extensionpath)
216 files2, pyversions2 = do_move (basedir, [(installedpath, "")])
218 pyversions.update(pyversions2)
220 # /usr/share/python-support/$package
221 if os.path.isdir(os.path.join(basedir,"usr/share/python-support")):
222 for ent in os.listdir(os.path.join(basedir,"usr/share/python-support")):
223 if os.path.isdir(os.path.join(basedir, "usr/share/python-support", ent)):
224 files.update(do_simple_move(basedir, os.path.join("usr/share/python-support", ent)))
227 if not os.path.isdir(os.path.join(basedir,sourcepath)):
228 os.makedirs(os.path.join(basedir,sourcepath))
229 out=file(public_file, "w")
231 out.write("pyversions=%s\n\n"%(','.join(x.replace('python','') for x in sorted(pyversions))))
232 elif options.version:
233 out.write("pyversions=%s\n\n"%options.version)
234 for filename in sorted(files):
235 out.write(filename+"\n")