]>
git.ipfire.org Git - people/stevee/pakfire.git/blob - 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):
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
),
93 (_("Size"), util
.format_size(self
.size
)),
94 (_("Repo"), self
.repo
.name
),
95 (_("Summary"), self
.summary
),
96 (_("Groups"), " ".join(self
.groups
)),
98 (_("License"), self
.license
),
101 caption
= _("Description")
102 for line
in util
.text_wrap(self
.description
):
103 items
.append((caption
, line
))
107 items
.append((_("UUID"), self
.uuid
))
108 items
.append((_("Build ID"), self
.build_id
))
109 items
.append((_("Build date"), self
.build_date
))
110 items
.append((_("Build host"), self
.build_host
))
112 caption
= _("Provides")
113 for prov
in sorted(self
.provides
):
114 items
.append((caption
, prov
))
117 caption
= _("Requires")
118 for req
in sorted(self
.requires
):
119 items
.append((caption
, req
))
122 format
= "%%-%ds : %%s" % (max([len(k
) for k
, v
in items
]))
125 for caption
, value
in items
:
126 s
.append(format
% (caption
, value
))
128 s
.append("") # New line at the end
130 # XXX why do we need to decode this?
131 return "\n".join([str.decode("utf-8") for str in s
])
137 "version" : self
.version
,
138 "release" : self
.release
,
139 "epoch" : self
.epoch
,
141 "groups" : self
.groups
,
142 "summary" : self
.summary
,
143 "description" : self
.description
,
144 "maintainer" : self
.maintainer
,
146 "license" : self
.license
,
147 "hash1" : self
.hash1
,
148 "vendor" : self
.vendor
,
149 "build_host" : self
.build_host
,
150 "build_time" : self
.build_time
,
152 "inst_size" : self
.inst_size
,
164 Return the size of the package file.
166 This should be overloaded by another class and returns 0 for
179 Indicates whether a package is located "local" means on disk
180 and has not be downloaded.
184 ### META INFORMATION ###
188 raise NotImplementedError
191 def friendly_name(self
):
192 return "%s-%s.%s" % (self
.name
, self
.friendly_version
, self
.arch
)
195 def friendly_version(self
):
196 s
= "%s-%s" % (self
.version
, self
.release
)
199 s
= "%d:%s" % (self
.epoch
, s
)
208 # By default, every package is connected to a dummy repository
209 return self
.pakfire
.repos
.dummy
213 return self
.metadata
.get("PKG_NAME")
217 return self
.metadata
.get("PKG_VER")
223 for i
in ("PKG_RELEASE", "PKG_REL"):
224 ret
= self
.metadata
.get(i
, None)
232 epoch
= self
.metadata
.get("PKG_EPOCH", 0)
238 raise NotImplementedError
243 Say if a package belongs to the basic set
244 that is installed by default.
246 return "Base" in self
.groups
251 Return if a package is marked "critial".
253 return "Critical" in self
.groups
257 return self
.metadata
.get("TYPE", "unknown")
260 def maintainer(self
):
261 return self
.metadata
.get("PKG_MAINTAINER")
265 return self
.metadata
.get("PKG_LICENSE")
269 return self
.metadata
.get("PKG_SUMMARY")
272 def description(self
):
273 return self
.metadata
.get("PKG_DESCRIPTION")
277 return self
.metadata
.get("PKG_GROUPS", "").split()
281 return self
.metadata
.get("PKG_URL")
285 triggers
= self
.metadata
.get("PKG_TRIGGERS", "")
287 return triggers
.split()
291 raise NotImplementedError
294 def build_date(self
):
296 Automatically convert the UNIX timestamp from self.build_time to
297 a humanly readable format.
299 return "%s UTC" % datetime
.datetime
.utcfromtimestamp(self
.build_time
)
302 def build_host(self
):
303 return self
.metadata
.get("BUILD_HOST")
307 return self
.metadata
.get("BUILD_ID")
310 def build_time(self
):
311 build_time
= self
.metadata
.get("BUILD_TIME", 0)
313 return int(build_time
)
317 return self
.metadata
.get("PKG_UUID", None)
320 def supported_arches(self
):
321 return self
.metadata
.get("PKG_SUPPORTED_ARCHES", "all")
325 return self
.metadata
.get("PKG_VENDOR", "")
328 def prerequires(self
):
329 requires
= self
.metadata
.get("PKG_PREREQUIRES", "")
331 return requires
.split()
337 # The default attributes, that are process for the requires.
338 attrs
= ["PKG_REQUIRES", "PKG_DEPS",]
340 if self
.arch
== "src":
341 attrs
+= ["PKG_BUILD_DEPS",]
344 ret
= self
.metadata
.get(i
, ret
)
352 return self
.metadata
.get("PKG_PROVIDES", "").split()
356 return self
.metadata
.get("PKG_CONFLICTS", "").split()
360 return self
.metadata
.get("PKG_OBSOLETES", "").split()
362 def extract(self
, path
, prefix
=None):
363 raise NotImplementedError, "%s" % repr(self
)
365 def remove(self
, message
=None, prefix
=None):
366 # Make two filelists. One contains all binary files that need to be
367 # removed, the other one contains the configuration files which are
368 # kept. files and configfiles are disjunct.
370 configfiles
= self
.configfiles
372 for file in self
.filelist
:
373 if file in configfiles
:
376 assert file.startswith("/")
379 self
._remove
_files
(files
, message
, prefix
)
381 def _remove_files(self
, files
, message
, prefix
):
382 if prefix
in ("/", None):
388 message
= "%-10s : %s" % (message
, self
.friendly_name
)
389 pb
= util
.make_progress(message
, len(files
), eta
=False)
391 # Sort files by the length of their name to remove all files in
392 # a directory first and then check, if there are any files left.
393 files
.sort(cmp=lambda x
,y
: cmp(len(x
), len(y
)), reverse
=True)
402 logging
.debug("Removing file: %s" % _file
)
405 file = os
.path
.join(prefix
, _file
[1:])
406 assert file.startswith("%s/" % prefix
)
410 # If the file was removed by the user, we can skip it.
411 if not os
.path
.exists(file):
414 # Handle regular files and symlinks.
415 if os
.path
.isfile(file) or os
.path
.islink(file):
419 logging
.error("Cannot remove file: %s. Remove manually." % _file
)
421 # Handle directories.
422 # Skip removal if the directory is a mountpoint.
423 elif os
.path
.isdir(file) and not os
.path
.ismount(file):
424 # Try to remove the directory. If it is not empty, OSError is raised,
425 # but we are okay with that.
431 # Log all unhandled types.
433 logging
.warning("Cannot remove file: %s. Filetype is unhandled." % _file
)
438 # XXX Rename config files