Gentoo Archives: gentoo-commits

From: "Michał Górny" <mgorny@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/portage:master commit in: lib/portage/dbapi/, lib/portage/package/ebuild/, ...
Date: Fri, 09 Sep 2022 10:16:39
Message-Id: 1662718566.fa901a6510c4a5f72dec6aad86db6fe7efd6e7b3.mgorny@gentoo
1 commit: fa901a6510c4a5f72dec6aad86db6fe7efd6e7b3
2 Author: Sheng Yu <syu.os <AT> protonmail <DOT> com>
3 AuthorDate: Tue Sep 6 18:38:27 2022 +0000
4 Commit: Michał Górny <mgorny <AT> gentoo <DOT> org>
5 CommitDate: Fri Sep 9 10:16:06 2022 +0000
6 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=fa901a65
7
8 Add BUILD_ID to metadata during binary packaging
9
10 Also create placeholder for new multi-instance package before actually
11 packaging to avoid race.
12
13 Signed-off-by: Sheng Yu <syu.os <AT> protonmail.com>
14 Closes: https://github.com/gentoo/portage/pull/893
15 Signed-off-by: Michał Górny <mgorny <AT> gentoo.org>
16
17 bin/gpkg-helper.py | 4 +-
18 bin/misc-functions.sh | 6 +-
19 bin/quickpkg | 2 +-
20 lib/_emerge/Binpkg.py | 2 +-
21 lib/_emerge/BinpkgFetcher.py | 27 ++--
22 lib/_emerge/BinpkgPrefetcher.py | 8 +-
23 lib/_emerge/EbuildBinpkg.py | 35 +++--
24 lib/_emerge/Scheduler.py | 2 +-
25 lib/portage/dbapi/bintree.py | 145 +++++++++++----------
26 .../package/ebuild/_config/special_env_vars.py | 1 +
27 lib/portage/package/ebuild/doebuild.py | 3 +-
28 11 files changed, 126 insertions(+), 109 deletions(-)
29
30 diff --git a/bin/gpkg-helper.py b/bin/gpkg-helper.py
31 index d45177f3e..4752c84ee 100755
32 --- a/bin/gpkg-helper.py
33 +++ b/bin/gpkg-helper.py
34 @@ -19,7 +19,7 @@ def command_compose(args):
35 sys.stderr.write("4 arguments are required, got %s\n" % len(args))
36 return 1
37
38 - cpv, binpkg_path, metadata_dir, image_dir = args
39 + basename, binpkg_path, metadata_dir, image_dir = args
40
41 if not os.path.isdir(metadata_dir):
42 sys.stderr.write(usage)
43 @@ -31,7 +31,7 @@ def command_compose(args):
44 sys.stderr.write("Argument 4 is not a directory: '%s'\n" % image_dir)
45 return 1
46
47 - gpkg_file = portage.gpkg.gpkg(portage.settings, cpv, binpkg_path)
48 + gpkg_file = portage.gpkg.gpkg(portage.settings, basename, binpkg_path)
49 metadata = gpkg_file._generate_metadata_from_dir(metadata_dir)
50 gpkg_file.compress(image_dir, metadata)
51 return os.EX_OK
52
53 diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh
54 index faa8184c6..170e60d1c 100755
55 --- a/bin/misc-functions.sh
56 +++ b/bin/misc-functions.sh
57 @@ -512,6 +512,10 @@ __dyn_package() {
58 die "PORTAGE_BINPKG_TMPFILE is unset"
59 mkdir -p "${PORTAGE_BINPKG_TMPFILE%/*}" || die "mkdir failed"
60
61 + if [[ ! -z "${BUILD_ID}" ]]; then
62 + echo -n "${BUILD_ID}" > "${PORTAGE_BUILDDIR}"/build-info/BUILD_ID
63 + fi
64 +
65 if [[ "${BINPKG_FORMAT}" == "xpak" ]]; then
66 local tar_options=""
67
68 @@ -547,7 +551,7 @@ __dyn_package() {
69 elif [[ "${BINPKG_FORMAT}" == "gpkg" ]]; then
70 PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \
71 "${PORTAGE_PYTHON:-/usr/bin/python}" "${PORTAGE_BIN_PATH}"/gpkg-helper.py compress \
72 - "${CATEGORY}/${PF}" "${PORTAGE_BINPKG_TMPFILE}" "${PORTAGE_BUILDDIR}/build-info" "${D}"
73 + "${PF}${BUILD_ID:+-${BUILD_ID}}" "${PORTAGE_BINPKG_TMPFILE}" "${PORTAGE_BUILDDIR}/build-info" "${D}"
74 if [[ $? -ne 0 ]]; then
75 rm -f "${PORTAGE_BINPKG_TMPFILE}"
76 die "Failed to create binpkg file"
77
78 diff --git a/bin/quickpkg b/bin/quickpkg
79 index 773c1c07e..eef5f912f 100755
80 --- a/bin/quickpkg
81 +++ b/bin/quickpkg
82 @@ -206,7 +206,7 @@ def quickpkg_atom(options, infos, arg, eout):
83 finally:
84 if have_lock:
85 dblnk.unlockdb()
86 - pkg_info = bintree.inject(cpv, filename=binpkg_tmpfile)
87 + pkg_info = bintree.inject(cpv, current_pkg_path=binpkg_tmpfile)
88 # The pkg_info value ensures that the following getname call
89 # returns the correct path when FEATURES=binpkg-multi-instance
90 # is enabled, but fallback to cpv in case the inject call
91
92 diff --git a/lib/_emerge/Binpkg.py b/lib/_emerge/Binpkg.py
93 index 15eb56092..949ac8ee7 100644
94 --- a/lib/_emerge/Binpkg.py
95 +++ b/lib/_emerge/Binpkg.py
96 @@ -246,7 +246,7 @@ class Binpkg(CompositeTask):
97
98 if self._fetched_pkg:
99 pkg_path = self._bintree.getname(
100 - self._bintree.inject(pkg.cpv, filename=self._fetched_pkg),
101 + self._bintree.inject(pkg.cpv, current_pkg_path=self._fetched_pkg),
102 allocate_new=False,
103 )
104 else:
105
106 diff --git a/lib/_emerge/BinpkgFetcher.py b/lib/_emerge/BinpkgFetcher.py
107 index d5275ea11..b7021e276 100644
108 --- a/lib/_emerge/BinpkgFetcher.py
109 +++ b/lib/_emerge/BinpkgFetcher.py
110 @@ -12,6 +12,7 @@ import sys
111 import portage
112 from portage import os
113 from portage.const import SUPPORTED_GENTOO_BINPKG_FORMATS
114 +from portage.const import SUPPORTED_XPAK_EXTENSIONS, SUPPORTED_GPKG_EXTENSIONS
115 from portage.exception import FileNotFound, InvalidBinaryPackageFormat
116 from portage.util._async.AsyncTaskFuture import AsyncTaskFuture
117 from portage.util._pty import _create_pty_or_pipe
118 @@ -19,27 +20,31 @@ from portage.util._pty import _create_pty_or_pipe
119
120 class BinpkgFetcher(CompositeTask):
121
122 - __slots__ = ("pkg", "pretend", "logfile", "pkg_path")
123 + __slots__ = ("pkg", "pretend", "logfile", "pkg_path", "pkg_allocated_path")
124
125 def __init__(self, **kwargs):
126 CompositeTask.__init__(self, **kwargs)
127
128 pkg = self.pkg
129 bintree = pkg.root_config.trees["bintree"]
130 - binpkg_path = None
131 + instance_key = bintree.dbapi._instance_key(pkg.cpv)
132 + binpkg_format = bintree._remotepkgs[instance_key].get("BINPKG_FORMAT", None)
133
134 - if bintree._remote_has_index:
135 - instance_key = bintree.dbapi._instance_key(pkg.cpv)
136 + if binpkg_format is None:
137 binpkg_path = bintree._remotepkgs[instance_key].get("PATH")
138 - if binpkg_path:
139 - self.pkg_path = binpkg_path + ".partial"
140 + if binpkg_path.endswith(SUPPORTED_XPAK_EXTENSIONS):
141 + binpkg_format = "xpak"
142 + elif binpkg_path.endswith(SUPPORTED_GPKG_EXTENSIONS):
143 + binpkg_format = "gpkg"
144 else:
145 - self.pkg_path = (
146 - pkg.root_config.trees["bintree"].getname(pkg.cpv, allocate_new=True)
147 - + ".partial"
148 + raise InvalidBinaryPackageFormat(
149 + f"Unsupported binary package format from '{binpkg_path}'"
150 )
151 - else:
152 - raise FileNotFound("Binary packages index not found")
153 +
154 + self.pkg_allocated_path = pkg.root_config.trees["bintree"].getname(
155 + pkg.cpv, allocate_new=True, remote_binpkg_format=binpkg_format
156 + )
157 + self.pkg_path = self.pkg_allocated_path + ".partial"
158
159 def _start(self):
160 fetcher = _BinpkgFetcherProcess(
161
162 diff --git a/lib/_emerge/BinpkgPrefetcher.py b/lib/_emerge/BinpkgPrefetcher.py
163 index 3df9ebd76..912673bd1 100644
164 --- a/lib/_emerge/BinpkgPrefetcher.py
165 +++ b/lib/_emerge/BinpkgPrefetcher.py
166 @@ -11,6 +11,7 @@ class BinpkgPrefetcher(CompositeTask):
167
168 __slots__ = ("pkg",) + (
169 "pkg_path",
170 + "pkg_allocated_path",
171 "_bintree",
172 )
173
174 @@ -23,6 +24,7 @@ class BinpkgPrefetcher(CompositeTask):
175 scheduler=self.scheduler,
176 )
177 self.pkg_path = fetcher.pkg_path
178 + self.pkg_allocated_path = fetcher.pkg_allocated_path
179 self._start_task(fetcher, self._fetcher_exit)
180
181 def _fetcher_exit(self, fetcher):
182 @@ -45,7 +47,11 @@ class BinpkgPrefetcher(CompositeTask):
183 self.wait()
184 return
185
186 - self._bintree.inject(self.pkg.cpv, filename=self.pkg_path)
187 + self._bintree.inject(
188 + self.pkg.cpv,
189 + current_pkg_path=self.pkg_path,
190 + allocated_pkg_path=self.pkg_allocated_path,
191 + )
192
193 self._current_task = None
194 self.returncode = os.EX_OK
195
196 diff --git a/lib/_emerge/EbuildBinpkg.py b/lib/_emerge/EbuildBinpkg.py
197 index ccdd30f7b..04a17f283 100644
198 --- a/lib/_emerge/EbuildBinpkg.py
199 +++ b/lib/_emerge/EbuildBinpkg.py
200 @@ -6,8 +6,6 @@ from _emerge.EbuildPhase import EbuildPhase
201
202 import portage
203 from portage import os
204 -from portage.const import SUPPORTED_GENTOO_BINPKG_FORMATS
205 -from portage.exception import InvalidBinaryPackageFormat
206
207
208 class EbuildBinpkg(CompositeTask):
209 @@ -15,30 +13,27 @@ class EbuildBinpkg(CompositeTask):
210 This assumes that src_install() has successfully completed.
211 """
212
213 - __slots__ = ("pkg", "settings") + ("_binpkg_tmpfile", "_binpkg_info")
214 + __slots__ = ("pkg", "settings") + (
215 + "_binpkg_tmpfile",
216 + "_binpkg_info",
217 + "pkg_allocated_path",
218 + )
219
220 def _start(self):
221 pkg = self.pkg
222 root_config = pkg.root_config
223 bintree = root_config.trees["bintree"]
224 - binpkg_format = self.settings.get(
225 - "BINPKG_FORMAT", SUPPORTED_GENTOO_BINPKG_FORMATS[0]
226 + pkg_allocated_path, build_id = bintree.getname_build_id(
227 + pkg.cpv, allocate_new=True
228 )
229 - if binpkg_format == "xpak":
230 - binpkg_tmpfile = os.path.join(
231 - bintree.pkgdir, pkg.cpv + ".tbz2." + str(portage.getpid())
232 - )
233 - elif binpkg_format == "gpkg":
234 - binpkg_tmpfile = os.path.join(
235 - bintree.pkgdir, pkg.cpv + ".gpkg.tar." + str(portage.getpid())
236 - )
237 - else:
238 - raise InvalidBinaryPackageFormat(binpkg_format)
239 - bintree._ensure_dir(os.path.dirname(binpkg_tmpfile))
240
241 - self._binpkg_tmpfile = binpkg_tmpfile
242 + self.pkg_allocated_path = pkg_allocated_path
243 + self._binpkg_tmpfile = self.pkg_allocated_path + "." + str(portage.getpid())
244 self.settings["PORTAGE_BINPKG_TMPFILE"] = self._binpkg_tmpfile
245
246 + if "binpkg-multi-instance" in self.settings.features:
247 + self.settings["BUILD_ID"] = str(build_id)
248 +
249 package_phase = EbuildPhase(
250 background=self.background,
251 phase="package",
252 @@ -61,7 +56,11 @@ class EbuildBinpkg(CompositeTask):
253
254 pkg = self.pkg
255 bintree = pkg.root_config.trees["bintree"]
256 - self._binpkg_info = bintree.inject(pkg.cpv, filename=self._binpkg_tmpfile)
257 + self._binpkg_info = bintree.inject(
258 + pkg.cpv,
259 + current_pkg_path=self._binpkg_tmpfile,
260 + allocated_pkg_path=self.pkg_allocated_path,
261 + )
262
263 self._current_task = None
264 self.returncode = os.EX_OK
265
266 diff --git a/lib/_emerge/Scheduler.py b/lib/_emerge/Scheduler.py
267 index bc3531627..9e210f182 100644
268 --- a/lib/_emerge/Scheduler.py
269 +++ b/lib/_emerge/Scheduler.py
270 @@ -975,7 +975,7 @@ class Scheduler(PollScheduler):
271 continue
272
273 if fetched:
274 - bintree.inject(x.cpv, filename=fetched)
275 + bintree.inject(x.cpv, current_pkg_path=fetched)
276
277 infloc = os.path.join(build_dir_path, "build-info")
278 ensure_dirs(infloc)
279
280 diff --git a/lib/portage/dbapi/bintree.py b/lib/portage/dbapi/bintree.py
281 index 814e6627c..7f5dc051c 100644
282 --- a/lib/portage/dbapi/bintree.py
283 +++ b/lib/portage/dbapi/bintree.py
284 @@ -47,6 +47,7 @@ from portage.exception import (
285 from portage.localization import _
286 from portage.output import colorize
287 from portage.package.ebuild.profile_iuse import iter_iuse_vars
288 +from portage.util import ensure_dirs
289 from portage.util.file_copy import copyfile
290 from portage.util.futures import asyncio
291 from portage.util.futures.executor.fork import ForkExecutor
292 @@ -733,7 +734,8 @@ class binarytree:
293 # assuming that it will be deleted by eclean-pkg when its
294 # time comes.
295 mynewcpv = _pkg_str(mynewcpv, metadata=metadata, db=self.dbapi)
296 - update_path = self.getname(mynewcpv, allocate_new=True) + ".partial"
297 + allocated_pkg_path = self.getname(mynewcpv, allocate_new=True)
298 + update_path = allocated_pkg_path + ".partial"
299 self._ensure_dir(os.path.dirname(update_path))
300 update_path_lock = None
301 try:
302 @@ -747,7 +749,11 @@ class binarytree:
303 mybinpkg.update_metadata(mydata, new_basename=mynewcpv)
304 else:
305 raise InvalidBinaryPackageFormat(binpkg_format)
306 - self.inject(mynewcpv, filename=update_path)
307 + self.inject(
308 + mynewcpv,
309 + current_pkg_path=update_path,
310 + allocated_pkg_path=allocated_pkg_path,
311 + )
312 finally:
313 if update_path_lock is not None:
314 try:
315 @@ -1590,13 +1596,17 @@ class binarytree:
316 self._additional_pkgs[instance_key] = pkg
317 self.dbapi.cpv_inject(pkg)
318
319 - def inject(self, cpv, filename=None):
320 + def inject(self, cpv, current_pkg_path=None, allocated_pkg_path=None):
321 """Add a freshly built package to the database. This updates
322 $PKGDIR/Packages with the new package metadata (including MD5).
323 @param cpv: The cpv of the new package to inject
324 @type cpv: string
325 - @param filename: File path of the package to inject, or None if it's
326 - already in the location returned by getname()
327 + @param current_pkg_path: File path of the package to inject,
328 + or None if it's already in the location returned by getname()
329 + @type filename: string
330 + @rtype: _pkg_str or None
331 + @param allocated_pkg_path: File path of the package that was newly
332 + allocated or None if it's not allocated.
333 @type filename: string
334 @rtype: _pkg_str or None
335 @return: A _pkg_str instance on success, or None on failure.
336 @@ -1604,10 +1614,10 @@ class binarytree:
337 mycat, mypkg = catsplit(cpv)
338 if not self.populated:
339 self.populate()
340 - if filename is None:
341 + if current_pkg_path is None:
342 full_path = self.getname(cpv)
343 else:
344 - full_path = filename
345 + full_path = current_pkg_path
346 try:
347 s = os.stat(full_path)
348 except OSError as e:
349 @@ -1615,7 +1625,7 @@ class binarytree:
350 raise
351 del e
352 writemsg(
353 - _("!!! Binary package does not exist: '%s'\n") % full_path,
354 + f"!!! Binary package does not exist: '{full_path}'\n",
355 noiselevel=-1,
356 )
357 return
358 @@ -1664,48 +1674,19 @@ class binarytree:
359 try:
360 os.makedirs(self.pkgdir, exist_ok=True)
361 pkgindex_lock = lockfile(self._pkgindex_file, wantnewlockfile=1)
362 - if filename is not None:
363 - new_filename = self.getname(cpv, allocate_new=True)
364 + if current_pkg_path is not None:
365 + if allocated_pkg_path is not None:
366 + new_path = allocated_pkg_path
367 + else:
368 + new_path = self.getname(cpv, allocate_new=True)
369 try:
370 - samefile = os.path.samefile(filename, new_filename)
371 + samefile = os.path.samefile(current_pkg_path, new_path)
372 except OSError:
373 samefile = False
374 if not samefile:
375 - self._ensure_dir(os.path.dirname(new_filename))
376 - _movefile(filename, new_filename, mysettings=self.settings)
377 - full_path = new_filename
378 -
379 - basename = os.path.basename(full_path)
380 - pf = catsplit(cpv)[1]
381 - if (build_id is None) and (not fetched) and binpkg_format:
382 - # Apply the newly assigned BUILD_ID. This is intended
383 - # to occur only for locally built packages. If the
384 - # package was fetched, we want to preserve its
385 - # attributes, so that we can later distinguish that it
386 - # is identical to its remote counterpart.
387 - build_id = self._parse_build_id(basename)
388 - if build_id > 0:
389 - metadata["BUILD_ID"] = str(build_id)
390 - cpv = _pkg_str(
391 - cpv, metadata=metadata, settings=self.settings, db=self.dbapi
392 - )
393 - if binpkg_format == "xpak":
394 - if basename.endswith(".xpak"):
395 - binpkg = portage.xpak.tbz2(full_path)
396 - binary_data = binpkg.get_data()
397 - binary_data[b"BUILD_ID"] = _unicode_encode(
398 - metadata["BUILD_ID"]
399 - )
400 - binpkg.recompose_mem(portage.xpak.xpak_mem(binary_data))
401 - elif binpkg_format == "gpkg":
402 - binpkg = portage.gpkg.gpkg(self.settings, cpv, full_path)
403 - binpkg_metadata = binpkg.get_metadata()
404 - binpkg_metadata["BUILD_ID"] = _unicode_encode(
405 - metadata["BUILD_ID"]
406 - )
407 - binpkg.update_metadata(binpkg_metadata)
408 - else:
409 - raise InvalidBinaryPackageFormat(basename)
410 + self._ensure_dir(os.path.dirname(new_path))
411 + _movefile(current_pkg_path, new_path, mysettings=self.settings)
412 + full_path = new_path
413
414 self._file_permissions(full_path)
415 pkgindex = self._load_pkgindex()
416 @@ -2055,7 +2036,12 @@ class binarytree:
417 return ""
418 return mymatch
419
420 - def getname(self, cpv, allocate_new=None):
421 + def getname(self, cpv, allocate_new=None, remote_binpkg_format=None):
422 + return self.getname_build_id(
423 + cpv, allocate_new=allocate_new, remote_binpkg_format=remote_binpkg_format
424 + )[0]
425 +
426 + def getname_build_id(self, cpv, allocate_new=None, remote_binpkg_format=None):
427 """Returns a file location for this package.
428 If cpv has both build_time and build_id attributes, then the
429 path to the specific corresponding instance is returned.
430 @@ -2072,8 +2058,9 @@ class binarytree:
431 cpv = _pkg_str(cpv)
432
433 filename = None
434 + build_id = None
435 if allocate_new:
436 - filename = self._allocate_filename(cpv)
437 + filename, build_id = self._allocate_filename(cpv)
438 elif self._is_specific_instance(cpv):
439 instance_key = self.dbapi._instance_key(cpv)
440 path = self._pkg_paths.get(instance_key)
441 @@ -2090,7 +2077,7 @@ class binarytree:
442 if filename is not None:
443 filename = os.path.join(self.pkgdir, filename)
444 elif instance_key in self._additional_pkgs:
445 - return None
446 + return (None, None)
447
448 if filename is None:
449 try:
450 @@ -2133,7 +2120,7 @@ class binarytree:
451 else:
452 raise InvalidBinaryPackageFormat(binpkg_format)
453
454 - return filename
455 + return (filename, build_id)
456
457 def _is_specific_instance(self, cpv):
458 specific = True
459 @@ -2154,22 +2141,26 @@ class binarytree:
460 max_build_id = x.build_id
461 return max_build_id
462
463 - def _allocate_filename(self, cpv):
464 - try:
465 - binpkg_format = cpv.binpkg_format
466 - except AttributeError:
467 - binpkg_format = self.settings.get(
468 - "BINPKG_FORMAT", SUPPORTED_GENTOO_BINPKG_FORMATS[0]
469 - )
470 + def _allocate_filename(self, cpv, remote_binpkg_format=None):
471 + if remote_binpkg_format is None:
472 + try:
473 + binpkg_format = cpv.binpkg_format
474 + except AttributeError:
475 + binpkg_format = self.settings.get(
476 + "BINPKG_FORMAT", SUPPORTED_GENTOO_BINPKG_FORMATS[0]
477 + )
478 + else:
479 + binpkg_format = remote_binpkg_format
480
481 + # Do not create a new placeholder to avoid overwriting existing binpkgs.
482 if binpkg_format == "xpak":
483 - return os.path.join(self.pkgdir, cpv + ".tbz2")
484 + return (os.path.join(self.pkgdir, cpv + ".tbz2"), None)
485 elif binpkg_format == "gpkg":
486 - return os.path.join(self.pkgdir, cpv + ".gpkg.tar")
487 + return (os.path.join(self.pkgdir, cpv + ".gpkg.tar"), None)
488 else:
489 raise InvalidBinaryPackageFormat(binpkg_format)
490
491 - def _allocate_filename_multi(self, cpv):
492 + def _allocate_filename_multi(self, cpv, remote_binpkg_format=None):
493
494 # First, get the max build_id found when _populate was
495 # called.
496 @@ -2181,29 +2172,39 @@ class binarytree:
497 pf = catsplit(cpv)[1]
498 build_id = max_build_id + 1
499
500 - try:
501 - binpkg_format = cpv.binpkg_format
502 - except AttributeError:
503 - binpkg_format = self.settings.get(
504 - "BINPKG_FORMAT", SUPPORTED_GENTOO_BINPKG_FORMATS[0]
505 - )
506 + if remote_binpkg_format is None:
507 + try:
508 + binpkg_format = cpv.binpkg_format
509 + except AttributeError:
510 + binpkg_format = self.settings.get(
511 + "BINPKG_FORMAT", SUPPORTED_GENTOO_BINPKG_FORMATS[0]
512 + )
513 + else:
514 + binpkg_format = remote_binpkg_format
515
516 if binpkg_format == "xpak":
517 - filename_format = "%s-%s.xpak"
518 + binpkg_suffix = "xpak"
519 elif binpkg_format == "gpkg":
520 - filename_format = "%s-%s.gpkg.tar"
521 + binpkg_suffix = "gpkg.tar"
522 else:
523 raise InvalidBinaryPackageFormat(binpkg_format)
524
525 while True:
526 - filename = filename_format % (
527 - os.path.join(self.pkgdir, cpv.cp, pf),
528 - build_id,
529 + filename = (
530 + f"{os.path.join(self.pkgdir, cpv.cp, pf)}-{build_id}.{binpkg_suffix}"
531 )
532 if os.path.exists(filename):
533 build_id += 1
534 else:
535 - return filename
536 + try:
537 + # Avoid races
538 + ensure_dirs(os.path.dirname(filename))
539 + with open(filename, "x") as f:
540 + pass
541 + except FileExistsError:
542 + build_id += 1
543 + continue
544 + return (filename, build_id)
545
546 @staticmethod
547 def _parse_build_id(filename):
548
549 diff --git a/lib/portage/package/ebuild/_config/special_env_vars.py b/lib/portage/package/ebuild/_config/special_env_vars.py
550 index 04e4c5b9b..1de62e421 100644
551 --- a/lib/portage/package/ebuild/_config/special_env_vars.py
552 +++ b/lib/portage/package/ebuild/_config/special_env_vars.py
553 @@ -88,6 +88,7 @@ environ_whitelist += [
554 "BASH_FUNC____in_portage_iuse%%",
555 "BINPKG_FORMAT",
556 "BROOT",
557 + "BUILD_ID",
558 "BUILD_PREFIX",
559 "COLUMNS",
560 "D",
561
562 diff --git a/lib/portage/package/ebuild/doebuild.py b/lib/portage/package/ebuild/doebuild.py
563 index 8ee9f73c6..d0d134b39 100644
564 --- a/lib/portage/package/ebuild/doebuild.py
565 +++ b/lib/portage/package/ebuild/doebuild.py
566 @@ -1463,7 +1463,8 @@ def doebuild(
567 if retval == os.EX_OK:
568 if mydo == "package" and bintree is not None:
569 pkg = bintree.inject(
570 - mysettings.mycpv, filename=mysettings["PORTAGE_BINPKG_TMPFILE"]
571 + mysettings.mycpv,
572 + current_pkg_path=mysettings["PORTAGE_BINPKG_TMPFILE"],
573 )
574 if pkg is not None:
575 infoloc = os.path.join(