/[smeserver]/builds_bin/rpm-solver.py
ViewVC logotype

Annotation of /builds_bin/rpm-solver.py

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (hide annotations) (download) (as text)
Sat Dec 2 17:19:10 2006 UTC (17 years, 11 months ago) by slords
Branch: MAIN
CVS Tags: HEAD
Content type: text/x-python
Update build tools

1 slords 1.1 #! /usr/bin/env python
2    
3     # rpm-solver.py
4     # Given a pile of RPMs will check dependency closure, will attempt to figure out
5     # their installation order.
6     #
7     # Copyright 2005 Progeny Linux Systems, Inc.
8     #
9     # This program is free software; you can redistribute it and/or modify
10     # it under the terms of the GNU General Public License as published by
11     # the Free Software Foundation; either version 2 of the License, or
12     # (at your option) any later version.
13     #
14     # This program is distributed in the hope that it will be useful,
15     # but WITHOUT ANY WARRANTY; without even the implied warranty of
16     # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17     # GNU General Public License for more details.
18     #
19     # You should have received a copy of the GNU General Public License
20     # along with this program; if not, write to the Free Software
21     # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22     #
23     # Author: Sam Hart
24    
25     import os
26     import fnmatch
27     import getopt
28     import sys
29     import getopt
30     import rpm
31     import traceback
32     import commands
33     import tempfile
34    
35     class progress_bar:
36     def __init__(self, prefix="Progress :", prog_char="-", col=60, outnode=sys.stdout):
37     self.f = outnode
38     self.prog_char = prog_char
39     self.col = col
40     self.spinner = ["|", "/", "-", "\\"]
41     self.spin_count = 0
42     self.prefix = prefix
43    
44     def set(self, prefix="Progress :"):
45     self.prefix = prefix
46    
47     def clear(self):
48     self.f.write("\r")
49     for i in range(0, self.col):
50     self.f.write(" ")
51    
52     self.f.write("\r")
53     self.f.flush()
54    
55     def progress(self, percentage):
56     """Count must be out of 100%"""
57    
58     if percentage > 1.0:
59     percentage = 1.0
60    
61     self.f.write(("\r%s 0 |") % self.prefix)
62     width = self.col - len(("\r%s 0 100 |") % self.prefix) + 1
63     count = width * percentage
64    
65     i = 1
66     while i < count:
67     self.f.write(self.prog_char)
68     i = i + 1
69    
70     if count < width:
71     self.f.write(">")
72     while i < width:
73     self.f.write(" ")
74     i = i + 1
75    
76     if self.spin_count >= len(self.spinner):
77     self.spin_count = 0
78    
79     self.f.write(self.spinner[self.spin_count])
80     self.spin_count = self.spin_count + 1
81    
82     self.f.write(" 100 ")
83     self.f.flush()
84    
85     class rpm_solver:
86     def __init__(self, progress=0, verbose=0):
87     self.progress = progress
88     self.verbose = verbose
89     self._initdb = 0
90     col = commands.getoutput("echo \"$COLUMNS\"")
91     try:
92     columns = int(col)
93     except:
94     columns = 60
95     self.pb = progress_bar("rpm_solver :", "-", columns, sys.stderr)
96    
97    
98     def init_db(self, rpm_dir, avail_dir=None, recursive=0):
99     """ Init the database """
100    
101     self.solver_db = self.db(rpm_dir, recursive)
102     self.solver_db.populate_db(self.verbose, self.pb, 1, self.progress)
103    
104     self.use_avail = 0
105     self._initdb = 1
106    
107     if avail_dir:
108     self.avail_db = self.db(avail_dir, recursive)
109     self.avail_db.populate_db(self.verbose, self.pb, 0, self.progress)
110     self.use_avail = 1
111    
112     def what_provides(self, solver_db, name, version=None):
113     """ Given a name and a version, see what provides it """
114    
115     for hdr_key in solver_db.rpmdb.keys():
116     provides = solver_db.rpmdb[hdr_key][rpm.RPMTAG_PROVIDES]
117     if name in provides:
118     if version:
119     version_check = solver_db.rpmdb[hdr_key][rpm.RPMTAG_VERSION]
120     if version == version_check:
121     return hdr_key
122     else:
123     return hdr_key
124     file_list = solver_db.rpmdb[hdr_key][rpm.RPMTAG_FILENAMES]
125     if name in file_list:
126     return hdr_key
127    
128     return None
129    
130     def dep_closure(self):
131     """ Determine if they have dependency closure """
132    
133     needed = []
134     problems = []
135    
136     if self._initdb:
137     missing_deps = self.solver_db.ts.check()
138     if self.verbose > 1:
139     print "->Result of solver_db.ts.check():"
140     print missing_deps
141     if len(missing_deps):
142     for dep in missing_deps:
143     # XXX FIXME
144     # Okay, we completely ignore the version here, which is
145     # wrong wrong WRONG! We should be smacked.
146     if self.use_avail:
147     package = self.what_provides(self.avail_db, dep[1][0], dep[1][1])
148     if package:
149     needed.append(package)
150     else:
151     problems.append("%s needs %s" % (dep[0][0], dep[1][0]))
152     else:
153     package = self.what_provides(self.solver_db, dep[1][0])
154     if package:
155     needed.append(package)
156     else:
157     problems.append("%s needs %s" % (dep[0][0], dep[1][0]))
158     else:
159     problems.append("Database has not been populated")
160    
161     return needed, problems
162    
163     def _get_filename_from_hdr(self, pkg_te):
164     """ Given a package name, find the filename for it """
165    
166     pkg = pkg_te.N()
167    
168     for name in self.solver_db.rpmdb.keys():
169     if pkg == self.solver_db.rpmdb[name][rpm.RPMTAG_NAME]:
170     return name
171    
172     return None
173    
174     def order_solver(self):
175     """ Once the database has been populated, try to solve the order """
176    
177     order_pkg = []
178     order_filename = []
179    
180     self.solver_db.ts.order()
181     while 1:
182     try:
183     order_pkg.append(self.solver_db.ts.next())
184     except:
185     break
186    
187     #order_pkg = self.solver_db.ts()
188    
189     for pkg in order_pkg:
190     order_filename.append(self._get_filename_from_hdr(pkg))
191    
192     return order_filename
193    
194     class db:
195     def __init__(self, rpm_dir=".", recurse=1, ext="*.rpm"):
196     self.rpm_dir = rpm_dir
197     self.recurse = recurse
198     self.rpmdb = {}
199     self.ext = ext
200     self.tmp_dir = tempfile.mkdtemp()
201     self.ts = rpm.TransactionSet(self.tmp_dir)
202     self.ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
203    
204     def close(self):
205     self.ts.closeDB()
206     for root, dirs, files in os.walk(self.tmp_dir, topdown=False):
207     for name in files:
208     os.remove(os.path.join(root, name))
209     for name in dirs:
210     os.rmdir(os.path.join(root, name))
211    
212     def get_rpmdb_size(self):
213     return len(self.rpmdb)
214    
215     def populate_db(self, verbose, pb, pop_trans=1, progress=0):
216     """ Populate our own DB :-)"""
217    
218     self.rpm_filenames = self._list_files()
219    
220     i = 0.0
221     if progress:
222     pb.set("Populate RPMDB :")
223    
224     for filename in self.rpm_filenames:
225     if verbose: print "rpm_solver.db.populate_db : Adding " + str(filename)
226     if progress:
227     i = i + 1.0
228     percent = i / len(self.rpm_filenames)
229     pb.progress(percent)
230    
231     self.add(filename, pop_trans)
232    
233     if progress:
234     pb.clear()
235    
236     if verbose > 1:
237     print ("->The contents of this transaction set for %s:" % self.rpm_dir)
238     for tx in self.ts:
239     print tx
240    
241     def add(self, filename, pop_trans=1):
242     if filename.startswith("http://"):
243     # XXX FIXME:
244     # Okay, I give up about doing this nicely. Screw it.
245     tmpfile = tempfile.mktemp()
246     problem = 0
247     while 1:
248     output = commands.getoutput("wget -O %s %s" % (tmpfile, filename))
249     try:
250     fdno = os.open(tmpfile, os.O_RDONLY)
251     os.close(fdno)
252     break
253     except:
254     if problem > 10:
255     print "FATAL ERROR!"
256     print "Could not download " + filename
257     sys.exit(2)
258     else:
259     problem = problem + 1
260     localfile = tmpfile
261     else:
262     localfile = filename
263    
264     try:
265     fname = filename.split("/")[-1]
266     except:
267     fname = filename
268    
269     fdno = os.open(localfile, os.O_RDONLY)
270     hdr = self.ts.hdrFromFdno(fdno)
271     os.close(fdno)
272    
273     self.rpmdb[fname] = hdr
274     if pop_trans:
275     self.ts.addInstall(hdr,None)
276    
277     if filename.startswith("http://"):
278     os.unlink(tmpfile)
279    
280     def _list_files(self):
281     """List all the files in a directory"""
282    
283     root = self.rpm_dir
284     patterns = self.ext
285     recurse = self.recurse
286     return_folders = 0
287    
288     if root.startswith("http://"):
289     output = commands.getoutput("links -dump %s | grep \"http://\" | grep \".rpm\" | awk '{print $2}'" % root)
290     results = output.split("\n")
291     return results
292     else:
293     # Expand patterns from semicolon-separated string to list
294     pattern_list = patterns.split(';')
295    
296     class Bunch:
297     def __init__(self, **kwds): self.__dict__.update(kwds)
298     arg = Bunch(recurse=recurse, pattern_list=pattern_list, return_folders=return_folders, results=[])
299    
300     def visit(arg, dirname, files):
301     # Append to arg.results all relevant files
302     for name in files:
303     fullname = os.path.normpath(os.path.join(dirname, name))
304     fullname = fullname.rstrip()
305     if arg.return_folders or os.path.isfile(fullname):
306     for pattern in arg.pattern_list:
307     if fnmatch.fnmatch(name, pattern):
308     arg.results.append(fullname)
309     break
310     # Block recursion if disallowed
311     if not arg.recurse: files[:]=[]
312    
313     os.path.walk(root, visit, arg)
314     return arg.results
315    
316     def process(rpm_dir, solve_dir, check_only, recursive, progress, verbose):
317     """ Main process if ran from command line """
318    
319     solver = rpm_solver(progress, verbose)
320     solver.init_db(rpm_dir, solve_dir, recursive)
321     needed, problems = solver.dep_closure()
322    
323     if len(needed):
324     print "Error! The following packages are needed for dependency closure:\n"
325     for pkg in needed:
326     print "\t" + str(pkg)
327     if len(problems):
328     print "Error! The following problems were encountered:\n"
329     for pkg in problems:
330     print "\t" + str(pkg)
331    
332     if len(problems) or len(needed):
333     sys.exit(2)
334     elif check_only:
335     print ("The RPMs in %s have dependency closure" % rpm_dir)
336     else:
337     # Okay we do stuff
338     ordered = solver.order_solver()
339     i = 0
340     for name in ordered:
341     print ("%d:%s" % (i, name))
342     i = i + 1
343    
344     def usage():
345     print "rpm-solver.py -"
346     print " Given a directory of RPMs, attempt to order their"
347     print "installation or determine if they have dependency closure."
348     print "\nUSAGE:"
349     print " rpm-solver.py [options] <RPM_DIR>"
350     print "\nWhere [options] may be one of the following:"
351     print "\t-c | --check\tCheck for dependency closure only"
352     print "\t-s | --solve\tUse the pool of rpms specified for solving"
353     print "\t-v | --verbose\tBe verbose in processing"
354     print "\t-p | --progress\tUse progress bar"
355     print "\t-r | --recursive\tScan RPM_DIR recursively"
356     print "\n\n"
357    
358    
359     def main():
360     try:
361     opts, args = getopt.getopt(sys.argv[1:], "vprs:c", ["verbose", "progress", "recursive", "solve=", "check"])
362     except getopt.GetoptError:
363     # print help information and exit:
364     usage()
365     sys.exit(2)
366    
367     verbose = 0
368     progress = 0
369     recursive = 0
370     solve_dir = None
371     check_only = 0
372    
373     if len(sys.argv) < 2:
374     usage()
375     sys.exit(2)
376    
377     rpm_dir = sys.argv[-1]
378    
379     for o, a in opts:
380     if o in ("-v", "--verbose"):
381     verbose = verbose + 1
382    
383     if o in ("-p", "--progress"):
384     progress = 1
385    
386     if o in ("-r", "--recursive"):
387     recursive = 1
388    
389     if o in ("-s", "--solve"):
390     solve_dir = a
391    
392     if o in ("-c", "--check"):
393     check_only = 1
394    
395     if verbose > 1: print "WARNING: Excessive debugging"
396    
397     process(rpm_dir, solve_dir, check_only, recursive, progress, verbose)
398    
399     if __name__ == "__main__":
400     main()
401    
402     # vim:set ai et sts=4 sw=4 tw=80:

admin@koozali.org
ViewVC Help
Powered by ViewVC 1.2.1 RSS 2.0 feed