1 |
FEATURES=binpkg-multi-instance causes an integer build-id to be |
2 |
associated with each binary package instance. Inclusion of the build-id |
3 |
in the file name of the binary package file makes it possible to store |
4 |
an arbitrary number of binary packages built from the same ebuild. |
5 |
|
6 |
Having multiple instances is useful for a number of purposes, such as |
7 |
retaining builds that were built with different USE flags or linked |
8 |
against different versions of libraries. The location of any particular |
9 |
package within PKGDIR can be expressed as follows: |
10 |
|
11 |
${PKGDIR}/${CATEGORY}/${PN}/${PF}-${BUILD_ID}.xpak |
12 |
|
13 |
The build-id starts at 1 for the first build of a particular ebuild, |
14 |
and is incremented by 1 for each new build. It is possible to share a |
15 |
writable PKGDIR over NFS, and locking ensures that each package added |
16 |
to PKGDIR will have a unique build-id. It is not necessary to migrate |
17 |
an existing PKGDIR to the new layout, since portage is capable of |
18 |
working with a mixed PKGDIR layout, where packages using the old layout |
19 |
are allowed to remain in place. |
20 |
|
21 |
The new PKGDIR layout is backward-compatible with binhost clients |
22 |
running older portage, since the file format is identical, the |
23 |
per-package PATH attribute in the 'Packages' index directs them to |
24 |
download the file from the correct URI, and they automatically use |
25 |
BUILD_TIME metadata to select the latest builds. |
26 |
|
27 |
There is currently no automated way to prune old builds from PKGDIR, |
28 |
although it is possible to remove packages manually, and then run |
29 |
'emaint --fix binhost' to update the ${PKGDIR}/Packages index. Support |
30 |
for FEATURES=binpkg-multi-instance is planned for eclean-pkg. |
31 |
|
32 |
X-Gentoo-Bug: 150031 |
33 |
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=150031 |
34 |
--- |
35 |
PATCH 3/7 v3 fixes a couple of issues reported by David James: |
36 |
* Fix depgraph to exhaustively search for a binary package with the |
37 |
desired USE settings |
38 |
* Fix binarytree.inject to preserve multiple package instances with |
39 |
the same BUILD_ID (or even missing BUILD_ID). This is useful if the |
40 |
client has FEATURES=binpkg-multi-instance enabled, in order to |
41 |
preserve multiple instances from multiple binhosts that do not have |
42 |
FEATURES=binpkg-multi-instance enabled. In this case, the number |
43 |
in the local binpkg file name does not have to correspond to the |
44 |
BUILD_ID metadata in the package. |
45 |
|
46 |
bin/quickpkg | 1 - |
47 |
man/make.conf.5 | 27 + |
48 |
pym/_emerge/Binpkg.py | 33 +- |
49 |
pym/_emerge/BinpkgFetcher.py | 13 +- |
50 |
pym/_emerge/BinpkgPrefetcher.py | 2 +- |
51 |
pym/_emerge/BinpkgVerifier.py | 6 +- |
52 |
pym/_emerge/EbuildBinpkg.py | 9 +- |
53 |
pym/_emerge/EbuildBuild.py | 36 +- |
54 |
pym/_emerge/Package.py | 16 +- |
55 |
pym/_emerge/Scheduler.py | 6 +- |
56 |
pym/_emerge/clear_caches.py | 1 - |
57 |
pym/_emerge/depgraph.py | 16 +- |
58 |
pym/portage/const.py | 2 + |
59 |
pym/portage/dbapi/bintree.py | 683 +++++++++++++++++--------- |
60 |
pym/portage/emaint/modules/binhost/binhost.py | 47 +- |
61 |
15 files changed, 613 insertions(+), 285 deletions(-) |
62 |
|
63 |
diff --git a/bin/quickpkg b/bin/quickpkg |
64 |
index 2c69a69..8b71c3e 100755 |
65 |
--- a/bin/quickpkg |
66 |
+++ b/bin/quickpkg |
67 |
@@ -63,7 +63,6 @@ def quickpkg_atom(options, infos, arg, eout): |
68 |
pkgs_for_arg = 0 |
69 |
for cpv in matches: |
70 |
excluded_config_files = [] |
71 |
- bintree.prevent_collision(cpv) |
72 |
dblnk = vardb._dblink(cpv) |
73 |
have_lock = False |
74 |
|
75 |
diff --git a/man/make.conf.5 b/man/make.conf.5 |
76 |
index cd1ae21..1b71b97 100644 |
77 |
--- a/man/make.conf.5 |
78 |
+++ b/man/make.conf.5 |
79 |
@@ -256,6 +256,33 @@ has a \fB\-\-force\fR option that can be used to force regeneration of digests. |
80 |
Keep logs from successful binary package merges. This is relevant only when |
81 |
\fBPORT_LOGDIR\fR is set. |
82 |
.TP |
83 |
+.B binpkg\-multi\-instance |
84 |
+Enable support for multiple binary package instances per ebuild. |
85 |
+Having multiple instances is useful for a number of purposes, such as |
86 |
+retaining builds that were built with different USE flags or linked |
87 |
+against different versions of libraries. The location of any particular |
88 |
+package within PKGDIR can be expressed as follows: |
89 |
+ |
90 |
+ ${PKGDIR}/${CATEGORY}/${PN}/${PF}\-${BUILD_ID}.xpak |
91 |
+ |
92 |
+The build\-id starts at 1 for the first build of a particular ebuild, |
93 |
+and is incremented by 1 for each new build. It is possible to share a |
94 |
+writable PKGDIR over NFS, and locking ensures that each package added |
95 |
+to PKGDIR will have a unique build\-id. It is not necessary to migrate |
96 |
+an existing PKGDIR to the new layout, since portage is capable of |
97 |
+working with a mixed PKGDIR layout, where packages using the old layout |
98 |
+are allowed to remain in place. |
99 |
+ |
100 |
+The new PKGDIR layout is backward\-compatible with binhost clients |
101 |
+running older portage, since the file format is identical, the |
102 |
+per\-package PATH attribute in the 'Packages' index directs them to |
103 |
+download the file from the correct URI, and they automatically use |
104 |
+BUILD_TIME metadata to select the latest builds. |
105 |
+ |
106 |
+There is currently no automated way to prune old builds from PKGDIR, |
107 |
+although it is possible to remove packages manually, and then run |
108 |
+\(aqemaint \-\-fix binhost' to update the ${PKGDIR}/Packages index. |
109 |
+.TP |
110 |
.B buildpkg |
111 |
Binary packages will be created for all packages that are merged. Also see |
112 |
\fBquickpkg\fR(1) and \fBemerge\fR(1) \fB\-\-buildpkg\fR and |
113 |
diff --git a/pym/_emerge/Binpkg.py b/pym/_emerge/Binpkg.py |
114 |
index ded6dfd..7b7ae17 100644 |
115 |
--- a/pym/_emerge/Binpkg.py |
116 |
+++ b/pym/_emerge/Binpkg.py |
117 |
@@ -121,16 +121,11 @@ class Binpkg(CompositeTask): |
118 |
fetcher = BinpkgFetcher(background=self.background, |
119 |
logfile=self.settings.get("PORTAGE_LOG_FILE"), pkg=self.pkg, |
120 |
pretend=self.opts.pretend, scheduler=self.scheduler) |
121 |
- pkg_path = fetcher.pkg_path |
122 |
- self._pkg_path = pkg_path |
123 |
- # This gives bashrc users an opportunity to do various things |
124 |
- # such as remove binary packages after they're installed. |
125 |
- self.settings["PORTAGE_BINPKG_FILE"] = pkg_path |
126 |
|
127 |
if self.opts.getbinpkg and self._bintree.isremote(pkg.cpv): |
128 |
- |
129 |
msg = " --- (%s of %s) Fetching Binary (%s::%s)" %\ |
130 |
- (pkg_count.curval, pkg_count.maxval, pkg.cpv, pkg_path) |
131 |
+ (pkg_count.curval, pkg_count.maxval, pkg.cpv, |
132 |
+ fetcher.pkg_path) |
133 |
short_msg = "emerge: (%s of %s) %s Fetch" % \ |
134 |
(pkg_count.curval, pkg_count.maxval, pkg.cpv) |
135 |
self.logger.log(msg, short_msg=short_msg) |
136 |
@@ -149,7 +144,7 @@ class Binpkg(CompositeTask): |
137 |
# The fetcher only has a returncode when |
138 |
# --getbinpkg is enabled. |
139 |
if fetcher.returncode is not None: |
140 |
- self._fetched_pkg = True |
141 |
+ self._fetched_pkg = fetcher.pkg_path |
142 |
if self._default_exit(fetcher) != os.EX_OK: |
143 |
self._unlock_builddir() |
144 |
self.wait() |
145 |
@@ -163,9 +158,15 @@ class Binpkg(CompositeTask): |
146 |
|
147 |
verifier = None |
148 |
if self._verify: |
149 |
+ if self._fetched_pkg: |
150 |
+ path = self._fetched_pkg |
151 |
+ else: |
152 |
+ path = self.pkg.root_config.trees["bintree"].getname( |
153 |
+ self.pkg.cpv) |
154 |
logfile = self.settings.get("PORTAGE_LOG_FILE") |
155 |
verifier = BinpkgVerifier(background=self.background, |
156 |
- logfile=logfile, pkg=self.pkg, scheduler=self.scheduler) |
157 |
+ logfile=logfile, pkg=self.pkg, scheduler=self.scheduler, |
158 |
+ _pkg_path=path) |
159 |
self._start_task(verifier, self._verifier_exit) |
160 |
return |
161 |
|
162 |
@@ -181,10 +182,20 @@ class Binpkg(CompositeTask): |
163 |
logger = self.logger |
164 |
pkg = self.pkg |
165 |
pkg_count = self.pkg_count |
166 |
- pkg_path = self._pkg_path |
167 |
|
168 |
if self._fetched_pkg: |
169 |
- self._bintree.inject(pkg.cpv, filename=pkg_path) |
170 |
+ pkg_path = self._bintree.getname( |
171 |
+ self._bintree.inject(pkg.cpv, |
172 |
+ filename=self._fetched_pkg), |
173 |
+ allocate_new=False) |
174 |
+ else: |
175 |
+ pkg_path = self.pkg.root_config.trees["bintree"].getname( |
176 |
+ self.pkg.cpv) |
177 |
+ |
178 |
+ # This gives bashrc users an opportunity to do various things |
179 |
+ # such as remove binary packages after they're installed. |
180 |
+ self.settings["PORTAGE_BINPKG_FILE"] = pkg_path |
181 |
+ self._pkg_path = pkg_path |
182 |
|
183 |
logfile = self.settings.get("PORTAGE_LOG_FILE") |
184 |
if logfile is not None and os.path.isfile(logfile): |
185 |
diff --git a/pym/_emerge/BinpkgFetcher.py b/pym/_emerge/BinpkgFetcher.py |
186 |
index 543881e..a7f2d44 100644 |
187 |
--- a/pym/_emerge/BinpkgFetcher.py |
188 |
+++ b/pym/_emerge/BinpkgFetcher.py |
189 |
@@ -24,7 +24,8 @@ class BinpkgFetcher(SpawnProcess): |
190 |
def __init__(self, **kwargs): |
191 |
SpawnProcess.__init__(self, **kwargs) |
192 |
pkg = self.pkg |
193 |
- self.pkg_path = pkg.root_config.trees["bintree"].getname(pkg.cpv) |
194 |
+ self.pkg_path = pkg.root_config.trees["bintree"].getname( |
195 |
+ pkg.cpv) + ".partial" |
196 |
|
197 |
def _start(self): |
198 |
|
199 |
@@ -51,10 +52,12 @@ class BinpkgFetcher(SpawnProcess): |
200 |
# urljoin doesn't work correctly with |
201 |
# unrecognized protocols like sftp |
202 |
if bintree._remote_has_index: |
203 |
- rel_uri = bintree._remotepkgs[pkg.cpv].get("PATH") |
204 |
+ instance_key = bintree.dbapi._instance_key(pkg.cpv) |
205 |
+ rel_uri = bintree._remotepkgs[instance_key].get("PATH") |
206 |
if not rel_uri: |
207 |
rel_uri = pkg.cpv + ".tbz2" |
208 |
- remote_base_uri = bintree._remotepkgs[pkg.cpv]["BASE_URI"] |
209 |
+ remote_base_uri = bintree._remotepkgs[ |
210 |
+ instance_key]["BASE_URI"] |
211 |
uri = remote_base_uri.rstrip("/") + "/" + rel_uri.lstrip("/") |
212 |
else: |
213 |
uri = settings["PORTAGE_BINHOST"].rstrip("/") + \ |
214 |
@@ -128,7 +131,9 @@ class BinpkgFetcher(SpawnProcess): |
215 |
# the fetcher didn't already do it automatically. |
216 |
bintree = self.pkg.root_config.trees["bintree"] |
217 |
if bintree._remote_has_index: |
218 |
- remote_mtime = bintree._remotepkgs[self.pkg.cpv].get("MTIME") |
219 |
+ remote_mtime = bintree._remotepkgs[ |
220 |
+ bintree.dbapi._instance_key( |
221 |
+ self.pkg.cpv)].get("MTIME") |
222 |
if remote_mtime is not None: |
223 |
try: |
224 |
remote_mtime = long(remote_mtime) |
225 |
diff --git a/pym/_emerge/BinpkgPrefetcher.py b/pym/_emerge/BinpkgPrefetcher.py |
226 |
index ffa4900..7ca8970 100644 |
227 |
--- a/pym/_emerge/BinpkgPrefetcher.py |
228 |
+++ b/pym/_emerge/BinpkgPrefetcher.py |
229 |
@@ -27,7 +27,7 @@ class BinpkgPrefetcher(CompositeTask): |
230 |
|
231 |
verifier = BinpkgVerifier(background=self.background, |
232 |
logfile=self.scheduler.fetch.log_file, pkg=self.pkg, |
233 |
- scheduler=self.scheduler) |
234 |
+ scheduler=self.scheduler, _pkg_path=self.pkg_path) |
235 |
self._start_task(verifier, self._verifier_exit) |
236 |
|
237 |
def _verifier_exit(self, verifier): |
238 |
diff --git a/pym/_emerge/BinpkgVerifier.py b/pym/_emerge/BinpkgVerifier.py |
239 |
index 2c69792..7a6d15e 100644 |
240 |
--- a/pym/_emerge/BinpkgVerifier.py |
241 |
+++ b/pym/_emerge/BinpkgVerifier.py |
242 |
@@ -33,7 +33,6 @@ class BinpkgVerifier(CompositeTask): |
243 |
digests = _apply_hash_filter(digests, hash_filter) |
244 |
|
245 |
self._digests = digests |
246 |
- self._pkg_path = bintree.getname(self.pkg.cpv) |
247 |
|
248 |
try: |
249 |
size = os.stat(self._pkg_path).st_size |
250 |
@@ -90,8 +89,11 @@ class BinpkgVerifier(CompositeTask): |
251 |
if portage.output.havecolor: |
252 |
portage.output.havecolor = not self.background |
253 |
|
254 |
+ path = self._pkg_path |
255 |
+ if path.endswith(".partial"): |
256 |
+ path = path[:-len(".partial")] |
257 |
eout = EOutput() |
258 |
- eout.ebegin("%s %s ;-)" % (os.path.basename(self._pkg_path), |
259 |
+ eout.ebegin("%s %s ;-)" % (os.path.basename(path), |
260 |
" ".join(sorted(self._digests)))) |
261 |
eout.eend(0) |
262 |
|
263 |
diff --git a/pym/_emerge/EbuildBinpkg.py b/pym/_emerge/EbuildBinpkg.py |
264 |
index 34a6aef..6e098eb 100644 |
265 |
--- a/pym/_emerge/EbuildBinpkg.py |
266 |
+++ b/pym/_emerge/EbuildBinpkg.py |
267 |
@@ -10,13 +10,12 @@ class EbuildBinpkg(CompositeTask): |
268 |
This assumes that src_install() has successfully completed. |
269 |
""" |
270 |
__slots__ = ('pkg', 'settings') + \ |
271 |
- ('_binpkg_tmpfile',) |
272 |
+ ('_binpkg_tmpfile', '_binpkg_info') |
273 |
|
274 |
def _start(self): |
275 |
pkg = self.pkg |
276 |
root_config = pkg.root_config |
277 |
bintree = root_config.trees["bintree"] |
278 |
- bintree.prevent_collision(pkg.cpv) |
279 |
binpkg_tmpfile = os.path.join(bintree.pkgdir, |
280 |
pkg.cpv + ".tbz2." + str(os.getpid())) |
281 |
bintree._ensure_dir(os.path.dirname(binpkg_tmpfile)) |
282 |
@@ -43,8 +42,12 @@ class EbuildBinpkg(CompositeTask): |
283 |
|
284 |
pkg = self.pkg |
285 |
bintree = pkg.root_config.trees["bintree"] |
286 |
- bintree.inject(pkg.cpv, filename=self._binpkg_tmpfile) |
287 |
+ self._binpkg_info = bintree.inject(pkg.cpv, |
288 |
+ filename=self._binpkg_tmpfile) |
289 |
|
290 |
self._current_task = None |
291 |
self.returncode = os.EX_OK |
292 |
self.wait() |
293 |
+ |
294 |
+ def get_binpkg_info(self): |
295 |
+ return self._binpkg_info |
296 |
diff --git a/pym/_emerge/EbuildBuild.py b/pym/_emerge/EbuildBuild.py |
297 |
index b5b1e87..0e98602 100644 |
298 |
--- a/pym/_emerge/EbuildBuild.py |
299 |
+++ b/pym/_emerge/EbuildBuild.py |
300 |
@@ -1,6 +1,10 @@ |
301 |
# Copyright 1999-2014 Gentoo Foundation |
302 |
# Distributed under the terms of the GNU General Public License v2 |
303 |
|
304 |
+from __future__ import unicode_literals |
305 |
+ |
306 |
+import io |
307 |
+ |
308 |
import _emerge.emergelog |
309 |
from _emerge.EbuildExecuter import EbuildExecuter |
310 |
from _emerge.EbuildPhase import EbuildPhase |
311 |
@@ -15,7 +19,7 @@ from _emerge.TaskSequence import TaskSequence |
312 |
|
313 |
from portage.util import writemsg |
314 |
import portage |
315 |
-from portage import os |
316 |
+from portage import _encodings, _unicode_decode, _unicode_encode, os |
317 |
from portage.output import colorize |
318 |
from portage.package.ebuild.digestcheck import digestcheck |
319 |
from portage.package.ebuild.digestgen import digestgen |
320 |
@@ -317,9 +321,13 @@ class EbuildBuild(CompositeTask): |
321 |
phase="rpm", scheduler=self.scheduler, |
322 |
settings=self.settings)) |
323 |
else: |
324 |
- binpkg_tasks.add(EbuildBinpkg(background=self.background, |
325 |
+ task = EbuildBinpkg( |
326 |
+ background=self.background, |
327 |
pkg=self.pkg, scheduler=self.scheduler, |
328 |
- settings=self.settings)) |
329 |
+ settings=self.settings) |
330 |
+ binpkg_tasks.add(task) |
331 |
+ task.addExitListener( |
332 |
+ self._record_binpkg_info) |
333 |
|
334 |
if binpkg_tasks: |
335 |
self._start_task(binpkg_tasks, self._buildpkg_exit) |
336 |
@@ -356,6 +364,28 @@ class EbuildBuild(CompositeTask): |
337 |
self.returncode = packager.returncode |
338 |
self.wait() |
339 |
|
340 |
+ def _record_binpkg_info(self, task): |
341 |
+ if task.returncode != os.EX_OK: |
342 |
+ return |
343 |
+ |
344 |
+ # Save info about the created binary package, so that |
345 |
+ # identifying information can be passed to the install |
346 |
+ # task, to be recorded in the installed package database. |
347 |
+ pkg = task.get_binpkg_info() |
348 |
+ infoloc = os.path.join(self.settings["PORTAGE_BUILDDIR"], |
349 |
+ "build-info") |
350 |
+ info = { |
351 |
+ "BINPKGMD5": "%s\n" % pkg._metadata["MD5"], |
352 |
+ } |
353 |
+ if pkg.build_id is not None: |
354 |
+ info["BUILD_ID"] = "%s\n" % pkg.build_id |
355 |
+ for k, v in info.items(): |
356 |
+ with io.open(_unicode_encode(os.path.join(infoloc, k), |
357 |
+ encoding=_encodings['fs'], errors='strict'), |
358 |
+ mode='w', encoding=_encodings['repo.content'], |
359 |
+ errors='strict') as f: |
360 |
+ f.write(v) |
361 |
+ |
362 |
def _buildpkgonly_success_hook_exit(self, success_hooks): |
363 |
self._default_exit(success_hooks) |
364 |
self.returncode = None |
365 |
diff --git a/pym/_emerge/Package.py b/pym/_emerge/Package.py |
366 |
index 975335d..2c1a116 100644 |
367 |
--- a/pym/_emerge/Package.py |
368 |
+++ b/pym/_emerge/Package.py |
369 |
@@ -219,6 +219,8 @@ class Package(Task): |
370 |
else: |
371 |
raise TypeError("root_config argument is required") |
372 |
|
373 |
+ elements = [type_name, root, _unicode(cpv), operation] |
374 |
+ |
375 |
# For installed (and binary) packages we don't care for the repo |
376 |
# when it comes to hashing, because there can only be one cpv. |
377 |
# So overwrite the repo_key with type_name. |
378 |
@@ -229,14 +231,22 @@ class Package(Task): |
379 |
raise AssertionError( |
380 |
"Package._gen_hash_key() " + \ |
381 |
"called without 'repo_name' argument") |
382 |
- repo_key = repo_name |
383 |
+ elements.append(repo_name) |
384 |
+ elif type_name == "binary": |
385 |
+ # Including a variety of fingerprints in the hash makes |
386 |
+ # it possible to simultaneously consider multiple similar |
387 |
+ # packages. Note that digests are not included here, since |
388 |
+ # they are relatively expensive to compute, and they may |
389 |
+ # not necessarily be available. |
390 |
+ elements.extend([cpv.build_id, cpv.file_size, |
391 |
+ cpv.build_time, cpv.mtime]) |
392 |
else: |
393 |
# For installed (and binary) packages we don't care for the repo |
394 |
# when it comes to hashing, because there can only be one cpv. |
395 |
# So overwrite the repo_key with type_name. |
396 |
- repo_key = type_name |
397 |
+ elements.append(type_name) |
398 |
|
399 |
- return (type_name, root, _unicode(cpv), operation, repo_key) |
400 |
+ return tuple(elements) |
401 |
|
402 |
def _validate_deps(self): |
403 |
""" |
404 |
diff --git a/pym/_emerge/Scheduler.py b/pym/_emerge/Scheduler.py |
405 |
index 6e3bf1a..6b39e3b 100644 |
406 |
--- a/pym/_emerge/Scheduler.py |
407 |
+++ b/pym/_emerge/Scheduler.py |
408 |
@@ -862,8 +862,12 @@ class Scheduler(PollScheduler): |
409 |
continue |
410 |
fetched = fetcher.pkg_path |
411 |
|
412 |
+ if fetched is False: |
413 |
+ filename = bintree.getname(x.cpv) |
414 |
+ else: |
415 |
+ filename = fetched |
416 |
verifier = BinpkgVerifier(pkg=x, |
417 |
- scheduler=sched_iface) |
418 |
+ scheduler=sched_iface, _pkg_path=filename) |
419 |
current_task = verifier |
420 |
verifier.start() |
421 |
if verifier.wait() != os.EX_OK: |
422 |
diff --git a/pym/_emerge/clear_caches.py b/pym/_emerge/clear_caches.py |
423 |
index 513df62..cb0db10 100644 |
424 |
--- a/pym/_emerge/clear_caches.py |
425 |
+++ b/pym/_emerge/clear_caches.py |
426 |
@@ -7,7 +7,6 @@ def clear_caches(trees): |
427 |
for d in trees.values(): |
428 |
d["porttree"].dbapi.melt() |
429 |
d["porttree"].dbapi._aux_cache.clear() |
430 |
- d["bintree"].dbapi._aux_cache.clear() |
431 |
d["bintree"].dbapi._clear_cache() |
432 |
if d["vartree"].dbapi._linkmap is None: |
433 |
# preserve-libs is entirely disabled |
434 |
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py |
435 |
index e8a3110..b6014a4 100644 |
436 |
--- a/pym/_emerge/depgraph.py |
437 |
+++ b/pym/_emerge/depgraph.py |
438 |
@@ -5747,11 +5747,11 @@ class depgraph(object): |
439 |
if want_reinstall and matched_packages: |
440 |
continue |
441 |
|
442 |
- # Ignore USE deps for the initial match since we want to |
443 |
- # ensure that updates aren't missed solely due to the user's |
444 |
- # USE configuration. |
445 |
+ # For unbuilt ebuilds, ignore USE deps for the initial |
446 |
+ # match since we want to ensure that updates aren't |
447 |
+ # missed solely due to the user's USE configuration. |
448 |
for pkg in self._iter_match_pkgs(root_config, pkg_type, |
449 |
- atom.without_use if atom.package else atom, |
450 |
+ atom.without_use if (atom.package and not built) else atom, |
451 |
onlydeps=onlydeps): |
452 |
if have_new_virt is True and pkg.cp != atom_cp: |
453 |
# pull in a new-style virtual instead |
454 |
@@ -6014,6 +6014,10 @@ class depgraph(object): |
455 |
pkg, {}).setdefault( |
456 |
"respect_use", set()).update( |
457 |
reinstall_for_flags) |
458 |
+ # Continue searching for a binary |
459 |
+ # package instance built with the |
460 |
+ # desired USE settings. |
461 |
+ continue |
462 |
break |
463 |
|
464 |
if (((installed and changed_deps) or |
465 |
@@ -6023,6 +6027,10 @@ class depgraph(object): |
466 |
self._dynamic_config.\ |
467 |
ignored_binaries.setdefault( |
468 |
pkg, {})["changed_deps"] = True |
469 |
+ # Continue searching for a binary |
470 |
+ # package instance built with the |
471 |
+ # desired USE settings. |
472 |
+ continue |
473 |
break |
474 |
|
475 |
# Compare current config to installed package |
476 |
diff --git a/pym/portage/const.py b/pym/portage/const.py |
477 |
index febdb4a..c7ecda2 100644 |
478 |
--- a/pym/portage/const.py |
479 |
+++ b/pym/portage/const.py |
480 |
@@ -122,6 +122,7 @@ EBUILD_PHASES = ( |
481 |
SUPPORTED_FEATURES = frozenset([ |
482 |
"assume-digests", |
483 |
"binpkg-logs", |
484 |
+ "binpkg-multi-instance", |
485 |
"buildpkg", |
486 |
"buildsyspkg", |
487 |
"candy", |
488 |
@@ -268,6 +269,7 @@ LIVE_ECLASSES = frozenset([ |
489 |
]) |
490 |
|
491 |
SUPPORTED_BINPKG_FORMATS = ("tar", "rpm") |
492 |
+SUPPORTED_XPAK_EXTENSIONS = (".tbz2", ".xpak") |
493 |
|
494 |
# Time formats used in various places like metadata.chk. |
495 |
TIMESTAMP_FORMAT = "%a, %d %b %Y %H:%M:%S +0000" # to be used with time.gmtime() |
496 |
diff --git a/pym/portage/dbapi/bintree.py b/pym/portage/dbapi/bintree.py |
497 |
index cd30b67..9bc5d98 100644 |
498 |
--- a/pym/portage/dbapi/bintree.py |
499 |
+++ b/pym/portage/dbapi/bintree.py |
500 |
@@ -17,14 +17,13 @@ portage.proxy.lazyimport.lazyimport(globals(), |
501 |
'portage.update:update_dbentries', |
502 |
'portage.util:atomic_ofstream,ensure_dirs,normalize_path,' + \ |
503 |
'writemsg,writemsg_stdout', |
504 |
- 'portage.util.listdir:listdir', |
505 |
'portage.util.path:first_existing', |
506 |
'portage.util._urlopen:urlopen@_urlopen', |
507 |
'portage.versions:best,catpkgsplit,catsplit,_pkg_str', |
508 |
) |
509 |
|
510 |
from portage.cache.mappings import slot_dict_class |
511 |
-from portage.const import CACHE_PATH |
512 |
+from portage.const import CACHE_PATH, SUPPORTED_XPAK_EXTENSIONS |
513 |
from portage.dbapi.virtual import fakedbapi |
514 |
from portage.dep import Atom, use_reduce, paren_enclose |
515 |
from portage.exception import AlarmSignal, InvalidData, InvalidPackageName, \ |
516 |
@@ -71,18 +70,26 @@ class bindbapi(fakedbapi): |
517 |
_known_keys = frozenset(list(fakedbapi._known_keys) + \ |
518 |
["CHOST", "repository", "USE"]) |
519 |
def __init__(self, mybintree=None, **kwargs): |
520 |
- fakedbapi.__init__(self, **kwargs) |
521 |
+ # Always enable multi_instance mode for bindbapi indexing. This |
522 |
+ # does not affect the local PKGDIR file layout, since that is |
523 |
+ # controlled independently by FEATURES=binpkg-multi-instance. |
524 |
+ # The multi_instance mode is useful for the following reasons: |
525 |
+ # * binary packages with the same cpv from multiple binhosts |
526 |
+ # can be considered simultaneously |
527 |
+ # * if binpkg-multi-instance is disabled, it's still possible |
528 |
+ # to properly access a PKGDIR which has binpkg-multi-instance |
529 |
+ # layout (or mixed layout) |
530 |
+ fakedbapi.__init__(self, exclusive_slots=False, |
531 |
+ multi_instance=True, **kwargs) |
532 |
self.bintree = mybintree |
533 |
self.move_ent = mybintree.move_ent |
534 |
- self.cpvdict={} |
535 |
- self.cpdict={} |
536 |
# Selectively cache metadata in order to optimize dep matching. |
537 |
self._aux_cache_keys = set( |
538 |
- ["BUILD_TIME", "CHOST", "DEPEND", "EAPI", |
539 |
- "HDEPEND", "IUSE", "KEYWORDS", |
540 |
- "LICENSE", "PDEPEND", "PROPERTIES", "PROVIDE", |
541 |
- "RDEPEND", "repository", "RESTRICT", "SLOT", "USE", |
542 |
- "DEFINED_PHASES", "PROVIDES", "REQUIRES" |
543 |
+ ["BUILD_ID", "BUILD_TIME", "CHOST", "DEFINED_PHASES", |
544 |
+ "DEPEND", "EAPI", "HDEPEND", "IUSE", "KEYWORDS", |
545 |
+ "LICENSE", "MD5", "PDEPEND", "PROPERTIES", "PROVIDE", |
546 |
+ "PROVIDES", "RDEPEND", "repository", "REQUIRES", "RESTRICT", |
547 |
+ "SIZE", "SLOT", "USE", "_mtime_" |
548 |
]) |
549 |
self._aux_cache_slot_dict = slot_dict_class(self._aux_cache_keys) |
550 |
self._aux_cache = {} |
551 |
@@ -109,33 +116,49 @@ class bindbapi(fakedbapi): |
552 |
return fakedbapi.cpv_exists(self, cpv) |
553 |
|
554 |
def cpv_inject(self, cpv, **kwargs): |
555 |
- self._aux_cache.pop(cpv, None) |
556 |
- fakedbapi.cpv_inject(self, cpv, **kwargs) |
557 |
+ if not self.bintree.populated: |
558 |
+ self.bintree.populate() |
559 |
+ fakedbapi.cpv_inject(self, cpv, |
560 |
+ metadata=cpv._metadata, **kwargs) |
561 |
|
562 |
def cpv_remove(self, cpv): |
563 |
- self._aux_cache.pop(cpv, None) |
564 |
+ if not self.bintree.populated: |
565 |
+ self.bintree.populate() |
566 |
fakedbapi.cpv_remove(self, cpv) |
567 |
|
568 |
def aux_get(self, mycpv, wants, myrepo=None): |
569 |
if self.bintree and not self.bintree.populated: |
570 |
self.bintree.populate() |
571 |
- cache_me = False |
572 |
+ # Support plain string for backward compatibility with API |
573 |
+ # consumers (including portageq, which passes in a cpv from |
574 |
+ # a command-line argument). |
575 |
+ instance_key = self._instance_key(mycpv, |
576 |
+ support_string=True) |
577 |
if not self._known_keys.intersection( |
578 |
wants).difference(self._aux_cache_keys): |
579 |
- aux_cache = self._aux_cache.get(mycpv) |
580 |
+ aux_cache = self.cpvdict[instance_key] |
581 |
if aux_cache is not None: |
582 |
return [aux_cache.get(x, "") for x in wants] |
583 |
- cache_me = True |
584 |
mysplit = mycpv.split("/") |
585 |
mylist = [] |
586 |
tbz2name = mysplit[1]+".tbz2" |
587 |
if not self.bintree._remotepkgs or \ |
588 |
not self.bintree.isremote(mycpv): |
589 |
- tbz2_path = self.bintree.getname(mycpv) |
590 |
- if not os.path.exists(tbz2_path): |
591 |
+ try: |
592 |
+ tbz2_path = self.bintree._pkg_paths[instance_key] |
593 |
+ except KeyError: |
594 |
+ raise KeyError(mycpv) |
595 |
+ tbz2_path = os.path.join(self.bintree.pkgdir, tbz2_path) |
596 |
+ try: |
597 |
+ st = os.lstat(tbz2_path) |
598 |
+ except OSError: |
599 |
raise KeyError(mycpv) |
600 |
metadata_bytes = portage.xpak.tbz2(tbz2_path).get_data() |
601 |
def getitem(k): |
602 |
+ if k == "_mtime_": |
603 |
+ return _unicode(st[stat.ST_MTIME]) |
604 |
+ elif k == "SIZE": |
605 |
+ return _unicode(st.st_size) |
606 |
v = metadata_bytes.get(_unicode_encode(k, |
607 |
encoding=_encodings['repo.content'], |
608 |
errors='backslashreplace')) |
609 |
@@ -144,11 +167,9 @@ class bindbapi(fakedbapi): |
610 |
encoding=_encodings['repo.content'], errors='replace') |
611 |
return v |
612 |
else: |
613 |
- getitem = self.bintree._remotepkgs[mycpv].get |
614 |
+ getitem = self.cpvdict[instance_key].get |
615 |
mydata = {} |
616 |
mykeys = wants |
617 |
- if cache_me: |
618 |
- mykeys = self._aux_cache_keys.union(wants) |
619 |
for x in mykeys: |
620 |
myval = getitem(x) |
621 |
# myval is None if the key doesn't exist |
622 |
@@ -159,16 +180,24 @@ class bindbapi(fakedbapi): |
623 |
if not mydata.setdefault('EAPI', '0'): |
624 |
mydata['EAPI'] = '0' |
625 |
|
626 |
- if cache_me: |
627 |
- aux_cache = self._aux_cache_slot_dict() |
628 |
- for x in self._aux_cache_keys: |
629 |
- aux_cache[x] = mydata.get(x, '') |
630 |
- self._aux_cache[mycpv] = aux_cache |
631 |
return [mydata.get(x, '') for x in wants] |
632 |
|
633 |
def aux_update(self, cpv, values): |
634 |
if not self.bintree.populated: |
635 |
self.bintree.populate() |
636 |
+ build_id = None |
637 |
+ try: |
638 |
+ build_id = cpv.build_id |
639 |
+ except AttributeError: |
640 |
+ if self.bintree._multi_instance: |
641 |
+ # The cpv.build_id attribute is required if we are in |
642 |
+ # multi-instance mode, since otherwise we won't know |
643 |
+ # which instance to update. |
644 |
+ raise |
645 |
+ else: |
646 |
+ cpv = self._instance_key(cpv, support_string=True)[0] |
647 |
+ build_id = cpv.build_id |
648 |
+ |
649 |
tbz2path = self.bintree.getname(cpv) |
650 |
if not os.path.exists(tbz2path): |
651 |
raise KeyError(cpv) |
652 |
@@ -187,7 +216,7 @@ class bindbapi(fakedbapi): |
653 |
del mydata[k] |
654 |
mytbz2.recompose_mem(portage.xpak.xpak_mem(mydata)) |
655 |
# inject will clear stale caches via cpv_inject. |
656 |
- self.bintree.inject(cpv) |
657 |
+ self.bintree.inject(cpv, filename=tbz2path) |
658 |
|
659 |
def cp_list(self, *pargs, **kwargs): |
660 |
if not self.bintree.populated: |
661 |
@@ -219,7 +248,7 @@ class bindbapi(fakedbapi): |
662 |
if not self.bintree.isremote(pkg): |
663 |
pass |
664 |
else: |
665 |
- metadata = self.bintree._remotepkgs[pkg] |
666 |
+ metadata = self.bintree._remotepkgs[self._instance_key(pkg)] |
667 |
try: |
668 |
size = int(metadata["SIZE"]) |
669 |
except KeyError: |
670 |
@@ -300,6 +329,13 @@ class binarytree(object): |
671 |
|
672 |
if True: |
673 |
self.pkgdir = normalize_path(pkgdir) |
674 |
+ # NOTE: Event if binpkg-multi-instance is disabled, it's |
675 |
+ # still possible to access a PKGDIR which uses the |
676 |
+ # binpkg-multi-instance layout (or mixed layout). |
677 |
+ self._multi_instance = ("binpkg-multi-instance" in |
678 |
+ settings.features) |
679 |
+ if self._multi_instance: |
680 |
+ self._allocate_filename = self._allocate_filename_multi |
681 |
self.dbapi = bindbapi(self, settings=settings) |
682 |
self.update_ents = self.dbapi.update_ents |
683 |
self.move_slot_ent = self.dbapi.move_slot_ent |
684 |
@@ -310,7 +346,6 @@ class binarytree(object): |
685 |
self.invalids = [] |
686 |
self.settings = settings |
687 |
self._pkg_paths = {} |
688 |
- self._pkgindex_uri = {} |
689 |
self._populating = False |
690 |
self._all_directory = os.path.isdir( |
691 |
os.path.join(self.pkgdir, "All")) |
692 |
@@ -318,12 +353,14 @@ class binarytree(object): |
693 |
self._pkgindex_hashes = ["MD5","SHA1"] |
694 |
self._pkgindex_file = os.path.join(self.pkgdir, "Packages") |
695 |
self._pkgindex_keys = self.dbapi._aux_cache_keys.copy() |
696 |
- self._pkgindex_keys.update(["CPV", "MTIME", "SIZE"]) |
697 |
+ self._pkgindex_keys.update(["CPV", "SIZE"]) |
698 |
self._pkgindex_aux_keys = \ |
699 |
- ["BUILD_TIME", "CHOST", "DEPEND", "DESCRIPTION", "EAPI", |
700 |
- "HDEPEND", "IUSE", "KEYWORDS", "LICENSE", "PDEPEND", "PROPERTIES", |
701 |
- "PROVIDE", "RESTRICT", "RDEPEND", "repository", "SLOT", "USE", "DEFINED_PHASES", |
702 |
- "BASE_URI", "PROVIDES", "REQUIRES"] |
703 |
+ ["BASE_URI", "BUILD_ID", "BUILD_TIME", "CHOST", |
704 |
+ "DEFINED_PHASES", "DEPEND", "DESCRIPTION", "EAPI", |
705 |
+ "HDEPEND", "IUSE", "KEYWORDS", "LICENSE", "PDEPEND", |
706 |
+ "PKGINDEX_URI", "PROPERTIES", "PROVIDE", "PROVIDES", |
707 |
+ "RDEPEND", "repository", "REQUIRES", "RESTRICT", |
708 |
+ "SIZE", "SLOT", "USE"] |
709 |
self._pkgindex_aux_keys = list(self._pkgindex_aux_keys) |
710 |
self._pkgindex_use_evaluated_keys = \ |
711 |
("DEPEND", "HDEPEND", "LICENSE", "RDEPEND", |
712 |
@@ -336,6 +373,7 @@ class binarytree(object): |
713 |
"USE_EXPAND", "USE_EXPAND_HIDDEN", "USE_EXPAND_IMPLICIT", |
714 |
"USE_EXPAND_UNPREFIXED"]) |
715 |
self._pkgindex_default_pkg_data = { |
716 |
+ "BUILD_ID" : "", |
717 |
"BUILD_TIME" : "", |
718 |
"DEFINED_PHASES" : "", |
719 |
"DEPEND" : "", |
720 |
@@ -365,6 +403,7 @@ class binarytree(object): |
721 |
|
722 |
self._pkgindex_translated_keys = ( |
723 |
("DESCRIPTION" , "DESC"), |
724 |
+ ("_mtime_" , "MTIME"), |
725 |
("repository" , "REPO"), |
726 |
) |
727 |
|
728 |
@@ -455,16 +494,21 @@ class binarytree(object): |
729 |
mytbz2.recompose_mem(portage.xpak.xpak_mem(mydata)) |
730 |
|
731 |
self.dbapi.cpv_remove(mycpv) |
732 |
- del self._pkg_paths[mycpv] |
733 |
+ del self._pkg_paths[self.dbapi._instance_key(mycpv)] |
734 |
+ metadata = self.dbapi._aux_cache_slot_dict() |
735 |
+ for k in self.dbapi._aux_cache_keys: |
736 |
+ v = mydata.get(_unicode_encode(k)) |
737 |
+ if v is not None: |
738 |
+ v = _unicode_decode(v) |
739 |
+ metadata[k] = " ".join(v.split()) |
740 |
+ mynewcpv = _pkg_str(mynewcpv, metadata=metadata) |
741 |
new_path = self.getname(mynewcpv) |
742 |
- self._pkg_paths[mynewcpv] = os.path.join( |
743 |
+ self._pkg_paths[ |
744 |
+ self.dbapi._instance_key(mynewcpv)] = os.path.join( |
745 |
*new_path.split(os.path.sep)[-2:]) |
746 |
if new_path != mytbz2: |
747 |
self._ensure_dir(os.path.dirname(new_path)) |
748 |
_movefile(tbz2path, new_path, mysettings=self.settings) |
749 |
- self._remove_symlink(mycpv) |
750 |
- if new_path.split(os.path.sep)[-2] == "All": |
751 |
- self._create_symlink(mynewcpv) |
752 |
self.inject(mynewcpv) |
753 |
|
754 |
return moves |
755 |
@@ -645,55 +689,63 @@ class binarytree(object): |
756 |
# prior to performing package moves since it only wants to |
757 |
# operate on local packages (getbinpkgs=0). |
758 |
self._remotepkgs = None |
759 |
- self.dbapi._clear_cache() |
760 |
- self.dbapi._aux_cache.clear() |
761 |
+ self.dbapi.clear() |
762 |
+ _instance_key = self.dbapi._instance_key |
763 |
if True: |
764 |
pkg_paths = {} |
765 |
self._pkg_paths = pkg_paths |
766 |
- dirs = listdir(self.pkgdir, dirsonly=True, EmptyOnError=True) |
767 |
- if "All" in dirs: |
768 |
- dirs.remove("All") |
769 |
- dirs.sort() |
770 |
- dirs.insert(0, "All") |
771 |
+ dir_files = {} |
772 |
+ for parent, dir_names, file_names in os.walk(self.pkgdir): |
773 |
+ relative_parent = parent[len(self.pkgdir)+1:] |
774 |
+ dir_files[relative_parent] = file_names |
775 |
+ |
776 |
pkgindex = self._load_pkgindex() |
777 |
- pf_index = None |
778 |
if not self._pkgindex_version_supported(pkgindex): |
779 |
pkgindex = self._new_pkgindex() |
780 |
header = pkgindex.header |
781 |
metadata = {} |
782 |
+ basename_index = {} |
783 |
for d in pkgindex.packages: |
784 |
- metadata[d["CPV"]] = d |
785 |
+ cpv = _pkg_str(d["CPV"], metadata=d, |
786 |
+ settings=self.settings) |
787 |
+ d["CPV"] = cpv |
788 |
+ metadata[_instance_key(cpv)] = d |
789 |
+ path = d.get("PATH") |
790 |
+ if not path: |
791 |
+ path = cpv + ".tbz2" |
792 |
+ basename = os.path.basename(path) |
793 |
+ basename_index.setdefault(basename, []).append(d) |
794 |
+ |
795 |
update_pkgindex = False |
796 |
- for mydir in dirs: |
797 |
- for myfile in listdir(os.path.join(self.pkgdir, mydir)): |
798 |
- if not myfile.endswith(".tbz2"): |
799 |
+ for mydir, file_names in dir_files.items(): |
800 |
+ try: |
801 |
+ mydir = _unicode_decode(mydir, |
802 |
+ encoding=_encodings["fs"], errors="strict") |
803 |
+ except UnicodeDecodeError: |
804 |
+ continue |
805 |
+ for myfile in file_names: |
806 |
+ try: |
807 |
+ myfile = _unicode_decode(myfile, |
808 |
+ encoding=_encodings["fs"], errors="strict") |
809 |
+ except UnicodeDecodeError: |
810 |
+ continue |
811 |
+ if not myfile.endswith(SUPPORTED_XPAK_EXTENSIONS): |
812 |
continue |
813 |
mypath = os.path.join(mydir, myfile) |
814 |
full_path = os.path.join(self.pkgdir, mypath) |
815 |
s = os.lstat(full_path) |
816 |
- if stat.S_ISLNK(s.st_mode): |
817 |
+ |
818 |
+ if not stat.S_ISREG(s.st_mode): |
819 |
continue |
820 |
|
821 |
# Validate data from the package index and try to avoid |
822 |
# reading the xpak if possible. |
823 |
- if mydir != "All": |
824 |
- possibilities = None |
825 |
- d = metadata.get(mydir+"/"+myfile[:-5]) |
826 |
- if d: |
827 |
- possibilities = [d] |
828 |
- else: |
829 |
- if pf_index is None: |
830 |
- pf_index = {} |
831 |
- for mycpv in metadata: |
832 |
- mycat, mypf = catsplit(mycpv) |
833 |
- pf_index.setdefault( |
834 |
- mypf, []).append(metadata[mycpv]) |
835 |
- possibilities = pf_index.get(myfile[:-5]) |
836 |
+ possibilities = basename_index.get(myfile) |
837 |
if possibilities: |
838 |
match = None |
839 |
for d in possibilities: |
840 |
try: |
841 |
- if long(d["MTIME"]) != s[stat.ST_MTIME]: |
842 |
+ if long(d["_mtime_"]) != s[stat.ST_MTIME]: |
843 |
continue |
844 |
except (KeyError, ValueError): |
845 |
continue |
846 |
@@ -707,15 +759,14 @@ class binarytree(object): |
847 |
break |
848 |
if match: |
849 |
mycpv = match["CPV"] |
850 |
- if mycpv in pkg_paths: |
851 |
- # discard duplicates (All/ is preferred) |
852 |
- continue |
853 |
- mycpv = _pkg_str(mycpv) |
854 |
- pkg_paths[mycpv] = mypath |
855 |
+ instance_key = _instance_key(mycpv) |
856 |
+ pkg_paths[instance_key] = mypath |
857 |
# update the path if the package has been moved |
858 |
oldpath = d.get("PATH") |
859 |
if oldpath and oldpath != mypath: |
860 |
update_pkgindex = True |
861 |
+ # Omit PATH if it is the default path for |
862 |
+ # the current Packages format version. |
863 |
if mypath != mycpv + ".tbz2": |
864 |
d["PATH"] = mypath |
865 |
if not oldpath: |
866 |
@@ -725,11 +776,6 @@ class binarytree(object): |
867 |
if oldpath: |
868 |
update_pkgindex = True |
869 |
self.dbapi.cpv_inject(mycpv) |
870 |
- if not self.dbapi._aux_cache_keys.difference(d): |
871 |
- aux_cache = self.dbapi._aux_cache_slot_dict() |
872 |
- for k in self.dbapi._aux_cache_keys: |
873 |
- aux_cache[k] = d[k] |
874 |
- self.dbapi._aux_cache[mycpv] = aux_cache |
875 |
continue |
876 |
if not os.access(full_path, os.R_OK): |
877 |
writemsg(_("!!! Permission denied to read " \ |
878 |
@@ -737,13 +783,12 @@ class binarytree(object): |
879 |
noiselevel=-1) |
880 |
self.invalids.append(myfile[:-5]) |
881 |
continue |
882 |
- metadata_bytes = portage.xpak.tbz2(full_path).get_data() |
883 |
- mycat = _unicode_decode(metadata_bytes.get(b"CATEGORY", ""), |
884 |
- encoding=_encodings['repo.content'], errors='replace') |
885 |
- mypf = _unicode_decode(metadata_bytes.get(b"PF", ""), |
886 |
- encoding=_encodings['repo.content'], errors='replace') |
887 |
- slot = _unicode_decode(metadata_bytes.get(b"SLOT", ""), |
888 |
- encoding=_encodings['repo.content'], errors='replace') |
889 |
+ pkg_metadata = self._read_metadata(full_path, s, |
890 |
+ keys=chain(self.dbapi._aux_cache_keys, |
891 |
+ ("PF", "CATEGORY"))) |
892 |
+ mycat = pkg_metadata.get("CATEGORY", "") |
893 |
+ mypf = pkg_metadata.get("PF", "") |
894 |
+ slot = pkg_metadata.get("SLOT", "") |
895 |
mypkg = myfile[:-5] |
896 |
if not mycat or not mypf or not slot: |
897 |
#old-style or corrupt package |
898 |
@@ -767,16 +812,51 @@ class binarytree(object): |
899 |
writemsg("!!! %s\n" % line, noiselevel=-1) |
900 |
self.invalids.append(mypkg) |
901 |
continue |
902 |
- mycat = mycat.strip() |
903 |
- slot = slot.strip() |
904 |
- if mycat != mydir and mydir != "All": |
905 |
+ |
906 |
+ multi_instance = False |
907 |
+ invalid_name = False |
908 |
+ build_id = None |
909 |
+ if myfile.endswith(".xpak"): |
910 |
+ multi_instance = True |
911 |
+ build_id = self._parse_build_id(myfile) |
912 |
+ if build_id < 1: |
913 |
+ invalid_name = True |
914 |
+ elif myfile != "%s-%s.xpak" % ( |
915 |
+ mypf, build_id): |
916 |
+ invalid_name = True |
917 |
+ else: |
918 |
+ mypkg = mypkg[:-len(str(build_id))-1] |
919 |
+ elif myfile != mypf + ".tbz2": |
920 |
+ invalid_name = True |
921 |
+ |
922 |
+ if invalid_name: |
923 |
+ writemsg(_("\n!!! Binary package name is " |
924 |
+ "invalid: '%s'\n") % full_path, |
925 |
+ noiselevel=-1) |
926 |
+ continue |
927 |
+ |
928 |
+ if pkg_metadata.get("BUILD_ID"): |
929 |
+ try: |
930 |
+ build_id = long(pkg_metadata["BUILD_ID"]) |
931 |
+ except ValueError: |
932 |
+ writemsg(_("!!! Binary package has " |
933 |
+ "invalid BUILD_ID: '%s'\n") % |
934 |
+ full_path, noiselevel=-1) |
935 |
+ continue |
936 |
+ else: |
937 |
+ build_id = None |
938 |
+ |
939 |
+ if multi_instance: |
940 |
+ name_split = catpkgsplit("%s/%s" % |
941 |
+ (mycat, mypf)) |
942 |
+ if (name_split is None or |
943 |
+ tuple(catsplit(mydir)) != name_split[:2]): |
944 |
+ continue |
945 |
+ elif mycat != mydir and mydir != "All": |
946 |
continue |
947 |
if mypkg != mypf.strip(): |
948 |
continue |
949 |
mycpv = mycat + "/" + mypkg |
950 |
- if mycpv in pkg_paths: |
951 |
- # All is first, so it's preferred. |
952 |
- continue |
953 |
if not self.dbapi._category_re.match(mycat): |
954 |
writemsg(_("!!! Binary package has an " \ |
955 |
"unrecognized category: '%s'\n") % full_path, |
956 |
@@ -786,14 +866,23 @@ class binarytree(object): |
957 |
(mycpv, self.settings["PORTAGE_CONFIGROOT"]), |
958 |
noiselevel=-1) |
959 |
continue |
960 |
- mycpv = _pkg_str(mycpv) |
961 |
- pkg_paths[mycpv] = mypath |
962 |
+ if build_id is not None: |
963 |
+ pkg_metadata["BUILD_ID"] = _unicode(build_id) |
964 |
+ pkg_metadata["SIZE"] = _unicode(s.st_size) |
965 |
+ # Discard items used only for validation above. |
966 |
+ pkg_metadata.pop("CATEGORY") |
967 |
+ pkg_metadata.pop("PF") |
968 |
+ mycpv = _pkg_str(mycpv, |
969 |
+ metadata=self.dbapi._aux_cache_slot_dict( |
970 |
+ pkg_metadata)) |
971 |
+ pkg_paths[_instance_key(mycpv)] = mypath |
972 |
self.dbapi.cpv_inject(mycpv) |
973 |
update_pkgindex = True |
974 |
- d = metadata.get(mycpv, {}) |
975 |
+ d = metadata.get(_instance_key(mycpv), |
976 |
+ pkgindex._pkg_slot_dict()) |
977 |
if d: |
978 |
try: |
979 |
- if long(d["MTIME"]) != s[stat.ST_MTIME]: |
980 |
+ if long(d["_mtime_"]) != s[stat.ST_MTIME]: |
981 |
d.clear() |
982 |
except (KeyError, ValueError): |
983 |
d.clear() |
984 |
@@ -804,36 +893,30 @@ class binarytree(object): |
985 |
except (KeyError, ValueError): |
986 |
d.clear() |
987 |
|
988 |
+ for k in self._pkgindex_allowed_pkg_keys: |
989 |
+ v = pkg_metadata.get(k) |
990 |
+ if v is not None: |
991 |
+ d[k] = v |
992 |
d["CPV"] = mycpv |
993 |
- d["SLOT"] = slot |
994 |
- d["MTIME"] = _unicode(s[stat.ST_MTIME]) |
995 |
- d["SIZE"] = _unicode(s.st_size) |
996 |
|
997 |
- d.update(zip(self._pkgindex_aux_keys, |
998 |
- self.dbapi.aux_get(mycpv, self._pkgindex_aux_keys))) |
999 |
try: |
1000 |
self._eval_use_flags(mycpv, d) |
1001 |
except portage.exception.InvalidDependString: |
1002 |
writemsg(_("!!! Invalid binary package: '%s'\n") % \ |
1003 |
self.getname(mycpv), noiselevel=-1) |
1004 |
self.dbapi.cpv_remove(mycpv) |
1005 |
- del pkg_paths[mycpv] |
1006 |
+ del pkg_paths[_instance_key(mycpv)] |
1007 |
|
1008 |
# record location if it's non-default |
1009 |
if mypath != mycpv + ".tbz2": |
1010 |
d["PATH"] = mypath |
1011 |
else: |
1012 |
d.pop("PATH", None) |
1013 |
- metadata[mycpv] = d |
1014 |
- if not self.dbapi._aux_cache_keys.difference(d): |
1015 |
- aux_cache = self.dbapi._aux_cache_slot_dict() |
1016 |
- for k in self.dbapi._aux_cache_keys: |
1017 |
- aux_cache[k] = d[k] |
1018 |
- self.dbapi._aux_cache[mycpv] = aux_cache |
1019 |
+ metadata[_instance_key(mycpv)] = d |
1020 |
|
1021 |
- for cpv in list(metadata): |
1022 |
- if cpv not in pkg_paths: |
1023 |
- del metadata[cpv] |
1024 |
+ for instance_key in list(metadata): |
1025 |
+ if instance_key not in pkg_paths: |
1026 |
+ del metadata[instance_key] |
1027 |
|
1028 |
# Do not bother to write the Packages index if $PKGDIR/All/ exists |
1029 |
# since it will provide no benefit due to the need to read CATEGORY |
1030 |
@@ -1058,45 +1141,24 @@ class binarytree(object): |
1031 |
# The current user doesn't have permission to cache the |
1032 |
# file, but that's alright. |
1033 |
if pkgindex: |
1034 |
- # Organize remote package list as a cpv -> metadata map. |
1035 |
- remotepkgs = _pkgindex_cpv_map_latest_build(pkgindex) |
1036 |
remote_base_uri = pkgindex.header.get("URI", base_url) |
1037 |
- for cpv, remote_metadata in remotepkgs.items(): |
1038 |
- remote_metadata["BASE_URI"] = remote_base_uri |
1039 |
- self._pkgindex_uri[cpv] = url |
1040 |
- self._remotepkgs.update(remotepkgs) |
1041 |
- self._remote_has_index = True |
1042 |
- for cpv in remotepkgs: |
1043 |
+ for d in pkgindex.packages: |
1044 |
+ cpv = _pkg_str(d["CPV"], metadata=d, |
1045 |
+ settings=self.settings) |
1046 |
+ instance_key = _instance_key(cpv) |
1047 |
+ # Local package instances override remote instances |
1048 |
+ # with the same instance_key. |
1049 |
+ if instance_key in metadata: |
1050 |
+ continue |
1051 |
+ |
1052 |
+ d["CPV"] = cpv |
1053 |
+ d["BASE_URI"] = remote_base_uri |
1054 |
+ d["PKGINDEX_URI"] = url |
1055 |
+ self._remotepkgs[instance_key] = d |
1056 |
+ metadata[instance_key] = d |
1057 |
self.dbapi.cpv_inject(cpv) |
1058 |
- if True: |
1059 |
- # Remote package instances override local package |
1060 |
- # if they are not identical. |
1061 |
- hash_names = ["SIZE"] + self._pkgindex_hashes |
1062 |
- for cpv, local_metadata in metadata.items(): |
1063 |
- remote_metadata = self._remotepkgs.get(cpv) |
1064 |
- if remote_metadata is None: |
1065 |
- continue |
1066 |
- # Use digests to compare identity. |
1067 |
- identical = True |
1068 |
- for hash_name in hash_names: |
1069 |
- local_value = local_metadata.get(hash_name) |
1070 |
- if local_value is None: |
1071 |
- continue |
1072 |
- remote_value = remote_metadata.get(hash_name) |
1073 |
- if remote_value is None: |
1074 |
- continue |
1075 |
- if local_value != remote_value: |
1076 |
- identical = False |
1077 |
- break |
1078 |
- if identical: |
1079 |
- del self._remotepkgs[cpv] |
1080 |
- else: |
1081 |
- # Override the local package in the aux_get cache. |
1082 |
- self.dbapi._aux_cache[cpv] = remote_metadata |
1083 |
- else: |
1084 |
- # Local package instances override remote instances. |
1085 |
- for cpv in metadata: |
1086 |
- self._remotepkgs.pop(cpv, None) |
1087 |
+ |
1088 |
+ self._remote_has_index = True |
1089 |
|
1090 |
self.populated=1 |
1091 |
|
1092 |
@@ -1108,7 +1170,8 @@ class binarytree(object): |
1093 |
@param filename: File path of the package to inject, or None if it's |
1094 |
already in the location returned by getname() |
1095 |
@type filename: string |
1096 |
- @rtype: None |
1097 |
+ @rtype: _pkg_str or None |
1098 |
+ @return: A _pkg_str instance on success, or None on failure. |
1099 |
""" |
1100 |
mycat, mypkg = catsplit(cpv) |
1101 |
if not self.populated: |
1102 |
@@ -1126,24 +1189,44 @@ class binarytree(object): |
1103 |
writemsg(_("!!! Binary package does not exist: '%s'\n") % full_path, |
1104 |
noiselevel=-1) |
1105 |
return |
1106 |
- mytbz2 = portage.xpak.tbz2(full_path) |
1107 |
- slot = mytbz2.getfile("SLOT") |
1108 |
+ metadata = self._read_metadata(full_path, s) |
1109 |
+ slot = metadata.get("SLOT") |
1110 |
+ try: |
1111 |
+ self._eval_use_flags(cpv, metadata) |
1112 |
+ except portage.exception.InvalidDependString: |
1113 |
+ slot = None |
1114 |
if slot is None: |
1115 |
writemsg(_("!!! Invalid binary package: '%s'\n") % full_path, |
1116 |
noiselevel=-1) |
1117 |
return |
1118 |
- slot = slot.strip() |
1119 |
- self.dbapi.cpv_inject(cpv) |
1120 |
+ |
1121 |
+ fetched = False |
1122 |
+ try: |
1123 |
+ build_id = cpv.build_id |
1124 |
+ except AttributeError: |
1125 |
+ build_id = None |
1126 |
+ else: |
1127 |
+ instance_key = self.dbapi._instance_key(cpv) |
1128 |
+ if instance_key in self.dbapi.cpvdict: |
1129 |
+ # This means we've been called by aux_update (or |
1130 |
+ # similar). The instance key typically changes (due to |
1131 |
+ # file modification), so we need to discard existing |
1132 |
+ # instance key references. |
1133 |
+ self.dbapi.cpv_remove(cpv) |
1134 |
+ self._pkg_paths.pop(instance_key, None) |
1135 |
+ if self._remotepkgs is not None: |
1136 |
+ fetched = self._remotepkgs.pop(instance_key, None) |
1137 |
+ |
1138 |
+ cpv = _pkg_str(cpv, metadata=metadata, settings=self.settings) |
1139 |
|
1140 |
# Reread the Packages index (in case it's been changed by another |
1141 |
# process) and then updated it, all while holding a lock. |
1142 |
pkgindex_lock = None |
1143 |
- created_symlink = False |
1144 |
try: |
1145 |
pkgindex_lock = lockfile(self._pkgindex_file, |
1146 |
wantnewlockfile=1) |
1147 |
if filename is not None: |
1148 |
- new_filename = self.getname(cpv) |
1149 |
+ new_filename = self.getname(cpv, allocate_new=True) |
1150 |
try: |
1151 |
samefile = os.path.samefile(filename, new_filename) |
1152 |
except OSError: |
1153 |
@@ -1153,54 +1236,31 @@ class binarytree(object): |
1154 |
_movefile(filename, new_filename, mysettings=self.settings) |
1155 |
full_path = new_filename |
1156 |
|
1157 |
- self._file_permissions(full_path) |
1158 |
+ basename = os.path.basename(full_path) |
1159 |
+ pf = catsplit(cpv)[1] |
1160 |
+ if (build_id is None and not fetched and |
1161 |
+ basename.endswith(".xpak")): |
1162 |
+ # Apply the newly assigned BUILD_ID. This is intended |
1163 |
+ # to occur only for locally built packages. If the |
1164 |
+ # package was fetched, we want to preserve its |
1165 |
+ # attributes, so that we can later distinguish that it |
1166 |
+ # is identical to its remote counterpart. |
1167 |
+ build_id = self._parse_build_id(basename) |
1168 |
+ metadata["BUILD_ID"] = _unicode(build_id) |
1169 |
+ cpv = _pkg_str(cpv, metadata=metadata, |
1170 |
+ settings=self.settings) |
1171 |
+ binpkg = portage.xpak.tbz2(full_path) |
1172 |
+ binary_data = binpkg.get_data() |
1173 |
+ binary_data[b"BUILD_ID"] = _unicode_encode( |
1174 |
+ metadata["BUILD_ID"]) |
1175 |
+ binpkg.recompose_mem(portage.xpak.xpak_mem(binary_data)) |
1176 |
|
1177 |
- if self._all_directory and \ |
1178 |
- self.getname(cpv).split(os.path.sep)[-2] == "All": |
1179 |
- self._create_symlink(cpv) |
1180 |
- created_symlink = True |
1181 |
+ self._file_permissions(full_path) |
1182 |
pkgindex = self._load_pkgindex() |
1183 |
- |
1184 |
if not self._pkgindex_version_supported(pkgindex): |
1185 |
pkgindex = self._new_pkgindex() |
1186 |
|
1187 |
- # Discard remote metadata to ensure that _pkgindex_entry |
1188 |
- # gets the local metadata. This also updates state for future |
1189 |
- # isremote calls. |
1190 |
- if self._remotepkgs is not None: |
1191 |
- self._remotepkgs.pop(cpv, None) |
1192 |
- |
1193 |
- # Discard cached metadata to ensure that _pkgindex_entry |
1194 |
- # doesn't return stale metadata. |
1195 |
- self.dbapi._aux_cache.pop(cpv, None) |
1196 |
- |
1197 |
- try: |
1198 |
- d = self._pkgindex_entry(cpv) |
1199 |
- except portage.exception.InvalidDependString: |
1200 |
- writemsg(_("!!! Invalid binary package: '%s'\n") % \ |
1201 |
- self.getname(cpv), noiselevel=-1) |
1202 |
- self.dbapi.cpv_remove(cpv) |
1203 |
- del self._pkg_paths[cpv] |
1204 |
- return |
1205 |
- |
1206 |
- # If found, remove package(s) with duplicate path. |
1207 |
- path = d.get("PATH", "") |
1208 |
- for i in range(len(pkgindex.packages) - 1, -1, -1): |
1209 |
- d2 = pkgindex.packages[i] |
1210 |
- if path and path == d2.get("PATH"): |
1211 |
- # Handle path collisions in $PKGDIR/All |
1212 |
- # when CPV is not identical. |
1213 |
- del pkgindex.packages[i] |
1214 |
- elif cpv == d2.get("CPV"): |
1215 |
- if path == d2.get("PATH", ""): |
1216 |
- del pkgindex.packages[i] |
1217 |
- elif created_symlink and not d2.get("PATH", ""): |
1218 |
- # Delete entry for the package that was just |
1219 |
- # overwritten by a symlink to this package. |
1220 |
- del pkgindex.packages[i] |
1221 |
- |
1222 |
- pkgindex.packages.append(d) |
1223 |
- |
1224 |
+ d = self._inject_file(pkgindex, cpv, full_path) |
1225 |
self._update_pkgindex_header(pkgindex.header) |
1226 |
self._pkgindex_write(pkgindex) |
1227 |
|
1228 |
@@ -1208,6 +1268,73 @@ class binarytree(object): |
1229 |
if pkgindex_lock: |
1230 |
unlockfile(pkgindex_lock) |
1231 |
|
1232 |
+ # This is used to record BINPKGMD5 in the installed package |
1233 |
+ # database, for a package that has just been built. |
1234 |
+ cpv._metadata["MD5"] = d["MD5"] |
1235 |
+ |
1236 |
+ return cpv |
1237 |
+ |
1238 |
+ def _read_metadata(self, filename, st, keys=None): |
1239 |
+ if keys is None: |
1240 |
+ keys = self.dbapi._aux_cache_keys |
1241 |
+ metadata = self.dbapi._aux_cache_slot_dict() |
1242 |
+ else: |
1243 |
+ metadata = {} |
1244 |
+ binary_metadata = portage.xpak.tbz2(filename).get_data() |
1245 |
+ for k in keys: |
1246 |
+ if k == "_mtime_": |
1247 |
+ metadata[k] = _unicode(st[stat.ST_MTIME]) |
1248 |
+ elif k == "SIZE": |
1249 |
+ metadata[k] = _unicode(st.st_size) |
1250 |
+ else: |
1251 |
+ v = binary_metadata.get(_unicode_encode(k)) |
1252 |
+ if v is not None: |
1253 |
+ v = _unicode_decode(v) |
1254 |
+ metadata[k] = " ".join(v.split()) |
1255 |
+ metadata.setdefault("EAPI", "0") |
1256 |
+ return metadata |
1257 |
+ |
1258 |
+ def _inject_file(self, pkgindex, cpv, filename): |
1259 |
+ """ |
1260 |
+ Add a package to internal data structures, and add an |
1261 |
+ entry to the given pkgindex. |
1262 |
+ @param pkgindex: The PackageIndex instance to which an entry |
1263 |
+ will be added. |
1264 |
+ @type pkgindex: PackageIndex |
1265 |
+ @param cpv: A _pkg_str instance corresponding to the package |
1266 |
+ being injected. |
1267 |
+ @type cpv: _pkg_str |
1268 |
+ @param filename: Absolute file path of the package to inject. |
1269 |
+ @type filename: string |
1270 |
+ @rtype: dict |
1271 |
+ @return: A dict corresponding to the new entry which has been |
1272 |
+ added to pkgindex. This may be used to access the checksums |
1273 |
+ which have just been generated. |
1274 |
+ """ |
1275 |
+ # Update state for future isremote calls. |
1276 |
+ instance_key = self.dbapi._instance_key(cpv) |
1277 |
+ if self._remotepkgs is not None: |
1278 |
+ self._remotepkgs.pop(instance_key, None) |
1279 |
+ |
1280 |
+ self.dbapi.cpv_inject(cpv) |
1281 |
+ self._pkg_paths[instance_key] = filename[len(self.pkgdir)+1:] |
1282 |
+ d = self._pkgindex_entry(cpv) |
1283 |
+ |
1284 |
+ # If found, remove package(s) with duplicate path. |
1285 |
+ path = d.get("PATH", "") |
1286 |
+ for i in range(len(pkgindex.packages) - 1, -1, -1): |
1287 |
+ d2 = pkgindex.packages[i] |
1288 |
+ if path and path == d2.get("PATH"): |
1289 |
+ # Handle path collisions in $PKGDIR/All |
1290 |
+ # when CPV is not identical. |
1291 |
+ del pkgindex.packages[i] |
1292 |
+ elif cpv == d2.get("CPV"): |
1293 |
+ if path == d2.get("PATH", ""): |
1294 |
+ del pkgindex.packages[i] |
1295 |
+ |
1296 |
+ pkgindex.packages.append(d) |
1297 |
+ return d |
1298 |
+ |
1299 |
def _pkgindex_write(self, pkgindex): |
1300 |
contents = codecs.getwriter(_encodings['repo.content'])(io.BytesIO()) |
1301 |
pkgindex.write(contents) |
1302 |
@@ -1233,7 +1360,7 @@ class binarytree(object): |
1303 |
|
1304 |
def _pkgindex_entry(self, cpv): |
1305 |
""" |
1306 |
- Performs checksums and evaluates USE flag conditionals. |
1307 |
+ Performs checksums, and gets size and mtime via lstat. |
1308 |
Raises InvalidDependString if necessary. |
1309 |
@rtype: dict |
1310 |
@return: a dict containing entry for the give cpv. |
1311 |
@@ -1241,23 +1368,20 @@ class binarytree(object): |
1312 |
|
1313 |
pkg_path = self.getname(cpv) |
1314 |
|
1315 |
- d = dict(zip(self._pkgindex_aux_keys, |
1316 |
- self.dbapi.aux_get(cpv, self._pkgindex_aux_keys))) |
1317 |
- |
1318 |
+ d = dict(cpv._metadata.items()) |
1319 |
d.update(perform_multiple_checksums( |
1320 |
pkg_path, hashes=self._pkgindex_hashes)) |
1321 |
|
1322 |
d["CPV"] = cpv |
1323 |
- st = os.stat(pkg_path) |
1324 |
- d["MTIME"] = _unicode(st[stat.ST_MTIME]) |
1325 |
+ st = os.lstat(pkg_path) |
1326 |
+ d["_mtime_"] = _unicode(st[stat.ST_MTIME]) |
1327 |
d["SIZE"] = _unicode(st.st_size) |
1328 |
|
1329 |
- rel_path = self._pkg_paths[cpv] |
1330 |
+ rel_path = pkg_path[len(self.pkgdir)+1:] |
1331 |
# record location if it's non-default |
1332 |
if rel_path != cpv + ".tbz2": |
1333 |
d["PATH"] = rel_path |
1334 |
|
1335 |
- self._eval_use_flags(cpv, d) |
1336 |
return d |
1337 |
|
1338 |
def _new_pkgindex(self): |
1339 |
@@ -1311,15 +1435,17 @@ class binarytree(object): |
1340 |
return False |
1341 |
|
1342 |
def _eval_use_flags(self, cpv, metadata): |
1343 |
- use = frozenset(metadata["USE"].split()) |
1344 |
+ use = frozenset(metadata.get("USE", "").split()) |
1345 |
for k in self._pkgindex_use_evaluated_keys: |
1346 |
if k.endswith('DEPEND'): |
1347 |
token_class = Atom |
1348 |
else: |
1349 |
token_class = None |
1350 |
|
1351 |
+ deps = metadata.get(k) |
1352 |
+ if deps is None: |
1353 |
+ continue |
1354 |
try: |
1355 |
- deps = metadata[k] |
1356 |
deps = use_reduce(deps, uselist=use, token_class=token_class) |
1357 |
deps = paren_enclose(deps) |
1358 |
except portage.exception.InvalidDependString as e: |
1359 |
@@ -1349,46 +1475,129 @@ class binarytree(object): |
1360 |
return "" |
1361 |
return mymatch |
1362 |
|
1363 |
- def getname(self, pkgname): |
1364 |
- """Returns a file location for this package. The default location is |
1365 |
- ${PKGDIR}/All/${PF}.tbz2, but will be ${PKGDIR}/${CATEGORY}/${PF}.tbz2 |
1366 |
- in the rare event of a collision. The prevent_collision() method can |
1367 |
- be called to ensure that ${PKGDIR}/All/${PF}.tbz2 is available for a |
1368 |
- specific cpv.""" |
1369 |
+ def getname(self, cpv, allocate_new=None): |
1370 |
+ """Returns a file location for this package. |
1371 |
+ If cpv has both build_time and build_id attributes, then the |
1372 |
+ path to the specific corresponding instance is returned. |
1373 |
+ Otherwise, allocate a new path and return that. When allocating |
1374 |
+ a new path, behavior depends on the binpkg-multi-instance |
1375 |
+ FEATURES setting. |
1376 |
+ """ |
1377 |
if not self.populated: |
1378 |
self.populate() |
1379 |
- mycpv = pkgname |
1380 |
- mypath = self._pkg_paths.get(mycpv, None) |
1381 |
- if mypath: |
1382 |
- return os.path.join(self.pkgdir, mypath) |
1383 |
- mycat, mypkg = catsplit(mycpv) |
1384 |
- if self._all_directory: |
1385 |
- mypath = os.path.join("All", mypkg + ".tbz2") |
1386 |
- if mypath in self._pkg_paths.values(): |
1387 |
- mypath = os.path.join(mycat, mypkg + ".tbz2") |
1388 |
+ |
1389 |
+ try: |
1390 |
+ cpv.cp |
1391 |
+ except AttributeError: |
1392 |
+ cpv = _pkg_str(cpv) |
1393 |
+ |
1394 |
+ filename = None |
1395 |
+ if allocate_new: |
1396 |
+ filename = self._allocate_filename(cpv) |
1397 |
+ elif self._is_specific_instance(cpv): |
1398 |
+ instance_key = self.dbapi._instance_key(cpv) |
1399 |
+ path = self._pkg_paths.get(instance_key) |
1400 |
+ if path is not None: |
1401 |
+ filename = os.path.join(self.pkgdir, path) |
1402 |
+ |
1403 |
+ if filename is None and not allocate_new: |
1404 |
+ try: |
1405 |
+ instance_key = self.dbapi._instance_key(cpv, |
1406 |
+ support_string=True) |
1407 |
+ except KeyError: |
1408 |
+ pass |
1409 |
+ else: |
1410 |
+ filename = self._pkg_paths.get(instance_key) |
1411 |
+ if filename is not None: |
1412 |
+ filename = os.path.join(self.pkgdir, filename) |
1413 |
+ |
1414 |
+ if filename is None: |
1415 |
+ if self._multi_instance: |
1416 |
+ pf = catsplit(cpv)[1] |
1417 |
+ filename = "%s-%s.xpak" % ( |
1418 |
+ os.path.join(self.pkgdir, cpv.cp, pf), "1") |
1419 |
+ else: |
1420 |
+ filename = os.path.join(self.pkgdir, cpv + ".tbz2") |
1421 |
+ |
1422 |
+ return filename |
1423 |
+ |
1424 |
+ def _is_specific_instance(self, cpv): |
1425 |
+ specific = True |
1426 |
+ try: |
1427 |
+ build_time = cpv.build_time |
1428 |
+ build_id = cpv.build_id |
1429 |
+ except AttributeError: |
1430 |
+ specific = False |
1431 |
else: |
1432 |
- mypath = os.path.join(mycat, mypkg + ".tbz2") |
1433 |
- self._pkg_paths[mycpv] = mypath # cache for future lookups |
1434 |
- return os.path.join(self.pkgdir, mypath) |
1435 |
+ if build_time is None or build_id is None: |
1436 |
+ specific = False |
1437 |
+ return specific |
1438 |
+ |
1439 |
+ def _max_build_id(self, cpv): |
1440 |
+ max_build_id = 0 |
1441 |
+ for x in self.dbapi.cp_list(cpv.cp): |
1442 |
+ if (x == cpv and x.build_id is not None and |
1443 |
+ x.build_id > max_build_id): |
1444 |
+ max_build_id = x.build_id |
1445 |
+ return max_build_id |
1446 |
+ |
1447 |
+ def _allocate_filename(self, cpv): |
1448 |
+ return os.path.join(self.pkgdir, cpv + ".tbz2") |
1449 |
+ |
1450 |
+ def _allocate_filename_multi(self, cpv): |
1451 |
+ |
1452 |
+ # First, get the max build_id found when _populate was |
1453 |
+ # called. |
1454 |
+ max_build_id = self._max_build_id(cpv) |
1455 |
+ |
1456 |
+ # A new package may have been added concurrently since the |
1457 |
+ # last _populate call, so use increment build_id until |
1458 |
+ # we locate an unused id. |
1459 |
+ pf = catsplit(cpv)[1] |
1460 |
+ build_id = max_build_id + 1 |
1461 |
+ |
1462 |
+ while True: |
1463 |
+ filename = "%s-%s.xpak" % ( |
1464 |
+ os.path.join(self.pkgdir, cpv.cp, pf), build_id) |
1465 |
+ if os.path.exists(filename): |
1466 |
+ build_id += 1 |
1467 |
+ else: |
1468 |
+ return filename |
1469 |
+ |
1470 |
+ @staticmethod |
1471 |
+ def _parse_build_id(filename): |
1472 |
+ build_id = -1 |
1473 |
+ hyphen = filename.rfind("-", 0, -6) |
1474 |
+ if hyphen != -1: |
1475 |
+ build_id = filename[hyphen+1:-5] |
1476 |
+ try: |
1477 |
+ build_id = long(build_id) |
1478 |
+ except ValueError: |
1479 |
+ pass |
1480 |
+ return build_id |
1481 |
|
1482 |
def isremote(self, pkgname): |
1483 |
"""Returns true if the package is kept remotely and it has not been |
1484 |
downloaded (or it is only partially downloaded).""" |
1485 |
- if self._remotepkgs is None or pkgname not in self._remotepkgs: |
1486 |
+ if (self._remotepkgs is None or |
1487 |
+ self.dbapi._instance_key(pkgname) not in self._remotepkgs): |
1488 |
return False |
1489 |
# Presence in self._remotepkgs implies that it's remote. When a |
1490 |
# package is downloaded, state is updated by self.inject(). |
1491 |
return True |
1492 |
|
1493 |
- def get_pkgindex_uri(self, pkgname): |
1494 |
+ def get_pkgindex_uri(self, cpv): |
1495 |
"""Returns the URI to the Packages file for a given package.""" |
1496 |
- return self._pkgindex_uri.get(pkgname) |
1497 |
- |
1498 |
- |
1499 |
+ uri = None |
1500 |
+ metadata = self._remotepkgs.get(self.dbapi._instance_key(cpv)) |
1501 |
+ if metadata is not None: |
1502 |
+ uri = metadata["PKGINDEX_URI"] |
1503 |
+ return uri |
1504 |
|
1505 |
def gettbz2(self, pkgname): |
1506 |
"""Fetches the package from a remote site, if necessary. Attempts to |
1507 |
resume if the file appears to be partially downloaded.""" |
1508 |
+ instance_key = self.dbapi._instance_key(pkgname) |
1509 |
tbz2_path = self.getname(pkgname) |
1510 |
tbz2name = os.path.basename(tbz2_path) |
1511 |
resume = False |
1512 |
@@ -1404,10 +1613,10 @@ class binarytree(object): |
1513 |
self._ensure_dir(mydest) |
1514 |
# urljoin doesn't work correctly with unrecognized protocols like sftp |
1515 |
if self._remote_has_index: |
1516 |
- rel_url = self._remotepkgs[pkgname].get("PATH") |
1517 |
+ rel_url = self._remotepkgs[instance_key].get("PATH") |
1518 |
if not rel_url: |
1519 |
rel_url = pkgname+".tbz2" |
1520 |
- remote_base_uri = self._remotepkgs[pkgname]["BASE_URI"] |
1521 |
+ remote_base_uri = self._remotepkgs[instance_key]["BASE_URI"] |
1522 |
url = remote_base_uri.rstrip("/") + "/" + rel_url.lstrip("/") |
1523 |
else: |
1524 |
url = self.settings["PORTAGE_BINHOST"].rstrip("/") + "/" + tbz2name |
1525 |
@@ -1450,15 +1659,19 @@ class binarytree(object): |
1526 |
except AttributeError: |
1527 |
cpv = pkg |
1528 |
|
1529 |
+ _instance_key = self.dbapi._instance_key |
1530 |
+ instance_key = _instance_key(cpv) |
1531 |
digests = {} |
1532 |
- metadata = None |
1533 |
- if self._remotepkgs is None or cpv not in self._remotepkgs: |
1534 |
+ metadata = (None if self._remotepkgs is None else |
1535 |
+ self._remotepkgs.get(instance_key)) |
1536 |
+ if metadata is None: |
1537 |
for d in self._load_pkgindex().packages: |
1538 |
- if d["CPV"] == cpv: |
1539 |
+ if (d["CPV"] == cpv and |
1540 |
+ instance_key == _instance_key(_pkg_str(d["CPV"], |
1541 |
+ metadata=d, settings=self.settings))): |
1542 |
metadata = d |
1543 |
break |
1544 |
- else: |
1545 |
- metadata = self._remotepkgs[cpv] |
1546 |
+ |
1547 |
if metadata is None: |
1548 |
return digests |
1549 |
|
1550 |
diff --git a/pym/portage/emaint/modules/binhost/binhost.py b/pym/portage/emaint/modules/binhost/binhost.py |
1551 |
index 1138a8c..cf1213e 100644 |
1552 |
--- a/pym/portage/emaint/modules/binhost/binhost.py |
1553 |
+++ b/pym/portage/emaint/modules/binhost/binhost.py |
1554 |
@@ -7,6 +7,7 @@ import stat |
1555 |
import portage |
1556 |
from portage import os |
1557 |
from portage.util import writemsg |
1558 |
+from portage.versions import _pkg_str |
1559 |
|
1560 |
import sys |
1561 |
|
1562 |
@@ -38,7 +39,7 @@ class BinhostHandler(object): |
1563 |
if size is None: |
1564 |
return True |
1565 |
|
1566 |
- mtime = data.get("MTIME") |
1567 |
+ mtime = data.get("_mtime_") |
1568 |
if mtime is None: |
1569 |
return True |
1570 |
|
1571 |
@@ -90,6 +91,7 @@ class BinhostHandler(object): |
1572 |
def fix(self, **kwargs): |
1573 |
onProgress = kwargs.get('onProgress', None) |
1574 |
bintree = self._bintree |
1575 |
+ _instance_key = bintree.dbapi._instance_key |
1576 |
cpv_all = self._bintree.dbapi.cpv_all() |
1577 |
cpv_all.sort() |
1578 |
missing = [] |
1579 |
@@ -98,16 +100,21 @@ class BinhostHandler(object): |
1580 |
onProgress(maxval, 0) |
1581 |
pkgindex = self._pkgindex |
1582 |
missing = [] |
1583 |
+ stale = [] |
1584 |
metadata = {} |
1585 |
for d in pkgindex.packages: |
1586 |
- metadata[d["CPV"]] = d |
1587 |
- |
1588 |
- for i, cpv in enumerate(cpv_all): |
1589 |
- d = metadata.get(cpv) |
1590 |
+ cpv = _pkg_str(d["CPV"], metadata=d, |
1591 |
+ settings=bintree.settings) |
1592 |
+ d["CPV"] = cpv |
1593 |
+ metadata[_instance_key(cpv)] = d |
1594 |
+ if not bintree.dbapi.cpv_exists(cpv): |
1595 |
+ stale.append(cpv) |
1596 |
+ |
1597 |
+ for cpv in cpv_all: |
1598 |
+ d = metadata.get(_instance_key(cpv)) |
1599 |
if not d or self._need_update(cpv, d): |
1600 |
missing.append(cpv) |
1601 |
|
1602 |
- stale = set(metadata).difference(cpv_all) |
1603 |
if missing or stale: |
1604 |
from portage import locks |
1605 |
pkgindex_lock = locks.lockfile( |
1606 |
@@ -121,31 +128,39 @@ class BinhostHandler(object): |
1607 |
pkgindex = bintree._load_pkgindex() |
1608 |
self._pkgindex = pkgindex |
1609 |
|
1610 |
+ # Recount stale/missing packages, with lock held. |
1611 |
+ missing = [] |
1612 |
+ stale = [] |
1613 |
metadata = {} |
1614 |
for d in pkgindex.packages: |
1615 |
- metadata[d["CPV"]] = d |
1616 |
- |
1617 |
- # Recount missing packages, with lock held. |
1618 |
- del missing[:] |
1619 |
- for i, cpv in enumerate(cpv_all): |
1620 |
- d = metadata.get(cpv) |
1621 |
+ cpv = _pkg_str(d["CPV"], metadata=d, |
1622 |
+ settings=bintree.settings) |
1623 |
+ d["CPV"] = cpv |
1624 |
+ metadata[_instance_key(cpv)] = d |
1625 |
+ if not bintree.dbapi.cpv_exists(cpv): |
1626 |
+ stale.append(cpv) |
1627 |
+ |
1628 |
+ for cpv in cpv_all: |
1629 |
+ d = metadata.get(_instance_key(cpv)) |
1630 |
if not d or self._need_update(cpv, d): |
1631 |
missing.append(cpv) |
1632 |
|
1633 |
maxval = len(missing) |
1634 |
for i, cpv in enumerate(missing): |
1635 |
+ d = bintree._pkgindex_entry(cpv) |
1636 |
try: |
1637 |
- metadata[cpv] = bintree._pkgindex_entry(cpv) |
1638 |
+ bintree._eval_use_flags(cpv, d) |
1639 |
except portage.exception.InvalidDependString: |
1640 |
writemsg("!!! Invalid binary package: '%s'\n" % \ |
1641 |
bintree.getname(cpv), noiselevel=-1) |
1642 |
+ else: |
1643 |
+ metadata[_instance_key(cpv)] = d |
1644 |
|
1645 |
if onProgress: |
1646 |
onProgress(maxval, i+1) |
1647 |
|
1648 |
- for cpv in set(metadata).difference( |
1649 |
- self._bintree.dbapi.cpv_all()): |
1650 |
- del metadata[cpv] |
1651 |
+ for cpv in stale: |
1652 |
+ del metadata[_instance_key(cpv)] |
1653 |
|
1654 |
# We've updated the pkgindex, so set it to |
1655 |
# repopulate when necessary. |
1656 |
-- |
1657 |
2.0.5 |