fix pylint error for python-support
[tools/python-support.git] / movemodules
1 #! /usr/bin/python
2 #
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
6
7 from optparse import OptionParser
8 import os,os.path,re,sys
9 from hashlib import md5
10
11 sourcepath='usr/share/python-support'
12 modulepath='usr/share/pyshared'
13 extensionpath='usr/lib/pyshared'
14 installedpath='usr/lib/pymodules'
15
16 parser = OptionParser(usage="usage: %prog [options] [directory [...]]")
17
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")
22
23 (options, args) = parser.parse_args()
24
25 sys.path.append("/usr/share/python-support/private/")
26 from pysupport import py_supported,py_supported_short
27
28 # Set the umask so that directories are created with correct permissions
29 os.umask(022)
30
31 if not args:
32   parser.error("No directory to process.")
33
34 for basedir in args:
35   if not os.path.isdir(basedir):
36     parser.error("%s is not a directory."%basedir)
37
38 class filelist:
39   def __init__(self):
40     self.d={}
41     self.pylist=set()
42   def addsum(self,file,pyver,sum):
43     if file not in self.d:
44       self.d[file]={}
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
53       return False
54     elif re.search('\.so(?:\.\d+){0,3}$', file):
55       sys.stderr.write("WARNING: %s: versioned shared object\n"%file)
56       return False
57     try:
58       s=[ self.d[file][pyver] for pyver in self.pylist ]
59     except KeyError:
60       return False
61     return (s.count(s[0]) == len(self.pylist))
62   def list(self,file):
63     return self.d[file].keys()
64   def __iter__(self):
65     return iter(self.d)
66
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):
79           os.makedirs (destdir)
80         if os.path.lexists (destination):
81           os.remove (destination)
82         os.symlink (linkdest, destination)
83         os.remove (source)
84         try:
85           os.removedirs(os.path.dirname(source))
86         except OSError:
87           pass
88         return
89   os.renames (source, destination)
90
91 def do_simple_move (basedir, sourcedir):
92   fileset = set()
93   absdir=os.path.join(basedir, sourcedir)
94   for dir, dirs, files in os.walk (absdir):
95     reldir = dir[len(absdir):].lstrip("/")
96     for curfile in files:
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))
99   return fileset
100
101 def do_move (basedir, tuples):
102   file_dict=filelist()
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):
107         continue
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"]:
116             os.remove(absfile)
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())
122           else:
123             file_dict.addsum(relfile,pyvers,md5(file(absfile).read()).digest())
124
125   files = set()
126   pyversions = set()
127   for relfile in file_dict:
128     splitfile=not file_dict.isallthesame(relfile)
129     destdir=modulepath
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):
135           continue
136         if splitfile:
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)
144           try:
145             os.removedirs(os.path.dirname(sourcefile))
146           except OSError:
147             pass
148         else:
149           rename_subtle(sourcefile,os.path.join(basedir,destdir,relfile),sourcedir)
150           files.add(os.path.join("/",destdir,relfile))
151   if pyversions:
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
157
158 def do_listonly (basedir):
159   fileset = set()
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))
165   return fileset
166
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):
173       continue
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)
180
181 for basedir in args:
182   basedir=basedir.rstrip('/')
183   package=options.package
184   if not package:
185     package=os.path.split(basedir)[1]
186   if not package:
187     raise Exception("Unable to extract the package name.")
188
189   public_file = os.path.join(basedir,sourcepath,package+".public")
190   if os.path.isfile (public_file):
191     # movemodules was already run, do nothing
192     sys.exit(0)
193
194   # /usr/share/pyshared (files installed by hand)
195   files = do_listonly (basedir)
196
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")])
206   files.update(files2)
207
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", "")])
211   files.update(files2)
212   pyversions.update(pyversions2)
213
214   # /usr/lib/pymodules/pythonX.Y
215   do_eggrename (basedir, extensionpath)
216   files2, pyversions2 = do_move (basedir, [(installedpath, "")])
217   files.update(files2)
218   pyversions.update(pyversions2)
219
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)))
225
226   if files:
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")
230     if pyversions:
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")
236     out.close()
237