]>
Commit | Line | Data |
---|---|---|
47a4cb89 MT |
1 | #!/usr/bin/python |
2 | ||
c605d735 | 3 | import datetime |
47a4cb89 | 4 | import logging |
d4c94aa5 | 5 | import xml.sax.saxutils |
47a4cb89 | 6 | |
47a4cb89 MT |
7 | import util |
8 | ||
9 | from pakfire.i18n import _ | |
10 | ||
11 | class Package(object): | |
a2d1644c | 12 | def __init__(self, pakfire, repo=None): |
3723913b MT |
13 | self.pakfire = pakfire |
14 | self._repo = repo | |
47a4cb89 | 15 | |
911e0d8c MT |
16 | # Pointer to a package that is updated by this one. |
17 | self.old_package = None | |
18 | ||
47a4cb89 | 19 | def __repr__(self): |
3723913b | 20 | return "<%s %s>" % (self.__class__.__name__, self.friendly_name) |
47a4cb89 MT |
21 | |
22 | def __cmp__(self, other): | |
23 | # if packages differ names return in alphabetical order | |
24 | if not self.name == other.name: | |
25 | return cmp(self.name, other.name) | |
26 | ||
268dd720 | 27 | ret = util.version_compare(self.version_tuple, other.version_tuple) |
47a4cb89 | 28 | |
b566f7e3 MT |
29 | # XXX this is to move packages that have been built a while ago and |
30 | # do not have all the meta information that they won't be evaluated | |
31 | # as the best match. | |
32 | if not ret: | |
33 | if "X"*3 in (self.build_id, other.build_id): | |
34 | if self.build_id == "X"*3 and not other.build_id == "X"*3: | |
35 | ret = -1 | |
36 | ||
37 | elif not self.build_id == "X"*3 and other.build_id == "X"*3: | |
38 | ret = 1 | |
39 | # XXX hack end | |
40 | ||
ddd6b1de | 41 | # Compare the build times if we have a rebuilt package. |
a2d1644c | 42 | if not ret and self.build_time and other.build_time: |
ddd6b1de MT |
43 | ret = cmp(self.build_time, other.build_time) |
44 | ||
47a4cb89 MT |
45 | #if ret == 0: |
46 | # logging.debug("%s is equal to %s" % (self, other)) | |
47 | #elif ret < 0: | |
48 | # logging.debug("%s is more recent than %s" % (other, self)) | |
49 | #elif ret > 0: | |
50 | # logging.debug("%s is more recent than %s" % (self, other)) | |
51 | ||
4ad46eec MT |
52 | # If no rank could be created, sort by repository priority |
53 | if not ret: | |
54 | ret = cmp(self.repo, other.repo) | |
55 | ||
47a4cb89 MT |
56 | return ret |
57 | ||
a7277777 MT |
58 | def __hash__(self): |
59 | hashstr = ["%s" % s for s in (self.name, self.epoch, self.version, | |
60 | self.release, self.arch,)] | |
61 | ||
62 | return hash("-".join(hashstr)) | |
63 | ||
9afa5620 | 64 | def dump(self, short=False, long=False): |
47a4cb89 MT |
65 | if short: |
66 | return "%s.%s : %s" % (self.name, self.arch, self.summary) | |
67 | ||
68 | items = [ | |
69 | (_("Name"), self.name), | |
70 | (_("Arch"), self.arch), | |
71 | (_("Version"), self.version), | |
72 | (_("Release"), self.release), | |
73 | (_("Size"), util.format_size(self.size)), | |
a7cf3466 | 74 | (_("Repo"), self.repo.name), |
47a4cb89 | 75 | (_("Summary"), self.summary), |
8537c16d | 76 | (_("Groups"), " ".join(self.groups)), |
a7cf3466 | 77 | (_("URL"), self.url), |
47a4cb89 MT |
78 | (_("License"), self.license), |
79 | ] | |
80 | ||
81 | caption = _("Description") | |
82 | for line in util.text_wrap(self.description): | |
83 | items.append((caption, line)) | |
84 | caption = "" | |
85 | ||
9afa5620 | 86 | if long: |
1317485d | 87 | items.append((_("UUID"), self.uuid)) |
9afa5620 MT |
88 | items.append((_("Build ID"), self.build_id)) |
89 | items.append((_("Build date"), self.build_date)) | |
90 | items.append((_("Build host"), self.build_host)) | |
91 | ||
f81022c1 MT |
92 | caption = _("Provides") |
93 | for prov in sorted(self.provides): | |
94 | items.append((caption, prov)) | |
95 | caption = "" | |
96 | ||
97 | caption = _("Requires") | |
98 | for req in sorted(self.requires): | |
99 | items.append((caption, req)) | |
100 | caption = "" | |
101 | ||
47a4cb89 MT |
102 | format = "%%-%ds : %%s" % (max([len(k) for k, v in items])) |
103 | ||
104 | s = [] | |
105 | for caption, value in items: | |
106 | s.append(format % (caption, value)) | |
107 | ||
108 | s.append("") # New line at the end | |
109 | ||
52d73aa8 MT |
110 | # XXX why do we need to decode this? |
111 | return "\n".join([str.decode("utf-8") for str in s]) | |
47a4cb89 | 112 | |
47a4cb89 MT |
113 | @property |
114 | def info(self): | |
115 | info = { | |
116 | "name" : self.name, | |
117 | "version" : self.version, | |
118 | "release" : self.release, | |
119 | "epoch" : self.epoch, | |
120 | "arch" : self.arch, | |
8537c16d | 121 | "groups" : self.groups, |
47a4cb89 MT |
122 | "summary" : self.summary, |
123 | "description" : self.description, | |
124 | "maintainer" : self.maintainer, | |
125 | "url" : self.url, | |
126 | "license" : self.license, | |
d4c94aa5 MT |
127 | "hash1" : self.hash1, |
128 | "vendor" : self.vendor, | |
129 | "build_host" : self.build_host, | |
130 | "build_time" : self.build_time, | |
131 | "size" : self.size, | |
132 | "inst_size" : self.inst_size, | |
47a4cb89 MT |
133 | } |
134 | ||
135 | return info | |
136 | ||
d4c94aa5 MT |
137 | @property |
138 | def hash1(self): | |
139 | return "0"*40 | |
140 | ||
47a4cb89 MT |
141 | @property |
142 | def size(self): | |
143 | """ | |
144 | Return the size of the package file. | |
47a4cb89 | 145 | |
9e8b1d7a MT |
146 | This should be overloaded by another class and returns 0 for |
147 | virtual packages. | |
47a4cb89 | 148 | """ |
9e8b1d7a | 149 | return 0 |
47a4cb89 | 150 | |
d4c94aa5 MT |
151 | @property |
152 | def inst_size(self): | |
153 | # XXX to be done | |
154 | return 0 | |
155 | ||
edd6a268 MT |
156 | @property |
157 | def local(self): | |
158 | """ | |
159 | Indicates whether a package is located "local" means on disk | |
160 | and has not be downloaded. | |
161 | """ | |
162 | return False | |
163 | ||
47a4cb89 MT |
164 | ### META INFORMATION ### |
165 | ||
166 | @property | |
167 | def metadata(self): | |
9e8b1d7a | 168 | raise NotImplementedError |
47a4cb89 MT |
169 | |
170 | @property | |
171 | def friendly_name(self): | |
26fdc6dc | 172 | return "%s-%s.%s" % (self.name, self.friendly_version, self.arch) |
47a4cb89 MT |
173 | |
174 | @property | |
175 | def friendly_version(self): | |
176 | s = "%s-%s" % (self.version, self.release) | |
177 | ||
178 | if self.epoch: | |
179 | s = "%d:%s" % (self.epoch, s) | |
180 | ||
181 | return s | |
182 | ||
183 | @property | |
184 | def repo(self): | |
a2d1644c MT |
185 | if self._repo: |
186 | return self._repo | |
187 | ||
188 | # By default, every package is connected to a dummy repository | |
189 | return self.pakfire.repos.dummy | |
47a4cb89 MT |
190 | |
191 | @property | |
192 | def name(self): | |
193 | return self.metadata.get("PKG_NAME") | |
194 | ||
195 | @property | |
196 | def version(self): | |
197 | return self.metadata.get("PKG_VER") | |
198 | ||
199 | @property | |
200 | def release(self): | |
201 | ret = None | |
202 | ||
203 | for i in ("PKG_RELEASE", "PKG_REL"): | |
204 | ret = self.metadata.get(i, None) | |
205 | if ret: | |
206 | break | |
207 | ||
208 | return ret | |
209 | ||
210 | @property | |
211 | def epoch(self): | |
212 | epoch = self.metadata.get("PKG_EPOCH", 0) | |
213 | ||
214 | return int(epoch) | |
215 | ||
268dd720 MT |
216 | @property |
217 | def version_tuple(self): | |
218 | """ | |
219 | Returns a tuple like (epoch, version, release) that can | |
220 | be used to compare versions of packages. | |
221 | """ | |
222 | return (self.epoch, self.version, self.release) | |
223 | ||
47a4cb89 MT |
224 | @property |
225 | def arch(self): | |
226 | raise NotImplementedError | |
227 | ||
8537c16d MT |
228 | @property |
229 | def base(self): | |
230 | """ | |
231 | Say if a package belongs to the basic set | |
232 | that is installed by default. | |
233 | """ | |
234 | return "Base" in self.groups | |
235 | ||
236 | @property | |
237 | def critical(self): | |
238 | """ | |
239 | Return if a package is marked "critial". | |
240 | """ | |
241 | return "Critical" in self.groups | |
242 | ||
03f33deb MT |
243 | @property |
244 | def type(self): | |
245 | return self.metadata.get("TYPE", "unknown") | |
246 | ||
47a4cb89 MT |
247 | @property |
248 | def maintainer(self): | |
249 | return self.metadata.get("PKG_MAINTAINER") | |
250 | ||
251 | @property | |
252 | def license(self): | |
253 | return self.metadata.get("PKG_LICENSE") | |
254 | ||
255 | @property | |
256 | def summary(self): | |
257 | return self.metadata.get("PKG_SUMMARY") | |
258 | ||
259 | @property | |
260 | def description(self): | |
261 | return self.metadata.get("PKG_DESCRIPTION") | |
262 | ||
263 | @property | |
8537c16d MT |
264 | def groups(self): |
265 | return self.metadata.get("PKG_GROUPS", "").split() | |
47a4cb89 MT |
266 | |
267 | @property | |
268 | def url(self): | |
269 | return self.metadata.get("PKG_URL") | |
270 | ||
9c75e8ed MT |
271 | @property |
272 | def triggers(self): | |
273 | triggers = self.metadata.get("PKG_TRIGGERS", "") | |
274 | ||
275 | return triggers.split() | |
276 | ||
47a4cb89 MT |
277 | @property |
278 | def signature(self): | |
9e8b1d7a | 279 | raise NotImplementedError |
47a4cb89 MT |
280 | |
281 | @property | |
282 | def build_date(self): | |
c605d735 MT |
283 | """ |
284 | Automatically convert the UNIX timestamp from self.build_time to | |
285 | a humanly readable format. | |
286 | """ | |
287 | return "%s UTC" % datetime.datetime.utcfromtimestamp(self.build_time) | |
47a4cb89 MT |
288 | |
289 | @property | |
290 | def build_host(self): | |
c605d735 | 291 | return self.metadata.get("BUILD_HOST") |
47a4cb89 MT |
292 | |
293 | @property | |
294 | def build_id(self): | |
295 | return self.metadata.get("BUILD_ID") | |
296 | ||
ddd6b1de MT |
297 | @property |
298 | def build_time(self): | |
299 | build_time = self.metadata.get("BUILD_TIME", 0) | |
300 | ||
301 | return int(build_time) | |
302 | ||
1317485d MT |
303 | @property |
304 | def uuid(self): | |
305 | return self.metadata.get("PKG_UUID", None) | |
306 | ||
677ff42a MT |
307 | @property |
308 | def supported_arches(self): | |
309 | return self.metadata.get("PKG_SUPPORTED_ARCHES", "all") | |
310 | ||
ae20b05f MT |
311 | @property |
312 | def vendor(self): | |
313 | return self.metadata.get("PKG_VENDOR", "") | |
314 | ||
d4c94aa5 | 315 | @property |
71d3b468 MT |
316 | def prerequires(self): |
317 | requires = self.metadata.get("PKG_PREREQUIRES", "") | |
318 | ||
319 | return requires.split() | |
d4c94aa5 | 320 | |
743eaa12 | 321 | @property |
4496b160 MT |
322 | def requires(self): |
323 | ret = "" | |
324 | ||
325 | # The default attributes, that are process for the requires. | |
326 | attrs = ("PKG_REQUIRES", "PKG_DEPS") | |
327 | ||
328 | # Source packages do depend on their build dependencies. | |
329 | if self.arch == "src": | |
330 | attrs = ("PKG_BUILD_DEPS",) | |
331 | ||
332 | for i in attrs: | |
333 | ret = self.metadata.get(i, ret) | |
334 | if ret: | |
335 | break | |
336 | ||
71d3b468 | 337 | return ret.split() |
4496b160 | 338 | |
da77e6fa | 339 | @property |
ae20b05f | 340 | def provides(self): |
71d3b468 | 341 | return self.metadata.get("PKG_PROVIDES", "").split() |
da77e6fa | 342 | |
d4c94aa5 MT |
343 | @property |
344 | def conflicts(self): | |
71d3b468 | 345 | return self.metadata.get("PKG_CONFLICTS", "").split() |
d4c94aa5 | 346 | |
ae20b05f MT |
347 | @property |
348 | def obsoletes(self): | |
71d3b468 | 349 | return self.metadata.get("PKG_OBSOLETES", "").split() |
47a4cb89 | 350 | |
ae20b05f | 351 | def extract(self, path, prefix=None): |
d4c94aa5 | 352 | raise NotImplementedError, "%s" % repr(self) |