]> git.ipfire.org Git - pakfire.git/blame - src/pakfire/packages/file.py
Use autotools.
[pakfire.git] / src / pakfire / packages / file.py
CommitLineData
9e8b1d7a 1#!/usr/bin/python
b792d887
MT
2###############################################################################
3# #
4# Pakfire - The IPFire package management system #
5# Copyright (C) 2011 Pakfire development team #
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
9e8b1d7a 21
c2808056 22import hashlib
9e8b1d7a
MT
23import os
24import re
c2808056 25import shutil
114ac7ee 26import tarfile
4496b160 27import tempfile
9e8b1d7a 28
8b6bc023
MT
29import logging
30log = logging.getLogger("pakfire")
31
862bea4d 32import pakfire.filelist
97d7d682 33import pakfire.lzma as lzma
4496b160
MT
34import pakfire.util as util
35import pakfire.compress as compress
4496b160 36from pakfire.constants import *
75bb74a7 37from pakfire.i18n import _
9e8b1d7a 38
9db18669
MT
39import base
40import lexer
41import make
cd808413 42import tar
97d7d682 43
9db18669 44class FilePackage(base.Package):
9e8b1d7a
MT
45 """
46 This class is a wrapper that reads package data from the (outer)
47 tarball and should never be used solely.
48 """
9accbba5
MT
49 _type = None
50
3723913b 51 def __init__(self, pakfire, repo, filename):
9db18669 52 base.Package.__init__(self, pakfire, repo)
440cede8 53 self.filename = os.path.abspath(filename)
9e8b1d7a 54
36084c79 55 # Place to cache the metadata
9e8b1d7a
MT
56 self._metadata = {}
57
97d7d682 58 # Place to cache the filelist and payload compression algorithm.
862bea4d 59 self._filelist = None
97d7d682 60 self.__payload_compression = None
862bea4d 61
c07a3ca7
MT
62 # Store the format of this package file.
63 self.format = self.get_format()
64
65 # XXX need to make this much better.
9e8b1d7a
MT
66 self.check()
67
c07a3ca7
MT
68 # Read the info file.
69 if self.format >= 1:
70 a = self.open_archive()
71 f = a.extractfile("info")
72
9db18669 73 self.lexer = lexer.FileLexer(f.readlines())
c07a3ca7
MT
74
75 f.close()
76 a.close()
77
78 elif self.format == 0:
79 pass
80
81 else:
82 raise PackageFormatUnsupportedError, _("Filename: %s") % self.filename
83
9e8b1d7a
MT
84 def check(self):
85 """
86 Initially check if the given file is of the correct type and
87 can be opened.
88 """
89 if not tarfile.is_tarfile(self.filename):
90 raise FileError, "Given file is not of correct format: %s" % self.filename
91
cabf1fbe 92 assert self.format in PACKAGE_FORMATS_SUPPORTED, self.format
c07a3ca7
MT
93
94 def get_format(self):
95 a = self.open_archive()
96 try:
97 f = a.extractfile("pakfire-format")
98 except KeyError:
99 return 0
100
101 format = f.read()
102 try:
103 format = int(format)
104 except TypeError:
105 format = 0
106
107 f.close()
108 a.close()
109
110 return format
111
9e8b1d7a
MT
112 def __repr__(self):
113 return "<%s %s>" % (self.__class__.__name__, self.filename)
114
edd6a268
MT
115 @property
116 def local(self):
117 # A file package is always local.
118 return True
119
68c0e769
MT
120 def open_archive(self, mode="r"):
121 return tarfile.open(self.filename, mode=mode, format=tarfile.PAX_FORMAT)
9e8b1d7a 122
5b2a7a44
MT
123 def open_payload_archive(self):
124 a = self.open_archive()
4496b160 125
5b2a7a44
MT
126 # Find the payload data.
127 payload = a.extractfile("data.img")
4496b160
MT
128
129 # Decompress the payload if needed.
97d7d682 130 if self.payload_compression == "xz":
cd808413 131 payload_archive = tar.InnerTarFileXz.open(fileobj=payload)
4496b160 132
97d7d682 133 elif self.payload_compression == "none":
cd808413 134 payload_archive = tar.InnerTarFile.open(fileobj=payload)
4496b160
MT
135
136 else:
c844646a
MT
137 raise Exception, "Unhandled payload compression type: %s" % \
138 self.payload_compression
5b2a7a44
MT
139
140 return payload_archive
141
142 def extract(self, message=None, prefix=None):
143 log.debug("Extracting package %s" % self.friendly_name)
144
145 if prefix is None:
146 prefix = ""
147
148 # Open package data for read.
149 payload_archive = self.open_payload_archive()
4496b160 150
4496b160
MT
151 # Load progressbar.
152 pb = None
153 if message:
1e80d5d7 154 message = "%-10s : %s" % (message, self.friendly_name)
c2808056 155 pb = util.make_progress(message, len(self.filelist), eta=False)
4496b160 156
414f4a0b
MT
157 # Collect messages with errors and warnings, that are passed to
158 # the user.
159 messages = []
160
132bde17
MT
161 name2file = {}
162 for file in self.filelist:
efe141c7 163 if file.is_dir() and file.name.endswith("/"):
3ce6a8ad
MT
164 name = file.name[:-1]
165 else:
166 name = file.name
c2808056 167
132bde17 168 name2file[name] = file
c2808056 169
132bde17 170 i = 0
97d7d682
MT
171 while True:
172 member = payload_archive.next()
173 if not member:
174 break
175
3ce6a8ad
MT
176 # Check if file is also known in metadata.
177 name = member.name
178 if not name.startswith("/"):
179 name = "/%s" % name
180
181 try:
182 file = name2file[name]
183 except KeyError:
184 log.warning(_("File in archive is missing in file metadata: %s. Skipping.") % name)
c2808056
MT
185 continue
186
132bde17
MT
187 # Update progress.
188 if pb:
189 i += 1
190 pb.update(i)
191
4496b160
MT
192 target = os.path.join(prefix, member.name)
193
c2808056
MT
194 # Check if a configuration file is already present. We don't want to
195 # overwrite that.
196 if file.is_config():
197 config_save = "%s%s" % (target, CONFIG_FILE_SUFFIX_SAVE)
198 config_new = "%s%s" % (target, CONFIG_FILE_SUFFIX_NEW)
199
200 if os.path.exists(config_save) and not os.path.exists(target):
201 # Extract new configuration file, save it as CONFIG_FILE_SUFFIX_NEW,
202 # and reuse _SAVE.
203 payload_archive.extract(member, path=prefix)
204
205 shutil.move(target, config_new)
206 shutil.move(config_save, target)
207 continue
208
209 elif os.path.exists(target):
210 # If the files are identical, we skip the extraction of a
211 # new configuration file. We also do that when the new configuration file
212 # is a dummy file.
213 if file.size == 0:
214 continue
215
216 # Calc hash of the current configuration file.
e3bcfd23 217 config_hash1 = hashlib.new("sha512")
c2808056
MT
218 f = open(target)
219 while True:
220 buf = f.read(BUFFER_SIZE)
221 if not buf:
222 break
223 config_hash1.update(buf)
224 f.close()
225
226 if file.hash1 == config_hash1.hexdigest():
227 continue
228
229 # Backup old configuration file and extract new one.
230 shutil.move(target, config_save)
231 payload_archive.extract(member, path=prefix)
232
233 # Save new configuration file as CONFIG_FILE_SUFFIX_NEW and
234 # restore old configuration file.
235 shutil.move(target, config_new)
236 shutil.move(config_save, target)
237
238 if prefix:
239 config_new = os.path.relpath(config_new, prefix)
240 messages.append(_("Config file created as %s") % config_new)
241 continue
242
3c5a85f3
MT
243 # Don't overwrite target files if they already exist.
244 if file.is_datafile() and os.path.exists(target):
245 log.debug(_("Don't overwrite already existing datafile '/%s'") % member.name)
246 continue
247
4496b160
MT
248 # If the member is a directory and if it already exists, we
249 # don't need to create it again.
4496b160
MT
250 if os.path.exists(target):
251 if member.isdir():
252 continue
253
254 else:
255 # Remove file if it has been existant
414f4a0b
MT
256 try:
257 os.unlink(target)
258 except OSError:
259 messages.append(_("Could not remove file: /%s") % member.name)
4496b160
MT
260
261 #if self.pakfire.config.get("debug"):
262 # msg = "Creating file (%s:%03d:%03d) " % \
263 # (tarfile.filemode(member.mode), member.uid, member.gid)
264 # if member.issym():
265 # msg += "/%s -> %s" % (member.name, member.linkname)
266 # elif member.islnk():
267 # msg += "/%s link to /%s" % (member.name, member.linkname)
268 # else:
269 # msg += "/%s" % member.name
8b6bc023 270 # log.debug(msg)
4496b160
MT
271
272 payload_archive.extract(member, path=prefix)
273
274 # Close all open files.
275 payload_archive.close()
4496b160 276
4496b160
MT
277 if pb:
278 pb.finish()
279
414f4a0b
MT
280 # Print messages.
281 for msg in messages:
8b6bc023 282 log.warning(msg)
414f4a0b 283
5b2a7a44
MT
284 def open_file(self, filename):
285 payload_archive = self.open_payload_archive()
286
287 # Search for filename.
288 while True:
289 member = payload_archive.next()
290 if not member:
291 break
292
293 # Skip non-matching files.
294 if not filename in (member.name, "/%s" % member.name):
295 continue
296
297 return payload_archive.extractfile(member)
298
9db18669
MT
299 def open_makefile(self):
300 """
301 Opens the makefile inside the package.
302 """
303 f = self.open_file("%s.%s" % (self.name, MAKEFILE_EXTENSION))
304 if not f:
305 return
306
307 return make.Makefile(self.pakfire, lines=f.readlines())
308
9e8b1d7a
MT
309 @property
310 def metadata(self):
311 """
312 Read-in the metadata from the "info" file and cache it in _metadata.
313 """
c07a3ca7
MT
314 assert self.format == 0, self
315
9e8b1d7a 316 if not self._metadata:
36084c79
MT
317 a = self.open_archive()
318 f = a.extractfile("info")
9e8b1d7a
MT
319
320 for line in f.readlines():
321 m = re.match(r"^(\w+)=(.*)$", line)
322 if m is None:
323 continue
324
325 key, val = m.groups()
326 self._metadata[key] = val.strip("\"")
327
328 f.close()
36084c79 329 a.close()
9e8b1d7a
MT
330
331 return self._metadata
332
333 @property
334 def size(self):
335 """
336 Return the size of the package file.
337 """
338 return os.path.getsize(self.filename)
339
0304200a
MT
340 @property
341 def inst_size(self):
342 inst_size = 0
343
344 if self.format >= 1:
345 inst_size = self.lexer.package.get_var("size")
346 try:
347 inst_size = int(inst_size)
348 except TypeError:
349 inst_size = 0
350
351 return inst_size
352
3c5a85f3
MT
353 def read_plain_filelist(self, filename):
354 a = self.open_archive()
355 files = []
356
357 try:
358 f = a.extractfile(filename)
359 for line in f.readlines():
360 # Strip newline at end of line.
361 file = line.rstrip()
362
363 # Add a leading / is not present.
364 if not file.startswith("/"):
365 file = "/%s" % file
366
367 files.append(file)
368 f.close()
369
370 # Ignore if 'filename' does not exist.
371 except KeyError:
372 pass
373
374 finally:
375 a.close()
376
377 return files
378
862bea4d 379 def get_filelist(self):
3e4a9b06
MT
380 """
381 Return a list of the files that are contained in the package
382 payload.
383 """
862bea4d
MT
384 ret = []
385
36084c79 386 a = self.open_archive()
9e8b1d7a 387
c2808056 388 # Cache configfiles.
3c5a85f3
MT
389 if self.format >= 5:
390 filename = "configfiles"
391 else:
392 filename = "configs"
393 configfiles = self.read_plain_filelist(filename)
3e9ef68a 394
3c5a85f3
MT
395 # Cache datafiles.
396 datafiles = self.read_plain_filelist("datafiles")
c2808056
MT
397
398 f = a.extractfile("filelist")
18ae128e
MT
399 for line in f.readlines():
400 line = line.strip()
3e4a9b06 401
862bea4d
MT
402 file = pakfire.filelist.File(self.pakfire)
403
3e4a9b06 404 if self.format >= 1:
efe141c7 405 line = line.split(None, 8)
3e9ef68a 406
cabf1fbe
MT
407 # Check if fields do have the correct length.
408 if self.format >= 3 and len(line) <= 7:
409 continue
410 elif len(line) <= 6:
3e9ef68a
MT
411 continue
412
efe141c7
MT
413 # Switch the first and last argument in the line.
414 if self.format < 4:
415 line.append(line.pop(0))
416
417 name = line[-1]
3e4a9b06 418
c2808056
MT
419 if not name.startswith("/"):
420 name = "/%s" % name
421
422 # Check if configfiles.
423 if name in configfiles:
424 file.config = True
425
3c5a85f3
MT
426 # Check if this is a datafile.
427 if name in datafiles:
428 file.datafile = True
429
c2808056
MT
430 # Parse file type.
431 try:
efe141c7 432 file.type = int(line[0])
c2808056
MT
433 except ValueError:
434 file.type = 0
435
8fe16710
MT
436 # Parse the size information.
437 try:
efe141c7 438 file.size = int(line[1])
8fe16710 439 except ValueError:
8fe16710
MT
440 file.size = 0
441
c2808056 442 # Parse user and group.
efe141c7 443 file.user, file.group = line[2], line[3]
c2808056
MT
444
445 # Parse mode.
446 try:
efe141c7 447 file.mode = int(line[4])
c2808056
MT
448 except ValueError:
449 file.mode = 0
450
451 # Parse time.
452 try:
efe141c7 453 file.mtime = line[5]
c2808056
MT
454 except ValueError:
455 file.mtime = 0
456
457 # Parse hash1 (sha512).
efe141c7
MT
458 if not line[6] == "-":
459 file.hash1 = line[6]
18ae128e 460
efe141c7
MT
461 if self.format >= 3 and len(line) >= 9 and not line[7] == "-":
462 file.capabilities = line[7]
cabf1fbe 463
862bea4d
MT
464 else:
465 name = line
466
c2808056
MT
467 if not name.startswith("/"):
468 name = "/%s" % name
862bea4d
MT
469
470 file.name = name
471 file.pkg = self
472
473 ret.append(file)
9e8b1d7a
MT
474
475 f.close()
36084c79 476 a.close()
9e8b1d7a
MT
477
478 return ret
479
862bea4d
MT
480 @property
481 def filelist(self):
482 if self._filelist is None:
483 self._filelist = self.get_filelist()
484
485 return self._filelist
486
6ee3d6b9
MT
487 @property
488 def configfiles(self):
c2808056 489 return [f for f in self.filelist if f.is_config()]
6ee3d6b9 490
3c5a85f3
MT
491 @property
492 def datafiles(self):
493 return [f for f in self.filelist if f.is_datafile()]
494
9e8b1d7a
MT
495 @property
496 def payload_compression(self):
497 """
c07a3ca7 498 Return the (guessed) compression type of the payload.
9e8b1d7a 499 """
97d7d682
MT
500 # We cache that because this is costly.
501 if self.__payload_compression is None:
502 a = self.open_archive()
503 f = a.extractfile("data.img")
c07a3ca7 504
97d7d682
MT
505 # Go and guess what we do have here.
506 self.__payload_compression = compress.guess_algo(fileobj=f)
c07a3ca7 507
97d7d682
MT
508 f.close()
509 a.close()
c07a3ca7 510
97d7d682 511 return self.__payload_compression or "none"
9e8b1d7a 512
68c0e769
MT
513 ### SIGNATURE STUFF
514
9e8b1d7a 515 @property
68c0e769 516 def signatures(self):
9e8b1d7a 517 """
68c0e769 518 Read the signatures from the archive.
9e8b1d7a 519 """
68c0e769
MT
520 ret = {}
521
522 # Open the archive for reading.
523 a = self.open_archive()
524
525 for member in a.getmembers():
526 # Skip all files that are not a signature.
527 if not member.name.startswith("signatures/"):
528 continue
529
530 # Get the ID of the key.
531 key_id = os.path.basename(member.name)
532
533 # Get the content of the signature file.
534 f = a.extractfile(member.name)
4fffe3c4 535 signature = f.read()
68c0e769
MT
536 f.close()
537
4fffe3c4
MT
538 if signature:
539 ret[key_id] = signature
540
68c0e769
MT
541 # Close the archive.
542 a.close()
543
544 return ret
545
546 def has_signature(self, key_id):
547 """
548 Check if the file a signature of the given key.
549 """
a9260890
MT
550 f = self.open_file("signatures/%s" % key_id)
551 if f:
552 f.close()
553
554 return True
555
556 return False
68c0e769
MT
557
558 def __has_hardlinks(self):
559 """
560 Returns True when a file has a hardlink.
561 """
562 res = os.stat(self.filename)
563
564 return res.st_nlink > 1
565
566 def __remove_hardlinks(self):
567 """
568 Remove all hardlinks from this file that we can alter it in place.
569 """
570 if not self.__has_hardlinks():
571 return
572
573 # Open a file descriptor to the old file and remove the link from
574 # the filesystem.
575 f = open(self.filename, "rb")
576 os.unlink(self.filename)
577
578 # Create a new file with the exact same name for copying the data
579 # to.
580 g = open(self.filename, "wb")
581
582 # Copy the data.
583 while True:
584 buf = f.read(BUFFER_SIZE)
585 if not buf:
586 break
587
588 g.write(buf)
589
590 # Close all files.
591 f.close()
592 g.close()
593
594 # Make sure the whole process above worked fine.
595 assert self.__has_hardlinks() is False
596
597 def sign(self, key_id):
598 """
599 Sign the package with the given key.
600 """
601 # First check if the package has already been signed with this key.
602 # If true, we do not have anything to do here.
603 if self.has_signature(key_id):
6e7af0f5 604 return False
68c0e769
MT
605
606 # Remove all hardlinks.
607 self.__remove_hardlinks()
608
609 # XXX verify the content of the file here.
610
611 # Open the archive and read the checksum file.
612 a = self.open_archive()
36084c79 613
68c0e769
MT
614 f = a.extractfile("chksums")
615 cleartext = f.read()
36084c79 616
68c0e769
MT
617 f.close()
618 a.close()
619
620 # Create the signature.
621 signature = self.pakfire.keyring.sign(key_id, cleartext)
622
68c0e769 623 try:
6e7af0f5
MT
624 # Write the signature to a temporary file.
625 f = tempfile.NamedTemporaryFile(mode="w", delete=False)
68c0e769 626 f.write(signature)
9e8b1d7a 627 f.close()
68c0e769
MT
628
629 # Reopen the outer tarfile in write mode and append
630 # the new signature.
631 a = self.open_archive("a")
6e7af0f5 632 a.add(f.name, "signatures/%s" % key_id)
36084c79 633 a.close()
9e8b1d7a 634
68c0e769 635 finally:
6e7af0f5
MT
636 os.unlink(f.name)
637
638 return True
68c0e769
MT
639
640 def verify(self):
641 """
642 Verify the tarball against the given key.
643
644 If not key is given, only the checksums are compared to
645 the actual data.
646 """
647
648 # XXX replace Exception
649
650 # Read the data of the checksum file.
651 a = self.open_archive()
652 f = a.extractfile("chksums")
653 chksums = f.read()
654 f.close()
655 a.close()
656
657 sigs = []
658 for signature in self.signatures.values():
659 sigs += self.pakfire.keyring.verify(signature, chksums)
660
661 # Open the archive to access all files we will need.
662 a = self.open_archive()
663
664 # Read the chksums file.
665 chksums = {}
666 f = a.extractfile("chksums")
667 for line in f.readlines():
668 filename, chksum = line.split()
669 chksums[filename] = chksum
670 f.close()
671 a.close()
672
673 for filename, chksum in chksums.items():
674 ret = self.check_chksum(filename, chksum)
675
676 if ret:
677 log.debug("Checksum of %s matches." % filename)
678 continue
679 else:
680 log.debug("Checksum of %s does not match." % filename)
681
682 raise Exception, "Checksum does not match: %s" % filename
683
684 return sigs
685
686 def check_chksum(self, filename, chksum, algo="sha512"):
687 a = self.open_archive()
688 f = a.extractfile(filename)
689
690 h = hashlib.new(algo)
691 while True:
692 buf = f.read(BUFFER_SIZE)
693 if not buf:
694 break
695
696 h.update(buf)
697
698 f.close()
699 a.close()
9e8b1d7a 700
68c0e769 701 return h.hexdigest() == chksum
9e8b1d7a 702
66af936c
MT
703 @property
704 def hash1(self):
705 """
706 Calculate the hash1 of this package.
707 """
708 return util.calc_hash1(self.filename)
1b59091e
MT
709
710 @property
711 def type(self):
712 if self.format >= 2:
cc843cfe 713 type = self.lexer.package.get_var("type")
1b59091e
MT
714 elif self.format == 1:
715 type = self._type
716 else:
717 type = self.metadata.get("type")
718
719 assert type, self
720 return type
721
c07a3ca7
MT
722 @property
723 def name(self):
724 if self.format >= 1:
725 name = self.lexer.package.get_var("name")
726 elif self.format == 0:
727 name = self.metadata.get("PKG_NAME")
728
729 assert name, self
730 return name
66af936c 731
a5f5fced 732 @property
c07a3ca7
MT
733 def epoch(self):
734 if self.format >= 1:
735 epoch = self.lexer.package.get_var("epoch", 0)
736 elif self.format == 0:
737 epoch = self.metadata.get("PKG_EPOCH")
738
a5f5fced 739 try:
c07a3ca7
MT
740 epoch = int(epoch)
741 except TypeError:
742 epoch = 0
36084c79 743
c07a3ca7 744 return epoch
36084c79 745
c07a3ca7
MT
746 @property
747 def version(self):
748 if self.format >= 1:
749 version = self.lexer.package.get_var("version")
750 elif self.format == 0:
751 version = self.metadata.get("PKG_VER")
a5f5fced 752
c07a3ca7
MT
753 assert version, self
754 return version
755
756 @property
757 def release(self):
758 if self.format >= 1:
759 release = self.lexer.package.get_var("release")
760 elif self.format == 0:
761 release = self.metadata.get("PKG_REL")
762
763 assert release, self
764 return release
765
766 @property
767 def arch(self):
768 if self.format >= 1:
769 arch = self.lexer.package.get_var("arch")
770 elif self.format == 0:
771 arch = self.metadata.get("PKG_ARCH")
772
773 assert arch, self
774 return arch
775
776 @property
777 def vendor(self):
778 if self.format >= 1:
779 vendor = self.lexer.distro.get_var("vendor")
780 elif self.format == 0:
781 vendor = self.metadata.get("PKG_VENDOR")
782
783 return vendor
784
785 @property
786 def summary(self):
787 if self.format >= 1:
788 summary = self.lexer.package.get_var("summary")
789 elif self.format == 0:
790 summary = self.metadata.get("PKG_SUMMARY")
791
792 assert summary, self
793 return summary
794
795 @property
796 def description(self):
797 if self.format >= 1:
798 description = self.lexer.package.get_var("description")
799 elif self.format == 0:
800 description = self.metadata.get("PKG_DESC")
801
802 return description
803
804 @property
805 def groups(self):
806 if self.format >= 1:
807 groups = self.lexer.package.get_var("groups")
808 elif self.format == 0:
809 groups = self.metadata.get("PKG_GROUPS")
810
811 if groups:
812 return groups.split()
813
814 return []
815
816 @property
817 def license(self):
818 if self.format >= 1:
819 license = self.lexer.package.get_var("license")
820 elif self.format == 0:
821 license = self.metadata.get("PKG_LICENSE")
822
823 return license
824
825 @property
826 def url(self):
827 if self.format >= 1:
828 url = self.lexer.package.get_var("url")
829 elif self.format == 0:
830 url = self.metadata.get("PKG_URL")
831
832 return url
833
834 @property
835 def maintainer(self):
836 if self.format >= 1:
837 maintainer = self.lexer.package.get_var("maintainer")
838 elif self.format == 0:
839 maintainer = self.metadata.get("PKG_MAINTAINER")
840
841 return maintainer
842
843 @property
844 def uuid(self):
845 if self.format >= 1:
846 uuid = self.lexer.package.get_var("uuid")
847 elif self.format == 0:
848 uuid = self.metadata.get("PKG_UUID")
849
43acbf3c 850 assert uuid, self
c07a3ca7
MT
851 return uuid
852
853 @property
854 def build_id(self):
855 if self.format >= 1:
856 build_id = self.lexer.build.get_var("id")
857 elif self.format == 0:
858 build_id = self.metadata.get("BUILD_ID")
859
860 assert build_id, self
861 return build_id
862
863 @property
864 def build_host(self):
865 if self.format >= 1:
866 build_host = self.lexer.build.get_var("host")
867 elif self.format == 0:
868 build_host = self.metadata.get("BUILD_HOST")
869
870 assert build_host, self
871 return build_host
872
873 @property
874 def build_time(self):
875 if self.format >= 1:
876 build_time = self.lexer.build.get_var("time")
877 elif self.format == 0:
878 build_time = self.metadata.get("BUILD_TIME")
879
880 # XXX re-enable this later
881 #assert build_time, self
882
883 try:
884 build_time = int(build_time)
885 except TypeError:
886 build_time = 0
887
888 return build_time
889
890 @property
891 def provides(self):
892 if self.format >= 1:
893 provides = self.lexer.deps.get_var("provides")
894 elif self.format == 0:
895 provides = self.metadata.get("PKG_PROVIDES")
896
897 if not provides:
898 return []
899
e491c081
MT
900 provides = provides.splitlines()
901 return self.filter_deps(provides)
c07a3ca7
MT
902
903 @property
904 def requires(self):
905 if self.format >= 1:
906 requires = self.lexer.deps.get_var("requires")
907 elif self.format == 0:
908 requires = self.metadata.get("PKG_REQUIRES")
909
910 if not requires:
911 return []
912
e491c081
MT
913 requires = requires.splitlines()
914 return self.filter_deps(requires)
c07a3ca7
MT
915
916 @property
917 def prerequires(self):
918 if self.format >= 1:
919 prerequires = self.lexer.deps.get_var("prerequires")
920 elif self.format == 0:
921 prerequires = self.metadata.get("PKG_PREREQUIRES")
922
923 if not prerequires:
924 return []
925
e491c081
MT
926 prerequires = prerequires.splitlines()
927 return self.filter_deps(prerequires)
c07a3ca7
MT
928
929 @property
930 def obsoletes(self):
931 if self.format >= 1:
932 obsoletes = self.lexer.deps.get_var("obsoletes")
933 elif self.format == 0:
934 obsoletes = self.metadata.get("PKG_OBSOLETES")
935
936 if not obsoletes:
937 return []
938
e491c081
MT
939 obsoletes = obsoletes.splitlines()
940 return self.filter_deps(obsoletes)
c07a3ca7
MT
941
942 @property
943 def conflicts(self):
944 if self.format >= 1:
945 conflicts = self.lexer.deps.get_var("conflicts")
946 elif self.format == 0:
947 conflicts = self.metadata.get("PKG_CONFLICTS")
948
949 if not conflicts:
950 return []
a5f5fced 951
e491c081
MT
952 conflicts = conflicts.splitlines()
953 return self.filter_deps(conflicts)
e0636b31 954
a60f0f7d
MT
955 @property
956 def recommends(self):
a8f5d29e
MT
957 if self.format < 4:
958 return []
959
a60f0f7d
MT
960 recommends = self.lexer.deps.get_var("recommends")
961
962 if not recommends:
963 return []
964
965 recommends = recommends.splitlines()
966 return self.filter_deps(recommends)
967
968 @property
969 def suggests(self):
a8f5d29e
MT
970 if self.format < 4:
971 return []
972
a60f0f7d
MT
973 suggests = self.lexer.deps.get_var("suggests")
974
975 if not suggests:
976 return []
977
978 suggests = suggests.splitlines()
979 return self.filter_deps(suggests)
980
e0636b31
MT
981
982class SourcePackage(FilePackage):
1b59091e
MT
983 _type = "source"
984
985 @property
986 def arch(self):
987 return "src"
988
989 @property
990 def supported_arches(self):
991 if self.format >= 2:
992 arches = self.lexer.package.get_var("arch", "all")
993 elif self.format == 1:
994 # Format 1 did not support "supported_arches", so we assume "all".
995 arches = "all"
996 else:
997 arches = self.metadata.get("PKG_SUPPORTED_ARCHES", "all")
998
999 assert arches, self
1000 return arches
e0636b31 1001
9db18669
MT
1002 @property
1003 def requires(self):
1004 m = self.open_makefile()
1005 if not m:
1006 return []
1007
1008 requires = m.lexer.build.get_var("requires")
1009 requires = requires.splitlines()
1010
1011 return self.filter_deps(requires)
1012
e0636b31
MT
1013
1014class BinaryPackage(FilePackage):
1b59091e
MT
1015 _type = "binary"
1016
e0636b31
MT
1017 def get_scriptlet(self, type):
1018 a = self.open_archive()
1019
1020 # Path of the scriptlet in the tarball.
1021 path = "scriptlets/%s" % type
1022
1023 try:
1024 f = a.extractfile(path)
1025 except KeyError:
1026 # If the scriptlet is not available, we just return.
1027 return
1028
1029 scriptlet = f.read()
1030
1031 f.close()
1032 a.close()
1033
1034 return scriptlet