]> git.ipfire.org Git - pakfire.git/blame - pakfire/repository/index.py
Add support for prerequires.
[pakfire.git] / pakfire / repository / index.py
CommitLineData
47a4cb89
MT
1#!/usr/bin/python
2
3import logging
4import os
5
fa6d335b 6import database
3cf7127f 7import metadata
47a4cb89 8
c1fbb0b7 9import pakfire.compress as compress
8656150e 10import pakfire.downloader as downloader
a2d1644c 11import pakfire.packages as packages
89fac8cf 12import pakfire.satsolver as satsolver
a2d1644c
MT
13import pakfire.util as util
14
15from pakfire.constants import *
16from pakfire.i18n import _
ad963cd6 17
47a4cb89 18class Index(object):
3723913b 19 def __init__(self, pakfire, repo):
47a4cb89 20 self.pakfire = pakfire
d4c94aa5 21
c605d735
MT
22 # Create reference to repository and the solver repo.
23 self.repo = repo
24 self.solver_repo = repo.solver_repo
d4c94aa5 25
c605d735 26 self.init()
b6da0663 27
c605d735
MT
28 # Check, if initialization was okay.
29 self.check()
b6da0663 30
c605d735
MT
31 def __repr__(self):
32 return "<%s %s>" % (self.__class__.__name__, self.repo)
a2d1644c 33
c605d735
MT
34 def __len(self):
35 return len(self.repo)
47a4cb89 36
ae20b05f 37 @property
c605d735
MT
38 def cache(self):
39 return self.repo.cache
ae20b05f 40
c605d735 41 def init(self):
a2d1644c 42 pass
47a4cb89 43
c605d735
MT
44 def check(self):
45 """
46 Check if everything was correctly initialized.
47 """
66af936c
MT
48 raise NotImplementedError
49
47a4cb89 50 def update(self, force=False):
a2d1644c
MT
51 raise NotImplementedError
52
c605d735 53 def read(self, filename):
a2d1644c 54 """
c605d735 55 Read file in SOLV format from filename.
a2d1644c 56 """
c605d735 57 self.solver_repo.read(filename)
2568a6d1 58
c605d735
MT
59 def write(self, filename):
60 """
61 Write content to filename in SOLV format.
62 """
63 self.solver_repo.write(filename)
2568a6d1 64
018127aa
MT
65 def create_relation(self, *args, **kwargs):
66 return self.pakfire.create_relation(*args, **kwargs)
a2d1644c 67
c605d735
MT
68 def add_package(self, pkg):
69 # XXX Skip packages without a UUID
70 #if not pkg.uuid:
71 # logging.warning("Skipping package which lacks UUID: %s" % pkg)
72 # return
73 if not pkg.build_time:
74 return
a2d1644c 75
c605d735
MT
76 logging.debug("Adding package to index %s: %s" % (self, pkg))
77
78 solvable = satsolver.Solvable(self.solver_repo, pkg.name,
79 pkg.friendly_version, pkg.arch)
80
81 # Save metadata.
714392de
MT
82 if pkg.vendor:
83 solvable.set_vendor(pkg.vendor)
84
85 hash1 = pkg.hash1
86 assert hash1
87 solvable.set_hash1(hash1)
88
89 assert pkg.uuid
c605d735 90 solvable.set_uuid(pkg.uuid)
714392de
MT
91
92 if pkg.maintainer:
93 solvable.set_maintainer(pkg.maintainer)
94
95 if pkg.groups:
96 solvable.set_groups(" ".join(pkg.groups))
c605d735
MT
97
98 # Save upstream information (summary, description, license, url).
714392de
MT
99 if pkg.summary:
100 solvable.set_summary(pkg.summary)
101
102 if pkg.description:
103 solvable.set_description(pkg.description)
104
105 if pkg.license:
106 solvable.set_license(pkg.license)
107
108 if pkg.url:
109 solvable.set_url(pkg.url)
c605d735
MT
110
111 # Save build information.
714392de
MT
112 if pkg.build_host:
113 solvable.set_buildhost(pkg.build_host)
114
115 if pkg.build_time:
116 solvable.set_buildtime(pkg.build_time)
c605d735
MT
117
118 # Save filename.
119 filename = os.path.basename(pkg.filename)
714392de 120 assert filename
c605d735 121 solvable.set_filename(filename)
714392de 122
c605d735
MT
123 solvable.set_downloadsize(pkg.size)
124 solvable.set_installsize(pkg.inst_size)
125
126 # Import all requires.
71d3b468
MT
127 requires = pkg.requires
128 prerequires = pkg.prerequires
129 if prerequires:
130 requires.append("solvable:prereqmarker")
131 requires += prerequires
132
c605d735
MT
133 for req in pkg.requires:
134 rel = self.create_relation(req)
135 solvable.add_requires(rel)
136
137 # Import all provides.
138 for prov in pkg.provides:
139 rel = self.create_relation(prov)
140 solvable.add_provides(rel)
141
142 # Import all conflicts.
143 for conf in pkg.conflicts:
144 rel = self.create_relation(conf)
145 solvable.add_conflicts(rel)
146
147 # Import all obsoletes.
148 for obso in pkg.obsoletes:
149 rel = self.create_relation(obso)
150 solvable.add_obsoletes(rel)
151
152 # Import all files that are in the package.
153 rel = self.create_relation("solvable:filemarker")
154 solvable.add_provides(rel)
155 for file in pkg.filelist:
156 rel = self.create_relation(file)
157 solvable.add_provides(rel)
158
31267a64
MT
159 def clear(self):
160 """
161 Forget all packages from memory.
162 """
163 self.solver_repo.clear()
164
c605d735
MT
165
166class IndexSolv(Index):
167 def check(self):
168 pass # XXX to be done
a2d1644c 169
c605d735
MT
170 def update(self, force=False):
171 self._update_metadata(force)
172 self._update_database(force)
b6da0663 173
3cf7127f 174 def _update_metadata(self, force):
2568a6d1 175 filename = os.path.join(METADATA_DOWNLOAD_PATH, METADATA_DOWNLOAD_FILE)
3cf7127f
MT
176
177 # Marker if we need to do the download.
178 download = True
179
180 # Marker for the current metadata.
181 old_metadata = None
182
183 if not force:
184 # Check if file does exists and is not too old.
c605d735
MT
185 if self.cache.exists(filename):
186 age = self.cache.age(filename)
3cf7127f
MT
187 if age and age < TIME_10M:
188 download = False
189 logging.debug("Metadata is recent enough. I don't download it again.")
190
191 # Open old metadata for comparison.
192 old_metadata = metadata.Metadata(self.pakfire, self,
c605d735 193 self.cache.abspath(filename))
3cf7127f
MT
194
195 if download:
196 logging.debug("Going to (re-)download the repository metadata.")
197
198 # Initialize a grabber for download.
199 grabber = downloader.MetadataDownloader()
200 grabber = self.repo.mirrors.group(grabber)
201
202 data = grabber.urlread(filename, limit=METADATA_DOWNLOAD_LIMIT)
203
204 # Parse new metadata for comparison.
205 new_metadata = metadata.Metadata(self.pakfire, self, metadata=data)
206
207 if old_metadata and new_metadata < old_metadata:
208 logging.warning("The downloaded metadata was less recent than the current one. Trashing that.")
209
210 else:
211 # We explicitely rewrite the metadata if it is equal to have
212 # a new timestamp and do not download it over and over again.
c605d735 213 with self.cache.open(filename, "w") as o:
3cf7127f
MT
214 o.write(data)
215
216 # Parse the metadata that we just downloaded or load it from cache.
217 self.metadata = metadata.Metadata(self.pakfire, self,
c605d735 218 self.cache.abspath(filename))
3cf7127f
MT
219
220 def _update_database(self, force):
3cf7127f
MT
221 # Construct cache and download filename.
222 filename = os.path.join(METADATA_DOWNLOAD_PATH, self.metadata.database)
223
c605d735 224 if not self.cache.exists(filename):
3cf7127f
MT
225 # Initialize a grabber for download.
226 grabber = downloader.DatabaseDownloader(
227 text = _("%s: package database") % self.repo.name,
228 )
229 grabber = self.repo.mirrors.group(grabber)
230
231 data = grabber.urlread(filename)
232
c605d735 233 with self.cache.open(filename, "w") as o:
3cf7127f
MT
234 o.write(data)
235
93135ecb
MT
236 # decompress the database
237 if self.metadata.database_compression:
238 # Open input file and remove the file immediately.
239 # The fileobj is still open and the data will be removed
240 # when it is closed.
c605d735 241 compress.decompress(self.cache.abspath(filename),
c1fbb0b7 242 algo=self.metadata.database_compression)
3cf7127f 243
05e398fd 244 # check the hashsum of the downloaded file
c605d735 245 if not util.calc_hash1(self.cache.abspath(filename)) == self.metadata.database_hash1:
05e398fd
MT
246 # XXX an exception is not a very good idea because this file could
247 # be downloaded from another mirror. need a better way to handle this.
34102d90
MT
248
249 # Remove bad file from cache.
c605d735 250 self.cache.remove(filename)
34102d90 251
05e398fd
MT
252 raise Exception, "Downloaded file did not match the hashsum. Need to re-download it."
253
3cf7127f 254 # (Re-)open the database.
c605d735
MT
255 self.read(self.cache.abspath(filename))
256
257
258class IndexDir(Index):
8276111d
MT
259 def init(self):
260 self.pkg_type = None
261
262 if self.repo.type == "binary":
263 self.pkg_type = packages.BinaryPackage
264 elif self.repo.type == "source":
265 self.pkg_type = packages.SourcePackage
266
267 assert self.pkg_type
268
c605d735
MT
269 def check(self):
270 pass # XXX to be done
271
272 @property
273 def path(self):
274 path = self.repo.path
275
276 if path.startswith("file://"):
277 path = path[7:]
278
279 return path
3cf7127f
MT
280
281 def update(self, force=False):
c605d735 282 logging.debug("Updating repository index '%s' (force=%s)" % (self.path, force))
3cf7127f 283
c605d735
MT
284 # Do nothing if the update is not forced but populate the database
285 # if no packages are present.
286 if not force and len(self.repo):
2568a6d1
MT
287 return
288
c605d735
MT
289 # Collect all packages from default path.
290 self.collect_packages(self.path)
3cf7127f 291
c605d735 292 def collect_packages(self, path):
898278a2
MT
293 logging.debug("Collecting all packages from %s" % path)
294 pkgs = []
295
296 # Get a filelist of all files that could possibly be packages.
297 files = []
298 for dir, subdirs, _files in os.walk(path):
299 for file in sorted(_files):
c605d735
MT
300 # Skip files that do not have the right extension
301 if not file.endswith(".%s" % PACKAGE_EXTENSION):
302 continue
3cf7127f 303
898278a2
MT
304 file = os.path.join(dir, file)
305 files.append(file)
306
307 if not files:
308 return pkgs
309
310 # Create progress bar.
311 pb = util.make_progress(_("Loading from %s") % path, len(files))
312 i = 0
313
314 for file in files:
315 if pb:
316 i += 1
317 pb.update(i)
318
319 package = packages.open(self.pakfire, self.repo, file)
d4c94aa5 320
8276111d
MT
321 # Find all packages with the given type and skip those of
322 # the other type.
323 if isinstance(package, self.pkg_type):
324 # Check for binary packages if the architecture matches.
325 if isinstance(package, packages.BinaryPackage) and \
326 not package.arch in (self.repo.arch, "noarch"):
c605d735
MT
327 logging.warning("Skipped package with wrong architecture: %s (%s)" \
328 % (package.filename, package.arch))
c605d735
MT
329 continue
330
331 # Skip all source packages.
8276111d 332 else:
c605d735
MT
333 continue
334
335 self.add_package(package)
898278a2
MT
336 pkgs.append(package)
337
338 if pb:
339 pb.finish()
c605d735 340
898278a2 341 return pkgs
c605d735
MT
342
343
344class IndexLocal(Index):
345 def init(self):
346 self.db = database.DatabaseLocal(self.pakfire, self.repo)
347
348 def check(self):
349 # XXX Create the database and lock it or something.
350 pass
351
352 def update(self, force=True):
353 if self.solver_repo.size() == 0:
354 force = True
355
356 if force:
357 package_count = len(self.db)
358
359 # Nothing to do here, if there are no packages in the database.
360 if not package_count:
361 return
362
363 # Add all packages from the database to the index.
364 pb = util.make_progress(_("Loading installed packages"), package_count)
365
366 i = 0
367 for pkg in self.db.packages:
368 if pb:
369 i += 1
370 pb.update(i)
371
714392de 372 self.add_package(pkg)
c605d735
MT
373
374 if pb:
375 pb.finish()