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

Contents of /builds_bin/rpm-solver.py

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


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

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