]>
git.ipfire.org Git - pakfire.git/blob - python/pakfire/packages/base.py
2 ###############################################################################
4 # Pakfire - The IPFire package management system #
5 # Copyright (C) 2011 Pakfire development team #
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. #
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. #
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/>. #
20 ###############################################################################
25 import xml
.sax
.saxutils
27 import pakfire
.util
as util
28 from pakfire
.i18n
import _
30 class Package(object):
31 def __init__(self
, pakfire
, repo
=None):
32 self
.pakfire
= pakfire
35 # Pointer to a package that is updated by this one.
36 self
.old_package
= None
39 return "<%s %s>" % (self
.__class
__.__name
__, self
.friendly_name
)
41 def __cmp__(self
, other
):
42 # if packages differ names return in alphabetical order
43 if not self
.name
== other
.name
:
44 return cmp(self
.name
, other
.name
)
46 ret
= util
.version_compare(self
.pakfire
.pool
,
47 self
.friendly_version
, other
.friendly_version
)
49 # XXX this is to move packages that have been built a while ago and
50 # do not have all the meta information that they won't be evaluated
53 if "X"*3 in (self
.build_id
, other
.build_id
):
54 if self
.build_id
== "X"*3 and not other
.build_id
== "X"*3:
57 elif not self
.build_id
== "X"*3 and other
.build_id
== "X"*3:
61 # Compare the build times if we have a rebuilt package.
62 if not ret
and self
.build_time
and other
.build_time
:
63 ret
= cmp(self
.build_time
, other
.build_time
)
66 # logging.debug("%s is equal to %s" % (self, other))
68 # logging.debug("%s is more recent than %s" % (other, self))
70 # logging.debug("%s is more recent than %s" % (self, other))
72 # If no rank could be created, sort by repository priority
74 ret
= cmp(self
.repo
, other
.repo
)
79 hashstr
= ["%s" % s
for s
in (self
.name
, self
.epoch
, self
.version
,
80 self
.release
, self
.arch
,)]
82 return hash("-".join(hashstr
))
84 def dump(self
, short
=False, long=False, filelist
=False):
86 return "%s.%s : %s" % (self
.name
, self
.arch
, self
.summary
)
89 (_("Name"), self
.name
),
90 (_("Arch"), self
.arch
),
91 (_("Version"), self
.version
),
92 (_("Release"), self
.release
),
96 items
.append((_("Size"), util
.format_size(self
.size
)))
98 # filter dummy repository
99 if not self
.repo
== self
.pakfire
.repos
.dummy
:
100 items
.append((_("Repo"), self
.repo
.name
))
103 (_("Summary"), self
.summary
),
104 (_("Groups"), " ".join(self
.groups
)),
105 (_("URL"), self
.url
),
106 (_("License"), self
.license
),
109 caption
= _("Description")
110 for line
in util
.text_wrap(self
.description
):
111 items
.append((caption
, line
))
116 items
.append((_("Maintainer"), self
.maintainer
))
118 items
.append((_("UUID"), self
.uuid
))
119 items
.append((_("Build ID"), self
.build_id
))
120 items
.append((_("Build date"), self
.build_date
))
121 items
.append((_("Build host"), self
.build_host
))
123 caption
= _("Provides")
124 for prov
in sorted(self
.provides
):
125 items
.append((caption
, prov
))
128 caption
= _("Pre-requires")
129 for req
in sorted(self
.prerequires
):
130 items
.append((caption
, req
))
133 caption
= _("Requires")
134 for req
in sorted(self
.requires
):
135 items
.append((caption
, req
))
138 caption
= _("Conflicts")
139 for req
in sorted(self
.conflicts
):
140 items
.append((caption
, req
))
143 caption
= _("Obsoletes")
144 for req
in sorted(self
.obsoletes
):
145 items
.append((caption
, req
))
148 # Append filelist if requested.
150 for file in self
.filelist
:
151 items
.append((_("File"), file))
153 format
= "%%-%ds : %%s" % (max([len(k
) for k
, v
in items
]))
156 for caption
, value
in items
:
157 s
.append(format
% (caption
, value
))
159 s
.append("") # New line at the end
161 # XXX why do we need to decode this?
162 return "\n".join([str.decode("utf-8") for str in s
])
168 "version" : self
.version
,
169 "release" : self
.release
,
170 "epoch" : self
.epoch
,
172 "groups" : self
.groups
,
173 "summary" : self
.summary
,
174 "description" : self
.description
,
175 "maintainer" : self
.maintainer
,
177 "license" : self
.license
,
178 "hash1" : self
.hash1
,
179 "vendor" : self
.vendor
,
180 "build_date" : self
.build_date
,
181 "build_host" : self
.build_host
,
182 "build_id" : self
.build_id
,
183 "build_time" : self
.build_time
,
185 "inst_size" : self
.inst_size
,
197 Return the size of the package file.
199 This should be overloaded by another class and returns 0 for
206 raise NotImplementedError, "%s" % self
211 Indicates whether a package is located "local" means on disk
212 and has not be downloaded.
216 ### META INFORMATION ###
220 raise NotImplementedError
223 def friendly_name(self
):
224 return "%s-%s.%s" % (self
.name
, self
.friendly_version
, self
.arch
)
227 def friendly_version(self
):
228 s
= "%s-%s" % (self
.version
, self
.release
)
231 s
= "%d:%s" % (self
.epoch
, s
)
240 # By default, every package is connected to a dummy repository
241 return self
.pakfire
.repos
.dummy
245 return self
.metadata
.get("PKG_NAME")
249 return self
.metadata
.get("PKG_VER")
255 for i
in ("PKG_RELEASE", "PKG_REL"):
256 ret
= self
.metadata
.get(i
, None)
264 epoch
= self
.metadata
.get("PKG_EPOCH", 0)
270 raise NotImplementedError
275 Say if a package belongs to the basic set
276 that is installed by default.
278 return "Base" in self
.groups
283 Return if a package is marked "critial".
285 return "Critical" in self
.groups
289 return self
.metadata
.get("TYPE", "unknown")
292 def maintainer(self
):
293 return self
.metadata
.get("PKG_MAINTAINER")
297 return self
.metadata
.get("PKG_LICENSE")
301 return self
.metadata
.get("PKG_SUMMARY")
304 def description(self
):
305 return self
.metadata
.get("PKG_DESCRIPTION")
309 return self
.metadata
.get("PKG_GROUPS", "").split()
313 return self
.metadata
.get("PKG_URL")
317 triggers
= self
.metadata
.get("PKG_TRIGGERS", "")
319 return triggers
.split()
323 raise NotImplementedError
326 def build_date(self
):
328 Automatically convert the UNIX timestamp from self.build_time to
329 a humanly readable format.
331 if self
.build_time
is None:
334 return "%s UTC" % datetime
.datetime
.utcfromtimestamp(self
.build_time
)
337 def build_host(self
):
338 return self
.metadata
.get("BUILD_HOST")
342 return self
.metadata
.get("BUILD_ID")
345 def build_time(self
):
346 build_time
= self
.metadata
.get("BUILD_TIME", 0)
348 return int(build_time
)
352 return self
.metadata
.get("PKG_UUID", None)
355 def supported_arches(self
):
356 return self
.metadata
.get("PKG_SUPPORTED_ARCHES", "all")
360 return self
.metadata
.get("PKG_VENDOR", "")
363 def prerequires(self
):
364 requires
= self
.metadata
.get("PKG_PREREQUIRES", "")
366 return requires
.split()
372 # The default attributes, that are process for the requires.
373 attrs
= ["PKG_REQUIRES", "PKG_DEPS",]
375 if self
.arch
== "src":
376 attrs
+= ["PKG_BUILD_DEPS",]
379 ret
= self
.metadata
.get(i
, ret
)
387 return self
.metadata
.get("PKG_PROVIDES", "").split()
391 return self
.metadata
.get("PKG_CONFLICTS", "").split()
395 return self
.metadata
.get("PKG_OBSOLETES", "").split()
398 def scriptlets(self
):
399 return self
.metadata
.get("PKG_SCRIPTLETS", "").split()
403 raise NotImplementedError
405 def extract(self
, path
, prefix
=None):
406 raise NotImplementedError, "%s" % repr(self
)
408 def remove(self
, message
=None, prefix
=None):
409 # Make two filelists. One contains all binary files that need to be
410 # removed, the other one contains the configuration files which are
411 # kept. files and configfiles are disjunct.
413 configfiles
= self
.configfiles
415 for file in self
.filelist
:
416 if file in configfiles
:
419 assert file.startswith("/")
422 self
._remove
_files
(files
, message
, prefix
)
424 def _remove_files(self
, files
, message
, prefix
):
425 if prefix
in ("/", None):
431 message
= "%-10s : %s" % (message
, self
.friendly_name
)
432 pb
= util
.make_progress(message
, len(files
), eta
=False)
434 # Sort files by the length of their name to remove all files in
435 # a directory first and then check, if there are any files left.
436 files
.sort(cmp=lambda x
,y
: cmp(len(x
), len(y
)), reverse
=True)
445 logging
.debug("Removing file: %s" % _file
)
448 file = os
.path
.join(prefix
, _file
[1:])
449 assert file.startswith("%s/" % prefix
)
453 # If the file was removed by the user, we can skip it.
454 if not os
.path
.exists(file):
457 # Handle regular files and symlinks.
458 if os
.path
.isfile(file) or os
.path
.islink(file):
462 logging
.error("Cannot remove file: %s. Remove manually." % _file
)
464 # Handle directories.
465 # Skip removal if the directory is a mountpoint.
466 elif os
.path
.isdir(file) and not os
.path
.ismount(file):
467 # Try to remove the directory. If it is not empty, OSError is raised,
468 # but we are okay with that.
474 # Log all unhandled types.
476 logging
.warning("Cannot remove file: %s. Filetype is unhandled." % _file
)
481 # XXX Rename config files